Carrick
Servidor MCP hospedado que indexa codebases TypeScript entre limites de serviços e repositórios. Agentes buscam funções por intenção em vez de nome, leem os tipos reais em ambos os lados de uma chamada e veem todos os consumidores de uma rota antes de alterá-la.
Servidor MCP hospedado
npx add-mcp 'https://api.carrick.tools/mcp'Instala no Claude Code, Codex, Cursor e outros
Documentação
Carrick
Carrick é um índice ao vivo, ciente de tipos e intenções, de todos os serviços TypeScript na sua organização GitHub, exposto a agentes de IA de codificação através do Model Context Protocol.
Carrick escaneia projetos TypeScript usando npm, pnpm, Yarn, Bun ou Deno. Projetos Deno exigem Deno 2.9.4 ou mais recente; a Action fornece o runtime e lê manifestos
deno.jsonoudeno.jsoncexistentes. A preparação de dependências desativa scripts de ciclo de vida (detalhes). Recursos entre repositórios precisam de pelo menos dois serviços indexados no mesmo projeto Carrick; uma instalação de serviço único ainda recebe validação no mesmo repositório.
Comece agora: cadastre-se em app.carrick.tools · documentação completa em docs.carrick.tools
O que um agente pode perguntar
Conecte Claude Code, Cursor, Windsurf ou Codex ao endpoint MCP do Carrick. O Carrick responde perguntas semânticas sobre sua organização que um agente normalmente teria que buscar com grep entre repositórios, de forma ruim:
- "Quais funções lidam com assinatura de webhooks nos nossos serviços?"
- "Onde deduplicamos usuários por e-mail?"
- "O que chama
/api/userse qual formato de resposta eles esperam?" - "Mostre-me toda função que tenta novamente em erros de limite de taxa."
Isso funciona porque o índice combina fatos estruturais, tipos resolvidos e uma descrição por função do que o código realmente faz.
O que está no índice
Para cada função escaneada em cada repositório da sua organização, o Carrick armazena três camadas:
- Estrutural. Endpoints declarados, chamadas de saída feitas, montagens, caminhos normalizados.
- Ciente de tipos. Tipos de requisição e resposta resolvidos através do compilador TypeScript, para que a compatibilidade de tipos entre repositórios seja verificável.
- Ciente de intenções. Uma descrição de uma ou duas frases do que cada função faz, gerada no momento do escaneamento e armazenada junto com os dados estruturais e de tipos.
A camada de intenções é a diferença. É o que permite a um agente responder "onde deduplicamos usuários por e-mail" em vez de "quais funções são nomeadas dedupeUser."
Conecte seu agente
O endpoint MCP está em https://api.carrick.tools/mcp.
claude mcp add --scope user --transport http carrick https://api.carrick.tools/mcp
A autenticação recomendada é entrar-com-Carrick: seu agente abre um navegador, você clica em Aprovar uma vez, e nenhuma chave de API muda de mãos. Uma colagem manual de chave está disponível como alternativa. Para começar, cadastre-se em app.carrick.tools — o guia completo de configuração está em docs.carrick.tools.
Preencha o índice
O índice é preenchido executando a GitHub Action do Carrick em cada repositório TypeScript que você quer indexar. No branch principal, a action atualiza a contribuição daquele repositório para o índice. Em pull requests, o App Carrick posta um comentário de deriva para você (sem passos extras no workflow).
name: Carrick
on:
push:
branches: [main]
pull_request:
branches: [main]
# Lets Carrick re-trigger this repo's main scan when a sibling repo in the
# project changes. Optional today and dormant unless enabled server-side —
# included here so it's already wired if you ever turn it on.
repository_dispatch:
types: [carrick-sibling-updated]
permissions:
id-token: write
contents: read
jobs:
carrick:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Full git history lets Carrick diff against the last scan and run incrementally.
with:
fetch-depth: 0
- uses: carrick-tools/carrick@v1
Nenhum segredo é necessário. A permissão id-token: write permite que a action crie um token OIDC de curta duração do GitHub Actions, que o Carrick usa para verificar a identidade do repositório e autorizar o upload. Em pull requests, o App Carrick posta o comentário de deriva ele mesmo, então o workflow não precisa de permissões extras nem de um passo de postagem de comentário. Apenas certifique-se de que o GitHub App do Carrick está instalado na organização e que o repositório está conectado a um projeto no painel.
Pull requests abertos a partir de forks são ignorados graciosamente: o GitHub retém credenciais OIDC de execuções de fork, então a action imprime um aviso e sai com sucesso em vez de falhar a verificação. O escaneamento roda quando um mantenedor envia o branch para o próprio repositório.
Dependências
Os tipos de pacotes precisam de suas dependências no disco. Projetos Node usam node_modules instalado, e projetos Deno usam o cache de dependências do Deno. A Action prepara essas dependências antes da análise:
- Para projetos Node, a instalação roda para cada serviço que o escaneamento visitará, no lockfile mais próximo daquele serviço. Um monorepo cujos pacotes carregam seus próprios lockfiles é instalado pacote por pacote; um workspace que eleva para um lockfile na raiz é instalado uma vez. O lockfile escolhe o gerenciador:
package-lock.jsonrodanpm ci,pnpm-lock.yamlrodapnpm install --frozen-lockfile,yarn.lockrodayarn install,bun.lock/bun.lockbrodabun install. - Scripts de ciclo de vida são desativados em todos os casos, então nada no seu repositório executa durante um escaneamento.
- Cada comando de instalação tem um timeout de cinco minutos. Uma instalação falha ou com timeout imprime um aviso, e o escaneamento seguinte recusa qualquer serviço cujas dependências ainda estejam faltando (veja abaixo).
node_modulesexistente pula a instalação Node daquele serviço. Um manifesto Deno ainda dispara a preparação do cache Deno, que o cache Node separado não obtém de uma instalação Node.- Os caches de download dos gerenciadores de pacotes são restaurados entre execuções, com chave no hash de cada lockfile do qual a execução instala.
Para raízes Deno, a Action roda deno install --frozen --node-modules-dir=none.
Isso prepara o cache de dependências do Deno e impede que scripts de ciclo de vida do npm
executem mesmo quando o projeto os autoriza através de allowScripts. Uma raiz
com configuração Node e Deno prepara ambos os armazenamentos de dependências. Antes
da indexação local, instale Deno 2.9.4 ou mais recente e rode o mesmo comando de preparação
a partir da raiz do workspace Deno. Projetos que importam declarações geradas devem
gerar essas declarações através de seu build normal antes da indexação.
Um checkout não preparado é recusado
O Carrick recusa escanear um checkout que não consegue tipar, em vez de cobrar por um
índice cujos tipos são any e não dizer nada sobre o porquê. A verificação roda antes
do escaneamento começar, por serviço, no que é alcançável a partir do diretório
daquele próprio serviço — uma raiz de monorepo instalada não diz nada sobre um
workspace aninhado que não está. Duas coisas são recusadas:
- Dependências que um lockfile declara e a árvore não instalou. A
recusa nomeia o serviço e o comando exato, porque o lockfile nomeia
o gerenciador de pacotes. Uma árvore sem lockfile acima do serviço declara
nenhuma instalação e é escaneada como está. Serviços Deno são questionados apenas quando
sua configuração define
nodeModulesDir, já que o Deno de outra forma armazena em cache fora da árvore. - Um mapeamento de configuração cujo diretório alvo não está no checkout, que o
serviço importa através. Uma entrada
pathsTypeScript, uma chavepackage.jsonimportsou uma entrada de mapa de importação Deno apontando, por exemplo, para um cliente gerado cujo gerador não rodou. A recusa nomeia o mapeamento e o diretório faltante e para por aí: nada na configuração diz o que preenche um diretório gerado. Um mapeamento deixado por um pacote deletado, que nada importa, é registrado e escaneado além — nenhum tipo pode seranyatravés de um mapeamento que nenhum import usa.
Ambos são proxies, então sempre há um caminho além: --allow-unprepared no
comando, CARRICK_ALLOW_UNPREPARED=1 no ambiente, ou
allow-unprepared: true na Action (que install-dependencies: false
já implica). Um pipeline que escaneia um checkout nu deliberadamente continua
funcionando; apenas diz isso.
Serviços Deno normalmente omitem tsconfig e usam seu manifesto Deno mais próximo.
Uma configuração TypeScript ordinária explícita seleciona o caminho TypeScript. Uma
configuração Deno explícita deve nomear aquele manifesto mais próximo; deno.json tem precedência sobre
deno.jsonc quando ambos existem. Mapas de importação devem ser arquivos locais.
Desligue com:
- uses: carrick-tools/carrick@v1
with:
install-dependencies: false
Registros privados usam suas próprias credenciais. O Carrick não adiciona autenticação própria: coloque o token que seu .npmrc lê no ambiente do job e o passo de instalação o herda.
- uses: carrick-tools/carrick@v1
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
Re-analisando tudo
Um escaneamento lê o modelo uma vez por arquivo alterado e reutiliza o que já tem para
o resto, o que torna um escaneamento rotineiro barato. Ocasionalmente as respostas
em si precisam ser refeitas em vez dos arquivos: o Carrick começa a extrair
algo que não extraía antes, e o cache guarda respostas de antes de
conseguir. full-scan re-analisa cada arquivo por uma execução.
Peça, em vez de deixar ligado. Conecte ao input
workflow_dispatch do workflow, que é o que carrick init cria:
on:
workflow_dispatch:
inputs:
full-scan:
type: boolean
default: false
jobs:
carrick:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: carrick-tools/carrick@v1
with:
full-scan: ${{ inputs.full-scan }}
Então rode a partir da aba Actions, ou:
gh workflow run carrick.yml -f full-scan=true
Em qualquer outro gatilho a expressão está vazia e o escaneamento incremental roda exatamente como antes.
Um escaneamento ordinário indexa um commit uma vez por versão do scanner, então uma segunda execução em um commit inalterado não armazena nada e diz isso. Um escaneamento completo é a exceção: suas respostas substituem o que o índice guarda para aquele commit, porque re-analisar tudo é uma declaração de que as respostas armazenadas eram a parte desatualizada.
Ferramentas MCP
O endpoint MCP expõe o índice como ferramentas estruturadas que seu agente pode chamar diretamente.
| Ferramenta | Propósito |
|---|---|
search_by_intent | Encontrar funções pelo que fazem — uma consulta em inglês simples correspondida às descrições de intenção |
list_projects | Os projetos Carrick no seu workspace e os repositórios conectados de cada projeto |
list_services | Todo serviço que o Carrick indexou na sua organização |
list_function_intents | Descrições de uma ou duas frases de funções indexadas, pesquisáveis por serviço |
get_api_endpoints | Endpoints declarados por um dado serviço |
get_endpoint_types | Tipos de requisição e resposta resolvidos para um endpoint específico |
get_type_definition | Tipo TypeScript totalmente resolvido por nome, em toda a organização |
get_service_dependencies | Serviços que chamam um dado produtor |
check_compatibility | Se a chamada do serviço A ao serviço B corresponde ao contrato do produtor |
scaffold | Gera os arquivos para integrar um repositório: o workflow do GitHub Actions, um guia de agente e um esqueleto carrick.json |
Em pull requests
Em pull requests, o App Carrick posta um comentário resumindo a deriva detectada contra os serviços indexados: incompatibilidades de tipos entre produtores e consumidores, verbos HTTP incompatíveis, rotas faltantes ou órfãs e conflitos de versão de dependências npm. Ele atualiza o mesmo comentário no lugar a cada push no PR. Comentários de PR estão ativados por padrão para novos projetos e podem ser alternados por projeto no painel; execuções de PR nunca alteram o índice.
Configuração
Adicione um carrick.json a cada serviço indexado para ajudar a classificar chamadas de saída.
{
"serviceName": "order-service",
"internalEnvVars": ["USER_SERVICE_URL", "INVENTORY_API"],
"externalEnvVars": ["STRIPE_API", "GITHUB_API"],
"internalDomains": ["https://api.yourcompany.com"],
"externalDomains": ["https://api.stripe.com", "https://api.github.com"]
}
| Campo | Descrição |
|---|---|
serviceName | Nome amigável para este serviço |
internalEnvVars | Variáveis de ambiente apontando para outros serviços na sua organização. Chamadas são validadas contra o índice. |
externalEnvVars | Variáveis de ambiente apontando para APIs de terceiros. Chamadas são ignoradas. |
internalDomains | Prefixos de URL completos para serviços internos |
externalDomains | Prefixos de URL completos para APIs de terceiros a ignorar |
Quando o Carrick vê uma chamada como fetch(process.env.ORDER_SERVICE_URL + '/orders'), ele precisa saber se ORDER_SERVICE_URL aponta interna ou externamente. Variáveis de ambiente não classificadas aparecem como uma sugestão de configuração no comentário do PR.
Monorepos
carrick.json é opcional. Sem ele, o Carrick deriva serviços de manifestos de workspace npm ou pnpm; um repositório simples é um serviço. Uma configuração explícita tem precedência sobre a derivação. Para definir limites de serviço e inclusões de fonte compartilhada, declare um array services. Cada entrada é escaneada independentemente e indexada como seu próprio serviço:
{
"includes": {
"lambdas/_shared": {
"externalEnvVars": ["GITHUB_API_BASE"],
"externalDomains": ["https://api.github.com"]
}
},
"services": [
{
"serviceName": "check-or-upload",
"directory": "lambdas/check-or-upload",
"include": ["lambdas/_shared"],
"internalEnvVars": ["CARRICK_API_ENDPOINT"]
},
{
"serviceName": "dashboard",
"directory": "app",
"tsconfig": "tsconfig.json"
}
]
}
| Campo | Descrição |
|---|---|
serviceName | Nome do serviço, e a chave sob a qual o índice é escrito: contra o que as chamadas de um repositório irmão são correspondidas, e o que carrick status e as linhas hospedadas nomeiam. name é aceito como um alias dentro de uma entrada services |
directory | Raiz do serviço, relativa a carrick.json. Arquivos fora de todo diretório declarado são ignorados |
include | Raízes de fonte extras para puxar na resolução de tipos/funções (ex.: bibliotecas compartilhadas copiadas no build), relativas a carrick.json |
tsconfig | Caminho opcional de configuração TypeScript, relativo a directory. Serviços Deno normalmente omitem este campo e usam seu manifesto Deno mais próximo |
graphqlSchemas | Arquivos SDL GraphQL impressos que definem as operações que este serviço atende, relativos a carrick.json; globs são permitidos. Veja Esquemas GraphQL code-first |
Junto a services, o mapa opcional de nível superior includes declara a classificação para uma raiz de origem compartilhada uma única vez. Consulte Declarando uma raiz compartilhada uma vez. |
Cada serviço também aceita os campos de classificação de chamadas (internalEnvVars, externalEnvVars, internalDomains, externalDomains). Quando services está presente, quaisquer campos simples de nível superior irmãos são ignorados. Divergências entre serviços, conflitos de dependências e intenções duplicadas são detectados entre os serviços declarados, assim como são detectados entre repositórios.
Declarando uma raiz compartilhada uma vez
Quando vários serviços alcançam uma API de terceiros por meio do mesmo diretório compartilhado, as chamadas pertencem a esse diretório, não a cada serviço que o inclui. O mapa opcional de nível superior includes declara a classificação por raiz de origem compartilhada:
{
"includes": {
"lambdas/_shared": {
"externalEnvVars": ["GITHUB_API_BASE"],
"externalDomains": ["https://api.github.com"]
}
}
}
Cada chave é uma raiz de origem escrita como um serviço a nomeia em seu include (um ./ inicial e um / final são ignorados na correspondência). Cada valor aceita os mesmos quatro campos de classificação que um serviço aceita.
Todo serviço cujo include lista essa raiz herda essas declarações, unidas às suas próprias. Um serviço mantém tudo o que declara por conta própria, e um nome declarado em ambos os lugares aparece uma única vez. Um serviço que não inclui a raiz não herda nada.
Uma chave que nenhum serviço lista em seu include falha na verificação em vez de não fazer nada, de modo que uma raiz digitada incorretamente é relatada em vez de deixar as chamadas sem classificação.
Esquemas GraphQL code-first
O Carrick lê as operações de um servidor GraphQL a partir do SDL: arquivos .graphql/.gql no diretório do próprio serviço e literais de template gql. Um esquema construído em código (Pothos, TypeGraphQL, Nexus) não possui SDL no código-fonte, portanto suas consultas e mutações não são indexadas até que o serviço nomeie o esquema impresso:
{
"services": [
{
"serviceName": "api",
"directory": "apps/api",
"graphqlSchemas": ["apps/api/dist/schema.graphql"]
}
]
}
Cada entrada é um caminho relativo a carrick.json, ou um glob como packages/schema/generated/*.graphql. O arquivo pode estar em qualquer lugar do repositório, incluindo uma pasta de build como dist/ ou o diretório de outro aplicativo, desde que seja commitado. Cada campo Query, Mutation e Subscription que ele define é indexado como uma operação que este serviço atende. Um carrick.json simples de serviço único aceita o campo no nível superior.
Uma entrada que não corresponde a nenhum arquivo, ou um arquivo que não define nenhum campo raiz, é relatada como um aviso na saída da verificação. Quando um serviço depende de uma biblioteca GraphQL e atende rotas HTTP, mas não indexa nenhum campo de esquema GraphQL, a saída da verificação sugere essa configuração.
Uma vez que os campos do esquema são conhecidos, o Carrick também lê os módulos que constroem o esquema: arquivos que chamam por meio de um valor de builder criado a partir de uma biblioteca detectada pela verificação, incluindo módulos de campo que não exportam nada e apenas importam o builder. Cada um desses arquivos é analisado com a lista de campos do esquema, e um campo cujo resolver é encontrado ali é indexado na linha do resolver, em vez de no esquema impresso. Um campo sem resolver localizado permanece em sua linha de esquema.
Documentos GraphQL para a API de outra equipe
Os documentos GraphQL de um cliente são indexados como chamadas apenas quando são escritos contra um esquema que este repositório atende. O Carrick atribui cada documento (um arquivo .graphql/.gql, ou um template gql) ao arquivo de esquema commitado que contém seus campos raiz. Um arquivo de esquema conta como atendido quando um serviço o nomeia em graphqlSchemas, ou quando está sob o diretório de um serviço e esse serviço mostra que atende a um esquema: ele atende rotas HTTP, ou um resolver em seu código está vinculado a um dos campos do esquema. Qualquer outro arquivo de esquema commitado marca seus documentos como chamadas a uma API externa, e eles não são indexados como chamadas. Isso inclui um esquema de fornecedor baixado por uma etapa de codegen em dist/, ou uma cópia commitada dentro do próprio src/ de um aplicativo cliente. Os campos de uma cópia que está sob um serviço sem tal evidência não são indexados como operações desse serviço, e a saída da verificação nomeia o arquivo. A saída da verificação nomeia o arquivo de esquema e conta as operações, de modo que um esquema atendido aqui, mas não declarado, pode ser adicionado a graphqlSchemas.
Alguns documentos também são deixados de fora:
- um documento cujos campos nenhum esquema único contém;
- um documento cujos campos estão tanto em um esquema atendido quanto em um externo, quando as leituras de ambiente do arquivo não resolvem isso. Um arquivo que lê apenas variáveis de
internalEnvVarsconta como interno, e um que lê apenasexternalEnvVarsconta como externo.
Um documento cujos campos não aparecem em nenhum esquema que o repositório contém permanece como uma chamada, porque seu servidor pode ser outro repositório no projeto.
Onde uma chamada GraphQL é indexada
Um documento escrito em um arquivo .graphql/.gql e compilado em uma declaração tipada (OrdersDocument) é enviado a partir do código que passa essa declaração a um cliente, como useQuery(OrdersDocument). O Carrick indexa os campos da operação em cada uma dessas chamadas, resolvendo a importação por meio de caminhos relativos, aliases de caminho tsconfig e pacotes de workspace. Uma operação que nenhuma chamada executa permanece indexada em sua linha no arquivo de documento. Um template gql escrito no código-fonte permanece indexado onde é escrito.
Como funciona
- O SWC analisa cada arquivo TypeScript em um AST.
- Uma passagem de análise estática extrai exportações de funções, roteadores montados, chamadas HTTP com correspondência de padrões, esquemas e operações GraphQL e contratos de eventos WebSocket.
- Um agente de LLM lida com os casos que a correspondência de padrões não alcança: URLs dinâmicas, funções de fábrica, roteamento específico de frameworks.
- Um sidecar TypeScript resolve os tipos de requisição e resposta contra o compilador TypeScript real.
- Uma segunda passagem de LLM escreve a descrição de intenção por função.
- O índice da organização vive no DynamoDB e no S3 e é atualizado cada vez que o branch principal de um serviço é executado.
Licença
Licença Elastic 2.0. Copyright (c) 2026 Far Harbour B.V.
Desenvolvimento
Consulte AGENTS.md para convenções de build, teste e contribuição.
cargo test
cargo fmt
cargo clippy
Instale o hook de pré-commit opcional para executar formatação e testes antes de cada commit:
./scripts/install-hooks.sh