Skip to content

MCP Protocol Reference

This document describes how Orchestre implements the Model Context Protocol (MCP) for communication with Claude Code.

Overview

The Model Context Protocol (MCP) enables Claude Code to communicate with external tools and services. Orchestre implements MCP to provide its orchestration capabilities.

Protocol Flow

Message Format

Request Structure

typescript
interface MCPRequest {
  jsonrpc: "2.0";
  id: string | number;
  method: string;
  params?: any;
}

Response Structure

typescript
interface MCPResponse {
  jsonrpc: "2.0";
  id: string | number;
  result?: {
    content: Array<{
      type: "text";
      text: string;
    }>;
    isError?: boolean;
  };
  error?: {
    code: number;
    message: string;
    data?: any;
  };
}

Tool Registration

Tool Definition

typescript
interface ToolDefinition {
  name: string;
  description: string;
  inputSchema: {
    type: "object";
    properties: Record<string, any>;
    required?: string[];
  };
}

Example Tool Registration

typescript
server.addTool({
  name: "initialize_project",
  description: "Initialize a new project with a template",
  inputSchema: {
    type: "object",
    properties: {
      name: {
        type: "string",
        description: "Project name"
      },
      template: {
        type: "string",
        description: "Template to use",
        enum: ["makerkit-nextjs", "cloudflare-hono", "react-native-expo"]
      }
    },
    required: ["name", "template"]
  }
});

Tool Implementation

Basic Pattern

typescript
async function handleTool(params: any): Promise<ToolResponse> {
  try {
    // Validate input
    const validated = schema.parse(params);
    
    // Process request
    const result = await processLogic(validated);
    
    // Return formatted response
    return {
      content: [{
        type: "text",
        text: JSON.stringify(result, null, 2)
      }]
    };
  } catch (error) {
    return {
      content: [{
        type: "text",
        text: JSON.stringify({
          error: error.message,
          details: error.stack
        }, null, 2)
      }],
      isError: true
    };
  }
}

Error Handling

typescript
// Validation error
if (!params.requiredField) {
  return {
    content: [{
      type: "text",
      text: JSON.stringify({
        error: "Missing required field: requiredField",
        code: "VALIDATION_ERROR"
      }, null, 2)
    }],
    isError: true
  };
}

// API error
try {
  const result = await externalAPI.call();
} catch (apiError) {
  return {
    content: [{
      type: "text", 
      text: JSON.stringify({
        error: "External API failed",
        details: apiError.message,
        code: "API_ERROR"
      }, null, 2)
    }],
    isError: true
  };
}

Available Methods

Tool Discovery

Method: tools/list

Response:

json
{
  "tools": [
    {
      "name": "initialize_project",
      "description": "Initialize a new project with a template",
      "inputSchema": { ... }
    },
    // ... more tools
  ]
}

Tool Invocation

Method: tools/call

Request:

json
{
  "name": "analyze_project",
  "arguments": {
    "requirements": "Build a SaaS application"
  }
}

Response:

json
{
  "content": [
    {
      "type": "text",
      "text": "{\n  \"analysis\": { ... }\n}"
    }
  ]
}

Orchestre-Specific Extensions

Multi-Part Responses

For large responses, Orchestre may split content:

typescript
return {
  content: [
    {
      type: "text",
      text: "## Part 1: Analysis\n..."
    },
    {
      type: "text", 
      text: "## Part 2: Implementation\n..."
    }
  ]
};

Progress Indicators

For long-running operations:

typescript
// Not directly supported by MCP
// Orchestre returns status in response
return {
  content: [{
    type: "text",
    text: JSON.stringify({
      status: "in_progress",
      message: "Analyzing project structure...",
      progress: 0.3
    }, null, 2)
  }]
};

Configuration

Server Initialization

typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new Server(
  {
    name: "orchestre",
    version: "3.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

Environment Variables

typescript
// Access in tool implementations
const apiKey = process.env.GEMINI_API_KEY;
const debugMode = process.env.ORCHESTRE_DEBUG === "true";

Best Practices

1. Consistent Response Format

Always return structured JSON:

typescript
return {
  content: [{
    type: "text",
    text: JSON.stringify({
      success: true,
      data: result,
      metadata: {
        timestamp: new Date().toISOString(),
        version: "1.0.0"
      }
    }, null, 2)
  }]
};

2. Comprehensive Error Information

Include helpful error details:

typescript
catch (error) {
  return {
    content: [{
      type: "text",
      text: JSON.stringify({
        error: error.message,
        code: error.code || "UNKNOWN_ERROR",
        suggestion: getSuggestionForError(error),
        documentation: getDocumentationLink(error)
      }, null, 2)
    }],
    isError: true
  };
}

3. Input Validation

Use schemas for validation:

typescript
import { z } from "zod";

const inputSchema = z.object({
  name: z.string().min(1).max(100),
  options: z.object({
    feature: z.boolean().optional(),
    template: z.enum(["basic", "advanced"]).optional()
  }).optional()
});

// In tool handler
const validated = inputSchema.parse(params);

4. Timeout Handling

Implement timeouts for external calls:

typescript
const timeout = new Promise((_, reject) => 
  setTimeout(() => reject(new Error("Operation timed out")), 30000)
);

try {
  const result = await Promise.race([
    externalOperation(),
    timeout
  ]);
} catch (error) {
  // Handle timeout or operation error
}

Debugging

Enable Debug Logging

typescript
function debugLog(message: string, data?: any) {
  if (process.env.ORCHESTRE_DEBUG) {
    console.error(`[Orchestre Debug] ${message}`, 
      data ? JSON.stringify(data, null, 2) : "");
  }
}

// Use in tools
debugLog("Tool invoked", { name: toolName, params });

Request/Response Logging

typescript
server.use((req, res, next) => {
  debugLog("MCP Request", req);
  
  const originalSend = res.send;
  res.send = function(data) {
    debugLog("MCP Response", data);
    return originalSend.call(this, data);
  };
  
  next();
});

Security Considerations

Input Sanitization

typescript
// Sanitize file paths
const safePath = path.normalize(userPath)
  .replace(/^(\.\.(\/|\\|$))+/, '');

// Validate against directory traversal
if (!safePath.startsWith(projectRoot)) {
  throw new Error("Invalid path");
}

API Key Protection

typescript
// Never log API keys
const sanitizedEnv = {
  ...process.env,
  GEMINI_API_KEY: process.env.GEMINI_API_KEY ? "[REDACTED]" : undefined,
  OPENAI_API_KEY: process.env.OPENAI_API_KEY ? "[REDACTED]" : undefined
};

Integration Examples

Creating a New Tool

typescript
// 1. Define the tool
const newTool: ToolDefinition = {
  name: "custom_analysis",
  description: "Perform custom analysis",
  inputSchema: {
    type: "object",
    properties: {
      target: { type: "string" },
      depth: { type: "number", minimum: 1, maximum: 5 }
    },
    required: ["target"]
  }
};

// 2. Implement handler
async function handleCustomAnalysis(params: any) {
  const { target, depth = 3 } = params;
  
  // Implementation
  const result = await analyze(target, depth);
  
  return {
    content: [{
      type: "text",
      text: JSON.stringify(result, null, 2)
    }]
  };
}

// 3. Register with server
server.addTool(newTool, handleCustomAnalysis);

Calling Other Tools

typescript
// Within a tool implementation
async function complexOperation(params: any) {
  // First, analyze
  const analysis = await handleAnalyzeProject({
    requirements: params.description
  });
  
  // Then, generate plan
  const plan = await handleGeneratePlan({
    analysis: JSON.parse(analysis.content[0].text)
  });
  
  return plan;
}

Testing

Unit Testing Tools

typescript
import { describe, it, expect } from "vitest";

describe("initialize_project tool", () => {
  it("should create project with valid params", async () => {
    const result = await handleInitializeProject({
      name: "test-project",
      template: "cloudflare-hono"
    });
    
    expect(result.isError).toBeFalsy();
    expect(result.content[0].type).toBe("text");
    
    const data = JSON.parse(result.content[0].text);
    expect(data.success).toBe(true);
  });
  
  it("should handle missing template", async () => {
    const result = await handleInitializeProject({
      name: "test-project"
    });
    
    expect(result.isError).toBe(true);
  });
});

Integration Testing

typescript
// Test with actual MCP server
import { spawn } from "child_process";

const server = spawn("node", ["dist/server.js"]);

// Send MCP request
server.stdin.write(JSON.stringify({
  jsonrpc: "2.0",
  id: 1,
  method: "tools/call",
  params: {
    name: "analyze_project",
    arguments: { requirements: "test" }
  }
}));

// Read response
server.stdout.on("data", (data) => {
  const response = JSON.parse(data.toString());
  // Validate response
});

Performance Considerations

Response Size

Keep responses reasonable:

typescript
const MAX_RESPONSE_SIZE = 50000; // characters

if (result.length > MAX_RESPONSE_SIZE) {
  // Summarize or paginate
  return {
    content: [{
      type: "text",
      text: JSON.stringify({
        summary: summarize(result),
        truncated: true,
        size: result.length
      }, null, 2)
    }]
  };
}

Async Operations

Use streaming where possible:

typescript
// For large file operations
async function* processLargeFile(path: string) {
  const stream = fs.createReadStream(path);
  
  for await (const chunk of stream) {
    yield processChunk(chunk);
  }
}

Versioning

Protocol Version

Orchestre implements MCP version 0.1.0

Compatibility

typescript
// Check client version
if (clientVersion < "0.1.0") {
  return {
    content: [{
      type: "text",
      text: JSON.stringify({
        error: "Client version not supported",
        minimum: "0.1.0"
      }, null, 2)
    }],
    isError: true
  };
}

Resources

Built with ❤️ for the AI Coding community, by Praney Behl