All files / src/utils tool-result.ts

100% Statements 26/26
100% Branches 16/16
100% Functions 2/2
100% Lines 24/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 5021x 21x     21x     21x       96x 75x 75x 74x     21x 21x 20x       20x 16x 1x   15x 15x   11x 11x   15x 15x 2x     17x       21x 83x            
import { CallToolResultSchema, type CallToolResult } from '@modelcontextprotocol/sdk/types.js';
import { AjvJsonSchemaValidator } from '@modelcontextprotocol/sdk/validation/ajv-provider.js';
import type { EnhancedToolDefinition } from '../types';
 
const validators = new WeakMap<object, ReturnType<AjvJsonSchemaValidator['getValidator']>>();
 
/** Preserve the published text contract unless a tool explicitly returns an MCP envelope. */
export function formatToolResult(
  result: unknown,
  tool: EnhancedToolDefinition | null,
): CallToolResult {
  if (tool?.resultFormat !== 'mcp') {
    const text = JSON.stringify(result, null, 2);
    if (text === undefined) throw new Error('Tool returned no output');
    return { content: [{ type: 'text', text }] };
  }
 
  const parsed = CallToolResultSchema.safeParse(result);
  if (!parsed.success) throw new Error(`Invalid MCP tool output: ${parsed.error.message}`);
  const envelope = parsed.data;
  // MCP 2025-11-25 server/tools#structured-content: success must match outputSchema;
  // https://modelcontextprotocol.io/specification/2025-11-25/server/tools#structured-content
  // execution errors are exempt. The low-level SDK Server only validates the envelope.
  if (tool.outputSchema && !envelope.isError) {
    if (envelope.structuredContent === undefined) {
      throw new Error('Tool output is missing required structuredContent');
    }
    let validate = validators.get(tool.outputSchema);
    if (!validate) {
      // One compiler per immutable schema avoids $id collisions between tool definitions.
      validate = new AjvJsonSchemaValidator().getValidator(tool.outputSchema);
      validators.set(tool.outputSchema, validate);
    }
    const validation = validate(envelope.structuredContent);
    if (!validation.valid)
      throw new Error(`Invalid structured tool output: ${validation.errorMessage}`);
  }
  // Validation must not duplicate the payload or alter vendor extensions/content blocks.
  return result as CallToolResult;
}
 
/** Errors retain the text contract used by existing clients and expose typed error data. */
export function errorToolResult(error: object): CallToolResult {
  return {
    content: [{ type: 'text', text: JSON.stringify(error, null, 2) }],
    structuredContent: { error },
    isError: true,
  };
}