Comet Opik

oficial

Consulte e analise seus logs, traces, prompts e todos os outros dados de telemetria dos seus LLMs em linguagem natural.

O que você pode fazer com Comet Opik MCP?

  • Consultar traces e projetos — Pergunte "liste meus projetos Opik" ou "quais traces no projeto 'demo' falharam hoje?" para navegar pelos dados do workspace via list e read.

  • Pontuar e comentar traces — Anexe pontuações numéricas de feedback, como com um motivo, ou adicione comentários de texto livre a traces, spans ou threads.

  • Gerenciar versões de prompts — Salve novas versões de prompts, criando prompts por nome se ainda não existirem.

  • Inspecionar esquemas de escrita — Use schema para verificar formatos JSON exatos, campos obrigatórios e exemplos validados antes de construir qualquer operação de escrita.

  • Monitorar a saúde do projeto — Obtenha visões gerais de 7 dias com contagens de traces, taxas de erro, durações e custos, ou gráficos de métricas ao longo do tempo.

  • Triagem de problemas de Diagnóstico — Liste problemas abertos de insights de agentes, resolva ou feche-os, e habilite ou acione varreduras.

Documentação

Servidor MCP Opik

O servidor oficial do Model Context Protocol (MCP) para Opik, a plataforma de observabilidade e avaliação de LLM de código aberto, criada pela Comet. Conecte seu host de IA (Claude Code, Cursor, VS Code Copilot, Codex, opencode ou qualquer cliente MCP) diretamente ao seu workspace Opik: leia traces, registre scores e salve versões de prompts, tudo a partir do chat.

Feito para engenheiros de LLM que já usam Opik e querem controlá-lo a partir do mesmo assistente de IA com o qual programam.

Migrando do antigo npx opik-mcp? O servidor TypeScript está descontinuado e será encerrado em 2026-11-15. Substitua npx -y opik-mcp por uvx opik-mcp@latest na configuração do seu cliente MCP. Guia completo: 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

Início rápido

Um único comando registra o servidor nos clientes de IA da sua máquina, instala o pacote de skills do Opik e verifica a conexão. Requer uv e nenhum SDK do Opik:

uvx opik mcp configure

Ele detecta Claude Code, Cursor, VS Code Copilot, Codex e opencode, e usa o servidor hospedado no Opik Cloud (login pelo navegador, sem chave de API armazenada) ou este servidor local em outros casos. Qualquer outro cliente MCP pode usar a URL hospedada diretamente:

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

Add to Cursor Install in VS Code

Guia de configuração, solução de problemas e FAQ: comet.com/docs/opik/mcp-server. O restante deste README cobre o servidor local, que o comando acima configura para Opik self-hosted e de código aberto, e que você também pode configurar manualmente.


Instalação manual

opik-mcp é um pacote Python (requer Python 3.13+). A forma recomendada de executá-lo é uvx, que busca e executa a versão publicada mais recente sob demanda — sem instalação global, sem gerenciamento de virtualenv.

Instale uv uma vez:

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

Você precisará de duas coisas do seu workspace Opik:

  • OPIK_API_KEY — obtenha em comet.com/api/my/settings/.
  • OPIK_WORKSPACE — o nome do seu workspace (minúsculo, como aparece na URL). Ex.: https://www.comet.com/acme-ai/... → OPIK_WORKSPACE=acme-ai. COMET_WORKSPACE é aceito como alias descontinuado.

Cloud, com chave de API: defina-o, a menos que o padrão da sua conta seja o que você deseja. Se omitido, o servidor envia default, que a Comet resolve para o workspace padrão da sua conta. Isso funciona, mas se você realmente trabalha em um workspace nomeado, será direcionado para um diferente sem nenhum aviso — suas leituras virão do lugar errado em vez de falharem.

Cloud, via OAuth: deixe-o sem definir. O workspace vem do token que você autorizou, e o servidor ignora completamente essa configuração.

Local / código aberto: deixe-o sem definir. O Opik de código aberto tem um único workspace chamado default e não há como criar outros, que é exatamente o que o fallback fornece.

Comet self-hosted: defina-o. Diferente do código aberto, essas implantações têm workspaces nomeados reais, e o mesmo risco de workspace errado silencioso se aplica.

Qualquer que seja o caso, certifique-se de que o valor esteja realmente substituído. Trechos na internet trazem placeholders como <your-workspace> ou ${input:OPIK_WORKSPACE}; colados como estão, esses não são nomes de workspace. O servidor agora os recusa diretamente em vez de deixar o backend responder com um erro de autenticação que não explica nada.

Claude Code

Adicione o servidor com um comando:

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

Ou edite ~/.claude.json diretamente:

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

Reinicie o Claude Code. Verifique com /mcp — opik-mcp deve aparecer como conectado. Depois, no chat, pergunte: "liste meus projetos Opik" — o Claude chamará a ferramenta list e você verá os projetos do seu workspace.

Cursor

Edite ~/.cursor/mcp.json (global) ou .cursor/mcp.json (projeto), ou abra 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>"
      }
    }
  }
}

Recarregue o Cursor; o ponto verde ao lado de opik-mcp no painel MCP confirma a conexão. Pergunte no chat: "liste meus projetos Opik".

Timeout de 60s do Cursor. O Cursor impõe um timeout rígido para chamadas de ferramenta que não é reiniciado com notificações de progresso. Veja Limites conhecidos do host.

VS Code Copilot

.vscode/mcp.json no seu workspace (ou JSON de Configurações do Usuário):

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

Recarregue a janela; o indicador MCP do Copilot Chat mostra opik-mcp assim que o servidor estiver acessível. Pergunte no chat: "liste meus projetos Opik".

MCP Inspector (teste manual)

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

Opik self-hosted

Adicione COMET_URL_OVERRIDE (e OPIK_URL se o Opik estiver em um caminho não padrão) ao mesmo bloco env na configuração do seu host:

{
  "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": ""
      }
    }
  }
}

Omita OPIK_WORKSPACE em uma implantação de código aberto, onde default é o único workspace; mantenha-o em um Comet self-hosted, que tem workspaces nomeados reais.

Definir OPIK_MCP_ANALYTICS_SOURCE="" exclui sua instalação do rótulo de origem cloud-Comet em eventos de telemetria.


Ferramentas

opik-mcp expõe uma superfície pequena e orientada a resultados que cobre todo o ciclo de vida (ler → anotar → selecionar → criar → iterar).

FerramentaFinalidade
readLeitura universal por id / nome / URI opik://
listListagem universal com filtro opcional por nome + paginação
writeEscrita universal — registra traces/spans, pontua, comenta, salva prompts, gerencia datasets e experimentos
schemaInspeciona esquemas de operações de escrita (usado pelo LLM para construir payloads válidos)
read_skillLê uma das skills de agente do Opik incluídas neste servidor

read

Uma ferramenta para qualquer pergunta do tipo "me mostre X". Recebe um entity_type mais um id (UUID ou, para tipos nomeáveis, um nome) ou uma URI opik:// completa. Leituras compostas (trace, prompt, thread, agent_insights_issue) incorporam seus filhos, então uma única chamada retorna o quadro completo.

O registro que você nomeia volta inteiro. Filhos incorporados não: seus corpos são buscados com o truncate=true do backend, então um campo acima de ~10 KB é cortado no ClickHouse e imagens base64 são substituídas por "[image]" — um anexo repetido em 200 spans custaria mais do que todo o resto da leitura. A resposta informa isso em spanBodies / messageBodies, e qualquer filho está inteiro novamente através do seu próprio read("span", id) ou read("trace", trace_id), que acessam endpoints sem nenhum parâmetro truncate.

Uma coleção incorporada também tem comprimento limitado: 200 spans, 200 turnos, 100 versões de prompt. Além disso, spansTruncated / messagesTruncated / versionsTruncated é true e uma linha moreSpans / moreMessages / moreVersions ao lado carrega a contagem e a chamada list(...) exata que continua de onde a parte incorporada parou.

Entidades suportadas: project, trace, span, dataset, dataset_item, experiment, prompt, thread, agent_insights_issue. A busca por nome está disponível para project, experiment, prompt, dataset (mais lenta — duas chamadas de API — e pode retornar múltiplas correspondências). thread e agent_insights_issue são limitados ao projeto: passe project_id ou project_name, ou um link/URI que carregue o projeto. dataset e dataset_item eram chamados test_suite e test_suite_item antes; os nomes antigos ainda resolvem, mas não são divulgados e o novo código deve usar os novos.

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>",
)

Um link copiado da interface do Opik funciona como id: um link de thread ou um link da página de Diagnóstico carrega o projeto, então nenhum project_id é necessário e o tipo de entidade é obtido do link.

Uma leitura project responde "como está meu projeto" em uma única chamada. Retorna {project, summary, vocabulary, contains, url}: o registro, depois os quatro números que a página Logs mostra como cards (contagem de traces, taxa de erro, duração média, custo total) dos últimos 7 dias contra os 7 anteriores, apenas tráfego de SDK, como na tela. since / until movem essa janela; since="30d" é o que a interface abre. Uma taxa ou média em um período sem traces retorna como null, porque 0% de erros em uma semana sem tráfego parece uma semana saudável.

vocabulary é o mapa que você precisa antes de poder perguntar qualquer outra coisa: os nomes dos scores de feedback do projeto, suas chaves de uso de tokens e as regras de automação que pontuam seus traces. Esses são os nomes que vão em um filtro ou em series= abaixo, e adivinhá-los retorna uma página vazia que parece uma boa notícia. Nomes de scores e regras são limitados, sempre reportam o total real e nomeiam a chamada que retorna o restante; as chaves de uso são listadas por completo, já que nada mais as enumera. contains nomeia o experimento, dataset, versão de prompt e execução de otimização mais recentes, então "o que está acontecendo aqui" não precisa de mais quatro chamadas. Uma parte que falhou ao carregar informa isso em vez de parecer vazia, e uma parte vazia é omitida.

Uma leitura agent_insights_issue retorna {issue, example_trace_ids, details}: o registro do problema de Diagnóstico (nome, descrição, causa, correção sugerida, gravidade, status), os ids deduplicados dos traces que o exibem (a mesma amostra que a página de Diagnóstico mostra — abra um com read("trace", id)), e o detalhamento por dia. Os corpos dos traces não são incorporados, então a leitura permanece em uma única chamada de backend. since / until restringem as linhas por dia; o padrão é todo o período. Quando o servidor conhece a URL do Opik e o workspace da sessão, a leitura também carrega url (a página de Diagnóstico do problema) e trace_url_template (um link profundo para qualquer um dos traces de exemplo), para que o assistente possa entregar algo clicável; em uma sessão OAuth cujo workspace não pôde ser resolvido, os links são omitidos em vez de adivinhados.

Os próprios traces não carregam URL — um link para um deles não é derivável dos campos que um read ou list retorna, e um formato adivinhado retorna 404. As instruções da sessão nomeiam um modelo para isso, .../v1/session/redirect/projects/?trace_id={trace_id}&path=..., então o assistente preenche um id e entrega um link. Ele passa pelo redirecionamento do opik-backend, que resolve o projeto e o workspace a partir do trace, então funciona onde uma URL direta de projeto não funciona, incluindo uma sessão OAuth com workspace não resolvido. É o mesmo link que o SDK Python imprime para um trace.

list

Navegue ou pesquise uma coleção com paginação. Tipos limitados ao projeto (trace, span, thread, agent_insights_issue, dataset_item, prompt_version) precisam do pai: um UUID ou nome de projeto, um UUID de dataset ou um UUID de prompt.

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"')

Filtros. trace, span, thread, experiment e dataset_item aceitam uma string OQL, a mesma gramática do search_traces(filter_string=…) do SDK:

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

Strings vão entre aspas duplas, números são simples, duration está em milissegundos, datas são instantes ISO-8601 com fuso horário ("2026-09-08T10:00:00Z"). Scores e dicionários usam uma chave: feedback_scores.accuracy < 0.5, metadata.environment = "prod". AND é o único conector.

Como na página Logs da interface, as listas de trace, span e thread adicionam source = "sdk" para que traces de avaliador, playground e experimento fiquem fora do caminho; nomeie source você mesmo para vê-los. A primeira linha de saída ecoa o filtro que foi aplicado. Um filtro inválido falha antes de chegar ao backend com o que é necessário para corrigi-lo: a posição de um erro de sintaxe, o nome do campo mais próximo, os operadores válidos para o tipo do campo ou o formato esperado do valor. Campos com um conjunto fechado de valores (source, span type, thread status, visibility_mode) são verificados contra ele também, incluindo cada elemento de uma lista in. source é o que o backend valida por conta própria, e ele responde a um valor desconhecido com 500 em vez de 400, então source = "SDK" seria, de outra forma, um erro de servidor opaco para uma letra maiúscula. Os demais são comparados como strings e respondem com uma página vazia, o que parece "sem correspondências" quando na verdade significa "valor inexistente". Pergunte a schema("list.trace") (ou list.span, list.thread, list.experiment) para a referência completa de campos, incluindo os valores aceitos.

Encontrando um caso em um dataset. list(entity_type="dataset_item", dataset_id=…) filters on the case itself: data.<key> para as chaves com as quais o dataset foi construído, full_data para uma substring de todo o payload (uma varredura completa — nomeie uma chave quando puder), além de id, tags, source, trace_id, span_id e os timestamps. data.<key> aceita apenas os seis operadores de string (=, !=, contains, not_contains, starts_with, ends_with); o backend responde a uma comparação com 400, então este é recusado antes da chamada. O endpoint não tem ordenação nem busca de texto livre — sort é recusado em vez de ignorado. read(entity_type="dataset_item", id=…) retorna um caso completo, que é como um valor cortado pela tabela é lido de volta.

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

Com experiment_ids a mesma lista é a comparação — os casos com cada execução anexada — e filtra nas execuções (feedback_scores.<name>, output, duration). Os dois são conjuntos de campos diferentes em dois endpoints do backend: schema("list.dataset_item_case") são os casos do próprio dataset, schema("list.dataset_item") a comparação.

Ordenação. trace, span, thread e experiment aceitam sort="<field> [asc|desc]", desc por padrão e apenas um campo: sort="duration desc", sort="total_estimated_cost", sort="feedback_scores.accuracy asc", sort="usage.total_tokens". O campo é verificado contra a lista ordenável da entidade antes da chamada, porque o backend ignora silenciosamente campos pelos quais não pode ordenar. Em workspaces muito grandes, o backend descarta a ordenação por completo; o cabeçalho informa isso quando acontece.

dataset_item ordena apenas como comparação (com experiment_ids): o endpoint de itens não aceita parâmetro de ordenação, então uma ordenação em uma listagem simples é recusada em vez de ignorada.

Janela de tempo e busca. trace, span e thread aceitam since e until, cada um um intervalo relativo ("30m", "1h", "7d") ou um instante ISO-8601 com fuso horário, então "a última hora" não precisa de aritmética de relógio. A janela é pelo horário de criação do registro, o que é barato para o backend e concorda com start_time em segundos para tráfego ao vivo. Para um limite exato, coloque start_time em filters. Os mesmos três tipos aceitam search, texto livre correspondido em qualquer lugar de id, nome, entrada, saída, metadados, tags e id do thread. A busca varre todo o projeto no backend, então a primeira chamada em um projeto grande pode levar dezenas de segundos. Essas chamadas recebem um timeout de 60 segundos. Adicionar since as torna rápidas novamente.

Lendo a tabela. Durações são rotuladas como duration_ms / ttft_ms e mostradas como milissegundos inteiros; o campo permanece duration em filters e sort. Timestamps são mostrados ao segundo e custos como decimais simples. Linhas de projeto carregam last_updated_trace_at para que você veja qual projeto tem tráfego ao vivo; linhas de thread carregam a primeira mensagem. Uma página vazia sob uma janela de tempo informa quando o último trace do projeto chegou, e uma página vazia sob o source = "sdk" padrão informa como ver as outras fontes. Um project_name com erro de digitação retorna com o nome existente mais próximo.

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")

Problemas de Diagnostics. agent_insights_issue é a página Diagnostics via MCP: as falhas recorrentes que o job de Diagnostics do Opik agrupou para um projeto, classificadas como a UI as classifica (mais recentemente vistas primeiro). As colunas são severity, status, total_occurrences (soma de todos os tempos), latest_count (o dia de relatório mais recente, o número ao qual a descrição do próprio problema se refere) e last_seen. Problemas abertos são listados por padrão; passe status="resolved" ou "closed" para os demais. read e list também respondem a issue, que é como a UI chama esses; o nome longo é o que está no enum entity_type, então essa entidade não aparece lá duas vezes. As contagens são de todos os tempos, então correspondem à UI; o mesmo since / until que para traces estreita a janela, truncada para dias de relatório UTC porque Diagnostics agrega por dia.

Uma lista vazia explica por que está vazia, porque "nada está quebrado" e "ninguém ativou Diagnostics" pareceriam iguais de outra forma. Há cinco estados: Diagnostics indisponível nesta implantação, não habilitado para este projeto, desligado, habilitado mas não escaneado recentemente, ou habilitado e limpo com o horário da última varredura. Os que você pode agir nomeiam a chamada a fazer, e cada estado vincula a página Diagnostics do projeto.

Uma lista não vazia se data. Os problemas são o que a última varredura agrupou, então a resposta termina com Report covers data through <time>, e quando a janela que você perguntou ultrapassa isso, ela nomeia a cauda descoberta e como fechá-la: um trigger quando uma revarredura alcança o suficiente, caso contrário traces brutos com o since que ela fornece. Peça uma semana em um projeto escaneado diariamente e o último dia estará faltando na resposta agrupada; é isso que informa isso.

write("agent_insights_job.enable", {"project_name": "demo"}) ativa Diagnostics. Ele escaneia diariamente a partir de então, e chamá-lo novamente é seguro. write("agent_insights_job.trigger", …) escaneia as últimas 24 horas agora, sem esperar pela execução noturna. Ambos exigem a mesma permissão que ler problemas exige, e ambos recusam onde a implantação não tem Diagnostics.

Um problema avança em seu ciclo de vida com write("agent_insights_issue.resolve", {"issue_id": "<uuid>", "project_name": "demo"}) — resolvido — ou …close para um que não vale a pena agir, e …reopen para colocar qualquer um de volta na lista aberta. Todos os três exigem a mesma permissão e respondem com um link para a visão para a qual o problema foi movido, já que um problema resolvido não está mais na página padrão. Se uma falha está corrigida é um julgamento, então esses são para quando você perguntar: o assistente não tem negócio arrumando a lista enquanto faz triagem dela.

Métricas ao longo do tempo. project_metric gráfica uma métrica para um projeto como uma tabela de buckets de tempo: contagens de trace, span e thread, durações, taxas de erro, custos, uso de tokens e pontuações de feedback. Ela responde à pergunta que segue a visão geral, que é quando algo mudou.

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

Linhas são buckets de tempo, não registros, então page, size e sort são recusados em vez de ignorados. interval é hourly, daily, weekly ou total; se omitido, segue a janela como a aba Metrics faz — por hora até 3 dias, diariamente até 30, semanalmente além — então um gráfico padrão tem algumas dezenas de linhas qualquer que seja o intervalo, e um mês por hora (721 linhas) é algo que você pede. since / until aceitam as mesmas formas que em qualquer outro lugar e padronizam para os últimos 7 dias. filters usa os campos da entidade sobre a qual a métrica é, então uma métrica de span é filtrada por campos de span.

breakdown divide cada bucket por tags, name, error_info, error_type, model, provider, span_type, guardrail_name ou metadata.<key>. Nem toda métrica aceita cada um desses, e sete não aceitam nenhum; a ferramenta sabe quais e informa antes de chamar o backend, nomeando uma métrica que responde à mesma pergunta onde uma existir. Três famílias retornam como várias séries de uma vez (uma duração como p50/p90/p99, uma pontuação de feedback por nome, uso de token por chave), e o backend gráfica uma delas por vez ao agrupar, então series= escolhe: um percentil, um nome de pontuação ou uma chave de uso. Duração padroniza para p50 e uso de token para total_tokens, e o que foi usado é ecoado na primeira linha.

Buckets vazios são omitidos e contados abaixo, então um mês tranquilo é algumas linhas em vez de uma coluna de zeros, e uma taxa sobre um bucket sem traces é ausente em vez de relatada como zero.

Pergunte a schema("list.project_metric") para a tabela de métricas, os intervalos e a matriz de agrupamento por métrica.

Os nomes de um projeto. score_name lista os nomes de pontuação de feedback registrados em um projeto e online_rule os avaliadores de regras de automação configurados nele, que é de onde a maioria desses nomes vem. Ambos são as mesmas listas que read("project", …) carrega, completas e paginadas, para quando a versão limitada na visão geral não é suficiente.

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

write

Despachante universal de escrita. Passe operation + data e o despachante valida o payload, aplica o verbo REST correto e retorna a resposta do backend.

Operações:

OperaçãoO que faz
trace.createRegistra um único trace (ou um lote). Pai para spans / scores / comentários.
trace.updateFinaliza ou altera um trace existente.
span.createRegistra um span em um trace existente (ou um lote).
score.createAnexa uma pontuação de feedback numérica a um trace, span ou thread.
comment.createAnexa um comentário de texto livre a um trace, span ou thread.
prompt_version.saveSalva uma nova versão de prompt (cria o prompt por nome se ausente).
dataset.createCria um dataset — type: "test_suite" o torna um conjunto de teste de avaliação.
dataset_item.upsertFaz upsert de itens em um dataset (sempre a forma de envelope).
experiment.createCria um experimento com escopo em um dataset.
experiment_item.createAnexa linhas de trace + dataset_item a um experimento.
thread.closeFecha um thread (marca como inativo). Passe thread_id e o projeto.
thread.openReabre um thread fechado. Passe thread_id e o projeto.
agent_insights_job.enableAtiva Diagnostics para um projeto (varreduras diárias, seguro repetir).
agent_insights_job.triggerExecuta uma varredura Diagnostics agora, nas últimas 24 horas.
agent_insights_issue.resolveMarca um problema Diagnostics como resolvido (pergunte ao usuário primeiro).
agent_insights_issue.closeMarca um problema Diagnostics como não vale a pena agir (pergunte ao usuário primeiro).
agent_insights_issue.reopenColoca um problema Diagnostics resolvido ou fechado de volta na lista aberta.
write(
    operation="score.create",
    data={
        "target": "trace",
        "target_id": "7f2e3c8a-…",
        "name": "helpfulness",
        "value": 0.9,
        "reason": "great recovery",
    },
)

schema

Inspecione a forma JSON exata e os campos obrigatórios de qualquer operação de escrita antes de chamá-la — útil quando você não tem certeza de como data deve parecer. Retorna o schema, o escopo OAuth e um exemplo validado. Apenas consulta, sem chamada ao backend.

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

A mesma ferramenta responde a list.trace, list.span, list.thread e list.experiment com a referência da ferramenta list para essa entidade: cada campo filtrável com seu tipo e operadores válidos, os campos ordenáveis, se uma janela de tempo e busca de texto livre se aplicam, e dois exemplos de filtros.

schema(operation="list.trace")

Configuração

Cada configuração é uma variável de ambiente. As obrigatórias estão em negrito.

Identidade / endpoint

VariávelPadrãoObservações
OPIK_API_KEY—Obrigatório para qualquer leitura/escrita autenticada.
OPIK_WORKSPACEnão definidoNome do workspace. Na nuvem com uma chave de API, não definir envia default, que resolve para o workspace padrão da sua conta — defina-o explicitamente se você trabalha em um diferente, ou as leituras virão do workspace errado silenciosamente. Deixe não definido com OAuth (o token o carrega) e em instalações locais/OSS (default é o único workspace lá).
COMET_WORKSPACE—Alias obsoleto para OPIK_WORKSPACE (compatibilidade reversa). OPIK_WORKSPACE vence se ambos estiverem definidos.
COMET_WORKSPACE_IDnão definidoUUID opcional do workspace. Gravado em eventos de análise quando definido, e tem precedência sobre o resolvido. Raramente necessário — instalações OAuth obtêm o UUID do token automaticamente.
COMET_URL_OVERRIDEhttps://www.comet.comDefina para o seu host Comet auto-hospedado, ou https://dev.comet.com para staging.
OPIK_URLderivado de COMET_URL_OVERRIDE + /opik/apiSubstitua apenas se o Opik estiver em um host/caminho diferente da interface do Comet.
OPIK_DEFAULT_PROJECT_NAMEnão definidoQuando definido, o blob instructions por sessão informa ao LLM para passar isso como project_name em cada chamada de ferramenta, a menos que o usuário nomeie um projeto diferente.

Servidor / transporte

VariávelPadrãoObservações
OPIK_MCP_TRANSPORTstdiostdio para iniciado pelo host, streamable-http para escutar em uma porta.
OPIK_MCP_HOST127.0.0.1Host de bind do uvicorn (somente streamable-http).
OPIK_MCP_PORT8080Porta de bind do uvicorn (somente streamable-http).
OPIK_MCP_RELOADfalsetrue para habilitar --reload do uvicorn (somente desenvolvimento).
OPIK_MCP_AS_URLnão definidoURL do servidor de autorização OAuth, anunciada em /.well-known/oauth-protected-resource (RFC 9728) e usada como alvo de proxy para sondagens de descoberta AS. Necessário para hosts MCP inicializarem o fluxo OAuth via HTTP.
OPIK_MCP_RESOURCE_URInão definidoURI pública canônica deste servidor, anunciada como resource nos metadados do recurso protegido e usada para derivar a dica WWW-Authenticate.
OPIK_MCP_OAUTH_VALIDATION_CACHE_TTL_S30Por quanto tempo uma resposta "válida" da introspecção de token do opik-backend é confiável antes que a próxima solicitação no mesmo token OAuth pergunte novamente. Limita a carga do backend adicionada pela validação por solicitação e a janela em que um token expirado ainda é encaminhado (essa janela também termina no primeiro 401 que o backend retorna). Limitado pelo próprio expires_at do token quando o backend relata um.
OPIK_MCP_LOG_LEVELINFOLimite do logger stderr.

Escolhendo um transporte

Duas formas de bearer, dois contratos no transporte HTTP. Um token de acesso OAuth opik_mcp_at_… é validado em cada solicitação contra o endpoint de introspecção de token do opik-backend (em cache, veja OPIK_MCP_OAUTH_VALIDATION_CACHE_TTL_S); um token expirado ou revogado recebe um HTTP 401 com WWW-Authenticate: Bearer error="invalid_token", que é o que os hosts MCP usam para sua concessão silenciosa de refresh_token. Uma chave de API do Opik não é validada localmente: ela é encaminhada literalmente ao opik-backend, que é seu único ponto de aplicação. Escolha o transporte pela forma de implantação:

CenárioTransporte
Cliente MCP e Opik na mesma máquina (instalação OSS local)stdio (recomendado — mais simples, sem porta, sem configuração OAuth)
Cliente MCP local → Opik remoto (nuvem Comet / auto-hospedado)stdio com OPIK_API_KEY, ou HTTP com OAuth (OPIK_MCP_AS_URL apontando para o backend)
opik-mcp hospedado atrás da mesma borda que opik-backendHTTP — os bearers são validados pelo backend por solicitação

Nota para instalações OSS locais: o backend OSS não autentica solicitações, então um opik-mcp HTTP na frente dele é tão aberto quanto a própria API REST OSS. Mantenha o bind padrão 127.0.0.1 (e prefira stdio) em redes compartilhadas.

Telemetria

Eventos de uso anônimos (apenas tipo de evento + tempo — sem conteúdo de consulta). Um digest SHA-256 da sua chave de API é incluído para que o suporte possa encontrar sua conta; a chave bruta nunca sai do processo. Desativar: OPIK_MCP_ANALYTICS_ENABLED=false.

VariávelPadrãoObservações
OPIK_MCP_ANALYTICS_ENABLEDtrueDefina para false para desativar toda a telemetria.
OPIK_MCP_ANALYTICS_URLhttps://stats.comet.com/notify/event/Substituição para staging.
OPIK_MCP_ANALYTICS_ENVIRONMENTprodTag em cada evento (prod / staging / dev).
OPIK_MCP_ANALYTICS_SOURCEcomet.comO receptor usa isso para marcar on_prem=False. Instalações on-prem devem substituir para "" ou seu próprio domínio.
OPIK_MCP_ANALYTICS_CONNECT_TIMEOUT_S5.0Timeout de conexão HTTP.
OPIK_MCP_ANALYTICS_TOTAL_TIMEOUT_S10.0Timeout total de solicitação HTTP.

Limites conhecidos do host

Os hosts diferem em quanto tempo permitem que uma única chamada de ferramenta execute:

  • Claude Code — sem timeout documentado de chamada de ferramenta. Recomendado.
  • Cursor — timeout fixo de 60s que não reinicia no progresso (bug upstream).
  • MCP Inspector — MAX_TOTAL_TIMEOUT limita a duração total (padrão 60s). Aumente na interface do Inspector para operações longas.

Se uma chamada ficar presa, defina OPIK_MCP_LOG_LEVEL=DEBUG para o log completo da solicitação.


Solução de problemas

OPIK_API_KEY não é reconhecido — a variável não está chegando ao processo do servidor. No Claude Code / Cursor / VS Code, as variáveis de ambiente só se aplicam quando dentro do bloco env da configuração do servidor MCP, não no seu shell. Reinicie o host após editar.

Chamada do Cursor expira em 60s — bug conhecido do Cursor, não do opik-mcp. Ou estreite a chamada (menor size, uma janela mais restrita), ou execute a mesma operação no Claude Code, que não tem limite fixo.

Servidor não aparecendo, login não abrindo, workspace errado, uvx não encontrado. Isso é abordado na seção de solução de problemas da documentação. opik mcp status (da mesma CLI uvx opik) lista todos os clientes que têm o servidor configurado e se a configuração dele divergiu.


Desenvolvimento

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

Alvos comuns:

AlvoO que faz
make installuv sync --locked --extra dev
make runExecuta o servidor MCP (stdio por padrão).
make run-devExecuta com logging DEBUG + uvicorn --reload.
make devExecuta via mcp dev (wrapper de modo dev do Inspector).
make inspectInicia o MCP Inspector contra um servidor em execução.
make testuv run pytest -q.
make lintruff check + verificação de formatação.
make formatruff format + ruff check --fix.
make typecheckmypy.
make checklint + typecheck + test.

Estrutura do repositório:

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

Obter ajuda


Atualizando da v2? O servidor TypeScript legado ainda é publicado no npm como opik-mcp@^2 (npx -y opik-mcp); sua fonte está na tag git legacy-typescript-final. Veja legacy/typescript/DEPRECATED.md para a política de suporte.


Licença

Apache-2.0.