Hardcore Tutorial 2026-06-16

MCP Server
с нуля

Platform-инженеру, которому нужно отдать агенту доступ к internal API без N×M обёрток под каждый Host, этот текст даёт полный стек: Model Context Protocol поверх JSON-RPC 2.0, реализация на Python/FastMCP, stdio → streamable HTTP, asyncio handlers, structured errors, ChromaDB knowledge base. Внутри — wire-level разбор транспортов, матрица REST vs MCP, семь шагов HowTo, три метрики производительности и финальный блок про bare-metal Apple Silicon для macOS-only toolchains (Keychain, TCC, osascript).

Архитектура MCP Server FastMCP 2026: JSON-RPC, Tools, Resources, streamable HTTP

01 · Зачем писать свой MCP Server

Готовые server-filesystem/postgres/github не знают ваш entitlement layer, billing rules и redaction policy. Custom MCP — тонкая façade: 3–7 операций с JSON Schema, scoped tokens, audit log каждого tools/call. Паттерн 2026: REST внутри, MCP снаружи.

Протокол — в обзоре MCP как HTTP эпохи ИИ; здесь — implementation depth.

02 · Три системных боли

1. N×M интеграций. Cursor, Claude Desktop, CI — три адаптера на один «lookup order». MCP коллапсирует surface в один процесс/endpoint.

2. Privilege inheritance. stdio-server — дочерний процесс Host с UID пользователя, полный доступ к ~/.ssh, Keychain items, env с production tokens.

3. Opaque failure modes. Любой байт на stdout ломает JSON-RPC framing; sync blocking IO даёт timeout без stack trace в Host UI.

03 · Wire protocol: Host / Client / Server

Host (Cursor) ──spawn──▶ MCP Server (stdio)
     │ JSON-RPC 2.0 frames on stdin/stdout
     │ initialize → capabilities negotiate
     │ tools/list → JSON Schema[]
     └ tools/call(name, args) → structured content

stdio: subprocess, zero network attack surface — идеален для local dev. Streamable HTTP: single endpoint, bidirectional JSON-RPC, OAuth 2.1 на edge — default для team deploy. Legacy SSE уходит в 2025–2026 миграциях.

{ "jsonrpc": "2.0", "id": 42, "method": "tools/call",
  "params": { "name": "lookup_account", "arguments": { "email": "dev@example.com" } } }

04 · Матрица: REST vs Function Calling vs MCP

ОсьRESTFunction CallingMCP
DiscoveryOpenAPI (human)Embedded in prompttools/list machine-readable
SessionStatelessPer-request ad hocCapability negotiation
Latency overheadBaseline HTTPIn-process+JSON-RPC frame (~µs stdio)
Metal/macOS toolsN/AHost-specificstdio on bare Mac

Метрика 1: FastMCP сокращает boilerplate на 60–70 % vs raw SDK (five-tool benchmark). Метрика 2: повторные ops tasks — −38…−55 % input tokens после замены pasted API docs. Метрика 3: idle stdio process 50–120 MB RSS; budget 256 MB в prod.

05 · Окружение: Python 3.11+ и uv

mkdir my-mcp-server && cd my-mcp-server
uv init && uv venv && source .venv/bin/activate
uv add "mcp[cli]" fastmcp httpx pydantic chromadb
mcp --help

На macOS system Python устаревший — Homebrew/pyenv. Node 20+ для Inspector. Working server за <20 мин vs ~90 мин manual schema wiring.

06 · Hello World: stdio framing

from fastmcp import FastMCP
mcp = FastMCP("hello-mcp")
@mcp.tool()
def greet(name: str) -> str:
  return f"Hello, {name}"
if __name__ == "__main__": mcp.run()

Cursor config: absolute path к venv python + server.py. Failures: stderr pollution, wrong interpreter без fastmcp in site-packages.

07 · Tools: sync, async, guardrails

PatternHandlerConstraint
lookup_accountsyncemail validation
fetch_weatherasync httpxtimeout 10s
query_ticketsasync pgLIMIT ≤50
write_notesync Pathresolve + prefix check
summarize_metricsasync gather≤10 service_ids
from mcp.server.fastmcp.exceptions import ToolError
@mcp.tool()
async def create_refund(ticket_id: str, cents: int) -> str:
  try: return (await billing.preview(ticket_id, cents)).summary
  except billing.NotFound: raise ToolError(f"{ticket_id} not found")

Cap tools at 10–15; >20 — measurable wrong-tool rate в agent logs.

08 · Resources: read-only URI layer

@mcp.resource("kb://doc/{path:path}")
def kb_doc(path: str) -> str:
  full = (DOCS_ROOT / path).resolve()
  if not str(full).startswith(str(DOCS_ROOT)): raise PermissionError
  return full.read_text()

Hosts часто auto-fetch resources без approval — zero secrets, zero raw PII dumps.

09 · Prompts: orchestration templates

@mcp.prompt()
def triage_ticket(ticket_id: str) -> str:
  return f"Triage {ticket_id}: query_tickets → hypothesis → no prod writes"

См. Agent Skill guide — Skill = procedure, Prompt = dialog skeleton.

10 · Streamable HTTP: prod transport

mcp.run(transport="streamable-http", host="0.0.0.0", port=8080)

TLS terminate at nginx/Caddy; rate limit 60 req/min/token; log request_id per tools/call. Public stdio tunnel = monthly credential leak reports.

11 · Debug pipeline (4 layers)

  1. npx @modelcontextprotocol/inspector python server.py
  2. logging.basicConfig(stream=sys.stderr) — never print stdout
  3. pytest + ClientSession + stdio_client
  4. Host smoke: read-only, bounded write, approval dialog
СимптомПричинаFix
Connection closedcrash / stdout pollutionrun server solo, check stderr
tools/call timeoutsync block IOasync + timeouts
HTTP 401expired bearerrotate token

12 · Production: 7-step hardening

  1. Pin mcp/fastmcp versions
  2. Vault-injected secrets
  3. systemd Restart=always, MemoryMax=256M
  4. /health off MCP route
  5. Prometheus: alert error_rate >5% / 5min
  6. OAuth minimal scopes
  7. Staging on isolated Mac — OpenClaw MCP

13 · Knowledge base: ChromaDB + sqlite-fts5

Enterprise default: index markdown, tools search_docs/get_doc, resource kb://. Wiki 2000 pages ≈ 180 MB sqlite-fts5, search p95 <200 ms on M4 (unified memory, no PCIe hop to discrete GPU).

@mcp.tool()
async def search_docs(q: str, limit: int = 5) -> list[dict]:
  hits = await index.search(q, top_k=min(limit, 20))
  return [{"path": h.path, "score": h.score} for h in hits]

14 · Экосystem June 2026

10 000+ public servers (Glama, Smithery). SDKs: Python FastMCP, TS @modelcontextprotocol/sdk, Go/Rust gateways. MCP ≠ A2A: tools vs agent routing. Registry search before rewrite.

15 · Bare-metal Mac: wipe before return

Полный path: Hello World → typed tools → HTTP → KB → observability. Custom MCP beats dumping OpenAPI into context.

Limits daily driver: stdio inherits Keychain + TCC; Linux Docker не эмулирует Seatbelt/security CLI; cloud VM latency skews stdio benchmarks used for capacity planning; shared machine with App Store signing keys = blast radius.

Sprint MCP dev с write-capable tools: аренда Mac mini M4 — Apple Silicon как в prod, clean user, SSH/VNC для GUI approvals, wipe без следов. Day billing = 1–2 week pilot без CapEx.

MacDate · MCP Dev

Собирайте MCP на Mac, который можно стереть — не на машине с signing keys.

Mac mini M4 / Mac Studio: SSH/VNC, посуточная оплата, чеклисты для FastMCP + Inspector + multi-Host smoke.

Bare-metal тарифы · Чек-лист первой аренды

Читать далее