Skip to main content
Glama

compare

Analyze and compare data schemas between producers and consumers to detect mismatches and prevent runtime errors through contract validation.

Instructions

Full analysis pipeline: extract producer schemas, trace consumer usage, and compare them to find mismatches. Returns a detailed report.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
producerDirYesPath to MCP server source directory
consumerDirYesPath to consumer/client source directory
formatNoOutput format
strictNoStrict mode for comparison
directionNoData flow direction (default: producer_to_consumer)

Implementation Reference

  • src/index.ts:178-192 (registration)
    Registration of the 'compare' tool in the MCP ListTools response, defining name, description, and input schema.
    {
      name: 'compare',
      description: 'Full analysis pipeline: extract producer schemas, trace consumer usage, and compare them to find mismatches. Returns a detailed report.',
      inputSchema: {
        type: 'object',
        properties: {
          producerDir: { type: 'string', description: 'Path to MCP server source directory' },
          consumerDir: { type: 'string', description: 'Path to consumer/client source directory' },
          format: { type: 'string', enum: ['json', 'markdown', 'summary'], description: 'Output format' },
          strict: { type: 'boolean', description: 'Strict mode for comparison' },
          direction: { type: 'string', enum: ['producer_to_consumer', 'consumer_to_producer', 'bidirectional'], description: 'Data flow direction (default: producer_to_consumer)' },
        },
        required: ['producerDir', 'consumerDir'],
      },
    },
  • Zod input schema (CompareInput) used for validating 'compare' tool arguments.
    const CompareInput = z.object({
      producerDir: z.string().describe('Path to MCP server source directory'),
      consumerDir: z.string().describe('Path to consumer/client source directory'),
      format: z.enum(['json', 'markdown', 'summary']).optional().describe('Output format (default: json)'),
      strict: z.boolean().optional().describe('Strict mode: treat missing optional properties as warnings'),
      direction: z.enum(['producer_to_consumer', 'consumer_to_producer', 'bidirectional']).optional().describe('Data flow direction for compatibility checking (default: producer_to_consumer)'),
    });
  • Tool handler in CallToolRequest: parses input, invokes compareDirectories, formats and returns the result.
    case 'compare': {
      const input = CompareInput.parse(args);
      log(`Comparing: ${input.producerDir} vs ${input.consumerDir}`);
    
      const result = await compareDirectories(
        input.producerDir,
        input.consumerDir,
        {
          strict: input.strict,
          direction: input.direction
        }
      );
    
      const format = (input.format || 'json') as OutputFormat;
      const output = formatResult(result, format);
    
      log(`Analysis complete: ${result.summary.matchCount} matches, ${result.summary.mismatchCount} mismatches`);
    
      return {
        content: [
          {
            type: 'text',
            text: output,
          },
        ],
      };
    }
  • Convenience helper: orchestrates extraction from directories and calls compareSchemas.
    export async function compareDirectories(
      backendDir: string,
      frontendDir: string,
      options: CompareOptions = {}
    ): Promise<TraceResult> {
      // Import dynamically to avoid circular deps
      const { extractProducerSchemas } = await import('../extract/index.js');
      const { traceConsumerUsage } = await import('../trace/index.js');
      
      console.log(`\n[Compare] Backend: ${backendDir}`);
      console.log(`[Compare] Frontend: ${frontendDir}\n`);
      
      const producers = await extractProducerSchemas({ rootDir: backendDir });
      const consumers = await traceConsumerUsage({ rootDir: frontendDir });
      
      return compareSchemas(producers, consumers, options);
    }
  • Core comparison function: matches producers and consumers, detects argument and property mismatches.
    export function compareSchemas(
      producers: ProducerSchema[],
      consumers: ConsumerSchema[],
      options: CompareOptions = {}
    ): TraceResult {
      // Default to producer_to_consumer (API response pattern)
      const direction = options.direction || 'producer_to_consumer';
    
      console.log(`[Comparator] Comparing ${producers.length} producers vs ${consumers.length} consumers`);
      console.log(`[Comparator] Direction: ${direction}`);
    
      const matches: Match[] = [];
      const mismatches: Mismatch[] = [];
    
      // Index producers by tool name for quick lookup
      const producerMap = new Map<string, ProducerSchema>();
      for (const producer of producers) {
        producerMap.set(producer.toolName, producer);
      }
    
      // Analyze each consumer usage
      for (const consumer of consumers) {
        const producer = producerMap.get(consumer.toolName);
    
        if (!producer) {
          // Tool not found in producer definitions
          mismatches.push({
            toolName: consumer.toolName,
            issueType: 'UNKNOWN_TOOL',
            description: `Tool "${consumer.toolName}" is called but not defined in producer`,
            consumerLocation: consumer.callSite,
          });
          continue;
        }
    
        // Check argument mismatches (consumer → producer direction)
        const argMismatches = checkArgumentMismatches(producer, consumer, direction);
        mismatches.push(...argMismatches);
    
        // Check expected property mismatches (producer → consumer direction)
        const propMismatches = checkPropertyMismatches(producer, consumer, options, direction);
        mismatches.push(...propMismatches);
    
        // If no mismatches for this call, it's a match
        if (argMismatches.length === 0 && propMismatches.length === 0) {
          matches.push({
            toolName: consumer.toolName,
            producerLocation: producer.location,
            consumerLocation: consumer.callSite,
          });
        }
      }
    
      return {
        timestamp: new Date().toISOString(),
        producerSource: producers.length > 0 ? producers[0].location.file : '',
        consumerSource: consumers.length > 0 ? consumers[0].callSite.file : '',
        matches,
        mismatches,
        summary: {
          totalTools: producers.length,
          totalCalls: consumers.length,
          matchCount: matches.length,
          mismatchCount: mismatches.length,
        },
      };
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Mnehmos/trace-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server