Codefluss Logo

Custom Clients

Custom Client Entwicklung

Diese Anleitung zeigt, wie du eigene MCP-Clients fĂŒr die Codefluss API entwickelst.

Grundlagen

MCP basiert auf JSON-RPC 2.0 ĂŒber HTTP. Jeder HTTP-Client kann als MCP-Client dienen.

Minimaler Client

fetch-basierter Client

class CodeflussMCPClient { private baseUrl = 'https://api.codefluss.com/api/mcp'; private apiKey: string; private requestId = 0; constructor(apiKey: string) { this.apiKey = apiKey; } private async request<T>(method: string, params?: Record<string, unknown>): Promise<T> { const response = await fetch(this.baseUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.apiKey}`, }, body: JSON.stringify({ jsonrpc: '2.0', id: ++this.requestId, method, params, }), }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (data.error) { throw new Error(`MCP Error ${data.error.code}: ${data.error.message}`); } return data.result as T; } async initialize() { return this.request<{ protocolVersion: string; capabilities: Record<string, unknown>; serverInfo: { name: string; version: string }; }>('initialize', { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'custom-client', version: '1.0.0', }, }); } async listTools() { return this.request<{ tools: Array<{ name: string; description: string; inputSchema: Record<string, unknown>; }>; }>('tools/list'); } async callTool(name: string, args: Record<string, unknown>) { return this.request<{ content: Array<{ type: string; text?: string; resource?: Record<string, unknown>; }>; }>('tools/call', { name, arguments: args }); } async listResources() { return this.request<{ resources: Array<{ uri: string; name: string; mimeType: string; }>; }>('resources/list'); } async readResource(uri: string) { return this.request<{ contents: Array<{ uri: string; mimeType: string; text: string; }>; }>('resources/read', { uri }); } }

Request/Response Flow

1. Initialize

Jede Session beginnt mit der Initialisierung:

const client = new CodeflussMCPClient('cf_live_xxx'); const serverInfo = await client.initialize(); console.log('Connected to:', serverInfo.serverInfo.name); console.log('Version:', serverInfo.serverInfo.version); console.log('Capabilities:', serverInfo.capabilities);

Response:

{ "protocolVersion": "2024-11-05", "capabilities": { "tools": {}, "resources": { "subscribe": false } }, "serverInfo": { "name": "codefluss-mcp", "version": "0.0.1-alpha" } }

2. Tools auflisten

const { tools } = await client.listTools(); tools.forEach(tool => { console.log(`${tool.name}: ${tool.description}`); });

3. Tool aufrufen

const result = await client.callTool('list_projects', {}); const projects = JSON.parse(result.content[0].text); console.log('Projects:', projects);

Error Handling

JSON-RPC Fehler

interface MCPError { code: number; message: string; data?: unknown; } // Standard JSON-RPC Fehlercodes const ErrorCodes = { PARSE_ERROR: -32700, INVALID_REQUEST: -32600, METHOD_NOT_FOUND: -32601, INVALID_PARAMS: -32602, INTERNAL_ERROR: -32603, // MCP-spezifische Codes UNAUTHORIZED: -32001, RATE_LIMITED: -32002, RESOURCE_NOT_FOUND: -32003, } as const;

Robuster Client

class RobustMCPClient extends CodeflussMCPClient { private maxRetries = 3; private retryDelay = 1000; protected async request<T>(method: string, params?: Record<string, unknown>): Promise<T> { let lastError: Error | null = null; for (let attempt = 0; attempt < this.maxRetries; attempt++) { try { return await super.request<T>(method, params); } catch (error) { lastError = error as Error; // Nicht bei Client-Fehlern wiederholen if (error instanceof MCPError) { if (error.code === ErrorCodes.INVALID_PARAMS) { throw error; } if (error.code === ErrorCodes.UNAUTHORIZED) { throw error; } } // Rate Limiting: Exponential Backoff if (error instanceof MCPError && error.code === ErrorCodes.RATE_LIMITED) { await this.sleep(this.retryDelay * Math.pow(2, attempt)); continue; } // Netzwerkfehler: Kurz warten und wiederholen await this.sleep(this.retryDelay); } } throw lastError; } private sleep(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } }

VollstÀndiges Beispiel

import { CodeflussMCPClient } from './mcp-client'; async function main() { const client = new CodeflussMCPClient(process.env.CODEFLUSS_API_KEY!); try { // 1. Initialisieren console.log('Connecting to Codefluss MCP...'); const info = await client.initialize(); console.log(`Connected: ${info.serverInfo.name} v${info.serverInfo.version}`); // 2. VerfĂŒgbare Tools anzeigen console.log('\nAvailable tools:'); const { tools } = await client.listTools(); tools.forEach(t => console.log(` - ${t.name}`)); // 3. Projekte abrufen console.log('\nFetching projects...'); const result = await client.callTool('list_projects', {}); const projects = JSON.parse(result.content[0].text); console.log(`Found ${projects.length} projects:`); projects.forEach((p: { name: string; id: string }) => { console.log(` - ${p.name} (${p.id})`); }); // 4. Design Tokens eines Projekts lesen if (projects.length > 0) { console.log(`\nReading design tokens for ${projects[0].name}...`); const tokens = await client.callTool('get_design_tokens', { projectId: projects[0].id, }); console.log('Tokens:', JSON.parse(tokens.content[0].text)); } } catch (error) { console.error('Error:', error); process.exit(1); } } main();

SDK Empfehlung

FĂŒr produktive Anwendungen empfehlen wir das offizielle MCP SDK:

npm install @modelcontextprotocol/sdk

Verwendung mit SDK

import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { HttpClientTransport } from '@modelcontextprotocol/sdk/client/http.js'; const transport = new HttpClientTransport({ url: 'https://api.codefluss.com/api/mcp', headers: { Authorization: `Bearer ${process.env.CODEFLUSS_API_KEY}`, }, }); const client = new Client({ name: 'my-app', version: '1.0.0', }); await client.connect(transport); // Tools auflisten const { tools } = await client.listTools(); // Tool aufrufen const result = await client.callTool('list_projects', {});

Best Practices

  1. Connection Pooling: FĂŒr hĂ€ufige Requests HTTP Keep-Alive nutzen
  2. Caching: Tool-Liste und Resources können gecacht werden
  3. Timeouts: Angemessene Timeouts setzen (empfohlen: 30s)
  4. Logging: Requests/Responses fĂŒr Debugging loggen
  5. Type Safety: TypeScript mit generierten Types verwenden

NĂ€chste Schritte