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:
- Registers Tools: Exposes functions Claude can call
- Handles Requests: Processes tool invocations
- Returns Results: Sends structured data back to Claude
- Manages State: Maintains context between calls
The MCP Server
// 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:
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:
// 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:
Command Recognition
User: /orchestrate "build a payment system"Claude Interprets
- Claude reads the command definition
- Understands it needs to call MCP tools
- Prepares tool invocations
MCP Request
typescript// Claude sends to MCP server { tool: "analyze_project", arguments: { requirements: "build a payment system", context: { /* current project state */ } } }Orchestre Processes
- Receives request via MCP
- Executes tool handler
- May call external APIs (Gemini, GPT-4)
- Returns structured data
Claude Uses Results
- Receives tool response
- Incorporates into reasoning
- Generates implementation
Response Format
MCP tools return structured responses:
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:
{
"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
| Option | Purpose | Example |
|---|---|---|
command | Executable to run | "node" |
args | Command arguments | ["dist/server.js"] |
env | Environment variables | {"API_KEY": "..."} |
cwd | Working directory | "/path/to/orchestre" |
Advanced MCP Features
Tool Composition
Tools can work together:
// 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:
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:
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
// ✅ 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
// ✅ 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
// 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
handler: async (args) => {
return Promise.race([
performOperation(args),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Operation timeout')), 30000)
)
]);
}3. Security
Input Validation
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
// Never execute arbitrary code
// ❌ Dangerous
eval(args.code);
// ✅ Safe
const ast = parseCode(args.code);
const analysis = analyzeAST(ast);Debugging MCP
Logging
Enable detailed logging:
// 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:
// 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
# Check registration
grep -r "tool_name" src/
# Verify in server.ts
tools: {
tool_name: toolDefinition // Must match
}Invalid Response
// Always return proper format
return {
content: [{
type: 'text',
text: JSON.stringify(data, null, 2)
}]
};Extending Orchestre
Adding New Tools
- Create Tool Definition
// src/tools/myNewTool.ts
export const myNewTool: ToolDefinition = {
name: 'my_new_tool',
description: 'What this tool does',
input_schema: { /* ... */ },
handler: async (args) => { /* ... */ }
};- Register in Server
// src/server.ts
import { myNewTool } from './tools/myNewTool';
const tools = {
// ... existing tools
my_new_tool: myNewTool
};- Add Types
// 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:
// 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
- No Bidirectional Communication: Tools can't ask questions back
- Stateless: Each invocation is independent
- Text-Based: Binary data needs encoding
- Synchronous: No real-time streaming yet
Workarounds
State Management
// Use file system for state
const stateFile = '.orchestre/state.json';
const state = await readState(stateFile);
// ... modify state
await writeState(stateFile, state);Progress Updates
// 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.
