Comet Opik
oficialConsulte 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
listeread. -
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
schemapara 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. Substituanpx -y opik-mcpporuvx opik-mcp@latestna 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
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 emcomet.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
defaulte 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).
| Ferramenta | Finalidade |
|---|---|
read | Leitura universal por id / nome / URI opik:// |
list | Listagem universal com filtro opcional por nome + paginação |
write | Escrita universal — registra traces/spans, pontua, comenta, salva prompts, gerencia datasets e experimentos |
schema | Inspeciona esquemas de operações de escrita (usado pelo LLM para construir payloads válidos) |
read_skill | Lê 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ção | O que faz |
|---|---|
trace.create | Registra um único trace (ou um lote). Pai para spans / scores / comentários. |
trace.update | Finaliza ou altera um trace existente. |
span.create | Registra um span em um trace existente (ou um lote). |
score.create | Anexa uma pontuação de feedback numérica a um trace, span ou thread. |
comment.create | Anexa um comentário de texto livre a um trace, span ou thread. |
prompt_version.save | Salva uma nova versão de prompt (cria o prompt por nome se ausente). |
dataset.create | Cria um dataset — type: "test_suite" o torna um conjunto de teste de avaliação. |
dataset_item.upsert | Faz upsert de itens em um dataset (sempre a forma de envelope). |
experiment.create | Cria um experimento com escopo em um dataset. |
experiment_item.create | Anexa linhas de trace + dataset_item a um experimento. |
thread.close | Fecha um thread (marca como inativo). Passe thread_id e o projeto. |
thread.open | Reabre um thread fechado. Passe thread_id e o projeto. |
agent_insights_job.enable | Ativa Diagnostics para um projeto (varreduras diárias, seguro repetir). |
agent_insights_job.trigger | Executa uma varredura Diagnostics agora, nas últimas 24 horas. |
agent_insights_issue.resolve | Marca um problema Diagnostics como resolvido (pergunte ao usuário primeiro). |
agent_insights_issue.close | Marca um problema Diagnostics como não vale a pena agir (pergunte ao usuário primeiro). |
agent_insights_issue.reopen | Coloca 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ável | Padrão | Observações |
|---|---|---|
OPIK_API_KEY | — | Obrigatório para qualquer leitura/escrita autenticada. |
OPIK_WORKSPACE | não definido | Nome 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_ID | não definido | UUID 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_OVERRIDE | https://www.comet.com | Defina para o seu host Comet auto-hospedado, ou https://dev.comet.com para staging. |
OPIK_URL | derivado de COMET_URL_OVERRIDE + /opik/api | Substitua apenas se o Opik estiver em um host/caminho diferente da interface do Comet. |
OPIK_DEFAULT_PROJECT_NAME | não definido | Quando 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ável | Padrão | Observações |
|---|---|---|
OPIK_MCP_TRANSPORT | stdio | stdio para iniciado pelo host, streamable-http para escutar em uma porta. |
OPIK_MCP_HOST | 127.0.0.1 | Host de bind do uvicorn (somente streamable-http). |
OPIK_MCP_PORT | 8080 | Porta de bind do uvicorn (somente streamable-http). |
OPIK_MCP_RELOAD | false | true para habilitar --reload do uvicorn (somente desenvolvimento). |
OPIK_MCP_AS_URL | não definido | URL 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_URI | não definido | URI 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_S | 30 | Por 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_LEVEL | INFO | Limite 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ário | Transporte |
|---|---|
| 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-backend | HTTP — 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ável | Padrão | Observações |
|---|---|---|
OPIK_MCP_ANALYTICS_ENABLED | true | Defina para false para desativar toda a telemetria. |
OPIK_MCP_ANALYTICS_URL | https://stats.comet.com/notify/event/ | Substituição para staging. |
OPIK_MCP_ANALYTICS_ENVIRONMENT | prod | Tag em cada evento (prod / staging / dev). |
OPIK_MCP_ANALYTICS_SOURCE | comet.com | O 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_S | 5.0 | Timeout de conexão HTTP. |
OPIK_MCP_ANALYTICS_TOTAL_TIMEOUT_S | 10.0 | Timeout 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_TIMEOUTlimita 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:
| Alvo | O que faz |
|---|---|
make install | uv sync --locked --extra dev |
make run | Executa o servidor MCP (stdio por padrão). |
make run-dev | Executa com logging DEBUG + uvicorn --reload. |
make dev | Executa via mcp dev (wrapper de modo dev do Inspector). |
make inspect | Inicia o MCP Inspector contra um servidor em execução. |
make test | uv run pytest -q. |
make lint | ruff check + verificação de formatação. |
make format | ruff format + ruff check --fix. |
make typecheck | mypy. |
make check | lint + 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
- Abra um problema para bugs e solicitações de recursos
- Documentação do Opik para documentação do SDK / backend
- Slack da comunidade Comet para perguntas
Atualizando da v2? O servidor TypeScript legado ainda é publicado no npm como
opik-mcp@^2(npx -y opik-mcp); sua fonte está na tag gitlegacy-typescript-final. Vejalegacy/typescript/DEPRECATED.mdpara a política de suporte.
Licença
Apache-2.0.