Серверы MCP для DevOps: что это такое и два варианта, которые вы можете создать на этой неделе
Серверы MCP для DevOps: предоставляют Claude прямой доступ к Kubernetes, AWS и руководствам по работе. Три типа серверов, сервер MCP Kubernetes с режимом только для чтения и распространённые ошибки.
Источники: What Is an MCP Server and Why Every DevOps Team Should Have One in 2026
MCP, протокол контекста модели, — это открытый стандарт от Anthropic, позволяющий ИИ-моделям структурированно взаимодействовать с внешними системами: инструментами, API, базами данных и сервисами. Без него Claude работает изолированно, и каждый факт приходится вводить вручную. С его помощью Claude может напрямую обращаться к этим системам.
«Сервер» в MCP — это всего лишь небольшая программа. Она предоставляет Claude доступ к инструментам и данным, обрабатывает взаимодействие и работает там, где находятся данные, будь то ноутбук, кластер или сервер. Это не тяжелая инфраструктура, и рабочий сервер MCP может представлять собой скрипт на Python из 50 строк.
Аналогия, помогающая понять
Интеграции с Slack позволяют провести хорошее сравнение. Раньше кто-то вручную публиковал сообщение «Начато развертывание в продакшен». После внедрения CI/CD-пайплайнов оно публикуется автоматически, а Slack становится центром, отслеживающим происходящее. MCP делает то же самое для Claude: раньше человек вручную предоставлял информацию об инфраструктуре, а теперь система поддерживает прямое соединение и может проверять данные самостоятельно.
Почему команды DevOps получают наибольшую выгоду
Большинство примеров использования MCP предназначены для разработчиков приложений. Однако DevOps-инженеры, SRE и инженеры платформ получают ещё большую выгоду, поскольку их основная работа заключается в управлении системами, уже имеющими API: Kubernetes, AWS, Datadog или Grafana, PagerDuty, Terraform Cloud, GitHub, Confluence и Notion. Каждая из этих систем может быть подключена к Claude через MCP. После этого не нужно переключаться между инструментами для сбора информации — достаточно просто задавать вопросы.
Как команды используют серверы MCP
- Вопросы о кластерах. При запросе о том, какие производственные развертывания работают более 30 дней без обновлений, Claude обращается к серверу MCP Kubernetes, который запрашивает информацию у кластера и возвращает ответ. Для этого не требуется использование kubectl и копирование-вставка текста.
- Вопросы о затратах. При запросе о том, какие сервисы AWS показали рост нагрузки на этой неделе по сравнению с прошлой, Claude обращается к серверу AWS и инструменту Cost Explorer, после чего получает ответ в простом языке.
- Контекст инцидентов. При запросе о инцидентах в PagerDuty за последние 24 часа и о том, открыты ли ещё какие-либо из них, система получает актуальную информацию с сервера PagerDuty и дает краткое резюме.
- Поиск руководства. При запросе руководства по проблеме исчерпания пула подключений к базе данных система ищет соответствующий документ в базе знаний через сервер Confluence и возвращает именно его.
Три вида серверов
Команды, которые начинают работу, обычно создают три типа серверов.
Серверы для отслеживания только для чтения
Они соединяют Claude с API мониторинга и инфраструктуры, чтобы он мог описывать текущее состояние системы. Типичными примерами являются сервер состояния кластера Kubernetes, сервер инвентаризации AWS, сервер метрик Datadog и сервер логов CloudWatch. Риск минимален, поскольку Claude может выполнять только операции чтения. Именно с этого следует начинать, чтобы завоевать доверие перед предоставлением прав на запись.
Серверы баз знаний
Эти ссылки связывают Claude с внутренней документацией для поиска и извлечения информации: сервером руководств Confluence или Notion, сервером wiki GitHub, папкой с локальными файлами в формате markdown или хранилищем записей о принятых архитектурных решениях. Риск практически равен нулю, поскольку речь идет о поиске в документах. Они важны, потому что создают впечатление, что Claude действительно хорошо знает компанию.
Серверы действий
Они позволяют Claude выполнять такие задачи, как создание тикетов, запуск пайплайнов или перезапуск сервисов. Примерами являются создание тикетов в Jira, управление инцидентами в PagerDuty, создание pull-запросов в GitHub и уведомления в Slack. Риск находится на уровне от среднего до высокого, поскольку теперь Claude вносит изменения. Перед операциями записи всегда должны пройти процедуры утверждения, так что перезапуск пода никогда не происходит без шага подтверждения.
Создание сервера MCP для Kubernetes
Далее приведен рабочий сервер Kubernetes только для чтения на Python. Сначала установите SDK:
pip install mcp kubernetes --break-system-packages
Затем сохраните сервер как k8s_mcp_server.py:
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
from kubernetes import client, config as k8s_config
# Load kubeconfig
try:
k8s_config.load_incluster_config()
except:
k8s_config.load_kube_config()
v1 = client.CoreV1Api()
apps_v1 = client.AppsV1Api()
# Create MCP server
app = Server("k8s-devops-server")
@app.list_tools()
async def list_tools() -> list[types.Tool]:
"""Tell Claude what tools are available."""
return [
types.Tool(
name="get_pods",
description="Get all pods in a namespace with their status, restarts, and age.",
inputSchema={
"type": "object",
"properties": {
"namespace": {
"type": "string",
"description": "Kubernetes namespace (e.g. production, staging)"
}
},
"required": ["namespace"]
}
),
types.Tool(
name="get_pod_logs",
description="Get recent logs from a pod or deployment.",
inputSchema={
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Pod or deployment name"
},
"namespace": {
"type": "string",
"description": "Kubernetes namespace"
},
"lines": {
"type": "integer",
"description": "Number of log lines to return (default 50)"
}
},
"required": ["name", "namespace"]
}
),
types.Tool(
name="get_deployments",
description="List all deployments with replica counts and status.",
inputSchema={
"type": "object",
"properties": {
"namespace": {
"type": "string",
"description": "Kubernetes namespace"
}
},
"required": ["namespace"]
}
),
types.Tool(
name="get_events",
description="Get recent Kubernetes events. Useful for diagnosing issues.",
inputSchema={
"type": "object",
"properties": {
"namespace": {
"type": "string",
"description": "Kubernetes namespace"
}
},
"required": ["namespace"]
}
),
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
"""Handle tool calls from Claude."""
if name == "get_pods":
namespace = arguments["namespace"]
pods = v1.list_namespaced_pod(namespace=namespace)
lines = [f"Pods in {namespace}:\n"]
for pod in pods.items:
phase = pod.status.phase
restarts = sum(
cs.restart_count
for cs in (pod.status.container_statuses or [])
)
name_str = pod.metadata.name
lines.append(f" {name_str}: {phase} | restarts: {restarts}")
return [types.TextContent(type="text", text="\n".join(lines))]
elif name == "get_pod_logs":
pod_name = arguments["name"]
namespace = arguments["namespace"]
lines = arguments.get("lines", 50)
import subprocess
result = subprocess.run(
f"kubectl logs {pod_name} -n {namespace} --tail={lines}",
shell=True, capture_output=True, text=True
)
output = result.stdout or result.stderr or "No logs found"
return [types.TextContent(type="text", text=output)]
elif name == "get_deployments":
namespace = arguments["namespace"]
deployments = apps_v1.list_namespaced_deployment(namespace=namespace)
lines = [f"Deployments in {namespace}:\n"]
for d in deployments.items:
desired = d.spec.replicas or 0
available = d.status.available_replicas or 0
status = "✅" if desired == available else "⚠️"
lines.append(
f" {status} {d.metadata.name}: {available}/{desired} replicas"
)
return [types.TextContent(type="text", text="\n".join(lines))]
elif name == "get_events":
namespace = arguments["namespace"]
events = v1.list_namespaced_event(namespace=namespace)
# Sort by last timestamp, show recent ones
sorted_events = sorted(
events.items,
key=lambda e: e.last_timestamp or e.event_time,
reverse=True
)[:20]
lines = [f"Recent events in {namespace}:\n"]
for e in sorted_events:
lines.append(
f" [{e.type}] {e.reason}: {e.message[:100]}"
)
return [types.TextContent(type="text", text="\n".join(lines))]
return [types.TextContent(type="text", text=f"Unknown tool: {name}")]
async def main():
async with stdio_server() as (read_stream, write_stream):
await app.run(read_stream, write_stream, app.create_initialization_options())
if __name__ == "__main__":
asyncio.run(main())
Чтобы подключить его к Claude Desktop, отредактируйте файл конфигурации, который находится по одному из следующих путей:
Mac: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
и добавьте запись сервера:
{
"mcpServers": {
"kubernetes": {
"command": "python",
"args": ["/path/to/k8s_mcp_server.py"]
}
}
}
Перезапустите Claude Desktop — и он будет готов. Запрос о том, сколько подов сейчас работает в производстве, заставляет Claude фактически проверить это.
Создание сервера руководств по эксплуатации
Этот инструмент может оказаться ещё более ценным. Он указывает Claude на руководства команды, поэтому во время инцидента Claude находит нужное руководство вместо того, чтобы кто-то вручную искал его в Confluence.
import asyncio
import os
import glob
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
app = Server("runbook-server")
RUNBOOKS_PATH = os.path.expanduser("~/runbooks") # point to your runbooks folder
def load_all_runbooks() -> dict[str, str]:
"""Load all markdown runbooks from the folder."""
runbooks = {}
for filepath in glob.glob(f"{RUNBOOKS_PATH}/**/*.md", recursive=True):
with open(filepath, 'r', errors='replace') as f:
content = f.read()
name = os.path.relpath(filepath, RUNBOOKS_PATH)
runbooks[name] = content
return runbooks
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="search_runbooks",
description=(
"Search your team's runbooks for a specific topic, service, or error. "
"Use this when someone asks about a procedure, a known issue, "
"or how to fix something."
),
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "What to search for (e.g. 'database connection pool', 'nginx 502', 'SSL cert renewal')"
}
},
"required": ["query"]
}
),
types.Tool(
name="list_runbooks",
description="List all available runbooks by category.",
inputSchema={
"type": "object",
"properties": {}
}
),
types.Tool(
name="get_runbook",
description="Get the full content of a specific runbook by name.",
inputSchema={
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Runbook filename (from list_runbooks)"
}
},
"required": ["name"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
runbooks = load_all_runbooks()
if name == "list_runbooks":
names = sorted(runbooks.keys())
output = "Available runbooks:\n" + "\n".join(f" - {n}" for n in names)
return [types.TextContent(type="text", text=output)]
elif name == "search_runbooks":
query = arguments["query"].lower()
matches = []
for rb_name, content in runbooks.items():
if query in content.lower() or query in rb_name.lower():
# Return first 500 chars as preview
preview = content[:500].strip()
matches.append(f"📄 {rb_name}\n{preview}\n...")
if not matches:
return [types.TextContent(
type="text",
text=f"No runbooks found for '{query}'"
)]
return [types.TextContent(
type="text",
text=f"Found {len(matches)} runbook(s):\n\n" + "\n\n---\n\n".join(matches)
)]
elif name == "get_runbook":
rb_name = arguments["name"]
content = runbooks.get(rb_name)
if not content:
return [types.TextContent(type="text", text=f"Runbook not found: {rb_name}")]
return [types.TextContent(type="text", text=content)]
return [types.TextContent(type="text", text=f"Unknown tool: {name}")]
async def main():
async with stdio_server() as (read_stream, write_stream):
await app.run(read_stream, write_stream, app.create_initialization_options())
if __name__ == "__main__":
asyncio.run(main())
Добавьте его в конфигурацию Claude Desktop рядом с другим сервером:
{
"mcpServers": {
"kubernetes": {
"command": "python",
"args": ["/path/to/k8s_mcp_server.py"]
},
"runbooks": {
"command": "python",
"args": ["/path/to/runbook_mcp_server.py"]
}
}
}
После этого запрос вроде «Каков руководство по работе с высоким потреблением памяти в API-подах?» возвращает собственное руководство команды, а не общий результат поиска.
Существующие серверы
Не всё нужно создавать с нуля. Официальный набор серверов-образцов на GitHub в организации modelcontextprotocol включает GitHub (репозитории, pull-запросы, задачи и коммиты), Postgres, Slack, поиск в базе знаний AWS, доступ к локальной файловой системе и поиск в Brave. Существуют также серверы, созданные сообществом, для Datadog (метрики, дашборды и мониторы), PagerDuty (инциденты и уведомления) и Jira (заявки и спринты). Стоит сначала ознакомиться с этим набором, ведь кто-то уже может иметь то, что нужно.
Ошибки, которых следует избегать
- Чрезмерно раннее предоставление прав на запись. Начните с режима только для чтения. После того как будет установлена доверие, постепенно добавляйте инструменты для записи.
- Создание одного огромного сервера. Маленькие, специализированные серверы легче обслуживать, их проще отлаживать, а также их проще передавать конкретным пользователям.
k8s-serverдолжен содержать только инструменты Kubernetes,aws-server— только инструменты AWS, аrunbook-server— только документацию. - Игнорирование логирования. Фиксируйте каждый вызов инструмента, включая то, что запрашивал Claude, когда это происходило и что было получено в ответ, особенно до добавления инструментов для записи.
- Игнорирование проверки записей. Любое действие, которое создает заявку, перезапускает сервис или объединяет pull request, требует этапа подтверждения, при котором Claude сначала показывает, что собирается сделать.
Утро с правильной настройкой
Типичная сессия начинается с запроса о состоянии производства. Claude проверяет сервер Kubernetes и докладывает о развертываниях и подах, выделяет поды с частыми перезагрузками, а затем занимается решением проблемы.
You: What's the state of production right now?
Claude: [checks kubernetes MCP server]
3 deployments all healthy, 47/47 pods running.
One pod in payments namespace has 8 restarts - api-worker-6d4f9b.
Everything else looks stable.
You: Why is that pod restarting?
Claude: [checks pod logs via MCP]
Logs show OOMKilled. Memory limit is 256Mi but it's hitting
248Mi regularly. Looks like it needs a bump to at least 512Mi.
Want me to check if we have a runbook for this?
You: Yes
Claude: [searches runbook MCP server]
Found it - "memory-limit-tuning.md"
The runbook says: increase limit in Helm values,
run helm upgrade, monitor for 10 minutes.
Last updated by Rahul on March 12th.
You: What did our AWS bill look like this week?
Claude: [checks AWS MCP server]
$4,823 this week vs $4,210 last week - up 14.5%.
Spike is from EC2 in us-east-1. Looks like
3 r6g.2xlarge instances were added on Tuesday.
Весь этот процесс занимает примерно 90 секунд, без переключения вкладок, без копирования-вставки и без поиска.
Начиная с этой недели
Создание пяти серверов сразу — ошибка. Выберите один. Для большинства команд DevOps лучшим первым вариантом является сервер с руководствами, поскольку он имеет режим только для чтения, сразу приносит пользу и работает с уже существующими документами. Для запуска требуется три шага:
- Соберите руководства команды в одну папку, например
~/runbooks/. - Возьмите приведенный выше код сервера с руководствами и настройте его на работу с этой папкой.
- Добавьте его в настройки Claude Desktop и перезагрузите программу.
Это позволит создать помощника, который знает процедуры команды. Когда он окажется надежным, добавьте сервер Kubernetes, затем AWS, а потом все остальные.
Более широкая перспектива
MCP ещё молод, и экосистема развивается быстро, но уже сейчас ясно несколько вещей. Команды, использующие серверы MCP, получают совершенно иной опыт работы с ИИ по сравнению с командами, которые ими не пользуются. Чтобы перейти от чат-окна к инструменту, функционирующему как коллега по команде, необходимо наличие сервера, предоставляющего доступ к нужным системам. Команды, создающие такие решения сейчас, задают стандарты для использования ИИ в работе с инфраструктурой. Это не исследовательский проект — команды уже используют их в производственных условиях, причём вышеупомянутый сервер Kubernetes может быть запущен примерно за 20 минут.
Связанные статьи
- Личный сервер MCP, позволяющий Claude искать руководства вашей команды — индексация markdown-руководств и отчетов о расследованиях в ChromaDB, предоставление возможностей семантического поиска через сервер MCP и возможность для Claude находить процедуры реагирования на инциденты за секунды.
- Практические заметки: kagent — нативная для Kubernetes платформа для ИИ-агентов, которую стоит использовать — пошаговое руководство по использованию платформы kagent: контракты, проверки и готовые блоки кода для команд, внедряющих эту архитектуру.