This is the full 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 отображаются в списке Отладка Если tools не появляются: 1. Проверьте логи: `~/Library/Logs/Claude/mcp*.log` (macOS) 2. Убедитесь, что путь к серверу абсолютный 3. Проверьте, что сервер запускается вручную без ошибок *** ## VS Code (Copilot) [Заголовок раздела «VS Code (Copilot)»](#vs-code-copilot) VS Code с GitHub Copilot поддерживает MCP через файл конфигурации. ### Расположение конфигурации [Заголовок раздела «Расположение конфигурации»](#расположение-конфигурации-1) * .vscode/ * **mcp.json** ← конфигурация MCP * settings.json Или глобально: `~/.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** ← конфигурация MCP Глобально: `~/.cursor/mcp.json` ### Формат конфигурации [Заголовок раздела «Формат конфигурации»](#формат-конфигурации-2) ```json { "mcpServers": { "имя-сервера": { "command": "команда", "args": ["аргументы"], "env": {} } } } ``` Заметка Формат идентичен Claude Desktop — можно скопировать конфигурацию. ### Примеры [Заголовок раздела «Примеры»](#примеры-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 | Промпт не найден | Совет Подробнее об ошибках и их решениях см. [Troubleshooting](/reference/troubleshooting/). *** ## 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` Совет MCP Inspector — ваш лучший друг при разработке! Используйте его для отладки перед интеграцией с Claude. ## Следующие шаги [Заголовок раздела «Следующие шаги»](#следующие-шаги) Примеры кода [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 | Смена портов Если порты заняты, используйте переменные окружения: ```bash CLIENT_PORT=8080 SERVER_PORT=8081 npx @modelcontextprotocol/inspector python server.py ``` ### Интерфейс 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 GetUsersAsync( [McpParameter("Лимит", Required = false)] int limit = 10, [McpParameter("Смещение", Required = false)] int offset = 0) { return await _db.Users .Skip(offset) .Take(limit) .Select(u => new { u.Id, u.Name, u.Email }) .ToArrayAsync(); } [McpTool("find_user", "Поиск пользователя")] public async Task FindUserAsync( [McpParameter("ID пользователя")] int id) { var user = await _db.Users.FindAsync(id); if (user == null) throw new McpException(McpErrorCode.NotFound, "Пользователь не найден"); return new { user.Id, user.Name, user.Email, user.CreatedAt }; } } ``` ## Ресурсы (Resources) [Заголовок раздела «Ресурсы (Resources)»](#ресурсы-resources) Resources/ConfigResources.cs ```csharp public class ConfigResources { private readonly IConfiguration _config; public ConfigResources(IConfiguration config) { _config = config; } [McpResource("config://app", "App Configuration", "Конфигурация приложения")] public string GetAppConfig() { var config = new { Version = _config["Version"], Environment = _config["Environment"], Features = _config.GetSection("Features").Get() }; return JsonSerializer.Serialize(config); } [McpResource("config://database", "Database Configuration", "Конфигурация БД")] public string GetDatabaseConfig() { return JsonSerializer.Serialize(new { Provider = _config["Database:Provider"], Host = _config["Database:Host"], Database = _config["Database:Name"] }); } } ``` ### Динамический ресурс [Заголовок раздела «Динамический ресурс»](#динамический-ресурс) Resources/FileResources.cs ```csharp public class FileResources { [McpResource("file://{path}", "File Reader", "Чтение файлов")] public async Task ReadFileAsync(string path) { var content = await File.ReadAllTextAsync(path); var mimeType = GetMimeType(path); return new ResourceContent { Uri = $"file://{path}", MimeType = mimeType, Text = content }; } private static string GetMimeType(string path) { return Path.GetExtension(path).ToLower() switch { ".json" => "application/json", ".xml" => "application/xml", ".html" => "text/html", ".css" => "text/css", ".js" => "application/javascript", _ => "text/plain" }; } } ``` ## Промпты (Prompts) [Заголовок раздела «Промпты (Prompts)»](#промпты-prompts) Prompts/PromptTemplates.cs ````csharp public class PromptTemplates { [McpPrompt("code_review", "Промпт для код-ревью")] public static IEnumerable CodeReview( [McpParameter("Язык программирования")] string language, [McpParameter("Код для ревью")] string code) { yield return Message.User($@" Проведи код-ревью следующего {language} кода: ```{language} {code} ``` Обрати внимание на: 1. Потенциальные баги 2. Производительность 3. Безопасность 4. Best practices для {language} "); } [McpPrompt("sql_expert", "Помощник по SQL")] public static IEnumerable SqlExpert( [McpParameter("Вопрос о SQL")] string question, [McpParameter("Схема БД", Required = false)] string? schema = null) { yield return Message.Assistant("Я SQL эксперт. Помогу с запросами и оптимизацией."); var prompt = schema != null ? $"Схема БД:\n{schema}\n\nВопрос: {question}" : question; yield return Message.User(prompt); } } ```` ## Dependency Injection [Заголовок раздела «Dependency Injection»](#dependency-injection) Program.cs ```csharp using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; var builder = Host.CreateApplicationBuilder(args); // Регистрация сервисов builder.Services.AddHttpClient(); builder.Services.AddDbContext(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); // Настройка MCP сервера builder.Services.AddMcpServer("my-server", "1.0.0", mcp => { mcp.AddTools(); mcp.AddTools(); mcp.AddTools(); mcp.AddTools(); mcp.AddResources(); mcp.AddPrompts(); }); var host = builder.Build(); await host.RunAsync(); ``` ## ASP.NET Core интеграция [Заголовок раздела «ASP.NET Core интеграция»](#aspnet-core-интеграция) Program.cs ```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddMcpServer("web-server", "1.0.0", mcp => { mcp.AddTools(); mcp.AddResources(); }); var app = builder.Build(); // HTTP endpoint для MCP app.MapMcpServer("/mcp"); // SSE endpoint app.MapMcpSse("/mcp/sse"); app.Run(); ``` ## Обработка ошибок [Заголовок раздела «Обработка ошибок»](#обработка-ошибок) Tools/SecureTools.cs ```csharp public class SecureTools { [McpTool("secure_operation", "Безопасная операция")] public async Task SecureOperationAsync( [McpParameter("Данные")] string data) { try { // Валидация if (string.IsNullOrWhiteSpace(data)) throw new McpException( McpErrorCode.InvalidParams, "Данные не могут быть пустыми" ); // Авторизация if (!await IsAuthorizedAsync()) throw new McpException( McpErrorCode.Forbidden, "Недостаточно прав" ); // Выполнение return await ProcessDataAsync(data); } catch (OperationCanceledException) { throw new McpException( McpErrorCode.RequestCancelled, "Операция отменена" ); } catch (Exception ex) when (ex is not McpException) { // Логирование _logger.LogError(ex, "Ошибка при выполнении операции"); throw new McpException( McpErrorCode.InternalError, "Внутренняя ошибка сервера" ); } } } ``` ## Логирование [Заголовок раздела «Логирование»](#логирование) Tools/LoggedTools.cs ```csharp using Microsoft.Extensions.Logging; public class LoggedTools { private readonly ILogger _logger; public LoggedTools(ILogger logger) { _logger = logger; } [McpTool("process", "Обработка данных")] public async Task ProcessAsync(string data) { _logger.LogInformation("Начало обработки данных: {Length} символов", data.Length); try { var result = await DoProcessAsync(data); _logger.LogInformation("Обработка завершена успешно"); return result; } catch (Exception ex) { _logger.LogError(ex, "Ошибка обработки"); throw; } } } ``` ## Структура проекта [Заголовок раздела «Структура проекта»](#структура-проекта) ```plaintext MyMcpServer/ ├── src/ │ ├── MyMcpServer/ │ │ ├── Program.cs │ │ ├── Tools/ │ │ │ ├── CalculatorTools.cs │ │ │ └── FileTools.cs │ │ ├── Resources/ │ │ │ └── ConfigResources.cs │ │ ├── Prompts/ │ │ │ └── PromptTemplates.cs │ │ └── MyMcpServer.csproj │ └── MyMcpServer.Tests/ ├── Dockerfile ├── MyMcpServer.sln └── README.md ``` ## Docker [Заголовок раздела «Docker»](#docker) Dockerfile ```dockerfile FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY ["src/MyMcpServer/MyMcpServer.csproj", "MyMcpServer/"] RUN dotnet restore "MyMcpServer/MyMcpServer.csproj" COPY src/ . RUN dotnet build "MyMcpServer/MyMcpServer.csproj" -c Release -o /app/build RUN dotnet publish "MyMcpServer/MyMcpServer.csproj" -c Release -o /app/publish FROM mcr.microsoft.com/dotnet/runtime:8.0 WORKDIR /app COPY --from=build /app/publish . ENTRYPOINT ["dotnet", "MyMcpServer.dll"] ``` ## Конфигурация Claude Desktop [Заголовок раздела «Конфигурация Claude Desktop»](#конфигурация-claude-desktop) claude\_desktop\_config.json ```json { "mcpServers": { "dotnet-server": { "command": "dotnet", "args": ["run", "--project", "/path/to/MyMcpServer"] } } } ``` ### Опубликованное приложение [Заголовок раздела «Опубликованное приложение»](#опубликованное-приложение) claude\_desktop\_config.json ```json { "mcpServers": { "dotnet-server": { "command": "/path/to/MyMcpServer" } } } ``` ## Тестирование [Заголовок раздела «Тестирование»](#тестирование) Tests/CalculatorToolsTests.cs ```csharp using Xunit; using ModelContextProtocol.Testing; public class CalculatorToolsTests { [Fact] public void Add_ReturnsCorrectSum() { var result = CalculatorTools.Add(2, 3); Assert.Equal(5, result); } [Fact] public void Divide_ThrowsOnZero() { var exception = Assert.Throws(() => CalculatorTools.Divide(1, 0)); Assert.Equal(McpErrorCode.InvalidParams, exception.Code); } } ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [GitHub: twenzel/modelcontextprotocol](https://github.com/twenzel/modelcontextprotocol) * [NuGet: ModelContextProtocol](https://www.nuget.org/packages/ModelContextProtocol) * [Microsoft .NET Documentation](https://docs.microsoft.com/dotnet/) # Go SDK > Создание MCP серверов на Go для микросервисной архитектуры **mark3labs/mcp-go** — рекомендуемый Go SDK с поддержкой SSE, stdio транспортов и production-ready архитектурой. ## Установка [Заголовок раздела «Установка»](#установка) Terminal ```bash go get github.com/mark3labs/mcp-go ``` **Требования:** * Go 1.21+ ## Базовый сервер [Заголовок раздела «Базовый сервер»](#базовый-сервер) main.go ```go package main import ( "context" "log" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) func main() { s := server.NewMCPServer( "my-server", "1.0.0", server.WithDescription("MCP сервер на Go"), ) if err := server.ServeStdio(s); err != nil { log.Fatal(err) } } ``` ## Инструменты (Tools) [Заголовок раздела «Инструменты (Tools)»](#инструменты-tools) ### Базовый инструмент [Заголовок раздела «Базовый инструмент»](#базовый-инструмент) 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("calculator", "1.0.0") // Регистрация инструмента через AddTool 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("Результат: %f", a+b)), nil }) server.ServeStdio(s) } ``` ### Функциональный стиль [Заголовок раздела «Функциональный стиль»](#функциональный-стиль) tools.go ```go // Использование NewToolWithRawSchema для полного контроля divideTool := mcp.NewToolWithRawSchema("divide", "Деление чисел", map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "a": map[string]interface{}{"type": "number", "description": "Делимое"}, "b": map[string]interface{}{"type": "number", "description": "Делитель"}, }, "required": []string{"a", "b"}, }, ) s.AddTool(divideTool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { a := req.Params.Arguments["a"].(float64) b := req.Params.Arguments["b"].(float64) if b == 0 { return nil, fmt.Errorf("деление на ноль") } return mcp.NewToolResultText(fmt.Sprintf("Результат: %f", a/b)), nil }) ``` ### Инструмент с HTTP запросами [Заголовок раздела «Инструмент с HTTP запросами»](#инструмент-с-http-запросами) http\_tools.go ```go import ( "io" "net/http" ) fetchTool := mcp.NewTool("fetch_url", mcp.WithDescription("Загрузка содержимого URL"), mcp.WithString("url", mcp.Required(), mcp.Description("URL для загрузки")), ) s.AddTool(fetchTool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { url := req.Params.Arguments["url"].(string) resp, err := http.Get(url) if err != nil { return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } return mcp.NewToolResultText(string(body)), nil }) ``` ### Инструмент с файловой системой [Заголовок раздела «Инструмент с файловой системой»](#инструмент-с-файловой-системой) file\_tools.go ```go import ( "os" "path/filepath" "strings" ) readFileTool := mcp.NewTool("read_file", mcp.WithDescription("Чтение файла"), mcp.WithString("path", mcp.Required(), mcp.Description("Путь к файлу")), ) s.AddTool(readFileTool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { path := req.Params.Arguments["path"].(string) // Проверка безопасности cleanPath := filepath.Clean(path) if !strings.HasPrefix(cleanPath, "/allowed/") { return nil, fmt.Errorf("доступ запрещён") } content, err := os.ReadFile(cleanPath) if err != nil { return nil, err } return mcp.NewToolResultText(string(content)), nil }) ``` ### Инструмент с базой данных [Заголовок раздела «Инструмент с базой данных»](#инструмент-с-базой-данных) db\_tools.go ```go import ( "database/sql" _ "github.com/lib/pq" ) type QueryParams struct { SQL string `json:"sql"` } type DBTool struct { db *sql.DB } func NewDBTool(connStr string) (*DBTool, error) { db, err := sql.Open("postgres", connStr) if err != nil { return nil, err } return &DBTool{db: db}, nil } func (t *DBTool) Query(ctx context.Context, params json.RawMessage) ([]mcp.Content, error) { var p QueryParams if err := json.Unmarshal(params, &p); err != nil { return nil, err } rows, err := t.db.QueryContext(ctx, p.SQL) if err != nil { return nil, err } defer rows.Close() var results []map[string]interface{} columns, _ := rows.Columns() for rows.Next() { values := make([]interface{}, len(columns)) pointers := make([]interface{}, len(columns)) for i := range values { pointers[i] = &values[i] } rows.Scan(pointers...) row := make(map[string]interface{}) for i, col := range columns { row[col] = values[i] } results = append(results, row) } output, _ := json.MarshalIndent(results, "", " ") return []mcp.Content{ mcp.TextContent(string(output)), }, nil } ``` ## Ресурсы (Resources) [Заголовок раздела «Ресурсы (Resources)»](#ресурсы-resources) resources.go ```go func configResource(ctx context.Context, uri string) (mcp.ResourceContent, error) { config := map[string]interface{}{ "version": "1.0.0", "environment": "production", } data, _ := json.Marshal(config) return mcp.ResourceContent{ URI: uri, MimeType: "application/json", Text: string(data), }, nil } func main() { server := mcp.NewServer("config-server", "1.0.0") server.AddResource(mcp.Resource{ URI: "config://app", Name: "App Configuration", Description: "Конфигурация приложения", MimeType: "application/json", Handler: configResource, }) server.Run() } ``` ### Динамический ресурс [Заголовок раздела «Динамический ресурс»](#динамический-ресурс) resources.go ```go func fileResource(ctx context.Context, uri string) (mcp.ResourceContent, error) { // Извлечение пути из URI path := strings.TrimPrefix(uri, "file://") content, err := os.ReadFile(path) if err != nil { return mcp.ResourceContent{}, err } return mcp.ResourceContent{ URI: uri, MimeType: "text/plain", Text: string(content), }, nil } server.AddResource(mcp.Resource{ URI: "file://{path}", Name: "File Reader", Description: "Чтение файлов", Handler: fileResource, }) ``` ## Промпты (Prompts) [Заголовок раздела «Промпты (Prompts)»](#промпты-prompts) prompts.go ````go type CodeReviewParams struct { Language string `json:"language"` Code string `json:"code"` } func codeReviewPrompt(ctx context.Context, params json.RawMessage) ([]mcp.Message, error) { var p CodeReviewParams if err := json.Unmarshal(params, &p); err != nil { return nil, err } prompt := fmt.Sprintf(` Проведи код-ревью следующего %s кода: %s%s %s %s Обрати внимание на: 1. Потенциальные баги 2. Производительность 3. Безопасность 4. Best practices `, p.Language, "```", p.Language, p.Code, "```") return []mcp.Message{ {Role: "user", Content: mcp.TextContent(prompt)}, }, nil } server.AddPrompt(mcp.Prompt{ Name: "code_review", Description: "Промпт для код-ревью", Schema: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "language": map[string]interface{}{"type": "string"}, "code": map[string]interface{}{"type": "string"}, }, "required": []string{"language", "code"}, }, Handler: codeReviewPrompt, }) ```` ## Обработка ошибок [Заголовок раздела «Обработка ошибок»](#обработка-ошибок) errors.go ```go import "github.com/paulsmith/mcp-go/errors" func safeTool(ctx context.Context, params json.RawMessage) ([]mcp.Content, error) { var p Params if err := json.Unmarshal(params, &p); err != nil { return nil, errors.InvalidParams("Неверный формат параметров") } if !isAllowed(p.Path) { return nil, errors.Forbidden("Доступ запрещён") } data, err := loadData(p.Path) if err != nil { if os.IsNotExist(err) { return nil, errors.NotFound("Ресурс не найден") } return nil, errors.Internal("Внутренняя ошибка") } return []mcp.Content{mcp.TextContent(data)}, nil } ``` ## Логирование [Заголовок раздела «Логирование»](#логирование) main.go ```go import ( "log/slog" "os" ) func main() { // Настройка структурированного логирования logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ Level: slog.LevelInfo, })) slog.SetDefault(logger) server := mcp.NewServer("logged-server", "1.0.0") server.SetLogger(logger) server.Run() } ``` ## Middleware [Заголовок раздела «Middleware»](#middleware) middleware.go ```go func loggingMiddleware(next mcp.ToolHandler) mcp.ToolHandler { return func(ctx context.Context, params json.RawMessage) ([]mcp.Content, error) { start := time.Now() slog.Info("Tool called", "params", string(params)) result, err := next(ctx, params) duration := time.Since(start) if err != nil { slog.Error("Tool failed", "error", err, "duration", duration) } else { slog.Info("Tool completed", "duration", duration) } return result, err } } ``` ## Graceful Shutdown [Заголовок раздела «Graceful Shutdown»](#graceful-shutdown) main.go ```go func main() { server := mcp.NewServer("graceful-server", "1.0.0") // Настройка инструментов... // Graceful shutdown ctx, cancel := context.WithCancel(context.Background()) defer cancel() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigChan slog.Info("Получен сигнал завершения") cancel() }() if err := server.RunWithContext(ctx); err != nil && err != context.Canceled { log.Fatal(err) } } ``` ## Структура проекта [Заголовок раздела «Структура проекта»](#структура-проекта) ```plaintext my-mcp-server/ ├── cmd/ │ └── server/ │ └── main.go ├── internal/ │ ├── tools/ │ │ ├── calculator.go │ │ └── filesystem.go │ ├── resources/ │ │ └── config.go │ └── prompts/ │ └── templates.go ├── go.mod ├── go.sum ├── Dockerfile └── README.md ``` ## Docker [Заголовок раздела «Docker»](#docker) Dockerfile ```dockerfile # Build stage 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 # Runtime stage FROM alpine:latest RUN apk --no-cache add ca-certificates COPY --from=builder /app/mcp-server /usr/local/bin/ CMD ["mcp-server"] ``` ## Конфигурация Claude Desktop [Заголовок раздела «Конфигурация Claude Desktop»](#конфигурация-claude-desktop) claude\_desktop\_config.json ```json { "mcpServers": { "go-server": { "command": "/path/to/mcp-server" } } } ``` ## SSE транспорт [Заголовок раздела «SSE транспорт»](#sse-транспорт) main.go ```go package main import ( "log" "net/http" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) func main() { s := server.NewMCPServer("sse-server", "1.0.0") // Добавление инструментов... // SSE сервер sseServer := server.NewSSEServer(s) http.HandleFunc("/sse", sseServer.HandleSSE) http.HandleFunc("/message", sseServer.HandleMessage) log.Println("SSE сервер запущен на :3000") log.Fatal(http.ListenAndServe(":3000", nil)) } ``` ## Альтернативные SDK [Заголовок раздела «Альтернативные SDK»](#альтернативные-sdk) * **riza-io/mcp-go** — Производственная реализация для enterprise * **paulsmith/mcp-go** — Минималистичная реализация ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [GitHub: mark3labs/mcp-go](https://github.com/mark3labs/mcp-go) * [GitHub: riza-io/mcp-go](https://github.com/riza-io/mcp-go) * [Go Modules](https://pkg.go.dev/github.com/mark3labs/mcp-go) # Java SDK > Официальный Java SDK для Model Context Protocol — создание MCP-серверов и клиентов с поддержкой Spring Boot Официальный **Java SDK** для Model Context Protocol обеспечивает интеграцию Java-приложений с AI-моделями через стандартизированный интерфейс. Поддерживает синхронные и асинхронные паттерны взаимодействия. ## Требования [Заголовок раздела «Требования»](#требования) * **Java 17+** * Maven или Gradle * Docker и npx (для тестов) ## Установка [Заголовок раздела «Установка»](#установка) * Maven ```xml io.modelcontextprotocol.sdk mcp-bom LATEST pom import io.modelcontextprotocol.sdk mcp ``` * Gradle ```kotlin dependencies { implementation(platform("io.modelcontextprotocol.sdk:mcp-bom:LATEST")) implementation("io.modelcontextprotocol.sdk:mcp") } ``` ## Архитектура SDK [Заголовок раздела «Архитектура SDK»](#архитектура-sdk) ### Модульная структура [Заголовок раздела «Модульная структура»](#модульная-структура) | Модуль | Описание | | -------------- | ----------------------------------------------------- | | `mcp-bom` | Версии зависимостей (Bill of Materials) | | `mcp-core` | Эталонная реализация (STDIO, JDK HttpClient, Servlet) | | `mcp-json` | Абстракция JSON-сериализации | | `mcp-jackson2` | Реализация JSON на базе Jackson | | `mcp` | Удобный бандл (core + Jackson) | | `mcp-test` | Утилиты для тестирования | | `mcp-spring` | Интеграция со Spring (WebClient, WebFlux, WebMVC) | ### Ключевые решения [Заголовок раздела «Ключевые решения»](#ключевые-решения) **JSON-сериализация:** * Jackson как реализация по умолчанию * Абстракция `mcp-json` для альтернативных библиотек **Модель программирования:** * Reactive Streams для публичных API * Project Reactor как внутренняя реализация * Синхронный фасад для блокирующих сценариев **Observability:** * SLF4J для логирования * Reactor Context для распространения трассировки ## Создание клиента [Заголовок раздела «Создание клиента»](#создание-клиента) ```java import io.modelcontextprotocol.sdk.McpClient; import io.modelcontextprotocol.sdk.McpClientSession; // Создание клиента McpClient client = McpClient.builder() .clientInfo("my-client", "1.0.0") .build(); // Подключение через stdio McpClientSession session = client.connect(stdioTransport); // Получение списка инструментов ListToolsResult tools = session.listTools().block(); // Вызов инструмента CallToolResult result = session.callTool( "weather", Map.of("location", "Moscow") ).block(); ``` ## Создание сервера [Заголовок раздела «Создание сервера»](#создание-сервера) ```java import io.modelcontextprotocol.sdk.McpServer; import io.modelcontextprotocol.sdk.McpServerSession; // Создание сервера McpServer server = McpServer.builder() .serverInfo("my-server", "1.0.0") .capabilities(ServerCapabilities.builder() .tools(true) .resources(true) .build()) .build(); // Регистрация инструмента server.addTool( "calculator", "Выполняет математические вычисления", inputSchema, (arguments) -> { String expression = arguments.get("expression").asText(); double result = evaluate(expression); return new CallToolResult(List.of( new TextContent("Результат: " + result) )); } ); // Запуск сервера server.start(stdioTransport); ``` ## Транспорты [Заголовок раздела «Транспорты»](#транспорты) ### Клиентские транспорты [Заголовок раздела «Клиентские транспорты»](#клиентские-транспорты) **JDK HttpClient (по умолчанию):** ```java HttpClientTransport transport = HttpClientTransport.builder() .endpoint(URI.create("http://localhost:8080/mcp")) .build(); ``` **Spring WebClient:** ```java WebClientTransport transport = WebClientTransport.builder() .webClient(webClient) .endpoint(URI.create("http://localhost:8080/mcp")) .build(); ``` ### Серверные транспорты [Заголовок раздела «Серверные транспорты»](#серверные-транспорты) **Jakarta Servlet:** ```java ServletTransport transport = new ServletTransport(server); // Регистрация в Servlet-контейнере ``` **Spring WebFlux:** ```java @Bean RouterFunction mcpRoutes(McpServer server) { return WebFluxTransport.routes(server); } ``` **Spring WebMVC:** ```java @Bean RouterFunction mcpRoutes(McpServer server) { return WebMvcTransport.routes(server); } ``` ## Интеграция со Spring Boot [Заголовок раздела «Интеграция со Spring Boot»](#интеграция-со-spring-boot) Совет Для Spring-приложений рекомендуется использовать [Spring AI MCP](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html), расширяющий Java SDK с автоконфигурацией. ### Клиент Spring Boot [Заголовок раздела «Клиент Spring Boot»](#клиент-spring-boot) ```java @Configuration public class McpClientConfig { @Bean McpClient mcpClient() { return McpClient.builder() .clientInfo("spring-client", "1.0.0") .build(); } } ``` ### Сервер Spring Boot [Заголовок раздела «Сервер Spring Boot»](#сервер-spring-boot) ```java @Configuration public class McpServerConfig { @Bean McpServer mcpServer() { return McpServer.builder() .serverInfo("spring-server", "1.0.0") .capabilities(ServerCapabilities.builder() .tools(true) .build()) .build(); } @Bean RouterFunction mcpRoutes(McpServer server) { return WebFluxTransport.routes(server); } } ``` ## Авторизация [Заголовок раздела «Авторизация»](#авторизация) SDK предоставляет pluggable hooks для авторизации без встроенной реализации: ```java McpServer server = McpServer.builder() .serverInfo("secure-server", "1.0.0") .authorizationHandler((request) -> { String token = request.getHeader("Authorization"); return validateToken(token); }) .build(); ``` Заметка Авторизация интегрирована в транспортный слой. Используйте Spring Security, MicroProfile JWT или собственные решения. ## Асинхронная модель [Заголовок раздела «Асинхронная модель»](#асинхронная-модель) ### Реактивный подход (рекомендуется) [Заголовок раздела «Реактивный подход (рекомендуется)»](#реактивный-подход-рекомендуется) ```java // Асинхронное выполнение Mono resultMono = session.callTool("weather", args); resultMono .doOnSuccess(result -> log.info("Результат: {}", result)) .doOnError(error -> log.error("Ошибка: {}", error)) .subscribe(); ``` ### Синхронный фасад [Заголовок раздела «Синхронный фасад»](#синхронный-фасад) ```java // Блокирующее выполнение CallToolResult result = session.callTool("weather", args).block(); ``` ## Сборка из исходников [Заголовок раздела «Сборка из исходников»](#сборка-из-исходников) ```bash # Сборка без тестов ./mvnw clean install -DskipTests # Запуск тестов (требуется Docker и npx) ./mvnw test ``` ## Документация [Заголовок раздела «Документация»](#документация) * [Features](https://modelcontextprotocol.io/sdk/java/mcp-overview#features) — обзор возможностей * [Architecture](https://modelcontextprotocol.io/sdk/java/mcp-overview#architecture) — архитектура SDK * [MCP Client](https://modelcontextprotocol.io/sdk/java/mcp-client) — использование клиента * [MCP Server](https://modelcontextprotocol.io/sdk/java/mcp-server) — создание сервера * [Spring AI MCP](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html) — интеграция со Spring ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [GitHub](https://github.com/modelcontextprotocol/java-sdk) * [Maven Central](https://central.sonatype.com/artifact/io.modelcontextprotocol.sdk/mcp) * [Spring Initializer](https://start.spring.io) — быстрый старт со Spring AI MCP # Kotlin SDK > Официальный Kotlin Multiplatform SDK для Model Context Protocol — JVM, WebAssembly и Native платформы Официальный **Kotlin Multiplatform SDK** для Model Context Protocol предоставляет клиентские и серверные возможности для интеграции с LLM на различных платформах: JVM, WebAssembly и Native (iOS). ## Возможности [Заголовок раздела «Возможности»](#возможности) * Создание MCP-клиентов для подключения к любым MCP-серверам * Создание MCP-серверов с resources, prompts и tools * Поддержка транспортов: stdio, SSE, WebSocket * Обработка всех протокольных сообщений и событий жизненного цикла * **Multiplatform**: JVM, Wasm/JS, Native (iOS) ## Требования [Заголовок раздела «Требования»](#требования) * **Kotlin 2.2+** * **JVM 11+** (для JVM-платформы) * Ktor (клиент и/или сервер) ## Установка [Заголовок раздела «Установка»](#установка) ```kotlin repositories { mavenCentral() } dependencies { // MCP SDK implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion") // Ktor клиент (выберите движок) implementation("io.ktor:ktor-client-cio:$ktorVersion") // Ktor сервер (для SSE/HTTP) implementation("io.ktor:ktor-server-netty:$ktorVersion") } ``` Заметка MCP SDK использует Ktor, но не включает зависимость движка. Добавьте [Ktor client](https://ktor.io/docs/client-dependencies.html#engine-dependency) и/или [Ktor server](https://ktor.io/docs/server-dependencies.html#engine-dependency) самостоятельно. ## Создание клиента [Заголовок раздела «Создание клиента»](#создание-клиента) ```kotlin import io.modelcontextprotocol.kotlin.sdk.client.Client import io.modelcontextprotocol.kotlin.sdk.client.StdioClientTransport import io.modelcontextprotocol.kotlin.sdk.Implementation val client = Client( clientInfo = Implementation( name = "example-client", version = "1.0.0" ) ) val transport = StdioClientTransport( inputStream = processInputStream, outputStream = processOutputStream ) // Подключение к серверу client.connect(transport) // Список доступных ресурсов val resources = client.listResources() // Чтение ресурса val resourceContent = client.readResource( ReadResourceRequest(uri = "file:///example.txt") ) ``` ## Создание сервера [Заголовок раздела «Создание сервера»](#создание-сервера) ```kotlin import io.modelcontextprotocol.kotlin.sdk.server.Server import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions import io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransport import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities val server = Server( serverInfo = Implementation( name = "example-server", version = "1.0.0" ), options = ServerOptions( capabilities = ServerCapabilities( resources = ServerCapabilities.Resources( subscribe = true, listChanged = true ) ) ) ) { "Этот сервер предоставляет примеры ресурсов и демонстрирует возможности MCP." } // Добавление ресурса server.addResource( uri = "file:///example.txt", name = "Example Resource", description = "Пример текстового файла", mimeType = "text/plain" ) { request -> ReadResourceResult( contents = listOf( TextResourceContents( text = "Содержимое примера ресурса.", uri = request.uri, mimeType = "text/plain" ) ) ) } // Запуск с stdio транспортом val transport = StdioServerTransport() server.connect(transport) ``` ## SSE Transport (Ktor) [Заголовок раздела «SSE Transport (Ktor)»](#sse-transport-ktor) ### В Application [Заголовок раздела «В Application»](#в-application) ```kotlin import io.ktor.server.application.* import io.modelcontextprotocol.kotlin.sdk.server.mcp fun Application.module() { mcp { Server( serverInfo = Implementation( name = "example-sse-server", version = "1.0.0" ), options = ServerOptions( capabilities = ServerCapabilities( prompts = ServerCapabilities.Prompts(listChanged = null), resources = ServerCapabilities.Resources( subscribe = null, listChanged = null ) ) ), ) { "SSE-сервер для prompts и resources." } } } ``` ### В кастомном Route [Заголовок раздела «В кастомном Route»](#в-кастомном-route) ```kotlin import io.ktor.server.application.* import io.ktor.server.sse.SSE import io.modelcontextprotocol.kotlin.sdk.server.mcp fun Application.module() { install(SSE) routing { route("myRoute") { mcp { Server( serverInfo = Implementation( name = "example-sse-server", version = "1.0.0" ), options = ServerOptions( capabilities = ServerCapabilities( prompts = ServerCapabilities.Prompts(listChanged = null), resources = ServerCapabilities.Resources( subscribe = null, listChanged = null ) ) ), ) { "Подключитесь через SSE для взаимодействия с MCP-сервером." } } } } } ``` ## Примеры проектов [Заголовок раздела «Примеры проектов»](#примеры-проектов) | Проект | Описание | | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | | [kotlin-mcp-server](https://github.com/modelcontextprotocol/kotlin-sdk/tree/main/samples/kotlin-mcp-server) | Multiplatform сервер (JVM, Wasm) с различными транспортами | | [weather-stdio-server](https://github.com/modelcontextprotocol/kotlin-sdk/tree/main/samples/weather-stdio-server) | Сервер погоды через STDIO | | [kotlin-mcp-client](https://github.com/modelcontextprotocol/kotlin-sdk/tree/main/samples/kotlin-mcp-client) | Интерактивный клиент с Anthropic API | ## Работа с инструментами [Заголовок раздела «Работа с инструментами»](#работа-с-инструментами) ```kotlin // Добавление инструмента server.addTool( name = "calculator", description = "Выполняет математические вычисления", inputSchema = JsonSchema( type = "object", properties = mapOf( "expression" to JsonSchema(type = "string") ), required = listOf("expression") ) ) { arguments -> val expression = arguments["expression"]?.jsonPrimitive?.content val result = evaluateExpression(expression) CallToolResult( content = listOf( TextContent(text = "Результат: $result") ) ) } ``` ## Работа с prompts [Заголовок раздела «Работа с prompts»](#работа-с-prompts) ```kotlin // Добавление prompt server.addPrompt( name = "greeting", description = "Генерирует приветствие", arguments = listOf( PromptArgument( name = "name", description = "Имя для приветствия", required = true ) ) ) { arguments -> val name = arguments["name"] GetPromptResult( description = "Приветствие для $name", messages = listOf( PromptMessage( role = Role.USER, content = TextContent("Привет, $name!") ) ) ) } ``` ## Multiplatform поддержка [Заголовок раздела «Multiplatform поддержка»](#multiplatform-поддержка) ### JVM [Заголовок раздела «JVM»](#jvm) build.gradle.kts ```kotlin kotlin { jvm() sourceSets { jvmMain.dependencies { implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion") implementation("io.ktor:ktor-client-cio:$ktorVersion") } } } ``` ### WebAssembly / JS [Заголовок раздела «WebAssembly / JS»](#webassembly--js) ```kotlin kotlin { wasmJs { browser() } sourceSets { wasmJsMain.dependencies { implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion") implementation("io.ktor:ktor-client-js:$ktorVersion") } } } ``` ### Native (iOS) [Заголовок раздела «Native (iOS)»](#native-ios) ```kotlin kotlin { iosArm64() iosSimulatorArm64() sourceSets { iosMain.dependencies { implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion") implementation("io.ktor:ktor-client-darwin:$ktorVersion") } } } ``` ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [GitHub](https://github.com/modelcontextprotocol/kotlin-sdk) * [Maven Central](https://central.sonatype.com/artifact/io.modelcontextprotocol/kotlin-sdk) * [Ktor Documentation](https://ktor.io/docs/) # PHP SDK > Создание MCP серверов на PHP с использованием php-mcp/server **php-mcp/server** — рекомендуемый PHP SDK с attribute-based конфигурацией, PSR-11 dependency injection и полной поддержкой MCP спецификации. ## Установка [Заголовок раздела «Установка»](#установка) Terminal ```bash composer require php-mcp/server ``` **Требования:** * PHP 8.1+ * Composer ## Базовый сервер [Заголовок раздела «Базовый сервер»](#базовый-сервер) server.php ```php withDescription('Мой MCP сервер на PHP'); // Запуск $server->run(); ``` ## Инструменты через атрибуты [Заголовок раздела «Инструменты через атрибуты»](#инструменты-через-атрибуты) Самый удобный способ — использовать PHP атрибуты: Calculator.php ```php withTools($calculator) ->run(); ``` ## Ресурсы через атрибуты [Заголовок раздела «Ресурсы через атрибуты»](#ресурсы-через-атрибуты) ConfigProvider.php ```php '1.0.0', 'environment' => 'production', 'debug' => false, ]); } #[McpResource( uri: 'file://{path}', name: 'File Reader', description: 'Чтение файла' )] public function readFile( #[ResourceParameter(description: 'Путь к файлу')] string $path ): string { if (!file_exists($path)) { throw new \RuntimeException("Файл не найден: $path"); } return file_get_contents($path); } } ``` ## Промпты через атрибуты [Заголовок раздела «Промпты через атрибуты»](#промпты-через-атрибуты) PromptTemplates.php ````php getContainer(); // С PHP-DI $container = \DI\ContainerBuilder::buildDevContainer(); $server = McpServer::create('my-server', '1.0.0') ->withContainer($container) ->withTools(Calculator::class) // Резолвится через контейнер ->run(); ``` ### Пример с сервисом БД [Заголовок раздела «Пример с сервисом БД»](#пример-с-сервисом-бд) DatabaseTools.php ```php db->query($sql); return $stmt->fetchAll(PDO::FETCH_ASSOC); } #[McpTool( name: 'insert', description: 'Вставка данных' )] public function insert(string $table, array $data): int { $columns = implode(', ', array_keys($data)); $placeholders = implode(', ', array_fill(0, count($data), '?')); $stmt = $this->db->prepare( "INSERT INTO $table ($columns) VALUES ($placeholders)" ); $stmt->execute(array_values($data)); return (int) $this->db->lastInsertId(); } } ``` ## Batch Processing [Заголовок раздела «Batch Processing»](#batch-processing) SDK поддерживает пакетную обработку JSON-RPC запросов: server.php ```php withBatchProcessing(true) ->withMaxBatchSize(100) ->run(); ``` ## Кэширование [Заголовок раздела «Кэширование»](#кэширование) Интеллектуальное кэширование схем: server.php ```php withCache($cache) ->withCacheTtl(3600) // 1 час ->run(); ``` ## Обработка ошибок [Заголовок раздела «Обработка ошибок»](#обработка-ошибок) SecureTools.php ```php isAllowed($path)) { throw McpError::forbidden('Доступ запрещён'); } if (!file_exists($path)) { throw McpError::notFound("Файл не найден: $path"); } return file_get_contents($path); } #[McpTool(name: 'validate')] public function validate(array $data): bool { if (empty($data['required_field'])) { throw McpError::invalidParams('Отсутствует обязательное поле'); } return true; } private function isAllowed(string $path): bool { return str_starts_with($path, '/allowed/'); } } ``` ## Логирование [Заголовок раздела «Логирование»](#логирование) server.php ```php pushHandler(new StreamHandler('/var/log/mcp.log')); $server = McpServer::create('logged-server', '1.0.0') ->withLogger($logger) ->run(); ``` ## Интеграция с Laravel [Заголовок раздела «Интеграция с Laravel»](#интеграция-с-laravel) ### Сервис-провайдер [Заголовок раздела «Сервис-провайдер»](#сервис-провайдер) app/Providers/McpServiceProvider.php ```php app->singleton(McpServer::class, function ($app) { return McpServer::create('laravel-mcp', '1.0.0') ->withContainer($app) ->withTools(LaravelTools::class) ->withResources(LaravelResources::class); }); } } ``` ### Artisan команда [Заголовок раздела «Artisan команда»](#artisan-команда) app/Console/Commands/McpServe.php ```php info('Запуск MCP сервера...'); $server->run(); } } ``` ## Структура проекта [Заголовок раздела «Структура проекта»](#структура-проекта) ```plaintext my-mcp-server/ ├── src/ │ ├── Server.php │ ├── Tools/ │ │ ├── Calculator.php │ │ └── Filesystem.php │ ├── Resources/ │ │ └── ConfigProvider.php │ └── Prompts/ │ └── Templates.php ├── bin/ │ └── server.php ├── composer.json └── README.md ``` ### bin/server.php [Заголовок раздела «bin/server.php»](#binserverphp) bin/server.php ```php #!/usr/bin/env php withTools(new Calculator()) ->withTools(new Filesystem()) ->withResources(new ConfigProvider()) ->withPrompts(new Templates()) ->run(); ``` ## Конфигурация Claude Desktop [Заголовок раздела «Конфигурация Claude Desktop»](#конфигурация-claude-desktop) claude\_desktop\_config.json ```json { "mcpServers": { "php-server": { "command": "php", "args": ["/path/to/bin/server.php"] } } } ``` ## Альтернативные SDK [Заголовок раздела «Альтернативные SDK»](#альтернативные-sdk) * **logiscape/mcp-sdk-php** — полная реализация с поддержкой stdio и HTTP * **mcp-framework** — фреймворк с routing и middleware ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [GitHub: php-mcp/server](https://github.com/php-mcp/server) * [GitHub: logiscape/mcp-sdk-php](https://github.com/logiscape/mcp-sdk-php) * [YouTube: PHP MCP Server Tutorial](https://www.youtube.com/watch?v=9IE0l_wl6Zo) * [Playbooks: PHP MCP](https://playbooks.com/mcp/php) # Python SDK (FastMCP) > Полное руководство по созданию MCP серверов на Python с использованием FastMCP FastMCP — официальный высокоуровневый SDK для создания MCP серверов на Python. Использует type hints и docstrings для автоматической генерации схем. ## Установка [Заголовок раздела «Установка»](#установка) * uv Terminal ```bash uv add mcp ``` * pip Terminal ```bash pip install mcp ``` * poetry Terminal ```bash poetry add mcp ``` С CLI утилитами: * uv Terminal ```bash uv add "mcp[cli]" ``` * pip Terminal ```bash pip install "mcp[cli]" ``` * poetry Terminal ```bash poetry add "mcp[cli]" ``` **Требования:** * Python 3.10+ * asyncio поддержка ## Базовый сервер [Заголовок раздела «Базовый сервер»](#базовый-сервер) ```python from mcp.server.fastmcp import FastMCP # Создание сервера mcp = FastMCP( name="my-server", instructions="Сервер для работы с данными. Используй tool1 для X, tool2 для Y." ) # Запуск if __name__ == "__main__": mcp.run() ``` ## Транспорты [Заголовок раздела «Транспорты»](#транспорты) FastMCP поддерживает несколько транспортов для связи с клиентами: ### Stdio (по умолчанию) [Заголовок раздела «Stdio (по умолчанию)»](#stdio-по-умолчанию) ```python # Стандартный запуск через stdio mcp.run() # Или явно mcp.run(transport="stdio") ``` ### SSE (Server-Sent Events) [Заголовок раздела «SSE (Server-Sent Events)»](#sse-server-sent-events) ```python # Запуск HTTP сервера с SSE mcp.run(transport="sse", host="0.0.0.0", port=8080) ``` ### StreamableHTTP [Заголовок раздела «StreamableHTTP»](#streamablehttp) ```python # Современный HTTP транспорт с поддержкой стриминга mcp.run(transport="streamable-http", host="0.0.0.0", port=8080) ``` ## Инструменты (Tools) [Заголовок раздела «Инструменты (Tools)»](#инструменты-tools) Инструменты — функции, которые AI может вызывать. ### Базовый инструмент [Заголовок раздела «Базовый инструмент»](#базовый-инструмент) ```python @mcp.tool() def add(a: int, b: int) -> int: """ Сложение двух чисел. Args: a: Первое число b: Второе число Returns: Сумма чисел """ return a + b ``` ### Асинхронный инструмент [Заголовок раздела «Асинхронный инструмент»](#асинхронный-инструмент) ```python import aiohttp @mcp.tool() async def fetch_url(url: str) -> str: """ Загрузка содержимого URL. Args: url: URL для загрузки Returns: Содержимое страницы """ async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.text() ``` ### Инструмент с опциональными параметрами [Заголовок раздела «Инструмент с опциональными параметрами»](#инструмент-с-опциональными-параметрами) ```python from typing import Optional @mcp.tool() def search( query: str, limit: int = 10, offset: int = 0, category: Optional[str] = None ) -> list[dict]: """ Поиск в базе данных. Args: query: Поисковый запрос limit: Максимум результатов (по умолчанию 10) offset: Смещение для пагинации category: Опциональная категория фильтра """ results = database.search(query) if category: results = [r for r in results if r["category"] == category] return results[offset:offset + limit] ``` ### Инструмент с Enum [Заголовок раздела «Инструмент с Enum»](#инструмент-с-enum) ```python from enum import Enum class Priority(Enum): LOW = "low" MEDIUM = "medium" HIGH = "high" @mcp.tool() def create_task(title: str, priority: Priority) -> dict: """ Создание задачи с приоритетом. Args: title: Название задачи priority: Приоритет (low, medium, high) """ return { "id": generate_id(), "title": title, "priority": priority.value } ``` ### Инструмент с Pydantic моделью [Заголовок раздела «Инструмент с Pydantic моделью»](#инструмент-с-pydantic-моделью) ```python from pydantic import BaseModel, Field class CreateUserInput(BaseModel): name: str = Field(..., description="Имя пользователя") email: str = Field(..., description="Email адрес") age: int = Field(ge=0, le=150, description="Возраст") @mcp.tool() def create_user(data: CreateUserInput) -> dict: """Создание нового пользователя""" user = User( name=data.name, email=data.email, age=data.age ) db.save(user) return user.to_dict() ``` ## Ресурсы (Resources) [Заголовок раздела «Ресурсы (Resources)»](#ресурсы-resources) Ресурсы — данные, которые AI может прочитать. ### Статический ресурс [Заголовок раздела «Статический ресурс»](#статический-ресурс) ```python @mcp.resource("config://app") def get_app_config() -> str: """Конфигурация приложения""" return json.dumps({ "version": "1.0.0", "debug": False, "database": "postgresql://localhost/myapp" }) ``` ### Динамический ресурс с параметрами [Заголовок раздела «Динамический ресурс с параметрами»](#динамический-ресурс-с-параметрами) ```python @mcp.resource("file://{path}") def read_file(path: str) -> str: """ Чтение файла по пути. Args: path: Путь к файлу """ with open(path, "r") as f: return f.read() ``` ### Ресурс с бинарными данными [Заголовок раздела «Ресурс с бинарными данными»](#ресурс-с-бинарными-данными) ```python import base64 @mcp.resource("image://{name}") def get_image(name: str) -> tuple[str, str]: """ Получение изображения. Returns: Кортеж (base64_data, mime_type) """ with open(f"images/{name}", "rb") as f: data = base64.b64encode(f.read()).decode() return data, "image/png" ``` ### Ресурс из базы данных [Заголовок раздела «Ресурс из базы данных»](#ресурс-из-базы-данных) ```python @mcp.resource("db://users/{user_id}") async def get_user(user_id: int) -> str: """Получение пользователя из БД""" async with db.acquire() as conn: user = await conn.fetchrow( "SELECT * FROM users WHERE id = $1", user_id ) return json.dumps(dict(user)) ``` ## Промпты (Prompts) [Заголовок раздела «Промпты (Prompts)»](#промпты-prompts) Промпты — шаблоны для структурированных запросов. ### Базовый промпт [Заголовок раздела «Базовый промпт»](#базовый-промпт) ````python @mcp.prompt() def code_review(language: str, code: str) -> str: """ Промпт для код-ревью. Args: language: Язык программирования code: Код для ревью """ return f""" Проведи детальное код-ревью следующего {language} кода: ```{language} {code} ```` Проверь: 1. Потенциальные баги и ошибки 2. Производительность и оптимизации 3. Соответствие best practices 4. Читаемость и поддерживаемость 5. Безопасность Предложи конкретные улучшения с примерами кода. """ ````plaintext ### Промпт с несколькими сообщениями ```python from mcp.server.fastmcp import Message @mcp.prompt() def debug_assistant(error: str, context: str) -> list[Message]: """Промпт для помощи с отладкой""" return [ Message( role="system", content="Ты опытный отладчик Python. Анализируй ошибки и предлагай решения." ), Message( role="user", content=f""" Ошибка: {error} Контекст: {context} Помоги найти и исправить проблему. """ ) ] ```` ## Контекст и зависимости [Заголовок раздела «Контекст и зависимости»](#контекст-и-зависимости) ### Доступ к контексту запроса [Заголовок раздела «Доступ к контексту запроса»](#доступ-к-контексту-запроса) ```python from mcp.server.fastmcp import Context @mcp.tool() async def get_request_info(ctx: Context) -> dict: """Информация о текущем запросе""" return { "request_id": ctx.request_id, "client_id": ctx.client_id, } ``` ### Логирование через контекст [Заголовок раздела «Логирование через контекст»](#логирование-через-контекст) ```python @mcp.tool() async def process_data(ctx: Context, data: str) -> str: """Обработка данных с логированием""" ctx.info(f"Начало обработки: {len(data)} символов") ctx.debug("Детали обработки...") result = transform(data) ctx.info("Обработка завершена") return result ``` ### Отслеживание прогресса [Заголовок раздела «Отслеживание прогресса»](#отслеживание-прогресса) ```python @mcp.tool() async def long_task(ctx: Context, items: list[str]) -> str: """Длительная задача с прогрессом""" total = len(items) for i, item in enumerate(items): await ctx.report_progress( progress=i, total=total, message=f"Обработка {item}" ) await process_item(item) return f"Обработано {total} элементов" ``` ### Dependency Injection с Lifespan [Заголовок раздела «Dependency Injection с Lifespan»](#dependency-injection-с-lifespan) ```python from contextlib import asynccontextmanager from dataclasses import dataclass @dataclass class AppContext: db: DatabaseConnection cache: CacheClient @asynccontextmanager async def lifespan(server: FastMCP): """Управление жизненным циклом зависимостей""" db = await DatabaseConnection.connect("postgresql://localhost/app") cache = await CacheClient.connect("redis://localhost") try: yield AppContext(db=db, cache=cache) finally: await db.disconnect() await cache.disconnect() mcp = FastMCP("my-server", lifespan=lifespan) @mcp.tool() async def query_with_cache(ctx: Context, sql: str) -> list[dict]: """Запрос с кешированием""" app: AppContext = ctx.request_context.lifespan_context # Проверка кеша cached = await app.cache.get(sql) if cached: return cached # Запрос к БД result = await app.db.query(sql) await app.cache.set(sql, result, ttl=300) return result ``` ## Возврат изображений [Заголовок раздела «Возврат изображений»](#возврат-изображений) ```python from mcp.server.fastmcp import Image @mcp.tool() def create_chart(data: list[float]) -> Image: """Создание графика из данных""" import matplotlib.pyplot as plt import io plt.figure(figsize=(10, 6)) plt.plot(data) plt.title("График данных") buf = io.BytesIO() plt.savefig(buf, format='png') buf.seek(0) return Image(data=buf.read(), format="png") @mcp.tool() def get_screenshot(url: str) -> Image: """Скриншот веб-страницы""" from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page() page.goto(url) screenshot = page.screenshot() browser.close() return Image(data=screenshot, format="png") ``` ## Композиция серверов [Заголовок раздела «Композиция серверов»](#композиция-серверов) Используйте `mcp.mount()` для объединения нескольких серверов: ```python from mcp.server.fastmcp import FastMCP # Основной сервер main = FastMCP("main-server") # Подсерверы files_server = FastMCP("files") db_server = FastMCP("database") @files_server.tool() def read_file(path: str) -> str: """Чтение файла""" return open(path).read() @db_server.tool() def query(sql: str) -> list[dict]: """SQL запрос""" return db.execute(sql) # Монтирование подсерверов с префиксами main.mount("files", files_server) main.mount("db", db_server) # Инструменты будут доступны как: # - files_read_file # - db_query if __name__ == "__main__": main.run() ``` ## Обработка ошибок [Заголовок раздела «Обработка ошибок»](#обработка-ошибок) ```python from mcp.server.fastmcp import McpError @mcp.tool() def divide(a: float, b: float) -> float: """Деление чисел""" if b == 0: raise McpError("INVALID_PARAMS", "Деление на ноль невозможно") return a / b @mcp.tool() def read_secure_file(path: str) -> str: """Чтение файла с проверкой доступа""" if not path.startswith("/allowed/"): raise McpError("FORBIDDEN", "Доступ к этому файлу запрещён") return open(path).read() ``` ## Аннотации инструментов [Заголовок раздела «Аннотации инструментов»](#аннотации-инструментов) ```python from mcp.server.fastmcp.tools import ToolAnnotations @mcp.tool( annotations=ToolAnnotations( title="Запрос к БД", read_only_hint=True, open_world_hint=False ) ) def safe_query(sql: str) -> list[dict]: """Безопасный SQL запрос (только SELECT)""" if not sql.strip().upper().startswith("SELECT"): raise ValueError("Только SELECT запросы") return db.execute(sql) ``` ## Модульная структура [Заголовок раздела «Модульная структура»](#модульная-структура) Для больших проектов рекомендуется разделять код на модули: ```plaintext my-server/ ├── src/ │ ├── __init__.py │ ├── server.py # Точка входа │ ├── tools/ │ │ ├── __init__.py │ │ ├── calculator.py │ │ ├── filesystem.py │ │ └── database.py │ ├── resources/ │ │ ├── __init__.py │ │ └── config.py │ └── prompts/ │ ├── __init__.py │ └── templates.py ├── tests/ ├── pyproject.toml └── README.md ``` ### src/server.py [Заголовок раздела «src/server.py»](#srcserverpy) ```python from mcp.server.fastmcp import FastMCP mcp = FastMCP("my-server") # Импорт модулей (автоматическая регистрация декораторов) from .tools import calculator, filesystem, database from .resources import config from .prompts import templates if __name__ == "__main__": mcp.run() ``` ### src/tools/calculator.py [Заголовок раздела «src/tools/calculator.py»](#srctoolscalculatorpy) ```python from ..server import mcp @mcp.tool() def add(a: float, b: float) -> float: """Сложение""" return a + b @mcp.tool() def multiply(a: float, b: float) -> float: """Умножение""" return a * b ``` ### Альтернатива: композиция через mount [Заголовок раздела «Альтернатива: композиция через mount»](#альтернатива-композиция-через-mount) src/tools/calculator.py ```python from mcp.server.fastmcp import FastMCP calculator = FastMCP("calculator") @calculator.tool() def add(a: float, b: float) -> float: """Сложение""" return a + b # src/server.py from mcp.server.fastmcp import FastMCP from .tools.calculator import calculator mcp = FastMCP("my-server") mcp.mount("calc", calculator) # Инструменты: calc_add, calc_multiply ``` ## Тестирование [Заголовок раздела «Тестирование»](#тестирование) ### Unit тесты [Заголовок раздела «Unit тесты»](#unit-тесты) ```python import pytest from server import mcp @pytest.mark.asyncio async def test_add_tool(): result = await mcp.call_tool("add", {"a": 2, "b": 3}) assert result == 5 @pytest.mark.asyncio async def test_divide_by_zero(): with pytest.raises(McpError) as exc: await mcp.call_tool("divide", {"a": 1, "b": 0}) assert "Деление на ноль" in str(exc.value) ``` ### Интеграционные тесты с MCP Inspector [Заголовок раздела «Интеграционные тесты с MCP Inspector»](#интеграционные-тесты-с-mcp-inspector) ```bash # Запуск инспектора npx @modelcontextprotocol/inspector python server.py # Или через uvx uvx mcp-inspector python server.py ``` ## Конфигурация для клиентов [Заголовок раздела «Конфигурация для клиентов»](#конфигурация-для-клиентов) * python claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "python", "args": ["-m", "src.server"], "cwd": "/path/to/my-server", "env": { "DATABASE_URL": "postgresql://localhost/myapp" } } } } ``` * uv claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "uv", "args": ["run", "python", "-m", "src.server"], "cwd": "/path/to/my-server" } } } ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [GitHub: modelcontextprotocol/python-sdk](https://github.com/modelcontextprotocol/python-sdk) * [Официальная документация](https://modelcontextprotocol.io/docs/develop/build-server) * [Примеры серверов](https://github.com/modelcontextprotocol/servers) * [Habr: MCP сервер на Python](https://habr.com/ru/companies/amvera/articles/931874/) # Ruby SDK > Официальный Ruby SDK для Model Context Protocol — создание MCP-серверов с поддержкой Rails и Rack Официальный **Ruby SDK** для Model Context Protocol предоставляет полноценную поддержку создания MCP-серверов и клиентов с интеграцией в Rails и Rack-приложения. ## Возможности [Заголовок раздела «Возможности»](#возможности) * JSON-RPC 2.0 обработка сообщений * Инициализация протокола и согласование capabilities * Регистрация и вызов инструментов (tools) * Регистрация и выполнение prompts * Регистрация и получение ресурсов (resources) * Транспорты: stdio и Streamable HTTP (включая SSE) * Уведомления об изменениях списков ## Установка [Заголовок раздела «Установка»](#установка) * Gemfile ```ruby gem 'mcp' ``` ```bash bundle install ``` * Gem ```bash gem install mcp ``` ## Создание сервера [Заголовок раздела «Создание сервера»](#создание-сервера) ### Базовый сервер [Заголовок раздела «Базовый сервер»](#базовый-сервер) ```ruby require "mcp" server = MCP::Server.new( name: "my_server", title: "Мой MCP Сервер", version: "1.0.0", instructions: "Используйте инструменты этого сервера" ) ``` ### Интеграция с Rails [Заголовок раздела «Интеграция с Rails»](#интеграция-с-rails) ```ruby class McpController < ApplicationController def index server = MCP::Server.new( name: "my_server", title: "Rails MCP Server", version: "1.0.0", tools: [WeatherTool, CalculatorTool], prompts: [GreetingPrompt], server_context: { user_id: current_user.id } ) render json: server.handle_json(request.body.read) end end ``` ### Stdio Transport [Заголовок раздела «Stdio Transport»](#stdio-transport) ```ruby require "mcp" server = MCP::Server.new( name: "example_server", tools: [ExampleTool] ) transport = MCP::Server::Transports::StdioTransport.new(server) transport.open ``` ## Создание инструментов [Заголовок раздела «Создание инструментов»](#создание-инструментов) ### Через класс [Заголовок раздела «Через класс»](#через-класс) ```ruby class WeatherTool < MCP::Tool title "Погода" description "Получает текущую погоду для указанной локации" input_schema( properties: { location: { type: "string", description: "Город" }, units: { type: "string", enum: ["metric", "imperial"] } }, required: ["location"] ) output_schema( properties: { temperature: { type: "number" }, condition: { type: "string" }, humidity: { type: "integer" } }, required: ["temperature", "condition", "humidity"] ) annotations( read_only_hint: true, destructive_hint: false, idempotent_hint: true ) def self.call(location:, units: "metric", server_context:) weather = fetch_weather(location, units) MCP::Tool::Response.new( [{ type: "text", text: weather.to_json }], structured_content: weather ) end end ``` ### Через define [Заголовок раздела «Через define»](#через-define) ```ruby tool = MCP::Tool.define( name: "calculator", title: "Калькулятор", description: "Выполняет математические вычисления" ) do |args, server_context:| expression = args["expression"] result = eval(expression) # Используйте безопасный парсер! MCP::Tool::Response.new([{ type: "text", text: "Результат: #{result}" }]) end ``` ### Через server.define\_tool [Заголовок раздела «Через server.define\_tool»](#через-serverdefine_tool) ```ruby server.define_tool( name: "echo", description: "Возвращает переданное сообщение" ) do |args, server_context:| MCP::Tool::Response.new([{ type: "text", text: args["message"] }]) end ``` ## Создание prompts [Заголовок раздела «Создание prompts»](#создание-prompts) ```ruby class GreetingPrompt < MCP::Prompt prompt_name "greeting" title "Приветствие" description "Генерирует приветственное сообщение" arguments [ MCP::Prompt::Argument.new( name: "name", title: "Имя", description: "Имя для приветствия", required: true ) ] def self.template(args, server_context:) MCP::Prompt::Result.new( description: "Приветствие для #{args['name']}", messages: [ MCP::Prompt::Message.new( role: "user", content: MCP::Content::Text.new("Привет!") ), MCP::Prompt::Message.new( role: "assistant", content: MCP::Content::Text.new("Привет, #{args['name']}!") ) ] ) end end ``` ## Работа с ресурсами [Заголовок раздела «Работа с ресурсами»](#работа-с-ресурсами) ```ruby # Определение ресурса resource = MCP::Resource.new( uri: "https://example.com/docs", name: "documentation", title: "Документация", description: "Техническая документация", mime_type: "text/html" ) server = MCP::Server.new( name: "my_server", resources: [resource] ) # Обработчик чтения ресурсов server.resources_read_handler do |params| [{ uri: params[:uri], mimeType: "text/plain", text: "Содержимое ресурса: #{params[:uri]}" }] end ``` ## Уведомления [Заголовок раздела «Уведомления»](#уведомления) ```ruby # Создание сервера с Streamable HTTP server = MCP::Server.new(name: "my_server") transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) server.transport = transport # Уведомление об изменении списка инструментов server.define_tool(name: "new_tool") { |**args| { result: "ok" } } server.notify_tools_list_changed # Уведомление об изменении prompts server.notify_prompts_list_changed # Уведомление об изменении ресурсов server.notify_resources_list_changed ``` Заметка Для stateless режима (без сессий) используйте `stateless: true`: ```ruby transport = MCP::Server::Transports::StreamableHTTPTransport.new( server, stateless: true ) ``` ## Создание клиента [Заголовок раздела «Создание клиента»](#создание-клиента) ```ruby # HTTP-клиент (требуется gem 'faraday') http_transport = MCP::Client::HTTP.new( url: "https://api.example.com/mcp", headers: { "Authorization" => "Bearer token" } ) client = MCP::Client.new(transport: http_transport) # Список инструментов tools = client.tools tools.each do |tool| puts "Tool: #{tool.name}" puts "Description: #{tool.description}" end # Вызов инструмента response = client.call_tool( tool: tools.first, arguments: { message: "Hello!" } ) ``` ## Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация) ```ruby MCP.configure do |config| # Обработчик исключений config.exception_reporter = ->(exception, server_context) { Bugsnag.notify(exception) do |report| report.add_metadata(:mcp, server_context) end } # Инструментирование config.instrumentation_callback = ->(data) { Rails.logger.info "MCP: #{data.inspect}" # data содержит: method, tool_name, prompt_name, # resource_uri, error, duration } # Версия протокола (опционально) config.protocol_version = "2025-06-18" end ``` ## Server Context [Заголовок раздела «Server Context»](#server-context) `server_context` — хэш с контекстной информацией, передаваемой в tools, prompts и callbacks: ```ruby server = MCP::Server.new( name: "my_server", server_context: { user_id: current_user.id, request_id: request.uuid } ) # Доступен в инструментах class MyTool < MCP::Tool def self.call(server_context:, **args) user = User.find(server_context[:user_id]) # ... end end ``` ## Кастомные методы [Заголовок раздела «Кастомные методы»](#кастомные-методы) ```ruby # Определение кастомного JSON-RPC метода server.define_custom_method(method_name: "add") do |params| params[:a] + params[:b] end # Запрос # {"jsonrpc": "2.0", "id": 1, "method": "add", "params": {"a": 5, "b": 3}} # Ответ # {"jsonrpc": "2.0", "id": 1, "result": 8} ``` ## Поддерживаемые методы [Заголовок раздела «Поддерживаемые методы»](#поддерживаемые-методы) | Метод | Описание | | -------------------------- | ------------------------ | | `initialize` | Инициализация протокола | | `ping` | Проверка доступности | | `tools/list` | Список инструментов | | `tools/call` | Вызов инструмента | | `prompts/list` | Список prompts | | `prompts/get` | Получение prompt | | `resources/list` | Список ресурсов | | `resources/read` | Чтение ресурса | | `resources/templates/list` | Список шаблонов ресурсов | ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [GitHub](https://github.com/modelcontextprotocol/ruby-sdk) * [RubyGems](https://rubygems.org/gems/mcp) * [RubyDoc](https://rubydoc.info/gems/mcp) # Rust SDK (RMCP) > Создание высокопроизводительных MCP серверов на Rust с RMCP RMCP — официальный Rust SDK для MCP с процедурными макросами для минимального бойлерплейта. ## Установка [Заголовок раздела «Установка»](#установка) Добавьте в `Cargo.toml`: * stdio Cargo.toml ```toml [dependencies] rmcp = { version = "0.1", features = ["server", "transport-io"] } tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1" ``` * HTTP/SSE Cargo.toml ```toml [dependencies] rmcp = { version = "0.1", features = ["server", "transport-sse-server"] } tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1" ``` **Требования:** * Rust 1.75+ * Cargo ## Базовый сервер [Заголовок раздела «Базовый сервер»](#базовый-сервер) src/main.rs ```rust use rmcp::{Server, ServerHandler, model::ServerInfo}; use rmcp::transport::io::stdio; struct MyServer; impl ServerHandler for MyServer { fn get_info(&self) -> ServerInfo { ServerInfo { name: "rust-server".into(), version: "1.0.0".into(), ..Default::default() } } } #[tokio::main] async fn main() -> Result<(), Box> { let server = Server::new(MyServer); let transport = stdio(); server.run(transport).await?; Ok(()) } ``` ## Инструменты (Tools) [Заголовок раздела «Инструменты (Tools)»](#инструменты-tools) ### Макрос #\[tool] [Заголовок раздела «Макрос #\[tool\]»](#макрос-tool) src/tools.rs ```rust use rmcp::tool; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, schemars::JsonSchema)] struct AddParams { /// Первое число a: f64, /// Второе число b: f64, } #[tool( name = "add", description = "Сложение двух чисел" )] async fn add(params: AddParams) -> String { format!("Результат: {}", params.a + params.b) } ``` ### Tool Router для группировки [Заголовок раздела «Tool Router для группировки»](#tool-router-для-группировки) src/calculator.rs ```rust use rmcp::{tool, tool_router}; #[tool_router] impl Calculator { #[tool(description = "Сложение")] async fn add(&self, a: f64, b: f64) -> f64 { a + b } #[tool(description = "Умножение")] async fn multiply(&self, a: f64, b: f64) -> f64 { a * b } #[tool(description = "Деление")] async fn divide(&self, a: f64, b: f64) -> Result { if b == 0.0 { Err("Деление на ноль".into()) } else { Ok(a / b) } } } ``` ### Регистрация инструментов [Заголовок раздела «Регистрация инструментов»](#регистрация-инструментов) src/server.rs ```rust use rmcp::{Server, ServerHandler}; struct MyServer { calculator: Calculator, } impl ServerHandler for MyServer { fn get_info(&self) -> ServerInfo { ServerInfo::new("calc-server", "1.0.0") } fn list_tools(&self) -> Vec { self.calculator.list_tools() } async fn call_tool(&self, name: &str, params: Value) -> ToolResult { self.calculator.call_tool(name, params).await } } ``` ### Инструмент с HTTP запросами [Заголовок раздела «Инструмент с HTTP запросами»](#инструмент-с-http-запросами) src/http\_tools.rs ```rust use rmcp::{tool_router, tool}; use reqwest::Client; struct HttpTools { client: Client, } #[tool_router] impl HttpTools { #[tool(description = "Загрузка содержимого URL")] async fn fetch_url(&self, url: String) -> Result { self.client .get(&url) .send() .await .map_err(|e| e.to_string())? .text() .await .map_err(|e| e.to_string()) } #[tool(description = "POST запрос")] async fn post_json(&self, url: String, body: String) -> Result { self.client .post(&url) .header("Content-Type", "application/json") .body(body) .send() .await .map_err(|e| e.to_string())? .text() .await .map_err(|e| e.to_string()) } } ``` ### Инструмент с файловой системой [Заголовок раздела «Инструмент с файловой системой»](#инструмент-с-файловой-системой) src/file\_tools.rs ```rust use rmcp::{tool_router, tool}; use std::path::Path; use tokio::fs; struct FileTools { allowed_path: String, } #[tool_router] impl FileTools { #[tool(description = "Чтение файла")] async fn read_file(&self, path: String) -> Result { let full_path = Path::new(&self.allowed_path).join(&path); // Проверка безопасности if !full_path.starts_with(&self.allowed_path) { return Err("Доступ запрещён".into()); } fs::read_to_string(&full_path) .await .map_err(|e| format!("Ошибка чтения: {}", e)) } #[tool(description = "Запись файла")] async fn write_file(&self, path: String, content: String) -> Result { let full_path = Path::new(&self.allowed_path).join(&path); if !full_path.starts_with(&self.allowed_path) { return Err("Доступ запрещён".into()); } fs::write(&full_path, content) .await .map_err(|e| format!("Ошибка записи: {}", e))?; Ok(format!("Файл сохранён: {}", path)) } } ``` ## Ресурсы (Resources) [Заголовок раздела «Ресурсы (Resources)»](#ресурсы-resources) src/resources.rs ```rust use rmcp::{resource, Resource, ResourceContent}; #[resource( uri = "config://app", name = "App Configuration", description = "Конфигурация приложения", mime_type = "application/json" )] async fn app_config() -> ResourceContent { let config = serde_json::json!({ "version": "1.0.0", "environment": "production" }); ResourceContent::text(config.to_string()) } ``` ### Динамический ресурс с шаблоном [Заголовок раздела «Динамический ресурс с шаблоном»](#динамический-ресурс-с-шаблоном) src/resources.rs ```rust use rmcp::{resource_template, ResourceContent}; #[resource_template( uri_template = "file://{path}", name = "File Reader", description = "Чтение файлов" )] async fn read_file(path: String) -> Result { let content = tokio::fs::read_to_string(&path) .await .map_err(|e| e.to_string())?; Ok(ResourceContent::text(content)) } ``` ### Список ресурсов [Заголовок раздела «Список ресурсов»](#список-ресурсов) src/server.rs ```rust impl ServerHandler for MyServer { fn list_resources(&self) -> Vec { vec![ Resource::new("config://app", "Конфигурация"), Resource::template("file://{path}", "Файлы"), ] } async fn read_resource(&self, uri: &str) -> ResourceResult { match uri { "config://app" => app_config().await, uri if uri.starts_with("file://") => { let path = uri.strip_prefix("file://").unwrap(); read_file(path.to_string()).await } _ => Err("Resource not found".into()), } } } ``` ## Промпты (Prompts) [Заголовок раздела «Промпты (Prompts)»](#промпты-prompts) src/prompts.rs ````rust use rmcp::{prompt, Prompt, PromptMessage}; #[prompt( name = "code_review", description = "Промпт для код-ревью" )] async fn code_review(language: String, code: String) -> Vec { vec![ PromptMessage::user(format!( "Проведи код-ревью следующего {} кода:\n\n```{}\n{}\n```", language, language, code )) ] } #[prompt( name = "debug_helper", description = "Помощь с отладкой" )] async fn debug_helper(error: String, context: String) -> Vec { vec![ PromptMessage::system("Ты опытный отладчик. Анализируй ошибки и предлагай решения."), PromptMessage::user(format!( "Ошибка: {}\n\nКонтекст:\n{}", error, context )) ] } ```` ## Обработка ошибок [Заголовок раздела «Обработка ошибок»](#обработка-ошибок) src/error.rs ```rust use mcp_rust_sdk::error::McpError; use thiserror::Error; #[derive(Error, Debug)] pub enum ServerError { #[error("Файл не найден: {0}")] FileNotFound(String), #[error("Доступ запрещён: {0}")] AccessDenied(String), #[error("Неверные параметры: {0}")] InvalidParams(String), } impl From for McpError { fn from(err: ServerError) -> Self { match err { ServerError::FileNotFound(msg) => McpError::not_found(msg), ServerError::AccessDenied(msg) => McpError::forbidden(msg), ServerError::InvalidParams(msg) => McpError::invalid_params(msg), } } } ``` ## Логирование [Заголовок раздела «Логирование»](#логирование) src/logging.rs ```rust use tracing::{info, error, debug}; use tracing_subscriber; fn setup_logging() { tracing_subscriber::fmt() .with_env_filter("mcp=debug,my_server=info") .init(); } impl ToolHandler for MyTool { async fn execute(&self, params: Value) -> ToolResult { info!("Выполнение инструмента с параметрами: {:?}", params); match self.do_work(params).await { Ok(result) => { debug!("Успешное выполнение"); Ok(result) } Err(e) => { error!("Ошибка: {:?}", e); Err(e) } } } } ``` ## Транспорты [Заголовок раздела «Транспорты»](#транспорты) ### Stdio (стандартный) [Заголовок раздела «Stdio (стандартный)»](#stdio-стандартный) src/main.rs ```rust use rmcp::transport::io::stdio; let transport = stdio(); server.run(transport).await?; ``` ### SSE Server [Заголовок раздела «SSE Server»](#sse-server) src/main.rs ```rust use rmcp::transport::sse_server::SseServerTransport; use axum::Router; let (transport, router) = SseServerTransport::new("/mcp"); let app = Router::new().merge(router); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?; tokio::spawn(async move { axum::serve(listener, app).await.unwrap(); }); server.run(transport).await?; ``` ## Полный пример сервера [Заголовок раздела «Полный пример сервера»](#полный-пример-сервера) src/main.rs ```rust use rmcp::{Server, ServerHandler, tool_router, tool}; use rmcp::model::ServerInfo; use rmcp::transport::io::stdio; struct Calculator; #[tool_router] impl Calculator { #[tool(description = "Сложение")] async fn add(&self, a: f64, b: f64) -> f64 { a + b } #[tool(description = "Умножение")] async fn multiply(&self, a: f64, b: f64) -> f64 { a * b } } struct MyServer { calc: Calculator, } impl ServerHandler for MyServer { fn get_info(&self) -> ServerInfo { ServerInfo::new("calc-server", "1.0.0") } fn list_tools(&self) -> Vec { self.calc.list_tools() } async fn call_tool(&self, name: &str, params: serde_json::Value) -> rmcp::ToolResult { self.calc.call_tool(name, params).await } } #[tokio::main] async fn main() -> Result<(), Box> { tracing_subscriber::fmt().init(); let server = Server::new(MyServer { calc: Calculator }); server.run(stdio()).await?; Ok(()) } ``` ## Структура проекта [Заголовок раздела «Структура проекта»](#структура-проекта) ```plaintext my-mcp-server/ ├── src/ │ ├── main.rs │ ├── lib.rs │ ├── tools/ │ │ ├── mod.rs │ │ ├── calculator.rs │ │ └── filesystem.rs │ ├── resources/ │ │ ├── mod.rs │ │ └── config.rs │ └── prompts/ │ ├── mod.rs │ └── templates.rs ├── Cargo.toml ├── Dockerfile └── README.md ``` ## Docker [Заголовок раздела «Docker»](#docker) Dockerfile ```dockerfile # Build stage FROM rust:1.75 as builder WORKDIR /app COPY . . RUN cargo build --release # Runtime stage 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"] ``` ## Конфигурация Claude Desktop [Заголовок раздела «Конфигурация Claude Desktop»](#конфигурация-claude-desktop) claude\_desktop\_config.json ```json { "mcpServers": { "rust-server": { "command": "/path/to/target/release/mcp-server" } } } ``` ## Cargo.toml [Заголовок раздела «Cargo.toml»](#cargotoml) Cargo.toml ```toml [package] name = "mcp-server" version = "1.0.0" edition = "2021" [dependencies] rmcp = { version = "0.1", features = ["server", "transport-io"] } tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1" schemars = "0.8" tracing = "0.1" tracing-subscriber = "0.3" # Для HTTP axum = "0.7" rmcp = { version = "0.1", features = ["server", "transport-sse-server"] } ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [GitHub: modelcontextprotocol/rust-sdk (RMCP)](https://github.com/modelcontextprotocol/rust-sdk) * [crates.io: rmcp](https://crates.io/crates/rmcp) * [Официальная документация](https://modelcontextprotocol.io/docs/develop/build-server) # Swift SDK > Официальный Swift SDK для Model Context Protocol — создание MCP-серверов и клиентов для Apple платформ и Linux Официальный **Swift SDK** для Model Context Protocol реализует клиентские и серверные компоненты согласно спецификации MCP 2025-03-26 для Apple платформ и Linux. ## Требования [Заголовок раздела «Требования»](#требования) * **Swift 6.0+** (Xcode 16+) ### Поддержка платформ [Заголовок раздела «Поддержка платформ»](#поддержка-платформ) | Платформа | Минимальная версия | | ------------------ | ----------------------------------------------- | | macOS | 13.0+ | | iOS / Mac Catalyst | 16.0+ | | watchOS | 9.0+ | | tvOS | 16.0+ | | visionOS | 1.0+ | | Linux | glibc или musl (Ubuntu, Debian, Fedora, Alpine) | ## Установка [Заголовок раздела «Установка»](#установка) ### Swift Package Manager [Заголовок раздела «Swift Package Manager»](#swift-package-manager) Package.swift ```swift dependencies: [ .package(url: "https://github.com/modelcontextprotocol/swift-sdk.git", from: "0.10.0") ] // В target .target( name: "YourTarget", dependencies: [ .product(name: "MCP", package: "swift-sdk") ] ) ``` ## Клиент [Заголовок раздела «Клиент»](#клиент) ### Базовая настройка [Заголовок раздела «Базовая настройка»](#базовая-настройка) ```swift import MCP // Инициализация клиента let client = Client(name: "MyApp", version: "1.0.0") // Подключение через транспорт let transport = StdioTransport() let result = try await client.connect(transport: transport) // Проверка capabilities сервера if result.capabilities.tools != nil { // Сервер поддерживает инструменты } ``` ### Транспорты для клиента [Заголовок раздела «Транспорты для клиента»](#транспорты-для-клиента) **Stdio (для локальных процессов):** ```swift let transport = StdioTransport() try await client.connect(transport: transport) ``` **HTTP (для удалённых серверов):** ```swift let transport = HTTPClientTransport( endpoint: URL(string: "http://localhost:8080")!, streaming: true // SSE для real-time обновлений ) try await client.connect(transport: transport) ``` ### Работа с инструментами [Заголовок раздела «Работа с инструментами»](#работа-с-инструментами) ```swift // Список инструментов let (tools, cursor) = try await client.listTools() print("Инструменты: \(tools.map { $0.name }.joined(separator: ", "))") // Вызов инструмента let (content, isError) = try await client.callTool( name: "image-generator", arguments: [ "prompt": "Горный пейзаж на закате", "style": "photorealistic", "width": 1024, "height": 768 ] ) // Обработка результата for item in content { switch item { case .text(let text): print("Текст: \(text)") case .image(let data, let mimeType, let metadata): print("Изображение: \(mimeType)") case .audio(let data, let mimeType): print("Аудио: \(mimeType)") case .resource(let uri, let mimeType, let text): print("Ресурс: \(uri)") } } ``` ### Работа с ресурсами [Заголовок раздела «Работа с ресурсами»](#работа-с-ресурсами) ```swift // Список ресурсов let (resources, nextCursor) = try await client.listResources() // Чтение ресурса let contents = try await client.readResource(uri: "resource://example") // Подписка на обновления if result.capabilities.resources.subscribe { try await client.subscribeToResource(uri: "resource://example") await client.onNotification(ResourceUpdatedNotification.self) { message in print("Ресурс \(message.params.uri) обновлён") let updated = try await client.readResource(uri: message.params.uri) } } ``` ### Работа с prompts [Заголовок раздела «Работа с prompts»](#работа-с-prompts) ```swift // Список prompts let (prompts, nextCursor) = try await client.listPrompts() // Получение prompt с аргументами let (description, messages) = try await client.getPrompt( name: "customer-service", arguments: [ "customerName": "Алиса", "orderNumber": "ORD-12345", "issue": "задержка доставки" ] ) for message in messages { if case .text(text: let text) = message.content { print("\(message.role): \(text)") } } ``` ### Sampling (сервер → клиент) [Заголовок раздела «Sampling (сервер → клиент)»](#sampling-сервер--клиент) ```swift // Регистрация обработчика sampling await client.withSamplingHandler { parameters in // Просмотр запроса (human-in-the-loop) print("Сервер запрашивает: \(parameters.messages)") // Вызов вашего LLM let completion = try await callYourLLMService( messages: parameters.messages, maxTokens: parameters.maxTokens, temperature: parameters.temperature ) // Возврат результата return CreateSamplingMessage.Result( model: "your-model-name", stopReason: .endTurn, role: .assistant, content: .text(completion) ) } ``` Совет Sampling-запросы идут от **сервера к клиенту**, позволяя серверам запрашивать AI-помощь, пока клиенты контролируют доступ к модели. ### Batching запросов [Заголовок раздела «Batching запросов»](#batching-запросов) ```swift var toolTasks: [Task] = [] // Отправка пакета запросов try await client.withBatch { batch in for i in 0..<10 { toolTasks.append( try await batch.addRequest( CallTool.request(.init(name: "square", arguments: ["n": Value(i)])) ) ) } } // Обработка результатов for (index, task) in toolTasks.enumerated() { do { let result = try await task.value print("\(index): \(result.content)") } catch { print("\(index) failed: \(error)") } } ``` ## Сервер [Заголовок раздела «Сервер»](#сервер) ### Базовая настройка [Заголовок раздела «Базовая настройка»](#базовая-настройка-1) ```swift import MCP let server = Server( name: "MyModelServer", version: "1.0.0", capabilities: .init( prompts: .init(listChanged: true), resources: .init(subscribe: true, listChanged: true), tools: .init(listChanged: true) ) ) let transport = StdioTransport() try await server.start(transport: transport) ``` ### Регистрация инструментов [Заголовок раздела «Регистрация инструментов»](#регистрация-инструментов) ```swift // Список инструментов await server.withMethodHandler(ListTools.self) { _ in let tools = [ Tool( name: "weather", description: "Получить погоду для локации", inputSchema: .object([ "properties": .object([ "location": .string("Город или координаты"), "units": .string("metric или imperial") ]) ]) ), Tool( name: "calculator", description: "Выполнить вычисления", inputSchema: .object([ "properties": .object([ "expression": .string("Математическое выражение") ]) ]) ) ] return .init(tools: tools) } // Вызов инструмента await server.withMethodHandler(CallTool.self) { params in switch params.name { case "weather": let location = params.arguments?["location"]?.stringValue ?? "Unknown" let weather = getWeatherData(location: location) return .init( content: [.text("Погода в \(location): \(weather.temperature)°")], isError: false ) case "calculator": if let expression = params.arguments?["expression"]?.stringValue { let result = evaluateExpression(expression) return .init(content: [.text("\(result)")], isError: false) } return .init(content: [.text("Нет выражения")], isError: true) default: return .init(content: [.text("Неизвестный инструмент")], isError: true) } } ``` ### Регистрация ресурсов [Заголовок раздела «Регистрация ресурсов»](#регистрация-ресурсов) ```swift await server.withMethodHandler(ListResources.self) { params in let resources = [ Resource( name: "База знаний", uri: "resource://knowledge-base", description: "Документация и статьи" ), Resource( name: "Статус системы", uri: "resource://system/status", description: "Текущее состояние системы" ) ] return .init(resources: resources, nextCursor: nil) } await server.withMethodHandler(ReadResource.self) { params in switch params.uri { case "resource://knowledge-base": return .init(contents: [ Resource.Content.text("# База знаний\n\nСодержимое...", uri: params.uri) ]) case "resource://system/status": let status = getCurrentSystemStatus() return .init(contents: [ Resource.Content.text(status.toJSON(), uri: params.uri, mimeType: "application/json") ]) default: throw MCPError.invalidParams("Неизвестный URI: \(params.uri)") } } ``` ### Initialize Hook [Заголовок раздела «Initialize Hook»](#initialize-hook) ```swift try await server.start(transport: transport) { clientInfo, clientCapabilities in // Валидация клиента guard clientInfo.name != "BlockedClient" else { throw MCPError.invalidRequest("Клиент заблокирован") } // Проверка capabilities if clientCapabilities.sampling == nil { print("Клиент не поддерживает sampling") } print("Подключён: \(clientInfo.name) v\(clientInfo.version)") } ``` ## Graceful Shutdown с Service Lifecycle [Заголовок раздела «Graceful Shutdown с Service Lifecycle»](#graceful-shutdown-с-service-lifecycle) ```swift import MCP import ServiceLifecycle import Logging struct MCPService: Service { let server: Server let transport: Transport func run() async throws { try await server.start(transport: transport) try await Task.sleep(for: .days(365 * 100)) } func shutdown() async throws { await server.stop() } } // Использование let server = Server(name: "MyServer", version: "1.0.0", capabilities: ...) let transport = StdioTransport() let mcpService = MCPService(server: server, transport: transport) let serviceGroup = ServiceGroup( services: [mcpService], configuration: .init(gracefulShutdownSignals: [.sigterm, .sigint]), logger: Logger(label: "mcp-server") ) try await serviceGroup.run() ``` ## Транспорты [Заголовок раздела «Транспорты»](#транспорты) | Транспорт | Описание | Платформы | | --------------------- | ----------------------- | ---------------- | | `StdioTransport` | stdio (stdin/stdout) | Apple, Linux | | `HTTPClientTransport` | Streamable HTTP | Все с Foundation | | `InMemoryTransport` | В памяти для тестов | Все | | `NetworkTransport` | Apple Network.framework | Только Apple | ### Кастомный транспорт [Заголовок раздела «Кастомный транспорт»](#кастомный-транспорт) ```swift public actor MyCustomTransport: Transport { public nonisolated let logger: Logger private var isConnected = false public func connect() async throws { isConnected = true } public func disconnect() async { isConnected = false } public func send(_ data: Data) async throws { // Отправка данных } public func receive() -> AsyncThrowingStream { // Получение данных } } ``` ## Логирование [Заголовок раздела «Логирование»](#логирование) ```swift import Logging import MCP LoggingSystem.bootstrap { label in var handler = StreamLogHandler.standardOutput(label: label) handler.logLevel = .debug return handler } let logger = Logger(label: "com.example.mcp") let transport = StdioTransport(logger: logger) ``` ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [GitHub](https://github.com/modelcontextprotocol/swift-sdk) * [MCP Specification](https://modelcontextprotocol.io/specification/2025-03-26/) * [Swift Service Lifecycle](https://github.com/swift-server/swift-service-lifecycle) # TypeScript SDK > Создание MCP серверов на TypeScript с официальным SDK для Node.js Официальный TypeScript SDK предоставляет полную реализацию MCP протокола для Node.js с поддержкой stdio, HTTP и SSE транспортов. Версии SDK * **v1.x** — стабильная версия для продакшена * **v2.x alpha** — новые фичи: StreamableHTTP, улучшенные сессии ## Установка [Заголовок раздела «Установка»](#установка) * npm Terminal ```bash # Стабильная v1.x npm install @modelcontextprotocol/sdk # Альфа v2.x с StreamableHTTP npm install @modelcontextprotocol/sdk@next ``` * pnpm Terminal ```bash pnpm add @modelcontextprotocol/sdk ``` * yarn Terminal ```bash yarn add @modelcontextprotocol/sdk ``` **Требования:** * Node.js 18+ * TypeScript 5.0+ ## Конфигурация TypeScript [Заголовок раздела «Конфигурация TypeScript»](#конфигурация-typescript) tsconfig.json ```json { "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "outDir": "./dist" }, "include": ["src/**/*"] } ``` ## Базовый сервер [Заголовок раздела «Базовый сервер»](#базовый-сервер) ```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", }); // Запуск через stdio const transport = new StdioServerTransport(); await server.connect(transport); ``` ## Инструменты (Tools) [Заголовок раздела «Инструменты (Tools)»](#инструменты-tools) ### Базовый инструмент [Заголовок раздела «Базовый инструмент»](#базовый-инструмент) ```typescript server.tool( "add", { a: { type: "number", description: "Первое число" }, b: { type: "number", description: "Второе число" }, }, async ({ a, b }) => ({ content: [{ type: "text", text: `Результат: ${a + b}` }], }) ); ``` ### Инструмент с валидацией [Заголовок раздела «Инструмент с валидацией»](#инструмент-с-валидацией) ```typescript import { z } from "zod"; const SearchSchema = z.object({ query: z.string().min(1).describe("Поисковый запрос"), limit: z.number().int().min(1).max(100).default(10).describe("Лимит результатов"), offset: z.number().int().min(0).default(0).describe("Смещение"), }); server.tool( "search", SearchSchema, async (params) => { const { query, limit, offset } = params; const results = await database.search(query, { limit, offset }); return { content: [ { type: "text", text: JSON.stringify(results, null, 2), }, ], }; } ); ``` ### Инструмент с обработкой ошибок [Заголовок раздела «Инструмент с обработкой ошибок»](#инструмент-с-обработкой-ошибок) ```typescript server.tool( "divide", { a: { type: "number" }, b: { type: "number" }, }, async ({ a, b }) => { if (b === 0) { return { content: [{ type: "text", text: "Ошибка: деление на ноль" }], isError: true, }; } return { content: [{ type: "text", text: `Результат: ${a / b}` }], }; } ); ``` ### Инструмент с изображением [Заголовок раздела «Инструмент с изображением»](#инструмент-с-изображением) ```typescript import fs from "fs/promises"; server.tool( "get_screenshot", { path: { type: "string", description: "Путь к изображению" }, }, async ({ path }) => { const data = await fs.readFile(path); const base64 = data.toString("base64"); return { content: [ { type: "image", data: base64, mimeType: "image/png", }, ], }; } ); ``` ## Ресурсы (Resources) [Заголовок раздела «Ресурсы (Resources)»](#ресурсы-resources) ### Статический ресурс [Заголовок раздела «Статический ресурс»](#статический-ресурс) ```typescript server.resource( "config://app", async () => ({ contents: [ { uri: "config://app", mimeType: "application/json", text: JSON.stringify({ version: "1.0.0", environment: "production", }), }, ], }) ); ``` ### Динамический ресурс с шаблоном [Заголовок раздела «Динамический ресурс с шаблоном»](#динамический-ресурс-с-шаблоном) ```typescript server.resource( "file://{path}", async ({ path }) => { const content = await fs.readFile(path, "utf-8"); const mimeType = getMimeType(path); return { contents: [ { uri: `file://${path}`, mimeType, text: content, }, ], }; } ); ``` ### Список ресурсов [Заголовок раздела «Список ресурсов»](#список-ресурсов) ```typescript server.setResourcesHandler(async () => { const files = await fs.readdir("./data"); return { resources: files.map((file) => ({ uri: `file://data/${file}`, name: file, description: `Файл ${file}`, mimeType: getMimeType(file), })), }; }); ``` ## Промпты (Prompts) [Заголовок раздела «Промпты (Prompts)»](#промпты-prompts) ### Базовый промпт [Заголовок раздела «Базовый промпт»](#базовый-промпт) ```typescript server.prompt( "code_review", { language: { type: "string", description: "Язык программирования" }, code: { type: "string", description: "Код для ревью" }, }, async ({ language, code }) => ({ messages: [ { role: "user", content: { type: "text", text: `Проведи код-ревью следующего ${language} кода:\n\n\`\`\`${language}\n${code}\n\`\`\``, }, }, ], }) ); ``` ### Промпт с системным сообщением [Заголовок раздела «Промпт с системным сообщением»](#промпт-с-системным-сообщением) ```typescript server.prompt( "sql_expert", { question: { type: "string" }, schema: { type: "string" }, }, async ({ question, schema }) => ({ messages: [ { role: "assistant", content: { type: "text", text: "Я SQL эксперт. Помогу с оптимизацией запросов и проектированием схемы.", }, }, { role: "user", content: { type: "text", text: `Схема БД:\n${schema}\n\nВопрос: ${question}`, }, }, ], }) ); ``` ## Транспорты [Заголовок раздела «Транспорты»](#транспорты) ### Stdio (стандартный) [Заголовок раздела «Stdio (стандартный)»](#stdio-стандартный) ```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" }); const transport = new StdioServerTransport(); await server.connect(transport); ``` ### SSE (Server-Sent Events) [Заголовок раздела «SSE (Server-Sent Events)»](#sse-server-sent-events) ```typescript import express from "express"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; const app = express(); const server = new McpServer({ name: "sse-server", version: "1.0.0" }); // Хранение сессий const sessions = new Map(); app.get("/sse", (req, res) => { const sessionId = crypto.randomUUID(); const transport = new SSEServerTransport("/message", res); sessions.set(sessionId, transport); server.connect(transport); res.on("close", () => sessions.delete(sessionId)); }); app.post("/message", async (req, res) => { const sessionId = req.query.sessionId as string; const transport = sessions.get(sessionId); if (!transport) { return res.status(404).json({ error: "Session not found" }); } await transport.handlePostMessage(req, res); }); app.listen(3000); ``` ### StreamableHTTP (v2 alpha) [Заголовок раздела «StreamableHTTP (v2 alpha)»](#streamablehttp-v2-alpha) ```typescript import express from "express"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; const app = express(); app.use(express.json()); const server = new McpServer({ name: "http-server", version: "1.0.0" }); // Управление сессиями const transports = new Map(); app.post("/mcp", async (req, res) => { const sessionId = req.headers["mcp-session-id"] as string | undefined; let transport: StreamableHTTPServerTransport; if (sessionId && transports.has(sessionId)) { transport = transports.get(sessionId)!; } else if (!sessionId && isInitializeRequest(req.body)) { transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => crypto.randomUUID(), onsessioninitialized: (id) => { transports.set(id, transport); }, }); await server.connect(transport); } else { return res.status(400).json({ error: "Invalid session" }); } await transport.handleRequest(req, res); }); app.delete("/mcp", async (req, res) => { const sessionId = req.headers["mcp-session-id"] as string; const transport = transports.get(sessionId); if (transport) { await transport.close(); transports.delete(sessionId); } res.status(204).end(); }); app.listen(3000); ``` ## Модульная структура [Заголовок раздела «Модульная структура»](#модульная-структура) ```plaintext my-mcp-server/ ├── src/ │ ├── index.ts # Entry point │ ├── server.ts # Server setup │ ├── tools/ │ │ ├── index.ts │ │ ├── calculator.ts │ │ └── filesystem.ts │ ├── resources/ │ │ ├── index.ts │ │ └── config.ts │ └── prompts/ │ ├── index.ts │ └── templates.ts ├── tests/ ├── package.json ├── tsconfig.json └── README.md ``` ### src/server.ts [Заголовок раздела «src/server.ts»](#srcserverts) ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { registerCalculatorTools } from "./tools/calculator.js"; import { registerFilesystemTools } from "./tools/filesystem.js"; import { registerResources } from "./resources/index.js"; import { registerPrompts } from "./prompts/index.js"; export function createServer() { const server = new McpServer({ name: "modular-server", version: "1.0.0", }); registerCalculatorTools(server); registerFilesystemTools(server); registerResources(server); registerPrompts(server); return server; } ``` ### src/tools/calculator.ts [Заголовок раздела «src/tools/calculator.ts»](#srctoolscalculatorts) ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; export function registerCalculatorTools(server: McpServer) { server.tool( "add", { a: { type: "number" }, b: { type: "number" } }, async ({ a, b }) => ({ content: [{ type: "text", text: String(a + b) }], }) ); server.tool( "multiply", { a: { type: "number" }, b: { type: "number" } }, async ({ a, b }) => ({ content: [{ type: "text", text: String(a * b) }], }) ); } ``` ## Starter Template [Заголовок раздела «Starter Template»](#starter-template) Рекомендуется использовать готовый шаблон: ```bash # Клонирование шаблона git clone https://github.com/alexanderop/mcp-server-starter-ts.git my-server cd my-server npm install # Разработка npm run dev # Сборка npm run build # Тестирование с инспектором npm run inspector ``` Шаблон включает: * Dual transport (stdio + HTTP/SSE) * Docker support с multi-stage builds * MCP Inspector интеграция * Hygen генераторы для быстрого добавления tools ## Конфигурация клиентов [Заголовок раздела «Конфигурация клиентов»](#конфигурация-клиентов) * node claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "node", "args": ["/path/to/dist/index.js"] } } } ``` * npx claude\_desktop\_config.json ```json { "mcpServers": { "my-server": { "command": "npx", "args": ["-y", "@myorg/mcp-server"] } } } ``` ## Тестирование [Заголовок раздела «Тестирование»](#тестирование) ### Unit тесты с Jest [Заголовок раздела «Unit тесты с Jest»](#unit-тесты-с-jest) ```typescript import { createServer } from "../src/server.js"; describe("Calculator Tools", () => { const server = createServer(); test("add returns correct sum", async () => { const result = await server.callTool("add", { a: 2, b: 3 }); expect(result.content[0].text).toBe("5"); }); test("divide by zero returns error", async () => { const result = await server.callTool("divide", { a: 1, b: 0 }); expect(result.isError).toBe(true); }); }); ``` ### MCP Inspector [Заголовок раздела «MCP Inspector»](#mcp-inspector) ```bash npx @modelcontextprotocol/inspector node dist/index.js ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [GitHub: modelcontextprotocol/typescript-sdk](https://github.com/modelcontextprotocol/typescript-sdk) * [Starter Template](https://github.com/alexanderop/mcp-server-starter-ts) * [npm: @modelcontextprotocol/sdk](https://www.npmjs.com/package/@modelcontextprotocol/sdk) * [Официальная документация](https://modelcontextprotocol.io/docs/develop/build-server) # Безопасность MCP серверов > Best practices по безопасности MCP серверов для production ## Основные принципы [Заголовок раздела «Основные принципы»](#основные-принципы) 1. **Принцип минимальных привилегий** — сервер должен иметь только необходимые права 2. **Валидация всех входных данных** — никогда не доверяйте входным параметрам 3. **Изоляция** — запускайте серверы в sandbox окружении 4. **Аудит** — логируйте все операции ## Session Security [Заголовок раздела «Session Security»](#session-security) session.py ```python import secrets import uuid def generate_session_id() -> str: """Безопасная генерация session ID""" return str(uuid.uuid4()) + "-" + secrets.token_hex(16) # Привязка к пользователю sessions = {} def create_session(user_id: str) -> str: session_id = generate_session_id() sessions[session_id] = { "user_id": user_id, "created_at": datetime.utcnow(), "expires_at": datetime.utcnow() + timedelta(hours=1) } return session_id ``` ## Валидация входных данных [Заголовок раздела «Валидация входных данных»](#валидация-входных-данных) validation.py ```python from pydantic import BaseModel, Field, validator import re class FileReadInput(BaseModel): path: str = Field(..., max_length=255) @validator('path') def validate_path(cls, v): # Запрет path traversal if '..' in v or v.startswith('/'): raise ValueError('Invalid path') # Только разрешённые символы if not re.match(r'^[\w\-./]+$', v): raise ValueError('Invalid characters in path') return v @mcp.tool() def read_file(input: FileReadInput) -> str: """Безопасное чтение файла""" allowed_dir = "/app/data" full_path = os.path.join(allowed_dir, input.path) # Проверка, что путь внутри allowed_dir real_path = os.path.realpath(full_path) if not real_path.startswith(os.path.realpath(allowed_dir)): raise McpError("FORBIDDEN", "Access denied") return open(real_path).read() ``` ## Sandboxing [Заголовок раздела «Sandboxing»](#sandboxing) ### Docker с ограничениями [Заголовок раздела «Docker с ограничениями»](#docker-с-ограничениями) docker-compose.yml ```yaml services: mcp-server: read_only: true cap_drop: - ALL security_opt: - no-new-privileges:true tmpfs: - /tmp:size=100M ulimits: nofile: soft: 1024 hard: 2048 ``` ### seccomp профиль [Заголовок раздела «seccomp профиль»](#seccomp-профиль) seccomp.json ```json { "defaultAction": "SCMP_ACT_ERRNO", "syscalls": [ { "names": ["read", "write", "open", "close", "stat", "fstat"], "action": "SCMP_ACT_ALLOW" } ] } ``` ## Rate Limiting [Заголовок раздела «Rate Limiting»](#rate-limiting) rate\_limiter.py ```python from collections import defaultdict import time class RateLimiter: def __init__(self, max_requests: int, window_seconds: int): self.max_requests = max_requests self.window = window_seconds self.requests = defaultdict(list) def is_allowed(self, client_id: str) -> bool: now = time.time() window_start = now - self.window # Очистка старых запросов self.requests[client_id] = [ t for t in self.requests[client_id] if t > window_start ] if len(self.requests[client_id]) >= self.max_requests: return False self.requests[client_id].append(now) return True limiter = RateLimiter(max_requests=100, window_seconds=60) ``` ## Логирование и аудит [Заголовок раздела «Логирование и аудит»](#логирование-и-аудит) audit.py ```python import logging import json audit_logger = logging.getLogger("audit") audit_logger.setLevel(logging.INFO) def audit_log(event: str, user_id: str, details: dict): audit_logger.info(json.dumps({ "timestamp": datetime.utcnow().isoformat(), "event": event, "user_id": user_id, "details": details })) @mcp.tool() def sensitive_operation(data: str, ctx: Context) -> str: audit_log("sensitive_operation", ctx.user_id, {"data_length": len(data)}) return process(data) ``` ## Полезные ссылки [Заголовок раздела «Полезные ссылки»](#полезные-ссылки) * [MCP Security Best Practices](https://modelcontextprotocol.io/specification/draft/basic/security_best_practices) * [OWASP Top 10](https://owasp.org/www-project-top-ten/) # Context7 MCP Server > MCP сервер для получения актуальной документации библиотек в реальном времени **Context7** — это MCP сервер, который предоставляет AI-ассистентам доступ к актуальной документации и примерам кода для любых библиотек. Решает проблему устаревших знаний LLM о API и синтаксисе. ## Зачем нужен Context7 [Заголовок раздела «Зачем нужен Context7»](#зачем-нужен-context7) Актуальные данные LLM обучены на данных прошлых лет. Context7 предоставляет документацию в реальном времени Меньше галлюцинаций Вместо выдумывания API, ассистент получает реальные примеры из официальной документации 30+ клиентов Поддержка Cursor, Claude Desktop, VS Code, Windsurf, Zed и многих других Простая интеграция Один npx-запуск или удалённый сервер — никаких сложных настроек ## Архитектура [Заголовок раздела «Архитектура»](#архитектура) ## Два режима работы [Заголовок раздела «Два режима работы»](#два-режима-работы) ### Удалённый сервер (рекомендуется) [Заголовок раздела «Удалённый сервер (рекомендуется)»](#удалённый-сервер-рекомендуется) Не требует локальной установки. Подключение к `https://mcp.context7.com/mcp`: ```json { "mcpServers": { "context7": { "url": "https://mcp.context7.com/mcp" } } } ``` ### Локальный сервер [Заголовок раздела «Локальный сервер»](#локальный-сервер) Запуск через npx без установки: ```bash npx -y @upstash/context7-mcp@latest ``` ## Инструменты [Заголовок раздела «Инструменты»](#инструменты) Context7 предоставляет два MCP-инструмента: ### resolve-library-id [Заголовок раздела «resolve-library-id»](#resolve-library-id) Поиск библиотеки по названию и получение Context7-совместимого ID. | Параметр | Тип | Описание | | ------------- | ------ | ------------------------------ | | `libraryName` | string | Название библиотеки для поиска | **Ответ содержит:** * Library ID в формате `/org/project` * Описание библиотеки * Количество сниппетов кода * Репутация источника (High/Medium/Low) * Benchmark Score (качество, макс. 100) * Доступные версии ### get-library-docs [Заголовок раздела «get-library-docs»](#get-library-docs) Получение документации по library ID. | Параметр | Тип | Описание | | ----------------------------- | ---------------- | ---------------------------------------------------- | | `context7CompatibleLibraryID` | string | ID библиотеки из resolve-library-id | | `mode` | `code` \| `info` | `code` — примеры кода, `info` — концептуальные гайды | | `topic` | string? | Фильтр по теме (напр., `hooks`, `routing`) | | `page` | number? | Страница пагинации (1-10) | ## Установка по клиентам [Заголовок раздела «Установка по клиентам»](#установка-по-клиентам) * Claude Desktop Добавьте в `claude_desktop_config.json`: **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` **Windows:** `%APPDATA%\Claude\claude_desktop_config.json` ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` Или удалённый сервер: ```json { "mcpServers": { "context7": { "url": "https://mcp.context7.com/mcp" } } } ``` * Cursor Добавьте в `~/.cursor/mcp.json`: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` Или через Settings → MCP Servers → Add Server: * **Type:** command * **Command:** `npx -y @upstash/context7-mcp@latest` * VS Code Добавьте в settings.json: ```json { "mcp": { "servers": { "context7": { "type": "stdio", "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } } ``` * Claude Code Добавьте в `~/.claude/settings.json`: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` * Windsurf Добавьте в `~/.codeium/windsurf/mcp_config.json`: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` * Zed Добавьте в settings.json Zed: ```json { "context_servers": { "context7": { "command": { "path": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } } ``` ## Использование [Заголовок раздела «Использование»](#использование) После подключения Context7 используйте фразу **“use context7”** в промпте: ```plaintext Создай React хук для debounce. use context7 ``` ```plaintext Как настроить маршрутизацию в Next.js 15? use context7 ``` ```plaintext Покажи пример работы с Prisma транзакциями. use context7 ``` AI-ассистент автоматически: 1. Вызовет `resolve-library-id` для поиска библиотеки 2. Получит актуальную документацию через `get-library-docs` 3. Использует реальные примеры кода в ответе ## Режимы документации [Заголовок раздела «Режимы документации»](#режимы-документации) Совет По умолчанию используется `mode: code`. Для вопросов об архитектуре и концепциях явно укажите: “Объясни архитектуру Next.js App Router. use context7 mode info” ## API Key (опционально) [Заголовок раздела «API Key (опционально)»](#api-key-опционально) Для увеличения лимитов запросов получите бесплатный API-ключ на [context7.com/dashboard](https://context7.com/dashboard). * Переменная окружения ```bash export CONTEXT7_API_KEY=ctx7sk_your_key_here ``` * CLI флаг ```json { "mcpServers": { "context7": { "command": "npx", "args": [ "-y", "@upstash/context7-mcp@latest", "--api-key", "ctx7sk_your_key_here" ] } } } ``` * HTTP Header Для удалённого сервера используйте заголовок: ```plaintext Authorization: Bearer ctx7sk_your_key_here ``` или ```plaintext Context7-API-Key: ctx7sk_your_key_here ``` ## HTTP Transport [Заголовок раздела «HTTP Transport»](#http-transport) Для продакшен-деплоя Context7 поддерживает HTTP режим: ```bash npx -y @upstash/context7-mcp --transport http --port 3000 ``` | Флаг | Описание | | ------------------- | ------------------------------------------- | | `--transport stdio` | Стандартный ввод/вывод (по умолчанию) | | `--transport http` | HTTP сервер | | `--port 3000` | Порт для HTTP (только с `--transport http`) | | `--api-key ` | API ключ (только с `--transport stdio`) | ## Формат Library ID [Заголовок раздела «Формат Library ID»](#формат-library-id) Context7 использует формат `/org/project` или `/org/project/version`: | Пример | Описание | | ------------------------- | -------------------------- | | `/vercel/next.js` | Next.js (последняя версия) | | `/vercel/next.js/v14.3.0` | Next.js конкретной версии | | `/mongodb/docs` | MongoDB документация | | `/supabase/supabase` | Supabase | | `/withastro/docs` | Astro | ## Proxy-поддержка [Заголовок раздела «Proxy-поддержка»](#proxy-поддержка) Context7 автоматически использует прокси из переменных окружения: ```bash export HTTPS_PROXY=http://proxy.example.com:8080 # или export HTTP_PROXY=http://proxy.example.com:8080 ``` ## Troubleshooting [Заголовок раздела «Troubleshooting»](#troubleshooting) Rate Limit При ошибке “Rate limited” создайте бесплатный API-ключ на [context7.com/dashboard](https://context7.com/dashboard) для увеличения лимитов. Library not found Если библиотека не найдена, попробуйте альтернативные названия: * `react` вместо `reactjs` * `next.js` вместо `nextjs` * Полное имя пакета `@tanstack/react-query` ### Проверка подключения [Заголовок раздела «Проверка подключения»](#проверка-подключения) ```bash # Локальный сервер npx @modelcontextprotocol/inspector npx -y @upstash/context7-mcp # Откройте http://localhost:6274 # Tools → resolve-library-id → libraryName: "react" ``` ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [GitHub: upstash/context7](https://github.com/upstash/context7) * [Context7 Dashboard](https://context7.com/dashboard) * [NPM: @upstash/context7-mcp](https://www.npmjs.com/package/@upstash/context7-mcp) # Everything Server > Тестовый MCP-сервер, демонстрирующий все возможности протокола — prompts, tools, resources, sampling и другие функции **Everything Server** — тестовый сервер, демонстрирующий все возможности MCP протокола. Не предназначен для production-использования, но служит отличным референсом для разработчиков MCP-клиентов. Заметка Этот сервер реализует prompts, tools, resources, sampling и другие возможности для демонстрации функционала MCP. ## Возможности [Заголовок раздела «Возможности»](#возможности) * Полный набор демонстрационных инструментов * Примеры prompts с аргументами и автодополнением * Динамические и статические ресурсы * Подписки на ресурсы и уведомления * Симуляция логирования * Поддержка всех транспортов (stdio, SSE, Streamable HTTP) ## Установка [Заголовок раздела «Установка»](#установка) * NPX (stdio) ```bash npx -y @modelcontextprotocol/server-everything ``` * NPX (SSE) ```bash npx @modelcontextprotocol/server-everything sse ``` * NPX (Streamable HTTP) ```bash npx @modelcontextprotocol/server-everything streamableHttp ``` * Docker ```bash docker run -i --rm mcp/everything ``` ## Инструменты [Заголовок раздела «Инструменты»](#инструменты) ### echo [Заголовок раздела «echo»](#echo) Возвращает переданное сообщение. Использует Zod для валидации. | Параметр | Тип | Описание | | --------- | ------ | ------------------ | | `message` | string | Сообщение для echo | ### get-annotated-message [Заголовок раздела «get-annotated-message»](#get-annotated-message) Возвращает сообщение с аннотациями `priority` и `audience` на основе типа. | Параметр | Тип | Описание | | ------------- | ------ | ------------------------------ | | `messageType` | string | `error`, `success` или `debug` | ### get-env [Заголовок раздела «get-env»](#get-env) Возвращает все переменные окружения процесса в формате JSON. ### get-sum [Заголовок раздела «get-sum»](#get-sum) Вычисляет сумму двух чисел. | Параметр | Тип | Описание | | -------- | ------ | ------------ | | `a` | number | Первое число | | `b` | number | Второе число | ### get-tiny-image [Заголовок раздела «get-tiny-image»](#get-tiny-image) Возвращает миниатюрный PNG-логотип MCP как `image` content item. ### get-resource-links [Заголовок раздела «get-resource-links»](#get-resource-links) Возвращает текстовый блок с несколькими `resource_link` элементами. | Параметр | Тип | Описание | | -------- | ------ | ------------------------ | | `count` | number | Количество ссылок (1–10) | ### get-resource-reference [Заголовок раздела «get-resource-reference»](#get-resource-reference) Возвращает конкретный ресурс по типу и ID. | Параметр | Тип | Описание | | -------------- | ------- | --------------------- | | `resourceType` | string | `text` или `blob` | | `resourceId` | integer | Идентификатор ресурса | ### get-roots-list [Заголовок раздела «get-roots-list»](#get-roots-list) Возвращает последний список roots, полученных от клиента. ### gzip-file-as-resource [Заголовок раздела «gzip-file-as-resource»](#gzip-file-as-resource) Сжимает данные и регистрирует как session-ресурс. | Параметр | Тип | Описание | | ------------ | ------ | ------------------------------ | | `name` | string | Имя ресурса | | `data` | string | URL или data URI | | `outputType` | string | `resource_link` или `resource` | ### get-structured-content [Заголовок раздела «get-structured-content»](#get-structured-content) Демонстрирует структурированные ответы с `outputSchema`. | Параметр | Тип | Описание | | ---------- | ------ | ------------------ | | `location` | string | Локация для погоды | ### trigger-long-running-operation [Заголовок раздела «trigger-long-running-operation»](#trigger-long-running-operation) Симулирует многошаговую операцию с отчётами о прогрессе. | Параметр | Тип | Описание | | ---------- | ------ | --------------------- | | `duration` | number | Длительность операции | | `steps` | number | Количество шагов | ### toggle-simulated-logging [Заголовок раздела «toggle-simulated-logging»](#toggle-simulated-logging) Включает/выключает симуляцию логирования для сессии. ### toggle-subscriber-updates [Заголовок раздела «toggle-subscriber-updates»](#toggle-subscriber-updates) Включает/выключает симуляцию обновлений ресурсов по подпискам. ### trigger-sampling-request [Заголовок раздела «trigger-sampling-request»](#trigger-sampling-request) Отправляет `sampling/createMessage` запрос клиенту/LLM. | Параметр | Тип | Описание | | -------- | ------ | -------------- | | `prompt` | string | Промпт для LLM | ## Prompts [Заголовок раздела «Prompts»](#prompts) ### simple-prompt [Заголовок раздела «simple-prompt»](#simple-prompt) Простой prompt без аргументов. Возвращает статическое сообщение. ### args-prompt [Заголовок раздела «args-prompt»](#args-prompt) Prompt с аргументами. | Аргумент | Обязательный | Описание | | -------- | ------------ | ----------- | | `city` | Да | Город | | `state` | Нет | Штат/регион | ### completable-prompt [Заголовок раздела «completable-prompt»](#completable-prompt) Демонстрирует автодополнение аргументов. Значения `department` определяют варианты для `name`. | Аргумент | Описание | | ------------ | ----------------------------- | | `department` | Отдел (с автодополнением) | | `name` | Имя (контекстные предложения) | ### resource-prompt [Заголовок раздела «resource-prompt»](#resource-prompt) Prompt со встроенным ресурсом. | Аргумент | Описание | | -------------- | ----------------- | | `resourceType` | `Text` или `Blob` | | `resourceId` | ID ресурса | ## Ресурсы [Заголовок раздела «Ресурсы»](#ресурсы) ### Динамические текстовые [Заголовок раздела «Динамические текстовые»](#динамические-текстовые) ```plaintext demo://resource/dynamic/text/{index} ``` Генерируются на лету. ### Динамические бинарные [Заголовок раздела «Динамические бинарные»](#динамические-бинарные) ```plaintext demo://resource/dynamic/blob/{index} ``` Base64-данные, генерируются на лету. ### Статические документы [Заголовок раздела «Статические документы»](#статические-документы) ```plaintext demo://resource/static/document/ ``` Файлы из директории `docs/` сервера. ### Сессионные ресурсы [Заголовок раздела «Сессионные ресурсы»](#сессионные-ресурсы) ```plaintext demo://resource/session/ ``` Ресурсы, зарегистрированные динамически для текущей сессии. ## Подписки и уведомления [Заголовок раздела «Подписки и уведомления»](#подписки-и-уведомления) * Симуляция обновлений **отключена по умолчанию** * Клиенты могут подписываться через `resources/subscribe` и `resources/unsubscribe` * Инструмент `toggle-subscriber-updates` запускает интервал с `notifications/resources/updated` * Поддержка нескольких клиентов — подписки отслеживаются для каждой сессии ## Логирование [Заголовок раздела «Логирование»](#логирование) * Симуляция логирования **отключена по умолчанию** * Инструмент `toggle-simulated-logging` запускает периодические сообщения разных уровней * Уровни: debug, info, notice, warning, error, critical, alert, emergency * Клиенты могут установить минимальный уровень через `logging/setLevel` ## Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация) ### Claude Desktop [Заголовок раздела «Claude Desktop»](#claude-desktop) ```json { "mcpServers": { "everything": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-everything"] } } } ``` ### VS Code [Заголовок раздела «VS Code»](#vs-code) ```json { "servers": { "everything": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-everything"] } } } ``` ## Запуск с разными транспортами [Заголовок раздела «Запуск с разными транспортами»](#запуск-с-разными-транспортами) ### stdio (по умолчанию) [Заголовок раздела «stdio (по умолчанию)»](#stdio-по-умолчанию) ```bash npx @modelcontextprotocol/server-everything # или явно npx @modelcontextprotocol/server-everything stdio ``` ### HTTP+SSE (deprecated в 2025-03-26) [Заголовок раздела «HTTP+SSE (deprecated в 2025-03-26)»](#httpsse-deprecated-в-2025-03-26) ```bash npx @modelcontextprotocol/server-everything sse ``` ### Streamable HTTP (рекомендуется) [Заголовок раздела «Streamable HTTP (рекомендуется)»](#streamable-http-рекомендуется) ```bash npx @modelcontextprotocol/server-everything streamableHttp ``` ## Разработка из исходников [Заголовок раздела «Разработка из исходников»](#разработка-из-исходников) ```bash cd src/everything npm install npm run start:sse # или npm run start:streamableHttp ``` ## Сборка Docker-образа [Заголовок раздела «Сборка Docker-образа»](#сборка-docker-образа) ```bash docker build -t mcp/everything -f src/everything/Dockerfile . ``` ## Использование для тестирования [Заголовок раздела «Использование для тестирования»](#использование-для-тестирования) Everything Server идеален для: * **Тестирования MCP-клиентов** — проверка поддержки всех функций протокола * **Изучения MCP** — понимание как работают разные примитивы * **Прототипирования** — быстрая проверка идей перед реализацией * **Отладки** — изолированная среда для диагностики проблем Совет Изучите исходный код Everything Server для понимания паттернов реализации MCP-серверов. ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [Исходный код на GitHub](https://github.com/modelcontextprotocol/servers/tree/main/src/everything) * [NPM пакет](https://www.npmjs.com/package/@modelcontextprotocol/server-everything) * [Архитектура](https://github.com/modelcontextprotocol/servers/tree/main/src/everything/docs/architecture.md) * [Структура проекта](https://github.com/modelcontextprotocol/servers/tree/main/src/everything/docs/structure.md) # Fetch Server > MCP-сервер для получения веб-контента с автоматической конвертацией HTML в Markdown **Fetch Server** позволяет LLM получать и обрабатывать контент с веб-страниц, автоматически конвертируя HTML в Markdown для удобного чтения. Поддерживает постраничное чтение длинных документов. Осторожно Сервер может получить доступ к локальным/внутренним IP-адресам и представляет потенциальный риск безопасности. Используйте с осторожностью, чтобы не раскрыть конфиденциальные данные. ## Возможности [Заголовок раздела «Возможности»](#возможности) * Получение контента с веб-страниц * Автоматическая конвертация HTML в Markdown * Постраничное чтение длинных документов * Настраиваемый User-Agent * Поддержка прокси * Соблюдение robots.txt ## Установка [Заголовок раздела «Установка»](#установка) * uvx ```bash uvx mcp-server-fetch ``` * pip ```bash pip install mcp-server-fetch python -m mcp_server_fetch ``` * Docker ```bash docker run -i --rm mcp/fetch ``` Совет Опционально установите Node.js для использования более надёжного HTML-упрощителя. ## Инструменты [Заголовок раздела «Инструменты»](#инструменты) ### fetch [Заголовок раздела «fetch»](#fetch) Получает URL и извлекает содержимое в формате Markdown. | Параметр | Тип | По умолчанию | Описание | | ------------- | ------- | ------------ | -------------------------------------- | | `url` | string | — | URL для получения | | `max_length` | integer | 5000 | Максимум символов | | `start_index` | integer | 0 | Начальный индекс символа | | `raw` | boolean | false | Получить сырой контент без конвертации | **Пример использования:** ```json { "name": "fetch", "arguments": { "url": "https://example.com/article", "max_length": 10000 } } ``` ### Постраничное чтение [Заголовок раздела «Постраничное чтение»](#постраничное-чтение) Сервер автоматически обрезает ответ до `max_length`. Используйте `start_index` для чтения продолжения: 1. Первый запрос: `{"url": "...", "start_index": 0}` 2. Если контент обрезан, следующий запрос: `{"url": "...", "start_index": 5000}` 3. Продолжайте до получения всей нужной информации ## Prompts [Заголовок раздела «Prompts»](#prompts) ### fetch [Заголовок раздела «fetch»](#fetch-1) Prompt для получения URL и извлечения содержимого в Markdown. | Аргумент | Тип | Описание | | -------- | ------ | ----------------- | | `url` | string | URL для получения | ## Настройка [Заголовок раздела «Настройка»](#настройка) ### robots.txt [Заголовок раздела «robots.txt»](#robotstxt) По умолчанию сервер соблюдает robots.txt для автономных запросов (через tool), но игнорирует для пользовательских (через prompt). Отключение: ```bash uvx mcp-server-fetch --ignore-robots-txt ``` ### User-Agent [Заголовок раздела «User-Agent»](#user-agent) Сервер использует разные User-Agent в зависимости от источника запроса: **Автономный запрос (tool):** ```plaintext ModelContextProtocol/1.0 (Autonomous; +https://github.com/modelcontextprotocol/servers) ``` **Пользовательский запрос (prompt):** ```plaintext ModelContextProtocol/1.0 (User-Specified; +https://github.com/modelcontextprotocol/servers) ``` Настройка: ```bash uvx mcp-server-fetch --user-agent="CustomBot/1.0" ``` ### Прокси [Заголовок раздела «Прокси»](#прокси) ```bash uvx mcp-server-fetch --proxy-url=http://proxy.example.com:8080 ``` ## Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация) ### Claude Desktop [Заголовок раздела «Claude Desktop»](#claude-desktop) * uvx ```json { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] } } } ``` * Docker ```json { "mcpServers": { "fetch": { "command": "docker", "args": ["run", "-i", "--rm", "mcp/fetch"] } } } ``` * pip ```json { "mcpServers": { "fetch": { "command": "python", "args": ["-m", "mcp_server_fetch"] } } } ``` ### VS Code [Заголовок раздела «VS Code»](#vs-code) * uvx ```json { "mcp": { "servers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] } } } } ``` * Docker ```json { "mcp": { "servers": { "fetch": { "command": "docker", "args": ["run", "-i", "--rm", "mcp/fetch"] } } } } ``` ## Конфигурация для Windows [Заголовок раздела «Конфигурация для Windows»](#конфигурация-для-windows) При проблемах с таймаутами на Windows установите переменную `PYTHONIOENCODING`: * uvx ```json { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"], "env": { "PYTHONIOENCODING": "utf-8" } } } } ``` * pip ```json { "mcpServers": { "fetch": { "command": "python", "args": ["-m", "mcp_server_fetch"], "env": { "PYTHONIOENCODING": "utf-8" } } } } ``` ## Отладка [Заголовок раздела «Отладка»](#отладка) Используйте MCP Inspector: ```bash npx @modelcontextprotocol/inspector uvx mcp-server-fetch ``` Или при разработке: ```bash cd path/to/servers/src/fetch npx @modelcontextprotocol/inspector uv run mcp-server-fetch ``` ## Примеры использования [Заголовок раздела «Примеры использования»](#примеры-использования) ### Получение статьи [Заголовок раздела «Получение статьи»](#получение-статьи) ```json { "name": "fetch", "arguments": { "url": "https://docs.anthropic.com/en/docs/overview", "max_length": 8000 } } ``` ### Получение сырого HTML [Заголовок раздела «Получение сырого HTML»](#получение-сырого-html) ```json { "name": "fetch", "arguments": { "url": "https://example.com/page", "raw": true } } ``` ### Чтение длинного документа [Заголовок раздела «Чтение длинного документа»](#чтение-длинного-документа) ```json // Первый запрос { "name": "fetch", "arguments": { "url": "https://example.com/long-article", "max_length": 5000, "start_index": 0 } } // Продолжение { "name": "fetch", "arguments": { "url": "https://example.com/long-article", "max_length": 5000, "start_index": 5000 } } ``` ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [Исходный код на GitHub](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch) * [PyPI пакет](https://pypi.org/project/mcp-server-fetch/) # Filesystem Server > MCP-сервер для работы с файловой системой — чтение, запись, редактирование файлов и директорий с контролем доступа **Filesystem Server** реализует полный набор операций для работы с файловой системой. Сервер обеспечивает безопасный доступ к файлам через систему разрешённых директорий и поддерживает MCP Roots для динамического управления доступом. ## Возможности [Заголовок раздела «Возможности»](#возможности) * Чтение и запись файлов (текст и бинарные данные) * Создание, перемещение и удаление директорий * Поиск файлов по шаблонам glob * Получение метаданных файлов * Динамический контроль доступа через [MCP Roots](https://modelcontextprotocol.io/docs/learn/client-concepts#roots) ## Установка [Заголовок раздела «Установка»](#установка) * NPX ```bash npx -y @modelcontextprotocol/server-filesystem /path/to/directory ``` * Docker ```bash docker run -i --rm \ --mount type=bind,src=/Users/username/Desktop,dst=/projects/Desktop \ mcp/filesystem /projects ``` ## Инструменты (Tools) [Заголовок раздела «Инструменты (Tools)»](#инструменты-tools) ### read\_text\_file [Заголовок раздела «read\_text\_file»](#read_text_file) Читает содержимое текстового файла. | Параметр | Тип | Описание | | -------- | ------- | ------------------------ | | `path` | string | Путь к файлу | | `head` | number? | Только первые N строк | | `tail` | number? | Только последние N строк | Заметка Нельзя указывать `head` и `tail` одновременно. Файл всегда читается как UTF-8. ### read\_media\_file [Заголовок раздела «read\_media\_file»](#read_media_file) Читает изображение или аудио файл, возвращая base64 с соответствующим MIME-типом. | Параметр | Тип | Описание | | -------- | ------ | ----------------- | | `path` | string | Путь к медиафайлу | ### read\_multiple\_files [Заголовок раздела «read\_multiple\_files»](#read_multiple_files) Читает несколько файлов одновременно. Ошибка чтения одного файла не прерывает чтение остальных. | Параметр | Тип | Описание | | -------- | --------- | --------------------- | | `paths` | string\[] | Массив путей к файлам | ### write\_file [Заголовок раздела «write\_file»](#write_file) Создаёт новый файл или перезаписывает существующий. | Параметр | Тип | Описание | | --------- | ------ | ---------------- | | `path` | string | Путь для записи | | `content` | string | Содержимое файла | Осторожно Используйте с осторожностью — инструмент перезаписывает существующие файлы без предупреждения. ### edit\_file [Заголовок раздела «edit\_file»](#edit_file) Выполняет точечное редактирование с сопоставлением шаблонов и сохранением форматирования. | Параметр | Тип | Описание | | -------- | -------- | ------------------------------------------------- | | `path` | string | Путь к файлу | | `edits` | array | Список правок: `{oldText, newText}` | | `dryRun` | boolean? | Предпросмотр без применения (по умолчанию: false) | **Возможности:** * Сопоставление построчного и многострочного контента * Нормализация пробелов с сохранением отступов * Множественные правки с корректным позиционированием * Определение и сохранение стиля отступов * Вывод diff в стиле Git Совет Рекомендуется сначала использовать `dryRun: true` для предпросмотра изменений. ### create\_directory [Заголовок раздела «create\_directory»](#create_directory) Создаёт директорию (включая родительские, если нужно). | Параметр | Тип | Описание | | -------- | ------ | ----------------- | | `path` | string | Путь к директории | ### list\_directory [Заголовок раздела «list\_directory»](#list_directory) Выводит содержимое директории с префиксами `[FILE]` или `[DIR]`. | Параметр | Тип | Описание | | -------- | ------ | ----------------- | | `path` | string | Путь к директории | ### list\_directory\_with\_sizes [Заголовок раздела «list\_directory\_with\_sizes»](#list_directory_with_sizes) Выводит содержимое с размерами файлов и статистикой. | Параметр | Тип | Описание | | -------- | ------- | ----------------------------- | | `path` | string | Путь к директории | | `sortBy` | string? | Сортировка: `name` или `size` | ### move\_file [Заголовок раздела «move\_file»](#move_file) Перемещает или переименовывает файл/директорию. | Параметр | Тип | Описание | | ------------- | ------ | ------------- | | `source` | string | Исходный путь | | `destination` | string | Целевой путь | Заметка Операция завершится ошибкой, если целевой путь уже существует. ### search\_files [Заголовок раздела «search\_files»](#search_files) Рекурсивный поиск файлов по шаблонам glob. | Параметр | Тип | Описание | | ----------------- | ---------- | -------------------- | | `path` | string | Начальная директория | | `pattern` | string | Шаблон поиска (glob) | | `excludePatterns` | string\[]? | Исключаемые шаблоны | ```json { "path": "/projects", "pattern": "**/*.ts", "excludePatterns": ["node_modules/**", "dist/**"] } ``` ### directory\_tree [Заголовок раздела «directory\_tree»](#directory_tree) Возвращает рекурсивную JSON-структуру директории. | Параметр | Тип | Описание | | ----------------- | ---------- | -------------------- | | `path` | string | Начальная директория | | `excludePatterns` | string\[]? | Исключаемые шаблоны | ### get\_file\_info [Заголовок раздела «get\_file\_info»](#get_file_info) Получает метаданные файла или директории. | Параметр | Тип | Описание | | -------- | ------ | ------------ | | `path` | string | Путь к файлу | **Возвращает:** размер, время создания, время изменения, время доступа, тип, права доступа. ### list\_allowed\_directories [Заголовок раздела «list\_allowed\_directories»](#list_allowed_directories) Выводит список разрешённых директорий сервера. Параметров не требует. ## Аннотации инструментов [Заголовок раздела «Аннотации инструментов»](#аннотации-инструментов) Сервер использует [MCP ToolAnnotations](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#toolannotations) для классификации инструментов: | Инструмент | readOnlyHint | idempotentHint | destructiveHint | | ---------------------------- | ------------ | -------------- | --------------- | | read\_text\_file | `true` | – | – | | read\_media\_file | `true` | – | – | | read\_multiple\_files | `true` | – | – | | list\_directory | `true` | – | – | | list\_directory\_with\_sizes | `true` | – | – | | directory\_tree | `true` | – | – | | search\_files | `true` | – | – | | get\_file\_info | `true` | – | – | | list\_allowed\_directories | `true` | – | – | | create\_directory | `false` | `true` | `false` | | write\_file | `false` | `true` | `true` | | edit\_file | `false` | `false` | `true` | | move\_file | `false` | `false` | `false` | ## Контроль доступа [Заголовок раздела «Контроль доступа»](#контроль-доступа) ### Через аргументы командной строки [Заголовок раздела «Через аргументы командной строки»](#через-аргументы-командной-строки) ```bash mcp-server-filesystem /path/to/dir1 /path/to/dir2 ``` ### Через MCP Roots (рекомендуется) [Заголовок раздела «Через MCP Roots (рекомендуется)»](#через-mcp-roots-рекомендуется) Клиенты, поддерживающие [Roots](https://modelcontextprotocol.io/docs/learn/client-concepts#roots), могут динамически обновлять разрешённые директории без перезапуска сервера. 1. Сервер запускается с директориями из командной строки (или без них) 2. Клиент подключается и отправляет `initialize` с capabilities 3. Если клиент поддерживает roots, сервер запрашивает их через `roots/list` 4. Roots от клиента **полностью заменяют** директории из командной строки 5. При изменениях клиент отправляет `notifications/roots/list_changed` Осторожно Если сервер запущен без аргументов и клиент не поддерживает roots (или передаёт пустой список), сервер вернёт ошибку при инициализации. ## Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация) ### Claude Desktop [Заголовок раздела «Claude Desktop»](#claude-desktop) * NPX ```json { "mcpServers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/Users/username/Desktop", "/path/to/other/allowed/dir" ] } } } ``` * Docker ```json { "mcpServers": { "filesystem": { "command": "docker", "args": [ "run", "-i", "--rm", "--mount", "type=bind,src=/Users/username/Desktop,dst=/projects/Desktop", "--mount", "type=bind,src=/path/to/dir,dst=/projects/dir,ro", "mcp/filesystem", "/projects" ] } } } ``` Совет При использовании Docker добавьте `,ro` к mount для директорий только на чтение. ### VS Code [Заголовок раздела «VS Code»](#vs-code) ```json { "servers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "${workspaceFolder}" ] } } } ``` ## Сборка Docker-образа [Заголовок раздела «Сборка Docker-образа»](#сборка-docker-образа) ```bash docker build -t mcp/filesystem -f src/filesystem/Dockerfile . ``` ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [Исходный код на GitHub](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) * [NPM пакет](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem) # Git Server > MCP-сервер для взаимодействия с Git-репозиториями — статус, diff, коммиты, ветки и история изменений **Git Server** предоставляет полный набор инструментов для работы с Git-репозиториями через MCP. Позволяет LLM читать, искать и манипулировать Git-репозиториями. Заметка Сервер находится в активной разработке. Функциональность и доступные инструменты могут расширяться. ## Возможности [Заголовок раздела «Возможности»](#возможности) * Просмотр статуса рабочей директории * Просмотр различий (staged, unstaged, между ветками) * Создание коммитов и управление staging area * Создание и переключение веток * Просмотр истории с фильтрацией по датам ## Установка [Заголовок раздела «Установка»](#установка) * uvx ```bash uvx mcp-server-git --repository /path/to/repo ``` * pip ```bash pip install mcp-server-git python -m mcp_server_git --repository /path/to/repo ``` * Docker ```bash docker run --rm -i \ --mount type=bind,src=/Users/username,dst=/Users/username \ mcp/git ``` ## Инструменты [Заголовок раздела «Инструменты»](#инструменты) ### git\_status [Заголовок раздела «git\_status»](#git_status) Показывает статус рабочей директории. | Параметр | Тип | Описание | | ----------- | ------ | ---------------------- | | `repo_path` | string | Путь к Git-репозиторию | ### git\_diff\_unstaged [Заголовок раздела «git\_diff\_unstaged»](#git_diff_unstaged) Показывает изменения в рабочей директории, не добавленные в staging. | Параметр | Тип | По умолчанию | Описание | | --------------- | ------ | ------------ | ------------------ | | `repo_path` | string | — | Путь к репозиторию | | `context_lines` | number | 3 | Строк контекста | ### git\_diff\_staged [Заголовок раздела «git\_diff\_staged»](#git_diff_staged) Показывает изменения, добавленные в staging для коммита. | Параметр | Тип | По умолчанию | Описание | | --------------- | ------ | ------------ | ------------------ | | `repo_path` | string | — | Путь к репозиторию | | `context_lines` | number | 3 | Строк контекста | ### git\_diff [Заголовок раздела «git\_diff»](#git_diff) Показывает различия между ветками или коммитами. | Параметр | Тип | По умолчанию | Описание | | --------------- | ------ | ------------ | ------------------------ | | `repo_path` | string | — | Путь к репозиторию | | `target` | string | — | Целевая ветка или коммит | | `context_lines` | number | 3 | Строк контекста | **Пример:** ```json { "repo_path": "/projects/myapp", "target": "main", "context_lines": 5 } ``` ### git\_add [Заголовок раздела «git\_add»](#git_add) Добавляет файлы в staging area. | Параметр | Тип | Описание | | ----------- | --------- | --------------------- | | `repo_path` | string | Путь к репозиторию | | `files` | string\[] | Массив путей к файлам | ### git\_reset [Заголовок раздела «git\_reset»](#git_reset) Убирает все файлы из staging area. | Параметр | Тип | Описание | | ----------- | ------ | ------------------ | | `repo_path` | string | Путь к репозиторию | ### git\_commit [Заголовок раздела «git\_commit»](#git_commit) Записывает изменения в репозиторий. | Параметр | Тип | Описание | | ----------- | ------ | ------------------ | | `repo_path` | string | Путь к репозиторию | | `message` | string | Сообщение коммита | **Возвращает:** подтверждение с хешем нового коммита. ### git\_log [Заголовок раздела «git\_log»](#git_log) Показывает историю коммитов с опциональной фильтрацией по датам. | Параметр | Тип | По умолчанию | Описание | | ----------------- | ------- | ------------ | ------------------ | | `repo_path` | string | — | Путь к репозиторию | | `max_count` | number | 10 | Максимум коммитов | | `start_timestamp` | string? | — | Начальная дата | | `end_timestamp` | string? | — | Конечная дата | **Форматы дат:** * ISO 8601: `2024-01-15T14:30:25` * Относительные: `2 weeks ago`, `yesterday` * Абсолютные: `2024-01-15`, `Jan 15 2024` **Возвращает:** массив коммитов с хешем, автором, датой и сообщением. ### git\_create\_branch [Заголовок раздела «git\_create\_branch»](#git_create_branch) Создаёт новую ветку. | Параметр | Тип | Описание | | ------------- | ------- | ------------------------------------- | | `repo_path` | string | Путь к репозиторию | | `branch_name` | string | Имя новой ветки | | `base_branch` | string? | Базовая ветка (по умолчанию: текущая) | ### git\_checkout [Заголовок раздела «git\_checkout»](#git_checkout) Переключает ветку. | Параметр | Тип | Описание | | ------------- | ------ | ------------------ | | `repo_path` | string | Путь к репозиторию | | `branch_name` | string | Имя ветки | ### git\_show [Заголовок раздела «git\_show»](#git_show) Показывает содержимое коммита. | Параметр | Тип | Описание | | ----------- | ------ | ------------------------- | | `repo_path` | string | Путь к репозиторию | | `revision` | string | Ревизия (хеш, ветка, тег) | ### git\_branch [Заголовок раздела «git\_branch»](#git_branch) Выводит список веток. | Параметр | Тип | Описание | | -------------- | ------- | ------------------------------------------ | | `repo_path` | string | Путь к репозиторию | | `branch_type` | string | `local`, `remote` или `all` | | `contains` | string? | SHA коммита, который должен содержаться | | `not_contains` | string? | SHA коммита, который НЕ должен содержаться | ## Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация) ### Claude Desktop [Заголовок раздела «Claude Desktop»](#claude-desktop) * uvx ```json { "mcpServers": { "git": { "command": "uvx", "args": ["mcp-server-git", "--repository", "/path/to/repo"] } } } ``` * Docker ```json { "mcpServers": { "git": { "command": "docker", "args": [ "run", "--rm", "-i", "--mount", "type=bind,src=/Users/username,dst=/Users/username", "mcp/git" ] } } } ``` * pip ```json { "mcpServers": { "git": { "command": "python", "args": ["-m", "mcp_server_git", "--repository", "/path/to/repo"] } } } ``` ### VS Code [Заголовок раздела «VS Code»](#vs-code) ```json { "servers": { "git": { "command": "uvx", "args": ["mcp-server-git"] } } } ``` **С Docker:** ```json { "mcp": { "servers": { "git": { "command": "docker", "args": [ "run", "--rm", "-i", "--mount", "type=bind,src=${workspaceFolder},dst=/workspace", "mcp/git" ] } } } } ``` ### Zed [Заголовок раздела «Zed»](#zed) ```json { "context_servers": [ "mcp-server-git": { "command": { "path": "uvx", "args": ["mcp-server-git"] } } ] } ``` ## Примеры использования [Заголовок раздела «Примеры использования»](#примеры-использования) ### Просмотр изменений и создание коммита [Заголовок раздела «Просмотр изменений и создание коммита»](#просмотр-изменений-и-создание-коммита) 1. Проверить статус: `git_status` 2. Посмотреть unstaged изменения: `git_diff_unstaged` 3. Добавить файлы: `git_add` с массивом файлов 4. Посмотреть staged изменения: `git_diff_staged` 5. Создать коммит: `git_commit` с сообщением ### Сравнение с другой веткой [Заголовок раздела «Сравнение с другой веткой»](#сравнение-с-другой-веткой) ```json { "name": "git_diff", "arguments": { "repo_path": "/projects/myapp", "target": "feature/new-api", "context_lines": 5 } } ``` ### Поиск коммитов за период [Заголовок раздела «Поиск коммитов за период»](#поиск-коммитов-за-период) ```json { "name": "git_log", "arguments": { "repo_path": "/projects/myapp", "max_count": 20, "start_timestamp": "2024-01-01", "end_timestamp": "2024-01-31" } } ``` ### Поиск веток с определённым коммитом [Заголовок раздела «Поиск веток с определённым коммитом»](#поиск-веток-с-определённым-коммитом) ```json { "name": "git_branch", "arguments": { "repo_path": "/projects/myapp", "branch_type": "all", "contains": "abc123" } } ``` ## Отладка [Заголовок раздела «Отладка»](#отладка) ```bash npx @modelcontextprotocol/inspector uvx mcp-server-git ``` Просмотр логов Claude: ```bash tail -n 20 -f ~/Library/Logs/Claude/mcp*.log ``` ## Сборка Docker-образа [Заголовок раздела «Сборка Docker-образа»](#сборка-docker-образа) ```bash cd src/git docker build -t mcp/git . ``` ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [Исходный код на GitHub](https://github.com/modelcontextprotocol/servers/tree/main/src/git) * [PyPI пакет](https://pypi.org/project/mcp-server-git/) # Memory Server > MCP-сервер для персистентной памяти на основе графа знаний — сохранение информации о пользователе между сессиями **Memory Server** реализует персистентную память с помощью локального графа знаний. Позволяет Claude запоминать информацию о пользователе между чатами, создавая долговременные воспоминания. ## Основные концепции [Заголовок раздела «Основные концепции»](#основные-концепции) ### Сущности (Entities) [Заголовок раздела «Сущности (Entities)»](#сущности-entities) Сущности — первичные узлы графа знаний. Каждая сущность имеет: * **name** — уникальный идентификатор * **entityType** — тип (person, organization, event и т.д.) * **observations** — список наблюдений ```json { "name": "John_Smith", "entityType": "person", "observations": ["Говорит по-испански", "Работает в Anthropic"] } ``` ### Отношения (Relations) [Заголовок раздела «Отношения (Relations)»](#отношения-relations) Отношения определяют направленные связи между сущностями. Всегда хранятся в активном залоге. ```json { "from": "John_Smith", "to": "Anthropic", "relationType": "works_at" } ``` ### Наблюдения (Observations) [Заголовок раздела «Наблюдения (Observations)»](#наблюдения-observations) Наблюдения — дискретные факты о сущности: * Хранятся как строки * Привязаны к конкретным сущностям * Могут добавляться и удаляться независимо * Должны быть атомарными (один факт на наблюдение) ```json { "entityName": "John_Smith", "observations": [ "Говорит по-испански", "Закончил университет в 2019", "Предпочитает утренние встречи" ] } ``` ## Установка [Заголовок раздела «Установка»](#установка) * NPX ```bash npx -y @modelcontextprotocol/server-memory ``` * Docker ```bash docker run -i -v claude-memory:/app/dist --rm mcp/memory ``` ## Инструменты [Заголовок раздела «Инструменты»](#инструменты) ### create\_entities [Заголовок раздела «create\_entities»](#create_entities) Создаёт несколько новых сущностей в графе знаний. | Параметр | Тип | Описание | | ---------- | ----- | ------------------------- | | `entities` | array | Массив объектов сущностей | Каждый объект содержит: * `name` (string) — идентификатор сущности * `entityType` (string) — тип сущности * `observations` (string\[]) — начальные наблюдения Заметка Сущности с уже существующими именами игнорируются. ### create\_relations [Заголовок раздела «create\_relations»](#create_relations) Создаёт отношения между сущностями. | Параметр | Тип | Описание | | ----------- | ----- | ------------------------- | | `relations` | array | Массив объектов отношений | Каждый объект содержит: * `from` (string) — исходная сущность * `to` (string) — целевая сущность * `relationType` (string) — тип отношения (в активном залоге) Заметка Дублирующиеся отношения пропускаются. ### add\_observations [Заголовок раздела «add\_observations»](#add_observations) Добавляет новые наблюдения к существующим сущностям. | Параметр | Тип | Описание | | -------------- | ----- | -------------------------- | | `observations` | array | Массив объектов наблюдений | Каждый объект содержит: * `entityName` (string) — целевая сущность * `contents` (string\[]) — новые наблюдения **Возвращает:** добавленные наблюдения по сущностям. Осторожно Завершится ошибкой, если сущность не существует. ### delete\_entities [Заголовок раздела «delete\_entities»](#delete_entities) Удаляет сущности и связанные отношения. | Параметр | Тип | Описание | | ------------- | --------- | ---------------------------- | | `entityNames` | string\[] | Имена сущностей для удаления | Заметка Каскадное удаление связанных отношений. Молча игнорирует несуществующие сущности. ### delete\_observations [Заголовок раздела «delete\_observations»](#delete_observations) Удаляет конкретные наблюдения из сущностей. | Параметр | Тип | Описание | | ----------- | ----- | ------------------------ | | `deletions` | array | Массив объектов удаления | Каждый объект содержит: * `entityName` (string) — целевая сущность * `observations` (string\[]) — наблюдения для удаления ### delete\_relations [Заголовок раздела «delete\_relations»](#delete_relations) Удаляет конкретные отношения из графа. | Параметр | Тип | Описание | | ----------- | ----- | ------------------------- | | `relations` | array | Массив объектов отношений | ### read\_graph [Заголовок раздела «read\_graph»](#read_graph) Читает весь граф знаний. Параметров не требует. **Возвращает:** полную структуру графа со всеми сущностями и отношениями. ### search\_nodes [Заголовок раздела «search\_nodes»](#search_nodes) Поиск узлов по запросу. | Параметр | Тип | Описание | | -------- | ------ | ---------------- | | `query` | string | Поисковый запрос | **Ищет по:** * Именам сущностей * Типам сущностей * Содержимому наблюдений **Возвращает:** найденные сущности и их отношения. ### open\_nodes [Заголовок раздела «open\_nodes»](#open_nodes) Получает конкретные узлы по именам. | Параметр | Тип | Описание | | -------- | --------- | --------------------- | | `names` | string\[] | Массив имён сущностей | **Возвращает:** * Запрошенные сущности * Отношения между ними Заметка Несуществующие узлы молча пропускаются. ## Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация) ### Claude Desktop [Заголовок раздела «Claude Desktop»](#claude-desktop) * NPX ```json { "mcpServers": { "memory": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-memory"] } } } ``` * Docker ```json { "mcpServers": { "memory": { "command": "docker", "args": [ "run", "-i", "-v", "claude-memory:/app/dist", "--rm", "mcp/memory" ] } } } ``` ### Кастомный путь к файлу памяти [Заголовок раздела «Кастомный путь к файлу памяти»](#кастомный-путь-к-файлу-памяти) ```json { "mcpServers": { "memory": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-memory"], "env": { "MEMORY_FILE_PATH": "/path/to/custom/memory.jsonl" } } } } ``` По умолчанию файл памяти: `memory.jsonl` в директории сервера. ### VS Code [Заголовок раздела «VS Code»](#vs-code) * NPX ```json { "servers": { "memory": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-memory"] } } } ``` * Docker ```json { "servers": { "memory": { "command": "docker", "args": [ "run", "-i", "-v", "claude-memory:/app/dist", "--rm", "mcp/memory" ] } } } ``` ## Системный промпт [Заголовок раздела «Системный промпт»](#системный-промпт) Для эффективного использования памяти добавьте в системный промпт (например, в “Custom Instructions” проекта Claude): ```text Следуйте этим шагам для каждого взаимодействия: 1. Идентификация пользователя: - Предполагайте, что вы общаетесь с default_user - Если пользователь не идентифицирован, попробуйте его определить 2. Извлечение памяти: - Всегда начинайте чат со слова "Вспоминаю..." и извлекайте всю релевантную информацию из графа знаний - Называйте граф знаний "памятью" 3. Память: - Во время разговора отслеживайте новую информацию в категориях: a) Базовая идентичность (возраст, пол, локация, работа, образование) b) Поведение (интересы, привычки) c) Предпочтения (стиль общения, язык) d) Цели (цели, стремления) e) Отношения (личные и профессиональные связи) 4. Обновление памяти: - Если получена новая информация, обновите память: a) Создайте сущности для организаций, людей, событий b) Свяжите их с существующими сущностями c) Сохраните факты как наблюдения ``` ## Примеры использования [Заголовок раздела «Примеры использования»](#примеры-использования) ### Создание сущности [Заголовок раздела «Создание сущности»](#создание-сущности) ```json { "name": "create_entities", "arguments": { "entities": [ { "name": "Иван_Петров", "entityType": "person", "observations": [ "Разработчик Python", "Живёт в Москве" ] }, { "name": "TechCorp", "entityType": "organization", "observations": ["IT-компания"] } ] } } ``` ### Создание отношения [Заголовок раздела «Создание отношения»](#создание-отношения) ```json { "name": "create_relations", "arguments": { "relations": [ { "from": "Иван_Петров", "to": "TechCorp", "relationType": "работает_в" } ] } } ``` ### Поиск информации [Заголовок раздела «Поиск информации»](#поиск-информации) ```json { "name": "search_nodes", "arguments": { "query": "разработчик" } } ``` ## Сборка Docker-образа [Заголовок раздела «Сборка Docker-образа»](#сборка-docker-образа) ```bash docker build -t mcp/memory -f src/memory/Dockerfile . ``` Осторожно Если вы используете Docker volume для хранения, удалите старый файл `index.js` из volume перед запуском нового контейнера. ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [Исходный код на GitHub](https://github.com/modelcontextprotocol/servers/tree/main/src/memory) * [NPM пакет](https://www.npmjs.com/package/@modelcontextprotocol/server-memory) # Эталонные MCP серверы > Официальные эталонные серверы Model Context Protocol от Anthropic — готовые решения для файловой системы, Git, веб-запросов, памяти и других задач На этой странице собраны **эталонные серверы MCP** от Anthropic и **рекомендуемые сторонние серверы**, которые значительно расширяют возможности AI-ассистентов. Рекомендуем **Context7** — сервер для получения актуальной документации библиотек. Решает проблему устаревших знаний AI. [Подробнее →](/servers/context7/) ## Эталонные серверы Anthropic [Заголовок раздела «Эталонные серверы Anthropic»](#эталонные-серверы-anthropic) **Эталонные серверы MCP** — это официальные реализации от команды Anthropic, демонстрирующие лучшие практики создания MCP-совместимых серверов. ## Категории серверов [Заголовок раздела «Категории серверов»](#категории-серверов) Filesystem Полный доступ к файловой системе: чтение, запись, редактирование файлов, работа с директориями и поиск. Fetch Получение контента с веб-страниц с автоматической конвертацией HTML в Markdown. Git Взаимодействие с Git-репозиториями: статус, diff, коммиты, ветки и просмотр истории. Memory Граф знаний для долговременной памяти — сохранение информации о пользователе между сессиями. Time Работа с временными зонами: получение текущего времени и конвертация между часовыми поясами. Sequential Thinking Структурированное пошаговое мышление с возможностью ветвления и пересмотра решений. ## Быстрый старт [Заголовок раздела «Быстрый старт»](#быстрый-старт) Все серверы поддерживают несколько способов запуска: ### Через uvx (рекомендуется для Python-серверов) [Заголовок раздела «Через uvx (рекомендуется для Python-серверов)»](#через-uvx-рекомендуется-для-python-серверов) ```bash uvx mcp-server-filesystem /path/to/directory ``` ### Через npx (для Node.js-серверов) [Заголовок раздела «Через npx (для Node.js-серверов)»](#через-npx-для-nodejs-серверов) ```bash npx -y @modelcontextprotocol/server-memory ``` ### Через Docker [Заголовок раздела «Через Docker»](#через-docker) ```bash docker run -i --rm mcp/filesystem /projects ``` ## Конфигурация для Claude Desktop [Заголовок раздела «Конфигурация для Claude Desktop»](#конфигурация-для-claude-desktop) Добавьте в файл `claude_desktop_config.json`: ```json { "mcpServers": { "filesystem": { "command": "uvx", "args": ["mcp-server-filesystem", "/Users/username/Documents"] }, "memory": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-memory"] } } } ``` ## Конфигурация для VS Code [Заголовок раздела «Конфигурация для VS Code»](#конфигурация-для-vs-code) Добавьте в `.vscode/mcp.json` или настройки пользователя: ```json { "servers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "${workspaceFolder}"] } } } ``` ## Доступные серверы [Заголовок раздела «Доступные серверы»](#доступные-серверы) ### Рекомендуемые [Заголовок раздела «Рекомендуемые»](#рекомендуемые) [Context7 ⭐ ](/servers/context7/)Актуальная документация библиотек в реальном времени [Filesystem ](/servers/filesystem/)Чтение, запись и управление файлами с контролем доступа [Git ](/servers/git/)Полный набор инструментов для работы с Git [Memory ](/servers/memory/)Граф знаний для персистентной памяти ### Все серверы [Заголовок раздела «Все серверы»](#все-серверы) [Fetch ](/servers/fetch/)Получение веб-контента с конвертацией в Markdown [Time ](/servers/time/)Работа с временными зонами IANA [Sequential Thinking ](/servers/sequentialthinking/)Пошаговое решение сложных задач [Everything ](/servers/everything/)Тестовый сервер со всеми возможностями MCP ## Исходный код [Заголовок раздела «Исходный код»](#исходный-код) Все эталонные серверы доступны в официальном репозитории: * [github.com/modelcontextprotocol/servers](https://github.com/modelcontextprotocol/servers) Репозиторий включает: * Исходный код всех серверов * Dockerfile для каждого сервера * Примеры конфигурации * Тесты и документацию ## Создание собственного сервера [Заголовок раздела «Создание собственного сервера»](#создание-собственного-сервера) Эталонные серверы — отличная отправная точка для создания собственных MCP-серверов. Изучите: 1. **Filesystem** — пример работы с инструментами и аннотациями 2. **Memory** — реализация персистентного хранилища 3. **Everything** — демонстрация всех возможностей протокола # Sequential Thinking Server > MCP-сервер для структурированного пошагового решения задач с возможностью ветвления и пересмотра решений **Sequential Thinking Server** предоставляет инструмент для динамичного и рефлексивного решения задач через структурированный мыслительный процесс. Позволяет разбивать сложные проблемы на шаги, пересматривать решения и исследовать альтернативные пути. ## Возможности [Заголовок раздела «Возможности»](#возможности) * Разбиение сложных задач на управляемые шаги * Пересмотр и уточнение мыслей по мере углубления понимания * Ветвление на альтернативные пути рассуждений * Динамическая корректировка общего числа шагов * Генерация и проверка гипотез решения ## Установка [Заголовок раздела «Установка»](#установка) * NPX ```bash npx -y @modelcontextprotocol/server-sequential-thinking ``` * Docker ```bash docker run --rm -i mcp/sequentialthinking ``` ## Инструмент [Заголовок раздела «Инструмент»](#инструмент) ### sequential\_thinking [Заголовок раздела «sequential\_thinking»](#sequential_thinking) Фасилитирует детальный пошаговый мыслительный процесс для решения задач и анализа. | Параметр | Тип | Обязательный | Описание | | ------------------- | ------- | ------------ | ------------------------------------- | | `thought` | string | Да | Текущий шаг мышления | | `nextThoughtNeeded` | boolean | Да | Нужен ли следующий шаг | | `thoughtNumber` | integer | Да | Номер текущей мысли | | `totalThoughts` | integer | Да | Оценочное общее число мыслей | | `isRevision` | boolean | Нет | Пересматривает ли предыдущее мышление | | `revisesThought` | integer | Нет | Какая мысль пересматривается | | `branchFromThought` | integer | Нет | Точка ветвления | | `branchId` | string | Нет | Идентификатор ветки | | `needsMoreThoughts` | boolean | Нет | Нужно ли больше мыслей | ## Применение [Заголовок раздела «Применение»](#применение) Sequential Thinking предназначен для: * **Разбиения сложных задач** — декомпозиция проблем на шаги * **Планирования и проектирования** — с возможностью пересмотра решений * **Анализа с коррекцией курса** — изменение направления при необходимости * **Задач с неизвестным scope** — когда полный объём неясен изначально * **Поддержки контекста** — сохранение контекста на протяжении нескольких шагов * **Фильтрации** — отсеивание нерелевантной информации ## Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация) ### Claude Desktop [Заголовок раздела «Claude Desktop»](#claude-desktop) * NPX ```json { "mcpServers": { "sequential-thinking": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-sequential-thinking"] } } } ``` * Docker ```json { "mcpServers": { "sequentialthinking": { "command": "docker", "args": ["run", "--rm", "-i", "mcp/sequentialthinking"] } } } ``` ### VS Code [Заголовок раздела «VS Code»](#vs-code) * NPX ```json { "servers": { "sequential-thinking": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-sequential-thinking"] } } } ``` * Docker ```json { "servers": { "sequential-thinking": { "command": "docker", "args": ["run", "--rm", "-i", "mcp/sequentialthinking"] } } } ``` ### Codex CLI [Заголовок раздела «Codex CLI»](#codex-cli) ```bash codex mcp add sequential-thinking npx -y @modelcontextprotocol/server-sequential-thinking ``` ## Отключение логирования [Заголовок раздела «Отключение логирования»](#отключение-логирования) Для отключения логирования информации о мыслях установите переменную окружения: ```json { "mcpServers": { "sequential-thinking": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-sequential-thinking"], "env": { "DISABLE_THOUGHT_LOGGING": "true" } } } } ``` ## Пример использования [Заголовок раздела «Пример использования»](#пример-использования) ### Базовое рассуждение [Заголовок раздела «Базовое рассуждение»](#базовое-рассуждение) ```json { "name": "sequential_thinking", "arguments": { "thought": "Начинаю анализ требований к системе авторизации", "thoughtNumber": 1, "totalThoughts": 5, "nextThoughtNeeded": true } } ``` ### Продолжение цепочки [Заголовок раздела «Продолжение цепочки»](#продолжение-цепочки) ```json { "name": "sequential_thinking", "arguments": { "thought": "Определяю основные компоненты: JWT токены, refresh механизм, хранилище сессий", "thoughtNumber": 2, "totalThoughts": 5, "nextThoughtNeeded": true } } ``` ### Пересмотр предыдущей мысли [Заголовок раздела «Пересмотр предыдущей мысли»](#пересмотр-предыдущей-мысли) ```json { "name": "sequential_thinking", "arguments": { "thought": "Пересматриваю решение — лучше использовать OAuth 2.0 вместо custom JWT", "thoughtNumber": 3, "totalThoughts": 6, "nextThoughtNeeded": true, "isRevision": true, "revisesThought": 2 } } ``` ### Ветвление [Заголовок раздела «Ветвление»](#ветвление) ```json { "name": "sequential_thinking", "arguments": { "thought": "Альтернативный подход: использовать сторонний identity provider", "thoughtNumber": 3, "totalThoughts": 5, "nextThoughtNeeded": true, "branchFromThought": 2, "branchId": "alt-auth" } } ``` ### Завершение цепочки [Заголовок раздела «Завершение цепочки»](#завершение-цепочки) ```json { "name": "sequential_thinking", "arguments": { "thought": "Заключение: выбираю OAuth 2.0 с Auth0 как identity provider", "thoughtNumber": 5, "totalThoughts": 5, "nextThoughtNeeded": false } } ``` ## Сценарии применения [Заголовок раздела «Сценарии применения»](#сценарии-применения) ### Архитектурное планирование [Заголовок раздела «Архитектурное планирование»](#архитектурное-планирование) 1. Определение требований 2. Анализ ограничений 3. Выбор технологий 4. Проектирование компонентов 5. Определение интерфейсов 6. Валидация решения ### Отладка сложных багов [Заголовок раздела «Отладка сложных багов»](#отладка-сложных-багов) 1. Описание симптомов 2. Формулирование гипотез 3. Проверка первой гипотезы 4. Пересмотр при неудаче 5. Ветвление на альтернативные причины 6. Подтверждение решения ### Code Review [Заголовок раздела «Code Review»](#code-review) 1. Понимание контекста изменений 2. Анализ архитектурного соответствия 3. Проверка edge cases 4. Оценка производительности 5. Формулирование рекомендаций Совет Sequential Thinking особенно полезен, когда LLM нужно “думать вслух” и структурировать сложный анализ. ## Сборка Docker-образа [Заголовок раздела «Сборка Docker-образа»](#сборка-docker-образа) ```bash docker build -t mcp/sequentialthinking -f src/sequentialthinking/Dockerfile . ``` ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [Исходный код на GitHub](https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking) * [NPM пакет](https://www.npmjs.com/package/@modelcontextprotocol/server-sequential-thinking) # Time Server > MCP-сервер для работы с временными зонами — получение текущего времени и конвертация между часовыми поясами IANA **Time Server** предоставляет возможности для работы со временем и часовыми поясами. Позволяет LLM получать текущее время и выполнять конвертацию между часовыми поясами, используя имена IANA с автоматическим определением системной временной зоны. ## Возможности [Заголовок раздела «Возможности»](#возможности) * Получение текущего времени в любой временной зоне * Конвертация времени между часовыми поясами * Автоматическое определение системной временной зоны * Поддержка всех временных зон IANA ## Установка [Заголовок раздела «Установка»](#установка) * uvx ```bash uvx mcp-server-time ``` * pip ```bash pip install mcp-server-time python -m mcp_server_time ``` * Docker ```bash docker run -i --rm -e LOCAL_TIMEZONE mcp/time ``` ## Инструменты [Заголовок раздела «Инструменты»](#инструменты) ### get\_current\_time [Заголовок раздела «get\_current\_time»](#get_current_time) Получает текущее время в указанной временной зоне или системной зоне. | Параметр | Тип | Описание | | ---------- | ------ | ----------------------- | | `timezone` | string | Имя временной зоны IANA | **Примеры временных зон IANA:** * `Europe/Moscow` * `America/New_York` * `Europe/London` * `Asia/Tokyo` * `Australia/Sydney` **Пример запроса:** ```json { "name": "get_current_time", "arguments": { "timezone": "Europe/Warsaw" } } ``` **Пример ответа:** ```json { "timezone": "Europe/Warsaw", "datetime": "2024-01-01T13:00:00+01:00", "is_dst": false } ``` ### convert\_time [Заголовок раздела «convert\_time»](#convert_time) Конвертирует время между временными зонами. | Параметр | Тип | Описание | | ----------------- | ------ | ---------------------------------- | | `source_timezone` | string | Исходная временная зона IANA | | `time` | string | Время в 24-часовом формате (HH:MM) | | `target_timezone` | string | Целевая временная зона IANA | **Пример запроса:** ```json { "name": "convert_time", "arguments": { "source_timezone": "America/New_York", "time": "16:30", "target_timezone": "Asia/Tokyo" } } ``` **Пример ответа:** ```json { "source": { "timezone": "America/New_York", "datetime": "2024-01-01T12:30:00-05:00", "is_dst": false }, "target": { "timezone": "Asia/Tokyo", "datetime": "2024-01-01T12:30:00+09:00", "is_dst": false }, "time_difference": "+13.0h" } ``` ## Конфигурация [Заголовок раздела «Конфигурация»](#конфигурация) ### Claude Desktop [Заголовок раздела «Claude Desktop»](#claude-desktop) * uvx ```json { "mcpServers": { "time": { "command": "uvx", "args": ["mcp-server-time"] } } } ``` * Docker ```json { "mcpServers": { "time": { "command": "docker", "args": ["run", "-i", "--rm", "-e", "LOCAL_TIMEZONE", "mcp/time"] } } } ``` * pip ```json { "mcpServers": { "time": { "command": "python", "args": ["-m", "mcp_server_time"] } } } ``` ### VS Code [Заголовок раздела «VS Code»](#vs-code) * uvx ```json { "mcp": { "servers": { "time": { "command": "uvx", "args": ["mcp-server-time"] } } } } ``` * Docker ```json { "mcp": { "servers": { "time": { "command": "docker", "args": ["run", "-i", "--rm", "mcp/time"] } } } } ``` ### Zed [Заголовок раздела «Zed»](#zed) ```json { "context_servers": [ "mcp-server-time": { "command": "uvx", "args": ["mcp-server-time"] } ] } ``` ### Zencoder [Заголовок раздела «Zencoder»](#zencoder) ```json { "command": "uvx", "args": ["mcp-server-time"] } ``` ## Настройка системной временной зоны [Заголовок раздела «Настройка системной временной зоны»](#настройка-системной-временной-зоны) По умолчанию сервер автоматически определяет системную временную зону. Можно переопределить через аргумент `--local-timezone`: ```json { "command": "python", "args": ["-m", "mcp_server_time", "--local-timezone=America/New_York"] } ``` ## Примеры вопросов для Claude [Заголовок раздела «Примеры вопросов для Claude»](#примеры-вопросов-для-claude) 1. “Который сейчас час?” — использует системную временную зону 2. “Сколько времени в Токио?” 3. “Когда в Нью-Йорке 16:00, сколько времени в Лондоне?” 4. “Конвертируй 9:30 утра по токийскому времени в нью-йоркское” 5. “Какая разница во времени между Москвой и Сиднеем?” ## Отладка [Заголовок раздела «Отладка»](#отладка) Используйте MCP Inspector: ```bash npx @modelcontextprotocol/inspector uvx mcp-server-time ``` Или при разработке: ```bash cd path/to/servers/src/time npx @modelcontextprotocol/inspector uv run mcp-server-time ``` ## Популярные временные зоны IANA [Заголовок раздела «Популярные временные зоны IANA»](#популярные-временные-зоны-iana) | Зона | Регион | | --------------------- | ---------------------- | | `Europe/Moscow` | Москва, Россия | | `Europe/Kiev` | Киев, Украина | | `Europe/Minsk` | Минск, Беларусь | | `Asia/Almaty` | Алматы, Казахстан | | `Europe/London` | Лондон, Великобритания | | `Europe/Paris` | Париж, Франция | | `Europe/Berlin` | Берлин, Германия | | `America/New_York` | Нью-Йорк, США | | `America/Los_Angeles` | Лос-Анджелес, США | | `Asia/Tokyo` | Токио, Япония | | `Asia/Shanghai` | Шанхай, Китай | | `Asia/Dubai` | Дубай, ОАЭ | | `Australia/Sydney` | Сидней, Австралия | Совет Полный список временных зон IANA доступен на [Wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). ## Сборка Docker-образа [Заголовок раздела «Сборка Docker-образа»](#сборка-docker-образа) ```bash cd src/time docker build -t mcp/time . ``` ## Ссылки [Заголовок раздела «Ссылки»](#ссылки) * [Исходный код на GitHub](https://github.com/modelcontextprotocol/servers/tree/main/src/time) * [PyPI пакет](https://pypi.org/project/mcp-server-time/)