Свой MCP-сервер за вечер
Model Context Protocol — это способ дать Claude доступ к вашим собственным инструментам. Не просто функции в промпте, а полноценный сервер, который подключается к любому MCP-совместимому клиенту: Claude Desktop, Claude Code, Cursor, другим.
За вечер можно поднять свой сервер под рабочую задачу. Пошагово.
Зачем это вам
Простой пример. У компании есть внутренняя база: Confluence, Notion или что-то самописное. Хочется, чтобы Claude мог её искать и читать, не копируя вручную каждую статью в чат. Решение — MCP-сервер, который оборачивает API вашей базы и даёт его Claude.
Второй пример — локальные скрипты. У вас есть набор утилит: запустить тесты определённым образом, собрать проект с особыми флагами, прогнать линтер по нестандартной конфигурации. MCP упаковывает их в инструменты, которые Claude может вызывать сам.
Структура минимального сервера
Берём TypeScript и официальный SDK.
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
index.ts:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "my-tools", version: "0.1.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "search_docs",
description: "Search internal documentation",
inputSchema: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (req) => {
if (req.params.name === "search_docs") {
const q = (req.params.arguments as { query: string }).query;
const results = await doActualSearch(q);
return { content: [{ type: "text", text: results }] };
}
throw new Error("Unknown tool");
});
const transport = new StdioServerTransport();
await server.connect(transport);
Всё. Это полностью рабочий MCP-сервер. Запускается как stdio-процесс — клиент общается с ним через stdin/stdout в формате JSON-RPC.
Подключаем к Claude Code
В ~/.config/claude-code/mcp_settings.json:
{
"mcpServers": {
"my-tools": {
"command": "node",
"args": ["/absolute/path/to/my-mcp-server/index.js"]
}
}
}
Перезапускаем Claude Code. В диалоге появляется инструмент search_docs. Claude сам решит, когда его вызвать.
Что важно в реальных серверах
Типизация через zod. Описывайте схемы входа/выхода явно. Claude читает описание и использует его, чтобы правильно сформировать вызов. Плохое описание — плохие вызовы.
Осмысленные ошибки. Если инструмент упал — не просто Error: failed, а Failed to fetch from Notion API: token expired, re-auth with <url>. Claude использует эти сообщения, чтобы понять что случилось и как исправить.
Идемпотентность. Claude может повторить вызов, если ответ кажется неполным. Инструмент, делающий запись, должен это пережить.
Granularity. Один большой инструмент do_everything — плохо. Десять узких — хорошо. Модель выбирает, какой нужен.
Когда MCP лишний
Если ваша задача — одноразовая автоматизация скриптом на 50 строк, не нужен MCP. Запустите claude напрямую, попросите сделать и всё.
MCP оправдан, когда один и тот же набор инструментов нужен постоянно, причём разным агентам (вы лично через Claude Desktop + коллеги через Cursor + CI-job через SDK). Тогда имеет смысл вынести логику в сервер и переиспользовать.
Куда развиваться
В протоколе есть ещё prompts и resources — не только tools. Prompts это готовые шаблоны, которые клиент может предложить пользователю. Resources — файлы/данные, которые Claude может читать как контекст. Для больших баз знаний это удобнее, чем tools.
Если сервер получается полезным — опубликуйте на npm, добавьте в официальный реестр MCP. Станет заметнее и другие ИИ-агенты смогут его использовать.
Нужен свой MCP-сервер под задачу? Напишите, построю.