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.json ou deno.jsonc existentes. 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/users e 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.json roda npm ci, pnpm-lock.yaml roda pnpm install --frozen-lockfile, yarn.lock roda yarn install, bun.lock/bun.lockb roda bun 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_modules existente 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 paths TypeScript, uma chave package.json imports ou 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 ser any atravé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.

FerramentaPropósito
search_by_intentEncontrar funções pelo que fazem — uma consulta em inglês simples correspondida às descrições de intenção
list_projectsOs projetos Carrick no seu workspace e os repositórios conectados de cada projeto
list_servicesTodo serviço que o Carrick indexou na sua organização
list_function_intentsDescrições de uma ou duas frases de funções indexadas, pesquisáveis por serviço
get_api_endpointsEndpoints declarados por um dado serviço
get_endpoint_typesTipos de requisição e resposta resolvidos para um endpoint específico
get_type_definitionTipo TypeScript totalmente resolvido por nome, em toda a organização
get_service_dependenciesServiços que chamam um dado produtor
check_compatibilitySe a chamada do serviço A ao serviço B corresponde ao contrato do produtor
scaffoldGera 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"]
}
CampoDescrição
serviceNameNome amigável para este serviço
internalEnvVarsVariáveis de ambiente apontando para outros serviços na sua organização. Chamadas são validadas contra o índice.
externalEnvVarsVariáveis de ambiente apontando para APIs de terceiros. Chamadas são ignoradas.
internalDomainsPrefixos de URL completos para serviços internos
externalDomainsPrefixos 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"
    }
  ]
}
CampoDescrição
serviceNameNome 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
directoryRaiz do serviço, relativa a carrick.json. Arquivos fora de todo diretório declarado são ignorados
includeRaízes de fonte extras para puxar na resolução de tipos/funções (ex.: bibliotecas compartilhadas copiadas no build), relativas a carrick.json
tsconfigCaminho opcional de configuração TypeScript, relativo a directory. Serviços Deno normalmente omitem este campo e usam seu manifesto Deno mais próximo
graphqlSchemasArquivos 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 internalEnvVars conta como interno, e um que lê apenas externalEnvVars conta 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

  1. O SWC analisa cada arquivo TypeScript em um AST.
  2. 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.
  3. 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.
  4. Um sidecar TypeScript resolve os tipos de requisição e resposta contra o compilador TypeScript real.
  5. Uma segunda passagem de LLM escreve a descrição de intenção por função.
  6. 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