Skip to main content

mcp-client-integration

Integrates MCP clients using Python SDK v2 and TypeScript SDK v2 to connect to MCP servers, manage tools/resources/prompts, handle transport (stdio/SSE), error recovery, and implement structured calling conventions in AI agent applications.

跳到安装

来源信息

仓库
paulpas/agent-skill-router
最近来源活动
2026年9月23日 21:59
检测到的 SKILL.md 语言
英语
星标
6
分支
0

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
mcp-client-integration
description
Integrates MCP clients using Python SDK v2 and TypeScript SDK v2 to connect to MCP servers, manage tools/resources/prompts, handle transport (stdio/SSE), error recovery, and implement structured calling conventions in AI agent applications.
license
MIT
compatibility
opencode
metadata
{"version":"1.0.0","domain":"coding","role":"implementation","scope":"implementation","output-format":"code","triggers":"mcp client, mcp integration, how do i connect to mcp, consuming mcp servers, claude mcp client, typescript mcp, tool invocation","related-skills":"mcp-server-fastmcp-python, mcp-tool-design-patterns","archetypes":"tactical","anti_triggers":"brainstorming, vague ideation","response_profile":{"verbosity":"low","directive_strength":"high","abstraction_level":"operational"}}
# MCP Client Integration Implement MCP clients that connect to MCP servers, discover tools/resources/prompts, handle transport layers (stdio, SSE), and invoke server capabilities with proper error recovery in AI agent applications. ## TL;DR Checklist - [ ] Choose transport type: stdio (local), SSE (HTTP), or custom connection - [ ] Initialize client with proper server configuration and timeout settings - [ ] Implement tool discovery and resource caching mechanisms - [ ] Handle all error cases: timeouts, malformed responses, tool not found - [ ] Close connections gracefully on shutdown (context managers / async cleanup) - [ ] Test tool invocation with realistic error scenarios before production - [ ] Document required environment variables and server endpoints --- ## When to Use Use this skill when: - Building an AI agent that needs to consume tools from external MCP servers - Integrating Claude SDK with MCP clients for tool discovery and invocation - Connecting to local stdio-based servers (e.g., filesystem, database servers) - Consuming HTTP SSE-based MCP servers (streaming transport) - Implementing fallback/retry logic for unreliable MCP server connections - Caching tool/resource metadata to reduce server load --- ## When NOT to Use Avoid this skill for: - Building an MCP server (use `mcp-server-fastmcp-python` instead) - One-off tool calls without agent context (use raw HTTP requests) - Servers that don't implement MCP protocol (use native SDKs) - Synchronous code that can't handle async/await patterns (refactor to async) - Simple shell commands or subprocess calls (use `subprocess` module directly) --- ## Core Workflow ### 1. **Initialize Client with Transport** Choose the appropriate transport layer based on server type: - **Stdio**: Local in-process or subprocess servers - **SSE (HTTP)**: Remote servers, streaming responses - **Custom**: Bidirectional WebSocket or other protocols **Checkpoint:** Server is running, endpoint/executable is accessible, credentials are configured. ### 2. **Discover Available Capabilities** List and cache: - **Tools**: Callable functions with inputs/outputs - **Resources**: Named data sources (files, databases, API endpoints) - **Prompts**: Pre-defined prompt templates **Checkpoint:** Tool catalog is populated, resource URIs are validated. ### 3. **Invoke Tools with Error Handling** Call tools with proper: - Type validation for inputs - Timeout constraints - Error classification (recoverable vs permanent) - Retry logic for transient failures **Checkpoint:** Tool invocation succeeds or raises descriptive error with context. ### 4. **Manage Connection Lifecycle** Maintain connection with: - Graceful initialization (handshake, capability negotiation) - Periodic health checks for long-lived connections - Proper cleanup on shutdown (close, disconnect) - Recovery from connection loss **Checkpoint:** Client can reconnect automatically, resources are freed on exit. --- ## Implementation Patterns ### Pattern 1: Python Client with Stdio Transport Use this for local MCP servers running as subprocesses (e.g., `mcp-server-filesystem`, `mcp-server-postgres`). ```python import asyncio import json from mcp import ClientSession, StdioServerParameters from anthropic import Anthropic class MCPClientManager: """Manage MCP client connection and tool invocation.""" def __init__(self, server_path: str, server_args: list = None): """Initialize stdio-based MCP client. Args: server_path: Path to MCP server executable server_args: Command-line arguments for server Raises: FileNotFoundError: If server executable doesn't exist ValueError: If server_path is empty """ if not server_path: raise ValueError("server_path cannot be empty") self.server_path = server_path self.server_args = server_args or [] self.session: ClientSession = None self.tools_cache: dict = {} async def connect(self) -> None: """Establish connection to MCP server via stdio. Raises: ConnectionError: If server fails to start or handshake fails TimeoutError: If connection takes longer than 10 seconds """ params = StdioServerParameters( command=self.server_path, args=self.server_args ) try: self.session = await asyncio.wait_for( ClientSession.create(params), timeout=10.0 ) except asyncio.TimeoutError: raise TimeoutError(f"MCP server {self.server_path} failed to start within 10s") except Exception as e: raise ConnectionError(f"Failed to connect to MCP server: {e}") async def discover_tools(self) -> list[dict]: """Discover available tools from MCP server. Returns: List of tool definitions with name, description, input schema Raises: RuntimeError: If not connected to server ValueError: If tool discovery fails """ if not self.session: raise RuntimeError("Not connected. Call connect() first.") try: response = await self.session.list_tools() self.tools_cache = {tool.name: tool for tool in response.tools} return [ { "name": tool.name, "description": tool.description, "input_schema": tool.inputSchema } for tool in response.tools ] except Exception as e: raise ValueError(f"Tool discovery failed: {e}") async def invoke_tool(self, tool_name: str, arguments: dict) -> str: """Invoke a tool on the MCP server. Args: tool_name: Name of tool to invoke arguments: Input arguments (must match tool's inputSchema) Returns: Tool result as JSON string Raises: ValueError: If tool not found or arguments invalid TimeoutError: If invocation exceeds 30 seconds RuntimeError: If server returns error response """ if not self.session: raise RuntimeError("Not connected. Call connect() first.") if tool_name not in self.tools_cache: raise ValueError(f"Tool '{tool_name}' not found. Available: {list(self.tools_cache.keys())}") try: result = await asyncio.wait_for( self.session.call_tool(tool_name, arguments), timeout=30.0 ) return json.dumps(result.content) except asyncio.TimeoutError: raise TimeoutError(f"Tool invocation '{tool_name}' exceeded 30s timeout") except Exception as e: raise RuntimeError(f"Tool invocation failed: {tool_name}: {e}") async def close(self) -> None: """Close MCP session gracefully. Raises: RuntimeError: If close fails (logs error, continues shutdown) """ if self.session: try: await self.session.close() except Exception as e: print(f"Warning: Error closing MCP session: {e}") finally: self.session = None async def example_usage(): """Example: Connect to filesystem server and list files.""" manager = MCPClientManager("/usr/local/bin/mcp-server-filesystem") try: await manager.connect() tools = await manager.discover_tools() print(f"Available tools: {[t['name'] for t in tools]}") # Invoke 'list_directory' tool result = await manager.invoke_tool( "list_directory", {"path": "/tmp"} ) print(f"Directory listing: {result}") finally: await manager.close() # Run example if __name__ == "__main__": asyncio.run(example_usage()) ``` --- ### Pattern 2: TypeScript Client with Tool Caching and Retry Use this for integrating MCP clients with Claude SDK in TypeScript agents. ```typescript import Anthropic from "@anthropic-ai/sdk"; interface ToolDefinition { name: string; description: string; input_schema: Record<string, unknown>; } class MCPClientWithRetry { private client: Anthropic; private toolsCache: Map<string, ToolDefinition> = new Map(); private maxRetries: number = 3; private retryDelayMs: number = 1000; constructor(apiKey?: string) { this.client = new Anthropic({ apiKey: apiKey || process.env.ANTHROPIC_API_KEY, }); } /** * Discover and cache tools from MCP server. * Caching reduces server load on repeated agent loops. */ async discoverTools(): Promise<ToolDefinition[]> { // In real implementation, fetch from MCP server // For demo, return mock tools const tools: ToolDefinition[] = [ { name: "get_weather", description: "Get current weather for a location", input_schema: { type: "object", properties: { location: { type: "string", description: "City name", }, }, required: ["location"], }, }, ]; tools.forEach((tool) => this.toolsCache.set(tool.name, tool)); return tools; } /** * Invoke tool with exponential backoff retry logic. * Handles transient failures gracefully. */ async invokeTool( toolName: string, arguments: Record<string, unknown> ): Promise<string> { if (!this.toolsCache.has(toolName)) { throw new Error( `Tool '${toolName}' not found. Available: ${Array.from(this.toolsCache.keys()).join(", ")}` ); } let lastError: Error | null = null; for (let attempt = 1; attempt <= this.maxRetries; attempt++) { try { // In real implementation, call actual MCP server // For demo, simulate tool invocation return await this.simulateToolCall(toolName, arguments); } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); // Check if error is retryable if (!this.isRetryable(lastError)) { throw lastError; } // Exponential backoff: 1s, 2s, 4s if (attempt < this.maxRetries) { const delayMs = this.retryDelayMs * Math.pow(2, attempt - 1); console.warn( `Tool invocation failed (attempt ${attempt}/${this.maxRetries}), retrying in ${delayMs}ms: ${lastError.message}` ); await new Promise((resolve) => setTimeout(resolve, delayMs)); } } } throw new Error( `Tool invocation '${toolName}' failed after ${this.maxRetries} retries: ${lastError?.message}` ); } /** * Classify errors as retryable (transient) or permanent. */ private isRetryable(error: Error): boolean { const message = error.message.toLowerCase(); return ( message.includes("timeout") || message.includes("econnrefused") || message.includes("econnreset") || message.includes("503") || message.includes("service unavailable") ); } /** * Simulate tool call (replace with actual MCP server invocation). */ private async simulateToolCall( toolName: string, arguments: Record<string, unknown> ): Promise<string> { // Simulate network call await new Promise((resolve) => setTimeout(resolve, 50)); return JSON.stringify({ tool: toolName, input: arguments, result: "Tool executed successfully", }); } /** * Run agent loop with tool use. * Demonstrates integration with Claude SDK. */ async runAgent(userMessage: string): Promise<string> { const tools = await this.discoverTools(); const messages: Anthropic.MessageParam[] = [ { role: "user", content: userMessage, }, ]; // Convert tool definitions to Claude SDK format const claudeTools: Anthropic.Tool[] = tools.map((tool) => ({ name: tool.name, description: tool.description, input_schema: tool.input_schema, })); let response = await this.client.messages.create({ model: "claude-3-5-sonnet-20241022", max_tokens: 1024, tools: claudeTools, messages: messages, }); // Process tool calls in agent loop while (response.stop_reason === "tool_use") { const toolUseBlock = response.content.find( (block) => block.type === "tool_use" ) as Anthropic.ToolUseBlock | undefined; if (!toolUseBlock) break; const toolName = toolUseBlock.name; const toolInput = toolUseBlock.input as Record<string, unknown>; try { const toolResult = await this.invokeTool(toolName, toolInput); // Continue conversation with tool result messages.push({ role: "assistant", content: response.content, }); messages.push({ role: "user", content: [ { type: "tool_result", tool_use_id: toolUseBlock.id, content: toolResult, }, ], }); response = await this.client.messages.create({ model: "claude-3-5-sonnet-20241022", max_tokens: 1024, tools: claudeTools, messages: messages, }); } catch (error) { // Return error to Claude const errorMsg = error instanceof Error ? error.message : String(error); messages.push({ role: "user", content: [ { type: "tool_result", tool_use_id: toolUseBlock.id, is_error: true, content: errorMsg, }, ], }); response = await this.client.messages.create({ model: "claude-3-5-sonnet-20241022", max_tokens: 1024, tools: claudeTools, messages: messages, }); } } // Extract final text response const textBlock = response.content.find((block) => block.type === "text"); return textBlock && "text" in textBlock ? textBlock.text : ""; } } // Usage const client = new MCPClientWithRetry(); client.runAgent("What's the weather in San Francisco?"); ``` --- ### Pattern 3: BAD vs GOOD Error Handling #### ❌ BAD: Silent Failures and Unclear Recovery ```python async def invoke_tool_bad(tool_name: str, args: dict) -> dict: """Dangerous: Silently fails, no clear error context.""" try:
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看