context-mode 0.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/.claude-plugin/plugin.json +29 -0
- package/.mcp.json +8 -0
- package/LICENSE +21 -0
- package/README.md +304 -0
- package/build/cli.d.ts +10 -0
- package/build/cli.js +193 -0
- package/build/executor.d.ts +27 -0
- package/build/executor.js +255 -0
- package/build/runtime.d.ts +24 -0
- package/build/runtime.js +167 -0
- package/build/server.d.ts +2 -0
- package/build/server.js +457 -0
- package/build/store.d.ts +39 -0
- package/build/store.js +212 -0
- package/package.json +64 -0
- package/skills/context-mode/SKILL.md +124 -0
- package/skills/context-mode/references/anti-patterns.md +257 -0
- package/skills/context-mode/references/patterns-javascript.md +298 -0
- package/skills/context-mode/references/patterns-python.md +304 -0
- package/skills/context-mode/references/patterns-shell.md +277 -0
package/build/server.js
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { PolyglotExecutor } from "./executor.js";
|
|
6
|
+
import { ContentStore } from "./store.js";
|
|
7
|
+
import { detectRuntimes, getRuntimeSummary, getAvailableLanguages, hasBunRuntime, } from "./runtime.js";
|
|
8
|
+
const runtimes = detectRuntimes();
|
|
9
|
+
const available = getAvailableLanguages(runtimes);
|
|
10
|
+
const server = new McpServer({
|
|
11
|
+
name: "context-mode",
|
|
12
|
+
version: "0.4.0",
|
|
13
|
+
});
|
|
14
|
+
const executor = new PolyglotExecutor({ runtimes });
|
|
15
|
+
// Lazy singleton — no DB overhead unless index/search is used
|
|
16
|
+
let _store = null;
|
|
17
|
+
function getStore() {
|
|
18
|
+
if (!_store)
|
|
19
|
+
_store = new ContentStore();
|
|
20
|
+
return _store;
|
|
21
|
+
}
|
|
22
|
+
// Build description dynamically based on detected runtimes
|
|
23
|
+
const langList = available.join(", ");
|
|
24
|
+
const bunNote = hasBunRuntime()
|
|
25
|
+
? " (Bun detected — JS/TS runs 3-5x faster)"
|
|
26
|
+
: "";
|
|
27
|
+
// ─────────────────────────────────────────────────────────
|
|
28
|
+
// Tool: execute
|
|
29
|
+
// ─────────────────────────────────────────────────────────
|
|
30
|
+
server.registerTool("execute", {
|
|
31
|
+
title: "Execute Code",
|
|
32
|
+
description: `Execute code in a sandboxed subprocess. Only stdout enters context — raw data stays in the subprocess. Use instead of bash/cat when output would exceed 20 lines.${bunNote} Available: ${langList}.`,
|
|
33
|
+
inputSchema: z.object({
|
|
34
|
+
language: z
|
|
35
|
+
.enum([
|
|
36
|
+
"javascript",
|
|
37
|
+
"typescript",
|
|
38
|
+
"python",
|
|
39
|
+
"shell",
|
|
40
|
+
"ruby",
|
|
41
|
+
"go",
|
|
42
|
+
"rust",
|
|
43
|
+
"php",
|
|
44
|
+
"perl",
|
|
45
|
+
"r",
|
|
46
|
+
])
|
|
47
|
+
.describe("Runtime language"),
|
|
48
|
+
code: z
|
|
49
|
+
.string()
|
|
50
|
+
.describe("Source code to execute. Use console.log (JS/TS), print (Python/Ruby/Perl/R), echo (Shell), echo (PHP), or fmt.Println (Go) to output a summary to context."),
|
|
51
|
+
timeout: z
|
|
52
|
+
.number()
|
|
53
|
+
.optional()
|
|
54
|
+
.default(30000)
|
|
55
|
+
.describe("Max execution time in ms"),
|
|
56
|
+
}),
|
|
57
|
+
}, async ({ language, code, timeout }) => {
|
|
58
|
+
try {
|
|
59
|
+
const result = await executor.execute({ language, code, timeout });
|
|
60
|
+
if (result.timedOut) {
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: "text",
|
|
65
|
+
text: `Execution timed out after ${timeout}ms\n\nPartial stdout:\n${result.stdout}\n\nstderr:\n${result.stderr}`,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
isError: true,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (result.exitCode !== 0) {
|
|
72
|
+
return {
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: "text",
|
|
76
|
+
text: `Exit code: ${result.exitCode}\n\nstdout:\n${result.stdout}\n\nstderr:\n${result.stderr}`,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
isError: true,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
content: [
|
|
84
|
+
{ type: "text", text: result.stdout || "(no output)" },
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
90
|
+
return {
|
|
91
|
+
content: [
|
|
92
|
+
{ type: "text", text: `Runtime error: ${message}` },
|
|
93
|
+
],
|
|
94
|
+
isError: true,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
// ─────────────────────────────────────────────────────────
|
|
99
|
+
// Helper: index stdout into FTS5 knowledge base
|
|
100
|
+
// ─────────────────────────────────────────────────────────
|
|
101
|
+
function indexStdout(stdout, source) {
|
|
102
|
+
const store = getStore();
|
|
103
|
+
const indexed = store.index({ content: stdout, source });
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: "text",
|
|
108
|
+
text: `Indexed ${indexed.totalChunks} sections (${indexed.codeChunks} with code) from: ${indexed.label}\nUse search() to query this content.`,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// ─────────────────────────────────────────────────────────
|
|
114
|
+
// Tool: execute_file
|
|
115
|
+
// ─────────────────────────────────────────────────────────
|
|
116
|
+
server.registerTool("execute_file", {
|
|
117
|
+
title: "Execute File Processing",
|
|
118
|
+
description: "Read a file and process it without loading contents into context. The file is read into a FILE_CONTENT variable inside the sandbox. Only your printed summary enters context.",
|
|
119
|
+
inputSchema: z.object({
|
|
120
|
+
path: z
|
|
121
|
+
.string()
|
|
122
|
+
.describe("Absolute file path or relative to project root"),
|
|
123
|
+
language: z
|
|
124
|
+
.enum([
|
|
125
|
+
"javascript",
|
|
126
|
+
"typescript",
|
|
127
|
+
"python",
|
|
128
|
+
"shell",
|
|
129
|
+
"ruby",
|
|
130
|
+
"go",
|
|
131
|
+
"rust",
|
|
132
|
+
"php",
|
|
133
|
+
"perl",
|
|
134
|
+
"r",
|
|
135
|
+
])
|
|
136
|
+
.describe("Runtime language"),
|
|
137
|
+
code: z
|
|
138
|
+
.string()
|
|
139
|
+
.describe("Code to process FILE_CONTENT. Print summary via console.log/print/echo."),
|
|
140
|
+
timeout: z
|
|
141
|
+
.number()
|
|
142
|
+
.optional()
|
|
143
|
+
.default(30000)
|
|
144
|
+
.describe("Max execution time in ms"),
|
|
145
|
+
}),
|
|
146
|
+
}, async ({ path, language, code, timeout }) => {
|
|
147
|
+
try {
|
|
148
|
+
const result = await executor.executeFile({
|
|
149
|
+
path,
|
|
150
|
+
language,
|
|
151
|
+
code,
|
|
152
|
+
timeout,
|
|
153
|
+
});
|
|
154
|
+
if (result.timedOut) {
|
|
155
|
+
return {
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: "text",
|
|
159
|
+
text: `Timed out processing ${path} after ${timeout}ms`,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
isError: true,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (result.exitCode !== 0) {
|
|
166
|
+
return {
|
|
167
|
+
content: [
|
|
168
|
+
{
|
|
169
|
+
type: "text",
|
|
170
|
+
text: `Error processing ${path} (exit ${result.exitCode}):\n${result.stderr || result.stdout}`,
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
isError: true,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
content: [
|
|
178
|
+
{ type: "text", text: result.stdout || "(no output)" },
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
184
|
+
return {
|
|
185
|
+
content: [
|
|
186
|
+
{ type: "text", text: `Runtime error: ${message}` },
|
|
187
|
+
],
|
|
188
|
+
isError: true,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
// ─────────────────────────────────────────────────────────
|
|
193
|
+
// Tool: index
|
|
194
|
+
// ─────────────────────────────────────────────────────────
|
|
195
|
+
server.registerTool("index", {
|
|
196
|
+
title: "Index Content",
|
|
197
|
+
description: "Index documentation or knowledge content into a searchable BM25 knowledge base. " +
|
|
198
|
+
"Chunks markdown by headings (keeping code blocks intact) and stores in ephemeral FTS5 database. " +
|
|
199
|
+
"The full content does NOT stay in context — only a brief summary is returned.\n\n" +
|
|
200
|
+
"WHEN TO USE:\n" +
|
|
201
|
+
"- Documentation from Context7, Skills, or MCP tools (API docs, framework guides, code examples)\n" +
|
|
202
|
+
"- API references (endpoint details, parameter specs, response schemas)\n" +
|
|
203
|
+
"- MCP tools/list output (exact tool signatures and descriptions)\n" +
|
|
204
|
+
"- Skill prompts and instructions that are too large for context\n" +
|
|
205
|
+
"- README files, migration guides, changelog entries\n" +
|
|
206
|
+
"- Any content with code examples you may need to reference precisely\n\n" +
|
|
207
|
+
"After indexing, use 'search' to retrieve specific sections on-demand.\n" +
|
|
208
|
+
"Do NOT use for: log files, test output, CSV, build output — use 'execute_file' for those.",
|
|
209
|
+
inputSchema: z.object({
|
|
210
|
+
content: z
|
|
211
|
+
.string()
|
|
212
|
+
.optional()
|
|
213
|
+
.describe("Raw text/markdown to index. Provide this OR path, not both."),
|
|
214
|
+
path: z
|
|
215
|
+
.string()
|
|
216
|
+
.optional()
|
|
217
|
+
.describe("File path to read and index (content never enters context). Provide this OR content."),
|
|
218
|
+
source: z
|
|
219
|
+
.string()
|
|
220
|
+
.optional()
|
|
221
|
+
.describe("Label for the indexed content (e.g., 'Context7: React useEffect', 'Skill: frontend-design')"),
|
|
222
|
+
}),
|
|
223
|
+
}, async ({ content, path, source }) => {
|
|
224
|
+
if (!content && !path) {
|
|
225
|
+
return {
|
|
226
|
+
content: [
|
|
227
|
+
{
|
|
228
|
+
type: "text",
|
|
229
|
+
text: "Error: Either content or path must be provided",
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
isError: true,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
const store = getStore();
|
|
237
|
+
const result = store.index({ content, path, source });
|
|
238
|
+
return {
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
type: "text",
|
|
242
|
+
text: `Indexed ${result.totalChunks} sections (${result.codeChunks} with code) from: ${result.label}\nUse search() to query this content.`,
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
249
|
+
return {
|
|
250
|
+
content: [
|
|
251
|
+
{ type: "text", text: `Index error: ${message}` },
|
|
252
|
+
],
|
|
253
|
+
isError: true,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
// ─────────────────────────────────────────────────────────
|
|
258
|
+
// Tool: search
|
|
259
|
+
// ─────────────────────────────────────────────────────────
|
|
260
|
+
server.registerTool("search", {
|
|
261
|
+
title: "Search Indexed Content",
|
|
262
|
+
description: "Search previously indexed content using BM25 full-text search. " +
|
|
263
|
+
"Returns the top matching chunks with heading context and full content. " +
|
|
264
|
+
"Use after 'index' to retrieve specific documentation sections, code examples, or API details on demand.\n\n" +
|
|
265
|
+
"WHEN TO USE:\n" +
|
|
266
|
+
"- Find specific code examples ('useEffect cleanup pattern')\n" +
|
|
267
|
+
"- Look up API signatures ('Supabase RLS policy syntax')\n" +
|
|
268
|
+
"- Get configuration details ('Tailwind responsive breakpoints')\n" +
|
|
269
|
+
"- Find migration steps ('App Router data fetching')\n\n" +
|
|
270
|
+
"Returns exact content — not summaries. Each result includes heading hierarchy and full section text.",
|
|
271
|
+
inputSchema: z.object({
|
|
272
|
+
query: z.string().describe("Natural language search query"),
|
|
273
|
+
limit: z
|
|
274
|
+
.number()
|
|
275
|
+
.optional()
|
|
276
|
+
.default(3)
|
|
277
|
+
.describe("Maximum results to return (default: 3)"),
|
|
278
|
+
}),
|
|
279
|
+
}, async ({ query, limit }) => {
|
|
280
|
+
try {
|
|
281
|
+
const store = getStore();
|
|
282
|
+
const results = store.search(query, limit);
|
|
283
|
+
if (results.length === 0) {
|
|
284
|
+
return {
|
|
285
|
+
content: [
|
|
286
|
+
{
|
|
287
|
+
type: "text",
|
|
288
|
+
text: `No results found for: "${query}". Make sure content has been indexed first.`,
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const formatted = results
|
|
294
|
+
.map((r, i) => {
|
|
295
|
+
const header = `--- Result ${i + 1} [${r.source}] (${r.contentType}) ---`;
|
|
296
|
+
const heading = `## ${r.title}`;
|
|
297
|
+
return `${header}\n${heading}\n\n${r.content}`;
|
|
298
|
+
})
|
|
299
|
+
.join("\n\n");
|
|
300
|
+
return {
|
|
301
|
+
content: [{ type: "text", text: formatted }],
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
306
|
+
return {
|
|
307
|
+
content: [
|
|
308
|
+
{ type: "text", text: `Search error: ${message}` },
|
|
309
|
+
],
|
|
310
|
+
isError: true,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
// ─────────────────────────────────────────────────────────
|
|
315
|
+
// Tool: fetch_and_index
|
|
316
|
+
// ─────────────────────────────────────────────────────────
|
|
317
|
+
const HTML_TO_MARKDOWN_CODE = `
|
|
318
|
+
const url = process.argv[1];
|
|
319
|
+
if (!url) { console.error("No URL provided"); process.exit(1); }
|
|
320
|
+
|
|
321
|
+
async function main() {
|
|
322
|
+
const resp = await fetch(url);
|
|
323
|
+
if (!resp.ok) { console.error("HTTP " + resp.status); process.exit(1); }
|
|
324
|
+
|
|
325
|
+
let html = await resp.text();
|
|
326
|
+
|
|
327
|
+
// Strip script, style, nav, header, footer tags with content
|
|
328
|
+
html = html.replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, "");
|
|
329
|
+
html = html.replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, "");
|
|
330
|
+
html = html.replace(/<nav[^>]*>[\\s\\S]*?<\\/nav>/gi, "");
|
|
331
|
+
html = html.replace(/<header[^>]*>[\\s\\S]*?<\\/header>/gi, "");
|
|
332
|
+
html = html.replace(/<footer[^>]*>[\\s\\S]*?<\\/footer>/gi, "");
|
|
333
|
+
|
|
334
|
+
// Convert headings to markdown
|
|
335
|
+
html = html.replace(/<h1[^>]*>(.*?)<\\/h1>/gi, "\\n# $1\\n");
|
|
336
|
+
html = html.replace(/<h2[^>]*>(.*?)<\\/h2>/gi, "\\n## $1\\n");
|
|
337
|
+
html = html.replace(/<h3[^>]*>(.*?)<\\/h3>/gi, "\\n### $1\\n");
|
|
338
|
+
html = html.replace(/<h4[^>]*>(.*?)<\\/h4>/gi, "\\n#### $1\\n");
|
|
339
|
+
|
|
340
|
+
// Convert code blocks
|
|
341
|
+
html = html.replace(/<pre[^>]*><code[^>]*class="[^"]*language-(\\w+)"[^>]*>([\\s\\S]*?)<\\/code><\\/pre>/gi,
|
|
342
|
+
(_, lang, code) => "\\n\\\`\\\`\\\`" + lang + "\\n" + decodeEntities(code) + "\\n\\\`\\\`\\\`\\n");
|
|
343
|
+
html = html.replace(/<pre[^>]*><code[^>]*>([\\s\\S]*?)<\\/code><\\/pre>/gi,
|
|
344
|
+
(_, code) => "\\n\\\`\\\`\\\`\\n" + decodeEntities(code) + "\\n\\\`\\\`\\\`\\n");
|
|
345
|
+
html = html.replace(/<code[^>]*>([^<]*)<\\/code>/gi, "\\\`$1\\\`");
|
|
346
|
+
|
|
347
|
+
// Convert links
|
|
348
|
+
html = html.replace(/<a[^>]*href="([^"]*)"[^>]*>(.*?)<\\/a>/gi, "[$2]($1)");
|
|
349
|
+
|
|
350
|
+
// Convert lists
|
|
351
|
+
html = html.replace(/<li[^>]*>(.*?)<\\/li>/gi, "- $1\\n");
|
|
352
|
+
|
|
353
|
+
// Convert paragraphs and line breaks
|
|
354
|
+
html = html.replace(/<p[^>]*>(.*?)<\\/p>/gi, "\\n$1\\n");
|
|
355
|
+
html = html.replace(/<br\\s*\\/?>/gi, "\\n");
|
|
356
|
+
html = html.replace(/<hr\\s*\\/?>/gi, "\\n---\\n");
|
|
357
|
+
|
|
358
|
+
// Strip remaining HTML tags
|
|
359
|
+
html = html.replace(/<[^>]+>/g, "");
|
|
360
|
+
|
|
361
|
+
// Decode HTML entities
|
|
362
|
+
html = decodeEntities(html);
|
|
363
|
+
|
|
364
|
+
// Clean up whitespace
|
|
365
|
+
html = html.replace(/\\n{3,}/g, "\\n\\n").trim();
|
|
366
|
+
|
|
367
|
+
console.log(html);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function decodeEntities(s) {
|
|
371
|
+
return s
|
|
372
|
+
.replace(/&/g, "&")
|
|
373
|
+
.replace(/</g, "<")
|
|
374
|
+
.replace(/>/g, ">")
|
|
375
|
+
.replace(/"/g, '"')
|
|
376
|
+
.replace(/'/g, "'")
|
|
377
|
+
.replace(/'/g, "'")
|
|
378
|
+
.replace(///g, "/")
|
|
379
|
+
.replace(/ /g, " ");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
main();
|
|
383
|
+
`;
|
|
384
|
+
server.registerTool("fetch_and_index", {
|
|
385
|
+
title: "Fetch & Index URL",
|
|
386
|
+
description: "Fetches URL content, converts HTML to markdown, and indexes into the searchable knowledge base. " +
|
|
387
|
+
"Raw content never enters context — only a brief confirmation is returned.\n\n" +
|
|
388
|
+
"Use INSTEAD of WebFetch/Context7 when you need to reference web documentation later via search.\n\n" +
|
|
389
|
+
"After fetching, use 'search' to retrieve specific sections on-demand.",
|
|
390
|
+
inputSchema: z.object({
|
|
391
|
+
url: z.string().describe("The URL to fetch and index"),
|
|
392
|
+
source: z
|
|
393
|
+
.string()
|
|
394
|
+
.optional()
|
|
395
|
+
.describe("Label for the indexed content (e.g., 'React useEffect docs', 'Supabase Auth API')"),
|
|
396
|
+
}),
|
|
397
|
+
}, async ({ url, source }) => {
|
|
398
|
+
try {
|
|
399
|
+
// Execute fetch inside subprocess — raw HTML never enters context
|
|
400
|
+
const fetchCode = `process.argv[1] = ${JSON.stringify(url)};\n${HTML_TO_MARKDOWN_CODE}`;
|
|
401
|
+
const result = await executor.execute({
|
|
402
|
+
language: "javascript",
|
|
403
|
+
code: fetchCode,
|
|
404
|
+
timeout: 30_000,
|
|
405
|
+
});
|
|
406
|
+
if (result.exitCode !== 0) {
|
|
407
|
+
return {
|
|
408
|
+
content: [
|
|
409
|
+
{
|
|
410
|
+
type: "text",
|
|
411
|
+
text: `Failed to fetch ${url}: ${result.stderr || result.stdout}`,
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
isError: true,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
if (!result.stdout || result.stdout.trim().length === 0) {
|
|
418
|
+
return {
|
|
419
|
+
content: [
|
|
420
|
+
{
|
|
421
|
+
type: "text",
|
|
422
|
+
text: `Fetched ${url} but got empty content after HTML conversion`,
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
isError: true,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
// Index the markdown into FTS5
|
|
429
|
+
return indexStdout(result.stdout, source ?? url);
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
433
|
+
return {
|
|
434
|
+
content: [
|
|
435
|
+
{ type: "text", text: `Fetch error: ${message}` },
|
|
436
|
+
],
|
|
437
|
+
isError: true,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
// ─────────────────────────────────────────────────────────
|
|
442
|
+
// Server startup
|
|
443
|
+
// ─────────────────────────────────────────────────────────
|
|
444
|
+
async function main() {
|
|
445
|
+
const transport = new StdioServerTransport();
|
|
446
|
+
await server.connect(transport);
|
|
447
|
+
console.error("Context Mode MCP server v0.4.0 running on stdio");
|
|
448
|
+
console.error(`Detected runtimes:\n${getRuntimeSummary(runtimes)}`);
|
|
449
|
+
if (!hasBunRuntime()) {
|
|
450
|
+
console.error("\nPerformance tip: Install Bun for 3-5x faster JS/TS execution");
|
|
451
|
+
console.error(" curl -fsSL https://bun.sh/install | bash");
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
main().catch((err) => {
|
|
455
|
+
console.error("Fatal:", err);
|
|
456
|
+
process.exit(1);
|
|
457
|
+
});
|
package/build/store.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContentStore — FTS5 BM25-based knowledge base for context-mode.
|
|
3
|
+
*
|
|
4
|
+
* Chunks markdown content by headings (keeping code blocks intact),
|
|
5
|
+
* stores in SQLite FTS5, and retrieves via BM25-ranked search.
|
|
6
|
+
*
|
|
7
|
+
* Use for documentation, API references, and any content where
|
|
8
|
+
* you need EXACT text later — not summaries.
|
|
9
|
+
*/
|
|
10
|
+
export interface IndexResult {
|
|
11
|
+
sourceId: number;
|
|
12
|
+
label: string;
|
|
13
|
+
totalChunks: number;
|
|
14
|
+
codeChunks: number;
|
|
15
|
+
}
|
|
16
|
+
export interface SearchResult {
|
|
17
|
+
title: string;
|
|
18
|
+
content: string;
|
|
19
|
+
source: string;
|
|
20
|
+
rank: number;
|
|
21
|
+
contentType: "code" | "prose";
|
|
22
|
+
}
|
|
23
|
+
export interface StoreStats {
|
|
24
|
+
sources: number;
|
|
25
|
+
chunks: number;
|
|
26
|
+
codeChunks: number;
|
|
27
|
+
}
|
|
28
|
+
export declare class ContentStore {
|
|
29
|
+
#private;
|
|
30
|
+
constructor(dbPath?: string);
|
|
31
|
+
index(options: {
|
|
32
|
+
content?: string;
|
|
33
|
+
path?: string;
|
|
34
|
+
source?: string;
|
|
35
|
+
}): IndexResult;
|
|
36
|
+
search(query: string, limit?: number): SearchResult[];
|
|
37
|
+
getStats(): StoreStats;
|
|
38
|
+
close(): void;
|
|
39
|
+
}
|