1. Vấn Đề Thực Tế: Tích Hợp Lẻ Tẻ 🔥
Bạn đang dùng Claude Desktop. Muốn nó xem Google Calendar, đọc Notion, check Gmail, truy vấn PostgreSQL nội bộ. Thông thường, mỗi tích hợp bạn phải viết code riêng: parse API response của Google, xử lý OAuth của Notion, format email từ Gmail, build query cho SQL.
Mỗi AI tool (Claude, Cursor, Zed, VS Code) lại tích hợp lẻ tẻ với từng service → trùng lặp code, không có chuẩn chung, bảo trì cực kỳ mệt mỏi. Một thay đổi API từ Google Calendar làm hỏng integration ở 5 nơi khác nhau. Team phải maintain 10 wrapper khác nhau cho 10 service. Không có discoverability — AI không biết service nào có sẵn, capability gì.
Model Context Protocol (MCP) chính là giải pháp: "USB-C cho AI" — một chuẩn duy nhất để bất kỳ AI app nào cũng kết nối được với bất kỳ external system nào.
MCP được Anthropic công bố tháng 11/2024 dưới dạng open specification kèm SDK TypeScript và Python chính thức. Ngay lập tức được adopt bởi Cursor (codebase analysis), Zed (editor integration), Replit (Agent Protocol), CodeGPT, VS Code Copilot, và hàng chục server community.
(Claude, Cursor, Zed)
(1 per server)
(Stdio / HTTP)
(Tools/Resources/Prompts)
(API, DB, Files)
Thay vì viết 10 wrapper cho 10 service, bạn viết 1 MCP server cho mỗi service — sau đó mọi AI client đều dùng được. Write once, connect everywhere. 🎯
2. Kiến Trúc MCP Deep Dive 🏗️
MCP theo kiến trúc Client-Server dựa trên JSON-RPC 2.0 — một chuẩn RPC nhẹ, stateless, được dùng rộng rãi (Ethereum JSON-RPC, Language Server Protocol). Có 3 participant chính:
- MCP Host: AI application (Claude Desktop, Cursor, Zed) — điều phối nhiều MCP client
- MCP Client: Component trong host, maintain kết nối dedicated với 1 MCP server
- MCP Server: Program cung cấp context (tools, resources, prompts) cho client
📋 JSON-RPC 2.0 Message Format
Mọi giao tiếp giữa client ↔ server đều là JSON-RPC 2.0 messages. Ví dụ real request resources/list:
// Client → Server: Request list resources { "jsonrpc": "2.0", "id": 1, "method": "resources/list", "params": {} } // Server → Client: Response with resources { "jsonrpc": "2.0", "id": 1, "result": { "resources": [ { "uri": "file:///docs/report.pdf", "name": "Q4 Report", "description": "Quarterly financial report", "mimeType": "application/pdf" }, { "uri": "calendar://events/2024", "name": "2024 Calendar", "description": "All calendar events for 2024", "mimeType": "application/json" } ] } } // Notification (no response needed) { "jsonrpc": "2.0", "method": "notifications/resources/updated", "params": { "uri": "file:///docs/report.pdf" } }
jsonrpc: "2.0"— mandatory fieldid— correlates request/response (null cho notification)method— RPC method name (tools/list, tools/call, resources/read, prompts/list, prompts/get)params— method-specific parameters
🚀 Transport: Stdio vs Streamable HTTP
MCP định nghĩa 2 transport mechanism — layer ngoài cùng handle connection, framing, auth:
| Aspect | Stdio Transport | Streamable HTTP Transport |
|---|---|---|
| Mechanism | Standard Input/Output streams | HTTP POST + optional Server-Sent Events |
| Use Case | Local servers (same machine) | Remote servers (network) |
| Performance | ⚡ Zero network overhead, fastest | Network latency, but streaming capable |
| Auth | OS process permissions | OAuth 2.1, Bearer tokens, API keys |
| Scalability | 1 client per server process | Many clients per server |
| Example | Filesystem server, SQLite server | Sentry MCP, GitHub MCP, Remote DB |
🔄 Lifecycle: Init → Capability Negotiation → Operation
MCP là stateful protocol yêu cầu lifecycle management:
- Initialize: Client gửi
initializevới protocol version, client info, capabilities - Capability Negotiation: Server response với server capabilities (tools, resources, prompts, logging, sampling support)
- Operation: Client/server exchange RPC calls (tools/call, resources/read, prompts/get, prompts/get)
- Shutdown: Graceful disconnect
// 1. Client → Server: Initialize { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": { "tools": {}, "resources": {}, "prompts": {} }, "clientInfo": { "name": "Claude Desktop", "version": "0.8.0" } } } // 2. Server → Client: Capabilities { "jsonrpc": "2.0", "id": 1, "result": { "protocolVersion": "2024-11-05", "capabilities": { "tools": { "listChanged": true }, "resources": { "subscribe": true, "listChanged": true }, "prompts": { "listChanged": true }, "logging": {} }, "serverInfo": { "name": "weather-server", "version": "1.0.0" } } } // 3. Client → Server: Initialized notification (ready) { "jsonrpc": "2.0", "method": "notifications/initialized", "params": {} }
3. Core Components: Tools, Resources, Prompts, Sampling ⚙️
MCP định nghĩa 4 primitive cốt lõi mà server cung cấp cho client. Mỗi primitive serve mục đích khác nhau — hiểu rõ để thiết kế server đúng chỗ.
🔧 Tools: Hàm AI Gọi Được (Model-Controlled)
Tools là functions mà LLM tự quyết định khi nào gọi dựa trên user request. Model thấy tool description, input schema, tự chọn tool phù hợp. Tools có thể write (modify state): call API, write file, create calendar event, send Slack message.
get_weather tool
{
"name": "get_weather",
"description": "Lấy dự báo thời tiết cho một thành phố",
"inputSchema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "Tên thành phố (VD: Hanoi, Tokyo)" },
"unit": { "type": "string", "enum": ["c", "f"], "description": "Đơn vị nhiệt độ", "default": "c" }
},
"required": ["city"]
}
}
Protocol operations:
tools/list— discover available tools (returns array of tool definitions)tools/call— execute specific tool with arguments (returns result or error)
📄 Resources: Dữ Liệu Read-Only (Application-Controlled)
Resources cung cấp structured access đến thông tin để AI app retrieve và đưa vào context cho model. Khác với tools, resources không modify state — chỉ read. Application (không phải model) quyết định khi nào fetch resource, cách dùng (search embedding, pass to model, display UI).
file:///docs/report.pdf— Direct resource: fixed URI, specific documentfile:///workspace/— Directory listing resourcecalendar://events/2024— Direct resource: all 2024 eventstravel://activities/{city}/{category}— Resource Template: dynamic URI với parameterspostgres://queries/{table}/select— Template cho DB queries
Resource discovery patterns:
- Direct Resources: Fixed URI → specific data
- Resource Templates: URI pattern với parameters → flexible queries, self-documenting (title, description, mimeType)
Protocol operations: resources/list, resources/templates/list, resources/read, resources/subscribe, resources/unsubscribe
💬 Prompts: Template Tương Tác (User-Controlled)
Prompts là pre-built instruction templates mà user chọn để guide model làm việc với specific tools/resources. User (không phải model) chọn prompt → prompt inject instructions + tool/resource references vào conversation.
plan_vacation
{
"name": "plan_vacation",
"description": "Lập kế hoạch du lịch chi tiết",
"arguments": [
{ "name": "destination", "description": "Điểm đến", "required": true },
{ "name": "duration", "description": "Số ngày", "required": true },
{ "name": "budget", "description": "Ngân sách (USD)", "required": false }
]
}
Khi user chọn prompt này, client inject vào conversation:
"Bạn là travel planner. Sử dụng tools: searchFlights, createCalendarEvent, sendEmail. Lập kế hoạch 7 ngày Barcelona cho user, budget $3000."
Protocol operations: prompts/list, prompts/get
🎲 Sampling: LLM Tự Chọn Sub-Model (Server-Initiated)
Sampling là capability cho phép MCP server yêu cầu client (host) thực hiện LLM completion thay mặt server.
Server gửi sampling/createMessage với prompt, model preferences, max tokens → client chọn model phù hợp (có thể dùng model nhỏ hơn, local model, hoặc model chuyên biệt) → trả về completion.
Protocol operation: sampling/createMessage — supports model preferences (cost, speed, intelligence priority), temperature, maxTokens, stopSequences.
4. Implementation: Code Thật (Python + TypeScript) 🐍📘
Dưới đây là implementation hoàn chỉnh, chạy được — KHÔNG dùng exec code injection, dùng official SDK đúng cách.
🐍 Python SDK: Weather Server Example
Cài đặt: pip install mcp (official SDK từ Anthropic)
# weather_server.py - MCP Server cung cấp tool get_weather # Chạy: python weather_server.py from mcp.server import Server from mcp.types import Tool, TextContent import mcp.server.stdio import asyncio import httpx import os # Khởi tạo server server = Server("weather-server") # Định nghĩa tools mà server cung cấp @server.list_tools() async def list_tools() -> list[Tool]: return [ Tool( name="get_weather", description="Lấy dự báo thời tiết hiện tại cho một thành phố", inputSchema={ "type": "object", "properties": { "city": { "type": "string", "description": "Tên thành phố (VD: Hanoi, Tokyo, New York)" }, "unit": { "type": "string", "enum": ["c", "f"], "description": "Đơn vị nhiệt độ: c (Celsius) hoặc f (Fahrenheit)", "default": "c" } }, "required": ["city"] } ) ] # Handle tool execution @server.call_tool() async def call_tool(name: str, arguments: dict) -> list[TextContent]: if name != "get_weather": raise ValueError(f"Unknown tool: {name}") city = arguments["city"] unit = arguments.get("unit", "c") # Gọi API thời tiết thực tế (OpenWeatherMap example) # Thay YOUR_API_KEY bằng key thật từ openweathermap.org api_key = os.getenv("OPENWEATHER_API_KEY", "demo") async with httpx.AsyncClient() as client: try: response = await client.get( "https://api.openweathermap.org/data/2.5/weather", params={ "q": city, "appid": api_key, "units": "metric" if unit == "c" else "imperial" }, timeout=10.0 ) response.raise_for_status() data = response.json() temp = data["main"]["temp"] description = data["weather"]["0"]["description"] humidity = data["main"]["humidity"] wind_speed = data["wind"]["speed"] unit_symbol = "°C" if unit == "c" else "°F" result = ( f"🌤️ Thời tiết tại {city}:\n" f" Nhiệt độ: {temp}{unit_symbol}\n" f" Điều kiện: {description}\n" f" Độ ẩm: {humidity}%\n" f" Gió: {wind_speed} m/s" ) except httpx.HTTPStatusError as e: if e.response.status_code == 404: result = f"❌ Không tìm thấy thành phố: {city}" else: result = f"❌ Lỗi API: {e.response.status_code}" except Exception as e: result = f"❌ Lỗi: {str(e)}" return [TextContent(type="text", text=result)] # Entry point async def main(): async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): await server.run( read_stream, write_stream, server.InitializationOptions( server_name="weather-server", server_version="1.0.0" ) ) if __name__ == "__main__": asyncio.run(main())
Server("weather-server")— tạo server instance với tên@server.list_tools()— decorator đăng ký handler chotools/list@server.call_tool()— handler chotools/call, nhận name + argumentsstdio_server()— context manager tạo stdio transport (read/write streams)server.run()— event loop chính, handle JSON-RPC messages
📘 TypeScript SDK: File System Server
Cài đặt: npm install @modelcontextprotocol/sdk
// filesystem-server.ts - MCP Server đọc/ghi file local // Biên dịch: npx tsx filesystem-server.ts import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, Tool, TextContent } from "@modelcontextprotocol/sdk/types.js"; import * as fs from "fs/promises"; import * as path from "path"; // Server instance const server = new Server( { name: "filesystem-server", version: "1.0.0" }, { capabilities: { capabilities: { tools: {}, resources: {} } } ); // Allowed base directory (security: sandbox) const BASE_DIR = process.env.FS_BASE_DIR || process.cwd(); // ===== TOOLS ===== server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "read_file", description: "Đọc nội dung file", inputSchema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] } }, { name: "write_file", description: "Ghi nội dung vào file", inputSchema: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } }, required: ["path", "content"] } }, { name: "list_directory", description: "Liệt kê file trong thư mục", inputSchema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] } } ] as Tool[] })); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const targetPath = path.resolve(BASE_DIR, args.path as string); // Security: prevent directory traversal if (!targetPath.startsWith(BASE_DIR)) { throw new Error("Access denied: path outside allowed directory"); } switch (name) { case "read_file": return { content: [{ type: "text", text: await fs.readFile(targetPath, "utf-8") }] }; case "write_file": await fs.writeFile(targetPath, args.content as string); return { content: [{ type: "text", text: "File written successfully" }] }; case "list_directory": const entries = await fs.readdir(targetPath, { withFileTypes: true }); return { content: [{ type: "text", text: entries.map(e => `${e.isDirectory() ? "📁" : "📄"} ${e.name}`).join("\n") }] }; default: throw new Error(`Unknown tool: ${name}`); } }); // ===== RESOURCES ===== server.setRequestHandler(ListResourcesRequestSchema, async () => { const entries = await fs.readdir(BASE_DIR, { withFileTypes: true }); return { resources: entries .filter(e => !e.isDirectory()) .map(e => ({ uri: `file://${path.relative(BASE_DIR, path.join(BASE_DIR, e.name))}`, name: e.name, mimeType: "text/plain" })) }; }); server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const uri = request.params.uri; const filePath = path.resolve(BASE_DIR, uri.replace("file://", "")); if (!filePath.startsWith(BASE_DIR)) throw new Error("Access denied"); return { contents: [{ uri, mimeType: "text/plain", text: await fs.readFile(filePath, "utf-8") }] }; }); // Start server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Filesystem MCP server running on stdio"); } main().catch(console.error);
🔗 Kết Nối Từ Claude Desktop
Cấu hình claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\):
// claude_desktop_config.json
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/absolute/path/to/weather_server.py"],
"env": {
"OPENWEATHER_API_KEY": "your-api-key-here"
}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"],
"env": {}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxx"
}
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://user:pass@localhost/db"],
"env": {}
}
}
}
Claude Desktop sẽ tự động launch server qua stdio, negotiate capabilities, và expose tools/resources vào conversation. Bạn có thể dùng multiple servers cùng lúc — mỗi server 1 process riêng biệt. 🎯
5. So Sánh: MCP vs Function Calling vs LangChain 📊
Ba approach kết nối AI với external systems. Hiểu sự khác biệt để chọn đúng tool cho use case.
| Tiêu Chí | MCP (Model Context Protocol) | Function Calling (OpenAI/Anthropic) | LangChain / LlamaIndex |
|---|---|---|---|
| Chuẩn hóa | ✅ Open spec, cross-vendor | ❌ Vendor-specific (OpenAI format) | ❌ Framework-specific |
| Dynamic Discovery | ✅ tools/list, resources/list runtime | ❌ Static schemà at compile time | ❌ Static tool registration |
| Reuse Across Clients | ✅ 1 server → Claude, Cursor, Zed, VS Code | ❌ Chỉ trong vendor ecosystem | ❌ Chỉ trong framework |
| Transport Flexibility | ✅ Stdio + Streamable HTTP | ❌ HTTP only (vendor API) | ❌ HTTP only |
| Resource/Prompt Support | ✅ Resources, Prompts, Sampling | ❌ Chỉ Functions | ✅ Tools + Retrievers (custom) |
| Auth & Security | ✅ OAuth 2.1, bearer tokens built-in | ❌ Vendor-managed | ❌ Custom implementation |
| Learning Curve | Medium (protocol concepts) | Low (simple JSON schema) | High (abstraction layers) |
| Ecosystem Maturity | Growing (100+ servers) | Mature (widely adopted) | Mature (large community) |
| Best For | Cross-client tools, enterprise integrations | Quick OpenAI/Anthropic integration | Complex RAG, multi-step chains |
- MCP: Build tool/server 1 lần, dùng ở nhiều AI client (Cursor + Claude + Zed). Enterprise internal tools. Cần resources/prompts/sampling.
- Function Calling: Quick prototype, chỉ dùng 1 vendor (OpenAI/Claude API), không cần cross-client reuse.
- LangChain: Complex workflows (RAG, multi-agent, chain-of-thought), cần orchestration logic phức tạp trong code Python/JS.
MCP không thay thế Function Calling hay LangChain — nó giải quyết bài toán khác: portability & standardization. Bạn có thể dùng LangChain để build logic phức tạp, nhưng expose qua MCP server để mọi client đều dùng được. 🔄
6. Use Cases Thực Tế Có Tên 🏢
🎯 Cursor: Codebase Analysis & Editing
Cursor (AI-first code editor) tích hợp MCP để cho phép AI phân tích codebase toàn bộ qua filesystem server, tìm hiểu dependencies qua GitHub server, chạy tests qua terminal server.
- User hỏi: "Refactor authentication module to use JWT"
- Cursor MCP client gọi
filesystem.read_file→ đọc auth module - Gọi
github.search_code→ tìm pattern JWT trong repo - Gọi
terminal.execute→ chạy test suite verify - Kết quả: AI hiểu context toàn bộ repo, không chỉ file hiện tại
⚡ Zed: Editor Integration
Zed (high-performance Rust editor) dùng MCP để extend AI capabilities mà không cần build từng integration. Zed's MCP client connect tới:
- Language servers qua MCP → AI hiểu type errors, go-to-definition
- Git server → AI tạo commit message, review diff
- Terminal server → AI run build, debug failing tests
🚀 Replit: Agent Protocol
Replit adopt MCP như nền tảng cho Agent Protocol — cho phép AI agents tự chủ chạy trên Replit environment:
- Agent spawn → MCP connect tới Replit filesystem + shell + package manager
- Agent tự install deps, write code, run tests, deploy
- MCP sampling → agent delegate subtasks đến smaller models cho cost optimization
📋 Case Study: Công Ty Nội Bộ (FinTech Startup)
Bối cảnh: Team 20 engineers, dùng Claude Desktop + Cursor + VS Code. Có 15 internal services: PostgreSQL, Redis, Kafka, Internal API, GitLab, Jira, Confluence, Slack.
Trước MCP: Mỗi engineer tự viết script query DB, call API. Không chia sẻ. Onboarding mới mất 2 tuần học internal tools.
Sau MCP (triển khai 2 tuần):
- Build 5 MCP servers: postgres, redis, kafka, internal-api, gitlab
- Deploy trên internal Kubernetes cluster (Streamable HTTP transport, OAuth 2.1)
- Cấu hình shared
claude_desktop_config.jsoncommit vào repo onboarding - Kết quả: Engineer mới hỏi "Show me orders > $1000 last week" → AI tự query postgres server → trả kết quả. Không cần biết SQL schema.
7. Pitfalls & Best Practices ⚠️
MCP server có quyền truy cập hệ thống (file, DB, shell). Luôn sandbox:
- Filesystem server: restrict
BASE_DIR, validate path traversal (path.resolve()+startsWith()) - Database server: read-only user, query timeout, row limit
- Shell server: allowlist commands, no
rm -rf,sudo - Chạy server trong container/user riêng, không root
Stdio transport: process crash → client phải detect và restart server. Implement health check ping.
Streamable HTTP: network partition → exponential backoff retry, idempotent requests cho tools.
Luôn set timeout cho tool calls (30s default), handle CancelledError gracefully.
MCP spec versioning: protocolVersion trong initialize (VD: "2024-11-05"). Client/server phải negotiate version tương thích. SDK có thể breaking change giữa minor versions. Pin SDK version trong requirements.txt/package.json. Test với mcp inspect trước khi deploy.
Dùng MCP Inspector (official tool): npx @modelcontextprotocol/inspector — UI visualize messages, test tools/resources, debug transport. Log JSON-RPC messages: set MCP_LOG_LEVEL=debug. Stdio server: run manual python server.py → type JSON-RPC request stdin → xem response stdout.
- ✅ Mỗi server chỉ làm 1 việc tốt (Single Responsibility)
- ✅ Tool names unique, descriptive (
get_weatherkhông phảiweather) - ✅ Input schema strict (required fields, enums, format validation)
- ✅ Resource URIs follows RFC 3986, mimeType accurate
- ✅ Implement
listChangednotifications cho dynamic tools/resources - ✅ Document server capabilities trong README (tools, resources, prompts, auth)
8. Kết Luận & Roadmap 🎯
Model Context Protocol (MCP) là bước tiến quan trọng nhất trong ecosystem AI agents năm 2024-2025. Nó giải quyết bài toán fragmentation — thay vì M AI clients × N external systems = M×N integrations, ta có M clients + N servers = M+N connections qua 1 chuẩn chung.
Spec 1.0 Release
SDK Maturity
100+ Servers
Enterprise Auth
Registry/Marketplace
Remote-first
Agent-to-Agent
Roadmap chính thức (từ modelcontextprotocol.io):
- Authorization: OAuth 2.1 full support, enterprise-managed auth (Active Directory, Okta integration)
- Registry: MCP Server Registry/Marketplace — discover, install, rate servers như npm
- Interceptors: Middleware layer cho logging, rate limiting, transformation
- File Uploads: Binary content support cho resources
- Triggers & Events: Server-initiated notifications cho real-time updates
- Agent-to-Agent: MCP as communication protocol giữa multiple AI agents
- Cài SDK:
pip install mcphoặcnpm i @modelcontextprotocol/sdk - Build server đầu tiên (filesystem hoặc weather example ở trên)
- Test với
npx @modelcontextprotocol/inspector - Connect vào Claude Desktop qua config JSON
- Publish server lên GitHub, contribute về community
- modelcontextprotocol.io — Official docs, spec, tutorials
- GitHub Organization — Spec, SDKs, reference servers, inspector
- SDK Documentation — Python, TypeScript, Kotlin, Swift, Rust
- Example Servers — Filesystem, GitHub, PostgreSQL, Slack, Brave Search...
- MCP Inspector Guide — Debug, test, visualize MCP traffic
MCP đang trở thành tiêu chuẩn de facto cho AI-tool integration. Sớm adopt = sớm tận hưởng sức mạnh của write once, connect everywhere. Hãy build server đầu tiên của bạn hôm nay! 🚀