Reference

MCP tools

Reference for every tool and resource the Carrick MCP server exposes at api.carrick.tools/mcp.

The Carrick MCP server exposes your indexed services as structured tools and resources. The endpoint is https://api.carrick.tools/mcp — one URL for everything. Your token is scoped to a workspace, and each tool call resolves to exactly one project inside it.

Tools are listed below in the order they typically fire during a coding session.

Every JSON data response carries a top-level carrick_version (the server build), omitted from the examples below for brevity. check_compatibility and get_service_graph additionally carry a matcher_version (the match-code release), shown in their examples because matcher-versus-scan version skew is worth watching.

Project scoping

A workspace can hold several projects (one project = one interconnected system of repos). Every data tool accepts two optional arguments that pick which project a call queries:

NameTypeNotes
projectstringCarrick project slug to query.
repostringYour repo as owner/repo (from the git remote) or just the repo name. Carrick resolves which project it belongs to.

Pass one of them on every call — a repo’s carrick.md usually pins the right project slug. When the workspace has a single project, both can be omitted. If a call can’t be resolved to exactly one project, the tool returns a short teaching message telling the agent what to pass; list_projects shows the options.

These two parameters are not repeated in the tables below.

Intent layer

search_by_intent(query, top_k?, similarity_threshold?)

Semantic search across every function in the project, ranked by how closely each matches your query.

Reach for it when the agent is asking a concept question (“verify a webhook signature”, “dedupe users by email”) where keywords do not help. It searches the whole project, not a sampled subset.

Parameters:

NameTypeRequiredDefaultNotes
querystringyesPlain-English description of what the function should do.
top_knumberno8How many matches to return (max 50).
similarity_thresholdnumberno0.3Score floor for inclusion. Raise to filter weak matches; lower to widen results.

Response shape:

{
  "query": "verify a webhook signature",
  "top_k": 8,
  "similarity_threshold": 0.3,
  "total_embedded_scanned": 1247,
  "total_above_threshold": 4,
  "results": [
    {
      "service": "billing",
      "repo": "billing-service",
      "name": "verifyStripeWebhook",
      "file_path": "src/webhooks/stripe.ts",
      "line_number": 24,
      "intent": "Verifies a Stripe webhook signature using the signing secret and rejects requests with mismatched HMACs.",
      "similarity": 0.82
    }
  ]
}

list_function_intents(service?, exclude_service?, limit?, offset?, typed_only?, name_contains?, intent_contains?)

Lists indexed functions alongside their LLM-generated intents. Useful for browsing a service’s surface area, or for comparing what sibling services have implemented.

Prefer search_by_intent for concept queries. Use this when you want to scan a whole service end to end, or to diff implementations across two services. Results are paginated: the full haystack runs to thousands of functions, so each call returns one page and a next_offset to fetch the next.

Parameters:

NameTypeRequiredDefaultNotes
servicestringnoRestrict to one service.
exclude_servicestringnoRestrict to everything except one service.
limitnumberno50Max functions per page (max 200).
offsetnumberno0Functions to skip before this page. Pass a previous response’s next_offset to page through.
typed_onlybooleannofalseOnly functions whose signature is fully explicit (every param and the return annotated).
name_containsstringnoCase-insensitive substring filter on the function name.
intent_containsstringnoCase-insensitive substring filter on the intent text.

Response shape:

{
  "total": 1247,
  "returned": 50,
  "offset": 0,
  "limit": 50,
  "has_more": true,
  "next_offset": 50,
  "services": ["billing", "checkout", "inventory"],
  "functions": [
    {
      "service": "billing",
      "repo": "billing-service",
      "name": "verifyStripeWebhook",
      "file_path": "src/webhooks/stripe.ts",
      "line_number": 24,
      "intent": "Verifies a Stripe webhook signature using the signing secret and rejects requests with mismatched HMACs.",
      "typed": true,
      "reason": ""
    }
  ]
}

total counts the filtered set before pagination. typed is true when every parameter and the return are explicitly annotated; otherwise reason names the inferred positions (e.g. param `role` and return are inferred). Fully scanned signatures also carry signature and a per-position types_explicit map.

Structural and type layer

list_projects()

Lists the projects in your workspace and each project’s connected repos. Call it when you don’t know which project slug or repo name to pass to the other tools. Cheap — it reads workspace metadata only, no scan data. Takes no parameters (not even project/repo).

Response shape:

{
  "projects": [
    {
      "slug": "storefront",
      "display_name": "Storefront",
      "repos": ["acme/billing-service", "acme/checkout"]
    }
  ],
  "usage": "Pass `project: \"<slug>\"` (or `repo: \"<owner/repo>\"`) on the other Carrick tools to query that system."
}

list_services()

Catalogue of every service in the project’s index. Cheap. Call this first when orienting.

Response shape:

{
  "services": [
    {
      "repo_name": "billing-service",
      "service_name": "billing",
      "endpoint_count": 14,
      "call_count": 9,
      "last_updated": "2026-05-24T19:02:11Z",
      "commit_hash": "a3f1c9d",
      "has_types": true
    }
  ]
}

endpoint_count and call_count count operations across every protocol, the same way get_api_endpoints lists them.

get_service_graph(service?)

The whole cross-service call graph in one response: consumer_service -> producer_service edges across every protocol, plus the calls that matched nothing (unmatched_calls) and the endpoints nobody calls (orphaned_endpoints). Reach for it first when orienting on topology, who depends on a service and what a change ripples into, instead of fanning out check_compatibility across every pair. For one pair in depth, use check_compatibility.

Edges never carry type verdicts: the graph answers who-calls-whom, and check_compatibility is where type agreement is decided.

Parameters:

NameTypeRequiredNotes
servicestringnoReturn only edges that touch this service (as consumer or producer), plus its unmatched calls and orphaned endpoints. Omit for the whole-project graph.

Response shape:

{
  "services": [
    { "service": "checkout", "repo_name": "checkout", "endpoint_count": 8, "call_count": 6 }
  ],
  "edges": [
    {
      "consumer_service": "checkout",
      "producer_service": "billing",
      "protocol": "http",
      "method": "POST",
      "path": "/api/v1/invoices",
      "call_file_location": "src/billing/client.ts:31",
      "matched": true
    }
  ],
  "unmatched_calls": [],
  "orphaned_endpoints": [],
  "edge_count": 1,
  "unmatched_count": 0,
  "orphaned_count": 0,
  "matcher_version": "0.3.1"
}

Every array is capped at 200 rows; the *_count fields are the pre-truncation totals, and per-array flags (truncated for edges, unmatched_truncated, orphaned_truncated) plus a note tell you when an array was capped, so you can pass service to scope down. When non-HTTP edges are present, a legend explains each protocol’s arrow direction (for pub/sub the edge points publisher to subscriber).

get_api_endpoints(service, method?, path_contains?)

The operations a service exposes, across protocols: HTTP endpoints (GET /api/users/:id), GraphQL fields (query user), socket events (chat:message (client→server)), and pub/sub topics (orders.created (pub/sub)). Returns one row per operation; HTTP rows are mount-aware, with fully-resolved paths.

Reach for it before writing client code that calls a service, or before changing one of its routes.

Parameters:

NameTypeRequiredNotes
servicestringyesThe service to inspect (fuzzy match by repo name, service name, or trailing segment).
methodstringnoFilter by operation label: HTTP method (GET, POST, …), GraphQL kind (QUERY, MUTATION, SUBSCRIPTION), socket direction (CLIENT->SERVER, SERVER->CLIENT), or PUBSUB for pub/sub topics.
path_containsstringnoSubstring filter on the path, GraphQL field, event name, or topic.

Response shape:

{
  "service": "billing",
  "endpoint_count": 1,
  "endpoints": [
    {
      "protocol": "http",
      "operation": "POST /api/v1/invoices",
      "method": "POST",
      "full_path": "/api/v1/invoices",
      "handler": "createInvoice",
      "owner": "billingRouter",
      "file_location": "src/routes/invoices.ts:42"
    }
  ]
}

For GraphQL, socket, and pub/sub rows, method/full_path carry the operation’s label pair instead — (QUERY, user), (CLIENT->SERVER, chat:message), or (PUBSUB, orders.created).

get_endpoint_types(service, method, path)

Resolved TypeScript request and response types for a single HTTP operation. Each entry tells you whether the type was explicitly annotated or inferred from the handler body, and includes the source location.

Reach for it before building a request body or parsing a response. Guessing JSON shapes from a sample is the most common source of contract drift.

Non-HTTP operations are type-checked too, but their type surface is not rendered here. Pass a GraphQL, socket, or pub/sub operation and the tool points you to check_compatibility for its per-pair verdict rather than returning type text.

Parameters:

NameTypeRequiredNotes
servicestringyesThe service exposing the endpoint.
methodstringyesHTTP method. A non-HTTP label (QUERY, MUTATION, CLIENT->SERVER, PUBSUB) is accepted but returns the redirect described above rather than type text.
pathstringyesAPI path (matched against the service’s mount graph). A GraphQL field, socket event, or pub/sub topic is accepted but triggers the redirect.

Response shape:

{
  "service": "billing",
  "method": "POST",
  "path": "/api/v1/invoices",
  "types": [
    {
      "type_alias": "CreateInvoiceRequest",
      "type_kind": "request_body",
      "is_explicit": true,
      "source_file": "src/types/invoices.ts",
      "source_line": 12,
      "definition": "{ customer_id: string; amount_cents: number; currency: string }"
    },
    {
      "type_alias": "Invoice",
      "type_kind": "response_body",
      "is_explicit": false,
      "source_file": "src/routes/invoices.ts",
      "source_line": 58,
      "definition": "{ id: string; status: 'open' | 'paid'; amount_cents: number }"
    }
  ]
}

get_type_definition(service, type_alias)

Fully resolved definition for one named type, with transitive dependencies expanded. Use it when get_endpoint_types references a named DTO, discriminated union, or composed type whose shape you need to read.

Parameters:

NameTypeRequired
servicestringyes
type_aliasstringyes

Response shape:

{
  "service": "billing",
  "type_alias": "Invoice",
  "definition": "{ id: string; status: InvoiceStatus; amount_cents: number; line_items: LineItem[] }",
  "expanded": "{ id: string; status: 'open' | 'paid' | 'void'; amount_cents: number; line_items: { sku: string; quantity: number; unit_price_cents: number }[] }"
}

check_compatibility(consumer_service, producer_service, method?, path?)

Diff a consumer’s outbound calls against a producer’s exposed operations, across every protocol. HTTP calls get method-and-path matching; GraphQL, socket, and pub/sub operations get exact-key existence checks. On top of structural matching, matched pairs carry per-pair type-check verdicts, the request/response payload shapes compared by the TypeScript compiler pass, so the tool tells you not just whether an operation exists but whether the types still line up. Surfaces missing operations (the consumer uses something the producer doesn’t expose), type-incompatible pairs, and unused ones (the producer exposes operations nobody calls).

Reach for it before removing a route, renaming a path, field, event, or topic, or changing a producer’s request or response shape.

Parameters:

NameTypeRequiredNotes
consumer_servicestringyesThe service making the calls.
producer_servicestringyesThe service exposing the operations.
methodstringnoFilter by operation label: HTTP method, GraphQL kind, socket direction, or PUBSUB.
pathstringnoFilter by HTTP path, GraphQL field, socket event name, or pub/sub topic.

Response shape:

{
  "consumer": "checkout",
  "producer": "billing",
  "compatible": false,
  "types_checked": true,
  "types_checked_note": "types_checked is response-level: true means at least one matched pair carried a stored type-check verdict, not that every pair was verified. Per-pair coverage is in type_verdicts.",
  "verdict_source": "ts_check@0.3.1",
  "type_verdicts": {
    "compatible": 3,
    "incompatible": 1,
    "unverifiable": 0,
    "not_compared": 1
  },
  "consumer_calls": 6,
  "matched_calls": 5,
  "producer_endpoints": 14,
  "issues": [
    {
      "severity": "error",
      "category": "missing_endpoint",
      "message": "Consumer calls POST /api/v1/invoices/draft but producer has no matching endpoint"
    },
    {
      "severity": "error",
      "category": "type_incompatible",
      "message": "POST /api/v1/invoices: consumer's request body doesn't match the producer's CreateInvoiceRequest (field `amount` expected number, got string)"
    },
    {
      "severity": "info",
      "category": "unused_endpoint",
      "message": "Producer exposes DELETE /api/v1/invoices/:id but consumer doesn't call it"
    }
  ],
  "matcher_version": "0.3.1"
}

compatible is false when any call is missing an endpoint or any matched pair is type-incompatible. A matched pair with no stored verdict is not_compared, never assumed compatible. types_checked is false for older scans with no persisted verdicts, in which case the answer falls back to structural matching only (verdict_source reads structural-matching-only). Issue categories: missing_endpoint and type_incompatible (errors), type_unverifiable and types_not_compared (warnings), and unused_endpoint (info).

get_service_dependencies(service?)

With no argument, returns project-wide npm dependency conflicts: any package pinned to more than one version across services. With a service name, returns that service’s merged dependency list (one {name, version} row per package).

Reach for it before adding or upgrading an npm dependency, or when a TypeScript build error looks like a version mismatch.

Parameters:

NameTypeRequiredNotes
servicestringnoOmit for the project-wide conflict view.

Project-wide response shape:

{
  "total_packages": 312,
  "conflict_count": 4,
  "conflicts": [
    {
      "package_name": "zod",
      "severity": "error",
      "versions": [
        { "service": "billing", "version": "3.22.4" },
        { "service": "checkout", "version": "4.0.1" }
      ]
    }
  ]
}

Per-service response shape:

{
  "service": "billing",
  "package_count": 87,
  "packages": [
    { "name": "zod", "version": "3.22.4" }
  ]
}

The list is alphabetical and capped; when it is truncated the response carries a truncation_note naming the cap.

Onboarding

scaffold()

Generates the files needed to onboard the current repo onto Carrick, and returns them with instructions for the agent to act on:

  • .github/workflows/carrick.yml — the scan workflow, written verbatim. Keyless via GitHub Actions OIDC; there is no secret to configure.
  • carrick.md — an instruction block telling the agent when to reach for Carrick’s tools, pinned to the resolved project slug. The instructions say to fold it into an existing AGENTS.md/CLAUDE.md when the repo has one.
  • carrick.json — a config skeleton the agent is told to populate by scanning the repo: the service name(s), and the env vars and domains that name internal services versus third-party APIs (using a services array for monorepos).

Your coding agent calls this once per repo during setup. Takes only the shared project/repo scoping arguments, and — unlike the data tools — works on a project whose repos have never been scanned, since that’s exactly when you run it.

Resources

Resources are read-only URIs that the MCP client can fetch directly. Resource URIs carry no project/repo arguments, so they resolve from the token alone: they work when the workspace has a single project, and return a teaching message otherwise.

carrick://services

Full service catalogue as JSON: the same service rows as list_services(), returned as a bare array, exposed as a resource so the client can subscribe to it.

carrick://services/{name}/types.d.ts

Bundled .d.ts file for one service. Useful when you want the agent to read the whole type surface area in one fetch instead of round-tripping get_type_definition for each name.

Errors and empty results

Every tool returns a single text content block containing JSON. Errors and empty results are represented as plain text inside the same block:

  • Missing service: Service "checkout" not found. Use list_services to see available services.
  • Unresolved project: a short message telling the agent to pass project or repo (and which slugs exist).
  • Project with no scan data yet: a message saying the project resolved but its repos haven’t been scanned since being connected.
  • Empty search: Scanned 1247 embedded intents but none scored above the similarity threshold of 0.3. Try a more concrete phrase, or relax similarity_threshold.
  • Missing types: Endpoint POST /api/v1/invoices exists but has no extracted types.

Treat any response whose first character is not { or [ as a textual diagnostic rather than a structured payload.

  • Connecting your agent covers the setup that gates access to these tools, and the instruction block that tells your agent when to reach for each.
  • Quickstart walks the full setup end to end.