[ ⌘K ]
← BACK TO SEARCH

viviooio/vivioo-mcp

critical

MCP Server for Vivioo agent trust infrastructure — browse agents, verify, jobs, apply.

This MCP server provides a trust infrastructure for AI agents, allowing them to browse a directory of agents, submit and verify their own profiles, ap...

purpose: This MCP server provides a trust infrastructure fothreat: network exposed
TypeScript0May 19, 2026May 20, 2026GITHUB
5/20/2026
high2 findings
src/server.ts
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  });
429  const data = await res.json();
430  return {
431    content: [{
432      type: 'text' as const,
433      text: JSON.stringify(data, null, 2),
434    }],
435  };
436}
high1 finding
src/server.ts
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  });
src/index.ts:1src/server.ts:1

// Network-exposed MCP server; any client can call register_webhook with an arbitrary URL.

The register_webhook tool accepts a webhookUrl from the user and sends it to the Vivioo backend API. The backend likely uses this URL to send POST requests. If the backend does not validate the URL, an attacker could provide a URL pointing to internal services (e.g., http://169.254.169.254/latest/meta-data/ for cloud metadata, or http://localhost:8080/admin) to perform SSRF attacks. The MCP server itself does not validate the URL before forwarding it.

ImpactAn attacker could exploit SSRF to access internal cloud metadata, internal APIs, or other services behind the firewall, potentially leading to credential theft or further compromise.

FixValidate the webhookUrl on the MCP server side before forwarding: ensure it uses HTTPS, is not a private IP, and matches an allowlist of domains. Alternatively, rely on the backend to validate, but document that validation is required.

high1 finding
src/server.ts
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  });
src/index.ts:1src/server.ts:1

// Network-exposed MCP server; any client can call verify_github with an arbitrary repoUrl.

The verify_github tool accepts a repoUrl from the user and forwards it to the Vivioo backend. The backend likely fetches the repo URL to verify it. If the backend does not validate the URL, an attacker could provide a URL pointing to internal services, enabling SSRF. The MCP server does not validate the URL before forwarding.

ImpactAn attacker could exploit SSRF to access internal cloud metadata, internal APIs, or other services behind the firewall, potentially leading to credential theft or further compromise.

FixValidate the repoUrl on the MCP server side: ensure it matches the pattern https://github.com/owner/repo and is not a private IP. Alternatively, rely on backend validation but document the requirement.

high1 finding
src/server.ts
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    });
src/index.ts:1src/server.ts:1

// Network-exposed MCP server; any client can call verify_agent with an arbitrary tweetUrl.

The verify_agent tool accepts a tweetUrl from the user and forwards it to the Vivioo backend. The backend likely fetches the tweet URL to verify the post. If the backend does not validate the URL, an attacker could provide a URL pointing to internal services, enabling SSRF. The MCP server does not validate the URL before forwarding.

ImpactAn attacker could exploit SSRF to access internal cloud metadata, internal APIs, or other services behind the firewall, potentially leading to credential theft or further compromise.

FixValidate the tweetUrl on the MCP server side: ensure it matches the pattern https://twitter.com/... or https://x.com/... and is not a private IP. Alternatively, rely on backend validation but document the requirement.

medium1 finding
src/server.ts
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}
src/index.ts:1src/server.ts:355

// Exploitable if the backend does not validate the URL. The MCP server is exposed to untrusted prompts.

The verify_github tool accepts a repoUrl parameter and forwards it to the backend without validation. If the backend uses this URL to make requests (e.g., to fetch repo info), an attacker could provide a URL pointing to internal services, leading to SSRF. Additionally, the URL could be used for injection if not sanitized.

ImpactAn attacker could exploit SSRF to access internal resources or perform injection attacks on the backend.

FixValidate repoUrl on the MCP server: ensure it matches the pattern https://github.com/owner/repo and is a valid GitHub URL. Also sanitize the input.

medium1 finding
src/server.ts
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`;
src/index.ts:1src/server.ts:1

// Network-exposed MCP server; any client can call browse_agents with an arbitrary slug.

While the slug is URL-encoded, there is no validation that it is a valid agent slug (e.g., alphanumeric with hyphens). An attacker could provide a slug with special characters that, while URL-encoded, might still cause issues on the backend (e.g., path traversal if the backend uses the slug in file paths). However, the primary risk is low due to encoding.

ImpactPotential for backend injection if the backend does not properly handle the slug. Limited impact given URL encoding.

FixValidate slug format (e.g., /^[a-zA-Z0-9_-]+$/) before constructing the URL.

medium1 finding
src/server.ts
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}` : ''}`;
src/index.ts:1src/server.ts:1

// Network-exposed MCP server; any client can call browse_jobs with arbitrary parameters.

The slug, category, and skill parameters are not validated before being passed to the backend. While URLSearchParams encodes them, an attacker could inject unexpected values that might cause backend issues (e.g., SQL injection if the backend uses them in queries).

ImpactPotential for backend injection attacks depending on backend implementation.

FixValidate each parameter against an allowlist of expected values (e.g., category should be one of the documented options).

medium1 finding
src/server.ts
402async function handleCheckNotifications(args: Record<string, unknown>) {
403  const params = new URLSearchParams();
404  if (args.agentSlug) params.set('agentSlug', args.agentSlug as string);
405  if (args.editKey) params.set('editKey', args.editKey as string);
406  if (args.unreadOnly !== undefined) params.set('unreadOnly', String(args.unreadOnly));
407  const url = `${VIVIOO_BASE}/api/notifications?${params.toString()}`;
src/index.ts:1src/server.ts:1

// Network-exposed MCP server; any client can call check_notifications with arbitrary parameters.

The agentSlug and editKey are passed as query parameters without validation. An attacker could inject special characters that might cause backend issues (e.g., HTTP parameter pollution or injection).

ImpactPotential for backend injection attacks depending on backend implementation.

FixValidate agentSlug format and ensure editKey is alphanumeric or properly sanitized.

medium1 finding
src/server.ts
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`;
src/index.ts:1src/server.ts:1

// Network-exposed MCP server; any client can call get_360 with an arbitrary slug.

The slug parameter is not validated beyond URL encoding. An attacker could provide a malicious slug that might cause backend issues.

ImpactPotential for backend injection attacks depending on backend implementation.

FixValidate slug format (e.g., /^[a-zA-Z0-9_-]+$/).

low1 finding
src/server.ts
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>;
src/index.ts:1src/server.ts:1

// Network-exposed MCP server; any client can call browse_agents without authentication.

The browse_agents tool requires no authentication and returns full agent profiles (including potentially sensitive information like builder names, work items, incidents). While this is by design for a directory, it could expose more data than intended if the backend returns internal fields.

ImpactAn attacker could enumerate all agents and access their full profiles without any authentication.

FixConsider rate-limiting or adding optional authentication for sensitive fields. Ensure the backend only returns public data.

low1 finding
src/server.ts
43const submitAgentDefinition = {
44  name: 'submit_agent',
45  title: 'Submit Agent',
46  description: 'Submit your agent to the Vivioo Agent Directory. Minimum: name, platform, builder, tagline, trustScore. Your builder can enhance the profile later on the website.',
47  inputSchema: {
48    type: 'object' as const,
49    properties: {
50      name: { type: 'string', description: 'Your agent name (max 100 chars)' },
51      platform: { type: 'string', description: 'What you run on: Claude, GPT, Custom, etc.' },
52      builder: { type: 'string', description: 'Who built you (max 100 chars)' },
53      tagline: { type: 'string', description: 'One line about what you do (max 300 chars)' },
54      trustScore: { type: 'number', description: '1–100. Be honest — low scores earn badges.' },
55      // ...
56    },
57    required: ['name', 'platform', 'builder', 'tagline', 'trustScore'],
58  },
59  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
60};
src/index.ts:1src/server.ts:43

// Exploitable if the backend does not enforce length limits. The MCP server is exposed to untrusted prompts.

The input schema for submit_agent mentions max lengths in descriptions (e.g., 'max 100 chars') but does not enforce them in the schema. The MCP server does not validate string lengths before forwarding to the backend. This could allow oversized inputs that might cause buffer overflows, denial of service, or injection if the backend does not handle them properly.

ImpactAn attacker could submit extremely long strings, potentially causing backend processing issues or injection attacks.

FixAdd maxLength constraints to the JSON schema for string fields (e.g., maxLength: 100 for name). Also validate on the server side before forwarding.

shell.execauth.noneenv.exposurenetwork.http
100
LLM-based
low findings+10
high findings+100
medium findings+90
scoringcompleted