mdcontext 0.0.1 → 0.1.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.
Files changed (140) hide show
  1. package/.changeset/README.md +28 -0
  2. package/.changeset/config.json +11 -0
  3. package/.github/workflows/ci.yml +83 -0
  4. package/.github/workflows/release.yml +113 -0
  5. package/.tldrignore +112 -0
  6. package/AGENTS.md +46 -0
  7. package/BACKLOG.md +338 -0
  8. package/README.md +231 -11
  9. package/biome.json +36 -0
  10. package/cspell.config.yaml +14 -0
  11. package/dist/chunk-KRYIFLQR.js +92 -0
  12. package/dist/chunk-S7E6TFX6.js +742 -0
  13. package/dist/chunk-VVTGZNBT.js +1519 -0
  14. package/dist/cli/main.d.ts +1 -0
  15. package/dist/cli/main.js +2015 -0
  16. package/dist/index.d.ts +266 -0
  17. package/dist/index.js +86 -0
  18. package/dist/mcp/server.d.ts +1 -0
  19. package/dist/mcp/server.js +376 -0
  20. package/docs/019-USAGE.md +586 -0
  21. package/docs/020-current-implementation.md +364 -0
  22. package/docs/021-DOGFOODING-FINDINGS.md +175 -0
  23. package/docs/BACKLOG.md +80 -0
  24. package/docs/DESIGN.md +439 -0
  25. package/docs/PROJECT.md +88 -0
  26. package/docs/ROADMAP.md +407 -0
  27. package/docs/test-links.md +9 -0
  28. package/package.json +69 -10
  29. package/pnpm-workspace.yaml +5 -0
  30. package/research/config-analysis/01-current-implementation.md +470 -0
  31. package/research/config-analysis/02-strategy-recommendation.md +428 -0
  32. package/research/config-analysis/03-task-candidates.md +715 -0
  33. package/research/config-analysis/033-research-configuration-management.md +828 -0
  34. package/research/config-analysis/034-research-effect-cli-config.md +1504 -0
  35. package/research/config-analysis/04-consolidated-task-candidates.md +277 -0
  36. package/research/dogfood/consolidated-tool-evaluation.md +373 -0
  37. package/research/dogfood/strategy-a/a-synthesis.md +184 -0
  38. package/research/dogfood/strategy-a/a1-docs.md +226 -0
  39. package/research/dogfood/strategy-a/a2-amorphic.md +156 -0
  40. package/research/dogfood/strategy-a/a3-llm.md +164 -0
  41. package/research/dogfood/strategy-b/b-synthesis.md +228 -0
  42. package/research/dogfood/strategy-b/b1-architecture.md +207 -0
  43. package/research/dogfood/strategy-b/b2-gaps.md +258 -0
  44. package/research/dogfood/strategy-b/b3-workflows.md +250 -0
  45. package/research/dogfood/strategy-c/c-synthesis.md +451 -0
  46. package/research/dogfood/strategy-c/c1-explorer.md +192 -0
  47. package/research/dogfood/strategy-c/c2-diver-memory.md +145 -0
  48. package/research/dogfood/strategy-c/c3-diver-control.md +148 -0
  49. package/research/dogfood/strategy-c/c4-diver-failure.md +151 -0
  50. package/research/dogfood/strategy-c/c5-diver-execution.md +221 -0
  51. package/research/dogfood/strategy-c/c6-diver-org.md +221 -0
  52. package/research/effect-cli-error-handling.md +845 -0
  53. package/research/effect-errors-as-values.md +943 -0
  54. package/research/errors-task-analysis/00-consolidated-tasks.md +207 -0
  55. package/research/errors-task-analysis/cli-commands-analysis.md +909 -0
  56. package/research/errors-task-analysis/embeddings-analysis.md +709 -0
  57. package/research/errors-task-analysis/index-search-analysis.md +812 -0
  58. package/research/mdcontext-error-analysis.md +521 -0
  59. package/research/npm_publish/011-npm-workflow-research-agent2.md +792 -0
  60. package/research/npm_publish/012-npm-workflow-research-agent1.md +530 -0
  61. package/research/npm_publish/013-npm-workflow-research-agent3.md +722 -0
  62. package/research/npm_publish/014-npm-workflow-synthesis.md +556 -0
  63. package/research/npm_publish/031-npm-workflow-task-analysis.md +134 -0
  64. package/research/semantic-search/002-research-embedding-models.md +490 -0
  65. package/research/semantic-search/003-research-rag-alternatives.md +523 -0
  66. package/research/semantic-search/004-research-vector-search.md +841 -0
  67. package/research/semantic-search/032-research-semantic-search.md +427 -0
  68. package/research/task-management-2026/00-synthesis-recommendations.md +295 -0
  69. package/research/task-management-2026/01-ai-workflow-tools.md +416 -0
  70. package/research/task-management-2026/02-agent-framework-patterns.md +476 -0
  71. package/research/task-management-2026/03-lightweight-file-based.md +567 -0
  72. package/research/task-management-2026/04-established-tools-ai-features.md +541 -0
  73. package/research/task-management-2026/linear/01-core-features-workflow.md +771 -0
  74. package/research/task-management-2026/linear/02-api-integrations.md +930 -0
  75. package/research/task-management-2026/linear/03-ai-features.md +368 -0
  76. package/research/task-management-2026/linear/04-pricing-setup.md +205 -0
  77. package/research/task-management-2026/linear/05-usage-patterns-best-practices.md +605 -0
  78. package/scripts/rebuild-hnswlib.js +63 -0
  79. package/src/cli/argv-preprocessor.test.ts +210 -0
  80. package/src/cli/argv-preprocessor.ts +202 -0
  81. package/src/cli/cli.test.ts +430 -0
  82. package/src/cli/commands/backlinks.ts +54 -0
  83. package/src/cli/commands/context.ts +197 -0
  84. package/src/cli/commands/index-cmd.ts +300 -0
  85. package/src/cli/commands/index.ts +13 -0
  86. package/src/cli/commands/links.ts +52 -0
  87. package/src/cli/commands/search.ts +451 -0
  88. package/src/cli/commands/stats.ts +146 -0
  89. package/src/cli/commands/tree.ts +107 -0
  90. package/src/cli/flag-schemas.ts +275 -0
  91. package/src/cli/help.ts +386 -0
  92. package/src/cli/index.ts +9 -0
  93. package/src/cli/main.ts +145 -0
  94. package/src/cli/options.ts +31 -0
  95. package/src/cli/typo-suggester.test.ts +105 -0
  96. package/src/cli/typo-suggester.ts +130 -0
  97. package/src/cli/utils.ts +126 -0
  98. package/src/core/index.ts +1 -0
  99. package/src/core/types.ts +140 -0
  100. package/src/embeddings/index.ts +8 -0
  101. package/src/embeddings/openai-provider.ts +165 -0
  102. package/src/embeddings/semantic-search.ts +583 -0
  103. package/src/embeddings/types.ts +82 -0
  104. package/src/embeddings/vector-store.ts +299 -0
  105. package/src/index/index.ts +4 -0
  106. package/src/index/indexer.ts +446 -0
  107. package/src/index/storage.ts +196 -0
  108. package/src/index/types.ts +109 -0
  109. package/src/index/watcher.ts +131 -0
  110. package/src/index.ts +8 -0
  111. package/src/mcp/server.ts +483 -0
  112. package/src/parser/index.ts +1 -0
  113. package/src/parser/parser.test.ts +291 -0
  114. package/src/parser/parser.ts +395 -0
  115. package/src/parser/section-filter.ts +270 -0
  116. package/src/search/query-parser.test.ts +260 -0
  117. package/src/search/query-parser.ts +319 -0
  118. package/src/search/searcher.test.ts +182 -0
  119. package/src/search/searcher.ts +602 -0
  120. package/src/summarize/budget-bugs.test.ts +620 -0
  121. package/src/summarize/formatters.ts +419 -0
  122. package/src/summarize/index.ts +20 -0
  123. package/src/summarize/summarizer.test.ts +275 -0
  124. package/src/summarize/summarizer.ts +528 -0
  125. package/src/summarize/verify-bugs.test.ts +238 -0
  126. package/src/utils/index.ts +1 -0
  127. package/src/utils/tokens.test.ts +142 -0
  128. package/src/utils/tokens.ts +186 -0
  129. package/tests/fixtures/cli/.mdcontext/config.json +8 -0
  130. package/tests/fixtures/cli/.mdcontext/indexes/documents.json +33 -0
  131. package/tests/fixtures/cli/.mdcontext/indexes/links.json +12 -0
  132. package/tests/fixtures/cli/.mdcontext/indexes/sections.json +233 -0
  133. package/tests/fixtures/cli/.mdcontext/vectors.bin +0 -0
  134. package/tests/fixtures/cli/.mdcontext/vectors.meta.json +1264 -0
  135. package/tests/fixtures/cli/README.md +9 -0
  136. package/tests/fixtures/cli/api-reference.md +11 -0
  137. package/tests/fixtures/cli/getting-started.md +11 -0
  138. package/tsconfig.json +26 -0
  139. package/vitest.config.ts +21 -0
  140. package/vitest.setup.ts +12 -0
@@ -0,0 +1,376 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ formatSummary,
4
+ search,
5
+ semanticSearch,
6
+ summarizeFile
7
+ } from "../chunk-VVTGZNBT.js";
8
+ import {
9
+ buildIndex,
10
+ parseFile
11
+ } from "../chunk-S7E6TFX6.js";
12
+
13
+ // src/mcp/server.ts
14
+ import * as path from "path";
15
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
16
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
17
+ import {
18
+ CallToolRequestSchema,
19
+ ListToolsRequestSchema
20
+ } from "@modelcontextprotocol/sdk/types.js";
21
+ import { Effect } from "effect";
22
+ var tools = [
23
+ {
24
+ name: "md_search",
25
+ description: "Search markdown documents by meaning using semantic search. Returns relevant sections based on natural language queries.",
26
+ inputSchema: {
27
+ type: "object",
28
+ properties: {
29
+ query: {
30
+ type: "string",
31
+ description: "Natural language search query"
32
+ },
33
+ limit: {
34
+ type: "number",
35
+ description: "Maximum number of results (default: 5)",
36
+ default: 5
37
+ },
38
+ path_filter: {
39
+ type: "string",
40
+ description: "Glob pattern to filter files (e.g., '*.md', 'docs/**/*.md')"
41
+ },
42
+ threshold: {
43
+ type: "number",
44
+ description: "Minimum similarity threshold 0-1 (default: 0.5)",
45
+ default: 0.5
46
+ }
47
+ },
48
+ required: ["query"]
49
+ }
50
+ },
51
+ {
52
+ name: "md_context",
53
+ description: "Get LLM-ready context from a markdown file. Provides compressed, token-efficient summaries at various detail levels.",
54
+ inputSchema: {
55
+ type: "object",
56
+ properties: {
57
+ path: {
58
+ type: "string",
59
+ description: "Path to the markdown file"
60
+ },
61
+ level: {
62
+ type: "string",
63
+ enum: ["full", "summary", "brief"],
64
+ description: "Compression level (default: summary)",
65
+ default: "summary"
66
+ },
67
+ max_tokens: {
68
+ type: "number",
69
+ description: "Maximum tokens to include in output"
70
+ }
71
+ },
72
+ required: ["path"]
73
+ }
74
+ },
75
+ {
76
+ name: "md_structure",
77
+ description: "Get the structure/outline of a markdown file. Shows heading hierarchy with token counts.",
78
+ inputSchema: {
79
+ type: "object",
80
+ properties: {
81
+ path: {
82
+ type: "string",
83
+ description: "Path to the markdown file"
84
+ }
85
+ },
86
+ required: ["path"]
87
+ }
88
+ },
89
+ {
90
+ name: "md_keyword_search",
91
+ description: "Search markdown documents by keyword search (headings, code blocks, lists, tables).",
92
+ inputSchema: {
93
+ type: "object",
94
+ properties: {
95
+ heading: {
96
+ type: "string",
97
+ description: "Filter by heading pattern (regex)"
98
+ },
99
+ path_filter: {
100
+ type: "string",
101
+ description: "Glob pattern to filter files"
102
+ },
103
+ has_code: {
104
+ type: "boolean",
105
+ description: "Only sections with code blocks"
106
+ },
107
+ has_list: {
108
+ type: "boolean",
109
+ description: "Only sections with lists"
110
+ },
111
+ has_table: {
112
+ type: "boolean",
113
+ description: "Only sections with tables"
114
+ },
115
+ limit: {
116
+ type: "number",
117
+ description: "Maximum results (default: 20)",
118
+ default: 20
119
+ }
120
+ }
121
+ }
122
+ },
123
+ {
124
+ name: "md_index",
125
+ description: "Build or rebuild the index for a directory. Required before using search tools.",
126
+ inputSchema: {
127
+ type: "object",
128
+ properties: {
129
+ path: {
130
+ type: "string",
131
+ description: "Directory to index (default: current directory)",
132
+ default: "."
133
+ },
134
+ force: {
135
+ type: "boolean",
136
+ description: "Force full rebuild (default: false)",
137
+ default: false
138
+ }
139
+ }
140
+ }
141
+ }
142
+ ];
143
+ var handleMdSearch = async (args, rootPath) => {
144
+ const query = args.query;
145
+ const limit = args.limit ?? 5;
146
+ const pathFilter = args.path_filter;
147
+ const threshold = args.threshold ?? 0.5;
148
+ const result = await Effect.runPromise(
149
+ semanticSearch(rootPath, query, {
150
+ limit,
151
+ threshold,
152
+ pathPattern: pathFilter
153
+ }).pipe(
154
+ Effect.catchAll((e) => Effect.succeed([{ error: e.message }]))
155
+ )
156
+ );
157
+ if (Array.isArray(result) && result.length > 0 && "error" in result[0]) {
158
+ return {
159
+ content: [
160
+ {
161
+ type: "text",
162
+ text: `Error: ${result[0].error}`
163
+ }
164
+ ],
165
+ isError: true
166
+ };
167
+ }
168
+ const formattedResults = result.map((r, i) => {
169
+ const similarity = (r.similarity * 100).toFixed(1);
170
+ return `${i + 1}. **${r.heading}** (${similarity}% match)
171
+ ${r.documentPath}`;
172
+ });
173
+ return {
174
+ content: [
175
+ {
176
+ type: "text",
177
+ text: formattedResults.length > 0 ? `Found ${formattedResults.length} results for "${query}":
178
+
179
+ ${formattedResults.join("\n\n")}` : `No results found for "${query}"`
180
+ }
181
+ ]
182
+ };
183
+ };
184
+ var handleMdContext = async (args, rootPath) => {
185
+ const filePath = args.path;
186
+ const level = args.level ?? "summary";
187
+ const maxTokens = args.max_tokens;
188
+ const resolvedPath = path.isAbsolute(filePath) ? filePath : path.join(rootPath, filePath);
189
+ const result = await Effect.runPromise(
190
+ summarizeFile(resolvedPath, { level, maxTokens }).pipe(
191
+ Effect.catchAll((e) => Effect.succeed({ error: e.message }))
192
+ )
193
+ );
194
+ if ("error" in result) {
195
+ return {
196
+ content: [{ type: "text", text: `Error: ${result.error}` }],
197
+ isError: true
198
+ };
199
+ }
200
+ return {
201
+ content: [{ type: "text", text: formatSummary(result) }]
202
+ };
203
+ };
204
+ var handleMdStructure = async (args, rootPath) => {
205
+ const filePath = args.path;
206
+ const resolvedPath = path.isAbsolute(filePath) ? filePath : path.join(rootPath, filePath);
207
+ const result = await Effect.runPromise(
208
+ parseFile(resolvedPath).pipe(
209
+ Effect.mapError((e) => new Error(`${e._tag}: ${e.message}`)),
210
+ Effect.catchAll((e) => Effect.succeed({ error: e.message }))
211
+ )
212
+ );
213
+ if ("error" in result) {
214
+ return {
215
+ content: [{ type: "text", text: `Error: ${result.error}` }],
216
+ isError: true
217
+ };
218
+ }
219
+ const formatSection = (section, depth = 0) => {
220
+ const indent = " ".repeat(depth);
221
+ const marker = "#".repeat(section.level);
222
+ const meta = [];
223
+ if (section.metadata.hasCode) meta.push("code");
224
+ if (section.metadata.hasList) meta.push("list");
225
+ if (section.metadata.hasTable) meta.push("table");
226
+ const metaStr = meta.length > 0 ? ` [${meta.join(", ")}]` : "";
227
+ let output = `${indent}${marker} ${section.heading}${metaStr} (${section.metadata.tokenCount} tokens)
228
+ `;
229
+ for (const child of section.children) {
230
+ output += formatSection(child, depth + 1);
231
+ }
232
+ return output;
233
+ };
234
+ const structure = result.sections.map((s) => formatSection(s)).join("");
235
+ return {
236
+ content: [
237
+ {
238
+ type: "text",
239
+ text: `# ${result.title}
240
+ Path: ${result.path}
241
+ Total tokens: ${result.metadata.tokenCount}
242
+
243
+ ${structure}`
244
+ }
245
+ ]
246
+ };
247
+ };
248
+ var handleMdKeywordSearch = async (args, rootPath) => {
249
+ const heading = args.heading;
250
+ const pathFilter = args.path_filter;
251
+ const hasCode = args.has_code;
252
+ const hasList = args.has_list;
253
+ const hasTable = args.has_table;
254
+ const limit = args.limit ?? 20;
255
+ const result = await Effect.runPromise(
256
+ search(rootPath, {
257
+ heading,
258
+ pathPattern: pathFilter,
259
+ hasCode,
260
+ hasList,
261
+ hasTable,
262
+ limit
263
+ }).pipe(
264
+ Effect.catchAll((e) => Effect.succeed([{ error: e.message }]))
265
+ )
266
+ );
267
+ if (Array.isArray(result) && result.length > 0 && "error" in result[0]) {
268
+ return {
269
+ content: [
270
+ {
271
+ type: "text",
272
+ text: `Error: ${result[0].error}`
273
+ }
274
+ ],
275
+ isError: true
276
+ };
277
+ }
278
+ const formattedResults = result.map((r, i) => {
279
+ const meta = [];
280
+ if (r.section.hasCode) meta.push("code");
281
+ if (r.section.hasList) meta.push("list");
282
+ if (r.section.hasTable) meta.push("table");
283
+ const metaStr = meta.length > 0 ? ` [${meta.join(", ")}]` : "";
284
+ return `${i + 1}. **${r.section.heading}**${metaStr}
285
+ ${r.section.documentPath} (${r.section.tokenCount} tokens)`;
286
+ });
287
+ return {
288
+ content: [
289
+ {
290
+ type: "text",
291
+ text: formattedResults.length > 0 ? `Found ${formattedResults.length} sections:
292
+
293
+ ${formattedResults.join("\n\n")}` : "No sections found matching criteria"
294
+ }
295
+ ]
296
+ };
297
+ };
298
+ var handleMdIndex = async (args, rootPath) => {
299
+ const indexPath = args.path ?? ".";
300
+ const force = args.force ?? false;
301
+ const resolvedPath = path.isAbsolute(indexPath) ? indexPath : path.join(rootPath, indexPath);
302
+ const result = await Effect.runPromise(
303
+ buildIndex(resolvedPath, { force }).pipe(
304
+ Effect.catchAll((e) => Effect.succeed({ error: e.message }))
305
+ )
306
+ );
307
+ if ("error" in result) {
308
+ return {
309
+ content: [{ type: "text", text: `Error: ${result.error}` }],
310
+ isError: true
311
+ };
312
+ }
313
+ return {
314
+ content: [
315
+ {
316
+ type: "text",
317
+ text: `Indexed ${result.documentsIndexed} documents, ${result.sectionsIndexed} sections, ${result.linksIndexed} links in ${result.duration}ms`
318
+ }
319
+ ]
320
+ };
321
+ };
322
+ var createServer = (rootPath) => {
323
+ const server = new Server(
324
+ {
325
+ name: "mdcontext-mcp",
326
+ version: "0.1.0"
327
+ },
328
+ {
329
+ capabilities: {
330
+ tools: {}
331
+ }
332
+ }
333
+ );
334
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
335
+ tools
336
+ }));
337
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
338
+ const { name, arguments: args } = request.params;
339
+ switch (name) {
340
+ case "md_search":
341
+ return handleMdSearch(args ?? {}, rootPath);
342
+ case "md_context":
343
+ return handleMdContext(args ?? {}, rootPath);
344
+ case "md_structure":
345
+ return handleMdStructure(args ?? {}, rootPath);
346
+ case "md_keyword_search":
347
+ return handleMdKeywordSearch(args ?? {}, rootPath);
348
+ case "md_index":
349
+ return handleMdIndex(args ?? {}, rootPath);
350
+ default:
351
+ return {
352
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
353
+ isError: true
354
+ };
355
+ }
356
+ });
357
+ return server;
358
+ };
359
+ var main = async () => {
360
+ const rootPath = process.cwd();
361
+ const server = createServer(rootPath);
362
+ const transport = new StdioServerTransport();
363
+ await server.connect(transport);
364
+ process.on("SIGINT", async () => {
365
+ await server.close();
366
+ process.exit(0);
367
+ });
368
+ process.on("SIGTERM", async () => {
369
+ await server.close();
370
+ process.exit(0);
371
+ });
372
+ };
373
+ main().catch((error) => {
374
+ console.error("Fatal error:", error);
375
+ process.exit(1);
376
+ });