Создание и самостоятельный хостинг MCP-сервера на VPS
Создайте MCP-сервер на TypeScript с нуля, разверните его на VPS с systemd и Nginx с TLS. Подключите Claude Desktop, Claude Code и Cursor к собственному серверу.
Каждый туториал по MCP-серверам заканчивается на "запустите локально через stdio." Этот проходит весь путь: сборка MCP-сервера на TypeScript, деплой на VPS за Nginx с TLS, защита аутентификацией и файрволом, подключение MCP-клиентов через интернет.
К концу этого руководства у вас будет продакшен-готовый MCP-сервер, к которому смогут подключаться Claude Desktop, Claude Code, Cursor или любой MCP-совместимый клиент.
Что мы будем строить?
MCP-клиенты отправляют JSON-RPC запросы по HTTPS. Nginx терминирует TLS и проксирует запросы к процессу MCP-сервера на локальном порту. MCP-сервер валидирует bearer-токен, обрабатывает запрос и возвращает результат.
MCP Client (Claude, Cursor, ...)
|
| HTTPS (port 443)
v
Nginx (TLS termination + reverse proxy)
|
| HTTP (port 3000, localhost only)
v
MCP Server (Node.js + Express)
|
v
SQLite database
Рабочий пример --- сервер заметок. Он предоставляет два инструмента (поиск заметок, создание заметки) и один ресурс (список всех заметок). Достаточно простой для понимания, достаточно полезный для расширения.
Какой транспорт использовать для удалённого MCP-сервера?
Streamable HTTP --- текущий стандарт спецификации MCP для удалённых деплоев. Он заменил устаревший SSE-транспорт и работает через обычные HTTP POST запросы. Stdio остаётся транспортом для локальных серверов, где клиент запускает сервер как дочерний процесс.
| Transport | Сценарий использования | Работает удалённо | Поддержка клиентами |
|---|---|---|---|
| stdio | Локальные инструменты, CLI-интеграции | Нет | Все клиенты |
| SSE (устарел) | Старые удалённые серверы | Да | Большинство клиентов (выводится из обращения) |
| Streamable HTTP | Удалённые серверы, продакшен | Да | Claude Desktop, Claude Code, Cursor |
Streamable HTTP отправляет каждое сообщение клиента как HTTP POST на один эндпоинт (например, /mcp). Сервер отвечает application/json для простых ответов или text/event-stream, когда нужно вернуть несколько сообщений потоком. Это значит, что он работает со стандартной HTTP-инфраструктурой, включая reverse proxy и балансировщики нагрузки.
Предварительные требования
- VPS с Debian 12 или Ubuntu 24.04 (в этом руководстве используется Debian 12)
- Доменное имя с DNS-записью, указывающей на ваш VPS (например,
mcp.example.com) - Node.js 20+ на VPS
- Базовое знакомство с TypeScript и Linux
Как настроить проект TypeScript MCP-сервера?
Начните на локальной машине или сразу на VPS. Инициализируйте проект, установите MCP SDK и настройте TypeScript.
mkdir mcp-notes-server && cd mcp-notes-server
npm init -y
npm install @modelcontextprotocol/sdk zod express better-sqlite3
npm install -D typescript @types/node @types/express @types/better-sqlite3
Создайте конфигурацию TypeScript:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
Сохраните как tsconfig.json в корне проекта.
Обновите package.json, добавив скрипты сборки и запуска, и укажите тип модуля:
{
"type": "module",
"scripts": {
"build": "tsc",
"start": "node build/index.js"
}
}
Создайте директорию с исходным кодом:
mkdir src
Как реализовать MCP-инструменты с валидацией входных данных?
MCP-сервер предоставляет AI-клиентам инструменты (tools), ресурсы (resources) и промпты (prompts). Инструменты --- функции, которые AI может вызывать. Ресурсы --- данные только для чтения. Мы реализуем оба варианта.
Создайте src/index.ts с настройкой базы данных и фабричной функцией, которая создаёт новый экземпляр MCP-сервера для каждого запроса. Stateless Streamable HTTP требует новый экземпляр McpServer на каждый запрос, потому что SDK не позволяет переподключить один и тот же сервер к другому транспорту.
import express from "express";
import { randomUUID } from "crypto";
import Database from "better-sqlite3";
import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
// --- Database setup ---
const db = new Database("notes.db");
db.pragma("journal_mode = WAL");
db.exec(`
CREATE TABLE IF NOT EXISTS notes (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
)
`);
// --- MCP Server factory ---
// Each stateless request needs its own McpServer + transport pair.
// The SDK binds a server to a single transport on connect() and
// throws if you try to reuse it. A factory function keeps it clean.
function createServer(): McpServer {
const server = new McpServer({
name: "notes-server",
version: "1.0.0",
});
Теперь зарегистрируйте инструменты внутри фабрики. Каждый инструмент использует Zod для валидации входных данных. SDK автоматически валидирует входные данные до вызова вашего обработчика.
// --- Tools ---
server.tool(
"search_notes",
"Search notes by keyword in title or content",
{
query: z.string().min(1).describe("Search keyword"),
},
async ({ query }) => {
const stmt = db.prepare(
"SELECT id, title, content, created_at FROM notes WHERE title LIKE ? OR content LIKE ? ORDER BY created_at DESC LIMIT 20"
);
const rows = stmt.all(`%${query}%`, `%${query}%`);
return {
content: [
{
type: "text" as const,
text: JSON.stringify(rows, null, 2),
},
],
};
}
);
server.tool(
"create_note",
"Create a new note with a title and content",
{
title: z.string().min(1).max(200).describe("Note title"),
content: z.string().min(1).describe("Note content"),
},
async ({ title, content }) => {
const id = randomUUID();
const stmt = db.prepare(
"INSERT INTO notes (id, title, content) VALUES (?, ?, ?)"
);
stmt.run(id, title, content);
return {
content: [
{
type: "text" as const,
text: `Note created with id: ${id}`,
},
],
};
}
);
Каждый ввод валидируется Zod до попадания в обработчик. Если клиент отправит { query: "" }, SDK автоматически вернёт ошибку валидации. Ручной парсинг не нужен.
Как добавить ресурсы в MCP-сервер?
Ресурсы (resources) дают AI-клиентам доступ к данным только для чтения. Они адресуются по URI. Добавьте ресурс со списком всех заметок, по-прежнему внутри фабрики createServer:
// --- Resources ---
server.resource(
"all-notes",
"notes://all",
{
description: "List all notes in the database",
mimeType: "application/json",
},
async (uri) => {
const rows = db.prepare(
"SELECT id, title, created_at FROM notes ORDER BY created_at DESC LIMIT 100"
).all();
return {
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify(rows, null, 2),
},
],
};
}
);
return server;
}
MCP-клиенты могут обнаружить этот ресурс и прочитать его без вызова инструмента. Claude Desktop показывает ресурсы в меню вложений. Claude Code предоставляет к ним доступ через @-упоминания.
Как защитить MCP-сервер токенной аутентификацией?
Продакшен MCP-серверу нужна аутентификация. Без неё любой, кто обнаружит ваш эндпоинт, сможет вызывать ваши инструменты. Используйте bearer-токен, проверяемый в Express middleware до того, как запросы дойдут до MCP-транспорта.
Сгенерируйте токен:
openssl rand -base64 32
Сохраните результат. Он понадобится и для конфигурации сервера, и для конфигурации клиента.
Добавьте middleware аутентификации и маршруты Express в src/index.ts:
// --- Auth middleware ---
const AUTH_TOKEN = process.env.MCP_AUTH_TOKEN;
if (!AUTH_TOKEN) {
console.error("MCP_AUTH_TOKEN environment variable is required");
process.exit(1);
}
function authenticateRequest(
req: express.Request,
res: express.Response,
next: express.NextFunction
): void {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
res.status(401).json({
jsonrpc: "2.0",
error: { code: -32001, message: "Missing or invalid authorization" },
id: null,
});
return;
}
const token = authHeader.slice(7);
if (token !== AUTH_TOKEN) {
res.status(403).json({
jsonrpc: "2.0",
error: { code: -32001, message: "Invalid token" },
id: null,
});
return;
}
next();
}
// --- Express app ---
const app = express();
app.use(express.json());
app.use("/mcp", authenticateRequest);
Middleware выполняется перед каждым запросом к /mcp. Он проверяет наличие Bearer-токена в заголовке Authorization и отклоняет неавторизованные запросы с JSON-RPC ошибкой.
Как настроить Streamable HTTP транспорт?
Добавьте настройку транспорта и обработчики маршрутов. Каждый запрос создаёт новый McpServer через фабрику и новый экземпляр транспорта:
// --- Streamable HTTP transport ---
app.post("/mcp", async (req, res) => {
const server = createServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
await server.connect(transport);
try {
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error("MCP request error:", error);
if (!res.headersSent) {
res.status(500).json({
jsonrpc: "2.0",
error: { code: -32603, message: "Internal server error" },
id: null,
});
}
}
});
app.get("/mcp", (_req, res) => {
res.status(405).json({
jsonrpc: "2.0",
error: { code: -32000, message: "Method not allowed. Use POST." },
id: null,
});
});
app.delete("/mcp", (_req, res) => {
res.status(405).json({
jsonrpc: "2.0",
error: { code: -32000, message: "Method not allowed." },
id: null,
});
});
// --- Health check ---
app.get("/health", (_req, res) => {
res.json({ status: "ok" });
});
// --- Start ---
const PORT = parseInt(process.env.PORT || "3000", 10);
app.listen(PORT, "127.0.0.1", () => {
console.log(`MCP server listening on http://127.0.0.1:${PORT}/mcp`);
});
sessionIdGenerator: undefined создаёт stateless-сервер. Каждый запрос независим, со своей парой McpServer + транспорт. Это проще в деплое и хорошо работает за reverse proxy без sticky sessions. Подключение к базе данных (db) живёт на уровне модуля и разделяется между запросами, так что данные сохраняются между вызовами.
Сервер слушает на 127.0.0.1, а не 0.0.0.0. Он принимает только локальные подключения. Nginx будет единственным способом добраться до него извне.
Соберите и проверьте локально:
npm run build
Убедитесь, что сборка прошла без ошибок.
Как протестировать MCP-сервер локально?
MCP Inspector --- официальный инструмент для тестирования. Он подключается к серверу и позволяет вызывать инструменты интерактивно.
Запустите сервер в одном терминале:
MCP_AUTH_TOKEN=test-token-local npm start
В другом терминале проверьте через curl:
curl -X POST http://127.0.0.1:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer test-token-local" \
-d '{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": { "name": "test-client", "version": "1.0.0" }
},
"id": 1
}'
Вы должны увидеть SSE-ответ (event: message с последующей строкой data:), содержащий capabilities сервера, включая объекты tools и resources. Это подтверждает, что сервер принимает подключения и отвечает на MCP initialization handshake.
Протестируйте инструмент create_note:
curl -X POST http://127.0.0.1:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer test-token-local" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "create_note",
"arguments": { "title": "Test Note", "content": "Hello from curl" }
},
"id": 2
}'
Ответ должен содержать "Note created with id: ...". Теперь протестируйте поиск:
curl -X POST http://127.0.0.1:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer test-token-local" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "search_notes",
"arguments": { "query": "Test" }
},
"id": 3
}'
В результатах должна быть только что созданная заметка. Проверьте, что запрос без заголовка Authorization возвращает ошибку 401:
curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:3000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{},"id":1}'
Должно вывести 401. Если так, middleware аутентификации работает.
Также можно использовать MCP Inspector для интерактивного тестирования через веб-интерфейс:
npx @modelcontextprotocol/inspector
Inspector откроет браузерный интерфейс, где можно подключиться к серверу, просмотреть инструменты и ресурсы и проверить их работу.
Как развернуть MCP-сервер на VPS?
Перенесите проект на VPS через rsync, scp или запушьте в Git-репозиторий и склонируйте на сервере. Дальнейшие шаги предполагают, что вы залогинены на VPS.
Создайте выделенного пользователя
Никогда не запускайте приложения от root. Создайте сервисного пользователя:
sudo useradd -r -m -s /usr/sbin/nologin mcpserver
Установите Node.js
Если Node.js 20+ ещё не установлен:
curl -fsSL https://deb.nodesource.com/setup_20.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
sudo apt-get install -y nodejs
node --version
npm --version
Первая команда скачивает скрипт установки в файл, чтобы вы могли проверить его перед запуском. Вывод должен показать v20.x.x или выше для Node.js и 10.x.x для npm. MCP SDK требует Node.js 18+, но 20 LTS рекомендуется для продакшена.
Настройте директорию проекта
sudo mkdir -p /opt/mcp-notes-server
sudo cp -r /path/to/your/project/* /opt/mcp-notes-server/
cd /opt/mcp-notes-server
sudo npm ci --omit=dev
sudo npm run build
sudo chown -R mcpserver:mcpserver /opt/mcp-notes-server
Проверьте владельца:
ls -la /opt/mcp-notes-server/
Все файлы должны принадлежать mcpserver:mcpserver.
Создайте файл окружения
Сохраните токен аутентификации в защищённом файле:
sudo mkdir -p /etc/mcp-notes-server
echo "MCP_AUTH_TOKEN=$(openssl rand -base64 32)" | sudo tee /etc/mcp-notes-server/env > /dev/null
sudo chmod 600 /etc/mcp-notes-server/env
sudo chown mcpserver:mcpserver /etc/mcp-notes-server/env
Прочитайте сгенерированный токен и сохраните его в безопасном месте. Он понадобится для настройки клиентов:
sudo cat /etc/mcp-notes-server/env
Создайте systemd-сервис
sudo tee /etc/systemd/system/mcp-notes-server.service > /dev/null << 'EOF'
[Unit]
Description=MCP Notes Server
After=network.target
[Service]
Type=simple
User=mcpserver
Group=mcpserver
WorkingDirectory=/opt/mcp-notes-server
EnvironmentFile=/etc/mcp-notes-server/env
ExecStart=/usr/bin/node build/index.js
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
# Hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/mcp-notes-server
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
EnvironmentFile загружает MCP_AUTH_TOKEN из защищённого файла. Секция hardening ограничивает возможности процесса. NoNewPrivileges предотвращает повышение привилегий. ProtectSystem=strict делает файловую систему read-only, кроме путей в ReadWritePaths. ProtectHome блокирует доступ к домашним директориям пользователей.
Активируйте и запустите сервис:
sudo systemctl enable --now mcp-notes-server
enable заставляет сервис запускаться при загрузке. --now запускает его немедленно.
Проверьте статус:
sudo systemctl status mcp-notes-server
Должно быть active (running). Если сервис упал, проверьте логи:
journalctl -u mcp-notes-server -n 50 --no-pager
Проверьте, что сервер отвечает локально:
TOKEN=$(sudo grep MCP_AUTH_TOKEN /etc/mcp-notes-server/env | cut -d= -f2)
curl -s http://127.0.0.1:3000/health
Должно вернуть {"status":"ok"}.
Как запустить MCP-сервер за Nginx с TLS?
Nginx терминирует TLS и проксирует запросы к MCP-серверу. MCP-сервер не обрабатывает TLS напрямую. Это стандартная схема для продакшен-деплоя Node.js.
Установите Nginx и Certbot
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
Настройте серверный блок Nginx
sudo tee /etc/nginx/sites-available/mcp.example.com.conf > /dev/null << 'NGINX'
server {
listen 80;
listen [::]:80;
server_name mcp.example.com;
# Certbot will add the redirect to HTTPS
location / {
return 444;
}
}
NGINX
Замените mcp.example.com на ваш домен.
Активируйте сайт и получите TLS-сертификат:
sudo ln -s /etc/nginx/sites-available/mcp.example.com.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d mcp.example.com
Certbot модифицирует конфигурацию, добавляя TLS-настройки и редирект с HTTP на HTTPS. Теперь обновите конфигурацию, добавив reverse proxy для MCP-эндпоинта:
sudo tee /etc/nginx/sites-available/mcp.example.com.conf > /dev/null << 'NGINX'
server {
listen 80;
listen [::]:80;
server_name mcp.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name mcp.example.com;
ssl_certificate /etc/letsencrypt/live/mcp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mcp.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Hide Nginx version
server_tokens off;
# MCP endpoint
location /mcp {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# Required for Streamable HTTP / SSE responses
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding off;
# Keep connection open for streaming responses
proxy_set_header Connection "";
# Extended timeouts for long-running tool calls
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# Forward client info
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Pass through Authorization header
proxy_set_header Authorization $http_authorization;
}
# Health check (no auth required)
location /health {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
# Block everything else
location / {
return 444;
}
}
NGINX
Директивы, важные для совместимости с MCP:
proxy_buffering offне даёт Nginx буферизовать SSE-ответы. Без этого потоковые ответы зависают до закрытия соединения.proxy_cache offисключает кеширование динамических ответов.chunked_transfer_encoding offпредотвращает проблемы с chunked-кодировкой в SSE-потоках.Connection ""держит соединение открытым без запуска WebSocket upgrade.proxy_read_timeout 300sпозволяет вызовам инструментов выполняться до 5 минут. Стандартные 60 секунд слишком мало для сложных операций.
Проверьте и перезагрузите:
sudo nginx -t
sudo systemctl reload nginx
Проверьте с локальной машины (не с сервера):
curl -s https://mcp.example.com/health
Должно вернуть {"status":"ok"}. Если так, Nginx корректно проксирует запросы к MCP-серверу через TLS.
Правила файрвола
Заблокируйте прямой доступ к порту 3000 извне. Только Nginx (на localhost) должен обращаться к MCP-серверу:
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Проверьте, что порт 3000 недоступен снаружи:
# From your local machine, this should time out or be refused:
curl -s --connect-timeout 5 http://YOUR_VPS_IP:3000/health
Подключение должно упасть. MCP-сервер доступен только через Nginx на порту 443.
Как подключить MCP-клиенты к удалённому серверу?
Сервер запущен за Nginx с TLS. Настроим MCP-клиенты для подключения.
Claude Code
Добавьте удалённый сервер через CLI:
claude mcp add --transport http \
--header "Authorization: Bearer YOUR_TOKEN_HERE" \
notes-server https://mcp.example.com/mcp
Замените YOUR_TOKEN_HERE на токен из /etc/mcp-notes-server/env.
Проверьте подключение:
claude mcp list
В Claude Code наберите /mcp, чтобы проверить статус сервера. notes-server должен отображаться как подключённый с доступными инструментами search_notes и create_note.
Claude Desktop
Claude Desktop подключается к удалённым MCP-серверам через Settings UI. Перейдите в Settings > Connectors и добавьте новый удалённый сервер с URL https://mcp.example.com/mcp.
Если нужно передать bearer-токен, можно также настроить через локальный файл .mcp.json или команду claude mcp add (показана выше) и импортировать сервер в Claude Desktop:
claude mcp add-from-claude-desktop
Cursor
Откройте настройки Cursor и перейдите к конфигурации MCP. Добавьте новый сервер в файле mcp.json (или через Cursor settings UI):
{
"mcpServers": {
"notes-server": {
"url": "https://mcp.example.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN_HERE"
}
}
}
}
Cursor сначала пробует Streamable HTTP при подключении к URL, поэтому дополнительная настройка транспорта не нужна.
Проверьте сквозное подключение
Из любого подключённого клиента попросите AI:
- Создать заметку: "Create a note titled 'Server Test' with content 'Connected successfully'"
- Найти её: "Search my notes for 'Server Test'"
Если обе операции вернули результаты, вся цепочка работает: клиент -> Nginx -> MCP-сервер -> SQLite и обратно.
Справочник MCP-примитивов
| Примитив | Назначение | Пример |
|---|---|---|
| Tools (инструменты) | Функции, которые AI может вызывать со структурированными входными данными | search_notes, create_note |
| Resources (ресурсы) | Данные только для чтения, на которые AI может ссылаться | notes://all список заметок |
| Prompts (промпты) | Готовые шаблоны для типовых задач | Не используются в этом туториале |
Tools --- самый часто реализуемый примитив. Resources полезны для предоставления справочных данных без вычислений. Prompts позволяют создавать многоразовые шаблоны, которые направляют AI через конкретные рабочие процессы.
Что-то пошло не так?
Сервер не запускается. Проверьте журнал:
journalctl -u mcp-notes-server -n 50 --no-pager
Частые причины: отсутствует переменная окружения MCP_AUTH_TOKEN, неправильные права на notes.db, устаревшая версия Node.js.
Nginx возвращает 502 Bad Gateway. MCP-сервер не запущен или не слушает порт 3000. Проверьте:
sudo systemctl status mcp-notes-server
curl -s http://127.0.0.1:3000/health
Клиент получает 401 Unauthorized. Токен в конфигурации клиента не совпадает с токеном на сервере. Перепроверьте:
sudo cat /etc/mcp-notes-server/env
Потоковые ответы зависают. Увеличьте proxy_read_timeout в конфигурации Nginx. 300 секунд покрывают большинство случаев, но долгим инструментам может понадобиться больше.
Ошибки сертификата. Проверьте валидность сертификата для вашего домена:
sudo certbot certificates
Если сертификат истёк, обновите его:
sudo certbot renew
Ошибка блокировки базы данных. SQLite WAL хорошо справляется с параллельным чтением, но параллельная запись может конфликтовать. Для высоконагруженных серверов рассмотрите переход на PostgreSQL.
Permission denied на notes.db. Пользователь mcpserver должен иметь права на запись в рабочую директорию для SQLite. Проверьте:
ls -la /opt/mcp-notes-server/notes.db
Файл должен принадлежать mcpserver:mcpserver. Если он был создан root при тестировании, исправьте:
sudo chown mcpserver:mcpserver /opt/mcp-notes-server/notes.db
sudo chmod 640 /opt/mcp-notes-server/notes.db
Клиент показывает "connection refused" или "timeout". Проверьте, что DNS A-запись указывает на IP вашего VPS и порты 80/443 открыты в файрволе:
sudo ufw status
dig +short mcp.example.com
Вывод dig должен показать IP-адрес вашего VPS. Если нет, обновите DNS-записи и подождите распространения.
Дальнейшие шаги
MCP-сервер запущен и защищён. Куда двигаться дальше:
- Добавьте больше инструментов для работы с вашими базами данных, API или файловыми системами
- Реализуйте OAuth 2.1 вместо bearer-токенов для многопользовательского доступа
- Добавьте rate limiting в Express или Nginx для защиты от злоупотреблений
- Настройте мониторинг через
journalctl -u mcp-notes-server -fи алертинг - Разверните несколько MCP-серверов за одним Nginx на разных путях
Официальная документация MCP SDK описывает продвинутые паттерны, включая stateful-сессии, отчёты о прогрессе и схемы структурированного вывода.
Copyright 2026 Virtua.Cloud. Vse prava zashchishcheny.
Готовы попробовать?
Разверните свой сервер за секунды. Linux, Windows или FreeBSD.
Смотреть тарифы VPS