MCP
MCP — открытый стандарт для расширения возможностей AI-агентов внешними инструментами. Creo поддерживает подключение MCP-серверов через два транспорта (stdio и http) с прозрачной интеграцией инструментов в единый tool registry.
Архитектура
graph TB
subgraph "Config"
GlobalMcp["~/.config/creo/mcp.json<br/>глобальный"]
ProjectMcp[".creo/mcp.json<br/>проектный"]
end
subgraph "Registration"
RegisterTool["mcp_register tool<br/>(через диалог с агентом)"]
Manual["Ручное редактирование<br/>mcp.json"]
end
subgraph "Connection"
LoadConfig["LoadConfig<br/>merge global + project"]
ConnectAll["ConnectAll<br/>параллельное подключение"]
Client1["Client #1<br/>stdio process"]
Client2["Client #2<br/>http endpoint"]
end
subgraph "Tool Bridging"
Adapters["ToolAdapter<br/>MCP → tool.Tool"]
Registry["tool.Registry<br/>единый реестр"]
end
GlobalMcp --> LoadConfig
ProjectMcp --> LoadConfig
RegisterTool --> WriteConfig
Manual --> LoadConfig
LoadConfig --> ConnectAll
ConnectAll --> Client1
ConnectAll --> Client2
Client1 --> Adapters
Client2 --> Adapters
Adapters --> Registry
Конфигурация mcp.json
Файл mcp.json содержит карту серверов: имя → конфигурация.
Расположение
| Scope | Путь | Применение |
|---|---|---|
| Глобальный | ~/.config/creo/mcp.json |
Действует во всех проектах |
| Проектный | .creo/mcp.json (в корне проекта) |
Действует только в текущем проекте, можно коммитить в git |
Проектный mcp.json перекрывает глобальный при совпадении имён серверов.
Структура ServerConfig
1type ServerConfig struct {
2 Type string `json:"type"` // "stdio" | "http"
3 Command string `json:"command,omitempty"` // stdio only
4 Args []string `json:"args,omitempty"` // stdio only
5 Env map[string]string `json:"env,omitempty"` // stdio only
6 URL string `json:"url,omitempty"` // http only
7 Headers map[string]string `json:"headers,omitempty"` // http only
8 AutoApprove bool `json:"autoApprove,omitempty"`
9}
Загрузка и слияние
LoadConfig(globalDir, projectDir) читает оба файла и merge'ит:
1result := Config{}
2// 1. Читать глобальный mcp.json → result
3// 2. Читать проектный mcp.json → projectCfg
4// 3. maps.Copy(result, projectCfg) — project перебивает global
Регистрация через диалог
Агент может зарегистрировать MCP-сервер через инструмент mcp_register. Процесс:
- Агент вызывает
mcp_registerс параметрами (name, scope, type, command/url, ...) WriteConfigзаписывает entry в mcp.json (project или global scope)NewClient+Connect— подключение к серверуonRegister(client)— callback регистрирует инструменты в tool registry- System message регенерируется для включения новых инструментов
mcp_register(
name: "filesystem",
scope: "project", // "project" | "global"
type: "stdio", // "stdio" | "http"
command: "npx", // stdio only
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"],
autoApprove: false // опционально
)
Параметры mcp_register
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
name |
string | да | Уникальное имя сервера (ключ в mcp.json) |
scope |
string | да | "global" или "project" |
type |
string | да | "stdio" или "http" |
command |
string | для stdio | Путь к исполняемому файлу |
args |
string[] | нет | Аргументы командной строки (stdio) |
env |
object | нет | Переменные окружения {KEY: value} (stdio) |
url |
string | для http | URL endpoint |
headers |
object | нет | HTTP-заголовки (http) |
autoApprove |
boolean | нет | Пропускать подтверждения для всех инструментов сервера |
Транспорты
stdio
Локальный процесс. Creo запускает команду через exec.Command с args и env:
1cmd := exec.Command(c.cfg.Command, c.cfg.Args...)
2if len(c.cfg.Env) > 0 {
3 cmd.Env = append(os.Environ(), envSlice(c.cfg.Env)...)
4}
5transport := &mcp.CommandTransport{Command: cmd}
Пример — gopls:
1{
2 "gopls": {
3 "type": "stdio",
4 "command": "gopls",
5 "args": ["mcp"]
6 }
7}
http / sse
Удалённый endpoint. HTTP-клиент с кастомными заголовками для авторизации:
1transport := &mcp.StreamableClientTransport{
2 Endpoint: c.cfg.URL,
3 DisableStandaloneSSE: true,
4 HTTPClient: newHTTPClientWithHeaders(c.cfg.Headers),
5}
HTTP-клиент имеет 30s timeout и инжектит заголовки через headerTransport:
1type headerTransport struct {
2 base http.RoundTripper
3 headers map[string]string
4}
Пример — Context7:
1{
2 "context7": {
3 "type": "http",
4 "url": "https://mcp.context7.com/mcp"
5 }
6}
Пример с авторизацией:
1{
2 "gitverse": {
3 "type": "http",
4 "url": "https://mcp.gitverse.ru",
5 "headers": {"Authorization": "Bearer <token>"}
6 }
7}
Подключение
ConnectAll — параллельное подключение
ConnectAll подключается ко всем серверам из конфига конкурентно — каждый сервер
в своей горутине. Медленные серверы (например, gopls индексирующий большой workspace)
не блокируют остальные.
1func ConnectAll(ctx context.Context, cfg Config) []*Client {
2 var wg sync.WaitGroup
3 for name, serverCfg := range cfg {
4 wg.Go(func() {
5 c := NewClient(name, serverCfg)
6 if err := c.Connect(ctx); err != nil {
7 log.Printf("mcp: failed to connect to server %q: %v", name, err)
8 return
9 }
10 results[idx] = result{client: c, ok: true}
11 })
12 }
13 wg.Wait()
14 return connectedClients
15}
Асинхронная загрузка — MCP-серверы подключаются в фоне, не блокируют запуск TUI. Инструменты появляются по мере подключения.
Client lifecycle
1type Client struct {
2 name string
3 cfg ServerConfig
4 session *mcp.ClientSession
5 tools []*mcp.Tool
6}
| Метод | Описание |
|---|---|
NewClient(name, cfg) |
Создание клиента |
Connect(ctx) |
Подключение + ListTools — обнаружение инструментов |
CallTool(ctx, name, args) |
Вызов инструмента на сервере |
Close() |
Закрытие соединения |
Tools() |
Список обнаруженных инструментов |
Name() |
Имя сервера |
Tool Bridging — интеграция в Registry
MCP-инструменты адаптируются к интерфейсу tool.Tool через ToolAdapter:
1type ToolAdapter struct {
2 serverName string
3 mcpTool *mcp.Tool
4 client *Client
5 autoApprove bool
6}
ToolAdapter
| Метод | Поведение |
|---|---|
Definition() |
LLM tool definition: name, description, inputSchema → model.Definition |
Summary() |
MCP-описание инструмента для system prompt |
Name() |
Имя инструмента (без префикса сервера) |
ServerName() |
Имя MCP-сервера |
IsAutoApproved() |
true если autoApprove: true в конфиге |
Execute(ctx, args) |
client.CallTool → конвертация результата в tool.Result |
Annotations() |
MCP-аннотации (ReadOnly, Destructive, Idempotent, OpenWorld) |
FormatCall(args) |
Форматирование для отображения |
FormatTitle(args) |
Заголовок для мини-режима |
Конвертация результатов
convertMCPContent преобразует MCP CallToolResult в model.Contents:
mcp.TextContent→model.TextContent- Пустой результат →
"(no output)"
convertInputSchema преобразует MCP inputSchema в model.Definition через
JSON marshal/unmarshal round-trip.
Регистрация в Registry
AdaptersFor(clients) создаёт ToolAdapter для каждого инструмента каждого клиента:
1func AdaptersFor(clients []*Client) []*ToolAdapter {
2 var adapters []*ToolAdapter
3 for _, c := range clients {
4 for _, mt := range c.Tools() {
5 adapters = append(adapters, NewToolAdapter(c.Name(), mt, c))
6 }
7 }
8 return adapters
9}
Адаптеры регистрируются в едином tool.Registry наравне со встроенными инструментами.
System prompt автоматически включает MCP-инструменты после подключения.
Коллизии имён
Имя MCP-инструмента — mcpTool.Name (без префикса сервера). Коллизии между инструментами
из разных серверов или со встроенными инструментами — ответственность пользователя.
Last-registered wins через registry.
autoApprove
Флаг autoApprove: true в конфиге сервера полностью отключает подтверждения
для всех инструментов этого сервера:
1func (t *ToolAdapter) IsAutoApproved() bool {
2 return t.autoApprove // из client.cfg.AutoApprove
3}
Инструменты такого сервера никогда не запрашивают подтверждение, независимо от YOLO-режима и области сессии.
Файлы
| Файл | Назначение |
|---|---|
internal/mcp/mcp.go |
Package doc |
internal/mcp/config.go |
ServerConfig, Config, LoadConfig, WriteConfig, ConfigPath |
internal/mcp/client.go |
Client, Connect, ConnectAll, CloseAll, CallTool |
internal/mcp/register.go |
RegisterTool — инструмент mcp_register для агента |
internal/mcp/tools.go |
ToolAdapter, AdaptersFor, convertMCPContent, convertInputSchema |