zigsm 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tools.js ADDED
@@ -0,0 +1,225 @@
1
+ import * as path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import z from "zod";
4
+ import { getStdLibItem, searchStdLib } from "./std.js";
5
+ import { cosineSimilarity, embedTexts, getVoyageConfig } from "./voyage.js";
6
+ let builtinEmbeddingState = null;
7
+ function builtinEmbeddingText(fn) {
8
+ return `${fn.func}\n${fn.signature}\n${fn.docs}`;
9
+ }
10
+ async function getBuiltinEmbeddings(functions) {
11
+ const voyage = getVoyageConfig();
12
+ if (!voyage) {
13
+ return null;
14
+ }
15
+ if (builtinEmbeddingState && builtinEmbeddingState.functions === functions) {
16
+ return builtinEmbeddingState;
17
+ }
18
+ const vectors = await embedTexts(functions.map((fn) => builtinEmbeddingText(fn)), voyage, "document");
19
+ const state = {
20
+ functions,
21
+ vectors,
22
+ };
23
+ builtinEmbeddingState = state;
24
+ return state;
25
+ }
26
+ function createListBuiltinFunctionsTool(builtinFunctions) {
27
+ return {
28
+ name: "list_builtin_functions",
29
+ config: {
30
+ description: "Lists all available Zig builtin functions. Builtin functions are provided by the compiler and are prefixed with '@'. The comptime keyword on a parameter means that the parameter must be known at compile time. Use this to discover what functions are available, then use 'get_builtin_function' to get detailed documentation.",
31
+ },
32
+ handler: async () => {
33
+ const functionList = builtinFunctions.map((fn) => `- ${fn.signature}`).join("\n");
34
+ const message = `Available ${builtinFunctions.length} builtin functions:\n\n${functionList}`;
35
+ return {
36
+ content: [
37
+ {
38
+ type: "text",
39
+ text: message,
40
+ },
41
+ ],
42
+ };
43
+ },
44
+ };
45
+ }
46
+ function getBuiltinFunctionTool(builtinFunctions) {
47
+ return {
48
+ name: "get_builtin_function",
49
+ config: {
50
+ description: "Search for Zig builtin functions by name and get their documentation, signatures, and usage information. Returns all matching functions ranked by relevance.",
51
+ inputSchema: {
52
+ function_name: z
53
+ .string()
54
+ .min(1, "Query cannot be empty")
55
+ .describe("Function name or keywords (e.g., '@addWithOverflow', 'overflow', 'atomic')"),
56
+ },
57
+ },
58
+ handler: async ({ function_name }) => {
59
+ const queryLower = function_name.toLowerCase().trim();
60
+ if (!queryLower) {
61
+ return {
62
+ content: [
63
+ {
64
+ type: "text",
65
+ text: "Please provide a function name or keywords. Try searching for a function name like '@addWithOverflow' or keywords like 'overflow' or 'atomic'.",
66
+ },
67
+ ],
68
+ };
69
+ }
70
+ const scoredFunctions = builtinFunctions.map((fn) => {
71
+ const funcLower = fn.func.toLowerCase();
72
+ let score = 0;
73
+ if (funcLower === queryLower)
74
+ score += 1000;
75
+ else if (funcLower.startsWith(queryLower))
76
+ score += 500;
77
+ else if (funcLower.includes(queryLower))
78
+ score += 300;
79
+ if (score > 0)
80
+ score += Math.max(0, 50 - fn.func.length);
81
+ return { ...fn, score };
82
+ });
83
+ try {
84
+ const voyage = getVoyageConfig();
85
+ if (voyage) {
86
+ const embeddingState = await getBuiltinEmbeddings(builtinFunctions);
87
+ if (embeddingState && embeddingState.vectors.length > 0) {
88
+ const queryVector = (await embedTexts([function_name], voyage, "query"))[0];
89
+ for (let i = 0; i < scoredFunctions.length; i++) {
90
+ const semantic = cosineSimilarity(queryVector, embeddingState.vectors[i]);
91
+ scoredFunctions[i].score += semantic * 220;
92
+ }
93
+ }
94
+ }
95
+ }
96
+ catch {
97
+ }
98
+ const rankedFunctions = scoredFunctions.filter((fn) => fn.score > 0);
99
+ rankedFunctions.sort((a, b) => b.score - a.score);
100
+ if (rankedFunctions.length === 0) {
101
+ return {
102
+ content: [
103
+ {
104
+ type: "text",
105
+ text: `No builtin functions found matching "${function_name}". Try using 'list_builtin_functions' to see available functions, or refine your search terms.`,
106
+ },
107
+ ],
108
+ };
109
+ }
110
+ const results = rankedFunctions
111
+ .map((fn) => `**${fn.func}**\n\`\`\`zig\n${fn.signature}\n\`\`\`\n\n${fn.docs}`)
112
+ .join("\n\n---\n\n");
113
+ const message = rankedFunctions.length === 1
114
+ ? results
115
+ : `Found ${rankedFunctions.length} matching functions:\n\n${results}`;
116
+ return {
117
+ content: [
118
+ {
119
+ type: "text",
120
+ text: message,
121
+ },
122
+ ],
123
+ };
124
+ },
125
+ };
126
+ }
127
+ function searchStdLibTool(wasmPath, stdSources, options) {
128
+ return {
129
+ name: "search_std_lib",
130
+ config: {
131
+ description: "Search the Zig standard library for functions, types, namespaces, and other declarations. Returns detailed markdown documentation for each matching item. Use this to explore the standard library and discover available functionality. Supports fuzzy matching and returns results ranked by relevance.",
132
+ inputSchema: {
133
+ query: z
134
+ .string()
135
+ .min(1, "Search query cannot be empty")
136
+ .describe("Search terms to find in the standard library (e.g., 'ArrayList', 'print', 'allocator', 'HashMap')"),
137
+ limit: z
138
+ .number()
139
+ .int()
140
+ .min(1)
141
+ .default(20)
142
+ .describe("Maximum number of results to return (default: 20)"),
143
+ },
144
+ },
145
+ handler: async ({ query, limit = 20 }) => {
146
+ try {
147
+ const markdown = await searchStdLib(wasmPath, stdSources, query, limit, {
148
+ zigVersion: options.zigVersion,
149
+ docSource: options.docSource,
150
+ });
151
+ return {
152
+ content: [
153
+ {
154
+ type: "text",
155
+ text: markdown,
156
+ },
157
+ ],
158
+ };
159
+ }
160
+ catch (error) {
161
+ return {
162
+ content: [
163
+ {
164
+ type: "text",
165
+ text: `Error searching standard library: ${error instanceof Error ? error.message : String(error)}`,
166
+ },
167
+ ],
168
+ };
169
+ }
170
+ },
171
+ };
172
+ }
173
+ function getStdLibItemTool(wasmPath, stdSources) {
174
+ return {
175
+ name: "get_std_lib_item",
176
+ config: {
177
+ description: "Get detailed documentation for a specific item in the Zig standard library. Provide the fully qualified name (e.g., 'std.ArrayList', 'std.debug.print', 'std.mem.Allocator') to get comprehensive documentation including function signatures, parameters, return types, error sets, example usage, and optionally source code.",
178
+ inputSchema: {
179
+ name: z
180
+ .string()
181
+ .min(1, "Item name cannot be empty")
182
+ .describe("Fully qualified name of the standard library item (e.g., 'std.ArrayList', 'std.debug.print', 'std.mem.Allocator')"),
183
+ get_source_file: z
184
+ .boolean()
185
+ .default(false)
186
+ .describe("Return the entire source file where this item is implemented (default: false - shows detailed documentation with item source code only)"),
187
+ },
188
+ },
189
+ handler: async ({ name, get_source_file = false, }) => {
190
+ try {
191
+ const markdown = await getStdLibItem(wasmPath, stdSources, name, get_source_file);
192
+ return {
193
+ content: [
194
+ {
195
+ type: "text",
196
+ text: markdown,
197
+ },
198
+ ],
199
+ };
200
+ }
201
+ catch (error) {
202
+ return {
203
+ content: [
204
+ {
205
+ type: "text",
206
+ text: `Error getting standard library item: ${error instanceof Error ? error.message : String(error)}`,
207
+ },
208
+ ],
209
+ };
210
+ }
211
+ },
212
+ };
213
+ }
214
+ export async function registerAllTools(mcpServer, builtinFunctions, stdSources, options) {
215
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
216
+ const wasmPath = path.join(currentDir, "main.wasm");
217
+ const listBuiltinFunctionsTool = createListBuiltinFunctionsTool(builtinFunctions);
218
+ mcpServer.registerTool(listBuiltinFunctionsTool.name, listBuiltinFunctionsTool.config, listBuiltinFunctionsTool.handler);
219
+ const getBuiltinFunction = getBuiltinFunctionTool(builtinFunctions);
220
+ mcpServer.registerTool(getBuiltinFunction.name, getBuiltinFunction.config, getBuiltinFunction.handler);
221
+ const stdLibSearch = searchStdLibTool(wasmPath, stdSources, options);
222
+ mcpServer.registerTool(stdLibSearch.name, stdLibSearch.config, stdLibSearch.handler);
223
+ const stdLibItem = getStdLibItemTool(wasmPath, stdSources);
224
+ mcpServer.registerTool(stdLibItem.name, stdLibItem.config, stdLibItem.handler);
225
+ }
package/dist/voyage.js ADDED
@@ -0,0 +1,88 @@
1
+ export function getVoyageConfig() {
2
+ const env = globalThis.process?.env;
3
+ const apiKey = env?.VOYAGE_API_KEY?.trim();
4
+ if (!apiKey) {
5
+ return null;
6
+ }
7
+ return {
8
+ apiKey,
9
+ model: env?.VOYAGE_MODEL?.trim() || "voyage-3-lite",
10
+ };
11
+ }
12
+ async function embedBatch(input, config, inputType) {
13
+ const maxAttempts = 3;
14
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
15
+ try {
16
+ const response = await fetch("https://api.voyageai.com/v1/embeddings", {
17
+ method: "POST",
18
+ headers: {
19
+ Authorization: `Bearer ${config.apiKey}`,
20
+ "Content-Type": "application/json",
21
+ },
22
+ body: JSON.stringify({
23
+ model: config.model,
24
+ input,
25
+ input_type: inputType,
26
+ }),
27
+ signal: AbortSignal.timeout(15_000),
28
+ });
29
+ if (!response.ok) {
30
+ const body = await response.text();
31
+ const retriable = response.status === 429 || response.status >= 500;
32
+ if (retriable && attempt < maxAttempts) {
33
+ await new Promise((resolve) => setTimeout(resolve, 300 * attempt));
34
+ continue;
35
+ }
36
+ throw new Error(`Voyage embeddings failed: ${response.status} ${response.statusText} ${body}`);
37
+ }
38
+ const json = (await response.json());
39
+ if (!Array.isArray(json.data) || json.data.length !== input.length) {
40
+ throw new Error("Voyage embeddings returned an invalid response shape");
41
+ }
42
+ return json.data.map((item) => item.embedding);
43
+ }
44
+ catch (error) {
45
+ if (attempt < maxAttempts) {
46
+ await new Promise((resolve) => setTimeout(resolve, 300 * attempt));
47
+ continue;
48
+ }
49
+ if (error instanceof Error) {
50
+ throw error;
51
+ }
52
+ throw new Error(String(error));
53
+ }
54
+ }
55
+ throw new Error("Voyage embeddings failed");
56
+ }
57
+ export async function embedTexts(texts, config, inputType) {
58
+ const batchSize = 64;
59
+ const vectors = [];
60
+ for (let i = 0; i < texts.length; i += batchSize) {
61
+ const batch = texts.slice(i, i + batchSize);
62
+ const batchVectors = await embedBatch(batch, config, inputType);
63
+ vectors.push(...batchVectors);
64
+ }
65
+ return vectors;
66
+ }
67
+ export function dot(a, b) {
68
+ const len = Math.min(a.length, b.length);
69
+ let sum = 0;
70
+ for (let i = 0; i < len; i++) {
71
+ sum += a[i] * b[i];
72
+ }
73
+ return sum;
74
+ }
75
+ export function magnitude(a) {
76
+ let sum = 0;
77
+ for (let i = 0; i < a.length; i++) {
78
+ sum += a[i] * a[i];
79
+ }
80
+ return Math.sqrt(sum);
81
+ }
82
+ export function cosineSimilarity(a, b) {
83
+ const denom = magnitude(a) * magnitude(b);
84
+ if (denom === 0) {
85
+ return 0;
86
+ }
87
+ return dot(a, b) / denom;
88
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "zigsm",
3
+ "version": "1.4.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "zsm": "dist/mcp.js"
7
+ },
8
+ "main": "dist/mcp.js",
9
+ "files": [
10
+ "dist/**/*",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "scripts": {
15
+ "dev": "bun run build && bunx @modelcontextprotocol/inspector bun dist/mcp.js",
16
+ "build": "zig build --release && tsc && cp zig-out/main.wasm mcp/index.html dist/",
17
+ "prepublishOnly": "npm run build",
18
+ "lint": "bunx biome check",
19
+ "lint:fix": "bunx biome check --write",
20
+ "typecheck": "tsc --noEmit"
21
+ },
22
+ "author": "Andrey Ryapov <dev@zigwasm.org> (https://zigwasm.org/)",
23
+ "license": "MIT",
24
+ "description": "MCP server that provides up-to-date documentation for the Zig programming language standard library and builtin functions.",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/zig-wasm/zig-mcp.git"
28
+ },
29
+ "keywords": [
30
+ "mcp",
31
+ "modelcontextprotocol",
32
+ "zig",
33
+ "ziglang",
34
+ "zig-docs",
35
+ "cli"
36
+ ],
37
+ "dependencies": {
38
+ "@modelcontextprotocol/sdk": "^1.15.0",
39
+ "cheerio": "^1.1.0",
40
+ "env-paths": "^3.0.0",
41
+ "zod": "^3.25.75"
42
+ },
43
+ "devDependencies": {
44
+ "@biomejs/biome": "2.1.1",
45
+ "@types/bun": "latest",
46
+ "typescript": "^5.8.3"
47
+ },
48
+ "engines": {
49
+ "bun": ">=1.0.0",
50
+ "node": ">=18.0.0"
51
+ }
52
+ }