Comet Opik

официальный

Запрашивайте и анализируйте ваши логи Opik, трейсы, промпты и все остальные телеметрические данные от ваших LLM на естественном языке.

Что можно делать с Comet Opik MCP?

  • Запросы трассировок и проектов — Спросите «перечисли мои проекты Opik» или «какие трассировки в проекте 'demo' завершились с ошибкой сегодня?», чтобы просмотреть данные рабочего пространства с помощью list и read.

  • Оценка и комментирование трассировок — Прикрепляйте числовые оценки обратной связи, например, с указанием причины, или добавляйте текстовые комментарии к трассировкам, спанам или потокам.

  • Управление версиями промптов — Сохраняйте новые версии промптов, создавая промпты по имени, если они еще не существуют.

  • Проверка схем записи — Используйте schema, чтобы проверить точные JSON-структуры, обязательные поля и проверенные примеры перед выполнением любой операции записи.

  • Мониторинг состояния проекта — Получайте обзоры за 7 дней с количеством трассировок, частотой ошибок, длительностью и стоимостью, или стройте графики метрик во времени.

  • Разбор проблем Diagnostics — Перечисляйте открытые проблемы агентских инсайтов, решайте или закрывайте их, а также включайте или запускайте сканирование.

Документация

Opik MCP Server

Официальный сервер Model Context Protocol (MCP) для Opik, платформы наблюдения и оценки LLM с открытым исходным кодом, созданной Comet. Подключите свой AI-хост (Claude Code, Cursor, VS Code Copilot, Codex, opencode или любой MCP-клиент) напрямую к вашему рабочему пространству Opik: читайте трейсы, записывайте оценки и сохраняйте версии промптов — всё прямо из чата.

Создан для LLM-инженеров, которые уже используют Opik и хотят управлять им из того же AI-ассистента, с которым они пишут код.

Миграция со старого npx opik-mcp? TypeScript-сервер устарел и будет отключён 2026-11-15. Замените npx -y opik-mcp на uvx opik-mcp@latest в конфигурации вашего MCP-клиента. Полное руководство: legacy/typescript/MIGRATION.md.

You:    "Which traces in project 'demo' failed today?"
Claude: → list(entity_type="trace", project_name="demo") → "Three traces failed…"

You:    "Score trace 7f2e… 0.9 on helpfulness with reason 'great recovery'."
Claude: → write(score.create) → done

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

Одна команда регистрирует сервер в AI-клиентах на вашей машине, устанавливает набор навыков Opik и проверяет подключение. Требуется uv и не требуется Opik SDK:

uvx opik mcp configure

Команда обнаруживает Claude Code, Cursor, VS Code Copilot, Codex и opencode и использует хостируемый сервер на Opik Cloud (вход через браузер, без хранения API-ключей) или этот локальный сервер в других случаях. Любой другой MCP-клиент может использовать хостируемый URL напрямую:

npx add-mcp https://www.comet.com/opik/api/v1/mcp --name opik-mcp

Add to Cursor Install in VS Code

Руководство по настройке, устранение неполадок и FAQ: comet.com/docs/opik/mcp-server. Остальная часть этого README описывает локальный сервер, который команда выше настраивает для самостоятельно размещённого Opik и Opik с открытым исходным кодом, и который вы также можете настроить вручную.


Ручная установка

opik-mcp — это Python-пакет (требуется Python 3.13+). Рекомендуемый способ запуска — uvx, который загружает и запускает последнюю опубликованную версию по требованию — без глобальной установки и управления виртуальными окружениями.

Установите uv один раз:

curl -LsSf https://astral.sh/uv/install.sh | sh   # macOS / Linux
# or: brew install uv

Вам понадобятся две вещи из вашего рабочего пространства Opik:

  • OPIK_API_KEY — получите его на comet.com/api/my/settings/.
  • OPIK_WORKSPACE — имя вашего рабочего пространства (в нижнем регистре, как оно указано в URL). Например, https://www.comet.com/acme-ai/... → OPIK_WORKSPACE=acme-ai. COMET_WORKSPACE принимается как устаревший псевдоним.

Облако, с API-ключом: укажите его, если рабочее пространство по умолчанию вашей учётной записи не то, что вам нужно. Если не указано, сервер отправляет default, который Comet сопоставляет с рабочим пространством по умолчанию вашей учётной записи. Это работает, но если вы на самом деле работаете в именованном рабочем пространстве, вас перенаправят в другое без каких-либо уведомлений — ваши чтения вернутся из неправильного места, а не завершатся ошибкой.

Облако, через OAuth: оставьте поле пустым. Рабочее пространство берётся из токена, который вы авторизовали, и сервер полностью игнорирует этот параметр.

Локально / с открытым исходным кодом: оставьте поле пустым. Opik с открытым исходным кодом имеет одно рабочее пространство с именем default и не позволяет создавать другие, что и даёт запасной вариант.

Самостоятельно размещённый Comet: укажите его. В отличие от версии с открытым исходным кодом, в таких развёртываниях есть настоящие именованные рабочие пространства, и применяется тот же риск тихого переключения на неправильное рабочее пространство.

Что бы ни применялось, убедитесь, что значение действительно подставлено. В сниппетах в интернете встречаются заполнители вроде <your-workspace> или ${input:OPIK_WORKSPACE}; вставленные как есть, они не являются именами рабочих пространств. Теперь сервер отклоняет их напрямую, а не позволяет бэкенду ответить ошибкой аутентификации, которая ничего не объясняет.

Claude Code

Добавьте сервер одной командой:

claude mcp add --transport stdio opik-mcp \
  --env OPIK_API_KEY=<your-key> \
  --env OPIK_WORKSPACE=<your-workspace> \
  -- uvx opik-mcp

Или отредактируйте ~/.claude.json напрямую:

{
  "mcpServers": {
    "opik-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": ["opik-mcp"],
      "env": {
        "OPIK_API_KEY": "<your-key>",
        "OPIK_WORKSPACE": "<your-workspace>"
      }
    }
  }
}

Перезапустите Claude Code. Проверьте с помощью /mcp — opik-mcp должен отобразиться как подключённый. Затем в чате спросите: «список моих проектов Opik» — Claude вызовет инструмент list и вы увидите проекты вашего рабочего пространства.

Cursor

Отредактируйте ~/.cursor/mcp.json (глобально) или .cursor/mcp.json (для проекта) или откройте Cmd+Shift+J → Features → Model Context Protocol:

{
  "mcpServers": {
    "opik-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": ["opik-mcp"],
      "env": {
        "OPIK_API_KEY": "<your-key>",
        "OPIK_WORKSPACE": "<your-workspace>"
      }
    }
  }
}

Перезагрузите Cursor; зелёная точка рядом с opik-mcp на панели MCP подтверждает подключение. Спросите в чате: «список моих проектов Opik».

Таймаут Cursor 60 секунд. Cursor применяет жёсткий таймаут вызова инструмента, который не сбрасывается при уведомлениях о прогрессе. См. Известные ограничения хостов.

VS Code Copilot

.vscode/mcp.json в вашем рабочем пространстве (или в JSON настроек пользователя):

{
  "servers": {
    "opik-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": ["opik-mcp"],
      "env": {
        "OPIK_API_KEY": "<your-key>",
        "OPIK_WORKSPACE": "<your-workspace>"
      }
    }
  }
}

Перезагрузите окно; индикатор MCP в Copilot Chat покажет opik-mcp, когда сервер станет доступен. Спросите в чате: «список моих проектов Opik».

MCP Inspector (ручное тестирование)

OPIK_API_KEY=<your-key> OPIK_WORKSPACE=<your-workspace> \
  npx @modelcontextprotocol/inspector uvx opik-mcp

Самостоятельно размещённый Opik

Добавьте COMET_URL_OVERRIDE (и OPIK_URL, если Opik находится по нестандартному пути) в тот же блок env в конфигурации вашего хоста:

{
  "mcpServers": {
    "opik-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": ["opik-mcp"],
      "env": {
        "OPIK_API_KEY": "<your-key>",
        "OPIK_WORKSPACE": "<your-workspace>",
        "COMET_URL_OVERRIDE": "https://opik.your-company.com",
        "OPIK_MCP_ANALYTICS_SOURCE": ""
      }
    }
  }
}

Опустите OPIK_WORKSPACE в развёртывании с открытым исходным кодом, где default — единственное рабочее пространство; сохраните его в самостоятельно размещённом Comet, где есть настоящие именованные.

Установка OPIK_MCP_ANALYTICS_SOURCE="" исключает вашу установку из облачной метки источника Comet в событиях телеметрии.


Инструменты

opik-mcp предоставляет небольшой, ориентированный на результат набор инструментов, покрывающий весь жизненный цикл (чтение → аннотирование → курирование → создание → итерация).

ИнструментНазначение
readУниверсальное чтение по id / имени / URI opik://
listУниверсальный список с необязательным фильтром по имени + пагинация
writeУниверсальная запись — логирование трейсов/спанов, оценка, комментарии, сохранение промптов, управление датасетами и экспериментами
schemaПросмотр схем операций записи (используется LLM для создания корректных полезных нагрузок)
read_skillЧтение одного из навыков агента Opik, входящих в этот сервер

read

Один инструмент для любых вопросов вида «покажи мне X». Принимает entity_type плюс id (UUID или, для типов с именами, имя) или полный URI opik://. Составные чтения (trace, prompt, thread, agent_insights_issue) встраивают свои дочерние элементы, так что один вызов возвращает полную картину.

Запись, которую вы называете, возвращается целиком. Встроенные дочерние элементы — нет: их тела извлекаются с помощью truncate=true бэкенда, поэтому поле размером более ~10 КБ обрезается в ClickHouse, а изображения base64 заменяются на "[image]" — одно вложение, продублированное на 200 спанов, иначе стоило бы больше, чем всё остальное в чтении. Ответ сообщает об этом в spanBodies / messageBodies, и любой дочерний элемент снова становится целым через собственный read("span", id) или read("trace", trace_id), которые обращаются к конечным точкам без параметра truncate вообще.

Встроенная коллекция также ограничена по длине: 200 спанов, 200 витков, 100 версий промптов. После этого spansTruncated / messagesTruncated / versionsTruncated становится true, а рядом с ним строка moreSpans / moreMessages / moreVersions содержит количество и точный вызов list(...), который продолжает с того места, где остановилась встроенная часть.

Поддерживаемые сущности: project, trace, span, dataset, dataset_item, experiment, prompt, thread, agent_insights_issue. Поиск по имени доступен для project, experiment, prompt, dataset (медленнее — два API- вызова — и может вернуть несколько совпадений). thread и agent_insights_issue привязаны к проекту: передайте project_id или project_name, или ссылку/URI, который содержит проект. dataset и dataset_item ранее назывались test_suite и test_suite_item; старые имена по-прежнему распознаются, но не рекламируются, и новый код должен использовать новые.

read(entity_type="trace", id="7f2e3c8a-…")
read(entity_type="project", id="demo")  # name lookup
read(entity_type="trace", id="opik://traces/7f2e3c8a-…")
read(entity_type="agent_insights_issue", id="<issue-uuid>", project_id="<project-uuid>")
read(
    entity_type="agent_insights_issue",
    id="https://www.comet.com/opik/<ws>/projects/<pid>/diagnostics?issue=<id>",
)

Ссылка, скопированная из интерфейса Opik, работает как id: ссылка на ветку или ссылка на страницу диагностики содержит проект, поэтому project_id не требуется, а тип сущности берётся из ссылки.

Чтение project отвечает на вопрос «как дела у моего проекта» одним вызовом. Оно возвращает {project, summary, vocabulary, contains, url}: запись, затем четыре показателя, которые страница Logs отображает в виде карточек (количество трейсов, доля ошибок, средняя длительность, общая стоимость) за последние 7 дней по сравнению с предыдущими 7, только SDK-трафик, как на экране. since / until перемещают это окно; since="30d" — то, что открывает интерфейс. Доля или среднее за период без трейсов возвращается как null, потому что 0% ошибок за неделю без трафика выглядит как здоровая неделя.

vocabulary — это карта, которая нужна, прежде чем задавать любые другие вопросы: имена оценок обратной связи проекта, его ключи использования токенов и правила автоматизации, оценивающие его трейсы. Это имена, которые используются в фильтре или в series= ниже, и их угадывание возвращает пустую страницу, которая выглядит как хорошие новости. Имена оценок и правила ограничены, всегда сообщают истинное общее количество и называют вызов, который возвращает остальное; ключи использования перечислены полностью, поскольку ничто другое их не перечисляет. contains называет самый свежий эксперимент, датасет, версию промпта и запуск оптимизации, так что вопрос «что здесь происходило» не требует ещё четырёх вызовов. Часть, которую не удалось загрузить, сообщает об этом, а не выглядит пустой, а пустая часть опускается.

Чтение agent_insights_issue возвращает {issue, example_trace_ids, details}: запись проблемы диагностики (имя, описание, причина, предлагаемое исправление, серьёзность, статус), дедуплицированные id трейсов, в которых она проявляется (та же выборка, которую показывает страница Diagnostics — откройте одну с помощью read("trace", id)), и разбивку по дням. Тела трейсов не встраиваются, поэтому чтение остаётся одним вызовом бэкенда. since / until сужают строки по дням; по умолчанию — за всё время. Когда сервер знает URL Opik и рабочее пространство сеанса, чтение также содержит url (страницу Diagnostics проблемы) и trace_url_template (глубокую ссылку для любого из примеров трейсов), чтобы ассистент мог передать вам что-то кликабельное; в OAuth-сеансе, где рабочее пространство не удалось определить, ссылки опускаются, а не угадываются.

Сами трейсы не содержат URL — ссылку для трейса нельзя вывести из полей, возвращаемых read или list, а угаданная форма даёт 404. В инструкциях сеанса вместо этого указан шаблон для неё, .../v1/session/redirect/projects/?trace_id={trace_id}&path=..., чтобы ассистент заполнил id и передал вам ссылку. Она проходит через редирект opik-backend, который определяет проект и рабочее пространство по трейсу, поэтому она работает там, где прямой URL проекта не может, включая OAuth-сеанс с неопределённым рабочим пространством. Это та же ссылка, которую Python SDK выводит для трейса.

list

Просмотр или поиск коллекции с пагинацией. Типы, привязанные к проекту (trace, span, thread, agent_insights_issue, dataset_item, prompt_version), требуют родителя: UUID или имя проекта, UUID датасета или UUID промпта.

list(entity_type="experiment", page=1, size=25)
list(entity_type="experiment", name="rerank")  # name substring filter
list(entity_type="agent_insights_issue", project_name="demo")  # open Diagnostics issues
list(entity_type="agent_insights_issue", project_id="<uuid>", status="resolved")
list(entity_type="trace", project_name="demo")  # latest traces of one project
list(
    entity_type="trace", project_name="demo", filters="error_info is_not_empty AND duration > 5000"
)
list(
    entity_type="span",
    project_name="demo",  # spans across the whole project
    filters='type = "llm" AND usage.total_tokens > 10000',
)
list(
    entity_type="thread",
    project_name="demo",
    filters="number_of_messages > 20 AND feedback_scores.helpfulness < 0.5",
)
list(entity_type="experiment", filters='dataset_id = "<dataset-uuid>" AND tags contains "baseline"')

Фильтры. trace, span, thread, experiment и dataset_item принимают строку OQL, ту же грамматику, что и search_traces(filter_string=…) в SDK:

<field>[.<key>] <op> <value> [AND ...]
ops: = != > >= < <= contains not_contains starts_with ends_with is_empty is_not_empty in not_in

Строки заключаются в двойные кавычки, числа указываются без кавычек, duration — в миллисекундах, даты — это моменты ISO-8601 с часовым поясом ("2026-09-08T10:00:00Z"). Оценки и словари принимают ключ: feedback_scores.accuracy < 0.5, metadata.environment = "prod". AND — единственный соединитель.

Как и на странице Logs в интерфейсе, списки трейсов, спанов и веток добавляют source = "sdk", чтобы трейсы оценщика, песочницы и экспериментов не мешали; назовите source сами, чтобы увидеть их. Первая строка вывода повторяет применённый фильтр. Плохой фильтр не доходит до бэкенда с тем, что нужно для его исправления: позицией синтаксической ошибки, ближайшим именем поля, допустимыми операторами для типа поля или ожидаемым форматом значения. Поля с закрытым набором значений (source, span type, thread status, visibility_mode) проверяются по нему тоже, включая каждый элемент списка in. source — это то, что бэкенд проверяет сам, и он отвечает на неизвестное значение кодом 500, а не 400, поэтому source = "SDK" иначе был бы непрозрачной серверной ошибкой для заглавной буквы. Остальные сравниваются как строки и отвечают пустой страницей, что читается как «нет совпадений», хотя означает «нет такого значения». Спросите schema("list.trace") (или list.span, list.thread, list.experiment) для полного справочника полей, включая допустимые значения.

Поиск одного случая в наборе данных. list(entity_type="dataset_item", dataset_id=…) filters on the case itself: data.<key> для ключей, с которыми был создан набор данных, full_data для подстроки всей полезной нагрузки (полное сканирование — укажите ключ, когда можете), плюс id, tags, source, trace_id, span_id и временные метки. data.<key> принимает только шесть строковых операторов (=, !=, contains, not_contains, starts_with, ends_with); бэкенд отвечает на сравнение кодом 400, поэтому этот оператор отклоняется до вызова. У конечной точки нет сортировки и полнотекстового поиска — sort отклоняется, а не игнорируется. read(entity_type="dataset_item", id=…) возвращает один случай целиком, что позволяет прочитать значение, обрезанное таблицей.

list(entity_type="dataset_item", dataset_id="<uuid>", filters='data.question contains "install"')
list(
    entity_type="dataset_item", dataset_id="<uuid>", filters='trace_id = "<trace-uuid>"'
)  # the case made from that trace
read(entity_type="dataset_item", id="<item-uuid>")  # the case, uncut

С experiment_ids тот же список становится сравнением — случаи с каждым прикреплённым прогоном — и фильтрует по прогонам (feedback_scores.<name>, output, duration). Это два разных набора полей на двух конечных точках бэкенда: schema("list.dataset_item_case") — собственные случаи набора данных, schema("list.dataset_item") — сравнение.

Сортировка. trace, span, thread и experiment принимают sort="<field> [asc|desc]", desc по умолчанию и только одно поле: sort="duration desc", sort="total_estimated_cost", sort="feedback_scores.accuracy asc", sort="usage.total_tokens". Поле проверяется по списку сортируемых полей сущности до вызова, потому что бэкенд молча игнорирует поля, по которым не может сортировать. На очень больших рабочих областях бэкенд полностью отключает сортировку; заголовок сообщает об этом, когда это происходит.

dataset_item сортирует только как сравнение (с experiment_ids): конечная точка элементов не принимает параметр сортировки, поэтому сортировка в обычном списке отклоняется, а не игнорируется.

Временное окно и поиск. trace, span и thread принимают since и until, каждый — относительный интервал ("30m", "1h", "7d") или момент времени в формате ISO-8601 с часовым поясом, так что «последний час» не требует арифметики с часами. Окно задаётся по времени создания записи, что дёшево для бэкенда и совпадает с start_time в пределах секунд для живого трафика. Для точной границы поместите start_time в filters. Те же три типа принимают search — свободный текст, сопоставляемый в любом месте id, name, input, output, metadata, tags и thread id. Поиск сканирует весь проект на бэкенде, поэтому первый вызов в большом проекте может занять десятки секунд. Эти вызовы получают тайм-аут 60 секунд. Добавление since снова делает их быстрыми.

Чтение таблицы. Длительности помечены duration_ms / ttft_ms и показаны в целых миллисекундах; поле остаётся duration в filters и sort. Временные метки показаны с точностью до секунды, а стоимости — как простые десятичные числа. Строки проекта несут last_updated_trace_at, чтобы вы видели, какой проект имеет живой трафик; строки thread несут первое сообщение. Пустая страница в временном окне сообщает, когда приземлился последний trace проекта, а пустая страница под стандартным source = "sdk" сообщает, как увидеть другие источники. Опечатка в project_name возвращается с ближайшим существующим именем.

list(
    entity_type="trace",
    project_name="demo",
    since="1h",
    filters="error_info is_not_empty",
    sort="duration desc",
)
list(entity_type="trace", project_name="demo", search="order-42")

Проблемы диагностики. agent_insights_issue — это страница Diagnostics через MCP: повторяющиеся сбои, сгруппированные заданием Diagnostics Opik для проекта, ранжированные так, как их ранжирует UI (сначала самые недавние). Столбцы: severity, status, total_occurrences (сумма за всё время), latest_count (самый последний день отчёта, число, на которое ссылается описание проблемы) и last_seen. Открытые проблемы перечислены по умолчанию; передайте status="resolved" или "closed" для остальных. read и list также отвечают на issue, как это называет UI; длинное имя — это то, что в перечислении entity_type, так что эта сущность не появляется там дважды. Счётчики за всё время, поэтому они совпадают с UI; те же since / until, что и для traces, сужают окно, усечённое до UTC-дней отчёта, потому что Diagnostics агрегирует по дням.

Пустой список объясняет, почему он пуст, потому что иначе «ничего не сломано» и «никто не включил Diagnostics» читаются одинаково. Есть пять состояний: Diagnostics недоступен в этом развёртывании, не включён для этого проекта, выключен, включён, но не сканировался недавно, или включён и чист с временем последнего сканирования. Те, на которые вы можете повлиять, называют вызов, который нужно сделать, и каждое состояние ссылается на страницу Diagnostics проекта.

Непустой список датирует себя. Проблемы — это то, что сгруппировало последнее сканирование, поэтому ответ заканчивается Report covers data through <time>, и когда окно, о котором вы спросили, выходит за его пределы, он называет непокрытый хвост и как его закрыть: триггер, когда повторное сканирование достигает достаточно далеко назад, иначе сырые traces с since, который он вам даёт. Спросите неделю для проекта, сканируемого еженощно, и последний день отсутствует в сгруппированном ответе; именно это здесь и сообщается.

write("agent_insights_job.enable", {"project_name": "demo"}) включает Diagnostics. С этого момента он сканирует ежедневно, и повторный вызов безопасен. write("agent_insights_job.trigger", …) сканирует последние 24 часа сейчас, не дожидаясь ночного запуска. Оба требуют того же разрешения, что и чтение проблем, и оба отказываются там, где в развёртывании нет Diagnostics.

Проблема проходит свой жизненный цикл с помощью write("agent_insights_issue.resolve", {"issue_id": "<uuid>", "project_name": "demo"}) — обработана — или …close для той, на которую не стоит реагировать, и …reopen, чтобы вернуть любую из них в открытый список. Все три требуют того же разрешения и отвечают ссылкой на представление, в которое переместилась проблема, поскольку решённая проблема больше не находится на странице по умолчанию. Исправлен ли сбой, — это оценочное суждение, поэтому эти действия для случаев, когда вы просите: у ассистента нет права прибирать список во время триажа.

Метрики во времени. project_metric отображает одну метрику для проекта в виде таблицы временных интервалов: количество traces, spans и threads, длительности, частоты ошибок, стоимости, использование токенов и оценки обратной связи. Он отвечает на вопрос, который следует за обзором, — когда что-то изменилось.

list(entity_type="project_metric", project_name="demo", metric_type="trace_count")
list(
    entity_type="project_metric",
    project_name="demo",
    metric_type="trace_error_rate",
    since="14d",
    interval="daily",
)
list(
    entity_type="project_metric", project_name="demo", metric_type="span_count", breakdown="model"
)  # one column per model
list(
    entity_type="project_metric",
    project_name="demo",
    metric_type="span_duration",
    breakdown="model",
    series="p99",
)  # the p99 of each model

Строки — это временные интервалы, а не записи, поэтому page, size и sort отклоняются, а не игнорируются. interval — это hourly, daily, weekly или total; если не указано, он следует окну так, как вкладка Metrics — почасово до 3 дней, ежедневно до 30, еженедельно за пределами — так что диаграмма по умолчанию составляет несколько десятков строк при любом диапазоне, а почасовой месяц (721 строка) — это то, о чём вы просите. since / until принимают те же формы, что и везде, и по умолчанию — последние 7 дней. filters использует поля той сущности, о которой метрика, поэтому метрика span фильтруется по полям span.

breakdown разбивает каждый интервал по tags, name, error_info, error_type, model, provider, span_type, guardrail_name или metadata.<key>. Не каждая метрика принимает каждый из них, и семь не принимают ни одного; инструмент знает, какие, и сообщает об этом до вызова бэкенда, называя метрику, которая отвечает на тот же вопрос, если такая существует. Три семейства возвращаются как несколько серий одновременно (длительность как p50/p90/p99, оценка обратной связи по имени, использование токенов по ключу), и бэкенд отображает одну из них за раз при группировке, поэтому series= выбирает её: процентиль, имя оценки или ключ использования. Длительность по умолчанию — p50, а использование токенов — total_tokens, и то, что использовалось, повторяется в первой строке.

Пустые интервалы пропускаются и подсчитываются внизу, так что тихий месяц — это несколько строк вместо столбца нулей, а частота за интервал без traces отсутствует, а не сообщается как ноль.

Спросите schema("list.project_metric") для таблицы метрик, интервалов и матрицы группировки по метрикам.

Имена проекта. score_name перечисляет имена оценок обратной связи, записанные в проекте, а online_rule — оценщики правил автоматизации, настроенные на нём, откуда и происходит большинство этих имён. Оба — те же списки, которые несёт read("project", …), полностью и с пагинацией, для случаев, когда ограниченной версии в обзоре недостаточно.

list(entity_type="score_name", project_name="demo")
list(entity_type="online_rule", project_name="demo")

write

Универсальный диспетчер записи. Передайте operation + data, и диспетчер проверит полезную нагрузку, применит правильный REST-глагол и вернёт ответ бэкенда.

Операции:

ОперацияЧто она делает
trace.createЗаписать один trace (или пакет). Родитель для spans / scores / comments.
trace.updateФинализировать или изменить существующий trace.
span.createЗаписать span на существующем trace (или пакет).
score.createПрикрепить числовую оценку обратной связи к trace, span или thread.
comment.createПрикрепить текстовый комментарий к trace, span или thread.
prompt_version.saveСохранить новую версию prompt (создаёт prompt по имени, если отсутствует).
dataset.createСоздать набор данных — type: "test_suite" делает его тестовым набором для оценки.
dataset_item.upsertВставить элементы в набор данных (всегда форма envelope).
experiment.createСоздать эксперимент, ограниченный набором данных.
experiment_item.createПрикрепить строки trace + dataset_item к эксперименту.
thread.closeЗакрыть thread (пометить как неактивный). Передайте thread_id и проект.
thread.openОткрыть закрытый thread. Передайте thread_id и проект.
agent_insights_job.enableВключить Diagnostics для проекта (ежедневные сканирования, безопасно повторять).
agent_insights_job.triggerЗапустить сканирование Diagnostics сейчас, за последние 24 часа.
agent_insights_issue.resolveПометить проблему Diagnostics как обработанную (сначала спросите пользователя).
agent_insights_issue.closeПометить проблему Diagnostics как не стоящую действий (сначала спросите пользователя).
agent_insights_issue.reopenВернуть решённую или закрытую проблему Diagnostics в открытый список.
write(
    operation="score.create",
    data={
        "target": "trace",
        "target_id": "7f2e3c8a-…",
        "name": "helpfulness",
        "value": 0.9,
        "reason": "great recovery",
    },
)

schema

Проверьте точную JSON-форму и обязательные поля любой операции записи перед вызовом — полезно, когда вы не уверены, как должен выглядеть data. Возвращает схему, область OAuth и один проверенный пример. Чистый поиск, без вызова бэкенда.

schema(operation="score.create")
schema(operation="prompt_version.save")

Тот же инструмент отвечает на list.trace, list.span, list.thread и list.experiment справочником инструмента list для этой сущности: каждое фильтруемое поле с его типом и допустимыми операторами, сортируемые поля, применимы ли временное окно и полнотекстовый поиск, и два примера фильтров.

schema(operation="list.trace")

Конфигурация

Каждый параметр — это переменная окружения. Обязательные выделены жирным.

Идентичность / конечная точка

ПеременнаяПо умолчаниюПримечания
OPIK_API_KEY—Обязательна для любого аутентифицированного чтения/записи.
OPIK_WORKSPACEне заданоИмя рабочей области. В облаке с API-ключом, если не задано, отправляется default, что разрешается в стандартную рабочую область вашей учетной записи — задайте её явно, если работаете в другой, иначе чтение будет молча выполняться из неправильной рабочей области. Оставьте не заданным при OAuth (токен несёт её) и в локальной/OSS-установке (default — единственная рабочая область там).
COMET_WORKSPACE—Устаревший псевдоним для OPIK_WORKSPACE (обратная совместимость). OPIK_WORKSPACE имеет приоритет, если заданы обе.
COMET_WORKSPACE_IDне заданоНеобязательный UUID рабочей области. Записывается в события аналитики, если задан, и имеет приоритет над разрешённым. Редко нужен — OAuth-установки получают UUID из токена автоматически.
COMET_URL_OVERRIDEhttps://www.comet.comУкажите ваш self-hosted Comet хост, или https://dev.comet.com для staging.
OPIK_URLвыводится из COMET_URL_OVERRIDE + /opik/apiПереопределяйте только если Opik живёт на другом хосте/пути, чем Comet UI.
OPIK_DEFAULT_PROJECT_NAMEне заданоЕсли задано, блоб instructions для каждой сессии сообщает LLM передавать это как project_name при каждом вызове инструмента, если пользователь не назвал другой проект.

Сервер / транспорт

ПеременнаяПо умолчаниюПримечания
OPIK_MCP_TRANSPORTstdiostdio для запуска хостом, streamable-http для прослушивания порта.
OPIK_MCP_HOST127.0.0.1Хост привязки uvicorn (только streamable-http).
OPIK_MCP_PORT8080Порт привязки uvicorn (только streamable-http).
OPIK_MCP_RELOADfalsetrue для включения uvicorn --reload (только для разработки).
OPIK_MCP_AS_URLне заданоURL сервера авторизации OAuth, рекламируется в /.well-known/oauth-protected-resource (RFC 9728) и используется как прокси-цель для AS-discovery запросов. Требуется для MCP-хостов, чтобы запустить OAuth-процесс через HTTP.
OPIK_MCP_RESOURCE_URIне заданоКанонический публичный URI этого сервера, рекламируется как resource в метаданных защищённого ресурса и используется для вывода подсказки WWW-Authenticate.
OPIK_MCP_OAUTH_VALIDATION_CACHE_TTL_S30Как долго «валидный» ответ от token introspection opik-backend доверяется перед тем, как следующий запрос с тем же OAuth-токеном спросит снова. Ограничивает нагрузку на бэкенд от проверки каждого запроса и окно, в котором просроченный токен всё ещё пересылается (это окно также заканчивается на первом 401, который возвращает бэкенд). Ограничено собственным expires_at токена, если бэкенд его сообщает.
OPIK_MCP_LOG_LEVELINFOПорог логгера stderr.

Выбор транспорта

Две формы bearer-токенов, два контракта на HTTP-транспорте. OAuth-токен доступа opik_mcp_at_… проверяется при каждом запросе через endpoint token introspection opik-backend (кэшируется, см. OPIK_MCP_OAUTH_VALIDATION_CACHE_TTL_S); просроченный или отозванный токен получает HTTP 401 с WWW-Authenticate: Bearer error="invalid_token", на что MCP-хосты опираются для тихого гранта refresh_token. Opik API-ключ не проверяется локально: он пересылается дословно в opik-backend, который является единственной точкой контроля. Выбирайте транспорт по форме развёртывания:

СценарийТранспорт
MCP-клиент и Opik на одной машине (локальная OSS-установка)stdio (рекомендуется — проще всего, без порта и настройки OAuth)
Локальный MCP-клиент → удалённый Opik (Comet cloud / self-hosted)stdio с OPIK_API_KEY, или HTTP с OAuth (OPIK_MCP_AS_URL указывает на бэкенд)
Хостируемый opik-mcp за тем же edge, что и opik-backendHTTP — bearer-токены проверяются бэкендом при каждом запросе

Примечание для локальных OSS-установок: OSS-бэкенд не аутентифицирует запросы, поэтому HTTP opik-mcp перед ним так же открыт, как и сам OSS REST API. Оставьте привязку по умолчанию 127.0.0.1 (и предпочитайте stdio) в общих сетях.

Телеметрия

Анонимные события использования (только тип события и тайминги — без содержимого запросов). Включается SHA-256 дайджест вашего API-ключа, чтобы поддержка могла найти вашу учётную запись; сырой ключ никогда не покидает процесс. Отказ: OPIK_MCP_ANALYTICS_ENABLED=false.

ПеременнаяПо умолчаниюПримечания
OPIK_MCP_ANALYTICS_ENABLEDtrueУстановите в false, чтобы отключить всю телеметрию.
OPIK_MCP_ANALYTICS_URLhttps://stats.comet.com/notify/event/Переопределение для staging.
OPIK_MCP_ANALYTICS_ENVIRONMENTprodТег на каждом событии (prod / staging / dev).
OPIK_MCP_ANALYTICS_SOURCEcomet.comПриёмник использует это для пометки on_prem=False. On-prem установки должны переопределить на "" или свой домен.
OPIK_MCP_ANALYTICS_CONNECT_TIMEOUT_S5.0HTTP таймаут соединения.
OPIK_MCP_ANALYTICS_TOTAL_TIMEOUT_S10.0HTTP общий таймаут запроса.

Известные ограничения хостов

Хосты различаются по тому, как долго они позволяют выполняться одному вызову инструмента:

  • Claude Code — нет документированного таймаута вызова инструмента. Рекомендуется.
  • Cursor — жёсткий таймаут 60 секунд, который не сбрасывается при прогрессе (известный баг).
  • MCP Inspector — MAX_TOTAL_TIMEOUT ограничивает общую длительность (по умолчанию 60 секунд). Поднимите его в UI Inspector для длительных операций.

Если вызов зависает, установите OPIK_MCP_LOG_LEVEL=DEBUG для полного журнала запросов.


Устранение неполадок

OPIK_API_KEY не подхватывается — переменная не достигает процесса сервера. В Claude Code / Cursor / VS Code переменные окружения применяются только внутри блока env конфигурации MCP-сервера, а не из вашей оболочки. Перезапустите хост после редактирования.

Вызов Cursor истекает через 60 секунд — известный баг Cursor, не opik-mcp. Либо сузьте вызов (меньше size, более узкое окно), либо выполните ту же операцию в Claude Code, где нет жёсткого лимита.

Сервер не отображается, вход не открывается, неправильная рабочая область, uvx не найден. Это описано в разделе устранения неполадок документации. opik mcp status (из того же CLI uvx opik) перечисляет каждый клиент, у которого настроен сервер, и не отклонялся ли его конфиг.


Разработка

git clone git@github.com:comet-ml/opik-mcp.git
cd opik-mcp
make install        # uv sync --locked --extra dev
make check          # lint + typecheck + test
make run-dev        # uvicorn with --reload + DEBUG logs
make inspect        # MCP Inspector against the running server

Общие цели:

ЦельЧто делает
make installuv sync --locked --extra dev
make runЗапуск MCP-сервера (stdio по умолчанию).
make run-devЗапуск с DEBUG-логированием + uvicorn --reload.
make devЗапуск через mcp dev (обёртка Inspector dev-mode).
make inspectЗапуск MCP Inspector против работающего сервера.
make testuv run pytest -q.
make lintruff check + проверка форматирования.
make formatruff format + ruff check --fix.
make typecheckmypy.
make checklint + typecheck + test.

Структура репозитория:

opik-mcp/
├── src/opik_mcp/        ← server, tools, analytics
├── tests/               ← pytest suites
├── scripts/             ← live-BE smoke + MCP-session smoke
├── legacy/typescript/   ← migration guide for the deprecated v2 TS server (source: tag `legacy-typescript-final`)
├── pyproject.toml
└── Makefile

Получить помощь


Обновление с v2? Легаси TypeScript-сервер всё ещё публикуется на npm как opik-mcp@^2 (npx -y opik-mcp); его исходный код находится на git-теге legacy-typescript-final. См. legacy/typescript/DEPRECATED.md для политики поддержки.


Лицензия

Apache-2.0.