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