Skip to content

Commit f62509f

Browse files
critesjoshclaude
andcommitted
v1.6.0: Add vitest test suite (106 tests) and extract format utils
Add comprehensive test coverage across all modules using vitest. Extract formatting functions from src/index.ts to src/utils/format.ts for testability. Tests run as prepublish guard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c610b5a commit f62509f

11 files changed

Lines changed: 3171 additions & 131 deletions

File tree

package-lock.json

Lines changed: 1503 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aztec-mcp-server",
3-
"version": "1.5.2",
3+
"version": "1.6.0",
44
"description": "MCP server for Aztec development - clones and searches Aztec documentation and examples",
55
"type": "module",
66
"main": "dist/index.js",
@@ -11,7 +11,9 @@
1111
"build": "tsc",
1212
"start": "node dist/index.js",
1313
"dev": "tsc --watch",
14-
"prepublishOnly": "npm run build"
14+
"test": "vitest run",
15+
"test:watch": "vitest",
16+
"prepublishOnly": "npm test && npm run build"
1517
},
1618
"keywords": [
1719
"aztec",
@@ -30,12 +32,13 @@
3032
},
3133
"dependencies": {
3234
"@modelcontextprotocol/sdk": "^1.0.0",
33-
"simple-git": "^3.27.0",
34-
"globby": "^14.0.2"
35+
"globby": "^14.0.2",
36+
"simple-git": "^3.27.0"
3537
},
3638
"devDependencies": {
3739
"@types/node": "^22.10.0",
38-
"typescript": "^5.7.0"
40+
"typescript": "^5.7.0",
41+
"vitest": "^4.0.18"
3942
},
4043
"engines": {
4144
"node": ">=18.0.0"

src/index.ts

Lines changed: 8 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ import {
2424
readAztecExample,
2525
readRepoFile,
2626
} from "./tools/index.js";
27+
import {
28+
formatSyncResult,
29+
formatStatus,
30+
formatSearchResults,
31+
formatExamplesList,
32+
formatExampleContent,
33+
formatFileContent,
34+
} from "./utils/format.js";
2735

2836
const server = new Server(
2937
{
@@ -316,127 +324,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
316324
}
317325
});
318326

319-
// --- Formatting helpers ---
320-
321-
function formatSyncResult(result: Awaited<ReturnType<typeof syncRepos>>): string {
322-
const lines = [
323-
result.success ? "✓ Sync completed" : "⚠ Sync completed with errors",
324-
"",
325-
`Version: ${result.version}`,
326-
result.message,
327-
"",
328-
"Repositories:",
329-
];
330-
331-
for (const repo of result.repos) {
332-
const icon = repo.status.toLowerCase().includes("error") ? "✗" : "✓";
333-
lines.push(` ${icon} ${repo.name}: ${repo.status}`);
334-
}
335-
336-
return lines.join("\n");
337-
}
338-
339-
function formatStatus(status: Awaited<ReturnType<typeof getStatus>>): string {
340-
const lines = [
341-
"Aztec MCP Server Status",
342-
"",
343-
`Repos directory: ${status.reposDir}`,
344-
"",
345-
"Repositories:",
346-
];
347-
348-
for (const repo of status.repos) {
349-
const icon = repo.cloned ? "✓" : "○";
350-
const commit = repo.commit ? ` (${repo.commit})` : "";
351-
lines.push(` ${icon} ${repo.name}${commit}`);
352-
lines.push(` ${repo.description}`);
353-
}
354-
355-
const clonedCount = status.repos.filter((r) => r.cloned).length;
356-
if (clonedCount === 0) {
357-
lines.push("");
358-
lines.push("No repositories cloned. Run aztec_sync_repos to get started.");
359-
}
360-
361-
return lines.join("\n");
362-
}
363-
364-
function formatSearchResults(
365-
result: ReturnType<typeof searchAztecCode>
366-
): string {
367-
const lines = [result.message, ""];
368-
369-
if (!result.success || result.results.length === 0) {
370-
return lines.join("\n");
371-
}
372-
373-
for (const match of result.results) {
374-
lines.push(`**${match.file}:${match.line}**`);
375-
lines.push("```");
376-
lines.push(match.content);
377-
lines.push("```");
378-
lines.push("");
379-
}
380-
381-
return lines.join("\n");
382-
}
383-
384-
function formatExamplesList(
385-
result: ReturnType<typeof listAztecExamples>
386-
): string {
387-
const lines = [result.message, ""];
388-
389-
if (!result.success || result.examples.length === 0) {
390-
return lines.join("\n");
391-
}
392-
393-
// Group by repo
394-
const byRepo = new Map<string, typeof result.examples>();
395-
for (const example of result.examples) {
396-
if (!byRepo.has(example.repo)) {
397-
byRepo.set(example.repo, []);
398-
}
399-
byRepo.get(example.repo)!.push(example);
400-
}
401-
402-
for (const [repo, examples] of byRepo) {
403-
lines.push(`**${repo}:**`);
404-
for (const example of examples) {
405-
lines.push(` - ${example.name}`);
406-
}
407-
lines.push("");
408-
}
409-
410-
return lines.join("\n");
411-
}
412-
413-
function formatExampleContent(
414-
result: ReturnType<typeof readAztecExample>
415-
): string {
416-
if (!result.success || !result.content) {
417-
return result.message;
418-
}
419-
420-
const lines = [
421-
`**${result.example!.name}** (${result.example!.repo})`,
422-
`Path: ${result.example!.path}`,
423-
"",
424-
"```noir",
425-
result.content,
426-
"```",
427-
];
428-
429-
return lines.join("\n");
430-
}
431-
432-
function formatFileContent(result: ReturnType<typeof readRepoFile>): string {
433-
if (!result.success || !result.content) {
434-
return result.message;
435-
}
436-
437-
return result.content;
438-
}
439-
440327
// --- Start server ---
441328

442329
async function main() {

src/utils/format.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/**
2+
* Formatting utilities for MCP tool responses
3+
*/
4+
5+
import type { SyncResult } from "../tools/sync.js";
6+
import type { SearchResult, FileInfo } from "./search.js";
7+
8+
export function formatSyncResult(result: SyncResult): string {
9+
const lines = [
10+
result.success ? "✓ Sync completed" : "⚠ Sync completed with errors",
11+
"",
12+
`Version: ${result.version}`,
13+
result.message,
14+
"",
15+
"Repositories:",
16+
];
17+
18+
for (const repo of result.repos) {
19+
const icon = repo.status.toLowerCase().includes("error") ? "✗" : "✓";
20+
lines.push(` ${icon} ${repo.name}: ${repo.status}`);
21+
}
22+
23+
return lines.join("\n");
24+
}
25+
26+
export function formatStatus(status: {
27+
reposDir: string;
28+
repos: {
29+
name: string;
30+
description: string;
31+
cloned: boolean;
32+
commit?: string;
33+
}[];
34+
}): string {
35+
const lines = [
36+
"Aztec MCP Server Status",
37+
"",
38+
`Repos directory: ${status.reposDir}`,
39+
"",
40+
"Repositories:",
41+
];
42+
43+
for (const repo of status.repos) {
44+
const icon = repo.cloned ? "✓" : "○";
45+
const commit = repo.commit ? ` (${repo.commit})` : "";
46+
lines.push(` ${icon} ${repo.name}${commit}`);
47+
lines.push(` ${repo.description}`);
48+
}
49+
50+
const clonedCount = status.repos.filter((r) => r.cloned).length;
51+
if (clonedCount === 0) {
52+
lines.push("");
53+
lines.push("No repositories cloned. Run aztec_sync_repos to get started.");
54+
}
55+
56+
return lines.join("\n");
57+
}
58+
59+
export function formatSearchResults(result: {
60+
success: boolean;
61+
results: SearchResult[];
62+
message: string;
63+
}): string {
64+
const lines = [result.message, ""];
65+
66+
if (!result.success || result.results.length === 0) {
67+
return lines.join("\n");
68+
}
69+
70+
for (const match of result.results) {
71+
lines.push(`**${match.file}:${match.line}**`);
72+
lines.push("```");
73+
lines.push(match.content);
74+
lines.push("```");
75+
lines.push("");
76+
}
77+
78+
return lines.join("\n");
79+
}
80+
81+
export function formatExamplesList(result: {
82+
success: boolean;
83+
examples: FileInfo[];
84+
message: string;
85+
}): string {
86+
const lines = [result.message, ""];
87+
88+
if (!result.success || result.examples.length === 0) {
89+
return lines.join("\n");
90+
}
91+
92+
// Group by repo
93+
const byRepo = new Map<string, FileInfo[]>();
94+
for (const example of result.examples) {
95+
if (!byRepo.has(example.repo)) {
96+
byRepo.set(example.repo, []);
97+
}
98+
byRepo.get(example.repo)!.push(example);
99+
}
100+
101+
for (const [repo, examples] of byRepo) {
102+
lines.push(`**${repo}:**`);
103+
for (const example of examples) {
104+
lines.push(` - ${example.name}`);
105+
}
106+
lines.push("");
107+
}
108+
109+
return lines.join("\n");
110+
}
111+
112+
export function formatExampleContent(result: {
113+
success: boolean;
114+
example?: FileInfo;
115+
content?: string;
116+
message: string;
117+
}): string {
118+
if (!result.success || !result.content) {
119+
return result.message;
120+
}
121+
122+
const lines = [
123+
`**${result.example!.name}** (${result.example!.repo})`,
124+
`Path: ${result.example!.path}`,
125+
"",
126+
"```noir",
127+
result.content,
128+
"```",
129+
];
130+
131+
return lines.join("\n");
132+
}
133+
134+
export function formatFileContent(result: {
135+
success: boolean;
136+
content?: string;
137+
message: string;
138+
}): string {
139+
if (!result.success || !result.content) {
140+
return result.message;
141+
}
142+
143+
return result.content;
144+
}

0 commit comments

Comments
 (0)