Vivioo-io/vivioo-mcp
criticalVivioo MCP Server — Agent Directory where AI agents can browse and list themselves. Connect via mcp.vivioo.io/sse
This MCP server provides a directory and job board for AI agents, allowing them to browse, submit, verify, and manage their profiles, apply to jobs, e...
418async function handleRegisterWebhook(args: Record<string, unknown>) {
419 const res = await fetchWithTimeout(`${VIVIOO_BASE}/api/notifications`, {
420 method: 'PUT',
421 headers: { 'Content-Type': 'application/json' },
422 body: JSON.stringify({
423 agentSlug: args.agentSlug,
424 editKey: args.editKey,
425 action: 'register-webhook',
426 webhookUrl: args.webhookUrl,
427 }),
428 });355async function handleVerifyGithub(args: Record<string, unknown>) {
356 const res = await fetchWithTimeout(`${VIVIOO_BASE}/api/showcase/verify/github`, {
357 method: 'POST',
358 headers: { 'Content-Type': 'application/json' },
359 body: JSON.stringify(args),
360 });
361 const data = await res.json();
362 return {
363 content: [{
364 type: 'text' as const,
365 text: JSON.stringify(data, null, 2),
366 }],
367 };
368}// Network-exposed MCP server; any client can call verify_github with an arbitrary repoUrl.
The verify_github tool accepts a repoUrl parameter and forwards it to the backend API without any validation. The backend likely fetches this URL to verify the repository. If the backend does not validate the URL, an attacker could provide a URL pointing to internal services, leading to SSRF. Additionally, the URL could be a malicious redirect or a local file URL.
ImpactAn attacker could cause the backend to make requests to internal services, potentially accessing sensitive data or performing actions on internal systems.
FixValidate the repoUrl on the backend to ensure it matches the pattern https://github.com/owner/repo and is a legitimate GitHub URL. Reject URLs pointing to private IPs or localhost.
304async function handleVerifyAgent(args: Record<string, unknown>) {
305 const { slug, editKey, xHandle, tweetUrl } = args as {
306 slug: string; editKey: string; xHandle?: string; tweetUrl?: string;
307 };
308
309 if (tweetUrl) {
310 // Step 3: Submit proof
311 const res = await fetchWithTimeout(`${VIVIOO_BASE}/api/showcase/verify/submit`, {
312 method: 'POST',
313 headers: { 'Content-Type': 'application/json' },
314 body: JSON.stringify({ slug, editKey, tweetUrl }),
315 });// Network-exposed MCP server; any client can call verify_agent with an arbitrary tweetUrl.
The verify_agent tool accepts a tweetUrl parameter and forwards it to the backend API without validation. The backend likely fetches this URL to verify the tweet. An attacker could provide a URL pointing to internal services, causing SSRF.
ImpactAn attacker could exploit SSRF to access internal services or cloud metadata by providing a malicious tweetUrl.
FixValidate the tweetUrl on the backend to ensure it is a legitimate Twitter/X URL (e.g., https://x.com/... or https://twitter.com/...). Reject URLs pointing to private IPs.
289async function handleSubmitAgent(args: Record<string, unknown>) {
290 const res = await fetchWithTimeout(`${VIVIOO_BASE}/api/showcase`, {
291 method: 'POST',
292 headers: { 'Content-Type': 'application/json' },
293 body: JSON.stringify(args),
294 });
295 const data = await res.json();
296 return {
297 content: [{
298 type: 'text' as const,
299 text: JSON.stringify(data, null, 2),
300 }],
301 };
302}// Exploitable by any attacker who can call the MCP tool. The backend may mitigate, but defense in depth is missing.
The submit_agent tool accepts arbitrary JSON input and forwards it directly to the backend API without any validation on the MCP server side. While the backend may validate, the MCP server does not enforce field lengths, types, or required fields beyond the schema definition. An attacker could send oversized strings (e.g., name > 100 chars) or unexpected data types, potentially causing backend errors or injection if the backend is not properly hardened.
ImpactAn attacker could send malformed or oversized data that might cause backend processing issues, denial of service, or exploit backend vulnerabilities. However, the backend likely has its own validation, so impact is limited.
FixAdd input validation on the MCP server side: enforce max lengths, check types, and sanitize strings before forwarding. Use a validation library or manual checks.
438async function handleGet360(args: Record<string, unknown>) {
439 const slug = args.slug as string | undefined;
440 const url = slug
441 ? `${VIVIOO_BASE}/api/360?slug=${encodeURIComponent(slug)}`
442 : `${VIVIOO_BASE}/api/360`;
443 const res = await fetchWithTimeout(url);// Network-exposed MCP server; any client can call get_360 with an arbitrary slug.
The get_360 tool accepts a slug parameter and uses it to construct a URL. While encodeURIComponent is used, there is no validation on the slug value. If the backend API uses the slug in a database query without sanitization, it could lead to injection attacks.
ImpactAn attacker could potentially manipulate the slug to access other API resources or cause unexpected behavior on the backend.
FixValidate the slug on the backend to ensure it matches expected patterns.
370async function handleBrowseJobs(args: Record<string, unknown>) {
371 const params = new URLSearchParams();
372 if (args.slug) params.set('slug', args.slug as string);
373 if (args.category) params.set('category', args.category as string);
374 if (args.skill) params.set('skill', args.skill as string);
375 const qs = params.toString();
376 const url = `${VIVIOO_BASE}/api/jobs${qs ? `?${qs}` : ''}`;
377 const res = await fetchWithTimeout(url);// Network-exposed MCP server; any client can call browse_jobs with arbitrary parameters.
The browse_jobs tool accepts slug, category, and skill parameters and passes them directly to the backend API without validation. If the backend does not sanitize these inputs, it could be vulnerable to injection attacks or unintended data exposure.
ImpactAn attacker could potentially manipulate parameters to access unauthorized data or cause the backend to behave unexpectedly.
FixValidate all input parameters on the backend to ensure they conform to expected formats and values.
242async function handleBrowseAgents(args: Record<string, unknown>) {
243 const slug = args.slug as string | undefined;
244 const url = slug
245 ? `${VIVIOO_BASE}/api/showcase?slug=${encodeURIComponent(slug)}`
246 : `${VIVIOO_BASE}/api/showcase`;
247
248 const res = await fetchWithTimeout(url);// Network-exposed MCP server; any client can call browse_agents with an arbitrary slug.
The browse_agents tool accepts a slug parameter and uses it to construct a URL. While encodeURIComponent is used, there is no validation on the slug value. If the backend API uses the slug in a database query without sanitization, it could lead to injection attacks. However, the primary risk is that the slug could be used to access unintended API endpoints if the backend does not properly validate it.
ImpactAn attacker could potentially manipulate the slug to access other API resources or cause unexpected behavior on the backend.
FixValidate the slug on the backend to ensure it matches expected patterns (e.g., alphanumeric with hyphens). Consider using a whitelist of allowed slugs.
242async function handleBrowseAgents(args: Record<string, unknown>) {
243 const slug = args.slug as string | undefined;
244 const url = slug
245 ? `${VIVIOO_BASE}/api/showcase?slug=${encodeURIComponent(slug)}`
246 : `${VIVIOO_BASE}/api/showcase`;
247
248 const res = await fetchWithTimeout(url);
249 const data = await res.json() as Record<string, unknown>;
250
251 // When listing all agents, return summaries only (full profiles are 100KB+)
252 if (!slug && data.success && Array.isArray(data.agents)) {
253 const summaries = (data.agents as Array<Record<string, unknown>>).map((a) => ({
254 slug: a.slug,
255 name: a.name,
256 platform: a.platform,
257 builder: a.builder,
258 tagline: a.tagline,
259 trustScore: a.trustScore,
260 verified: a.verified,
261 }));// Network-exposed MCP server; intended purpose is public directory, but some data may be sensitive.
Read-only tools like browse_agents, browse_jobs, get_360, and about_vivioo do not require any authentication. While this is by design for a public directory, it means anyone can access all agent profiles and job listings without restriction. This is not a vulnerability per se, but it may expose more information than intended if some agents expect privacy.
ImpactAny client can enumerate all agents and jobs, potentially accessing sensitive information that agents may not want public (e.g., weaknesses, incidents).
FixConsider implementing optional authentication for sensitive fields or providing a way for agents to mark certain information as private.