This is the abridged developer documentation for MCP Doc
# Model Context Protocol
> Первая русскоязычная документация по созданию MCP серверов на Python, TypeScript, Java, Kotlin, C#, Go, PHP, Ruby, Rust и Swift
## Быстрая навигация [Заголовок раздела «Быстрая навигация»](#быстрая-навигация) ### [Быстрый старт](/guides/getting-started/) [Создайте свой первый MCP сервер за 5 минут](/guides/getting-started/) ### [SDK](#sdk-по-языкам-программирования) [Выберите SDK для вашего языка программирования](#sdk-по-языкам-программирования) ### [Серверы](/servers/overview/) [Готовые MCP серверы для интеграции](/servers/overview/) ### [Развёртывание](#развёртывание) [Docker, Linux, Windows и удалённые транспорты](#развёртывание) ### [Безопасность](/security/best-practices/) [Лучшие практики защиты MCP серверов](/security/best-practices/) ### [Справочник](/reference/protocol/) [Протокол, клиенты и спецификация](/reference/protocol/) ## Что такое MCP? [Заголовок раздела «Что такое MCP?»](#что-такое-mcp) **Model Context Protocol (MCP)** — это открытый стандарт от Anthropic, который позволяет AI-ассистентам (Claude, ChatGPT, Copilot) безопасно подключаться к внешним источникам данных и инструментам. MCP решает фундаментальную проблему: как дать языковым моделям доступ к актуальной информации и возможность выполнять действия во внешних системах. Инструменты (Tools) Функции, которые AI может вызывать: запросы к API, работа с файлами, выполнение команд Ресурсы (Resources) Данные, которые AI может читать: файлы, базы данных, документация Промпты (Prompts) Шаблоны для структурированных запросов и инструкций Безопасность Изоляция, контроль доступа, аудит всех операций ## Популярные MCP серверы [Заголовок раздела «Популярные MCP серверы»](#популярные-mcp-серверы) ### [Context7](/servers/context7/) [Хит](/servers/context7/) [Актуальная документация библиотек в реальном времени. Больше никаких устаревших знаний!](/servers/context7/) ### [Filesystem](/servers/filesystem/) [Безопасное чтение и запись файлов с поддержкой разрешённых директорий](/servers/filesystem/) ### [Git](/servers/git/) [Работа с Git-репозиториями: коммиты, история, диффы, ветки](/servers/git/) ### [Memory](/servers/memory/) [Персистентная память для AI через граф знаний](/servers/memory/) [Смотреть все серверы →](/servers/overview/) ## SDK по языкам программирования [Заголовок раздела «SDK по языкам программирования»](#sdk-по-языкам-программирования) ### [Python SDK](/sdk/python/) [Рекомендуется](/sdk/python/) [FastMCP — самый простой способ создать MCP сервер. Декораторы, type hints, async.](/sdk/python/) ### [TypeScript SDK](/sdk/typescript/) [Официальный SDK для Node.js с поддержкой SSE и StreamableHTTP транспортов.](/sdk/typescript/) ### [Java SDK](/sdk/java/) [Reactive Streams API, Spring Boot интеграция, Jakarta Servlet транспорт.](/sdk/java/) ### [Kotlin SDK](/sdk/kotlin/) [Multiplatform SDK для JVM, WebAssembly и Native (iOS). Ktor интеграция.](/sdk/kotlin/) ### [C# SDK](/sdk/csharp/) [Атрибуты \[McpServerTool\] и полная интеграция с .NET DI.](/sdk/csharp/) ### [Go SDK](/sdk/go/) [Рекомендуется](/sdk/go/) [mark3labs/mcp-go — production-ready SDK с SSE поддержкой.](/sdk/go/) ### [PHP SDK](/sdk/php/) [Рекомендуется](/sdk/php/) [php-mcp/server — SDK с PHP атрибутами и PSR-11 dependency injection.](/sdk/php/) ### [Ruby SDK](/sdk/ruby/) [Rails и Rack интеграция. Streamable HTTP и stdio транспорты.](/sdk/ruby/) ### [Rust SDK](/sdk/rust/) [RMCP — proc-макросы #\[tool\], #\[resource\] для минимального бойлерплейта.](/sdk/rust/) ### [Swift SDK](/sdk/swift/) [Apple платформы и Linux. Service Lifecycle для graceful shutdown.](/sdk/swift/) ## Развёртывание [Заголовок раздела «Развёртывание»](#развёртывание) ### [Docker](/deployment/docker/) [Контейнеризация MCP серверов с volume mounting и network configuration.](/deployment/docker/) ### [Windows](/deployment/windows/) [Специфика установки на Windows: полные пути, node.exe, двойные слэши.](/deployment/windows/) ### [Ubuntu/Linux](/deployment/ubuntu/) [Развёртывание через uvx, local repository, Docker и systemd.](/deployment/ubuntu/) ### [NPM Registry](/deployment/npm/) [Публикация серверов в официальный MCP Registry через NPM.](/deployment/npm/) ### [HTTP/SSE](/deployment/http-sse/) [Удалённый транспорт для веб-интеграций и масштабируемых развёртываний.](/deployment/http-sse/) ## Архитектура MCP [Заголовок раздела «Архитектура MCP»](#архитектура-mcp) **Клиент** (Claude Desktop, Cursor, VS Code) подключается к **серверу** через транспорт (stdio или HTTP/SSE). Сервер предоставляет инструменты, ресурсы и промпты, которые клиент может использовать. ## Быстрый старт [Заголовок раздела «Быстрый старт»](#быстрый-старт) * Python server.py ```python from mcp.server.fastmcp import FastMCP mcp = FastMCP("My Server") @mcp.tool() def add(a: int, b: int) -> int: """Сложение двух чисел""" return a + b @mcp.resource("config://app") def get_config() -> str: """Конфигурация приложения""" return "debug=true" ``` * TypeScript server.ts ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new McpServer({ name: "my-server", version: "1.0.0", }); server.tool("add", { a: "number", b: "number" }, async ({ a, b }) => ({ content: [{ type: "text", text: String(a + b) }], })); const transport = new StdioServerTransport(); await server.connect(transport); ``` ## Официальные ресурсы [Заголовок раздела «Официальные ресурсы»](#официальные-ресурсы) * [modelcontextprotocol.io](https://modelcontextprotocol.io) — официальная документация Anthropic * [GitHub: modelcontextprotocol](https://github.com/modelcontextprotocol) — все официальные SDK * [MCP Registry](https://registry.modelcontextprotocol.io) — реестр готовых серверов
# Docker развёртывание
> Полное руководство по контейнеризации MCP серверов с Docker
Docker обеспечивает изоляцию, воспроизводимость и простоту развёртывания MCP серверов в любой среде. ## Базовый Dockerfile [Заголовок раздела «Базовый Dockerfile»](#базовый-dockerfile) ### Python (Multi-stage) [Заголовок раздела «Python (Multi-stage)»](#python-multi-stage) Dockerfile ```dockerfile # Build stage для зависимостей FROM python:3.12-slim AS builder WORKDIR /app # Установка build-зависимостей RUN apt-get update && apt-get install -y --no-install-recommends \ gcc libpq-dev && rm -rf /var/lib/apt/lists/* # Виртуальное окружение RUN python -m venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Production stage FROM python:3.12-slim # Non-root пользователь RUN useradd --create-home --shell /bin/bash mcp WORKDIR /home/mcp/app # Копирование venv из builder COPY --from=builder /opt/venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" # Копирование кода COPY --chown=mcp:mcp src/ ./src/ USER mcp CMD ["python", "-m", "src.server"] ``` ### Python (простой вариант) [Заголовок раздела «Python (простой вариант)»](#python-простой-вариант) Dockerfile ```dockerfile FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY src/ ./src/ CMD ["python", "-m", "src.server"] ``` ### TypeScript [Заголовок раздела «TypeScript»](#typescript) Dockerfile ```dockerfile # Multi-stage build FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage FROM node:20-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY package*.json ./ CMD ["node", "dist/index.js"] ``` ### Rust [Заголовок раздела «Rust»](#rust) Dockerfile ```dockerfile FROM rust:1.75 AS builder WORKDIR /app COPY . . RUN cargo build --release FROM debian:bookworm-slim RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* COPY --from=builder /app/target/release/mcp-server /usr/local/bin/ CMD ["mcp-server"] ``` ### Go [Заголовок раздела «Go»](#go) Dockerfile ```dockerfile FROM golang:1.21-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -o mcp-server ./cmd/server FROM alpine:latest RUN apk --no-cache add ca-certificates COPY --from=builder /app/mcp-server /usr/local/bin/ CMD ["mcp-server"] ``` ## docker-compose.yml [Заголовок раздела «docker-compose.yml»](#docker-composeyml) Для комплексных сервисов с зависимостями: docker-compose.yml ```yaml version: '3.8' services: mcp-server: build: . container_name: mcp-server restart: unless-stopped volumes: - ./data:/app/data:ro - ./config:/app/config:ro environment: - DATABASE_URL=postgresql://postgres:password@db:5432/mcp - LOG_LEVEL=info depends_on: - db networks: - mcp-network db: image: postgres:16-alpine container_name: mcp-db restart: unless-stopped volumes: - postgres_data:/var/lib/postgresql/data environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=password - POSTGRES_DB=mcp networks: - mcp-network volumes: postgres_data: networks: mcp-network: driver: bridge ``` ## Volume Mounting [Заголовок раздела «Volume Mounting»](#volume-mounting) Для доступа к данным на хосте: docker-compose.yml ```yaml services: mcp-server: volumes: # Только чтение для безопасности - /home/user/documents:/app/documents:ro # Запись для кэша - mcp-cache:/app/cache # Конфигурация - ./config.json:/app/config.json:ro ``` ## Environment Variables [Заголовок раздела «Environment Variables»](#environment-variables) Dockerfile ```dockerfile ENV MCP_SERVER_NAME=my-server ENV MCP_LOG_LEVEL=info ENV MCP_MAX_CONNECTIONS=100 ``` docker-compose.yml ```yaml services: mcp-server: environment: - MCP_SERVER_NAME=${SERVER_NAME:-default} - MCP_LOG_LEVEL=${LOG_LEVEL:-info} - DATABASE_URL env_file: - .env ``` .env ```bash SERVER_NAME=production-server LOG_LEVEL=warn DATABASE_URL=postgresql://user:pass@host:5432/db ``` ## Networking [Заголовок раздела «Networking»](#networking) ### HTTP/SSE транспорт [Заголовок раздела «HTTP/SSE транспорт»](#httpsse-транспорт) docker-compose.yml ```yaml services: mcp-server: ports: - "3000:3000" environment: - MCP_TRANSPORT=http - MCP_PORT=3000 ``` ### Internal network [Заголовок раздела «Internal network»](#internal-network) docker-compose.yml ```yaml services: mcp-server: networks: - internal app: networks: - internal environment: - MCP_SERVER_URL=http://mcp-server:3000 networks: internal: internal: true ``` ## Health Checks [Заголовок раздела «Health Checks»](#health-checks) Dockerfile ```dockerfile HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1 ``` docker-compose.yml ```yaml services: mcp-server: healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 start_period: 5s ``` ## Security Best Practices [Заголовок раздела «Security Best Practices»](#security-best-practices) ### Non-root user [Заголовок раздела «Non-root user»](#non-root-user) Dockerfile ```dockerfile FROM python:3.12-slim # Создание non-root пользователя RUN useradd --create-home --shell /bin/bash mcp USER mcp WORKDIR /home/mcp/app COPY --chown=mcp:mcp . . CMD ["python", "-m", "src.server"] ``` ### Read-only filesystem [Заголовок раздела «Read-only filesystem»](#read-only-filesystem) docker-compose.yml ```yaml services: mcp-server: read_only: true tmpfs: - /tmp volumes: - ./data:/app/data:ro ``` ### Capabilities [Заголовок раздела «Capabilities»](#capabilities) docker-compose.yml ```yaml services: mcp-server: cap_drop: - ALL security_opt: - no-new-privileges:true ``` ## Logging [Заголовок раздела «Logging»](#logging) docker-compose.yml ```yaml services: mcp-server: logging: driver: json-file options: max-size: "10m" max-file: "3" ``` ### Централизованное логирование [Заголовок раздела «Централизованное логирование»](#централизованное-логирование) docker-compose.yml ```yaml services: mcp-server: logging: driver: syslog options: syslog-address: "tcp://logs.example.com:514" tag: "mcp-server" ``` ## Production Deployment [Заголовок раздела «Production Deployment»](#production-deployment) ### Kubernetes-ready [Заголовок раздела «Kubernetes-ready»](#kubernetes-ready) Dockerfile ```dockerfile FROM python:3.12-slim # Labels для Kubernetes LABEL org.opencontainers.image.source="https://github.com/user/mcp-server" LABEL org.opencontainers.image.version="1.0.0" WORKDIR /app # Сигнал для graceful shutdown STOPSIGNAL SIGTERM COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY src/ ./src/ # Health check endpoint EXPOSE 8080 USER 1000 CMD ["python", "-m", "src.server"] ``` ### Kubernetes deployment [Заголовок раздела «Kubernetes deployment»](#kubernetes-deployment) deployment.yaml ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: mcp-server spec: replicas: 3 selector: matchLabels: app: mcp-server template: metadata: labels: app: mcp-server spec: containers: - name: mcp-server image: registry.example.com/mcp-server:1.0.0 ports: - containerPort: 3000 resources: limits: memory: "256Mi" cpu: "500m" requests: memory: "128Mi" cpu: "250m" livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 3000 initialDelaySeconds: 5 periodSeconds: 10 ``` ## Claude Desktop с Docker [Заголовок раздела «Claude Desktop с Docker»](#claude-desktop-с-docker) ### Настройка через wrapper script [Заголовок раздела «Настройка через wrapper script»](#настройка-через-wrapper-script) Создайте `run-mcp-docker.sh`: run-mcp-docker.sh ```bash #!/bin/bash docker run --rm -i \ -v /home/user/data:/app/data:ro \ -e LOG_LEVEL=info \ my-mcp-server:latest ``` ### Claude Desktop config [Заголовок раздела «Claude Desktop config»](#claude-desktop-config) claude\_desktop\_config.json ```json { "mcpServers": { "docker-server": { "command": "/path/to/run-mcp-docker.sh" } } } ``` ### Альтернативный метод [Заголовок раздела «Альтернативный метод»](#альтернативный-метод) claude\_desktop\_config.json ```json { "mcpServers": { "docker-server": { "command": "docker", "args": [ "run", "--rm", "-i", "-v", "/home/user/data:/app/data:ro", "my-mcp-server:latest" ] } } } ``` ## Сборка и публикация [Заголовок раздела «Сборка и публикация»](#сборка-и-публикация) Terminal ```bash # Сборка docker build -t my-mcp-server:1.0.0 . # Тегирование для registry docker tag my-mcp-server:1.0.0 registry.example.com/my-mcp-server:1.0.0 # Push docker push registry.example.com/my-mcp-server:1.0.0 ``` ## Multi-platform builds [Заголовок раздела «Multi-platform builds»](#multi-platform-builds) Terminal ```bash # Сборка для нескольких архитектур docker buildx build \ --platform linux/amd64,linux/arm64 \ -t my-mcp-server:1.0.0 \ --push . ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [Docker Best Practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) * [Azure MCP Container Sample](https://github.com/Azure-Samples/mcp-container-ts) * [Docker Compose Documentation](https://docs.docker.com/compose/)
# HTTP/SSE транспорт
> Развёртывание MCP серверов с HTTP и Server-Sent Events для веб-приложений
HTTP/SSE транспорт позволяет развёртывать MCP серверы как веб-сервисы, доступные через HTTP API. ## Архитектура [Заголовок раздела «Архитектура»](#архитектура) **Два канала связи:** * **HTTP POST** — запросы от клиента к серверу (JSON-RPC) * **SSE** — события от сервера к клиенту (Server-Sent Events) ## TypeScript реализация [Заголовок раздела «TypeScript реализация»](#typescript-реализация) ### Базовый сервер [Заголовок раздела «Базовый сервер»](#базовый-сервер) server.ts ```typescript import express from "express"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; const app = express(); app.use(express.json()); const server = new McpServer({ name: "http-server", version: "1.0.0", }); // Регистрация инструментов server.tool("hello", {}, async () => ({ content: [{ type: "text", text: "Hello from HTTP!" }], })); // Хранение сессий const sessions = new Map(); // SSE endpoint app.get("/sse", async (req, res) => { const sessionId = req.query.sessionId as string || crypto.randomUUID(); // Настройка SSE res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); res.setHeader("Access-Control-Allow-Origin", "*"); const transport = new StreamableHTTPServerTransport("/message", res); sessions.set(sessionId, transport); // Подключение сервера await server.connect(transport); // Отправка session ID клиенту res.write(`data: ${JSON.stringify({ sessionId })}\n\n`); // Cleanup при отключении req.on("close", () => { sessions.delete(sessionId); }); }); // POST endpoint для запросов app.post("/message", async (req, res) => { const sessionId = req.query.sessionId as string; const transport = sessions.get(sessionId); if (!transport) { res.status(404).json({ error: "Session not found" }); return; } try { await transport.handleRequest(req.body, res); } catch (error) { res.status(500).json({ error: "Internal server error" }); } }); // Health check app.get("/health", (req, res) => { res.json({ status: "ok", sessions: sessions.size }); }); app.listen(3000, () => { console.log("MCP HTTP server on http://localhost:3000"); }); ``` ### Клиент [Заголовок раздела «Клиент»](#клиент) client.ts ```typescript import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; const transport = new StreamableHTTPClientTransport( "http://localhost:3000/sse", "http://localhost:3000/message" ); const client = new Client({ name: "http-client", version: "1.0.0", }); await client.connect(transport); // Вызов инструмента const result = await client.callTool("hello", {}); console.log(result); ``` ## Python реализация [Заголовок раздела «Python реализация»](#python-реализация) ### FastAPI сервер [Заголовок раздела «FastAPI сервер»](#fastapi-сервер) server.py ```python from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse from sse_starlette.sse import EventSourceResponse import asyncio import uuid import json from mcp.server.fastmcp import FastMCP from mcp.server.sse import SseServerTransport app = FastAPI() mcp = FastMCP("http-server") # Хранение сессий sessions: dict[str, SseServerTransport] = {} @mcp.tool() def hello() -> str: """Приветствие""" return "Hello from HTTP!" @app.get("/sse") async def sse_endpoint(request: Request): session_id = str(uuid.uuid4()) async def event_generator(): transport = SseServerTransport() sessions[session_id] = transport # Отправляем session ID yield { "event": "session", "data": json.dumps({"sessionId": session_id}) } try: async for message in transport.receive(): yield { "event": "message", "data": json.dumps(message) } finally: del sessions[session_id] return EventSourceResponse(event_generator()) @app.post("/message") async def message_endpoint(request: Request, sessionId: str): transport = sessions.get(sessionId) if not transport: return {"error": "Session not found"}, 404 body = await request.json() response = await transport.handle_request(body) return response @app.get("/health") async def health(): return {"status": "ok", "sessions": len(sessions)} ``` ### Запуск [Заголовок раздела «Запуск»](#запуск) Terminal ```bash uvicorn server:app --host 0.0.0.0 --port 3000 ``` ## Nginx конфигурация [Заголовок раздела «Nginx конфигурация»](#nginx-конфигурация) /etc/nginx/sites-available/mcp-server ```nginx upstream mcp_server { server 127.0.0.1:3000; keepalive 32; } server { listen 443 ssl http2; server_name mcp.example.com; ssl_certificate /etc/letsencrypt/live/mcp.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mcp.example.com/privkey.pem; # SSE endpoint location /sse { proxy_pass http://mcp_server; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # SSE настройки proxy_buffering off; proxy_cache off; proxy_read_timeout 86400s; proxy_send_timeout 86400s; # Chunked transfer chunked_transfer_encoding on; } # Message endpoint location /message { proxy_pass http://mcp_server; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header Content-Type application/json; } # Health check location /health { proxy_pass http://mcp_server; } } ``` ## CORS настройка [Заголовок раздела «CORS настройка»](#cors-настройка) server.ts ```typescript import cors from "cors"; app.use(cors({ origin: ["https://allowed-origin.com"], methods: ["GET", "POST"], allowedHeaders: ["Content-Type", "Authorization"], credentials: true, })); ``` ## Аутентификация [Заголовок раздела «Аутентификация»](#аутентификация) ### Bearer token [Заголовок раздела «Bearer token»](#bearer-token) middleware/auth.ts ```typescript const authenticate = (req: Request, res: Response, next: NextFunction) => { const authHeader = req.headers.authorization; if (!authHeader?.startsWith("Bearer ")) { res.status(401).json({ error: "Unauthorized" }); return; } const token = authHeader.substring(7); try { const payload = verifyToken(token); req.user = payload; next(); } catch { res.status(401).json({ error: "Invalid token" }); } }; app.use("/sse", authenticate); app.use("/message", authenticate); ``` ### API Key [Заголовок раздела «API Key»](#api-key) middleware/apiKey.ts ```typescript const apiKeyAuth = (req: Request, res: Response, next: NextFunction) => { const apiKey = req.headers["x-api-key"]; if (!apiKey || !isValidApiKey(apiKey as string)) { res.status(401).json({ error: "Invalid API key" }); return; } next(); }; ``` ## Rate Limiting [Заголовок раздела «Rate Limiting»](#rate-limiting) middleware/rateLimit.ts ```typescript import rateLimit from "express-rate-limit"; const limiter = rateLimit({ windowMs: 60 * 1000, // 1 минута max: 100, // 100 запросов message: { error: "Too many requests" }, }); app.use("/message", limiter); ``` ## Мониторинг [Заголовок раздела «Мониторинг»](#мониторинг) ### Prometheus metrics [Заголовок раздела «Prometheus metrics»](#prometheus-metrics) metrics.ts ```typescript import { Counter, Histogram, Gauge, register } from "prom-client"; const requestCounter = new Counter({ name: "mcp_requests_total", help: "Total MCP requests", labelNames: ["method", "status"], }); const requestDuration = new Histogram({ name: "mcp_request_duration_seconds", help: "MCP request duration", labelNames: ["method"], }); const activeSessions = new Gauge({ name: "mcp_active_sessions", help: "Number of active sessions", }); // Middleware app.use((req, res, next) => { const start = Date.now(); res.on("finish", () => { const duration = (Date.now() - start) / 1000; requestCounter.inc({ method: req.method, status: res.statusCode }); requestDuration.observe({ method: req.method }, duration); }); next(); }); // Metrics endpoint app.get("/metrics", async (req, res) => { activeSessions.set(sessions.size); res.set("Content-Type", register.contentType); res.end(await register.metrics()); }); ``` ## Load Balancing [Заголовок раздела «Load Balancing»](#load-balancing) ### С sticky sessions [Заголовок раздела «С sticky sessions»](#с-sticky-sessions) /etc/nginx/nginx.conf ```nginx upstream mcp_servers { ip_hash; # Sticky sessions по IP server 127.0.0.1:3001; server 127.0.0.1:3002; server 127.0.0.1:3003; } ``` ### Redis для сессий [Заголовок раздела «Redis для сессий»](#redis-для-сессий) redis.ts ```typescript import Redis from "ioredis"; const redis = new Redis(); // Сохранение сессии await redis.set(`session:${sessionId}`, JSON.stringify(sessionData), "EX", 3600); // Получение сессии const data = await redis.get(`session:${sessionId}`); ``` ## Docker Compose [Заголовок раздела «Docker Compose»](#docker-compose) docker-compose.yml ```yaml version: '3.8' services: mcp-http: build: . ports: - "3000:3000" environment: - NODE_ENV=production - REDIS_URL=redis://redis:6379 depends_on: - redis deploy: replicas: 3 redis: image: redis:7-alpine volumes: - redis_data:/data nginx: image: nginx:alpine ports: - "443:443" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf - /etc/letsencrypt:/etc/letsencrypt:ro depends_on: - mcp-http volumes: redis_data: ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [TypeScript SDK - HTTP Transport](https://github.com/modelcontextprotocol/typescript-sdk) * [SSE Specification](https://html.spec.whatwg.org/multipage/server-sent-events.html) * [JSON-RPC 2.0](https://www.jsonrpc.org/specification)
# NPM Registry и публикация
> Руководство по публикации MCP серверов в NPM Registry и официальный MCP Registry
Публикация MCP серверов в NPM Registry позволяет легко распространять и устанавливать серверы через npm/npx. ## Подготовка пакета [Заголовок раздела «Подготовка пакета»](#подготовка-пакета) ### package.json [Заголовок раздела «package.json»](#packagejson) package.json ```json { "name": "@myorg/mcp-server-example", "version": "1.0.0", "description": "MCP сервер для примера", "type": "module", "main": "dist/index.js", "bin": { "mcp-server-example": "./dist/index.js" }, "files": [ "dist", "README.md" ], "scripts": { "build": "tsc", "prepare": "npm run build" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0" }, "devDependencies": { "typescript": "^5.0.0", "@types/node": "^20.0.0" }, "keywords": [ "mcp", "model-context-protocol", "ai", "claude" ], "license": "MIT", "repository": { "type": "git", "url": "https://github.com/myorg/mcp-server-example.git" }, "mcpName": "example" } ``` ### Важные поля [Заголовок раздела «Важные поля»](#важные-поля) | Поле | Описание | | --------- | ----------------------------------------------------- | | `name` | Уникальное имя пакета (рекомендуется с scope `@org/`) | | `bin` | Путь к исполняемому файлу | | `files` | Файлы для включения в пакет | | `mcpName` | Имя для MCP Registry (опционально) | ## Структура проекта [Заголовок раздела «Структура проекта»](#структура-проекта) ```plaintext mcp-server-example/ ├── src/ │ ├── index.ts │ └── tools/ ├── dist/ # Скомпилированный код ├── package.json ├── tsconfig.json ├── README.md └── LICENSE ``` ### src/index.ts [Заголовок раздела «src/index.ts»](#srcindexts) src/index.ts ```typescript #!/usr/bin/env node import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new McpServer({ name: "example", version: "1.0.0", }); // Регистрация инструментов server.tool("hello", {}, async () => ({ content: [{ type: "text", text: "Hello from MCP!" }], })); // Запуск const transport = new StdioServerTransport(); await server.connect(transport); ``` ## Публикация в NPM [Заголовок раздела «Публикация в NPM»](#публикация-в-npm) ### Регистрация на npmjs.com [Заголовок раздела «Регистрация на npmjs.com»](#регистрация-на-npmjscom) Terminal ```bash npm adduser ``` ### Проверка перед публикацией [Заголовок раздела «Проверка перед публикацией»](#проверка-перед-публикацией) Terminal ```bash # Что будет опубликовано npm pack --dry-run # Линтинг npm run lint # Тесты npm test # Сборка npm run build ``` ### Публикация [Заголовок раздела «Публикация»](#публикация) Terminal ```bash # Первая публикация npm publish --access public # Обновление версии npm version patch # 1.0.0 -> 1.0.1 npm version minor # 1.0.0 -> 1.1.0 npm version major # 1.0.0 -> 2.0.0 # Публикация обновления npm publish ``` ### Scoped packages [Заголовок раздела «Scoped packages»](#scoped-packages) Terminal ```bash # Публичный scoped пакет npm publish --access public # Приватный (требует платную подписку) npm publish ``` ## MCP Registry [Заголовок раздела «MCP Registry»](#mcp-registry) MCP Registry — официальный реестр MCP серверов, который агрегирует метаданные из NPM, PyPI и других источников. ### Добавление в MCP Registry [Заголовок раздела «Добавление в MCP Registry»](#добавление-в-mcp-registry) 1. Убедитесь, что `mcpName` указан в package.json: package.json ```json { "mcpName": "example" } ``` 2. Пакет должен быть опубликован в NPM 3. Регистрация в MCP Registry: Terminal ```bash # Установка CLI npm install -g @modelcontextprotocol/registry-cli # Аутентификация mcp-registry login # Публикация mcp-registry publish ``` ### server.json [Заголовок раздела «server.json»](#serverjson) Альтернативный способ — создать `server.json`: server.json ```json { "name": "example", "description": "Пример MCP сервера", "version": "1.0.0", "homepage": "https://github.com/myorg/mcp-server-example", "license": "MIT", "author": { "name": "My Organization" }, "categories": ["tools", "utilities"], "runtime": { "type": "npm", "package": "@myorg/mcp-server-example" } } ``` ## Использование опубликованного сервера [Заголовок раздела «Использование опубликованного сервера»](#использование-опубликованного-сервера) ### Через npx [Заголовок раздела «Через npx»](#через-npx) Terminal ```bash npx -y @myorg/mcp-server-example ``` ### Claude Desktop конфигурация [Заголовок раздела «Claude Desktop конфигурация»](#claude-desktop-конфигурация) claude\_desktop\_config.json ```json { "mcpServers": { "example": { "command": "npx", "args": ["-y", "@myorg/mcp-server-example"] } } } ``` ### Глобальная установка [Заголовок раздела «Глобальная установка»](#глобальная-установка) Terminal ```bash npm install -g @myorg/mcp-server-example ``` claude\_desktop\_config.json ```json { "mcpServers": { "example": { "command": "mcp-server-example" } } } ``` ## Private NPM Registry [Заголовок раздела «Private NPM Registry»](#private-npm-registry) ### Конфигурация .npmrc [Заголовок раздела «Конфигурация .npmrc»](#конфигурация-npmrc) .npmrc ```ini # .npmrc @myorg:registry=https://npm.mycompany.com //npm.mycompany.com/:_authToken=${NPM_TOKEN} ``` ### Публикация в приватный registry [Заголовок раздела «Публикация в приватный registry»](#публикация-в-приватный-registry) Terminal ```bash npm publish --registry https://npm.mycompany.com ``` ### Использование [Заголовок раздела «Использование»](#использование) claude\_desktop\_config.json ```json { "mcpServers": { "private-server": { "command": "npx", "args": [ "--registry", "https://npm.mycompany.com", "-y", "@myorg/private-mcp-server" ] } } } ``` ## Версионирование [Заголовок раздела «Версионирование»](#версионирование) ### Semantic Versioning [Заголовок раздела «Semantic Versioning»](#semantic-versioning) * **MAJOR** (1.0.0 → 2.0.0): Несовместимые изменения API * **MINOR** (1.0.0 → 1.1.0): Новые функции, обратная совместимость * **PATCH** (1.0.0 → 1.0.1): Исправления багов ### Pre-release версии [Заголовок раздела «Pre-release версии»](#pre-release-версии) Terminal ```bash npm version prerelease --preid=beta # 1.0.0 -> 1.0.1-beta.0 npm publish --tag beta ``` ### Использование beta версий [Заголовок раздела «Использование beta версий»](#использование-beta-версий) Terminal ```bash npx -y @myorg/mcp-server-example@beta ``` ## CI/CD публикация [Заголовок раздела «CI/CD публикация»](#cicd-публикация) ### GitHub Actions [Заголовок раздела «GitHub Actions»](#github-actions) .github/workflows/publish.yml ```yaml # .github/workflows/publish.yml name: Publish to NPM on: release: types: [published] jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' registry-url: 'https://registry.npmjs.org' - run: npm ci - run: npm run build - run: npm test - run: npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} ``` ### Получение NPM\_TOKEN [Заголовок раздела «Получение NPM\_TOKEN»](#получение-npm_token) 1. Зайдите на [npmjs.com](https://www.npmjs.com/) 2. Settings → Access Tokens → Generate New Token 3. Выберите “Automation” тип 4. Добавьте в GitHub Secrets ## README для NPM [Заголовок раздела «README для NPM»](#readme-для-npm) ```markdown # @myorg/mcp-server-example MCP сервер для примера. ## Установка \`\`\`bash npx -y @myorg/mcp-server-example \`\`\` ## Конфигурация Claude Desktop \`\`\`json { "mcpServers": { "example": { "command": "npx", "args": ["-y", "@myorg/mcp-server-example"] } } } \`\`\` ## Инструменты - `hello` - Приветственное сообщение - `add` - Сложение чисел ## Лицензия MIT ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [NPM Documentation](https://docs.npmjs.com/) * [MCP Registry](https://registry.modelcontextprotocol.io) * [MCP Registry Publishing](https://modelcontextprotocol.info/tools/registry/publishing/) * [@modelcontextprotocol/sdk](https://www.npmjs.com/package/@modelcontextprotocol/sdk)
# Развёртывание на Ubuntu/Linux
> Руководство по установке и настройке MCP серверов на Ubuntu и других Linux дистрибутивах
Linux — рекомендуемая платформа для production развёртывания MCP серверов благодаря стабильности, безопасности и удобству автоматизации. ## Подготовка системы [Заголовок раздела «Подготовка системы»](#подготовка-системы) ### Обновление пакетов [Заголовок раздела «Обновление пакетов»](#обновление-пакетов) Terminal ```bash sudo apt update && sudo apt upgrade -y ``` ### Установка зависимостей [Заголовок раздела «Установка зависимостей»](#установка-зависимостей) Terminal ```bash # Базовые инструменты sudo apt install -y curl git build-essential # Node.js (через NodeSource) curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt install -y nodejs # Python 3.12 sudo add-apt-repository -y ppa:deadsnakes/ppa sudo apt install -y python3.12 python3.12-venv python3-pip # uv (менеджер пакетов Python) curl -LsSf https://astral.sh/uv/install.sh | sh ``` ## Установка MCP серверов [Заголовок раздела «Установка MCP серверов»](#установка-mcp-серверов) ### Через uvx (рекомендуется для Python) [Заголовок раздела «Через uvx (рекомендуется для Python)»](#через-uvx-рекомендуется-для-python) Terminal ```bash # Одноразовый запуск uvx mcp-server-fetch # Или установка для постоянного использования uv pip install mcp-server-fetch ``` ### Через npm (для Node.js) [Заголовок раздела «Через npm (для Node.js)»](#через-npm-для-nodejs) Terminal ```bash # Глобальная установка npm install -g @modelcontextprotocol/server-filesystem # Или через npx npx -y @modelcontextprotocol/server-filesystem ``` ### Из исходников [Заголовок раздела «Из исходников»](#из-исходников) Terminal ```bash # Клонирование git clone https://github.com/user/mcp-server.git cd mcp-server # Python python3.12 -m venv .venv source .venv/bin/activate pip install -r requirements.txt # Node.js npm install npm run build ``` ## Systemd сервис [Заголовок раздела «Systemd сервис»](#systemd-сервис) ### Создание unit файла [Заголовок раздела «Создание unit файла»](#создание-unit-файла) Terminal ```bash sudo nano /etc/systemd/system/mcp-server.service ``` /etc/systemd/system/mcp-server.service ```ini [Unit] Description=MCP Server After=network.target [Service] Type=simple User=mcp Group=mcp WorkingDirectory=/opt/mcp-server Environment=PATH=/opt/mcp-server/.venv/bin:/usr/local/bin:/usr/bin ExecStart=/opt/mcp-server/.venv/bin/python -m src.server Restart=always RestartSec=5 # Безопасность NoNewPrivileges=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/opt/mcp-server/data PrivateTmp=yes [Install] WantedBy=multi-user.target ``` ### Управление сервисом [Заголовок раздела «Управление сервисом»](#управление-сервисом) Terminal ```bash # Перезагрузка systemd sudo systemctl daemon-reload # Запуск sudo systemctl start mcp-server # Автозапуск при загрузке sudo systemctl enable mcp-server # Статус sudo systemctl status mcp-server # Логи journalctl -u mcp-server -f ``` ## Создание пользователя [Заголовок раздела «Создание пользователя»](#создание-пользователя) Terminal ```bash # Создание системного пользователя sudo useradd --system --home-dir /opt/mcp-server --shell /bin/false mcp # Права на директорию sudo mkdir -p /opt/mcp-server sudo chown -R mcp:mcp /opt/mcp-server ``` ## Конфигурация Claude Desktop [Заголовок раздела «Конфигурация Claude Desktop»](#конфигурация-claude-desktop) Файл конфигурации: ```plaintext ~/.config/claude/claude_desktop_config.json ``` ### Примеры конфигурации [Заголовок раздела «Примеры конфигурации»](#примеры-конфигурации) #### Python сервер [Заголовок раздела «Python сервер»](#python-сервер) claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "/opt/mcp-server/.venv/bin/python", "args": ["-m", "src.server"], "cwd": "/opt/mcp-server", "env": { "DATABASE_URL": "postgresql://localhost/mydb" } } } } ``` #### Node.js сервер [Заголовок раздела «Node.js сервер»](#nodejs-сервер) claude\_desktop\_config.json ```json { "mcpServers": { "node-server": { "command": "node", "args": ["/opt/mcp-server/dist/index.js"] } } } ``` #### С uvx [Заголовок раздела «С uvx»](#с-uvx) claude\_desktop\_config.json ```json { "mcpServers": { "uvx-server": { "command": "uvx", "args": ["mcp-server-fetch"] } } } ``` ## Firewall (ufw) [Заголовок раздела «Firewall (ufw)»](#firewall-ufw) Terminal ```bash # Для HTTP/SSE транспорта sudo ufw allow 3000/tcp comment 'MCP Server' # Проверка sudo ufw status ``` ## Nginx reverse proxy [Заголовок раздела «Nginx reverse proxy»](#nginx-reverse-proxy) Для HTTP/SSE транспорта: /etc/nginx/sites-available/mcp-server ```nginx server { listen 443 ssl http2; server_name mcp.example.com; ssl_certificate /etc/letsencrypt/live/mcp.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mcp.example.com/privkey.pem; location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; # SSE настройки proxy_buffering off; proxy_read_timeout 86400s; proxy_send_timeout 86400s; } # Health check location /health { proxy_pass http://127.0.0.1:3000/health; } } ``` Terminal ```bash # Активация sudo ln -s /etc/nginx/sites-available/mcp-server /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ## Мониторинг [Заголовок раздела «Мониторинг»](#мониторинг) ### Prometheus metrics [Заголовок раздела «Prometheus metrics»](#prometheus-metrics) Terminal ```bash # Установка node_exporter sudo apt install prometheus-node-exporter ``` ### Логирование [Заголовок раздела «Логирование»](#логирование) Terminal ```bash # Настройка rsyslog sudo nano /etc/rsyslog.d/mcp-server.conf ``` ```title="/etc/rsyslog.d/mcp-server.conf" if $programname == 'mcp-server' then /var/log/mcp-server.log & stop ``` ### Logrotate [Заголовок раздела «Logrotate»](#logrotate) Terminal ```bash sudo nano /etc/logrotate.d/mcp-server ``` ```title="/etc/logrotate.d/mcp-server" /var/log/mcp-server.log { daily rotate 7 compress delaycompress missingok notifempty create 640 mcp mcp postrotate systemctl reload rsyslog > /dev/null 2>&1 || true endscript } ``` ## Автоматизация с Ansible [Заголовок раздела «Автоматизация с Ansible»](#автоматизация-с-ansible) playbook.yml ```yaml --- - hosts: mcp_servers become: yes tasks: - name: Create mcp user user: name: mcp system: yes home: /opt/mcp-server shell: /bin/false - name: Clone repository git: repo: https://github.com/user/mcp-server.git dest: /opt/mcp-server version: main - name: Install Python dependencies pip: requirements: /opt/mcp-server/requirements.txt virtualenv: /opt/mcp-server/.venv virtualenv_python: python3.12 - name: Copy systemd service template: src: mcp-server.service.j2 dest: /etc/systemd/system/mcp-server.service notify: restart mcp-server - name: Enable and start service systemd: name: mcp-server enabled: yes state: started handlers: - name: restart mcp-server systemd: name: mcp-server state: restarted daemon_reload: yes ``` ## Security Hardening [Заголовок раздела «Security Hardening»](#security-hardening) ### Эшелонированная защита (Defense in Depth) [Заголовок раздела «Эшелонированная защита (Defense in Depth)»](#эшелонированная-защита-defense-in-depth) Применяйте несколько уровней защиты: 1. **Пользователь**: Отдельный системный пользователь без shell 2. **Файловая система**: Минимальные права, read-only где возможно 3. **Сеть**: Firewall, только необходимые порты 4. **Процесс**: systemd ограничения, AppArmor/SELinux 5. **Мониторинг**: Fail2ban, логирование, алерты mcp-server.service ```ini # Расширенные ограничения systemd [Service] # Изоляция сети PrivateNetwork=no RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 # Изоляция файловой системы ProtectSystem=strict ProtectHome=yes PrivateTmp=yes PrivateDevices=yes ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes # Ограничение capabilities CapabilityBoundingSet=CAP_NET_BIND_SERVICE AmbientCapabilities= # Memory и процессы MemoryDenyWriteExecute=yes LockPersonality=yes RestrictRealtime=yes RestrictSUIDSGID=yes ``` ### AppArmor профиль [Заголовок раздела «AppArmor профиль»](#apparmor-профиль) Terminal ```bash sudo nano /etc/apparmor.d/opt.mcp-server ``` ```title="/etc/apparmor.d/opt.mcp-server" #include /opt/mcp-server/.venv/bin/python { #include #include /opt/mcp-server/** r, /opt/mcp-server/data/** rw, /tmp/** rw, deny /etc/passwd r, deny /etc/shadow r, } ``` ### Fail2ban [Заголовок раздела «Fail2ban»](#fail2ban) Terminal ```bash sudo nano /etc/fail2ban/jail.d/mcp-server.conf ``` /etc/fail2ban/jail.d/mcp-server.conf ```ini [mcp-server] enabled = true port = 3000 filter = mcp-server logpath = /var/log/mcp-server.log maxretry = 5 bantime = 3600 ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [Serena MCP on Ubuntu](https://itecsonline.com/post/how-to-install-serena-mcp-linux) * [Ubuntu MCP Server](https://lobehub.com/mcp/pazuzu1w-ubuntu_mcp_server) * [Systemd Documentation](https://www.freedesktop.org/software/systemd/man/systemd.service.html)
# Развёртывание на Windows
> Специфика установки и настройки MCP серверов на Windows
Развёртывание MCP серверов на Windows имеет ряд важных отличий от macOS и Linux. Эта документация поможет избежать типичных ошибок. Критически важно На Windows **всегда используйте абсолютные пути** к исполняемым файлам. Команды `npx`, `uvx` и относительные пути **не работают** в конфигурации Claude Desktop. ## Ключевые отличия [Заголовок раздела «Ключевые отличия»](#ключевые-отличия) | Аспект | macOS/Linux | Windows | | ---------- | ----------------- | ------------------------ | | Пути | `/path/to/server` | `C:\\path\\to\\server` | | Команды | `npx`, `uvx` | Полный путь к `node.exe` | | Слэши | `/` | `\\` (двойные в JSON) | | Переменные | `$HOME` | `%USERPROFILE%` | ## Установка зависимостей [Заголовок раздела «Установка зависимостей»](#установка-зависимостей) ### Node.js [Заголовок раздела «Node.js»](#nodejs) 1. Скачайте LTS версию с [nodejs.org](https://nodejs.org/) 2. Установите с опцией “Add to PATH” 3. Проверьте установку: PowerShell ```powershell node --version npm --version ``` ### Python [Заголовок раздела «Python»](#python) 1. Скачайте с [python.org](https://www.python.org/downloads/windows/) 2. **Важно:** Отметьте “Add Python to PATH” 3. Проверьте: PowerShell ```powershell python --version pip --version ``` ### uv (рекомендуется для Python) [Заголовок раздела «uv (рекомендуется для Python)»](#uv-рекомендуется-для-python) PowerShell ```powershell # Через pip pip install uv # Или через PowerShell irm https://astral.sh/uv/install.ps1 | iex ``` ## Claude Desktop конфигурация [Заголовок раздела «Claude Desktop конфигурация»](#claude-desktop-конфигурация) Файл конфигурации находится: ```plaintext %APPDATA%\Claude\claude_desktop_config.json ``` Или полный путь: ```plaintext C:\Users\\AppData\Roaming\Claude\claude_desktop_config.json ``` ### Правильный формат [Заголовок раздела «Правильный формат»](#правильный-формат) claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "C:\\Program Files\\nodejs\\node.exe", "args": ["C:\\Users\\username\\mcp-servers\\my-server\\dist\\index.js"] } } } ``` ### Типичные ошибки [Заголовок раздела «Типичные ошибки»](#типичные-ошибки) ❌ **Неправильно:** claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "npx", "args": ["-y", "@myorg/mcp-server"] } } } ``` ✅ **Правильно:** claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "C:\\Program Files\\nodejs\\node.exe", "args": [ "C:\\Users\\username\\AppData\\Roaming\\npm\\node_modules\\@myorg\\mcp-server\\dist\\index.js" ] } } } ``` ## Пути на Windows [Заголовок раздела «Пути на Windows»](#пути-на-windows) ### Нахождение путей [Заголовок раздела «Нахождение путей»](#нахождение-путей) PowerShell ```powershell # Путь к node.exe where node # Путь к npm packages npm root -g # Путь к Python where python # Путь к pip packages pip show mcp ``` ### Примеры путей [Заголовок раздела «Примеры путей»](#примеры-путей) | Программа | Типичный путь | | ------------ | --------------------------------------------------------------------------- | | node.exe | `C:\Program Files\nodejs\node.exe` | | npm modules | `C:\Users\\AppData\Roaming\npm\node_modules` | | python.exe | `C:\Users\\AppData\Local\Programs\Python\Python312\python.exe` | | pip packages | `C:\Users\\AppData\Local\Programs\Python\Python312\Lib\site-packages` | ## Конфигурация для Python [Заголовок раздела «Конфигурация для Python»](#конфигурация-для-python) ### С обычным Python [Заголовок раздела «С обычным Python»](#с-обычным-python) claude\_desktop\_config.json ```json { "mcpServers": { "python-server": { "command": "C:\\Users\\username\\AppData\\Local\\Programs\\Python\\Python312\\python.exe", "args": [ "-m", "src.server" ], "cwd": "C:\\Users\\username\\mcp-servers\\my-server" } } } ``` ### С uv [Заголовок раздела «С uv»](#с-uv) claude\_desktop\_config.json ```json { "mcpServers": { "uv-server": { "command": "C:\\Users\\username\\.local\\bin\\uv.exe", "args": [ "run", "python", "-m", "src.server" ], "cwd": "C:\\Users\\username\\mcp-servers\\my-server" } } } ``` ### С виртуальным окружением [Заголовок раздела «С виртуальным окружением»](#с-виртуальным-окружением) claude\_desktop\_config.json ```json { "mcpServers": { "venv-server": { "command": "C:\\Users\\username\\mcp-servers\\my-server\\.venv\\Scripts\\python.exe", "args": [ "-m", "src.server" ], "cwd": "C:\\Users\\username\\mcp-servers\\my-server" } } } ``` ## Конфигурация для Node.js [Заголовок раздела «Конфигурация для Node.js»](#конфигурация-для-nodejs) ### Локальный проект [Заголовок раздела «Локальный проект»](#локальный-проект) claude\_desktop\_config.json ```json { "mcpServers": { "node-server": { "command": "C:\\Program Files\\nodejs\\node.exe", "args": [ "C:\\Users\\username\\mcp-servers\\my-server\\dist\\index.js" ] } } } ``` ### Глобально установленный пакет [Заголовок раздела «Глобально установленный пакет»](#глобально-установленный-пакет) claude\_desktop\_config.json ```json { "mcpServers": { "global-server": { "command": "C:\\Program Files\\nodejs\\node.exe", "args": [ "C:\\Users\\username\\AppData\\Roaming\\npm\\node_modules\\@myorg\\mcp-server\\dist\\index.js" ] } } } ``` ## PowerShell скрипты [Заголовок раздела «PowerShell скрипты»](#powershell-скрипты) ### Wrapper скрипт [Заголовок раздела «Wrapper скрипт»](#wrapper-скрипт) Создайте `run-mcp.ps1`: run-mcp.ps1 ```powershell #!/usr/bin/env pwsh $ErrorActionPreference = "Stop" # Путь к проекту $projectPath = "C:\Users\username\mcp-servers\my-server" # Активация venv & "$projectPath\.venv\Scripts\Activate.ps1" # Запуск сервера python -m src.server ``` ### Использование в конфигурации [Заголовок раздела «Использование в конфигурации»](#использование-в-конфигурации) claude\_desktop\_config.json ```json { "mcpServers": { "ps-server": { "command": "powershell.exe", "args": [ "-ExecutionPolicy", "Bypass", "-File", "C:\\Users\\username\\mcp-servers\\run-mcp.ps1" ] } } } ``` ## Environment Variables [Заголовок раздела «Environment Variables»](#environment-variables) claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "C:\\Program Files\\nodejs\\node.exe", "args": ["C:\\path\\to\\server.js"], "env": { "DATABASE_URL": "postgresql://localhost/mydb", "LOG_LEVEL": "debug" } } } } ``` ## Troubleshooting [Заголовок раздела «Troubleshooting»](#troubleshooting) ### Проверка работоспособности [Заголовок раздела «Проверка работоспособности»](#проверка-работоспособности) PowerShell ```powershell # Тест Node.js сервера & "C:\Program Files\nodejs\node.exe" "C:\path\to\server\dist\index.js" # Тест Python сервера & "C:\path\to\.venv\Scripts\python.exe" -m src.server ``` ### Логирование [Заголовок раздела «Логирование»](#логирование) Для отладки включите логирование: claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "...", "args": ["..."], "env": { "DEBUG": "mcp:*", "LOG_LEVEL": "debug" } } } } ``` ### Типичные проблемы [Заголовок раздела «Типичные проблемы»](#типичные-проблемы) #### Сервер не запускается [Заголовок раздела «Сервер не запускается»](#сервер-не-запускается) 1. Проверьте пути — используйте полные абсолютные пути 2. Проверьте двойные слэши в JSON 3. Убедитесь, что все зависимости установлены #### Permission denied [Заголовок раздела «Permission denied»](#permission-denied) PowerShell ```powershell # Проверка прав на выполнение Get-ExecutionPolicy # Разрешение выполнения скриптов Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser ``` #### Модуль не найден [Заголовок раздела «Модуль не найден»](#модуль-не-найден) PowerShell ```powershell # Проверка установки npm пакета npm list -g @myorg/mcp-server # Переустановка npm install -g @myorg/mcp-server ``` ## WSL (Windows Subsystem for Linux) [Заголовок раздела «WSL (Windows Subsystem for Linux)»](#wsl-windows-subsystem-for-linux) Альтернативный подход — использование WSL: claude\_desktop\_config.json ```json { "mcpServers": { "wsl-server": { "command": "wsl.exe", "args": [ "--", "python3", "-m", "src.server" ], "cwd": "\\\\wsl$\\Ubuntu\\home\\user\\mcp-servers\\my-server" } } } ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [Windows MCP Installation Guide](https://github.com/trevorwilkerson/Windows-MCP-Server-Installation-Verification-Guide) * [MCP Market Windows Guide](https://mcpmarket.com/server/windows-installation-guide) * [Reddit: Installing MCP on Windows](https://www.reddit.com/r/ClaudeAI/comments/1hciaxk/solved_installing_mcp_servers_on_windows_with/)
# Пример калькулятора
> Базовый MCP сервер с математическими инструментами
## Python реализация [Заголовок раздела «Python реализация»](#python-реализация) server.py ```python from mcp.server.fastmcp import FastMCP from enum import Enum mcp = FastMCP("calculator") class Operation(Enum): ADD = "add" SUBTRACT = "subtract" MULTIPLY = "multiply" DIVIDE = "divide" @mcp.tool() def calculate(operation: Operation, a: float, b: float) -> float: """Выполняет математическую операцию""" match operation: case Operation.ADD: return a + b case Operation.SUBTRACT: return a - b case Operation.MULTIPLY: return a * b case Operation.DIVIDE: if b == 0: raise ValueError("Деление на ноль") return a / b @mcp.tool() def sqrt(n: float) -> float: """Квадратный корень""" if n < 0: raise ValueError("Отрицательное число") return n ** 0.5 @mcp.tool() def power(base: float, exp: float) -> float: """Возведение в степень""" return base ** exp if __name__ == "__main__": mcp.run() ``` ## Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация) claude\_desktop\_config.json ```json { "mcpServers": { "calculator": { "command": "python", "args": ["server.py"] } } } ```
# Начало работы с MCP
> Пошаговое руководство по созданию первого MCP сервера — от установки до тестирования
Model Context Protocol (MCP) позволяет создавать серверы, которые расширяют возможности AI-ассистентов. В этом руководстве мы создадим первый MCP сервер с нуля. ## Требования [Заголовок раздела «Требования»](#требования) * **Python 3.10+** или **Node.js 18+** * **pip** или **npm/pnpm** * **Claude Desktop** или другой MCP клиент для тестирования ## Выбор языка [Заголовок раздела «Выбор языка»](#выбор-языка) | Язык | Рекомендуется для | SDK | | ---------- | -------------------------------------- | ------------------------- | | Python | Новичков, ML/Data Science | FastMCP | | TypeScript | Web-разработчиков | @modelcontextprotocol/sdk | | PHP | Legacy систем, Laravel | php-mcp/server | | Rust | Production, высокой производительности | rust-sdk | | Go | Микросервисов | mcp-go | | C# | .NET экосистемы | modelcontextprotocol | ## Быстрый старт с Python [Заголовок раздела «Быстрый старт с Python»](#быстрый-старт-с-python) ### 1. Установка [Заголовок раздела «1. Установка»](#1-установка) Terminal ```bash # Создание виртуального окружения python -m venv .venv source .venv/bin/activate # Linux/macOS # .venv\Scripts\activate # Windows # Установка MCP SDK pip install mcp ``` ### 2. Создание сервера [Заголовок раздела «2. Создание сервера»](#2-создание-сервера) Создайте файл `server.py`: server.py ```python from mcp.server.fastmcp import FastMCP # Инициализация сервера mcp = FastMCP("Мой первый сервер") # Инструмент: функция, которую AI может вызвать @mcp.tool() def calculate(operation: str, a: float, b: float) -> float: """ Выполняет математическую операцию. Args: operation: Операция (add, subtract, multiply, divide) a: Первое число b: Второе число Returns: Результат вычисления """ match operation: case "add": return a + b case "subtract": return a - b case "multiply": return a * b case "divide": if b == 0: raise ValueError("Деление на ноль") return a / b case _: raise ValueError(f"Неизвестная операция: {operation}") # Ресурс: данные, которые AI может прочитать @mcp.resource("info://server") def get_server_info() -> str: """Информация о сервере""" return """ Сервер: Мой первый MCP сервер Версия: 1.0.0 Автор: kaktak.net """ # Промпт: шаблон для структурированных запросов @mcp.prompt() def math_prompt(problem: str) -> str: """Промпт для математических задач""" return f""" Реши следующую математическую задачу, используя инструмент calculate: {problem} Покажи пошаговое решение. """ if __name__ == "__main__": mcp.run() ``` ### 3. Тестирование с MCP Inspector [Заголовок раздела «3. Тестирование с MCP Inspector»](#3-тестирование-с-mcp-inspector) MCP Inspector — официальный инструмент для тестирования серверов: Terminal ```bash # Установка npm install -g @anthropic-ai/mcp-inspector # Запуск npx @modelcontextprotocol/inspector python server.py ``` Откройте в браузере и протестируйте: * Вкладка **Tools** — вызовите `calculate` * Вкладка **Resources** — прочитайте `info://server` * Вкладка **Prompts** — используйте `math_prompt` ### 4. Подключение к Claude Desktop [Заголовок раздела «4. Подключение к Claude Desktop»](#4-подключение-к-claude-desktop) Отредактируйте конфигурацию Claude Desktop: **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "python", "args": ["/полный/путь/к/server.py"] } } } ``` Перезапустите Claude Desktop. Сервер появится в списке доступных. ## Быстрый старт с TypeScript [Заголовок раздела «Быстрый старт с TypeScript»](#быстрый-старт-с-typescript) ### 1. Инициализация проекта [Заголовок раздела «1. Инициализация проекта»](#1-инициализация-проекта) Terminal ```bash mkdir my-mcp-server cd my-mcp-server npm init -y npm install @modelcontextprotocol/sdk npm install -D typescript @types/node npx tsc --init ``` ### 2. Создание сервера [Заголовок раздела «2. Создание сервера»](#2-создание-сервера-1) Создайте файл `src/index.ts`: src/index.ts ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new McpServer({ name: "my-server", version: "1.0.0", }); // Регистрация инструмента server.tool( "calculate", { operation: { type: "string", enum: ["add", "subtract", "multiply", "divide"] }, a: { type: "number" }, b: { type: "number" }, }, async ({ operation, a, b }) => { let result: number; switch (operation) { case "add": result = a + b; break; case "subtract": result = a - b; break; case "multiply": result = a * b; break; case "divide": if (b === 0) throw new Error("Деление на ноль"); result = a / b; break; default: throw new Error(`Неизвестная операция: ${operation}`); } return { content: [{ type: "text", text: `Результат: ${result}` }], }; } ); // Запуск сервера const transport = new StdioServerTransport(); await server.connect(transport); ``` ### 3. Сборка и запуск [Заголовок раздела «3. Сборка и запуск»](#3-сборка-и-запуск) Terminal ```bash npx tsc node dist/index.js ``` ### 4. Конфигурация Claude Desktop [Заголовок раздела «4. Конфигурация Claude Desktop»](#4-конфигурация-claude-desktop) claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "node", "args": ["/полный/путь/к/dist/index.js"] } } } ``` ## Структура проекта [Заголовок раздела «Структура проекта»](#структура-проекта) Рекомендуемая структура для MCP сервера: ```plaintext my-mcp-server/ ├── src/ │ ├── index.ts # Entry point │ ├── tools/ # Инструменты │ │ ├── calculator.ts │ │ └── filesystem.ts │ ├── resources/ # Ресурсы │ │ └── config.ts │ └── prompts/ # Промпты │ └── templates.ts ├── tests/ │ └── server.test.ts ├── package.json ├── tsconfig.json ├── Dockerfile # Для Docker развёртывания └── README.md ``` ## Ключевые концепции [Заголовок раздела «Ключевые концепции»](#ключевые-концепции) ### Инструменты (Tools) [Заголовок раздела «Инструменты (Tools)»](#инструменты-tools) Инструменты — это функции, которые AI может вызывать. Каждый инструмент имеет: * **Имя** — уникальный идентификатор * **Описание** — что делает инструмент (важно для AI) * **Схему параметров** — типы входных данных * **Обработчик** — логика выполнения tools.py ```python @mcp.tool() def search_database(query: str, limit: int = 10) -> list[dict]: """ Поиск в базе данных. Args: query: Поисковый запрос limit: Максимальное количество результатов """ # Логика поиска return results ``` ### Ресурсы (Resources) [Заголовок раздела «Ресурсы (Resources)»](#ресурсы-resources) Ресурсы — это данные, которые AI может прочитать: * **URI** — уникальный идентификатор (`file://`, `db://`, `api://`) * **MIME-тип** — формат данных * **Содержимое** — сами данные resources.py ```python @mcp.resource("config://database") def get_db_config() -> str: """Конфигурация базы данных""" return json.dumps({ "host": "localhost", "port": 5432, "database": "myapp" }) ``` ### Промпты (Prompts) [Заголовок раздела «Промпты (Prompts)»](#промпты-prompts) Промпты — шаблоны для структурированных запросов: prompts.py ````python @mcp.prompt() def code_review(language: str, code: str) -> str: """Промпт для ревью кода""" return f""" Проведи код-ревью следующего {language} кода: ```{language} {code} ``` Обрати внимание на: 1. Потенциальные баги 2. Производительность 3. Читаемость 4. Best practices """ ```` ## Следующие шаги [Заголовок раздела «Следующие шаги»](#следующие-шаги) * [Python SDK](/sdk/python/) — подробная документация FastMCP * [TypeScript SDK](/sdk/typescript/) — работа с Node.js * [Docker развёртывание](/deployment/docker/) — контейнеризация * [Безопасность](/security/best-practices/) — защита сервера ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [Официальная документация](https://modelcontextprotocol.io/docs/develop/build-server) * [MCP Inspector](https://github.com/modelcontextprotocol/inspector) * [Примеры серверов](https://github.com/modelcontextprotocol/servers)
# Оптимизация производительности
> Руководство по оптимизации MCP серверов для высокой производительности
Оптимизация MCP серверов критична для production развёртываний с высокой нагрузкой. ## Системный уровень [Заголовок раздела «Системный уровень»](#системный-уровень) ### CPU Isolation (Linux) [Заголовок раздела «CPU Isolation (Linux)»](#cpu-isolation-linux) Terminal ```bash # Выделение CPU cores для MCP сервера sudo cset shield --cpu 2-3 --kthread on # Запуск в изолированных CPU sudo cset shield --exec -- python -m src.server ``` ### cgroups v2 [Заголовок раздела «cgroups v2»](#cgroups-v2) Terminal ```bash # Создание cgroup sudo mkdir /sys/fs/cgroup/mcp-server # Лимит CPU (50%) echo "50000 100000" | sudo tee /sys/fs/cgroup/mcp-server/cpu.max # Лимит памяти (512MB) echo "536870912" | sudo tee /sys/fs/cgroup/mcp-server/memory.max ``` ### HugePages для ML workloads [Заголовок раздела «HugePages для ML workloads»](#hugepages-для-ml-workloads) Terminal ```bash # Включение HugePages echo 512 | sudo tee /proc/sys/vm/nr_hugepages # Проверка cat /proc/meminfo | grep Huge ``` ### Kernel tuning [Заголовок раздела «Kernel tuning»](#kernel-tuning) /etc/sysctl.d/99-mcp.conf ```bash # /etc/sysctl.d/99-mcp.conf # Network buffers net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 # Connections net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 65535 # File descriptors fs.file-max = 2097152 ``` ## Кэширование [Заголовок раздела «Кэширование»](#кэширование) ### Уровни кэширования [Заголовок раздела «Уровни кэширования»](#уровни-кэширования) ### Redis кэширование [Заголовок раздела «Redis кэширование»](#redis-кэширование) cache.py ```python import redis import json import hashlib redis_client = redis.Redis(host='localhost', port=6379, db=0) def cache_key(tool_name: str, params: dict) -> str: """Генерация ключа кэша""" param_hash = hashlib.md5(json.dumps(params, sort_keys=True).encode()).hexdigest() return f"mcp:tool:{tool_name}:{param_hash}" @mcp.tool() async def expensive_operation(query: str) -> dict: """Ресурсоёмкая операция с кэшированием""" key = cache_key("expensive_operation", {"query": query}) # Проверка кэша cached = redis_client.get(key) if cached: return json.loads(cached) # Выполнение операции result = await perform_expensive_query(query) # Сохранение в кэш (TTL 5 минут) redis_client.setex(key, 300, json.dumps(result)) return result ``` ### In-memory кэширование [Заголовок раздела «In-memory кэширование»](#in-memory-кэширование) memory\_cache.py ```python from functools import lru_cache from cachetools import TTLCache import asyncio # LRU кэш для синхронных функций @lru_cache(maxsize=1000) def get_config(key: str) -> str: return load_config(key) # TTL кэш для асинхронных cache = TTLCache(maxsize=1000, ttl=300) async def cached_fetch(url: str) -> str: if url in cache: return cache[url] result = await fetch_url(url) cache[url] = result return result ``` ## Connection Pooling [Заголовок раздела «Connection Pooling»](#connection-pooling) ### Database pool [Заголовок раздела «Database pool»](#database-pool) database.py ```python from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import sessionmaker engine = create_async_engine( "postgresql+asyncpg://user:pass@localhost/db", pool_size=20, max_overflow=10, pool_pre_ping=True, pool_recycle=3600, ) async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) @mcp.tool() async def query_db(sql: str) -> list: async with async_session() as session: result = await session.execute(text(sql)) return [dict(row) for row in result.fetchall()] ``` ### HTTP connection pool [Заголовок раздела «HTTP connection pool»](#http-connection-pool) http\_client.py ```python import aiohttp # Глобальный session connector = aiohttp.TCPConnector( limit=100, # Общий лимит соединений limit_per_host=30, # Лимит на хост keepalive_timeout=30, ) session = aiohttp.ClientSession(connector=connector) @mcp.tool() async def fetch_api(url: str) -> dict: async with session.get(url) as response: return await response.json() ``` ## Async/Await оптимизация [Заголовок раздела «Async/Await оптимизация»](#asyncawait-оптимизация) ### Параллельное выполнение [Заголовок раздела «Параллельное выполнение»](#параллельное-выполнение) parallel.py ```python import asyncio @mcp.tool() async def parallel_fetch(urls: list[str]) -> list[dict]: """Параллельная загрузка нескольких URL""" tasks = [fetch_url(url) for url in urls] results = await asyncio.gather(*tasks, return_exceptions=True) return [ {"url": url, "result": r if not isinstance(r, Exception) else str(r)} for url, r in zip(urls, results) ] ``` ### Batch processing [Заголовок раздела «Batch processing»](#batch-processing) batch.py ```python @mcp.tool() async def batch_process(items: list[dict]) -> list[dict]: """Пакетная обработка""" BATCH_SIZE = 100 results = [] for i in range(0, len(items), BATCH_SIZE): batch = items[i:i + BATCH_SIZE] batch_results = await asyncio.gather(*[ process_item(item) for item in batch ]) results.extend(batch_results) return results ``` ### Semaphore для ограничения concurrency [Заголовок раздела «Semaphore для ограничения concurrency»](#semaphore-для-ограничения-concurrency) semaphore.py ```python semaphore = asyncio.Semaphore(10) # Максимум 10 параллельных операций async def limited_fetch(url: str) -> str: async with semaphore: return await fetch_url(url) ``` ## Load Balancing [Заголовок раздела «Load Balancing»](#load-balancing) ### Nginx upstream [Заголовок раздела «Nginx upstream»](#nginx-upstream) /etc/nginx/nginx.conf ```nginx upstream mcp_servers { least_conn; # Выбор наименее загруженного server 127.0.0.1:3001 weight=5; server 127.0.0.1:3002 weight=3; server 127.0.0.1:3003 weight=2; keepalive 32; } ``` ### Kubernetes HPA [Заголовок раздела «Kubernetes HPA»](#kubernetes-hpa) hpa.yaml ```yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: mcp-server-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: mcp-server minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 ``` ## Мониторинг [Заголовок раздела «Мониторинг»](#мониторинг) ### Prometheus метрики [Заголовок раздела «Prometheus метрики»](#prometheus-метрики) metrics.py ```python from prometheus_client import Counter, Histogram, Gauge, start_http_server # Метрики REQUESTS = Counter('mcp_requests_total', 'Total requests', ['tool']) LATENCY = Histogram('mcp_request_latency_seconds', 'Request latency', ['tool']) ACTIVE = Gauge('mcp_active_requests', 'Active requests') def instrument_tool(func): """Декоратор для инструментации""" @wraps(func) async def wrapper(*args, **kwargs): tool_name = func.__name__ REQUESTS.labels(tool=tool_name).inc() ACTIVE.inc() with LATENCY.labels(tool=tool_name).time(): try: return await func(*args, **kwargs) finally: ACTIVE.dec() return wrapper # Запуск сервера метрик start_http_server(9090) ``` ### Grafana dashboard [Заголовок раздела «Grafana dashboard»](#grafana-dashboard) dashboard.json ```json { "panels": [ { "title": "Requests per Second", "type": "graph", "targets": [ { "expr": "rate(mcp_requests_total[1m])", "legendFormat": "{{tool}}" } ] }, { "title": "Latency P99", "type": "graph", "targets": [ { "expr": "histogram_quantile(0.99, rate(mcp_request_latency_seconds_bucket[5m]))", "legendFormat": "{{tool}}" } ] } ] } ``` ## Stress Testing [Заголовок раздела «Stress Testing»](#stress-testing) ### Locust [Заголовок раздела «Locust»](#locust) locustfile.py ```python # locustfile.py from locust import HttpUser, task, between class McpUser(HttpUser): wait_time = between(0.1, 0.5) @task(3) def call_tool(self): self.client.post("/message", json={ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "add", "arguments": {"a": 1, "b": 2} }, "id": 1 }) @task(1) def list_tools(self): self.client.post("/message", json={ "jsonrpc": "2.0", "method": "tools/list", "id": 1 }) ``` Terminal ```bash locust -f locustfile.py --host=http://localhost:3000 -u 100 -r 10 ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [Performance Optimization Guide](https://dev.to/nishantbijani/a-guide-to-optimizing-performance-and-security-for-mcp-servers-1pm9) * [Redis Documentation](https://redis.io/documentation) * [Prometheus Best Practices](https://prometheus.io/docs/practices/)
# Production Checklist
> 14 пунктов для подготовки MCP сервера к production — безопасность, производительность, мониторинг
## Чеклист перед деплоем [Заголовок раздела «Чеклист перед деплоем»](#чеклист-перед-деплоем) Проверьте каждый пункт перед выкладкой в production. ### Безопасность (Security) [Заголовок раздела «Безопасность (Security)»](#безопасность-security) #### 1. Валидация входных данных [Заголовок раздела «1. Валидация входных данных»](#1-валидация-входных-данных) ```python from pydantic import BaseModel, Field, validator from typing import Annotated class FileReadInput(BaseModel): path: Annotated[str, Field(max_length=255)] @validator('path') def validate_path(cls, v): # Запрещаем выход за пределы директории if '..' in v or v.startswith('/'): raise ValueError('Invalid path') return v @mcp.tool() def read_file(input: FileReadInput) -> str: """Безопасное чтение файла""" safe_path = ALLOWED_DIR / input.path return safe_path.read_text() ``` #### 2. Ограничение доступа к файловой системе [Заголовок раздела «2. Ограничение доступа к файловой системе»](#2-ограничение-доступа-к-файловой-системе) ```python from pathlib import Path ALLOWED_DIR = Path("/app/data").resolve() def safe_path(user_path: str) -> Path: """Проверяет, что путь в разрешённой директории""" requested = (ALLOWED_DIR / user_path).resolve() if not str(requested).startswith(str(ALLOWED_DIR)): raise PermissionError(f"Access denied: {user_path}") return requested ``` #### 3. Защита от инъекций [Заголовок раздела «3. Защита от инъекций»](#3-защита-от-инъекций) ```python # ❌ ОПАСНО: SQL инъекция @mcp.tool() def search_users(query: str) -> str: cursor.execute(f"SELECT * FROM users WHERE name LIKE '%{query}%'") # ✅ БЕЗОПАСНО: параметризованные запросы @mcp.tool() def search_users(query: str) -> str: cursor.execute( "SELECT * FROM users WHERE name LIKE ?", (f"%{query}%",) ) ``` #### 4. Секреты через переменные окружения [Заголовок раздела «4. Секреты через переменные окружения»](#4-секреты-через-переменные-окружения) ```python import os # ❌ ОПАСНО API_KEY = "sk-1234567890abcdef" # ✅ БЕЗОПАСНО API_KEY = os.environ.get("API_KEY") if not API_KEY: raise ValueError("API_KEY not set") ``` ```dockerfile # Docker ENV API_KEY=${API_KEY} ``` *** ### Производительность (Performance) [Заголовок раздела «Производительность (Performance)»](#производительность-performance) #### 5. Connection pooling для БД [Заголовок раздела «5. Connection pooling для БД»](#5-connection-pooling-для-бд) ```python import asyncpg # Глобальный пул соединений pool: asyncpg.Pool | None = None async def get_pool() -> asyncpg.Pool: global pool if pool is None: pool = await asyncpg.create_pool( DATABASE_URL, min_size=5, max_size=20, command_timeout=60 ) return pool @mcp.tool() async def query(sql: str) -> str: pool = await get_pool() async with pool.acquire() as conn: result = await conn.fetch(sql) return str(result) ``` #### 6. Кэширование [Заголовок раздела «6. Кэширование»](#6-кэширование) ```python from functools import lru_cache from datetime import datetime, timedelta # Простой кэш в памяти cache = {} CACHE_TTL = timedelta(minutes=5) def cached(key: str): def decorator(func): async def wrapper(*args, **kwargs): now = datetime.now() if key in cache: value, expires = cache[key] if now < expires: return value result = await func(*args, **kwargs) cache[key] = (result, now + CACHE_TTL) return result return wrapper return decorator @mcp.tool() @cached("weather") async def get_weather(city: str) -> str: """Погода с кэшированием на 5 минут""" # Запрос к API... return weather_data ``` #### 7. Таймауты для внешних вызовов [Заголовок раздела «7. Таймауты для внешних вызовов»](#7-таймауты-для-внешних-вызовов) ```python import asyncio import aiohttp @mcp.tool() async def fetch_url(url: str) -> str: """Загрузка URL с таймаутом""" timeout = aiohttp.ClientTimeout(total=30) async with aiohttp.ClientSession(timeout=timeout) as session: try: async with session.get(url) as response: return await response.text() except asyncio.TimeoutError: return "Error: Request timeout (30s)" ``` #### 8. Ограничение размера ответа [Заголовок раздела «8. Ограничение размера ответа»](#8-ограничение-размера-ответа) ```python MAX_RESPONSE_SIZE = 100 * 1024 # 100 KB @mcp.tool() def read_file(path: str) -> str: """Чтение файла с ограничением размера""" with open(path, 'r') as f: content = f.read(MAX_RESPONSE_SIZE) if len(content) == MAX_RESPONSE_SIZE: content += "\n\n[... truncated, file too large ...]" return content ``` *** ### Надёжность (Reliability) [Заголовок раздела «Надёжность (Reliability)»](#надёжность-reliability) #### 9. Graceful error handling [Заголовок раздела «9. Graceful error handling»](#9-graceful-error-handling) ```python from mcp.server.fastmcp import FastMCP import logging logger = logging.getLogger(__name__) @mcp.tool() async def risky_operation(data: str) -> str: """Операция с обработкой ошибок""" try: result = await process(data) return result except ConnectionError as e: logger.error(f"Connection failed: {e}") return "Error: Service temporarily unavailable" except ValueError as e: logger.warning(f"Invalid input: {e}") return f"Error: Invalid input - {e}" except Exception as e: logger.exception("Unexpected error") return "Error: Internal server error" ``` #### 10. Идемпотентные операции [Заголовок раздела «10. Идемпотентные операции»](#10-идемпотентные-операции) ```python import hashlib @mcp.tool() def create_file(name: str, content: str) -> str: """Идемпотентное создание файла""" # Используем хэш контента для уникальности content_hash = hashlib.sha256(content.encode()).hexdigest()[:8] filename = f"{name}_{content_hash}.txt" path = ALLOWED_DIR / filename # Если файл с таким содержимым уже есть — не перезаписываем if path.exists(): return f"File already exists: {filename}" path.write_text(content) return f"Created: {filename}" ``` #### 11. Health check endpoint [Заголовок раздела «11. Health check endpoint»](#11-health-check-endpoint) ```python from datetime import datetime start_time = datetime.now() @mcp.tool() def health_check() -> str: """Проверка здоровья сервера""" uptime = datetime.now() - start_time return f""" Status: OK Uptime: {uptime} Version: 1.0.0 Database: {"connected" if db_connected() else "disconnected"} """ ``` *** ### Мониторинг (Monitoring) [Заголовок раздела «Мониторинг (Monitoring)»](#мониторинг-monitoring) #### 12. Структурированные логи [Заголовок раздела «12. Структурированные логи»](#12-структурированные-логи) ```python import json import sys from datetime import datetime def log(level: str, message: str, **kwargs): """Структурированный лог в JSON""" entry = { "timestamp": datetime.utcnow().isoformat(), "level": level, "message": message, **kwargs } print(json.dumps(entry), file=sys.stderr) @mcp.tool() def process_data(data: str) -> str: log("INFO", "Processing started", data_length=len(data)) try: result = do_processing(data) log("INFO", "Processing completed", result_length=len(result)) return result except Exception as e: log("ERROR", "Processing failed", error=str(e)) raise ``` #### 13. Метрики производительности [Заголовок раздела «13. Метрики производительности»](#13-метрики-производительности) ```python import time from functools import wraps def timed(func): """Декоратор для измерения времени""" @wraps(func) async def wrapper(*args, **kwargs): start = time.perf_counter() try: return await func(*args, **kwargs) finally: elapsed = time.perf_counter() - start log("INFO", "Tool executed", tool=func.__name__, duration_ms=round(elapsed * 1000, 2)) return wrapper @mcp.tool() @timed async def slow_operation(data: str) -> str: """Операция с замером времени""" await asyncio.sleep(1) return "Done" ``` #### 14. Алерты на критические ошибки [Заголовок раздела «14. Алерты на критические ошибки»](#14-алерты-на-критические-ошибки) ```python import aiohttp ALERT_WEBHOOK = os.environ.get("ALERT_WEBHOOK") async def send_alert(message: str, severity: str = "error"): """Отправка алерта в Slack/Discord""" if not ALERT_WEBHOOK: return payload = { "text": f"[{severity.upper()}] MCP Server: {message}" } async with aiohttp.ClientSession() as session: await session.post(ALERT_WEBHOOK, json=payload) @mcp.tool() async def critical_operation(data: str) -> str: try: return await process(data) except Exception as e: await send_alert(f"Critical operation failed: {e}") raise ``` *** ## Docker Production Setup [Заголовок раздела «Docker Production Setup»](#docker-production-setup) ### Dockerfile [Заголовок раздела «Dockerfile»](#dockerfile) ```dockerfile FROM python:3.11-slim # Не запускать от root RUN useradd -m -u 1000 mcpuser WORKDIR /app # Зависимости COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Код COPY --chown=mcpuser:mcpuser . . USER mcpuser # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD python -c "import sys; sys.exit(0)" CMD ["python", "server.py"] ``` ### docker-compose.yml [Заголовок раздела «docker-compose.yml»](#docker-composeyml) ```yaml version: '3.8' services: mcp-server: build: . restart: unless-stopped environment: - DATABASE_URL=${DATABASE_URL} - API_KEY=${API_KEY} - LOG_LEVEL=INFO stdin_open: true tty: true deploy: resources: limits: memory: 512M cpus: '0.5' logging: driver: json-file options: max-size: "10m" max-file: "3" ``` *** ## 7-дневный путь обучения [Заголовок раздела «7-дневный путь обучения»](#7-дневный-путь-обучения) ### День 1: Основы [Заголовок раздела «День 1: Основы»](#день-1-основы) * [ ] Прочитать [Быстрый старт](/reference/quickstart/) * [ ] Создать первый сервер с 2 tools * [ ] Протестировать в MCP Inspector ### День 2: Примитивы MCP [Заголовок раздела «День 2: Примитивы MCP»](#день-2-примитивы-mcp) * [ ] Изучить [Примеры кода](/reference/examples/) * [ ] Добавить Resources к серверу * [ ] Добавить Prompts ### День 3: Интеграции [Заголовок раздела «День 3: Интеграции»](#день-3-интеграции) * [ ] Подключить базу данных (SQLite) * [ ] Создать tool для REST API * [ ] Обработать ошибки ### День 4: Клиенты [Заголовок раздела «День 4: Клиенты»](#день-4-клиенты) * [ ] Настроить [Claude Desktop](/reference/clients/) * [ ] Протестировать tools в реальном чате * [ ] Настроить VS Code или Cursor ### День 5: Отладка [Заголовок раздела «День 5: Отладка»](#день-5-отладка) * [ ] Изучить [Troubleshooting](/reference/troubleshooting/) * [ ] Настроить логирование в stderr * [ ] Написать unit-тесты ### День 6: Production [Заголовок раздела «День 6: Production»](#день-6-production) * [ ] Пройти этот чеклист (14 пунктов) * [ ] Создать Dockerfile * [ ] Настроить переменные окружения ### День 7: Деплой [Заголовок раздела «День 7: Деплой»](#день-7-деплой) * [ ] Собрать Docker image * [ ] Протестировать в production-like окружении * [ ] Задокументировать API своего сервера *** ## Быстрая проверка [Заголовок раздела «Быстрая проверка»](#быстрая-проверка) Используйте этот скрипт для проверки сервера: ```python #!/usr/bin/env python3 """pre_deploy_check.py - Проверка перед деплоем""" import subprocess import json import sys def check_server(command: list[str]) -> dict: """Проверяет MCP сервер""" results = { "initialize": False, "tools_list": False, "tool_call": False, "errors": [] } # 1. Initialize init_req = json.dumps({ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "check", "version": "1.0"} } }) + "\n" try: result = subprocess.run( command, input=init_req, capture_output=True, text=True, timeout=10 ) response = json.loads(result.stdout.strip()) if "result" in response: results["initialize"] = True print("✅ Initialize: OK") else: results["errors"].append(f"Initialize failed: {response}") print("❌ Initialize: FAILED") except Exception as e: results["errors"].append(str(e)) print(f"❌ Initialize: {e}") # 2. Tools list tools_req = json.dumps({ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} }) + "\n" try: result = subprocess.run( command, input=init_req + tools_req, capture_output=True, text=True, timeout=10 ) lines = result.stdout.strip().split('\n') if len(lines) >= 2: tools_response = json.loads(lines[1]) if "result" in tools_response and "tools" in tools_response["result"]: tools_count = len(tools_response["result"]["tools"]) results["tools_list"] = True print(f"✅ Tools list: OK ({tools_count} tools)") else: print("❌ Tools list: FAILED") else: print("❌ Tools list: No response") except Exception as e: results["errors"].append(str(e)) print(f"❌ Tools list: {e}") return results if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: python pre_deploy_check.py ") sys.exit(1) results = check_server(sys.argv[1:]) if results["initialize"] and results["tools_list"]: print("\n✅ Server is ready for deployment!") sys.exit(0) else: print("\n❌ Server has issues. Fix before deploying.") for error in results["errors"]: print(f" - {error}") sys.exit(1) ``` *** ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) Примеры кода [25+ примеров](/reference/examples/) — production-ready код Troubleshooting [Решение проблем](/reference/troubleshooting/) — ошибки и решения MCP Specification [Best Practices](https://modelcontextprotocol.io/specification/draft/basic/security_best_practices) — официальные рекомендации Инструменты [MCP Inspector](/reference/tools/) — отладка и тестирование
# Настройка клиентов
> Подключение MCP серверов к Claude Desktop, VS Code, Cursor и другим клиентам
Клиент MCP — это приложение, которое подключается к вашему серверу и использует его инструменты. Самые популярные клиенты: **Claude Desktop**, **VS Code** и **Cursor**. ## Claude Desktop [Заголовок раздела «Claude Desktop»](#claude-desktop) Claude Desktop — официальный клиент от Anthropic с полной поддержкой MCP. ### Расположение конфигурации [Заголовок раздела «Расположение конфигурации»](#расположение-конфигурации) * macOS ```plaintext ~/Library/Application Support/Claude/claude_desktop_config.json ``` * Windows ```plaintext %APPDATA%\Claude\claude_desktop_config.json ``` * Linux ```plaintext ~/.config/Claude/claude_desktop_config.json ``` ### Формат конфигурации [Заголовок раздела «Формат конфигурации»](#формат-конфигурации) ```json { "mcpServers": { "имя-сервера": { "command": "команда", "args": ["аргумент1", "аргумент2"], "env": { "ПЕРЕМЕННАЯ": "значение" } } } } ``` ### Примеры конфигураций [Заголовок раздела «Примеры конфигураций»](#примеры-конфигураций) * Python ```json { "mcpServers": { "my-python-server": { "command": "python", "args": ["/path/to/server.py"] } } } ``` С виртуальным окружением: ```json { "mcpServers": { "my-python-server": { "command": "/path/to/venv/bin/python", "args": ["/path/to/server.py"], "env": { "PYTHONPATH": "/path/to/project" } } } } ``` * Node.js ```json { "mcpServers": { "my-node-server": { "command": "node", "args": ["/path/to/dist/server.js"] } } } ``` С npx: ```json { "mcpServers": { "my-server": { "command": "npx", "args": ["-y", "@my-org/mcp-server"] } } } ``` * Docker ```json { "mcpServers": { "my-docker-server": { "command": "docker", "args": ["run", "-i", "--rm", "my-mcp-server:latest"] } } } ``` * uvx (Python) ```json { "mcpServers": { "my-server": { "command": "uvx", "args": ["my-mcp-package"] } } } ``` ### Несколько серверов [Заголовок раздела «Несколько серверов»](#несколько-серверов) ```json { "mcpServers": { "calculator": { "command": "python", "args": ["/path/to/calculator.py"] }, "database": { "command": "python", "args": ["/path/to/db_server.py"], "env": { "DATABASE_URL": "postgresql://localhost/mydb" } }, "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "ghp_xxxxxxxxxxxx" } } } } ``` ### Проверка подключения [Заголовок раздела «Проверка подключения»](#проверка-подключения) 1. Сохраните конфигурацию 2. Перезапустите Claude Desktop 3. Откройте новый чат 4. Нажмите на иконку 🔧 (инструменты) внизу 5. Убедитесь, что ваши tools отображаются в списке *** ## VS Code (Copilot) [Заголовок раздела «VS Code (Copilot)»](#vs-code-copilot) VS Code с GitHub Copilot поддерживает MCP через файл конфигурации. ### Расположение конфигурации [Заголовок раздела «Расположение конфигурации»](#расположение-конфигурации-1) Или глобально: `~/.vscode/mcp.json` ### Формат конфигурации [Заголовок раздела «Формат конфигурации»](#формат-конфигурации-1) ```json { "servers": { "имя-сервера": { "command": "команда", "args": ["аргумент1"], "env": { "КЛЮЧ": "значение" } } } } ``` ### Примеры [Заголовок раздела «Примеры»](#примеры) * Python ```json { "servers": { "my-mcp-server": { "command": "python", "args": ["${workspaceFolder}/server.py"] } } } ``` * Node.js ```json { "servers": { "my-mcp-server": { "command": "node", "args": ["${workspaceFolder}/dist/server.js"] } } } ``` * npx ```json { "servers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "${workspaceFolder}"] } } } ``` ### Переменные VS Code [Заголовок раздела «Переменные VS Code»](#переменные-vs-code) | Переменная | Значение | | -------------------- | -------------------------- | | `${workspaceFolder}` | Корень открытого workspace | | `${file}` | Текущий открытый файл | | `${env:NAME}` | Переменная окружения NAME | ### Включение MCP в Copilot [Заголовок раздела «Включение MCP в Copilot»](#включение-mcp-в-copilot) 1. Откройте настройки VS Code 2. Найдите `github.copilot.chat.experimental.mcp` 3. Включите опцию 4. Перезагрузите VS Code *** ## Cursor [Заголовок раздела «Cursor»](#cursor) Cursor — AI-first редактор с встроенной поддержкой MCP. ### Расположение конфигурации [Заголовок раздела «Расположение конфигурации»](#расположение-конфигурации-2) Глобально: `~/.cursor/mcp.json` ### Формат конфигурации [Заголовок раздела «Формат конфигурации»](#формат-конфигурации-2) ```json { "mcpServers": { "имя-сервера": { "command": "команда", "args": ["аргументы"], "env": {} } } } ``` ### Примеры [Заголовок раздела «Примеры»](#примеры-1) * Python ```json { "mcpServers": { "my-server": { "command": "python", "args": ["/Users/me/mcp/server.py"] } } } ``` * Node.js ```json { "mcpServers": { "my-server": { "command": "npx", "args": ["ts-node", "/Users/me/mcp/server.ts"] } } } ``` ### Активация в Cursor [Заголовок раздела «Активация в Cursor»](#активация-в-cursor) 1. Создайте `.cursor/mcp.json` в проекте или `~/.cursor/mcp.json` глобально 2. Перезапустите Cursor 3. Откройте Composer (Cmd+I / Ctrl+I) 4. MCP tools автоматически доступны *** ## Windsurf [Заголовок раздела «Windsurf»](#windsurf) Windsurf (от Codeium) также поддерживает MCP. ### Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация) \~/.windsurf/mcp.json ```json { "mcpServers": { "my-server": { "command": "python", "args": ["server.py"] } } } ``` *** ## Zed [Заголовок раздела «Zed»](#zed) Zed — быстрый редактор с поддержкой MCP через расширения. ### Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация-1) \~/.config/zed/settings.json ```json { "assistant": { "mcp_servers": { "my-server": { "command": "python", "args": ["server.py"] } } } } ``` *** ## Сравнение клиентов [Заголовок раздела «Сравнение клиентов»](#сравнение-клиентов) | Клиент | Файл конфигурации | Формат | Особенности | | -------------- | ---------------------------- | ------------- | ----------------------------- | | Claude Desktop | `claude_desktop_config.json` | `mcpServers` | Официальный, полная поддержка | | VS Code | `.vscode/mcp.json` | `servers` | Интеграция с Copilot | | Cursor | `.cursor/mcp.json` | `mcpServers` | AI-first редактор | | Windsurf | `~/.windsurf/mcp.json` | `mcpServers` | Codeium | | Zed | `settings.json` | `mcp_servers` | Быстрый редактор | *** ## Популярные MCP серверы [Заголовок раздела «Популярные MCP серверы»](#популярные-mcp-серверы) Готовые серверы, которые можно подключить: ### Официальные (от Anthropic) [Заголовок раздела «Официальные (от Anthropic)»](#официальные-от-anthropic) ```json { "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"] }, "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "ghp_xxx" } }, "postgres": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-postgres"], "env": { "DATABASE_URL": "postgresql://user:pass@localhost/db" } }, "sqlite": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-sqlite", "/path/to/database.db"] }, "puppeteer": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-puppeteer"] }, "brave-search": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-brave-search"], "env": { "BRAVE_API_KEY": "xxx" } } } } ``` ### Сторонние популярные [Заголовок раздела «Сторонние популярные»](#сторонние-популярные) ```json { "mcpServers": { "everything": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-everything"] }, "memory": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-memory"] }, "fetch": { "command": "npx", "args": ["-y", "@tokenizin/mcp-npx-fetch"] } } } ``` *** ## Безопасность [Заголовок раздела «Безопасность»](#безопасность) Внимание! MCP серверы имеют доступ к вашей системе. Подключайте только доверенные серверы! ### Рекомендации [Заголовок раздела «Рекомендации»](#рекомендации) 1. **Проверяйте исходный код** сервера перед подключением 2. **Ограничивайте доступ** к файловой системе (`server-filesystem` только к нужным папкам) 3. **Не храните секреты** в конфигурации — используйте переменные окружения 4. **Обновляйте серверы** регулярно 5. **Используйте Docker** для изоляции ненадёжных серверов ### Безопасная конфигурация [Заголовок раздела «Безопасная конфигурация»](#безопасная-конфигурация) ```json { "mcpServers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/Users/me/safe-folder" ] } } } ``` Не делайте так: ```json { "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/"] } } } ``` *** ## Troubleshooting [Заголовок раздела «Troubleshooting»](#troubleshooting) ### Tools не появляются [Заголовок раздела «Tools не появляются»](#tools-не-появляются) 1. Перезапустите клиент полностью 2. Проверьте синтаксис JSON (запятые, кавычки) 3. Проверьте абсолютные пути 4. Запустите сервер вручную: `python server.py` ### Ошибка “Command not found” [Заголовок раздела «Ошибка “Command not found”»](#ошибка-command-not-found) ```json { "mcpServers": { "my-server": { "command": "/usr/local/bin/python3", "args": ["/full/path/to/server.py"] } } } ``` ### Ошибка подключения [Заголовок раздела «Ошибка подключения»](#ошибка-подключения) Проверьте логи: * Claude Desktop: `~/Library/Logs/Claude/mcp*.log` * VS Code: Output → MCP * Cursor: Developer Tools → Console *** ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) Официальные серверы [MCP Servers](https://github.com/modelcontextprotocol/servers) — коллекция готовых серверов MCP Registry [registry.mcphub.io](https://registry.mcphub.io) — каталог MCP серверов Claude Desktop [claude.ai/download](https://claude.ai/download) — скачать клиент Troubleshooting [Решение проблем](/reference/troubleshooting/) — типичные ошибки
# Примеры кода
> 25+ готовых примеров MCP серверов — копируйте и адаптируйте под свои задачи
Коллекция готовых примеров для копирования и адаптации. Все примеры протестированы и работают с MCP Inspector. ## Базовые примеры [Заголовок раздела «Базовые примеры»](#базовые-примеры) ### Калькулятор [Заголовок раздела «Калькулятор»](#калькулятор) * Python calculator.py ```python from mcp.server.fastmcp import FastMCP mcp = FastMCP("calculator") @mcp.tool() def add(a: float, b: float) -> float: """Сложение двух чисел""" return a + b @mcp.tool() def subtract(a: float, b: float) -> float: """Вычитание: a - b""" return a - b @mcp.tool() def multiply(a: float, b: float) -> float: """Умножение двух чисел""" return a * b @mcp.tool() def divide(a: float, b: float) -> float: """Деление: a / b""" if b == 0: raise ValueError("Деление на ноль невозможно") return a / b @mcp.tool() def power(base: float, exponent: float) -> float: """Возведение в степень: base^exponent""" return base ** exponent if __name__ == "__main__": mcp.run(transport="stdio") ``` * TypeScript calculator.ts ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new McpServer({ name: "calculator", version: "1.0.0" }); const tools = [ { name: "add", description: "Сложение", op: (a: number, b: number) => a + b }, { name: "subtract", description: "Вычитание", op: (a: number, b: number) => a - b }, { name: "multiply", description: "Умножение", op: (a: number, b: number) => a * b }, { name: "divide", description: "Деление", op: (a: number, b: number) => { if (b === 0) throw new Error("Деление на ноль"); return a / b; }}, ]; server.setRequestHandler("tools/list", async () => ({ tools: tools.map(t => ({ name: t.name, description: t.description, inputSchema: { type: "object", properties: { a: { type: "number" }, b: { type: "number" } }, required: ["a", "b"] } })) })); server.setRequestHandler("tools/call", async (req) => { const tool = tools.find(t => t.name === req.params.name); if (!tool) throw new Error(`Unknown tool: ${req.params.name}`); const { a, b } = req.params.arguments as { a: number; b: number }; const result = tool.op(a, b); return { content: [{ type: "text", text: String(result) }] }; }); await server.connect(new StdioServerTransport()); ``` ### Работа с файлами [Заголовок раздела «Работа с файлами»](#работа-с-файлами) file\_manager.py ```python from mcp.server.fastmcp import FastMCP from pathlib import Path mcp = FastMCP("file-manager") # Укажите разрешённую директорию ALLOWED_DIR = Path("/tmp/mcp-files") ALLOWED_DIR.mkdir(exist_ok=True) def safe_path(filename: str) -> Path: """Проверка что путь внутри разрешённой директории""" path = (ALLOWED_DIR / filename).resolve() if not str(path).startswith(str(ALLOWED_DIR)): raise ValueError("Доступ запрещён") return path @mcp.tool() def read_file(filename: str) -> str: """Прочитать содержимое файла""" path = safe_path(filename) if not path.exists(): raise FileNotFoundError(f"Файл не найден: {filename}") return path.read_text() @mcp.tool() def write_file(filename: str, content: str) -> str: """Записать содержимое в файл""" path = safe_path(filename) path.write_text(content) return f"Записано {len(content)} символов в {filename}" @mcp.tool() def list_files() -> list[str]: """Список файлов в директории""" return [f.name for f in ALLOWED_DIR.iterdir() if f.is_file()] @mcp.tool() def delete_file(filename: str) -> str: """Удалить файл""" path = safe_path(filename) if not path.exists(): raise FileNotFoundError(f"Файл не найден: {filename}") path.unlink() return f"Файл {filename} удалён" if __name__ == "__main__": mcp.run(transport="stdio") ``` Безопасность Всегда проверяйте пути файлов! Функция `safe_path` предотвращает path traversal атаки (`../../../etc/passwd`). ## API интеграции [Заголовок раздела «API интеграции»](#api-интеграции) ### REST API клиент [Заголовок раздела «REST API клиент»](#rest-api-клиент) rest\_client.py ```python from mcp.server.fastmcp import FastMCP import httpx mcp = FastMCP("rest-client") @mcp.tool() async def http_get(url: str, headers: dict | None = None) -> dict: """Выполнить GET запрос к API""" async with httpx.AsyncClient(timeout=30) as client: response = await client.get(url, headers=headers or {}) return { "status": response.status_code, "headers": dict(response.headers), "body": response.text[:5000] # Ограничение размера } @mcp.tool() async def http_post(url: str, data: dict, headers: dict | None = None) -> dict: """Выполнить POST запрос к API""" async with httpx.AsyncClient(timeout=30) as client: response = await client.post(url, json=data, headers=headers or {}) return { "status": response.status_code, "body": response.text[:5000] } @mcp.tool() async def fetch_json(url: str) -> dict: """Получить JSON с URL""" async with httpx.AsyncClient(timeout=30) as client: response = await client.get(url) response.raise_for_status() return response.json() if __name__ == "__main__": mcp.run(transport="stdio") ``` ### Погода (Weather API) [Заголовок раздела «Погода (Weather API)»](#погода-weather-api) weather.py ```python from mcp.server.fastmcp import FastMCP import httpx mcp = FastMCP("weather") NWS_API = "https://api.weather.gov" HEADERS = {"User-Agent": "mcp-weather/1.0"} @mcp.tool() async def get_weather(latitude: float, longitude: float) -> dict: """Получить погоду по координатам (только США)""" async with httpx.AsyncClient() as client: # Получаем grid point points = await client.get( f"{NWS_API}/points/{latitude},{longitude}", headers=HEADERS ) points_data = points.json() # Получаем прогноз forecast_url = points_data["properties"]["forecast"] forecast = await client.get(forecast_url, headers=HEADERS) periods = forecast.json()["properties"]["periods"][:3] return { "location": points_data["properties"]["relativeLocation"]["properties"], "forecast": [ { "name": p["name"], "temperature": f"{p['temperature']}°{p['temperatureUnit']}", "description": p["shortForecast"] } for p in periods ] } @mcp.tool() async def get_alerts(state: str) -> list[dict]: """Получить погодные предупреждения по штату США""" async with httpx.AsyncClient() as client: response = await client.get( f"{NWS_API}/alerts/active?area={state.upper()}", headers=HEADERS ) features = response.json().get("features", []) return [ { "event": f["properties"]["event"], "headline": f["properties"]["headline"], "severity": f["properties"]["severity"] } for f in features[:5] ] if __name__ == "__main__": mcp.run(transport="stdio") ``` ## Базы данных [Заголовок раздела «Базы данных»](#базы-данных) ### SQLite [Заголовок раздела «SQLite»](#sqlite) sqlite\_server.py ```python from mcp.server.fastmcp import FastMCP import sqlite3 from contextlib import contextmanager mcp = FastMCP("sqlite") DB_PATH = "data.db" @contextmanager def get_db(): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row try: yield conn finally: conn.close() @mcp.tool() def init_database() -> str: """Инициализировать БД с примером таблицы""" with get_db() as conn: conn.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) """) conn.commit() return "База данных инициализирована" @mcp.tool() def query(sql: str) -> list[dict]: """Выполнить SELECT запрос (только чтение!)""" sql_lower = sql.lower().strip() if not sql_lower.startswith("select"): raise ValueError("Разрешены только SELECT запросы") with get_db() as conn: cursor = conn.execute(sql) return [dict(row) for row in cursor.fetchall()] @mcp.tool() def insert_user(name: str, email: str) -> dict: """Добавить пользователя""" with get_db() as conn: cursor = conn.execute( "INSERT INTO users (name, email) VALUES (?, ?)", (name, email) ) conn.commit() return {"id": cursor.lastrowid, "name": name, "email": email} @mcp.tool() def list_users() -> list[dict]: """Список всех пользователей""" return query("SELECT * FROM users ORDER BY created_at DESC") @mcp.resource("db://schema") def get_schema() -> str: """Схема базы данных""" with get_db() as conn: cursor = conn.execute( "SELECT sql FROM sqlite_master WHERE type='table'" ) return "\n\n".join(row[0] for row in cursor.fetchall() if row[0]) if __name__ == "__main__": mcp.run(transport="stdio") ``` ### PostgreSQL [Заголовок раздела «PostgreSQL»](#postgresql) postgres\_server.py ```python from mcp.server.fastmcp import FastMCP import asyncpg import os mcp = FastMCP("postgres") DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:pass@localhost/db") async def get_pool(): return await asyncpg.create_pool(DATABASE_URL, min_size=1, max_size=5) @mcp.tool() async def query(sql: str, params: list | None = None) -> list[dict]: """Выполнить SQL запрос""" pool = await get_pool() async with pool.acquire() as conn: rows = await conn.fetch(sql, *(params or [])) return [dict(row) for row in rows] @mcp.tool() async def execute(sql: str, params: list | None = None) -> str: """Выполнить SQL команду (INSERT/UPDATE/DELETE)""" pool = await get_pool() async with pool.acquire() as conn: result = await conn.execute(sql, *(params or [])) return result @mcp.tool() async def list_tables() -> list[str]: """Список таблиц в БД""" result = await query(""" SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' """) return [row["table_name"] for row in result] @mcp.tool() async def describe_table(table_name: str) -> list[dict]: """Описание структуры таблицы""" return await query(""" SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = $1 ORDER BY ordinal_position """, [table_name]) if __name__ == "__main__": mcp.run(transport="stdio") ``` ## Ресурсы и промпты [Заголовок раздела «Ресурсы и промпты»](#ресурсы-и-промпты) ### Ресурсы (Resources) [Заголовок раздела «Ресурсы (Resources)»](#ресурсы-resources) resources\_example.py ```python from mcp.server.fastmcp import FastMCP from datetime import datetime import json mcp = FastMCP("resources-demo") # Статический ресурс @mcp.resource("config://app") def get_config() -> str: """Конфигурация приложения""" return json.dumps({ "version": "1.0.0", "environment": "development", "features": ["tools", "resources", "prompts"] }, indent=2) # Динамический ресурс с параметром @mcp.resource("user://{user_id}") def get_user(user_id: str) -> str: """Информация о пользователе""" users = { "1": {"name": "Алиса", "role": "admin"}, "2": {"name": "Борис", "role": "user"}, } user = users.get(user_id, {"error": "Пользователь не найден"}) return json.dumps(user, ensure_ascii=False) # Ресурс с текущим временем @mcp.resource("system://time") def get_time() -> str: """Текущее время сервера""" return datetime.now().isoformat() # Ресурс со списком @mcp.resource("data://products") def get_products() -> str: """Каталог продуктов""" products = [ {"id": 1, "name": "Ноутбук", "price": 50000}, {"id": 2, "name": "Телефон", "price": 30000}, {"id": 3, "name": "Планшет", "price": 25000}, ] return json.dumps(products, ensure_ascii=False, indent=2) if __name__ == "__main__": mcp.run(transport="stdio") ``` ### Промпты (Prompts) [Заголовок раздела «Промпты (Prompts)»](#промпты-prompts) prompts\_example.py ````python from mcp.server.fastmcp import FastMCP from mcp.types import PromptMessage, TextContent mcp = FastMCP("prompts-demo") @mcp.prompt() def code_review(code: str, language: str = "python") -> list[PromptMessage]: """Промпт для code review""" return [ PromptMessage( role="user", content=TextContent( type="text", text=f"""Проведи code review следующего кода на {language}. Обрати внимание на: 1. Читаемость и стиль кода 2. Потенциальные баги 3. Производительность 4. Безопасность 5. Предложения по улучшению Код: ```{language} {code} ```""" ) ) ] @mcp.prompt() def explain_error(error_message: str, context: str = "") -> list[PromptMessage]: """Промпт для объяснения ошибки""" return [ PromptMessage( role="user", content=TextContent( type="text", text=f"""Объясни эту ошибку простым языком и предложи решение. Ошибка: {error_message} {"Контекст: " + context if context else ""} Пожалуйста: 1. Объясни что означает эта ошибка 2. Укажи возможные причины 3. Предложи способы исправления""" ) ) ] @mcp.prompt() def generate_tests(function_code: str, framework: str = "pytest") -> list[PromptMessage]: """Промпт для генерации тестов""" return [ PromptMessage( role="user", content=TextContent( type="text", text=f"""Напиши тесты для этой функции используя {framework}. Функция: ```python {function_code} ```` Требования: 1. Тесты на основные сценарии 2. Тесты на граничные случаи 3. Тесты на ошибки 4. Понятные названия тестов""" ) ) ] if **name** == “**main**”: mcp.run(transport=“stdio”) ````plaintext ## Продвинутые примеры ### Контекст и логирование ```python title="context_logging.py" from mcp.server.fastmcp import FastMCP, Context from datetime import datetime mcp = FastMCP("context-demo") @mcp.tool() async def long_task(steps: int, ctx: Context) -> str: """Задача с отчётом о прогрессе""" for i in range(steps): # Отчёт о прогрессе await ctx.report_progress(i + 1, steps) # Логирование await ctx.info(f"Выполнен шаг {i + 1} из {steps}") # Имитация работы import asyncio await asyncio.sleep(0.5) return f"Выполнено {steps} шагов" @mcp.tool() async def get_request_info(ctx: Context) -> dict: """Информация о текущем запросе""" return { "request_id": ctx.request_id, "client_id": getattr(ctx, 'client_id', 'unknown'), "timestamp": datetime.now().isoformat() } if __name__ == "__main__": mcp.run(transport="stdio") ```` ### Dependency Injection (Lifespan) [Заголовок раздела «Dependency Injection (Lifespan)»](#dependency-injection-lifespan) lifespan\_example.py ```python from mcp.server.fastmcp import FastMCP from contextlib import asynccontextmanager from dataclasses import dataclass import asyncpg @dataclass class AppContext: db_pool: asyncpg.Pool @asynccontextmanager async def app_lifespan(server: FastMCP): """Управление жизненным циклом приложения""" # Startup pool = await asyncpg.create_pool("postgresql://localhost/mydb") try: yield AppContext(db_pool=pool) finally: # Shutdown await pool.close() mcp = FastMCP("lifespan-demo", lifespan=app_lifespan) @mcp.tool() async def db_query(sql: str, ctx: Context) -> list[dict]: """Запрос к БД через пул соединений""" app: AppContext = ctx.request_context.lifespan_context async with app.db_pool.acquire() as conn: rows = await conn.fetch(sql) return [dict(row) for row in rows] if __name__ == "__main__": mcp.run(transport="stdio") ``` ### Обработка ошибок [Заголовок раздела «Обработка ошибок»](#обработка-ошибок) error\_handling.py ```python from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp.exceptions import ToolError mcp = FastMCP("error-demo") class ValidationError(ToolError): """Ошибка валидации""" pass class NotFoundError(ToolError): """Ресурс не найден""" pass @mcp.tool() def validate_email(email: str) -> dict: """Валидация email""" if not email: raise ValidationError("Email не может быть пустым") if "@" not in email: raise ValidationError("Email должен содержать @") parts = email.split("@") if len(parts) != 2 or not parts[1]: raise ValidationError("Неверный формат email") return {"email": email, "valid": True} @mcp.tool() def get_user(user_id: int) -> dict: """Получить пользователя (с обработкой ошибок)""" users = {1: "Алиса", 2: "Борис"} if user_id not in users: raise NotFoundError(f"Пользователь {user_id} не найден") return {"id": user_id, "name": users[user_id]} @mcp.tool() def divide_safe(a: float, b: float) -> float: """Безопасное деление""" try: return a / b except ZeroDivisionError: raise ToolError("Деление на ноль невозможно") if __name__ == "__main__": mcp.run(transport="stdio") ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) | Ресурс | Описание | | ------------------------------------------------------------------------ | ---------------------------- | | [Python SDK](https://github.com/modelcontextprotocol/python-sdk) | Официальный Python SDK | | [TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) | Официальный TypeScript SDK | | [FastMCP Docs](https://gofastmcp.com) | Документация FastMCP | | [MCP Examples](https://github.com/modelcontextprotocol/servers) | Официальные примеры серверов | | [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers) | Коллекция MCP серверов |
# Архитектура MCP
> Техническая спецификация Model Context Protocol — транспорты, примитивы, жизненный цикл
**Model Context Protocol (MCP)** — открытый протокол для интеграции AI-приложений с внешними источниками данных и инструментами. Создан Anthropic в ноябре 2024 года. ## Общая архитектура [Заголовок раздела «Общая архитектура»](#общая-архитектура) ### Роли [Заголовок раздела «Роли»](#роли) | Компонент | Описание | Примеры | | ---------- | ---------------------------------- | ------------------- | | **Host** | Приложение, запускающее клиент | Claude Desktop, IDE | | **Client** | Поддерживает соединение с сервером | Встроен в Host | | **Server** | Предоставляет capabilities | Ваш MCP сервер | *** ## Транспорты [Заголовок раздела «Транспорты»](#транспорты) MCP поддерживает два типа транспорта для коммуникации. ### stdio (Standard I/O) [Заголовок раздела «stdio (Standard I/O)»](#stdio-standard-io) Клиент запускает сервер как subprocess и общается через stdin/stdout. **Когда использовать:** * Локальная разработка * Интеграция с Claude Desktop * Простые сценарии **Пример запуска:** ```bash # Клиент запускает сервер python server.py # stdin → JSON-RPC запросы # stdout → JSON-RPC ответы # stderr → логи (не протокол!) ``` Осторожно **Важно:** stdout должен содержать ТОЛЬКО JSON-RPC сообщения. Все логи и отладочные сообщения — в stderr! ### Streamable HTTP [Заголовок раздела «Streamable HTTP»](#streamable-http) HTTP-транспорт для удалённых серверов (спецификация 2025). **Когда использовать:** * Удалённые серверы * Масштабируемые решения * Production окружения **Требования (2025):** * OAuth 2.1 обязателен для аутентификации * HTTPS для production *** ## Примитивы MCP [Заголовок раздела «Примитивы MCP»](#примитивы-mcp) MCP определяет три основных примитива для взаимодействия. ### Tools (Инструменты) [Заголовок раздела «Tools (Инструменты)»](#tools-инструменты) Функции, которые AI может вызывать для выполнения действий. ```python @mcp.tool() def send_email(to: str, subject: str, body: str) -> str: """Отправить email""" # Реализация... return "Email отправлен" ``` **Характеристики:** * Вызываются по инициативе AI * Могут изменять состояние * Возвращают результат **JSON-RPC:** ```json // Запрос: tools/call { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "send_email", "arguments": { "to": "user@example.com", "subject": "Hello", "body": "Message" } } } // Ответ { "jsonrpc": "2.0", "id": 1, "result": { "content": [ {"type": "text", "text": "Email отправлен"} ] } } ``` ### Resources (Ресурсы) [Заголовок раздела «Resources (Ресурсы)»](#resources-ресурсы) Данные, доступные для чтения по URI. ```python @mcp.resource("config://settings") def get_settings() -> str: """Настройки приложения""" return json.dumps({"theme": "dark", "lang": "ru"}) ``` **Характеристики:** * Read-only доступ * Идентифицируются по URI * Могут обновляться (подписка) **Схемы URI:** | Схема | Пример | Назначение | | ----------- | ----------------------------- | ------------ | | `file://` | `file:///path/to/file.txt` | Файлы | | `http://` | `http://api.example.com/data` | HTTP ресурсы | | `db://` | `db://users/123` | База данных | | `memory://` | `memory://cache` | Память | | `config://` | `config://settings` | Конфигурация | ### Prompts (Промпты) [Заголовок раздела «Prompts (Промпты)»](#prompts-промпты) Шаблоны для типовых запросов к AI. ````python @mcp.prompt() def code_review(language: str, code: str) -> str: """Шаблон для code review""" return f""" Проведи code review для следующего {language} кода: ```{language} {code} ```` Проверь: 1. Ошибки и баги 2. Стиль кода 3. Производительность 4. Безопасность """ ````plaintext **Характеристики:** - Генерируют текст для AI - Принимают аргументы - Упрощают типовые задачи --- ## JSON-RPC 2.0 MCP использует JSON-RPC 2.0 для всей коммуникации. ### Формат запроса ```json { "jsonrpc": "2.0", "id": 1, "method": "method_name", "params": { ... } } ```` ### Формат ответа [Заголовок раздела «Формат ответа»](#формат-ответа) ```json { "jsonrpc": "2.0", "id": 1, "result": { ... } } ``` ### Формат ошибки [Заголовок раздела «Формат ошибки»](#формат-ошибки) ```json { "jsonrpc": "2.0", "id": 1, "error": { "code": -32600, "message": "Invalid Request", "data": { ... } } } ``` ### Уведомления (без ответа) [Заголовок раздела «Уведомления (без ответа)»](#уведомления-без-ответа) ```json { "jsonrpc": "2.0", "method": "notifications/progress", "params": { "progress": 50, "total": 100 } } ``` *** ## Жизненный цикл соединения [Заголовок раздела «Жизненный цикл соединения»](#жизненный-цикл-соединения) ### 1. Initialize [Заголовок раздела «1. Initialize»](#1-initialize) ```json // Запрос { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": { "roots": { "listChanged": true } }, "clientInfo": { "name": "Claude Desktop", "version": "1.0.0" } } } // Ответ { "jsonrpc": "2.0", "id": 1, "result": { "protocolVersion": "2024-11-05", "capabilities": { "tools": {}, "resources": { "subscribe": true }, "prompts": {} }, "serverInfo": { "name": "my-server", "version": "1.0.0" } } } ``` ### 2. Initialized (уведомление) [Заголовок раздела «2. Initialized (уведомление)»](#2-initialized-уведомление) ```json { "jsonrpc": "2.0", "method": "notifications/initialized" } ``` *** ## Методы протокола [Заголовок раздела «Методы протокола»](#методы-протокола) ### Основные методы [Заголовок раздела «Основные методы»](#основные-методы) | Метод | Направление | Описание | | ------------- | --------------- | --------------------------- | | `initialize` | Client → Server | Инициализация соединения | | `initialized` | Client → Server | Подтверждение инициализации | | `shutdown` | Client → Server | Завершение работы | ### Tools [Заголовок раздела «Tools»](#tools) | Метод | Описание | | ------------ | ---------------------------- | | `tools/list` | Получить список инструментов | | `tools/call` | Вызвать инструмент | ### Resources [Заголовок раздела «Resources»](#resources) | Метод | Описание | | ----------------------- | ------------------------- | | `resources/list` | Получить список ресурсов | | `resources/read` | Прочитать ресурс | | `resources/subscribe` | Подписаться на обновления | | `resources/unsubscribe` | Отписаться от обновлений | ### Prompts [Заголовок раздела «Prompts»](#prompts) | Метод | Описание | | -------------- | --------------------------- | | `prompts/list` | Получить список промптов | | `prompts/get` | Получить содержимое промпта | ### Уведомления [Заголовок раздела «Уведомления»](#уведомления) | Метод | Направление | Описание | | ---------------------------------- | --------------- | ---------------------- | | `notifications/progress` | Server → Client | Прогресс операции | | `notifications/resources/updated` | Server → Client | Ресурс обновился | | `notifications/tools/list_changed` | Server → Client | Список tools изменился | *** ## Content Types [Заголовок раздела «Content Types»](#content-types) MCP поддерживает несколько типов контента в ответах. ### Text [Заголовок раздела «Text»](#text) ```json { "type": "text", "text": "Результат операции" } ``` ### Image [Заголовок раздела «Image»](#image) ```json { "type": "image", "data": "base64_encoded_image_data", "mimeType": "image/png" } ``` ### Resource [Заголовок раздела «Resource»](#resource) ```json { "type": "resource", "resource": { "uri": "file:///path/to/file.txt", "mimeType": "text/plain", "text": "Содержимое файла" } } ``` *** ## Коды ошибок [Заголовок раздела «Коды ошибок»](#коды-ошибок) ### JSON-RPC стандартные [Заголовок раздела «JSON-RPC стандартные»](#json-rpc-стандартные) | Код | Название | Описание | | -------- | ---------------- | -------------------------- | | `-32700` | Parse error | Невалидный JSON | | `-32600` | Invalid Request | Неверная структура запроса | | `-32601` | Method not found | Метод не существует | | `-32602` | Invalid params | Неверные параметры | | `-32603` | Internal error | Внутренняя ошибка сервера | ### MCP специфичные [Заголовок раздела «MCP специфичные»](#mcp-специфичные) | Код | Название | Описание | | -------- | ------------------ | -------------------- | | `-32000` | Connection closed | Соединение закрыто | | `-32001` | Request timeout | Таймаут запроса | | `-32002` | Resource not found | Ресурс не найден | | `-32003` | Tool not found | Инструмент не найден | | `-32004` | Prompt not found | Промпт не найден | *** ## Capabilities [Заголовок раздела «Capabilities»](#capabilities) При инициализации клиент и сервер обмениваются своими capabilities. ### Server Capabilities [Заголовок раздела «Server Capabilities»](#server-capabilities) ```json { "capabilities": { "tools": {}, "resources": { "subscribe": true, "listChanged": true }, "prompts": { "listChanged": true }, "logging": {} } } ``` ### Client Capabilities [Заголовок раздела «Client Capabilities»](#client-capabilities) ```json { "capabilities": { "roots": { "listChanged": true }, "sampling": {} } } ``` *** ## Версии протокола [Заголовок раздела «Версии протокола»](#версии-протокола) | Версия | Дата | Основные изменения | | ------------ | -------- | ----------------------------- | | `2024-11-05` | Nov 2024 | Первая публичная версия | | `2025-03-xx` | Mar 2025 | OAuth 2.1 обязателен для HTTP | | `2025-06-xx` | Jun 2025 | Streamable HTTP вместо SSE | *** ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) Спецификация MCP [modelcontextprotocol.io](https://modelcontextprotocol.io/specification) — официальная документация JSON-RPC 2.0 [jsonrpc.org](https://www.jsonrpc.org/specification) — спецификация протокола Быстрый старт [5 минут](/reference/quickstart/) — создайте первый сервер Примеры кода [25+ примеров](/reference/examples/) — готовый код
# Быстрый старт
> Запустите первый MCP сервер за 5 минут — пошаговое руководство для начинающих
Это руководство поможет вам создать и запустить первый MCP сервер за **5 минут**. Выберите ваш язык программирования и следуйте инструкциям. ## Что вы создадите [Заголовок раздела «Что вы создадите»](#что-вы-создадите) Простой MCP сервер с двумя инструментами: * `add` — сложение двух чисел * `greet` — приветствие по имени - Python 1. **Создайте папку проекта** ```bash mkdir my-mcp-server && cd my-mcp-server ``` 2. **Установите зависимости** ```bash pip install mcp ``` 3. **Создайте файл `server.py`** ```python from mcp.server.fastmcp import FastMCP # Создаём сервер mcp = FastMCP("my-first-server") @mcp.tool() def add(a: int, b: int) -> int: """Сложить два числа""" return a + b @mcp.tool() def greet(name: str) -> str: """Поприветствовать по имени""" return f"Привет, {name}!" @mcp.resource("info://about") def get_about() -> str: """Информация о сервере""" return "Мой первый MCP сервер!" if __name__ == "__main__": mcp.run(transport="stdio") ``` 4. **Запустите сервер** ```bash python server.py ``` 5. **Протестируйте с MCP Inspector** ```bash npx @modelcontextprotocol/inspector python server.py ``` Откройте в браузере. - TypeScript 1. **Создайте папку проекта** ```bash mkdir my-mcp-server && cd my-mcp-server npm init -y ``` 2. **Установите зависимости** ```bash npm install @modelcontextprotocol/sdk npm install -D typescript ts-node @types/node ``` 3. **Создайте файл `server.ts`** ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { ListToolsRequestSchema, CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; const server = new McpServer({ name: "my-first-server", version: "1.0.0", }); // Регистрируем инструменты server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "add", description: "Сложить два числа", inputSchema: { type: "object" as const, properties: { a: { type: "number", description: "Первое число" }, b: { type: "number", description: "Второе число" }, }, required: ["a", "b"], }, }, { name: "greet", description: "Поприветствовать по имени", inputSchema: { type: "object" as const, properties: { name: { type: "string", description: "Имя" }, }, required: ["name"], }, }, ], })); // Обрабатываем вызовы server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { case "add": { const a = request.params.arguments?.a as number; const b = request.params.arguments?.b as number; return { content: [{ type: "text", text: String(a + b) }] }; } case "greet": { const name = request.params.arguments?.name as string; return { content: [{ type: "text", text: `Привет, ${name}!` }] }; } default: throw new Error(`Unknown tool: ${request.params.name}`); } }); // Запускаем сервер const transport = new StdioServerTransport(); await server.connect(transport); ``` 4. **Добавьте скрипт в `package.json`** ```json { "type": "module", "scripts": { "start": "ts-node server.ts" } } ``` 5. **Запустите и протестируйте** ```bash npx @modelcontextprotocol/inspector npm start ``` - Go 1. **Создайте проект** ```bash mkdir my-mcp-server && cd my-mcp-server go mod init my-mcp-server go get github.com/mark3labs/mcp-go ``` 2. **Создайте файл `main.go`** ```go package main import ( "context" "fmt" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) func main() { s := server.NewMCPServer( "my-first-server", "1.0.0", ) // Инструмент сложения addTool := mcp.NewTool("add", mcp.WithDescription("Сложить два числа"), mcp.WithNumber("a", mcp.Required(), mcp.Description("Первое число")), mcp.WithNumber("b", mcp.Required(), mcp.Description("Второе число")), ) s.AddTool(addTool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { a := req.Params.Arguments["a"].(float64) b := req.Params.Arguments["b"].(float64) return mcp.NewToolResultText(fmt.Sprintf("%.0f", a+b)), nil }) // Инструмент приветствия greetTool := mcp.NewTool("greet", mcp.WithDescription("Поприветствовать по имени"), mcp.WithString("name", mcp.Required(), mcp.Description("Имя")), ) s.AddTool(greetTool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { name := req.Params.Arguments["name"].(string) return mcp.NewToolResultText(fmt.Sprintf("Привет, %s!", name)), nil }) // Запуск if err := server.ServeStdio(s); err != nil { panic(err) } } ``` 3. **Запустите** ```bash go run main.go ``` 4. **Протестируйте** ```bash npx @modelcontextprotocol/inspector go run main.go ``` ## Что происходит при запуске [Заголовок раздела «Что происходит при запуске»](#что-происходит-при-запуске) ## Проверка работы [Заголовок раздела «Проверка работы»](#проверка-работы) После запуска MCP Inspector: 1. Откройте 2. Нажмите **Connect** 3. Перейдите в раздел **Tools** 4. Выберите инструмент `add` 5. Введите параметры: `a = 5`, `b = 3` 6. Нажмите **Call Tool** 7. Результат: `8` ## Следующие шаги [Заголовок раздела «Следующие шаги»](#следующие-шаги) Примеры кода [25+ готовых примеров](/reference/examples/) для разных задач Инструменты [MCP Inspector и CLI](/reference/tools/) — полное руководство Настройка клиентов [Claude Desktop, VS Code, Cursor](/reference/clients/) Production Checklist [14 пунктов](/reference/checklist/) перед деплоем ## Частые вопросы новичков [Заголовок раздела «Частые вопросы новичков»](#частые-вопросы-новичков) ### Что такое `stdio` транспорт? [Заголовок раздела «Что такое stdio транспорт?»](#что-такое-stdio-транспорт) Stdio (standard input/output) — способ коммуникации через stdin/stdout. Клиент запускает ваш сервер как subprocess и общается через потоки ввода/вывода. Это самый простой и надёжный способ для локальной разработки. ### Почему `npx` перед командой? [Заголовок раздела «Почему npx перед командой?»](#почему-npx-перед-командой) `npx @modelcontextprotocol/inspector` запускает MCP Inspector без установки. Inspector — это веб-интерфейс для тестирования вашего сервера. ### Как добавить свой инструмент? [Заголовок раздела «Как добавить свой инструмент?»](#как-добавить-свой-инструмент) В Python (FastMCP): ```python @mcp.tool() def my_tool(param1: str, param2: int = 10) -> str: """Описание инструмента (будет видно в Claude)""" return f"Результат: {param1}, {param2}" ``` Описание в docstring автоматически становится description инструмента. ### Как подключить к Claude Desktop? [Заголовок раздела «Как подключить к Claude Desktop?»](#как-подключить-к-claude-desktop) См. раздел [Настройка клиентов](/reference/clients/) — там пошаговые инструкции для всех платформ.
# Инструменты разработчика
> MCP Inspector, CLI и инструменты отладки для разработки MCP серверов
## MCP Inspector [Заголовок раздела «MCP Inspector»](#mcp-inspector) **MCP Inspector** — официальный веб-интерфейс для тестирования и отладки MCP серверов. Позволяет интерактивно вызывать tools, читать resources и тестировать prompts. ### Быстрый запуск [Заголовок раздела «Быстрый запуск»](#быстрый-запуск) ```bash npx @modelcontextprotocol/inspector <команда_запуска_сервера> ``` * Python ```bash npx @modelcontextprotocol/inspector python server.py ``` * TypeScript ```bash npx @modelcontextprotocol/inspector npm run start ``` * Go ```bash npx @modelcontextprotocol/inspector go run main.go ``` * Docker ```bash npx @modelcontextprotocol/inspector docker run -i my-mcp-server ``` После запуска откройте **** в браузере. ### Порты Inspector [Заголовок раздела «Порты Inspector»](#порты-inspector) | Порт | Назначение | | ------ | ------------------------ | | `6274` | Веб-интерфейс Inspector | | `6277` | Внутренний proxy для MCP | ### Интерфейс Inspector [Заголовок раздела «Интерфейс Inspector»](#интерфейс-inspector) ### Вкладки Inspector [Заголовок раздела «Вкладки Inspector»](#вкладки-inspector) #### Tools [Заголовок раздела «Tools»](#tools) * Список всех зарегистрированных инструментов * Форма для ввода параметров * Кнопка вызова и отображение результата #### Resources [Заголовок раздела «Resources»](#resources) * Список доступных ресурсов (URI) * Чтение содержимого ресурса * Подписка на обновления #### Prompts [Заголовок раздела «Prompts»](#prompts) * Список prompt-шаблонов * Заполнение аргументов * Получение сформированного промпта #### Logs [Заголовок раздела «Logs»](#logs) * JSON-RPC сообщения в реальном времени * Ошибки и предупреждения * Debugging информация ### CLI режим Inspector [Заголовок раздела «CLI режим Inspector»](#cli-режим-inspector) Inspector можно использовать без веб-интерфейса: ```bash # Только проверка подключения npx @modelcontextprotocol/inspector --cli python server.py # Вызов конкретного tool echo '{"method":"tools/call","params":{"name":"add","arguments":{"a":5,"b":3}}}' | \ npx @modelcontextprotocol/inspector --cli python server.py ``` *** ## mcp CLI (Python) [Заголовок раздела «mcp CLI (Python)»](#mcp-cli-python) Пакет `mcp` включает CLI для разработки: ### Установка [Заголовок раздела «Установка»](#установка) ```bash pip install mcp ``` ### Команды [Заголовок раздела «Команды»](#команды) ```bash # Запуск сервера mcp run server.py # Запуск с отладкой mcp run server.py --debug # Информация о сервере mcp info server.py ``` ### Пример вывода `mcp info` [Заголовок раздела «Пример вывода mcp info»](#пример-вывода-mcp-info) ```plaintext Server: my-first-server Version: 1.0.0 Tools (2): - add: Сложить два числа - greet: Поприветствовать по имени Resources (1): - info://about Prompts (0): (none) ``` *** ## Отладка и логирование [Заголовок раздела «Отладка и логирование»](#отладка-и-логирование) ### Логирование в stderr [Заголовок раздела «Логирование в stderr»](#логирование-в-stderr) Важно! MCP использует stdout для JSON-RPC коммуникации. **Все логи должны идти в stderr**, иначе сервер сломается! * Python ```python import sys import logging # Настройка логгера на stderr logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', stream=sys.stderr # Важно! ) logger = logging.getLogger(__name__) @mcp.tool() def my_tool(param: str) -> str: logger.debug(f"Вызван my_tool с param={param}") return f"Результат: {param}" ``` * TypeScript ```typescript // Логирование в stderr function log(message: string) { process.stderr.write(`[DEBUG] ${new Date().toISOString()} ${message}\n`); } // Использование server.setRequestHandler(CallToolRequestSchema, async (request) => { log(`Tool called: ${request.params.name}`); // ... }); ``` * Go ```go import ( "log" "os" ) func init() { // Логи в stderr log.SetOutput(os.Stderr) } func main() { log.Println("Server starting...") // ... } ``` ### Уровни логирования [Заголовок раздела «Уровни логирования»](#уровни-логирования) ```python import logging # Для разработки logging.basicConfig(level=logging.DEBUG, stream=sys.stderr) # Для production logging.basicConfig(level=logging.WARNING, stream=sys.stderr) ``` | Уровень | Когда использовать | | ---------- | -------------------------------------- | | `DEBUG` | Детальная информация для отладки | | `INFO` | Подтверждение нормальной работы | | `WARNING` | Потенциальные проблемы | | `ERROR` | Ошибки, сервер продолжает работать | | `CRITICAL` | Критические ошибки, сервер остановится | *** ## Тестирование серверов [Заголовок раздела «Тестирование серверов»](#тестирование-серверов) ### Unit-тесты (Python) [Заголовок раздела «Unit-тесты (Python)»](#unit-тесты-python) ```python import pytest from mcp.server.fastmcp import FastMCP # Создаём тестовый сервер mcp = FastMCP("test-server") @mcp.tool() def add(a: int, b: int) -> int: """Сложить два числа""" return a + b @mcp.tool() def divide(a: float, b: float) -> float: """Разделить a на b""" if b == 0: raise ValueError("Деление на ноль") return a / b # Тесты class TestTools: def test_add(self): result = add(5, 3) assert result == 8 def test_add_negative(self): result = add(-5, 3) assert result == -2 def test_divide(self): result = divide(10, 2) assert result == 5.0 def test_divide_by_zero(self): with pytest.raises(ValueError, match="Деление на ноль"): divide(10, 0) ``` ### Integration-тесты (TypeScript) [Заголовок раздела «Integration-тесты (TypeScript)»](#integration-тесты-typescript) ```typescript import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { spawn, ChildProcess } from 'child_process'; describe('MCP Server Integration', () => { let serverProcess: ChildProcess; beforeAll(async () => { serverProcess = spawn('npm', ['run', 'start'], { stdio: ['pipe', 'pipe', 'pipe'] }); // Ждём запуска await new Promise(resolve => setTimeout(resolve, 1000)); }); afterAll(() => { serverProcess.kill(); }); it('should respond to initialize', async () => { const request = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'test', version: '1.0.0' } } }); serverProcess.stdin!.write(request + '\n'); const response = await new Promise(resolve => { serverProcess.stdout!.once('data', data => resolve(data.toString())); }); const parsed = JSON.parse(response); expect(parsed.result).toBeDefined(); expect(parsed.result.serverInfo).toBeDefined(); }); }); ``` ### Запуск тестов [Заголовок раздела «Запуск тестов»](#запуск-тестов) ```bash # Python pytest tests/ -v # TypeScript npm test # С coverage pytest tests/ --cov=src --cov-report=html npm test -- --coverage ``` *** ## JSON-RPC отладка [Заголовок раздела «JSON-RPC отладка»](#json-rpc-отладка) ### Ручное тестирование через stdin [Заголовок раздела «Ручное тестирование через stdin»](#ручное-тестирование-через-stdin) ```bash # Запустите сервер python server.py # В другом терминале отправьте запрос echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | python server.py ``` ### Пример JSON-RPC диалога [Заголовок раздела «Пример JSON-RPC диалога»](#пример-json-rpc-диалога) ```json // Запрос: initialize {"jsonrpc":"2.0","id":1,"method":"initialize","params":{ "protocolVersion":"2024-11-05", "capabilities":{}, "clientInfo":{"name":"test","version":"1.0"} }} // Ответ {"jsonrpc":"2.0","id":1,"result":{ "protocolVersion":"2024-11-05", "serverInfo":{"name":"my-server","version":"1.0.0"}, "capabilities":{"tools":{}} }} // Запрос: tools/list {"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}} // Ответ {"jsonrpc":"2.0","id":2,"result":{"tools":[ {"name":"add","description":"Сложить два числа","inputSchema":{...}} ]}} // Запрос: tools/call {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{ "name":"add", "arguments":{"a":5,"b":3} }} // Ответ {"jsonrpc":"2.0","id":3,"result":{ "content":[{"type":"text","text":"8"}] }} ``` *** ## Полезные скрипты [Заголовок раздела «Полезные скрипты»](#полезные-скрипты) ### Скрипт проверки здоровья сервера [Заголовок раздела «Скрипт проверки здоровья сервера»](#скрипт-проверки-здоровья-сервера) ```python #!/usr/bin/env python3 """health_check.py - Проверка работоспособности MCP сервера""" import subprocess import json import sys def check_server(command: list[str]) -> bool: """Проверяет, что сервер отвечает на initialize""" init_request = json.dumps({ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "health-check", "version": "1.0"} } }) + "\n" try: result = subprocess.run( command, input=init_request, capture_output=True, text=True, timeout=5 ) response = json.loads(result.stdout.strip()) if "result" in response and "serverInfo" in response["result"]: print(f"✅ Server OK: {response['result']['serverInfo']}") return True else: print(f"❌ Invalid response: {response}") return False except subprocess.TimeoutExpired: print("❌ Server timeout") return False except json.JSONDecodeError as e: print(f"❌ Invalid JSON: {e}") return False except Exception as e: print(f"❌ Error: {e}") return False if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: python health_check.py ") print("Example: python health_check.py python server.py") sys.exit(1) command = sys.argv[1:] success = check_server(command) sys.exit(0 if success else 1) ``` ### Использование [Заголовок раздела «Использование»](#использование) ```bash # Проверка Python сервера python health_check.py python server.py # Проверка Docker контейнера python health_check.py docker run -i my-mcp-server # В CI/CD python health_check.py python server.py && echo "Deploy OK" ``` *** ## Docker для разработки [Заголовок раздела «Docker для разработки»](#docker-для-разработки) ### docker-compose для отладки [Заголовок раздела «docker-compose для отладки»](#docker-compose-для-отладки) docker-compose.dev.yml ```yaml version: '3.8' services: mcp-server: build: context: . dockerfile: Dockerfile.dev volumes: - .:/app - /app/node_modules # Для Node.js environment: - DEBUG=1 - LOG_LEVEL=debug stdin_open: true tty: true inspector: image: node:20 command: npx @modelcontextprotocol/inspector nc mcp-server 3000 ports: - "6274:6274" depends_on: - mcp-server ``` ### Dockerfile.dev (Python) [Заголовок раздела «Dockerfile.dev (Python)»](#dockerfiledev-python) ```dockerfile FROM python:3.11-slim WORKDIR /app # Установка зависимостей для hot-reload RUN pip install watchdog COPY requirements.txt . RUN pip install -r requirements.txt # Копируем код COPY . . # Hot-reload с watchmedo CMD ["watchmedo", "auto-restart", "--patterns=*.py", "--recursive", "--", "python", "server.py"] ``` *** ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) MCP Inspector [GitHub репозиторий](https://github.com/modelcontextprotocol/inspector) — исходный код и документация Python SDK [mcp на PyPI](https://pypi.org/project/mcp/) — включает CLI инструменты Troubleshooting [Решение проблем](/reference/troubleshooting/) — типичные ошибки и их решения Production Checklist [Чеклист](/reference/checklist/) — подготовка к production
# Решение проблем
> Типичные ошибки MCP серверов и их решения — коды ошибок, отладка, FAQ
## Коды ошибок JSON-RPC [Заголовок раздела «Коды ошибок JSON-RPC»](#коды-ошибок-json-rpc) MCP использует стандартные коды ошибок JSON-RPC 2.0 плюс специфичные для протокола. ### Стандартные ошибки JSON-RPC [Заголовок раздела «Стандартные ошибки JSON-RPC»](#стандартные-ошибки-json-rpc) | Код | Название | Описание | | -------- | ---------------- | -------------------------------- | | `-32700` | Parse error | Невалидный JSON | | `-32600` | Invalid Request | Запрос не соответствует JSON-RPC | | `-32601` | Method not found | Метод не существует | | `-32602` | Invalid params | Неверные параметры | | `-32603` | Internal error | Внутренняя ошибка сервера | ### Ошибки MCP [Заголовок раздела «Ошибки MCP»](#ошибки-mcp) | Код | Название | Описание | | -------- | ------------------ | --------------------------------------- | | `-32000` | Connection closed | Соединение закрыто (часто из-за stdout) | | `-32001` | Request timeout | Таймаут запроса | | `-32002` | Resource not found | Ресурс не найден | | `-32003` | Tool not found | Инструмент не найден | | `-32004` | Prompt not found | Промпт не найден | *** ## Ошибка -32000: Connection closed [Заголовок раздела «Ошибка -32000: Connection closed»](#ошибка--32000-connection-closed) **Симптомы:** Сервер запускается, но сразу отключается. В логах: “Connection closed unexpectedly”. **Причина:** Вывод в stdout нарушает JSON-RPC протокол. ### Решение [Заголовок раздела «Решение»](#решение) * Python ```python import sys import logging # ❌ НЕПРАВИЛЬНО print("Debug message") # Ломает JSON-RPC! # ✅ ПРАВИЛЬНО logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) logger = logging.getLogger(__name__) logger.debug("Debug message") # Или напрямую print("Debug message", file=sys.stderr) ``` * TypeScript ```typescript // ❌ НЕПРАВИЛЬНО console.log("Debug message"); // Идёт в stdout! // ✅ ПРАВИЛЬНО console.error("Debug message"); // Идёт в stderr // Или создайте helper function log(msg: string) { process.stderr.write(`[DEBUG] ${msg}\n`); } ``` * Go ```go import ( "log" "os" ) // ❌ НЕПРАВИЛЬНО fmt.Println("Debug message") // ✅ ПРАВИЛЬНО log.SetOutput(os.Stderr) log.Println("Debug message") ``` Золотое правило **stdout** = только JSON-RPC сообщения **stderr** = логи, отладка, ошибки *** ## Ошибка -32002: Resource not found [Заголовок раздела «Ошибка -32002: Resource not found»](#ошибка--32002-resource-not-found) **Симптомы:** `{"error": {"code": -32002, "message": "Resource not found: xxx"}}` **Причина:** Клиент запрашивает ресурс, который не зарегистрирован на сервере. ### Решение [Заголовок раздела «Решение»](#решение-1) 1. Проверьте, что ресурс зарегистрирован: ```python @mcp.resource("config://settings") # Точный URI def get_settings(): return {"theme": "dark"} ``` 2. Проверьте URI — он должен точно совпадать: ```python # ❌ Зарегистрировано @mcp.resource("config://settings") # ❌ Запрашивается (не совпадает) # "config://setting" — нет 's' в конце # "config:/settings" — одна косая черта # "settings" — нет схемы # ✅ Правильный запрос # "config://settings" ``` 3. Проверьте список ресурсов в MCP Inspector: * Откройте вкладку Resources * Убедитесь, что нужный ресурс есть в списке *** ## Ошибка -32003: Tool not found [Заголовок раздела «Ошибка -32003: Tool not found»](#ошибка--32003-tool-not-found) **Симптомы:** `{"error": {"code": -32003, "message": "Tool not found: xxx"}}` ### Решение [Заголовок раздела «Решение»](#решение-2) 1. Проверьте регистрацию инструмента: ```python @mcp.tool() # Декоратор обязателен def my_tool(param: str) -> str: """Описание инструмента""" return f"Result: {param}" ``` 2. Проверьте имя инструмента: ```python # Имя берётся из имени функции @mcp.tool() def calculate_sum(a: int, b: int) -> int: # Имя: "calculate_sum" return a + b # Или можно задать явно @mcp.tool(name="sum") # Имя: "sum" def calculate_sum(a: int, b: int) -> int: return a + b ``` 3. Перезапустите сервер после изменений *** ## Tools не видны в Claude Desktop [Заголовок раздела «Tools не видны в Claude Desktop»](#tools-не-видны-в-claude-desktop) **Симптомы:** Сервер запускается, но инструменты не появляются в списке. ### Чеклист [Заголовок раздела «Чеклист»](#чеклист) 1. **Проверьте конфигурацию** ```bash # macOS cat ~/Library/Application\ Support/Claude/claude_desktop_config.json ``` JSON должен быть валидным (проверьте запятые и кавычки). 2. **Проверьте пути** Используйте абсолютные пути: ```json { "mcpServers": { "my-server": { "command": "/usr/bin/python3", "args": ["/Users/me/project/server.py"] } } } ``` 3. **Проверьте запуск вручную** ```bash python /Users/me/project/server.py ``` Если есть ошибки — исправьте их. 4. **Проверьте логи Claude** ```bash # macOS tail -f ~/Library/Logs/Claude/mcp*.log ``` 5. **Полностью перезапустите Claude** * Закройте все окна * Cmd+Q (macOS) или завершите процесс * Запустите заново ### Типичные ошибки конфигурации [Заголовок раздела «Типичные ошибки конфигурации»](#типичные-ошибки-конфигурации) ```json // ❌ Неправильно: относительный путь { "mcpServers": { "my-server": { "command": "python", "args": ["server.py"] } } } // ✅ Правильно: абсолютный путь { "mcpServers": { "my-server": { "command": "/usr/bin/python3", "args": ["/Users/me/project/server.py"] } } } ``` ```json // ❌ Неправильно: лишняя запятая { "mcpServers": { "my-server": { "command": "python", "args": ["server.py"], // <-- Лишняя запятая! } } } // ✅ Правильно { "mcpServers": { "my-server": { "command": "python", "args": ["server.py"] } } } ``` *** ## Ошибки подключения к базе данных [Заголовок раздела «Ошибки подключения к базе данных»](#ошибки-подключения-к-базе-данных) ### PostgreSQL [Заголовок раздела «PostgreSQL»](#postgresql) ```plaintext Error: connection refused Error: password authentication failed Error: database "xxx" does not exist ``` **Решения:** ```python import os from mcp.server.fastmcp import FastMCP mcp = FastMCP("db-server") # 1. Проверьте DATABASE_URL DATABASE_URL = os.getenv("DATABASE_URL") if not DATABASE_URL: raise ValueError("DATABASE_URL не установлена") # 2. Проверьте формат URL # postgresql://user:password@host:port/database # postgresql://postgres:secret@localhost:5432/mydb # 3. Используйте connection pool import asyncpg pool = None @mcp.tool() async def query_db(sql: str) -> str: global pool if pool is None: pool = await asyncpg.create_pool(DATABASE_URL) async with pool.acquire() as conn: result = await conn.fetch(sql) return str(result) ``` ### SQLite [Заголовок раздела «SQLite»](#sqlite) ```plaintext Error: unable to open database file Error: database is locked ``` **Решения:** ```python import sqlite3 import os # 1. Проверьте путь к файлу db_path = "/path/to/database.db" if not os.path.exists(db_path): raise FileNotFoundError(f"БД не найдена: {db_path}") # 2. Используйте правильные флаги conn = sqlite3.connect( db_path, check_same_thread=False, # Для многопоточности timeout=30.0 # Таймаут блокировки ) # 3. Для read-only доступа conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) ``` *** ## Ошибки с виртуальным окружением Python [Заголовок раздела «Ошибки с виртуальным окружением Python»](#ошибки-с-виртуальным-окружением-python) **Симптомы:** `ModuleNotFoundError: No module named 'mcp'` ### Решение [Заголовок раздела «Решение»](#решение-3) ```json { "mcpServers": { "my-server": { "command": "/path/to/venv/bin/python", "args": ["/path/to/server.py"] } } } ``` Или используйте `uvx`: ```json { "mcpServers": { "my-server": { "command": "uvx", "args": ["--from", "mcp", "mcp", "run", "server.py"] } } } ``` *** ## Ошибки TypeScript [Заголовок раздела «Ошибки TypeScript»](#ошибки-typescript) ### ”Cannot find module" [Заголовок раздела «”Cannot find module"»](#cannot-find-module) ```bash # Скомпилируйте TypeScript npx tsc # Или используйте ts-node npm install -D ts-node typescript @types/node ``` ```json { "mcpServers": { "my-server": { "command": "npx", "args": ["ts-node", "/path/to/server.ts"] } } } ``` ### "Top-level await” [Заголовок раздела «"Top-level await”»](#top-level-await) tsconfig.json ```json { "compilerOptions": { "module": "ESNext", "moduleResolution": "Node", "target": "ES2022" } } ``` package.json ```json { "type": "module" } ``` *** ## Таймауты и производительность [Заголовок раздела «Таймауты и производительность»](#таймауты-и-производительность) ### Симптомы [Заголовок раздела «Симптомы»](#симптомы) * Инструменты работают медленно * `Request timeout` ошибки * Claude “зависает” при вызове tool ### Решения [Заголовок раздела «Решения»](#решения) 1. **Добавьте прогресс-уведомления:** ```python @mcp.tool() async def long_operation(data: str) -> str: """Долгая операция""" # Уведомляем о прогрессе await mcp.request_context.session.send_progress( progress=0, total=100, message="Начинаю обработку..." ) # Делаем работу result = await process_data(data) await mcp.request_context.session.send_progress( progress=100, total=100, message="Готово!" ) return result ``` 2. **Используйте асинхронные операции:** ```python import asyncio import aiohttp @mcp.tool() async def fetch_data(url: str) -> str: """Получить данные из URL""" async with aiohttp.ClientSession() as session: async with session.get(url, timeout=30) as response: return await response.text() ``` 3. **Ограничьте размер ответа:** ```python @mcp.tool() def get_file(path: str) -> str: """Читает файл (макс 100KB)""" MAX_SIZE = 100 * 1024 with open(path, 'r') as f: content = f.read(MAX_SIZE) if len(content) == MAX_SIZE: content += "\n... (файл обрезан)" return content ``` *** ## Отладка с MCP Inspector [Заголовок раздела «Отладка с MCP Inspector»](#отладка-с-mcp-inspector) ### Запуск Inspector [Заголовок раздела «Запуск Inspector»](#запуск-inspector) ```bash npx @modelcontextprotocol/inspector python server.py ``` ### Что проверять [Заголовок раздела «Что проверять»](#что-проверять) 1. **Вкладка Tools** — все инструменты должны быть в списке 2. **Вызовите tool** — проверьте, что возвращается результат 3. **Вкладка Logs** — смотрите JSON-RPC сообщения ### CLI режим для автоматизации [Заголовок раздела «CLI режим для автоматизации»](#cli-режим-для-автоматизации) ```bash # Проверка initialize echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | python server.py # Список tools echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | python server.py ``` *** ## FAQ [Заголовок раздела «FAQ»](#faq) ### Как узнать версию MCP SDK? [Заголовок раздела «Как узнать версию MCP SDK?»](#как-узнать-версию-mcp-sdk) ```bash # Python pip show mcp # Node.js npm list @modelcontextprotocol/sdk ``` ### Как обновить SDK? [Заголовок раздела «Как обновить SDK?»](#как-обновить-sdk) ```bash # Python pip install --upgrade mcp # Node.js npm update @modelcontextprotocol/sdk ``` ### Можно ли использовать несколько серверов одновременно? [Заголовок раздела «Можно ли использовать несколько серверов одновременно?»](#можно-ли-использовать-несколько-серверов-одновременно) Да, добавьте несколько записей в `mcpServers`: ```json { "mcpServers": { "server1": { "command": "python", "args": ["server1.py"] }, "server2": { "command": "python", "args": ["server2.py"] } } } ``` ### Как передать секреты? [Заголовок раздела «Как передать секреты?»](#как-передать-секреты) Используйте переменные окружения: ```json { "mcpServers": { "my-server": { "command": "python", "args": ["server.py"], "env": { "API_KEY": "xxx", "DATABASE_URL": "postgresql://..." } } } } ``` ### Где хранятся логи? [Заголовок раздела «Где хранятся логи?»](#где-хранятся-логи) | Клиент | Логи | | ------------------------ | -------------------------------- | | Claude Desktop (macOS) | `~/Library/Logs/Claude/mcp*.log` | | Claude Desktop (Windows) | `%APPDATA%\Claude\logs\` | | VS Code | View → Output → MCP | | Cursor | Developer Tools → Console | *** ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) MCP Inspector [Инструменты разработчика](/reference/tools/) — подробнее о Inspector Production Checklist [Чеклист](/reference/checklist/) — подготовка к production Примеры кода [25+ примеров](/reference/examples/) — работающий код Настройка клиентов [Клиенты](/reference/clients/) — Claude, VS Code, Cursor
# C# SDK (.NET)
> Создание MCP серверов на C# с официальным Microsoft SDK
Официальный C# SDK обеспечивает полную интеграцию MCP протокола с .NET экосистемой, включая ASP.NET Core и dependency injection с атрибутами для регистрации. ## Установка [Заголовок раздела «Установка»](#установка) * Official SDK Terminal ```bash dotnet add package ModelContextProtocol ``` * McpDotNet Terminal ```bash dotnet add package McpDotNet ``` **Требования:** * .NET 8.0+ * Visual Studio 2022 или VS Code ## Базовый сервер [Заголовок раздела «Базовый сервер»](#базовый-сервер) Program.cs ```csharp using ModelContextProtocol; using ModelContextProtocol.Server; var builder = McpServerBuilder.Create("my-server", "1.0.0"); builder.WithDescription("MCP сервер на C#"); var server = builder.Build(); await server.RunStdioAsync(); ``` ## Инструменты (Tools) [Заголовок раздела «Инструменты (Tools)»](#инструменты-tools) ### Базовый инструмент [Заголовок раздела «Базовый инструмент»](#базовый-инструмент) Tools/CalculatorTools.cs ```csharp using ModelContextProtocol.Server; using System.ComponentModel; [McpServerToolType] // Атрибут класса для автоматического обнаружения public class CalculatorTools { [McpServerTool("add"), Description("Сложение двух чисел")] public static double Add(double a, double b) { return a + b; } [McpServerTool("divide"), Description("Деление чисел")] public static double Divide(double a, double b) { if (b == 0) throw new McpException(McpErrorCode.InvalidParams, "Деление на ноль"); return a / b; } } ``` ### Регистрация инструментов [Заголовок раздела «Регистрация инструментов»](#регистрация-инструментов) Program.cs ```csharp var builder = McpServerBuilder.Create("calculator", "1.0.0"); builder.AddTools(); var server = builder.Build(); await server.RunStdioAsync(); ``` ### Async инструмент [Заголовок раздела «Async инструмент»](#async-инструмент) Tools/HttpTools.cs ```csharp public class HttpTools { private readonly HttpClient _client; public HttpTools(HttpClient client) { _client = client; } [McpTool("fetch_url", "Загрузка содержимого URL")] public async Task FetchUrlAsync( [McpParameter("URL для загрузки")] string url) { var response = await _client.GetAsync(url); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } } ``` ### Инструмент с файловой системой [Заголовок раздела «Инструмент с файловой системой»](#инструмент-с-файловой-системой) Tools/FileTools.cs ```csharp public class FileTools { private readonly string _allowedPath; public FileTools(IConfiguration config) { _allowedPath = config["AllowedPath"] ?? "/allowed"; } [McpTool("read_file", "Чтение файла")] public async Task ReadFileAsync( [McpParameter("Путь к файлу")] string path) { var fullPath = Path.GetFullPath(path); if (!fullPath.StartsWith(_allowedPath)) throw new McpException(McpErrorCode.Forbidden, "Доступ запрещён"); if (!File.Exists(fullPath)) throw new McpException(McpErrorCode.NotFound, $"Файл не найден: {path}"); return await File.ReadAllTextAsync(fullPath); } [McpTool("list_files", "Список файлов в директории")] public string[] ListFiles( [McpParameter("Путь к директории")] string path, [McpParameter("Паттерн поиска", Required = false)] string? pattern = "*") { var fullPath = Path.GetFullPath(path); if (!fullPath.StartsWith(_allowedPath)) throw new McpException(McpErrorCode.Forbidden, "Доступ запрещён"); return Directory.GetFiles(fullPath, pattern ?? "*") .Select(Path.GetFileName) .ToArray()!; } } ``` ### Инструмент с базой данных [Заголовок раздела «Инструмент с базой данных»](#инструмент-с-базой-данных) Tools/DatabaseTools.cs ```csharp using Microsoft.EntityFrameworkCore; public class DatabaseTools { private readonly AppDbContext _db; public DatabaseTools(AppDbContext db) { _db = db; } [McpTool("get_users", "Получение списка пользователей")] public async Task