MCP (Model Context Protocol) — стандарт Anthropic для подключения внешних инструментов к языковым моделям. OpenClaw поддерживает MCP из коробки, и создать собственный сервер значительно проще, чем кажется. Этот гайд — от нуля до рабочего сервера, опубликованного в интернете.
Что такое MCP и зачем его создавать
MCP позволяет OpenClaw вызывать любой внешний код как инструмент. Когда вы просите агента «проверь цену на товар» или «обнови запись в базе», он вызывает MCP-сервер, который выполняет реальный HTTP-запрос или SQL-команду.
Зачем создавать свой сервер:
- Подключить внутренние API компании (CRM, ERP, базу данных)
- Интегрировать сервисы без готового MCP (Bitrix24, AmoCRM, 1C)
- Создать специализированные инструменты для своего workflow
- Монетизировать: продавать MCP-серверы другим командам
Установка и настройка окружения
Вам понадобится Node.js 18+ и TypeScript:
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
Создайте tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"strict": true
},
"include": ["src/**/*"]
}
Структура проекта
my-mcp-server/
├── src/
│ ├── index.ts # Точка входа
│ ├── tools/ # Инструменты (что умеет сервер)
│ │ ├── weather.ts
│ │ └── database.ts
│ └── resources/ # Ресурсы (данные для чтения)
│ └── config.ts
├── package.json
└── tsconfig.json
Создание минимального MCP-сервера
Файл src/index.ts:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
// Создаём сервер
const server = new Server(
{
name: "my-mcp-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Список доступных инструментов
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_weather",
description: "Получить текущую погоду для города",
inputSchema: {
type: "object",
properties: {
city: {
type: "string",
description: "Название города на русском или английском",
},
},
required: ["city"],
},
},
],
};
});
// Обработка вызовов инструментов
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "get_weather") {
const { city } = request.params.arguments as { city: string };
// Здесь ваш реальный код
const weather = await fetchWeather(city);
return {
content: [
{
type: "text",
text: `Погода в ${city}: ${weather.temp}°C, ${weather.description}`,
},
],
};
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
async function fetchWeather(city: string) {
// Пример с OpenWeatherMap API
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=YOUR_KEY&units=metric&lang=ru`
);
const data = await response.json();
return {
temp: Math.round(data.main.temp),
description: data.weather[0].description,
};
}
// Запуск сервера
const transport = new StdioServerTransport();
await server.connect(transport);
Подключение к OpenClaw
Добавьте сервер в конфигурацию OpenClaw (~/.openclaw/openclaw.json):
{
"mcpServers": {
"my-weather": {
"command": "node",
"args": ["/path/to/my-mcp-server/dist/index.js"]
}
}
}
Или через npx без сборки (для разработки):
{
"mcpServers": {
"my-weather": {
"command": "npx",
"args": ["tsx", "/path/to/my-mcp-server/src/index.ts"]
}
}
}
Перезапустите OpenClaw. Теперь агент может вызывать get_weather.
Практический пример: MCP для внутренней базы данных
Самый востребованный кейс — подключение PostgreSQL:
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
// Инструмент: безопасный SQL-запрос
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "query_customers") {
const { search } = request.params.arguments as { search: string };
// Параметризованный запрос — никакого SQL injection
const result = await pool.query(
"SELECT id, name, email FROM customers WHERE name ILIKE $1 LIMIT 10",
[`%${search}%`]
);
return {
content: [
{
type: "text",
text: JSON.stringify(result.rows, null, 2),
},
],
};
}
});
Важно: никогда не передавайте пользовательский ввод напрямую в SQL. Всегда используйте параметризованные запросы.
Добавление ресурсов (Resources)
Ресурсы — это данные, которые агент может читать, в отличие от инструментов (активных действий):
import {
ListResourcesRequestSchema,
ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "config://settings",
name: "Настройки системы",
description: "Текущие настройки приложения",
mimeType: "application/json",
},
],
};
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
if (request.params.uri === "config://settings") {
return {
contents: [
{
uri: "config://settings",
mimeType: "application/json",
text: JSON.stringify({ version: "1.0", env: "production" }),
},
],
};
}
});
Схема валидации с Zod
Используйте Zod для строгой типизации аргументов:
const WeatherSchema = z.object({
city: z.string().min(1).max(100),
units: z.enum(["metric", "imperial"]).default("metric"),
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "get_weather") {
// Если аргументы невалидны — Zod выбросит ошибку
const { city, units } = WeatherSchema.parse(request.params.arguments);
// ...
}
});
Публикация сервера
Чтобы другие могли использовать ваш сервер через npx:
# package.json
{
"name": "@myorg/mcp-weather",
"version": "1.0.0",
"bin": {
"mcp-weather": "./dist/index.js"
}
}
npm run build
npm publish --access public
Пользователи подключают его так:
{
"mcpServers": {
"weather": {
"command": "npx",
"args": ["-y", "@myorg/mcp-weather"]
}
}
}
Отладка MCP-сервера
Самый быстрый способ — MCP Inspector:
npx @modelcontextprotocol/inspector node dist/index.js
Откроется веб-интерфейс где можно вызывать инструменты вручную, смотреть запросы и ответы.
Для логирования внутри сервера используйте stderr (не stdout — он занят протоколом):
console.error("Debug:", someValue); // правильно
console.log("Debug:", someValue); // ломает протокол!
Частые ошибки
1. Смешиваете stdout и stderr
MCP работает через stdio. Любой вывод в stdout ломает протокол. Используйте только console.error для логов.
2. Не обрабатываете ошибки
Если инструмент выбрасывает исключение, OpenClaw получит пустой ответ. Оборачивайте в try/catch и возвращайте ошибку в content.
3. Синхронные тяжёлые операции
MCP-сервер однопоточный. Долгие синхронные операции блокируют его. Используйте async/await для всего.
4. Слишком широкий доступ Не давайте MCP-серверу прав больше чем нужно. Если инструмент только читает — используйте read-only подключение к БД.
Итог
MCP-сервер создаётся за пару часов. Ключевые шаги: установить SDK → объявить инструменты → реализовать обработчики → добавить в конфиг OpenClaw. Это открывает возможность подключить любой внутренний сервис компании к AI-агенту без изменений в самом OpenClaw.
Следующий шаг — изучите OpenClaw API для разработчиков и GitHub-интеграцию.