Home / Function/ handler() — mcp Function Reference

handler() — mcp Function Reference

Architecture documentation for the handler() function in explore-function.ts from the mcp codebase.

Entity Profile

Dependency Diagram

graph TD
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5["handler()"]
  ac037c5b_e159_85ae_5dd7_8b0efd91626f["asErrorResult()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| ac037c5b_e159_85ae_5dd7_8b0efd91626f
  b9fca090_95d0_4cf7_0bb0_7a7efcc55ccb["resolveOrFetchGraph()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| b9fca090_95d0_4cf7_0bb0_7a7efcc55ccb
  3d249764_f3e8_1721_0f24_435b6a46d3b1["classifyApiError()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| 3d249764_f3e8_1721_0f24_435b6a46d3b1
  ad473066_969b_8fc8_45fe_f01051030d72["findSymbol()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| ad473066_969b_8fc8_45fe_f01051030d72
  73a484a3_5184_3a80_94a1_6b67c84b4f69["buildSubdomainToParentMap()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| 73a484a3_5184_3a80_94a1_6b67c84b4f69
  3dd2d606_47d5_76c9_c834_83fbfb172417["resolveDomain()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| 3dd2d606_47d5_76c9_c834_83fbfb172417
  84fa1508_0318_0a6b_dd7c_b4917273b2b4["describeNode()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| 84fa1508_0318_0a6b_dd7c_b4917273b2b4
  d16dc47a_bbc0_2745_401a_8a4d3b67257e["normalizePath()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| d16dc47a_bbc0_2745_401a_8a4d3b67257e
  55bde18a_7860_173e_f211_5874970475e3["get()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| 55bde18a_7860_173e_f211_5874970475e3
  c652ed9f_2901_0635_2a5b_fc305e9cd6c1["has()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| c652ed9f_2901_0635_2a5b_fc305e9cd6c1
  46063d5f_ce29_e424_cd46_c460531d27b6["asTextContentResult()"]
  fdc0fb6f_bf66_23e1_e32e_b17145da77c5 -->|calls| 46063d5f_ce29_e424_cd46_c460531d27b6
  style fdc0fb6f_bf66_23e1_e32e_b17145da77c5 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

src/tools/explore-function.ts lines 170–353

export const handler: HandlerFunction = async (client, args, defaultWorkdir) => {
  const symbolArg = typeof args?.symbol === 'string' ? args.symbol.trim() : '';
  if (!symbolArg) {
    return asErrorResult({
      type: 'validation_error',
      message: 'Missing required "symbol" parameter.',
      code: 'MISSING_SYMBOL',
      recoverable: false,
      suggestion: 'Provide the name of a function to explore.',
    });
  }

  const direction = (args?.direction as string) || 'both';
  if (!['downstream', 'upstream', 'both'].includes(direction)) {
    return asErrorResult({
      type: 'validation_error',
      message: `Invalid direction "${direction}". Must be "downstream", "upstream", or "both".`,
      code: 'INVALID_DIRECTION',
      recoverable: false,
      suggestion: 'Use "downstream" (callees), "upstream" (callers), or "both".',
    });
  }
  const depth = Math.min(3, Math.max(1, Number(args?.depth) || 2));
  const rawDir = args?.directory as string | undefined;
  const directory = (rawDir && rawDir.trim()) || defaultWorkdir || process.cwd();

  if (!directory || typeof directory !== 'string') {
    return asErrorResult({
      type: 'validation_error',
      message: 'No directory provided and no default workdir configured.',
      code: 'MISSING_DIRECTORY',
      recoverable: false,
      suggestion: 'Provide a directory parameter or start the MCP server with a workdir argument.',
    });
  }

  let graph: IndexedGraph;
  try {
    graph = await resolveOrFetchGraph(client, directory);
  } catch (error: any) {
    return asErrorResult(classifyApiError(error));
  }

  // Resolve symbol name to a Function or Class node
  const matches = findSymbol(graph, symbolArg);
  const funcMatch = matches.find(n => n.labels?.[0] === 'Function' || n.labels?.[0] === 'Class');
  if (!funcMatch) {
    return asErrorResult({
      type: 'not_found_error',
      message: `No function or class matching "${symbolArg}" found in the code graph.`,
      code: 'SYMBOL_NOT_FOUND',
      recoverable: false,
      suggestion: 'Try a different function name or use partial matching.',
    });
  }

  const rootId = funcMatch.id;
  const subdomainToParent = buildSubdomainToParentMap(graph);
  const rootDomain = resolveDomain(graph, rootId, subdomainToParent);
  const rootSubName = rootDomain.subdomain;

  const lines: string[] = [];
  lines.push(`## ${describeNode(graph, rootId, null, subdomainToParent)}`);
  lines.push('');

  // Include source code for the root symbol (saves agent a Read round-trip)
  const rootSource = funcMatch.properties?.sourceCode as string | undefined;
  if (rootSource) {
    const sourceLines = rootSource.split('\n');
    const truncated = sourceLines.length > MAX_SOURCE_LINES;
    const displayLines = truncated ? sourceLines.slice(0, MAX_SOURCE_LINES) : sourceLines;
    const startLine = funcMatch.properties?.startLine as number | undefined;
    const filePath = normalizePath(funcMatch.properties?.filePath as string || '');
    const ext = filePath.split('.').pop() || '';
    lines.push(`### Source`);
    lines.push('```' + ext);
    if (startLine) {
      displayLines.forEach((l, i) => { lines.push(`${startLine + i}: ${l}`); });
    } else {
      displayLines.forEach(l => { lines.push(l); });
    }
    if (truncated) lines.push(`... (${sourceLines.length - MAX_SOURCE_LINES} more lines)`);
    lines.push('```');
    lines.push('');
  }

  // Downstream BFS (callees)
  if (direction === 'downstream' || direction === 'both') {
    lines.push('### Functions it calls:');
    let frontier = [rootId];
    const visited = new Set([rootId]);

    for (let d = 1; d <= depth; d++) {
      const nextFrontier: string[] = [];

      if (d === 1) {
        const callees = (graph.callAdj.get(rootId)?.out || [])
          .filter(id => { const l = graph.nodeById.get(id)?.labels?.[0]; return l === 'Function' || l === 'Class'; });

        if (callees.length === 0) {
          lines.push('  (none)');
        }
        for (const [i, cId] of callees.entries()) {
          visited.add(cId);
          nextFrontier.push(cId);
          lines.push(`  ${i + 1}. ${describeNode(graph, cId, rootSubName, subdomainToParent)}`);
        }
      } else {
        let anyFound = false;
        for (const parentId of frontier) {
          const parentNode = graph.nodeById.get(parentId);
          const parentName = parentNode?.properties?.name as string || '(unknown)';
          const callees = (graph.callAdj.get(parentId)?.out || [])
            .filter(id => { const l = graph.nodeById.get(id)?.labels?.[0]; return l === 'Function' || l === 'Class'; })
            .filter(id => !visited.has(id));

          if (callees.length === 0) continue;
          anyFound = true;
          lines.push(`  Via \`${parentName}\`:`);
          for (const cId of callees) {
            visited.add(cId);
            nextFrontier.push(cId);
            lines.push(`    → ${describeNode(graph, cId, rootSubName, subdomainToParent)}`);
          }
        }
        if (!anyFound) {
          lines.push(`  (no further calls at depth ${d})`);
        }
      }
      frontier = nextFrontier;
    }
    lines.push('');
  }

  // Upstream BFS (callers)
  if (direction === 'upstream' || direction === 'both') {
    lines.push('### Functions that call it:');
    let frontier = [rootId];
    const visited = new Set([rootId]);

    for (let d = 1; d <= depth; d++) {
      const nextFrontier: string[] = [];

      if (d === 1) {
        const callers = (graph.callAdj.get(rootId)?.in || [])
          .filter(id => { const l = graph.nodeById.get(id)?.labels?.[0]; return l === 'Function' || l === 'Class'; });

        if (callers.length === 0) {
          lines.push('  (none)');
        }
        for (const [i, cId] of callers.entries()) {
          visited.add(cId);
          nextFrontier.push(cId);
          lines.push(`  ${i + 1}. ${describeNode(graph, cId, rootSubName, subdomainToParent)}`);
        }
      } else {
        let anyFound = false;
        for (const parentId of frontier) {
          const parentNode = graph.nodeById.get(parentId);
          const parentName = parentNode?.properties?.name as string || '(unknown)';
          const callers = (graph.callAdj.get(parentId)?.in || [])
            .filter(id => { const l = graph.nodeById.get(id)?.labels?.[0]; return l === 'Function' || l === 'Class'; })
            .filter(id => !visited.has(id));

          if (callers.length === 0) continue;
          anyFound = true;
          lines.push(`  Via \`${parentName}\`:`);
          for (const cId of callers) {
            visited.add(cId);
            nextFrontier.push(cId);
            lines.push(`    → ${describeNode(graph, cId, rootSubName, subdomainToParent)}`);
          }
        }
        if (!anyFound) {
          lines.push(`  (no further callers at depth ${d})`);
        }
      }
      frontier = nextFrontier;
    }
    lines.push('');
  }

  return asTextContentResult(lines.join('\n'));
};

Domain

Subdomains

Frequently Asked Questions

What does handler() do?
handler() is a function in the mcp codebase.
What does handler() call?
handler() calls 11 function(s): asErrorResult, asTextContentResult, buildSubdomainToParentMap, classifyApiError, describeNode, findSymbol, get, has, and 3 more.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free