npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zt-playground/mcp-server

v3.0.0

Published

MCP server for ZT Playground — a workflow layer over issues, cycles, GitHub, monitoring/APM and analytics, plus guarded database/SSH dev-ops tooling.

Readme

ZT Playground — MCP Server

Servidor MCP que dá a um agente de IA (Claude, Cursor, Codex, …) acesso ao ZT Playground: issues, cycles, GitHub, monitoramento/APM, analytics e saved views — além de ferramentas guardadas de banco de dados e SSH.

v2.1 — camada de fluxo (my_work, issue_brief, start_work, submit_work, triage_inbox, standup), projeto padrão vindo do context (quase nada mais exige projectId), pessoas resolvidas por nome, e as tools de repositório local removidas por serem piores que o Read/Grep/Bash do próprio agente.

v2 — reescrita com foco em segurança, cobertura completa da API e boas práticas de MCP (tool annotations, paginação, redação de segredos, audit log).


Arquitetura

O servidor fala com três backends distintos:

| Backend | Como | Para quê | | --- | --- | --- | | HTTP API (X-ZT-API-Key) | https://zt-playground-api.vercel.app | Domínio do produto: projetos, issues, cycles, monitor, github, analytics, saved views | | Postgres (pg) | conexão direta configurada no context | Console SQL (leitura por padrão; escrita com confirmação) | | SSH (ssh2) | servidores do context | Logs, status de serviço e comandos em máquinas remotas |

Os caminhos de Postgres/SSH ignoram a validação e auditoria da API — por isso são protegidos por sessões read-only, denylist de comandos, verificação de host-key e um audit log local (veja Segurança).

Repositórios locais não estão aqui de propósito. O agente que hospeda este servidor já lê, busca e roda comandos na árvore de trabalho com o modelo de permissão dele; duplicar isso por MCP só entregava uma versão pior (truncada, sem contexto) e ainda contornava a aprovação do host. Para código, use as ferramentas nativas do agente.


Instalação

Pré-requisitos

  • Node.js v18+
  • Uma API Key do ZT Playground (gere em Configurações → API Keys)

Setup automático (recomendado)

O assistente detecta seus clientes MCP instalados (Claude Desktop, Cursor, VS Code, Windsurf, Codex) e adiciona a configuração:

npx @zt-playground/mcp-server --setup --key SUA_API_KEY

Configuração manual

Adicione ao ~/.claude/settings.json (global) ou .mcp.json (por projeto):

{
  "mcpServers": {
    "zt-playground": {
      "command": "npx",
      "args": ["@zt-playground/mcp-server"],
      "env": { "ZT_API_KEY": "SUA_API_KEY" }
    }
  }
}

Ou, se for rodar a partir do código clonado:

npm install && npm run build
# aponte "args" para ["/caminho/para/zt-mcp-server/dist/index.js"] com "command": "node"

Requisito de versão da API

As tools de fluxo dependem de campos que entraram na zt-playground-api junto com esta versão (GET /tasks/by-ref, filtro por responsável, status com lista, cycleId/storyPoints na criação). Contra uma API mais antiga o servidor continua funcionando: ele detecta a limitação, refaz a consulta de outro jeito e devolve note + appliedLocally dizendo o que foi filtrado no cliente. Assim que a API for reimplantada, os mesmos comandos passam a ser exatos e mais baratos.

Autenticação

Você pode autenticar de três formas (ordem de prioridade):

  1. Variável de ambiente ZT_API_KEY (e opcional ZT_API_URL).
  2. Ferramenta auth_setup dentro de uma conversa (salva a chave em ~/.zt-playground/auth.json, 0600).
  3. --setup --key ... no CLI.

| Variável | Descrição | Obrigatório | | --- | --- | --- | | ZT_API_KEY | API Key do ZT Playground | Sim* | | ZT_API_URL | URL da API (default: produção) | Não | | ZT_MCP_TOOLSET | full (default) ou core | Não | | ZT_MCP_HTTP_PORT | Sobe no transporte Streamable HTTP nessa porta (sem ela: stdio) | Não | | ZT_MCP_HTTP_HOST | Interface do transporte HTTP (default 127.0.0.1) | Não | | ZT_MCP_LIST_TTL_MS | TTL do cache de tools/list (default 3600000) | Não | | ZT_MCP_STRUCTURED | both (default) ou structured — ver abaixo | Não | | ZT_MCP_MONITOR_DSN | Liga a telemetria de tool calls (ver abaixo). Sem ela, nada é enviado | Não | | ZT_MCP_MONITOR_TIMEOUT_MS | Teto de um envio de telemetria (default 2000) | Não | | ZT_MCP_MONITOR_BATCH | Quantas chamadas o stdio junta antes de enviar (default 20) | Não |

* Não é obrigatório no boot — o servidor sobe sem chave e você pode autenticar via auth_setup.

Toolset core

tools/list é o único payload que toda sessão paga antes de fazer qualquer coisa. Com as 94 tools ele custa ~17,6 k tokens; ZT_MCP_TOOLSET=core registra as ~33 do dia a dia e cai para ~7,7 k — 56 % a menos, todo request.

O core cobre: as 6 tools de workflow (my_work, issue_brief, start_work, submit_work, triage_inbox, standup), issues (create/get/update/search), comentários, relações, ciclos, board, global_search, whoami, contexto, monitor (overview/list/get/create-issue), GitHub (overview/PRs/detail/create) e db_query/db_schema.

Fora do core ficam as tools de administração e de relatório — alertas, web-vitals, labels, anexos, views salvas, SSH, analytics de issues, épicos. Precisa de uma delas? ZT_MCP_TOOLSET=full (ou simplesmente não defina a variável) devolve tudo.

{
  "mcpServers": {
    "zt-playground": {
      "command": "npx",
      "args": ["-y", "@zt-playground/mcp-server"],
      "env": { "ZT_API_KEY": "SUA_API_KEY", "ZT_MCP_TOOLSET": "core" }
    }
  }
}

verbosity nas leituras pesadas

monitor_get_error, monitor_get_error_events, github_get_pr_detail, get_issue e search_issues aceitam verbosity: 'concise' | 'detailed' (default concise). O modo conciso projeta a resposta — stack cortado nos 10 primeiros frames, sem rawStackTrace/breadcrumbs/extra, PR sem a lista de arquivos — e diz o que ficou de fora. Medido contra dados reais: −93 % em monitor_get_error, −52 % em search_issues. Peça detailed quando a resposta curta não bastar.

Transporte Streamable HTTP

O default continua sendo stdio e não mudou. Com --http (ou ZT_MCP_HTTP_PORT) o mesmo servidor sobe em Streamable HTTP:

zt-playground-mcp --http 3005          # ou: ZT_MCP_HTTP_PORT=3005 zt-playground-mcp
  • MCP em POST /mcp; GET /health responde {"ok":true}.
  • A chave vem do request, não do disco. Mande X-ZT-API-Key (ou Authorization: Bearer …) em cada chamada; sem ela é 401. O ~/.zt-playground/auth.json continua sendo o caminho do stdio e não é lido nem escrito aqui — auth_setup recusa por HTTP.
  • Sem sessão: um servidor novo por request, nada guardado entre eles. É o que a revisão 2026-07-28 pede e o que torna isso publicável como função serverless — não existe sessão para prender um chamador a uma instância.
  • Um processo atende várias chaves ao mesmo tempo, cada uma com a sua identidade (assignee: "me" resolve por chamador).
curl -s http://127.0.0.1:3005/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -H 'X-ZT-API-Key: SUA_API_KEY' \
  -H 'Mcp-Method: tools/list' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

OAuth (o fluxo de autorização da spec nova) não está implementado — a chave por header é a única autenticação do transporte HTTP.

Cache de tools/list e outputSchema

Num cliente que fala 2026-07-28, o resultado de tools/list sai com ttlMs (1 h por default) e cacheScope: "public": o catálogo só muda quando o build ou o ZT_MCP_TOOLSET mudam, então não há motivo para reenviá-lo a cada reconexão. Requests 2025 recebem exatamente o que recebiam antes.

As nove tools de resposta estável (as 6 de workflow + get_issue, search_issues, get_board) declaram outputSchema e respondem também em structuredContent — JSON nativo, em vez de JSON escapado dentro de um bloco de texto. O default (both) manda os dois, que é o que a spec pede para clientes que só leem content. Se todos os seus clientes leem structuredContent, ZT_MCP_STRUCTURED=structured derruba a cópia em texto e a resposta fica menor do que era antes de existir schema.

Telemetria de tool calls (opt-in)

ZT_MCP_MONITOR_DSN liga o envio de cada chamada de tool para um projeto do ZT Monitor, pelo mesmo ingest que o SDK do browser usa:

ZT_MCP_MONITOR_DSN=https://<publicKey>@<host>/monitor/ingest/<projectId>

Cada chamada vira uma transação CUSTOM chamada mcp.tool/<nome>, com duração, bytes da resposta, sucesso/erro, toolset, transporte, a verbosity pedida e o projeto. Erro inesperado (5xx da API ou exceção não prevista) vira também um evento de erro; um 404 não — ele é o chamador errando, não o servidor.

Três garantias:

  • Sem a variável, nada acontece. Nenhuma alocação, nenhuma resolução de host, nenhum caminho novo.
  • Nenhuma falha ou lentidão de telemetria atrasa ou quebra uma tool. Medido: overhead p50 abaixo do ruído (−0,006 ms em 3.000 chamadas), p95 +0,13 ms. Com DSN morto, ou com um ingest que trava 3 s, a tool responde igual e na mesma hora.
  • Nada de setInterval como caminho principal. O envio começa dentro da chamada que o gerou. Sob HTTP ele vai para o waitUntil da plataforma quando existe, e senão ganha uma janela curta antes de a resposta sair — porque uma função serverless congela e um flush por timer nunca aconteceria. No stdio, onde o processo vive de verdade, as amostras esperam companhia (ZT_MCP_MONITOR_BATCH).

Aponte para um projeto próprio, ou leia os painéis com ?platform=mcp-server. getPerformanceOverview não filtra por tipo, então sem isso a latência de um standup entra no p95 das suas páginas web.


Fluxo de trabalho

Seis ferramentas respondem, cada uma em uma chamada, as perguntas com que um dia de trabalho começa e termina. Elas não acrescentam poder novo — compõem os mesmos endpoints, evitando que o agente monte a sequência errada.

| Ferramenta | Responde | | --- | --- | | my_work | O que eu pego agora — suas issues abertas por status, ordenadas por prioridade, com bloqueios sinalizados, o cycle ativo e o backlog sem dono | | issue_brief | Tudo sobre a ZT-42 — descrição, cycle, épico/sub-issues, relações com bloqueios em aberto, comentários recentes, repositório e nome de branch, atividade no git, issues vizinhas da mesma área | | start_work | Assume a issue: recusa se ainda houver bloqueio aberto, atribui a você, move para IN_PROGRESS e cria a branch no repositório certo | | submit_work | Abre o PR ligado à issue (Closes ZT-N → IN_REVIEW agora, DONE no merge) e comenta seu resumo na issue | | triage_inbox | O que espera decisão — issues sem dono por prioridade e erros do monitor ainda não virados em issue, cada linha com a chamada que resolve | | standup | O que andou e o que está em risco — entregue, em andamento por pessoa, bloqueado, bugs novos e o cycle com dias restantes |

O repositório de cada issue é inferido do campo repository dela contra os repos ligados ao projeto; quando é ambíguo, a ferramenta diz por quê e pede repoId.


Catálogo de ferramentas (94)

Todas as ferramentas trazem annotations (readOnlyHint/destructiveHint/idempotentHint) para o cliente exibir o risco, retornam structuredContent e têm o output limitado em tamanho.

Projeto padrão: projectId é opcional em todas as ferramentas de produto. Sem ele, vale o projeto do context ativo — e, se a workspace tiver um projeto só, esse. Passe explicitamente apenas para sair do padrão.

ZT-refs: onde se recebe uma issue, vale o id cru, ZT-42 ou 42.

Pessoas: assignee aceita nome ("Vinicius"), e-mail, id ou "me" — e "none" para trabalho sem dono.

Fluxo — my_work, issue_brief, start_work, submit_work, triage_inbox, standup

Auth & workspace — auth_setup, auth_status, whoami, list_users, get_dashboard, global_search

Projetos — list_projects, get_platforms

Issues — create_issue (com cycleId/storyPoints), get_issue, update_issue, search_issues (filtros, ordenação e paginação), delete_issue, report_bug, create_epic

Comentários / anexos / labels / relações — add_comment, get_comments, add_attachment, list_attachments, manage_labels, link_issues, unlink_issues, get_relations

Cycles (rotas em /sprints, modelo renomeado para Cycle) — list_cycles, create_cycle, get_cycle, update_cycle, complete_cycle, delete_cycle, assign_issues_to_cycle, remove_issue_from_cycle, get_cycle_metrics (burndown)

Saved views — view_list, view_create, view_get, view_update, view_delete, view_set_default

Atividade / board — get_project_activity, get_issue_activity, get_board

Issue analytics — get_project_health, get_issue_overview, get_issues_by_platform, get_issue_trends, get_area_analysis, get_repository_report

Monitor / APM — monitor_overview, monitor_list_errors, monitor_get_error, monitor_get_error_events, monitor_update_error, monitor_create_issue_from_error, monitor_performance, monitor_web_vitals, monitor_endpoints, monitor_transactions, monitor_error_platforms, monitor_list_alerts, monitor_create_alert, monitor_update_alert, monitor_delete_alert

GitHub — github_overview, github_list_repos, github_list_branches, github_list_commits, github_list_deployments, github_issue_git_activity, github_list_prs, github_get_pr_detail, github_get_pr_checks, github_create_branch, github_create_pr, github_merge_pr (gated por CI), github_pr_review, github_sync_repo

Context (infra local) — context_setup, context_get, context_list, context_set_active, context_remove

Banco de dados — db_query (read-only), db_execute → db_execute_confirm (write com confirmação), db_schema

SSH — ssh_exec, ssh_logs, ssh_status

Resources (contexto passivo por URI)

  • project://{id} · issue://{id}
  • cycle://current/{projectId} — o cycle ATIVO + burndown
  • health://{projectId} — saúde de issues + erros do monitor + p95
  • monitor://{projectId}/overview

Prompts (workflows reutilizáveis)

triage-error, daily-standup, cycle-review, release-notes, incident-review


Configurando o context (projeto padrão / DB / SSH)

O context define o projeto padrão de todas as ferramentas e os alvos de banco/SSH. Crie com context_setup:

{
  "projectId": "<id-do-projeto>",
  "projectName": "sinapsys",
  "databases": [
    { "name": "neon", "connectionString": "postgres://...", "readOnly": true }
  ],
  "ssh": [
    { "name": "prod", "host": "1.2.3.4", "user": "deploy", "keyPath": "~/.ssh/id_ed25519", "readOnly": true }
  ]
}

O context é salvo em ~/.zt-playground/contexts/*.json (0600). O context ativo persiste entre sessões (~/.zt-playground/active.json) e, havendo apenas um, é selecionado automaticamente.


Segurança

  • db_query é read-only de verdade: roda dentro de BEGIN TRANSACTION READ ONLY com statement_timeout, então escritas são rejeitadas pelo banco mesmo via multi-statement. Escritas exigem db_execute → db_execute_confirm (em transação, com rollback no erro) e DDL destrutivo é sinalizado.
  • Sem shell injection: ssh_logs faz escape dos argumentos antes de montar o comando remoto.
  • Denylist de comandos: ssh_exec bloqueia padrões catastróficos (rm -rf, force-push, curl | sh, fork bomb, shutdown, …) salvo allowDangerous: true. Servidores podem ser marcados readOnly.
  • Sem execução de código local: as tools de repositório local foram removidas na v2.1 — nenhum caminho deste servidor roda comando na sua máquina.
  • Verificação de host-key SSH: trust-on-first-use com detecção de mudança de chave (MITM); pino opcional via pinnedHostKey.
  • github_merge_pr exige CI verde (a menos de force: true) e registra a operação.
  • Segredos redigidos: context_get mascara senhas de DB e SSH.
  • Audit log: SQL de escrita, ssh_exec, merges e reviews são anexados em ~/.zt-playground/audit.log (0600).
  • Timeout/caps: todas as chamadas HTTP têm timeout; outputs grandes (stack traces, diffs, dumps) são truncados com marcador explícito.

Desenvolvimento

npm run dev      # roda via tsx (sem build)
npm run build    # compila para dist/
npm test         # build + testes dos helpers puros (node --test, sem dependências)
npm run setup    # assistente de configuração (precisa de build)

Publicando no npm

O token do npm não fica no ~/.npmrc. Ele mora em /etc/zt-secrets/npm-token, root:root 0600 — nenhum processo seu (nem um agente rodando na sua sessão) lê aquele arquivo sem a senha do PC.

Instalação, uma vez só:

sudo ./scripts/install-zt-publish.sh   # pede o token, cola sem ecoar

A partir daí:

zt-publish /caminho/do/pacote     # pede a senha do PC e publica
zt-publish . --dry-run            # valida o tarball sem enviar
zt-publish . --tag beta           # publica fora do latest
zt-publish --check-vault          # confere cofre e token, sem publicar nada

Vale para qualquer pacote do ZT, não só este — aponte o diretório.

Por que assim. O portão é uma linha: o sudo -k cat do token. O -k invalida o cache do sudo, então toda publicação pede a senha, não só a primeira dos últimos 15 minutos. O zt-publish é instalado root-owned de propósito: se fosse gravável por você, qualquer processo local poderia reescrevê-lo e você digitaria a senha para um script trocado. O único passo privilegiado é ler o token — o resto roda sem privilégio.

O token nunca entra no ~/.npmrc, nunca aparece em argv (logo não vaza no ps nem no histórico), nunca é impresso, e o npmrc temporário nasce em diretório 0700 e é apagado por trap mesmo se o publish falhar.

Antes de enviar, o comando recusa árvore git com alterações não commitadas, versão que já existe no registry, e build/test vermelhos. --skip-checks ignora esses trilhos.

O que isso não protege: qualquer coisa rodando como root, e você digitando a senha num prompt que não conferiu. Os dois scripts em scripts/ são curtos de propósito — leia antes de instalar.


Compatibilidade

Funciona com Claude Code/Desktop, Cursor, VS Code, Windsurf e OpenAI Codex (o --setup configura todos os detectados). O transporte default é stdio; logs vão para stderr. Streamable HTTP é opcional (--http) — ver acima.

Construído sobre @modelcontextprotocol/server 2.x, que atende as revisões 2025-11-25 e 2026-07-28 com o mesmo registro de tools: um cliente antigo não vê diferença nenhuma, e um cliente novo ganha as dicas de cache e o structuredContent. Requer Node 20+.