Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 40 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ An MCP (Model Context Protocol) server that provides local access to Aztec docum

## Features

- **Version Support**: Clone specific Aztec release tags (e.g., `v4.3.0`)
- **Local Repository Cloning**: Automatically clones Aztec repositories with sparse checkout for efficiency
- **Version Support**: Clone specific Aztec release tags (e.g., `v6.0.0-rc.6`)
- **Lightweight v6 Sync**: Clones only selected paths from `aztec-node`, pinned to the `labs` commit recorded in the requested `aztec-packages` release. Protocol and Noir sources are optional.
- **Fast Code Search**: Search Noir contracts and TypeScript files using ripgrep (with fallback)
- **Documentation Search**: Search Aztec documentation locally; with an API key, semantic vector search across the full corpora (framework docs, examples, Noir stdlib, TypeScript SDK, protocol circuits)
- **Error Lookup**: Static catalog (Solidity / circuit / TX / AVM errors) plus optional semantic fallback for unrecognized errors when an API key is configured
- **Example Discovery**: List and read Aztec contract examples
- **Version-sync Gate**: When using the hosted semantic backend, the server detects mismatches between your local clone tag and the indexed corpus and refuses to query across versions unless explicitly overridden
- **Version-sync Gate**: When using the hosted semantic backend, the server detects mismatches between your local release and the indexed corpus and refuses to query across versions unless explicitly overridden

## API Key (optional, recommended)

Expand Down Expand Up @@ -73,7 +73,7 @@ Add to your `.mcp.json`. The minimal config is just the command; add `env.API_KE
| `API_KEY` | unset | Personal API key from `/mcp-key` in the Noir Discord (<https://discord.gg/xMud5StFyA>). Unset → local-only mode. |
| `API_URL` | `https://aztec.adjacentpossible.dev` | DocsGPT backend the semantic search hits. Override to point at a self-hosted instance. |
| `REQUEST_TIMEOUT` | `60000` | Semantic-search request timeout (ms). |
| `AZTEC_DEFAULT_VERSION` | `v4.3.0` | Default version tag for `aztec_sync_repos`. |
| `AZTEC_DEFAULT_VERSION` | `v6.0.0-rc.6` | Default version tag for `aztec_sync_repos`. |
| `AZTEC_MCP_REPOS_DIR` | `~/.aztec-mcp/repos/` | Where local clones live. |

## Available Tools
Expand All @@ -83,24 +83,40 @@ Add to your `.mcp.json`. The minimal config is just the command; add `env.API_KE
Clone or update Aztec repositories locally. **Run this first** to enable other tools.

```
Clones:
- aztec-packages (docs, aztec-nr, noir-contracts) - sparse checkout
- aztec-examples (full)
- aztec-starter (full)
Default for v6:
- aztec-node: SDK, Aztec.nr, contract examples, playground, and documentation

Optional:
- aztec-packages: protocol, proving-system TypeScript, and L1 sources
- noir: language docs, standard library, and tooling
```

**Parameters:**

- `version` (string): Aztec version tag to clone (e.g., `v4.3.0`). Defaults to latest supported version.
- `version` (string): Aztec version tag to clone (e.g., `v6.0.0-rc.6`). Defaults to latest supported version.
- `force` (boolean): Force re-clone even if repos exist
- `repos` (string[]): Specific repos to sync
- `repos` (string[]): For v6, select optional `aztec-packages` and/or `noir` sources. `aztec-node` is always included to keep SDK and documentation aligned with the release. Older versions retain their legacy repository configuration.

**Example - Clone specific version:**

```
aztec_sync_repos({ version: "v4.3.0" })
aztec_sync_repos({ version: "v6.0.0-rc.6" })
```

The default sync uses a temporary, shallow, blob-filtered Git fetch to read the release's `labs` and Noir gitlinks. It then removes that temporary Git data and makes one persistent shallow, partial, sparse checkout of `https://github.com/aztec-labs-eng/aztec-node` at the pinned commit.

To include protocol/L1 and Noir sources:

```
aztec_sync_repos({ version: "v6.0.0-rc.6", repos: ["aztec-packages", "noir"] })
```

The `v6.0.0-rc.6` release pins `aztec-node` at `1b6de149f4fe1cde3c5fb876d3822289f490cd2a`. That checkout has no exact `rc.6` documentation snapshot, so local documentation search uses its unversioned docs and reports the commit as provenance. It does not substitute an older snapshot.

Sync metadata records the release and active checkout commits. Searches exclude leftover repositories from previous releases. Optional sources remain active on a same-version resync if their commits still match; when changing versions, specify the optional sources again. Interrupted v6 syncs block version-sensitive searches until sync succeeds, and retries preserve the requested optional repositories. Downgrading to a legacy release requires a full sync (omit `repos`) to avoid mixing releases. Existing installs should run `aztec_sync_repos` with the version above to migrate.

The companion repositories `aztec-examples`, `aztec-starter`, and `demo-wallet` are not offered for v6 because matching release tags are unavailable. Contract examples come from the pinned `aztec-node` checkout.

### `aztec_status`

Check the status of cloned repositories.
Expand Down Expand Up @@ -133,7 +149,7 @@ Search Aztec documentation. Local ripgrep by default; semantic vector search whe
- `maxResults` (number): Maximum results (default: 20 local; 5 semantic, max 20)
- `chunks` (number, semantic only): Number of result chunks (1-20). If omitted, `maxResults` is used.
- `useLocalFallback` (boolean, semantic only): If the semantic backend fails, fall back to local ripgrep. Default `false` so backend errors surface clearly.
- `allowVersionMismatch` (boolean, semantic only): Override the version-sync gate. Default `false`. The gate refuses to search when your local `aztec-packages` clone tag differs from the corpus version the backend has indexed.
- `allowVersionMismatch` (boolean, semantic only): Override the version-sync gate. Default `false`. The gate refuses to search when your local release version differs from the corpus version the backend has indexed, including release-candidate suffixes (`rc.1` and `rc.6` are distinct).

### `aztec_list_examples`

Expand Down Expand Up @@ -203,7 +219,7 @@ Set the default Aztec version with the `AZTEC_DEFAULT_VERSION` environment varia
"command": "npx",
"args": ["-y", "@aztec/mcp-server@latest"],
"env": {
"AZTEC_DEFAULT_VERSION": "v3.0.0-devnet.6-plugin.1"
"AZTEC_DEFAULT_VERSION": "v6.0.0-rc.6"
}
}
}
Expand All @@ -223,6 +239,10 @@ npm install
# Build
npm run build

npm test

node test.mjs

# Run locally
node dist/index.js
```
Expand All @@ -235,11 +255,13 @@ node dist/index.js

## Cloned Repositories

| Repository | Description | Checkout |
| ----------------------------------------------------------------- | ----------------- | -------------------------------------- |
| [aztec-packages](https://github.com/AztecProtocol/aztec-packages) | Main monorepo | Sparse: docs, aztec-nr, noir-contracts |
| [aztec-examples](https://github.com/AztecProtocol/aztec-examples) | Official examples | Full |
| [aztec-starter](https://github.com/AztecProtocol/aztec-starter) | Starter template | Full |
| Repository | v6 use | Checkout |
| --- | --- | --- |
| [aztec-node](https://github.com/aztec-labs-eng/aztec-node) | Default: SDK, Aztec.nr, contracts, docs | Shallow, partial, sparse; pinned by the release's `labs` gitlink |
| [aztec-packages](https://github.com/AztecProtocol/aztec-packages) | Optional: protocol and L1 sources | Shallow, partial, sparse; pinned to the release commit |
| [noir](https://github.com/noir-lang/noir) | Optional: language documentation and tooling | Shallow, partial, sparse; pinned by the release's Noir gitlink |

Versions before v6 retain the existing monorepo and companion repository configuration.

## License

Expand Down
21 changes: 11 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ const SEMANTIC_INSTRUCTIONS =
"natural-language documentation queries; `aztec_search_code` for " +
"regex-based code search; `aztec_lookup_error` for error diagnosis. " +
"The server enforces a version-sync gate between the user's local " +
"aztec-packages clone tag and the corpus version it queries — if the " +
"release version and the corpus version it queries — if the " +
"user gets a `version-mismatch` result, they can either run " +
"`aztec_sync_repos` with the corpus version, or pass " +
"`allowVersionMismatch: true` to query across versions intentionally.";

const LOCAL_ONLY_INSTRUCTIONS =
"This Aztec MCP server is running in LOCAL-ONLY mode (no API_KEY " +
"configured). It can answer questions from cloned aztec-packages, " +
"aztec-examples, and aztec-starter repositories using ripgrep search " +
"configured). It can answer questions from the pinned aztec-node checkout " +
"and optional protocol or Noir sources using ripgrep search " +
"and a static error catalog. It does NOT have semantic search across " +
"the full Aztec corpora (Aztec.nr framework, Noir stdlib, contract " +
"examples, TypeScript SDK, protocol circuits, etc.) and does NOT have " +
Expand Down Expand Up @@ -203,7 +203,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
type: "boolean",
description:
"Override the version-sync gate. By default the search refuses to run when the local " +
"aztec-packages clone tag differs from the corpus version the DocsGPT backend has indexed. " +
"release version differs from the corpus version the DocsGPT backend has indexed. " +
"Set true to query anyway (results reflect the corpus version, not your local clone).",
},
}
Expand All @@ -217,15 +217,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
name: "aztec_sync_repos",
description:
"Clone or update Aztec repositories locally. Run this first to enable searching. " +
"Clones: aztec-packages (docs, aztec-nr, contracts), aztec-examples, aztec-starter. " +
"For v6, sparsely clones aztec-node at the release-pinned labs commit. " +
"Protocol sources (aztec-packages) and Noir are optional. " +
"Specify a version to clone a specific Aztec release tag.",
inputSchema: {
type: "object" as const,
properties: {
version: {
type: "string",
description:
"Aztec version tag to clone (e.g., 'v4.3.0'). Defaults to latest supported version.",
"Aztec version tag to clone (e.g., 'v6.0.0-rc.6'). Defaults to latest supported version.",
},
force: {
type: "boolean",
Expand All @@ -235,7 +236,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
type: "array",
items: { type: "string" },
description:
"Specific repos to sync. Options: aztec-packages, aztec-examples, aztec-starter",
"For v6: aztec-node (always included), aztec-packages (optional protocol/L1 sources), noir (optional language sources). Older releases use the legacy repository set.",
},
},
},
Expand Down Expand Up @@ -271,7 +272,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
repo: {
type: "string",
description:
"Specific repo to search. Options: aztec-packages, aztec-examples, aztec-starter",
"Specific active repo to search. For v6: aztec-node, plus aztec-packages or noir if enabled during sync.",
},
maxResults: {
type: "number",
Expand Down Expand Up @@ -323,7 +324,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
path: {
type: "string",
description:
"File path relative to repos directory (e.g., 'aztec-packages/docs/docs/tutorials/...')",
"File path relative to repos directory (e.g., 'aztec-node/noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr')",
},
},
required: ["path"],
Expand Down Expand Up @@ -492,7 +493,7 @@ function ensureAutoResync(): void {
log(`Auto-resync partial: ${syncResult.message}`, "info");
} else {
log(
`Auto-resync failed: ${syncResult.message}. Local tools will use existing checkouts.`,
`Auto-resync failed: ${syncResult.message}`,
"warning"
);
}
Expand Down
37 changes: 35 additions & 2 deletions src/repos/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export interface RepoConfig {
name: string;
/** Git URL to clone from */
url: string;
/** Excluded from the default sync; available through repos. */
optional?: boolean;
/** Branch to checkout (defaults to main/master) */
branch?: string;
/** Tag to checkout (overrides branch if specified) */
Expand All @@ -31,7 +33,7 @@ export interface RepoConfig {
}

/** Default Aztec version (tag) to use - can be overridden via AZTEC_DEFAULT_VERSION env var */
export const DEFAULT_AZTEC_VERSION = process.env.AZTEC_DEFAULT_VERSION || "v4.3.0";
export const DEFAULT_AZTEC_VERSION = process.env.AZTEC_DEFAULT_VERSION || "v6.0.0-rc.6";

/**
* Base Aztec repository configurations (without version)
Expand Down Expand Up @@ -134,11 +136,42 @@ const BASE_REPOS: Omit<RepoConfig, "tag">[] = [

/**
* Get Aztec repositories configured for a specific version
* @param version - The Aztec version tag (e.g., "v4.3.0")
* @param version - The Aztec version tag (e.g., "v6.0.0-rc.6")
*/
export function getAztecRepos(version?: string): RepoConfig[] {
const tag = version || DEFAULT_AZTEC_VERSION;

if (Number(tag.replace(/^v/, "").split(".")[0]) >= 6) {
return [
{
name: "aztec-node",
url: "https://github.com/aztec-labs-eng/aztec-node",
description: "Aztec SDK, Aztec.nr, contracts, and docs pinned by the release's labs gitlink",
sparse: [
"noir-projects/aztec-nr",
"noir-projects/noir-contracts",
"yarn-project",
"playground",
"docs/docs-developers/docs",
"docs/docs-operate",
"docs/docs-participate",
`docs/developer_versioned_docs/version-${tag.startsWith("v") ? tag : `v${tag}`}`,
],
searchPatterns: { code: ["*.nr", "*.ts"], docs: ["*.md", "*.mdx"] },
},
{
name: "aztec-packages",
url: "https://github.com/AztecProtocol/aztec-packages",
tag,
optional: true,
description: "Optional Aztec protocol, proving system, and L1 sources",
sparse: ["noir-projects/fnd", "protocol", "barretenberg/ts/bb.js/src", "l1-contracts/src"],
searchPatterns: { code: ["*.nr", "*.ts", "*.sol"], docs: ["*.md", "*.mdx"] },
},
{ ...BASE_REPOS.find((repo) => repo.name === "noir")!, optional: true },
];
}

return BASE_REPOS.map((repo) => ({
...repo,
// Only apply version tag to Aztec repos that don't opt out
Expand Down
16 changes: 11 additions & 5 deletions src/tools/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
FileInfo,
} from "../utils/search.js";
import { isRepoCloned } from "../utils/git.js";
import { getActiveRepoNames, getDocsSource } from "../utils/sources.js";
import { getRepoNames } from "../repos/config.js";
import { DocsGPTClient, DocsGPTClientError } from "../backends/docsgpt-client.js";
import type { SemanticSearchResult } from "../backends/docsgpt-client.js";
Expand All @@ -32,6 +33,10 @@ export function searchAztecCode(options: {
} {
const { query, filePattern = "*.nr", repo, maxResults = 30 } = options;

const active = getActiveRepoNames();
if (repo && active && !active.includes(repo)) {
return { success: false, results: [], message: `Repository '${repo}' is not active for this release. Run aztec_sync_repos with repos to enable optional sources.` };
}
// Check if repos are cloned
if (repo && !isRepoCloned(repo)) {
return {
Expand All @@ -41,7 +46,7 @@ export function searchAztecCode(options: {
};
}

const anyCloned = getRepoNames().some(isRepoCloned);
const anyCloned = (getActiveRepoNames() ?? getRepoNames()).some(isRepoCloned);
if (!anyCloned) {
return {
success: false,
Expand Down Expand Up @@ -193,8 +198,9 @@ function ripgrepFallback(
): DocsSearchResult {
const { query, section, maxResults = 20 } = options;

if (!isRepoCloned("aztec-packages-docs")) {
const fallbackError = "aztec-packages-docs is not cloned. Run aztec_sync_repos first to get documentation.";
const source = getDocsSource();
if (!isRepoCloned(source.repo)) {
const fallbackError = `${source.repo} is not cloned. Run aztec_sync_repos first to get documentation.`;
if (semanticError !== undefined) {
return {
kind: "error",
Expand Down Expand Up @@ -225,7 +231,7 @@ function ripgrepFallback(
: "") +
(results.length > 0
? `Found ${results.length} documentation matches`
: "No documentation matches found"),
: "No documentation matches found") + (source.notice ? `\n${source.notice}` : ""),
},
};
}
Expand All @@ -240,7 +246,7 @@ export function listAztecExamples(options: { category?: string }): {
} {
const { category } = options;

const anyCloned = getRepoNames().some(isRepoCloned);
const anyCloned = (getActiveRepoNames() ?? getRepoNames()).some(isRepoCloned);
if (!anyCloned) {
return {
success: false,
Expand Down
Loading
Loading