Создайте ваш первый MCP-сервер
Перейти к разделу
Два варианта SDK: TypeScript или Python
У MCP есть официальные SDK на TypeScript и Python. Оба являются полноценными — выбирайте тот, что соответствует вашему стеку. TypeScript SDK использует @modelcontextprotocol/sdk. Python SDK называется просто mcp. Оба SDK предоставляют одинаковые абстракции и возможности.
# TypeScript — scaffold a new server
npx @modelcontextprotocol/create-server my-app-server
cd my-app-server
npm install
npm run build
# Python — scaffold a new server
uvx create-mcp-server
# Follow the prompts: name, description, etc.
cd my-app-server
uv syncСтруктура проекта: TypeScript
Сгенерированный TypeScript-проект минималистичен. Вот что вы получаете и что делает каждый файл.
my-app-server/
package.json # Dependencies and scripts
tsconfig.json # TypeScript config
src/
index.ts # Your MCP server — this is where you write code// package.json
{
"name": "my-app-server",
"version": "1.0.0",
"type": "module",
"bin": {
"my-app-server": "./build/index.js"
},
"scripts": {
"build": "tsc && chmod +x build/index.js",
"dev": "tsc --watch",
"start": "node build/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.0",
"zod": "^3.24.0"
},
"devDependencies": {
"typescript": "^5.8.0",
"@types/node": "^22.0.0"
}
}Минимальный TypeScript-сервер
Создадим максимально простой MCP-сервер — один resource, возвращающий приветствие. Это ваш Hello World.
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create the server
const server = new McpServer({
name: "my-app-server",
version: "1.0.0",
});
// Add a resource — read-only data
server.resource(
"greeting",
"myapp://greeting",
async (uri) => ({
contents: [{
uri: uri.href,
mimeType: "text/plain",
text: "Hello from my first MCP server!"
}]
})
);
// Add a tool — an action the AI can perform
server.tool(
"greet",
"Generate a personalized greeting",
{ name: z.string().describe("The person to greet") },
async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name}! Welcome to MCP.` }]
})
);
// Start the server with stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Server running on stdio");Обратите внимание: для логирования используется console.error, а не console.log. При транспорте stdio stdout зарезервирован для сообщений протокола MCP. Весь вывод для отладки должен идти в stderr.
Структура проекта: Python
my-app-server/
pyproject.toml # Dependencies and project config
src/
my_app_server/
__init__.py
server.py # Your MCP server# pyproject.toml
[project]
name = "my-app-server"
version = "1.0.0"
requires-python = ">=3.10"
dependencies = [
"mcp[cli]>=1.6.0",
]
[project.scripts]
my-app-server = "my_app_server.server:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"Минимальный Python-сервер
# src/my_app_server/server.py
from mcp.server.fastmcp import FastMCP
# Create the server
mcp = FastMCP("my-app-server")
# Add a resource — read-only data
@mcp.resource("myapp://greeting")
def get_greeting() -> str:
"""A simple greeting from the server."""
return "Hello from my first MCP server!"
# Add a tool — an action the AI can perform
@mcp.tool()
def greet(name: str) -> str:
"""Generate a personalized greeting.
Args:
name: The person to greet
"""
return f"Hello, {name}! Welcome to MCP."
def main():
mcp.run(transport="stdio")
if __name__ == "__main__":
main()Python SDK использует декораторы и аннотации типов — это похоже на написание FastAPI-приложения. TypeScript SDK использует цепочку методов со схемами Zod. Оба создают идентичные MCP-серверы.
Локальное тестирование с Claude Code
Самый быстрый способ протестировать ваш сервер — Claude Code. Добавьте сервер в конфигурацию MCP, и Claude Code автоматически подключится к нему.
# Build the TypeScript server first
cd my-app-server
npm run build
# Add to Claude Code (run from your project directory)
claude mcp add my-app -- node /absolute/path/to/my-app-server/build/index.js
# Or for Python
claude mcp add my-app -- uv run --directory /absolute/path/to/my-app-server my-app-serverЭто создаёт файл .mcp.json в директории вашего проекта. Вы также можете редактировать его вручную.
// .mcp.json (project root)
{
"mcpServers": {
"my-app": {
"command": "node",
"args": ["/absolute/path/to/my-app-server/build/index.js"],
"env": {
"DATABASE_URL": "postgresql://localhost:5432/mydb"
}
}
}
}Тестирование с MCP Inspector
MCP Inspector — веб-инструмент для тестирования вашего сервера без полноценного AI-клиента. Он показывает все ваши resources, tools и prompts и позволяет вызывать их в интерактивном режиме.
# Run MCP Inspector (TypeScript server)
npx @modelcontextprotocol/inspector node build/index.js
# Run MCP Inspector (Python server)
npx @modelcontextprotocol/inspector uv run my-app-server
# Opens a web UI at http://localhost:6274
# You can browse resources, call tools, and test promptsНастройка для Cursor
Cursor использует тот же формат конфигурации MCP. Добавьте файл .cursor/mcp.json в ваш проект.
// .cursor/mcp.json
{
"mcpServers": {
"my-app": {
"command": "node",
"args": ["./my-app-server/build/index.js"],
"env": {
"DATABASE_URL": "postgresql://localhost:5432/mydb"
}
}
}
}Добавление переменных окружения
MCP-серверам почти всегда нужны переменные окружения — URL баз данных, API-ключи, значения конфигурации. Вы можете передавать их через конфигурацию MCP или читать в коде вашего сервера.
// TypeScript — reading environment variables
const DATABASE_URL = process.env.DATABASE_URL;
if (!DATABASE_URL) {
console.error("DATABASE_URL is required");
process.exit(1);
}
server.tool(
"query-users",
"Count users in the database",
{},
async () => {
const pool = new Pool({ connectionString: DATABASE_URL });
const result = await pool.query("SELECT COUNT(*) FROM users");
return {
content: [{ type: "text", text: `Total users: ${result.rows[0].count}` }]
};
}
);# Python — reading environment variables
import os
DATABASE_URL = os.environ.get("DATABASE_URL")
if not DATABASE_URL:
raise RuntimeError("DATABASE_URL is required")
@mcp.tool()
def query_users() -> str:
"""Count users in the database."""
import psycopg
with psycopg.connect(DATABASE_URL) as conn:
count = conn.execute("SELECT COUNT(*) FROM users").fetchone()[0]
return f"Total users: {count}"Создайте MCP-сервер (TypeScript или Python), который открывает README вашего проекта как resource: 1. Создайте новый проект с помощью инструмента create-server 2. Добавьте resource с URI myapp://readme, который читает и возвращает файл README.md вашего проекта 3. Добавьте tool с именем summarize-readme, который читает README и возвращает первые 500 символов 4. Соберите сервер и протестируйте его с MCP Inspector 5. Добавьте его в конфигурацию Claude Code и убедитесь, что он там отображается Бонус: добавьте переменную окружения PROJECT_DIR, чтобы сервер знал, где найти README.
Подсказка
В TypeScript используйте fs.readFileSync для чтения файла. В Python используйте pathlib.Path. Не забудьте обработать случай, когда файл не существует — возвращайте понятное сообщение об ошибке вместо аварийного завершения.
- TypeScript SDK: @modelcontextprotocol/sdk со схемами Zod
- Python SDK: mcp с декораторами FastMCP и аннотациями типов
- Транспорт stdio: клиент запускает сервер как дочерний процесс, общение через stdin/stdout
- Используйте console.error (не console.log) для логирования при транспорте stdio
- MCP Inspector позволяет тестировать серверы без AI-клиента
- Настройка серверов в .mcp.json (Claude Code) или .cursor/mcp.json (Cursor)
В следующем уроке мы детально разберём Resources: открытие ваших данных — техника, которая даёт вам явное преимущество. Разблокируйте полный курс и продолжайте прямо сейчас.
2/7 завершено — продолжайте!