Skip to content

MCP Integration ​

The Model Context Protocol (MCP) is the foundation that enables Orchestre to extend Claude Code's capabilities. This guide provides a deep dive into how Orchestre leverages MCP to create a seamless development experience.

What is MCP? ​

The Model Context Protocol is an open standard that allows AI assistants like Claude to interact with external tools and services. Think of it as a bridge between Claude's intelligence and the outside world.

How Orchestre Uses MCP ​

Architecture Overview ​

Orchestre implements an MCP server that:

  1. Registers Tools: Exposes functions Claude can call
  2. Handles Requests: Processes tool invocations
  3. Returns Results: Sends structured data back to Claude
  4. Manages State: Maintains context between calls

The MCP Server ​

typescript
// src/server.ts - Simplified view
import { MCPServer } from '@modelcontextprotocol/sdk';

const server = new MCPServer({
  name: 'orchestre',
  version: '5.0.0',
  tools: {
    initialize_project: initializeProjectTool,
    analyze_project: analyzeProjectTool,
    generate_plan: generatePlanTool,
    multi_llm_review: multiLlmReviewTool,
    research: researchTool,
    take_screenshot: takeScreenshotTool,
    get_last_screenshot: getLastScreenshotTool,
    list_windows: listWindowsTool
  },
  prompts: [
    // 20 essential prompts registered dynamically
  ]
});

server.start();

Tool Implementation ​

Tool Structure ​

Each MCP tool follows a specific structure:

typescript
interface ToolDefinition {
  name: string;
  description: string;
  input_schema: {
    type: 'object';
    properties: Record<string, any>;
    required?: string[];
  };
  handler: (args: any) => Promise<ToolResponse>;
}

Example: Initialize Project Tool ​

Let's examine how the initialize_project tool works:

typescript
// src/tools/initializeProject.ts
export const initializeProjectTool: ToolDefinition = {
  name: 'initialize_project',
  description: 'Initialize a new Orchestre project with a template',
  input_schema: {
    type: 'object',
    properties: {
      template: {
        type: 'string',
        description: 'Template name (makerkit, cloudflare, react-native)',
        enum: ['makerkit', 'cloudflare', 'react-native']
      },
      projectName: {
        type: 'string',
        description: 'Name of the project'
      },
      targetPath: {
        type: 'string',
        description: 'Target directory path'
      }
    },
    required: ['template', 'projectName']
  },
  
  handler: async ({ template, projectName, targetPath }) => {
    // 1. Validate inputs
    const validatedInputs = initializeSchema.parse({
      template,
      projectName,
      targetPath
    });
    
    // 2. Create project structure
    const projectPath = await createProjectStructure(validatedInputs);
    
    // 3. Copy template files
    await copyTemplateFiles(template, projectPath);
    
    // 4. Install knowledge pack
    await installKnowledgePack(template, projectPath);
    
    // 5. Return structured result
    return {
      content: [{
        type: 'text',
        text: JSON.stringify({
          success: true,
          projectPath,
          template,
          commandsInstalled: getTemplateCommands(template),
          nextSteps: [
            `cd ${projectPath}`,
            'Read requirements.md',
            '/orchestrate requirements.md'
          ]
        }, null, 2)
      }]
    };
  }
};

MCP Communication Flow ​

Tool Invocation ​

When you use an Orchestre command in Claude Code:

  1. Command Recognition

    User: /orchestrate "build a payment system"
  2. Claude Interprets

    • Claude reads the command definition
    • Understands it needs to call MCP tools
    • Prepares tool invocations
  3. MCP Request

    typescript
    // Claude sends to MCP server
    {
      tool: "analyze_project",
      arguments: {
        requirements: "build a payment system",
        context: { /* current project state */ }
      }
    }
  4. Orchestre Processes

    • Receives request via MCP
    • Executes tool handler
    • May call external APIs (Gemini, GPT-4)
    • Returns structured data
  5. Claude Uses Results

    • Receives tool response
    • Incorporates into reasoning
    • Generates implementation

Response Format ​

MCP tools return structured responses:

typescript
interface ToolResponse {
  content: Array<{
    type: 'text' | 'image' | 'code';
    text?: string;
    data?: any;
    language?: string;
  }>;
  isError?: boolean;
}

Configuration ​

Claude Desktop Config ​

Orchestre integrates via Claude's configuration:

json
{
  "mcpServers": {
    "orchestre": {
      "command": "node",
      "args": ["/path/to/orchestre/dist/server.js"],
      "env": {
        "GEMINI_API_KEY": "your-key",
        "OPENAI_API_KEY": "your-key",
        "LOG_LEVEL": "info"
      }
    }
  }
}

Configuration Options ​

OptionPurposeExample
commandExecutable to run"node"
argsCommand arguments["dist/server.js"]
envEnvironment variables{"API_KEY": "..."}
cwdWorking directory"/path/to/orchestre"

Advanced MCP Features ​

Tool Composition ​

Tools can work together:

typescript
// In a command prompt
const result = await callTool('analyze_project', {
  requirements: userRequirements
});

if (result.complexity > 7) {
  const research = await callTool('research', {
    topic: 'handling complex architectures'
  });
  
  const plan = await callTool('generate_plan', {
    requirements: userRequirements,
    analysis: result,
    research: research
  });
}

Streaming Responses ​

For long-running operations:

typescript
handler: async function* (args) {
  yield { type: 'text', text: 'Starting analysis...' };
  
  const result1 = await analyzePhase1(args);
  yield { type: 'text', text: `Phase 1 complete: ${result1}` };
  
  const result2 = await analyzePhase2(args);
  yield { type: 'text', text: `Phase 2 complete: ${result2}` };
  
  return { type: 'text', text: JSON.stringify(finalResult) };
}

Error Handling ​

Robust error handling in MCP tools:

typescript
handler: async (args) => {
  try {
    const result = await riskyOperation(args);
    return {
      content: [{
        type: 'text',
        text: JSON.stringify(result)
      }]
    };
  } catch (error) {
    return {
      content: [{
        type: 'text',
        text: JSON.stringify({
          error: error.message,
          code: error.code || 'UNKNOWN_ERROR',
          suggestions: getSuggestions(error)
        })
      }],
      isError: true
    };
  }
}

MCP Best Practices ​

1. Tool Design ​

Single Responsibility

typescript
// ✅ Good: Focused tool
const analyzeCodeQuality = {
  name: 'analyze_code_quality',
  description: 'Analyze code quality metrics'
};

// ❌ Bad: Kitchen sink tool
const doEverything = {
  name: 'do_everything',
  description: 'Analyze, plan, execute, and deploy'
};

Clear Schemas

typescript
// ✅ Good: Well-defined schema
input_schema: {
  type: 'object',
  properties: {
    filePath: {
      type: 'string',
      description: 'Path to file to analyze'
    },
    metrics: {
      type: 'array',
      items: {
        type: 'string',
        enum: ['complexity', 'coverage', 'duplication']
      }
    }
  },
  required: ['filePath']
}

2. Performance ​

Efficient Operations

typescript
// Cache expensive operations
const cache = new Map();

handler: async (args) => {
  const cacheKey = JSON.stringify(args);
  if (cache.has(cacheKey)) {
    return cache.get(cacheKey);
  }
  
  const result = await expensiveOperation(args);
  cache.set(cacheKey, result);
  return result;
}

Timeout Handling

typescript
handler: async (args) => {
  return Promise.race([
    performOperation(args),
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error('Operation timeout')), 30000)
    )
  ]);
}

3. Security ​

Input Validation

typescript
import { z } from 'zod';

const schema = z.object({
  filePath: z.string().regex(/^[a-zA-Z0-9\/_-]+$/),
  content: z.string().max(100000)
});

handler: async (args) => {
  const validated = schema.parse(args); // Throws if invalid
  return processValidated(validated);
}

Sandboxing

typescript
// Never execute arbitrary code
// ❌ Dangerous
eval(args.code);

// ✅ Safe
const ast = parseCode(args.code);
const analysis = analyzeAST(ast);

Debugging MCP ​

Logging ​

Enable detailed logging:

typescript
// In your MCP server
import { logger } from './utils/logger';

handler: async (args) => {
  logger.info('Tool invoked', { tool: 'analyze_project', args });
  
  try {
    const result = await analyze(args);
    logger.info('Tool completed', { tool: 'analyze_project', result });
    return result;
  } catch (error) {
    logger.error('Tool failed', { tool: 'analyze_project', error });
    throw error;
  }
}

Testing Tools ​

Test MCP tools independently:

typescript
// test/tools/analyzeProject.test.ts
import { analyzeProjectTool } from '../../src/tools/analyzeProject';

describe('analyzeProject tool', () => {
  it('should analyze simple requirements', async () => {
    const result = await analyzeProjectTool.handler({
      requirements: 'Build a todo app'
    });
    
    expect(result.content[0].type).toBe('text');
    const parsed = JSON.parse(result.content[0].text);
    expect(parsed.complexity).toBeLessThan(5);
  });
});

Common Issues ​

Tool Not Found

bash
# Check registration
grep -r "tool_name" src/

# Verify in server.ts
tools: {
  tool_name: toolDefinition // Must match
}

Invalid Response

typescript
// Always return proper format
return {
  content: [{
    type: 'text',
    text: JSON.stringify(data, null, 2)
  }]
};

Extending Orchestre ​

Adding New Tools ​

  1. Create Tool Definition
typescript
// src/tools/myNewTool.ts
export const myNewTool: ToolDefinition = {
  name: 'my_new_tool',
  description: 'What this tool does',
  input_schema: { /* ... */ },
  handler: async (args) => { /* ... */ }
};
  1. Register in Server
typescript
// src/server.ts
import { myNewTool } from './tools/myNewTool';

const tools = {
  // ... existing tools
  my_new_tool: myNewTool
};
  1. Add Types
typescript
// src/types.ts
export interface MyNewToolArgs {
  // Define input types
}

export interface MyNewToolResult {
  // Define output types
}

Creating Tool Wrappers ​

Make tools easier to use in commands:

typescript
// src/utils/toolWrappers.ts
export async function analyzeComplexity(requirements: string) {
  const result = await callTool('analyze_project', {
    requirements,
    includeMetrics: true
  });
  
  return JSON.parse(result.content[0].text).complexity;
}

MCP Limitations ​

Current Limitations ​

  1. No Bidirectional Communication: Tools can't ask questions back
  2. Stateless: Each invocation is independent
  3. Text-Based: Binary data needs encoding
  4. Synchronous: No real-time streaming yet

Workarounds ​

State Management

typescript
// Use file system for state
const stateFile = '.orchestre/state.json';
const state = await readState(stateFile);
// ... modify state
await writeState(stateFile, state);

Progress Updates

typescript
// Return incremental results
return {
  content: [{
    type: 'text',
    text: JSON.stringify({
      status: 'in_progress',
      completed: 3,
      total: 10,
      message: 'Processing phase 3...'
    })
  }]
};

Future of MCP in Orchestre ​

Planned Enhancements ​

  • 🔮 Streaming Support: Real-time progress updates
  • 🔮 Binary Data: Direct file transfers
  • 🔮 Tool Chaining: Automatic tool composition
  • 🔮 State Management: Built-in context preservation

Summary ​

MCP is the backbone that makes Orchestre possible. By understanding how it works, you can:

  • Debug issues more effectively
  • Extend Orchestre with new tools
  • Optimize performance
  • Build more sophisticated workflows

The beauty of MCP is its simplicity - it's just functions that Claude can call, but this simple protocol enables incredibly powerful capabilities.

Next Steps ​

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