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

@expertcustom/core-mcp

v0.3.0

Published

MCP server to connect Claude Code (and other MCP clients) to ExpertCustom Core

Downloads

452

Readme

@expertcustom/core-mcp

Servidor MCP do ExpertCustom Core. Dá a uma IA as ferramentas para operar o Core de ponta a ponta: explorar o banco do cliente, expor tabelas como resources, montar telas e ler ou gravar dados.

O consumidor previsto é a IA CORE_BUILDER do Aurora, não uma pessoa. O Aurora executa este pacote como processo filho em sandbox e injeta a credencial por variável de ambiente — não há login interativo, arquivo de configuração nem comando de CLI.

A IA enxerga exatamente o que a chave de API permite: mesmo RBAC, mesmo filtro de tenant do painel. Não existe caminho privilegiado por aqui.


Registro no Aurora

No cadastro de MCP server do Aurora, preencha:

command: npx
args:    ["-y", "@expertcustom/[email protected]"]
env:     { CORE_API_URL: "https://core-api.expertcustom.com.br", CORE_API_KEY: "..." }
timeout: 60000

Fixe a versão. Sem ela, uma publicação nova alcança todas as IAs ligadas de uma vez.

npx -y sem versão busca sempre a última publicada. Isso significa que publicar algo quebrado derruba, no mesmo instante, todas as IAs vinculadas a este MCP — sem deploy, sem aviso e sem caminho de volta que não seja publicar um patch. Com a versão fixa, a atualização vira uma decisão: altere o @0.2.0 quando quiser, uma IA de cada vez.

timeout é o do Aurora, não o do MCP: dê a ele folga sobre o CORE_TIMEOUT_MS, senão o Aurora desiste antes de o Core responder e o erro que a IA lê não diz nada útil.

Variáveis de ambiente

| Variável | Obrigatória | Padrão | O que é | | ----------------- | ----------- | -------------------------------------- | ----------------------------------------------------------------------- | | CORE_API_KEY | sim | — | Chave de API do Core. Define qual tenant a IA enxerga e o que pode fazer. | | CORE_API_URL | não | https://core-api.expertcustom.com.br | URL do Core. Só mude para apontar a um Core local em desenvolvimento. | | CORE_TIMEOUT_MS | não | 30000 | Tempo limite de cada chamada, em milissegundos. |

As três são validadas no boot: faltando a chave, ou com URL/timeout inválidos, o processo escreve a mensagem no stderr e sai com código 1. É de propósito — um MCP que sobe sem credencial só descobriria o problema no meio de uma conversa, e quem estivesse do outro lado veria "erro ao consultar" sem causa nenhuma.

A chave é emitida no painel do Core (Configurações → Chaves de API). Ela é opaca para este pacote: vai inteira no header X-API-Key, sem interpretação. Guarde-a apenas no env do MCP server — ela é uma credencial de vida longa com as permissões do usuário que a emitiu.

CORE_TIMEOUT_MS cobre com folga listagem, gravação e geração de página. Quem estoura 30s é introspect_provider num banco grande — um dos bancos reais tem 4.179 tabelas. Suba o valor (120000, por exemplo) se for operar um banco desse porte.

Instalação

Nada a instalar: o npx -y acima baixa e executa. Para desenvolver contra um Core local:

pnpm install
pnpm build

CORE_API_URL=http://localhost:5080 \
CORE_API_KEY=<chave> \
node dist/index.js

Para ligar no Claude Code, em .mcp.json:

{
  "mcpServers": {
    "core": {
      "command": "npx",
      "args": ["-y", "@expertcustom/[email protected]"],
      "env": { "CORE_API_URL": "http://localhost:5080", "CORE_API_KEY": "..." }
    }
  }
}

As tools

Providers — a conexão com o banco do cliente. list_providers, get_provider, test_provider, introspect_provider, list_tables, describe_table.

Resources — o allowlist de tabelas e colunas. Nada no banco do cliente é consultável sem um. list_resources, get_resource, create_resource_from_table, update_resource, delete_resource, update_resource_fields, resync_resource, list_relations, create_relation, delete_relation.

Apps — o sistema gerado. list_apps, get_app, create_app, update_app, delete_app, get_app_auth_config, save_app_auth_config.

Pages — as telas. list_pages, get_page, create_page_from_resource, create_page, update_page, delete_page, list_page_revisions, revert_page.

Dados — leitura e escrita no banco do cliente, sempre através do allowlist. get_data_schema, list_records, get_record, create_record, update_record, delete_record, lookup_records.

E get_status, que responde se o Core está no ar — útil como primeiro diagnóstico quando outra tool falha.

O caminho típico de construção

list_providers  →  list_tables (com search!)  →  describe_table
      →  create_resource_from_table
      →  update_resource_fields   (aplica as suggestions: máscaras, autoFill, rótulos)
      →  update_resource          (displayColumn, e as flags app* para liberar ao usuário final)
      →  create_app
      →  create_page_from_resource
      →  update_app (homePageId, isPublished)

Regras que valem em todas elas

  • O Core não executa DDL. Não há tool que crie tabela ou coluna. Se a estrutura não existe no banco do cliente, o caminho é pedir ao responsável pelo banco e depois introspect_provider.
  • Tabela sem chave primária vira resource somente leitura, permanentemente: sem PK não existe WHERE capaz de identificar uma única linha, e a API recusa habilitar update ou delete.
  • Coluna binária não entra em listagem (20 linhas seriam 20 arquivos baixados) nem aceita filtro ou ordenação. Ela continua legível registro a registro, em get_record.
  • O documento de página é uma árvore de blocos validada no servidor. Documento inválido é recusado apontando qual bloco e qual campo — as tools repassam o texto inteiro.
  • Campo com autoFill não vai no formulário e tem o valor do request descartado: quem grava é o servidor.
  • Publicar não abre dado. app.isPublished tira o sistema do ar, page.isPublished some com a tela, e quem corta acesso a DADO são as flags app* do resource. São três chaves diferentes.

Sobre as mensagens de erro

As tools traduzem a resposta do Core para algo acionável em vez de repassar o status. Um campo inexistente num filtro, por exemplo, volta assim:

O campo 'nome_cliente' não existe neste resource.
(correlationId 3d35ddbf… — cite-o ao pedir ajuda; é por ele que se acha o erro no log do servidor)
Os campos de 'clientes' que existem e são legíveis: id, nome, email, cpf_cnpj, telefone, …

A lista dos campos válidos não vem do Core — ele omite o schema nas mensagens de erro de propósito, porque a resposta é vista por quem só tem permissão de tentar. Aqui a situação é outra: quem chamou a tool já podia ler o schema inteiro. O que faltava não era permissão, era a informação no lugar certo.


Teste de fumaça

Chama cada tool contra um Core real e diz quais responderam. Não valida o conteúdo da resposta: pega o que nenhum type-check pega — rota trocada, nome de parâmetro divergente, envelope inesperado. Exercita os handlers de verdade, não HTTP cru.

pnpm build

# somente leitura
CORE_API_URL=http://localhost:5080 CORE_API_KEY=<chave> pnpm smoke

# também as tools que gravam: criam objetos próprios (prefixo `smoke-`) e os apagam no fim
CORE_API_URL=http://localhost:5080 CORE_API_KEY=<chave> pnpm smoke -- --write

Contra um Core local, dá para deixar o script pegar o token sozinho:

CORE_API_URL=http://localhost:5080 \
[email protected] \
CORE_LOGIN_PASSWORD=... \
pnpm smoke -- --write

O /api/auth/login é limitado por taxa: rodando o script várias vezes seguidas ele responde 429. Nesse caso, guarde o token da primeira resposta e passe-o como CORE_API_KEY — o Core aceita as duas credenciais, e o cliente escolhe o header pelo formato (JWT vai em Authorization: Bearer, chave de API em X-API-Key).

O relatório sai como uma linha por tool (OK / FAIL / SKIP, com tempo e motivo) e o processo sai com código 1 se qualquer tool falhar ou se alguma tool registrada não tiver sido exercitada — uma tool nova sem roteiro é uma tool que ninguém testou.

Desenvolvimento

src/
  index.ts               entrada; imprime erro de configuração sem stack trace
  config.ts              constantes (URL de produção, timeout, versão)
  auth/
    credentials.ts       loadConfig(): valida o ambiente no boot e falha alto
    client.ts            coreRequest(), apiPath e a tradução de ProblemDetails para texto útil
  server/
    server.ts            ALL_TOOLS e HANDLERS — é só aqui que se registra uma tool
    tools/
      shared.ts          leitura de argumentos; cada função existe pela mensagem que produz
      providers.ts       o modelo de estilo dos demais módulos
      resources.ts  apps.ts  pages.ts  data.ts  status.ts
scripts/
  smoke.mjs              o teste de fumaça

Uma tool nova é um XXX_TOOL (JSON Schema de entrada, additionalProperties: false) mais um runXxx(creds, args) no módulo do assunto, registrados nas duas listas de server.ts — e uma etapa no roteiro de scripts/smoke.mjs, senão o próprio teste acusa a falta.

A descrição da tool e a mensagem de erro são a interface de uso. É por elas que a IA decide o que chamar e como se corrigir; trate-as como produto, não como comentário. A descrição diz quando usar e quando não usar; o erro diz o que fazer a seguir.