Skip to main content
Glama
README.md
# SEAF MCP Server

MCP-сервер для доступа к SEAF ArchTool REST API через протокол Model Context Protocol.

## Быстрый старт

```bash
docker build -t seaf-mcp:latest .
```

Подробная инструкция по установке, запуску локального портала SEAF и подключению MCP-клиентов (OpenCode, Claude Desktop, Cursor): **[INSTALL.md](INSTALL.md)**.

## Доступные tools

**Высокоуровневые (рекомендуются агенту):**
| Tool | Назначение |
|---|---|
| `list_sections` | Разделы `seaf.company.*` с числом объектов и описанием |
| `list_entities` | Объекты раздела с фильтрами (eq/ne/contains/missing/exists); фильтрация и пагинация на портале |
| `describe_section` | Атрибуты раздела (с частотой), поля-связи (исходящие/входящие), примеры ID |
| `inspect_sections` | 1-5 разделов за вызов: структура + до 3 образцов на раздел (образцы ограничены) |
| `find_entities` | Поиск по подстроке в ID/title/description |
| `get_entity` | Объект по ID |
| `get_relations` | Связи объекта: outgoing и incoming, сгруппированные по полю |
| `metamodel_guide` | Справочник метамодели (также MCP-ресурс `seaf://metamodel-guide`) |
| `get_dataset` | Именованный датасет `seaf.company.ds.*` целиком (например `seaf.company.ds.people`) |
| `query_dataset` | JSONata поверх датасета: `origin` — ID или `{алиас: id}`, `source` — выражение (включая служебные `seaf.ds.*`) |
| `describe_query_result` | Описание результата JSONata-запроса: тип, count, поля с покрытием по выборке 100, до 3 примеров |
| `seaf_jsonata_query` | Произвольный JSONata (POST) |
| `find_jsonata_examples` | Проверенные примеры JSONata: keyword-поиск по вопросу/группе/разделам |
| `find_relation_paths` | Многошаговые пути связей между разделами (BFS по карте `$rels`, до 10 маршрутов ≤5 шагов) |

Плюс raw: `get_health`, `get_seaf_core_api_core_storage_problems`, `get_seaf_core_api_core_storage_versions`. Остальные endpoints каталога помечены `"hidden": true`.

**Какой tool выбрать (пересекающиеся пары):**
- `describe_section` — один раздел без образцов; `inspect_sections` — 1-5 разделов с образцами (перед запросом со связями).
- `seaf_jsonata_query` — JSONata по манифесту модели; `query_dataset` — JSONata по датасету (другой корневой `$`).
- `find_entities` — примерное название → ID; `list_entities` — точные фильтры по полям в известном разделе.

Каждое описание tool построено как «Когда вызывать → что вернёт → Дальше».

Связи берутся из формальной схемы: при первом вызове сервер читает `$manifestschema()` и строит карту `поле → раздел` по `$ref: #/$rels/...` (с учётом `allOf`/`$defs`), затем кэширует её. Пользовательский ввод подставляется в JSONata только как JSON-литерал.

Справочник метамодели — `src/seaf_mcp/data/metamodel_guide.md`.
Cookbook проверенных запросов — `src/seaf_mcp/data/jsonata_cookbook.json` (интеграционный тест выполняет
каждую нешаблонную запись на портале, так что примеры не устаревают); пополняется по JSONL-журналу
(`SEAF_JSONATA_LOG_FILE` + `SEAF_JSONATA_LOG_INCLUDE_QUERY=true`).

### Исключенные endpoints
- **PUT/DELETE/PATCH** и **POST без `readOnly=true`** — записывающие операции
- **Wildcard paths** (`/:hash/*`)
- **SSE endpoints** (`/chat/message`)

### Правила
- Схемы аргументов строгие (`additionalProperties: false`): неизвестные/недостающие параметры возвращаются агенту как ошибка.
- Path-параметры URL-кодируются полностью (`/` → `%2F`), т.е. JSONata-запрос можно передавать как есть.
- При старте сервер падает, если в каталоге есть дублирующиеся имена tools или один параметр объявлен в нескольких местах (path/query/body).

## Формат ответов

```
Request:
GET /seaf-core/api/core/storage/versions

Status:
200

Response:
{
  "archHash": "...",
  "coreVersion": "2026.31.0",
  "enableChecker": true
}
```

Длинные ответы обрезаются по `MAX_RESPONSE_CHARS` (лимит — на весь текст ответа, включая
заголовки и примечание): `[truncated: showing N of M chars — <подсказка>]`. Для массивов
примечание сообщает исходное число элементов и советует фильтр/проекцию или индексный
фильтр `[[a..b]]`; для объектов — число ключей и первые из них.

Пустой успешный результат (undefined, `null`, `[]`, `{}`) помечается явно:
`The request succeeded, but the result is empty (...)` — `false` и `0` пустыми не считаются.

Ошибки API:
```
API request failed

Tool: get_seaf_core_api_core_storage_versions
Status: 404
Message:
Not found
```

Сетевые ошибки (нет соединения, таймаут) возвращаются как `Tool execution failed` с подсказкой.

Preflight: известные дорогие/ошибочные конструкции отклоняются до запроса к порталу
(голый `$`, `~> $count($)`, позиционный диапазон `x[a..b]`, slice `x[a:b]`,
`$lookup("строка", …)`, `$take`/`$slice`/`$group`/`$values` без объявления).
Строки, комментарии, backtick-имена и regex-литералы при проверке игнорируются;
индексный фильтр `x[[a..b]]` разрешён.

Ошибки JSONata (коды S0xxx/T0xxx) сопровождаются подсказкой по исправлению — только в tools,
где выражение пишет агент (`seaf_jsonata_query`, `query_dataset`): неподдерживаемый синтаксис
(позиционный диапазон, slice, `~`, `?.`), отсутствующие функции (`$take`/`$slice`/`$group`/`$values`
с полифиллами), баг `$` в цепочке `~>` и т.д. Стек и дамп контекста из тела ошибки срезаются.

## Структура проекта

```
seaf-mcp/
├── src/seaf_mcp/
│   ├── __main__.py            # python -m seaf_mcp
│   ├── server.py              # MCP сервер
│   ├── config.py              # Конфигурация из env vars
│   ├── endpoint_catalog.py    # Загрузка endpoints.json
│   ├── tool_factory.py        # Фильтрация, генерация tools, валидация аргументов
│   ├── seaf_tools.py          # Высокоуровневые tools (разделы, поиск, связи)
│   ├── rest_client.py         # Async HTTP клиент
│   ├── response_formatter.py  # Форматирование и обрезка ответов
│   ├── data/
│   │   └── metamodel_guide.md # Справочник метамодели SEAF
│   └── errors.py
├── tests/
├── endpoints.json
├── requirements.txt / requirements-dev.txt
└── Dockerfile
```

## Особенности SEAF API (проверено на 2026.31.0)
- `GET .../jsonata/:query` с произвольным выражением → **403 Access denied**; используйте `seaf_jsonata_query` (POST).
- Запрос без внешних скобок SEAF трактует как ID предопределённого запроса (`Cannot read properties of undefined`). `seaf_jsonata_query` оборачивает выражение в `(...)` автоматически (поле каталога `jsonataParams`).
- POST не поддерживает `params` — подставляйте значения прямо в выражение.
- Весь манифест (`$`) ≈ 4 MB — будет обрезан по `MAX_RESPONSE_CHARS`.
- Датасеты (`get_dataset`): POST `release-data-profile`, только `seaf.company.ds.*` (служебные `seaf.ds.*` — десятки МБ). Неизвестный ID → HTTP 200 с `null`.

Интеграционные тесты:
- локальный портал (example manifest): `SEAF_IT_URL=http://localhost:8080 .venv/bin/pytest tests/test_integration_portal.py`
- любой развёрнутый портал: `SEAF_CORP_IT_URL=https://seaf-portal.example.com .venv/bin/pytest tests/test_integration_corp.py`

## Дальше

- Авторизация при обращении к API
- Импорт каталога endpoints из OpenAPI, если спецификация появится

## Разработка

```bash
python3 -m venv .venv
.venv/bin/pip install -r requirements-dev.txt
.venv/bin/pytest

# Локальный запуск
API_BASE_URL=https://seaf-portal.example.com ENDPOINT_CATALOG_PATH=endpoints.json \
  PYTHONPATH=src .venv/bin/python -m seaf_mcp
```

TDQS

A3.9/5.0

Scored across 17 tools

Disambiguation4/5

The discovery cluster (list_sections / describe_section / inspect_sections) and the lookup cluster (get_entity / find_entities / list_entities) overlap conceptually, but the descriptions explicitly state when to prefer each and cross-reference alternatives. The JSONata family (query_dataset / seaf_jsonata_query / describe_query_result / find_jsonata_examples) is likewise differentiated by scope (dataset vs manifest vs result-shape vs examples). A few boundaries remain soft, but an agent can pick correctly.

Naming Consistency3/5

Most tools follow a clean verb_noun snake_case pattern (list_sections, get_entity, find_entities, describe_query_result). However, two tools are auto-derived raw endpoint names (get_seaf_core_api_core_storage_versions, get_seaf_core_api_core_storage_problems) that break the convention badly, and seaf_jsonata_query adds an inconsistent domain prefix.

Tool Count4/5

17 tools is on the heavier side but each maps to a distinct step in the discovery→inspect→query workflow, so they mostly earn their place. It could be trimmed (e.g. merging versions/problems into a diagnostics tool), but it is not bloated for the domain.

Completeness4/5

The surface covers health, section/structure discovery, entity lookup and relation traversal, metamodel guidance, JSONata examples, dataset access, query execution and result introspection — a full read-oriented lifecycle. Gaps are minor: no explicit dataset enumeration, and no write operations if mutation is in scope, but the read/query workflow has no dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues