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