Skip to content

Commit 3ab2fde

Browse files
critesjoshclaude
andcommitted
fix: validate tool requests before auto-resync and abort on sync failure
- Move tool name and required argument validation before the auto-resync so unknown tools and missing args don't trigger an expensive forced sync - Check syncRepos result and throw an McpError when auto-resync fails, instead of silently continuing against a partially updated checkout Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 879bf4a commit 3ab2fde

1 file changed

Lines changed: 48 additions & 28 deletions

File tree

src/index.ts

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -191,25 +191,57 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
191191
],
192192
}));
193193

194+
function validateToolRequest(name: string, args: Record<string, unknown> | undefined): void {
195+
switch (name) {
196+
case "aztec_sync_repos":
197+
case "aztec_status":
198+
case "aztec_list_examples":
199+
break;
200+
case "aztec_search_code":
201+
case "aztec_search_docs":
202+
if (!args?.query) throw new McpError(ErrorCode.InvalidParams, "query is required");
203+
break;
204+
case "aztec_read_example":
205+
if (!args?.name) throw new McpError(ErrorCode.InvalidParams, "name is required");
206+
break;
207+
case "aztec_read_file":
208+
if (!args?.path) throw new McpError(ErrorCode.InvalidParams, "path is required");
209+
break;
210+
default:
211+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
212+
}
213+
}
214+
194215
/**
195216
* Handle tool calls
196217
*/
197218
server.setRequestHandler(CallToolRequestSchema, async (request) => {
198219
const { name, arguments: args } = request.params;
199220

221+
// Validate tool name and required arguments before any expensive operations
222+
validateToolRequest(name, args);
223+
200224
// Auto re-sync if MCP server version changed since last sync
201-
const staleMetadata = name !== "aztec_sync_repos" ? needsResync() : null;
202-
if (staleMetadata) {
203-
const log = (message: string, level: string = "info") => {
204-
server.sendLoggingMessage({
205-
level: level as "info" | "debug" | "warning" | "error",
206-
logger: "aztec-sync",
207-
data: message,
208-
}).catch(() => {});
209-
};
210-
log(`Auto-syncing repos for MCP server v${MCP_VERSION} (was v${staleMetadata.mcpVersion})...`, "info");
211-
await syncRepos({ version: staleMetadata.aztecVersion, force: true, log });
212-
log("Auto-sync complete", "info");
225+
if (name !== "aztec_sync_repos") {
226+
const staleMetadata = needsResync();
227+
if (staleMetadata) {
228+
const log = (message: string, level: string = "info") => {
229+
server.sendLoggingMessage({
230+
level: level as "info" | "debug" | "warning" | "error",
231+
logger: "aztec-sync",
232+
data: message,
233+
}).catch(() => {});
234+
};
235+
log(`Auto-syncing repos for MCP server v${MCP_VERSION} (was v${staleMetadata.mcpVersion})...`, "info");
236+
const syncResult = await syncRepos({ version: staleMetadata.aztecVersion, force: true, log });
237+
if (!syncResult.success) {
238+
throw new McpError(
239+
ErrorCode.InternalError,
240+
`Auto-resync failed after MCP upgrade (v${staleMetadata.mcpVersion} → v${MCP_VERSION}): ${syncResult.message}`
241+
);
242+
}
243+
log("Auto-sync complete", "info");
244+
}
213245
}
214246

215247
try {
@@ -241,11 +273,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
241273
}
242274

243275
case "aztec_search_code": {
244-
if (!args?.query) {
245-
throw new McpError(ErrorCode.InvalidParams, "query is required");
246-
}
247276
const result = searchAztecCode({
248-
query: args.query as string,
277+
query: args!.query as string,
249278
filePattern: args?.filePattern as string | undefined,
250279
repo: args?.repo as string | undefined,
251280
maxResults: args?.maxResults as number | undefined,
@@ -255,11 +284,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
255284
}
256285

257286
case "aztec_search_docs": {
258-
if (!args?.query) {
259-
throw new McpError(ErrorCode.InvalidParams, "query is required");
260-
}
261287
const result = searchAztecDocs({
262-
query: args.query as string,
288+
query: args!.query as string,
263289
section: args?.section as string | undefined,
264290
maxResults: args?.maxResults as number | undefined,
265291
});
@@ -276,22 +302,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
276302
}
277303

278304
case "aztec_read_example": {
279-
if (!args?.name) {
280-
throw new McpError(ErrorCode.InvalidParams, "name is required");
281-
}
282305
const result = readAztecExample({
283-
name: args.name as string,
306+
name: args!.name as string,
284307
});
285308
text = formatExampleContent(result);
286309
break;
287310
}
288311

289312
case "aztec_read_file": {
290-
if (!args?.path) {
291-
throw new McpError(ErrorCode.InvalidParams, "path is required");
292-
}
293313
const result = readRepoFile({
294-
path: args.path as string,
314+
path: args!.path as string,
295315
});
296316
text = formatFileContent(result);
297317
break;

0 commit comments

Comments
 (0)