--- id: ai-mcp-integration-patterns title: MCP — Model Context Protocol / Tool Server category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [ai, mcp, llm, vibe-coding] tech_stack: { language: "TS / MCP SDK", applicable_to: ["Backend"] } applied_in: [] aliases: [MCP, Model Context Protocol, Anthropic, tool server, resource, prompts] --- # MCP (Model Context Protocol) > Anthropic 표준 — LLM 이 tool / resource / prompts 를 통일된 방식으로 사용. **Claude Desktop, Cursor, Cline, IDE 통합** 의 표준. JSON-RPC 기반, stdio / HTTP+SSE. ## 📖 핵심 개념 - Server: tool / resource / prompts 제공. - Client: LLM 앱 (Claude Desktop, IDE). - Tools: 함수 (action). - Resources: 데이터 (file / db). - Prompts: 재사용 prompt template. ## 💻 코드 패턴 ### MCP Server (TS, stdio) ```ts import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; const server = new Server({ name: 'acme-tools', version: '1.0.0' }, { capabilities: { tools: {}, resources: {} } }); server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'search_orders', description: 'Search orders by customer email', inputSchema: { type: 'object', properties: { email: { type: 'string' } }, required: ['email'], }, }, ], })); server.setRequestHandler(CallToolRequestSchema, async (req) => { if (req.params.name === 'search_orders') { const orders = await db.orders.findByEmail(req.params.arguments.email); return { content: [{ type: 'text', text: JSON.stringify(orders) }] }; } throw new Error('unknown tool'); }); const transport = new StdioServerTransport(); await server.connect(transport); ``` ### Run ```bash node dist/server.js ``` ### Client config (Claude Desktop) ```json // ~/Library/Application Support/Claude/claude_desktop_config.json { "mcpServers": { "acme": { "command": "node", "args": ["/path/to/server.js"], "env": { "DB_URL": "postgres://..." } } } } ``` → Claude 재시작 → tool 자동 인식. ### Resources (data exposure) ```ts server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [ { uri: 'acme://users', name: 'Users', mimeType: 'application/json' }, { uri: 'acme://orders/recent', name: 'Recent orders', mimeType: 'application/json' }, ], })); server.setRequestHandler(ReadResourceRequestSchema, async (req) => { if (req.params.uri === 'acme://users') { return { contents: [{ uri: req.params.uri, mimeType: 'application/json', text: JSON.stringify(await db.users.list()) }] }; } throw new Error('not found'); }); ``` ### Prompts (reusable template) ```ts server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [ { name: 'analyze-customer', description: 'Summarize a customer', arguments: [{ name: 'email', required: true }] }, ], })); server.setRequestHandler(GetPromptRequestSchema, async (req) => { if (req.params.name === 'analyze-customer') { return { messages: [{ role: 'user', content: { type: 'text', text: `Summarize ${req.params.arguments?.email}'s purchase history.` }, }], }; } }); ``` ### HTTP + SSE transport ```ts import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; app.get('/sse', async (req, res) => { const transport = new SSEServerTransport('/messages', res); await server.connect(transport); }); app.post('/messages', (req, res) => transport.handlePostMessage(req, res)); ``` → 멀티 client / cloud-deployed. ### Auth (HTTP) - OAuth: 사용자가 server 에 인증. - Bearer: API key. - TLS + CORS. ### Capabilities ```json { "tools": {}, "resources": { "subscribe": true }, "prompts": {}, "sampling": {} } ``` - Sampling: server 가 LLM 에 추가 호출 요청. - Subscribe: resource 변경 알림. ### Common MCP servers (커뮤니티) - @modelcontextprotocol/server-filesystem - @modelcontextprotocol/server-github - @modelcontextprotocol/server-postgres - 자체 = 회사 내 도구 wrap. ### 디버깅 ```bash # stdio 의 JSON-RPC trace MCP_DEBUG=true node server.js ``` ```ts // Inspector npx @modelcontextprotocol/inspector node server.js ``` ## 🤔 의사결정 기준 | 사용 | 추천 | |---|---| | Claude Desktop / Cursor 통합 | MCP server | | 회사 내 코딩 도우미 | MCP + private tools | | Public API → AI tool | OpenAI tool / function calling | | 프롬프트 템플릿 공유 | MCP prompts | | Cloud-hosted multi-user | MCP HTTP+SSE | | LangChain / LlamaIndex | tool wrapper (직접) | ## ❌ 안티패턴 - **Tool description 빈약**: LLM 이 못 고름. - **Tool 가 sensitive 작업 confirm 없이**: HITL 필요. - **PII resource 그대로 expose**: filter / mask. - **Tool error 그대로 throw**: LLM 이 복구 못 함. content + isError. - **Sync long task**: timeout. async + status. - **Auth 없는 prod HTTP**: 누구나 호출. - **Schema 자주 변경**: 등록된 tool descrip 깨짐. ## 🤖 LLM 활용 힌트 - 회사 도구 = MCP server 로 wrap → 모든 LLM 클라이언트 호환. - Tool 명확 descrip + JSON schema 작게. - Sensitive = HITL or audit. ## 🔗 관련 문서 - [[AI_Function_Calling_Deep]] - [[AI_Agentic_Patterns]] - [[Backend_Webhook_Patterns]]