MCP Server с нуля
Полное руководство для разработчиков

Если Claude / GPT / Cursor умеет только чат, но не может ходить в БД, читать файлы или вызывать API — проблема не в длине prompt, а в отсутствии переиспользуемого tool layer. Этот материал для backend- и fullstack-разработчиков: от Hello World до ChromaDB knowledge-base MCP Server, с разбором Tools / Resources / Prompts, транспортов stdio и HTTP+SSE, отладки, тестов и Docker production deploy. В конце у вас будут кастомные tools, вызываемые из Cursor, и понимание, как держать Server на выделенном удалённом Mac 7×24 (протокольный контекст — в разборе MCP).

01

Почему AI нужен MCP Server: LLM без инструментов — лишь «говорящий мозг»

Training cutoff у LLM фиксирован; CRM, Git-репозитории и internal API недоступны из weights. До 2024 для Claude писали Function Calling, для GPT — Plugins, для Cursor — отдельный формат: смена модели = переписывание adapter layer с нуля. MCP Server инкапсулирует tool capability в изолированный процесс — один раз написали, используете в Claude Desktop / Cursor / Gemini.

Типовые сценарии: SQL-запрос к Postgres из Claude Desktop; Cursor Agent читает project docs и правит код; GPT дергает ticketing system через HTTP MCP — под капотом один и тот же Server process.

Что даёт гайд: не теорию ради теории, а путь от say_hello до production-grade knowledge-base Server с vector retrieval. Целевая аудитория: разработчики с базой Python или TypeScript, расширяющие AI в IDE или Desktop host.

Шесть pain points: почему «обёртка над REST API» не решает задачу

  1. 01

    Vendor lock-in: OpenAI Function Calling и Claude Tool Use — разные JSON Schema и lifecycle; каждая смена вендора требует нового adapter.

  2. 02

    Tools не discoverable: REST живёт в static docs; runtime tools/list для автономного discovery capability отсутствует.

  3. 03

    IDE silos: Cursor, VS Code extensions и JetBrains plugins описывают tools по-разному — N×M интеграций вместо одного Server.

  4. 04

    Context/data split: LLM не может стабильно читать config, user prefs или live logs — нужен standardized read-only channel через Resources.

  5. 05

    Prompt templates разбросаны: code review, incident postmortem — нет единого registry; команды копируют шаблоны вручную.

  6. 06

    Local vs remote deploy chaos: stdio subprocess подходит для dev, но production HTTP gateway, auth и monitoring без общего паттерна (см. гайд по stdio subprocess governance).

«Подключить MCP Server к AI — как поставить IDE-плагин программисту: capability boundary смещается от chat к операциям над реальным миром.»

02

Что такое MCP: от Function Calling к открытому протоколу

Эволюция: Function Calling (2023)ChatGPT PluginsMCP (ноябрь 2024, Anthropic open source). MCP задуман как «USB-C» между AI и external world: Host (Cursor / Claude Desktop) встраивает MCP Client и открывает 1:1 session с вашим MCP Server.

Архитектура: Client / Server и три capability

  • Tools: callable operations с side effects — query БД, write файлов, HTTP requests.
  • Resources: read-only context — config, doc snippets, user profile; адресация через URI scheme.
  • Prompts: pre-built multi-turn skeleton — code review, incident postmortem.

Transport layer — JSON-RPC 2.0: initializetools/list / tools/callresources/read. Два lifecycle транспорта:

  • stdio: Host spawn Server как subprocess; stdin/stdout несут JSON-RPC frames; session завершается при exit процесса.
  • HTTP + SSE: Client подключается к remote URL; Server push event stream — team sharing и horizontal scale.

Полная спецификация: modelcontextprotocol.io.

MCP vs OpenAI FC vs LangChain Tools

ИзмерениеMCPOpenAI Function CallingLangChain Tools
ОткрытостьCross-vendor протокол, governance AAIFПривязка к OpenAI APIFramework abstraction, не transport standard
DiscoveryRuntime tools/listInline functions array в requestCode registration, нет standard discovery
Read-only dataResources + URI schemeНет first-class аналогаRetriever concept, не protocol-level
Prompt templatesPrompts standard interfaceНетPromptTemplate class
Transportstdio / HTTP+SSE / Streamable HTTPHTTPS API bundledЗависит от Agent runtime
ReusabilityОдин Server для Cursor + Claude + GeminiТолько OpenAI ecosystemCross-framework — rewrite bindings
03

Dev environment: Python FastMCP vs TypeScript SDK

Два основных стека: Python mcp + FastMCP (data/script, быстрый prototype) и TypeScript @modelcontextprotocol/sdk (Web/API integration, compile-time type safety). SDK repos: python-sdk, typescript-sdk.

bash
# Python stack
python -m venv .venv && source .venv/bin/activate
pip install "mcp[cli]" httpx pydantic

# TypeScript stack
npm init -y && npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node

Рекомендуемая структура проекта

tree
my-mcp-server/
├── pyproject.toml          # или package.json
├── src/
│   ├── server.py           # FastMCP entrypoint
│   ├── tools/              # tool modules
│   ├── resources/          # Resource providers
│   └── prompts/            # Prompt templates
├── tests/
│   └── test_tools.py       # pytest + ClientSession
├── Dockerfile
└── README.md

Шестишаговый checklist готовности окружения

  1. 01

    Выбор стека: data/ML — Python; Node fullstack — TypeScript.

  2. 02

    Virtual env + lock dependencies: pip freeze или package-lock.json — иначе schema drift между dev и prod.

  3. 03

    MCP Inspector: npx @modelcontextprotocol/inspector — visual debug JSON-RPC frames.

  4. 04

    Claude Desktop config: правка ~/Library/Application Support/Claude/claude_desktop_config.json, добавление command/args Server.

  5. 05

    Cursor config: Settings → MCP → Add Server; для stdio — python -m src.server или absolute path к interpreter.

  6. 06

    Verify Inspector: запуск Server → connect Inspector → tools/list возвращает non-empty list.

json
// Cursor / Claude Desktop MCP config
{
  "mcpServers": {
    "my-tools": {
      "command": "python",
      "args": ["-m", "src.server"],
      "env": { "API_KEY": "your-key" }
    }
  }
}
04

Hello World: первый MCP Server за 30 секунд

FastMCP + tool say_hello — проверка полного pipeline: code → Inspector → Cursor handshake.

python
# src/server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("hello-server")

@mcp.tool()
def say_hello(name: str = "World") -> str:
    """Приветствие по имени"""
    return f"Hello, {name}! MCP is working"

if __name__ == "__main__":
    mcp.run()  # default stdio transport
bash
# Debug через Inspector
npx @modelcontextprotocol/inspector python -m src.server

# Прямой stdio launch
python -m src.server

После добавления того же command в Cursor попросите Agent: «вызови say_hello для NodeMini». JSON result в ответе означает успешный Client ↔ Server handshake по JSON-RPC.

info

Примечание: FastMCP генерирует JSON Schema из docstring и type hints — ручное описание параметров не требуется.

05

Tools deep dive: от Schema к пяти production-grade tools

Tools — core capability MCP: AI выполняет side-effect operations через tools/call. Каждый Tool экспонирует name, description, inputSchema; FastMCP валидирует через Pydantic models.

python
from pydantic import BaseModel, Field

class SearchInput(BaseModel):
    query: str = Field(..., description="Поисковый запрос")
    limit: int = Field(10, ge=1, le=100, description="Число результатов")

@mcp.tool()
async def search_docs(params: SearchInput) -> str:
    """Поиск по документам"""
    results = await index.search(params.query, params.limit)
    return json.dumps(results, ensure_ascii=False)

Пять типовых Tool patterns

  • calculator: safe eval или ast.literal_eval; exec запрещён.
  • file_read / file_write: root directory whitelist против path traversal.
  • fetch_url (async): httpx + timeout + domain whitelist.
  • db_query: read-only SQL, parameterized queries; DDL запрещён.
  • get_current_time: ISO8601 с timezone — против time hallucination LLM.
python
import httpx
from datetime import datetime, timezone

@mcp.tool()
async def fetch_url(url: str) -> str:
    """HTTP GET (domain whitelist)"""
    allowed = ("api.github.com", "nodemini.com")
    if not any(url.startswith(f"https://{d}") for d in allowed):
        raise ValueError(f"Domain not allowed: {url}")
    async with httpx.AsyncClient(timeout=10.0) as client:
        resp = await client.get(url)
        resp.raise_for_status()
        return resp.text[:8000]

@mcp.tool()
def get_current_time() -> str:
    """Текущее UTC время"""
    return datetime.now(timezone.utc).isoformat()

Error handling: operational checklist

  • Structured errors: raise ValueError("human-readable msg") — Client передаёт message обратно в LLM context.
  • Retryable vs fatal: network timeout помечайте retryable; permission denied — fail fast.
  • Logging + redaction: log tool name и latency; не логируйте API keys и PII целиком.
  • Idempotency: write operations с idempotency_key — защита от duplicate Agent calls.
06

Resources: read-only context и URI schemes

Tool vs Resource: Tool имеет side effects и вызывается AI; Resource — read-only context, который Host inject до диалога или AI тянет через resources/read. URI schemes кастомные: config://, user://, file://.

python
@mcp.resource("config://app/settings")
def app_settings() -> str:
    """Static app config (text/plain)"""
    return open("config/settings.json").read()

@mcp.resource("user://{user_id}/profile")
def user_profile(user_id: str) -> str:
    """Dynamic user profile (application/json)"""
    return json.dumps(get_user(user_id))

MIME types для Resources

MIME typeНазначениеПример
text/plainLogs, READMEfile://logs/app.log
application/jsonConfig, API responseconfig://env
application/octet-streamBinary (base64)PDF summary
text/event-streamLive subscriptionLog tail, metrics stream

Filesystem Resource Server pattern: resources/list сканирует директории, resources/read читает по URI, resources/subscribe через watchfiles push updates — expose codebase docs для Cursor Agent без ручного copy-paste.

07

Prompts: переиспользуемые multi-turn templates

MCP Prompt — conversation skeleton, зарегистрированный на Server. Client получает message list через prompts/get с ролями user / assistant и parameter placeholders — team-wide code review и incident workflow без per-user prompt files.

python
from mcp.types import PromptMessage, TextContent

@mcp.prompt()
def code_review_prompt(language: str = "python") -> list[PromptMessage]:
    """Стандартный Code Review template"""
    return [
        PromptMessage(role="user", content=TextContent(
            type="text",
            text=f"Ты senior {language} engineer. Проверь diff по security, performance, readability."
        )),
        PromptMessage(role="assistant", content=TextContent(
            type="text",
            text="Вставь diff или укажи PR — выдам structured review по CHECKLIST."
        )),
    ]

Variables ({ticket_id}, {severity}) versionируются на Server; upgrade Server у Client = вся команда на актуальном review standard.

08

HTTP transport: от stdio к Streamable HTTP production deploy

ИзмерениеstdioHTTP + SSE / Streamable HTTP
DeployLocal subprocess, Host-spawnedStandalone service, URL connect
ScaleSingle machine, horizontal scale затруднёнLoad balancer, multiple replicas
AuthHost env varsBearer Token / API Key / mTLS
DebugInspector directcurl + SSE client
Best forPersonal dev, local CursorTeam sharing, SaaS integration
python
# Streamable HTTP (FastMCP 2026)
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("prod-server", host="0.0.0.0", port=8080)

# ... register tools ...

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

Production must-haves: Bearer Token middleware, CORS whitelist (только Host domains), rate limit (например 100 req/min/IP), HTTPS termination на reverse proxy. Remote gateway ops — в гайде по HTTP gateway governance.

warning

Внимание: HTTP MCP без auth на public internet — в 2026 по-прежнему массовая проблема (unauthorized exposed Servers). Обязательны auth и IP restrictions.

09

Debug и тесты: Inspector + pytest unit tests

MCP Inspector — official visual debugger: connect через stdio или URL, manual tools/list / tools/call, inspect JSON-RPC round trips — на порядок быстрее, чем reverse-engineering Cursor logs.

python
# tests/test_tools.py
import pytest
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

@pytest.mark.asyncio
async def test_say_hello():
    params = StdioServerParameters(command="python", args=["-m", "src.server"])
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("say_hello", {"name": "MCP"})
            assert "MCP" in result.content[0].text

Таблица типовых ошибок

СимптомПричинаFix
Server exit сразу после startstdout polluted print()Logs в stderr; stdout только JSON-RPC
tools/list пустойDecorator не registered / import order@mcp.tool() до run()
Cursor disconnectedWrong command path / venv не activeAbsolute paths; full python path в config
JSON-RPC parse errorNon-JSON на stdioDisable debug banner; log level WARNING+
10

Production deploy: Docker, cloud platforms, observability

dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY pyproject.toml .
RUN pip install --no-cache-dir .
COPY src/ src/
EXPOSE 8080
HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1
CMD ["python", "-m", "src.server", "--transport", "streamable-http"]

Platform selection: Railway / Render — быстрая validation; AWS ECS / GCP Cloud Run — enterprise compliance; VPS + Docker Compose — минимальная стоимость, self-managed patching.

Production parameters (EEAT)

  • Protocol version: MCP spec 2025-03-26 добавил Streamable HTTP; Client negotiates protocolVersion в initialize — Server декларирует compatible range.
  • Resource baseline: lightweight Tool Server ~128MB RAM; ChromaDB vector index — ≥2GB RAM + SSD persistent volume.
  • Observability stack: structured JSON logs → Loki/CloudWatch; Prometheus /metrics (tool QPS, P99 latency); Sentry для unhandled exceptions; /health для K8s liveness.
11

Hands-on: ChromaDB knowledge base MCP Server

Vectorize internal Wiki / Markdown и expose index_document, search_knowledge, write_note — Cursor Agent сначала ищет в company knowledge base, затем пишет код с grounded context.

Requirements: incremental indexing (watchfiles на docs/), semantic search Top-K, optional scratchpad writes.

python
import chromadb
from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction

client = chromadb.PersistentClient(path="./data/chroma")
collection = client.get_or_create_collection(
    "wiki", embedding_function=SentenceTransformerEmbeddingFunction()
)

@mcp.tool()
def search_knowledge(query: str, top_k: int = 5) -> str:
    """Semantic search по internal KB"""
    hits = collection.query(query_texts=[query], n_results=top_k)
    return json.dumps(hits["documents"][0], ensure_ascii=False)

@mcp.tool()
def index_document(path: str) -> str:
    """Index одного Markdown файла"""
    text = open(path).read()
    collection.upsert(ids=[path], documents=[text], metadatas=[{"path": path}])
    return f"Indexed: {path}"

Cursor demo query: «найди в knowledge base документы про MCP HTTP deploy и сформулируй three-step launch checklist» — Agent вызывает search_knowledge, затем отвечает по retrieved context. На большем scale vector store заменяют на Qdrant (remote gRPC).

12

Экосистема, тренды 2026 и learning path

Official и community Servers уже покрывают типовые задачи — не обязательно писать всё с нуля:

  • mcp-server-filesystem — sandboxed file I/O
  • mcp-server-github — PR/Issue API
  • brave-search / postgres / slack — search, SQL, team collaboration

Тренды 2026: MCP Marketplace, OAuth 2.1 tool authorization в spec roadmap, Streamable HTTP постепенно заменяет pure SSE. Learning path: ① spec → ② Hello World → ③ три Tool → ④ Resource → ⑤ pytest → ⑥ Docker → ⑦ Cursor.

Итог

От say_hello до ChromaDB knowledge base — full-stack MCP Server: Tools execution, Resources context, Prompts templates, dual transport, testing, production ops. Следующий шаг: fork community Server или wrap company API в team-standard tool layer.

Local stdio подходит для экспериментов, но несколько параллельных Server, persistent vector index и HTTP long connections быстро упирают 16GB laptop в swap. Дешёвый Linux VPS не тянет macOS-only toolchains (Xcode, codesign). Self-built HTTP gateway без session affinity и auth даёт connection leaks и unauthorized exposure — long-term stability редко совпадает с ожиданиями.

Командам, для которых MCP — production infrastructure плюс Cursor Agent и iOS/macOS CI, размещение MCP Server на выделенном cloud Mac 7×24 обычно предсказуемее, чем local laptop или generic VM. NodeMini Mac Mini cloud rental — execution layer для MCP + Agent: SSH node и Server config не меняются при смене underlying LLM. Спецификации — тарифы аренды, onboarding — Help Center.

FAQ

Частые вопросы

Python FastMCP — fastest path для data/script tools; TypeScript SDK — type safety и seamless Node integration. Протокол полностью совместим. Для нескольких Server 7×24 см. тарифы аренды remote Mac.

Function Calling привязан к OpenAI; MCP — open protocol для Claude/GPT/Gemini/Cursor с Resources и Prompts. Контекст — в разборе MCP.

Лёгкий stdio — локально; несколько Server + vector store + HTTP long connections — выделенный remote Mac. Onboarding: Help Center; ops — гайд по stdio subprocess governance.