mdcontext 0.0.1 → 0.2.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 (337) hide show
  1. package/.changeset/README.md +28 -0
  2. package/.changeset/config.json +11 -0
  3. package/.claude/settings.local.json +25 -0
  4. package/.github/workflows/ci.yml +83 -0
  5. package/.github/workflows/claude-code-review.yml +44 -0
  6. package/.github/workflows/claude.yml +85 -0
  7. package/.github/workflows/release.yml +113 -0
  8. package/.tldrignore +112 -0
  9. package/BACKLOG.md +338 -0
  10. package/CONTRIBUTING.md +186 -0
  11. package/NOTES/NOTES +44 -0
  12. package/README.md +434 -11
  13. package/biome.json +36 -0
  14. package/cspell.config.yaml +14 -0
  15. package/dist/chunk-23UPXDNL.js +3044 -0
  16. package/dist/chunk-2W7MO2DL.js +1366 -0
  17. package/dist/chunk-3NUAZGMA.js +1689 -0
  18. package/dist/chunk-7TOWB2XB.js +366 -0
  19. package/dist/chunk-7XOTOADQ.js +3065 -0
  20. package/dist/chunk-AH2PDM2K.js +3042 -0
  21. package/dist/chunk-BNXWSZ63.js +3742 -0
  22. package/dist/chunk-BTL5DJVU.js +3222 -0
  23. package/dist/chunk-HDHYG7E4.js +104 -0
  24. package/dist/chunk-HLR4KZBP.js +3234 -0
  25. package/dist/chunk-IP3FRFEB.js +1045 -0
  26. package/dist/chunk-KHU56VDO.js +3042 -0
  27. package/dist/chunk-KRYIFLQR.js +88 -0
  28. package/dist/chunk-LBSDNLEM.js +287 -0
  29. package/dist/chunk-MNTQ7HCP.js +2643 -0
  30. package/dist/chunk-MUJELQQ6.js +1387 -0
  31. package/dist/chunk-MXJGMSLV.js +2199 -0
  32. package/dist/chunk-N6QJGC3Z.js +2636 -0
  33. package/dist/chunk-OBELGBPM.js +1713 -0
  34. package/dist/chunk-OT7R5XTA.js +3192 -0
  35. package/dist/chunk-P7X4RA2T.js +106 -0
  36. package/dist/chunk-PIDUQNC2.js +3185 -0
  37. package/dist/chunk-POGCDIH4.js +3187 -0
  38. package/dist/chunk-PSIEOQGZ.js +3043 -0
  39. package/dist/chunk-PVRT3IHA.js +3238 -0
  40. package/dist/chunk-QNN4TT23.js +1430 -0
  41. package/dist/chunk-RE3R45RJ.js +3042 -0
  42. package/dist/chunk-S7E6TFX6.js +803 -0
  43. package/dist/chunk-SG6GLU4U.js +1378 -0
  44. package/dist/chunk-SJCDV2ST.js +274 -0
  45. package/dist/chunk-SYE5XLF3.js +104 -0
  46. package/dist/chunk-T5VLYBZD.js +103 -0
  47. package/dist/chunk-TOQB7VWU.js +3238 -0
  48. package/dist/chunk-VFNMZ4ZQ.js +3228 -0
  49. package/dist/chunk-VVTGZNBT.js +1629 -0
  50. package/dist/chunk-W7Q4RFEV.js +104 -0
  51. package/dist/chunk-XTYYVRLO.js +3190 -0
  52. package/dist/chunk-Y6MDYVJD.js +3063 -0
  53. package/dist/cli/main.d.ts +1 -0
  54. package/dist/cli/main.js +5458 -0
  55. package/dist/index.d.ts +653 -0
  56. package/dist/index.js +79 -0
  57. package/dist/mcp/server.d.ts +1 -0
  58. package/dist/mcp/server.js +472 -0
  59. package/dist/schema-BAWSG7KY.js +22 -0
  60. package/dist/schema-E3QUPL26.js +20 -0
  61. package/dist/schema-EHL7WUT6.js +20 -0
  62. package/docs/019-USAGE.md +625 -0
  63. package/docs/020-current-implementation.md +364 -0
  64. package/docs/021-DOGFOODING-FINDINGS.md +175 -0
  65. package/docs/BACKLOG.md +80 -0
  66. package/docs/CONFIG.md +1123 -0
  67. package/docs/DESIGN.md +439 -0
  68. package/docs/ERRORS.md +383 -0
  69. package/docs/PROJECT.md +88 -0
  70. package/docs/ROADMAP.md +407 -0
  71. package/docs/summarization.md +320 -0
  72. package/docs/test-links.md +9 -0
  73. package/justfile +40 -0
  74. package/package.json +74 -9
  75. package/pnpm-workspace.yaml +5 -0
  76. package/research/INDEX.md +315 -0
  77. package/research/code-review/README.md +90 -0
  78. package/research/code-review/cli-error-handling-review.md +979 -0
  79. package/research/code-review/code-review-validation-report.md +464 -0
  80. package/research/code-review/main-ts-review.md +1128 -0
  81. package/research/config-analysis/01-current-implementation.md +470 -0
  82. package/research/config-analysis/02-strategy-recommendation.md +428 -0
  83. package/research/config-analysis/03-task-candidates.md +715 -0
  84. package/research/config-analysis/033-research-configuration-management.md +828 -0
  85. package/research/config-analysis/034-research-effect-cli-config.md +1504 -0
  86. package/research/config-analysis/04-consolidated-task-candidates.md +277 -0
  87. package/research/config-docs/SUMMARY.md +357 -0
  88. package/research/config-docs/TEST-RESULTS.md +776 -0
  89. package/research/config-docs/TODO.md +542 -0
  90. package/research/config-docs/analysis.md +744 -0
  91. package/research/config-docs/fix-validation.md +502 -0
  92. package/research/config-docs/help-audit.md +264 -0
  93. package/research/config-docs/help-system-analysis.md +890 -0
  94. package/research/dogfood/consolidated-tool-evaluation.md +373 -0
  95. package/research/dogfood/strategy-a/a-synthesis.md +184 -0
  96. package/research/dogfood/strategy-a/a1-docs.md +226 -0
  97. package/research/dogfood/strategy-a/a2-amorphic.md +156 -0
  98. package/research/dogfood/strategy-a/a3-llm.md +164 -0
  99. package/research/dogfood/strategy-b/b-synthesis.md +228 -0
  100. package/research/dogfood/strategy-b/b1-architecture.md +207 -0
  101. package/research/dogfood/strategy-b/b2-gaps.md +258 -0
  102. package/research/dogfood/strategy-b/b3-workflows.md +250 -0
  103. package/research/dogfood/strategy-c/c-synthesis.md +451 -0
  104. package/research/dogfood/strategy-c/c1-explorer.md +192 -0
  105. package/research/dogfood/strategy-c/c2-diver-memory.md +145 -0
  106. package/research/dogfood/strategy-c/c3-diver-control.md +148 -0
  107. package/research/dogfood/strategy-c/c4-diver-failure.md +151 -0
  108. package/research/dogfood/strategy-c/c5-diver-execution.md +221 -0
  109. package/research/dogfood/strategy-c/c6-diver-org.md +221 -0
  110. package/research/effect-cli-error-handling.md +845 -0
  111. package/research/effect-errors-as-values.md +943 -0
  112. package/research/errors-task-analysis/00-consolidated-tasks.md +207 -0
  113. package/research/errors-task-analysis/cli-commands-analysis.md +909 -0
  114. package/research/errors-task-analysis/embeddings-analysis.md +709 -0
  115. package/research/errors-task-analysis/index-search-analysis.md +812 -0
  116. package/research/frontmatter/COMMENTS-ARE-SKIPPED.md +149 -0
  117. package/research/frontmatter/LLM-CODE-NAVIGATION.md +276 -0
  118. package/research/issue-review.md +603 -0
  119. package/research/llm-summarization/agent-cli-tools-2026.md +1082 -0
  120. package/research/llm-summarization/alternative-providers-2026.md +1428 -0
  121. package/research/llm-summarization/anthropic-2026.md +367 -0
  122. package/research/llm-summarization/claude-cli-integration.md +1706 -0
  123. package/research/llm-summarization/cli-integration-patterns.md +3155 -0
  124. package/research/llm-summarization/openai-2026.md +473 -0
  125. package/research/llm-summarization/openai-compatible-providers-2026.md +1022 -0
  126. package/research/llm-summarization/opencode-cli-integration.md +1552 -0
  127. package/research/llm-summarization/prompt-engineering-2026.md +1426 -0
  128. package/research/llm-summarization/prototype-results.md +56 -0
  129. package/research/llm-summarization/provider-switching-patterns-2026.md +2153 -0
  130. package/research/llm-summarization/typescript-llm-libraries-2026.md +2436 -0
  131. package/research/mdcontext-error-analysis.md +521 -0
  132. package/research/mdcontext-pudding/00-EXECUTIVE-SUMMARY.md +282 -0
  133. package/research/mdcontext-pudding/01-index-embed.md +956 -0
  134. package/research/mdcontext-pudding/02-search-COMMANDS.md +142 -0
  135. package/research/mdcontext-pudding/02-search-SUMMARY.md +146 -0
  136. package/research/mdcontext-pudding/02-search.md +970 -0
  137. package/research/mdcontext-pudding/03-context.md +779 -0
  138. package/research/mdcontext-pudding/04-navigation-and-analytics.md +803 -0
  139. package/research/mdcontext-pudding/04-tree.md +704 -0
  140. package/research/mdcontext-pudding/05-config.md +1038 -0
  141. package/research/mdcontext-pudding/06-links-summary.txt +87 -0
  142. package/research/mdcontext-pudding/06-links.md +679 -0
  143. package/research/mdcontext-pudding/07-stats.md +693 -0
  144. package/research/mdcontext-pudding/BUG-FIX-PLAN.md +388 -0
  145. package/research/mdcontext-pudding/P0-BUG-VALIDATION.md +167 -0
  146. package/research/mdcontext-pudding/README.md +168 -0
  147. package/research/mdcontext-pudding/TESTING-SUMMARY.md +128 -0
  148. package/research/npm_publish/011-npm-workflow-research-agent2.md +792 -0
  149. package/research/npm_publish/012-npm-workflow-research-agent1.md +530 -0
  150. package/research/npm_publish/013-npm-workflow-research-agent3.md +722 -0
  151. package/research/npm_publish/014-npm-workflow-synthesis.md +556 -0
  152. package/research/npm_publish/031-npm-workflow-task-analysis.md +134 -0
  153. package/research/research-quality-review.md +834 -0
  154. package/research/semantic-search/002-research-embedding-models.md +490 -0
  155. package/research/semantic-search/003-research-rag-alternatives.md +523 -0
  156. package/research/semantic-search/004-research-vector-search.md +841 -0
  157. package/research/semantic-search/032-research-semantic-search.md +427 -0
  158. package/research/semantic-search/embedding-text-analysis.md +156 -0
  159. package/research/semantic-search/multi-word-failure-reproduction.md +171 -0
  160. package/research/semantic-search/query-processing-analysis.md +207 -0
  161. package/research/semantic-search/root-cause-and-solution.md +114 -0
  162. package/research/semantic-search/threshold-validation-report.md +69 -0
  163. package/research/semantic-search/vector-search-analysis.md +63 -0
  164. package/research/task-management-2026/00-synthesis-recommendations.md +295 -0
  165. package/research/task-management-2026/01-ai-workflow-tools.md +416 -0
  166. package/research/task-management-2026/02-agent-framework-patterns.md +476 -0
  167. package/research/task-management-2026/03-lightweight-file-based.md +567 -0
  168. package/research/task-management-2026/04-established-tools-ai-features.md +541 -0
  169. package/research/task-management-2026/linear/01-core-features-workflow.md +771 -0
  170. package/research/task-management-2026/linear/02-api-integrations.md +930 -0
  171. package/research/task-management-2026/linear/03-ai-features.md +368 -0
  172. package/research/task-management-2026/linear/04-pricing-setup.md +205 -0
  173. package/research/task-management-2026/linear/05-usage-patterns-best-practices.md +605 -0
  174. package/research/test-path-issues.md +276 -0
  175. package/review/ALP-76/1-error-type-design.md +962 -0
  176. package/review/ALP-76/2-error-handling-patterns.md +906 -0
  177. package/review/ALP-76/3-error-presentation.md +624 -0
  178. package/review/ALP-76/4-test-coverage.md +625 -0
  179. package/review/ALP-76/5-migration-completeness.md +440 -0
  180. package/review/ALP-76/6-effect-best-practices.md +755 -0
  181. package/scripts/apply-branch-protection.sh +47 -0
  182. package/scripts/branch-protection-templates.json +79 -0
  183. package/scripts/prototype-summarization.ts +346 -0
  184. package/scripts/rebuild-hnswlib.js +58 -0
  185. package/scripts/setup-branch-protection.sh +64 -0
  186. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/active-provider.json +7 -0
  187. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.json +541 -0
  188. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.meta.json +5 -0
  189. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/config.json +8 -0
  190. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
  191. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
  192. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/documents.json +60 -0
  193. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/links.json +13 -0
  194. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/sections.json +1197 -0
  195. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/configuration-management.md +99 -0
  196. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/distributed-systems.md +92 -0
  197. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/error-handling.md +78 -0
  198. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/failure-automation.md +55 -0
  199. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/job-context.md +69 -0
  200. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/process-orchestration.md +99 -0
  201. package/src/cli/argv-preprocessor.test.ts +210 -0
  202. package/src/cli/argv-preprocessor.ts +202 -0
  203. package/src/cli/cli.test.ts +627 -0
  204. package/src/cli/commands/backlinks.ts +54 -0
  205. package/src/cli/commands/config-cmd.ts +642 -0
  206. package/src/cli/commands/context.ts +285 -0
  207. package/src/cli/commands/duplicates.ts +122 -0
  208. package/src/cli/commands/embeddings.ts +529 -0
  209. package/src/cli/commands/index-cmd.ts +480 -0
  210. package/src/cli/commands/index.ts +16 -0
  211. package/src/cli/commands/links.ts +52 -0
  212. package/src/cli/commands/search.ts +1281 -0
  213. package/src/cli/commands/stats.ts +149 -0
  214. package/src/cli/commands/tree.ts +128 -0
  215. package/src/cli/config-layer.ts +176 -0
  216. package/src/cli/error-handler.test.ts +235 -0
  217. package/src/cli/error-handler.ts +655 -0
  218. package/src/cli/flag-schemas.ts +341 -0
  219. package/src/cli/help.ts +588 -0
  220. package/src/cli/index.ts +9 -0
  221. package/src/cli/main.ts +435 -0
  222. package/src/cli/options.ts +41 -0
  223. package/src/cli/shared-error-handling.ts +199 -0
  224. package/src/cli/typo-suggester.test.ts +105 -0
  225. package/src/cli/typo-suggester.ts +130 -0
  226. package/src/cli/utils.ts +259 -0
  227. package/src/config/file-provider.test.ts +320 -0
  228. package/src/config/file-provider.ts +273 -0
  229. package/src/config/index.ts +72 -0
  230. package/src/config/integration.test.ts +667 -0
  231. package/src/config/precedence.test.ts +277 -0
  232. package/src/config/precedence.ts +451 -0
  233. package/src/config/schema.test.ts +414 -0
  234. package/src/config/schema.ts +603 -0
  235. package/src/config/service.test.ts +320 -0
  236. package/src/config/service.ts +243 -0
  237. package/src/config/testing.test.ts +264 -0
  238. package/src/config/testing.ts +110 -0
  239. package/src/core/index.ts +1 -0
  240. package/src/core/types.ts +113 -0
  241. package/src/duplicates/detector.test.ts +183 -0
  242. package/src/duplicates/detector.ts +414 -0
  243. package/src/duplicates/index.ts +18 -0
  244. package/src/embeddings/embedding-namespace.test.ts +300 -0
  245. package/src/embeddings/embedding-namespace.ts +947 -0
  246. package/src/embeddings/heading-boost.test.ts +222 -0
  247. package/src/embeddings/hnsw-build-options.test.ts +198 -0
  248. package/src/embeddings/hyde.test.ts +272 -0
  249. package/src/embeddings/hyde.ts +264 -0
  250. package/src/embeddings/index.ts +10 -0
  251. package/src/embeddings/openai-provider.ts +414 -0
  252. package/src/embeddings/pricing.json +22 -0
  253. package/src/embeddings/provider-constants.ts +204 -0
  254. package/src/embeddings/provider-errors.test.ts +967 -0
  255. package/src/embeddings/provider-errors.ts +565 -0
  256. package/src/embeddings/provider-factory.test.ts +240 -0
  257. package/src/embeddings/provider-factory.ts +225 -0
  258. package/src/embeddings/provider-integration.test.ts +788 -0
  259. package/src/embeddings/query-preprocessing.test.ts +187 -0
  260. package/src/embeddings/semantic-search-threshold.test.ts +508 -0
  261. package/src/embeddings/semantic-search.ts +1270 -0
  262. package/src/embeddings/types.ts +359 -0
  263. package/src/embeddings/vector-store.ts +708 -0
  264. package/src/embeddings/voyage-provider.ts +313 -0
  265. package/src/errors/errors.test.ts +845 -0
  266. package/src/errors/index.ts +533 -0
  267. package/src/index/ignore-patterns.test.ts +354 -0
  268. package/src/index/ignore-patterns.ts +305 -0
  269. package/src/index/index.ts +4 -0
  270. package/src/index/indexer.ts +684 -0
  271. package/src/index/storage.ts +260 -0
  272. package/src/index/types.ts +147 -0
  273. package/src/index/watcher.ts +189 -0
  274. package/src/index.ts +30 -0
  275. package/src/integration/search-keyword.test.ts +678 -0
  276. package/src/mcp/server.ts +612 -0
  277. package/src/parser/index.ts +1 -0
  278. package/src/parser/parser.test.ts +291 -0
  279. package/src/parser/parser.ts +394 -0
  280. package/src/parser/section-filter.test.ts +277 -0
  281. package/src/parser/section-filter.ts +392 -0
  282. package/src/search/__tests__/hybrid-search.test.ts +650 -0
  283. package/src/search/bm25-store.ts +366 -0
  284. package/src/search/cross-encoder.test.ts +253 -0
  285. package/src/search/cross-encoder.ts +406 -0
  286. package/src/search/fuzzy-search.test.ts +419 -0
  287. package/src/search/fuzzy-search.ts +273 -0
  288. package/src/search/hybrid-search.ts +448 -0
  289. package/src/search/path-matcher.test.ts +276 -0
  290. package/src/search/path-matcher.ts +33 -0
  291. package/src/search/query-parser.test.ts +260 -0
  292. package/src/search/query-parser.ts +319 -0
  293. package/src/search/searcher.test.ts +280 -0
  294. package/src/search/searcher.ts +724 -0
  295. package/src/search/wink-bm25.d.ts +30 -0
  296. package/src/summarization/cli-providers/claude.ts +202 -0
  297. package/src/summarization/cli-providers/detection.test.ts +273 -0
  298. package/src/summarization/cli-providers/detection.ts +118 -0
  299. package/src/summarization/cli-providers/index.ts +8 -0
  300. package/src/summarization/cost.test.ts +139 -0
  301. package/src/summarization/cost.ts +102 -0
  302. package/src/summarization/error-handler.test.ts +127 -0
  303. package/src/summarization/error-handler.ts +111 -0
  304. package/src/summarization/index.ts +102 -0
  305. package/src/summarization/pipeline.test.ts +498 -0
  306. package/src/summarization/pipeline.ts +231 -0
  307. package/src/summarization/prompts.test.ts +269 -0
  308. package/src/summarization/prompts.ts +133 -0
  309. package/src/summarization/provider-factory.test.ts +396 -0
  310. package/src/summarization/provider-factory.ts +178 -0
  311. package/src/summarization/types.ts +184 -0
  312. package/src/summarize/budget-bugs.test.ts +620 -0
  313. package/src/summarize/formatters.ts +419 -0
  314. package/src/summarize/index.ts +20 -0
  315. package/src/summarize/summarizer.test.ts +275 -0
  316. package/src/summarize/summarizer.ts +597 -0
  317. package/src/summarize/verify-bugs.test.ts +238 -0
  318. package/src/types/huggingface-transformers.d.ts +66 -0
  319. package/src/utils/index.ts +1 -0
  320. package/src/utils/tokens.test.ts +142 -0
  321. package/src/utils/tokens.ts +186 -0
  322. package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -0
  323. package/tests/fixtures/cli/.mdcontext/config.json +8 -0
  324. package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
  325. package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
  326. package/tests/fixtures/cli/.mdcontext/indexes/documents.json +33 -0
  327. package/tests/fixtures/cli/.mdcontext/indexes/links.json +12 -0
  328. package/tests/fixtures/cli/.mdcontext/indexes/sections.json +247 -0
  329. package/tests/fixtures/cli/README.md +9 -0
  330. package/tests/fixtures/cli/api-reference.md +11 -0
  331. package/tests/fixtures/cli/getting-started.md +11 -0
  332. package/tests/integration/embed-index.test.ts +712 -0
  333. package/tests/integration/search-context.test.ts +469 -0
  334. package/tests/integration/search-semantic.test.ts +522 -0
  335. package/tsconfig.json +26 -0
  336. package/vitest.config.ts +16 -0
  337. package/vitest.setup.ts +12 -0
@@ -0,0 +1,612 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MCP Server for mdcontext
5
+ *
6
+ * Exposes markdown analysis tools for Claude integration
7
+ */
8
+
9
+ import { createRequire } from 'node:module'
10
+ import * as path from 'node:path'
11
+
12
+ // Read version from package.json using createRequire for ESM compatibility
13
+ const require = createRequire(import.meta.url)
14
+ const packageJson = require('../../package.json') as { version: string }
15
+ const MCP_VERSION: string = packageJson.version
16
+
17
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js'
18
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
19
+ import type { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js'
20
+ import {
21
+ CallToolRequestSchema,
22
+ ListToolsRequestSchema,
23
+ } from '@modelcontextprotocol/sdk/types.js'
24
+ import { Effect } from 'effect'
25
+ import type { MdSection } from '../core/types.js'
26
+ import { semanticSearch } from '../embeddings/semantic-search.js'
27
+ import {
28
+ buildIndex,
29
+ getIncomingLinks,
30
+ getOutgoingLinks,
31
+ } from '../index/indexer.js'
32
+ import { parseFile } from '../parser/parser.js'
33
+ import { search } from '../search/searcher.js'
34
+ import { formatSummary, summarizeFile } from '../summarize/summarizer.js'
35
+
36
+ // Type alias for tool results - uses the SDK type
37
+
38
+ // ============================================================================
39
+ // Tool Definitions
40
+ // ============================================================================
41
+
42
+ const tools: Tool[] = [
43
+ {
44
+ name: 'md_search',
45
+ description:
46
+ 'Search markdown documents by meaning using semantic search. Returns relevant sections based on natural language queries.',
47
+ inputSchema: {
48
+ type: 'object',
49
+ properties: {
50
+ query: {
51
+ type: 'string',
52
+ description: 'Natural language search query',
53
+ },
54
+ limit: {
55
+ type: 'number',
56
+ description: 'Maximum number of results (default: 5)',
57
+ default: 5,
58
+ },
59
+ path_filter: {
60
+ type: 'string',
61
+ description:
62
+ "Glob pattern to filter files (e.g., '*.md', 'docs/**/*.md')",
63
+ },
64
+ threshold: {
65
+ type: 'number',
66
+ description: 'Minimum similarity threshold 0-1 (default: 0.35)',
67
+ default: 0.35,
68
+ },
69
+ },
70
+ required: ['query'],
71
+ },
72
+ },
73
+ {
74
+ name: 'md_context',
75
+ description:
76
+ 'Get LLM-ready context from a markdown file. Provides compressed, token-efficient summaries at various detail levels.',
77
+ inputSchema: {
78
+ type: 'object',
79
+ properties: {
80
+ path: {
81
+ type: 'string',
82
+ description: 'Path to the markdown file',
83
+ },
84
+ level: {
85
+ type: 'string',
86
+ enum: ['full', 'summary', 'brief'],
87
+ description: 'Compression level (default: summary)',
88
+ default: 'summary',
89
+ },
90
+ max_tokens: {
91
+ type: 'number',
92
+ description: 'Maximum tokens to include in output',
93
+ },
94
+ },
95
+ required: ['path'],
96
+ },
97
+ },
98
+ {
99
+ name: 'md_structure',
100
+ description:
101
+ 'Get the structure/outline of a markdown file. Shows heading hierarchy with token counts.',
102
+ inputSchema: {
103
+ type: 'object',
104
+ properties: {
105
+ path: {
106
+ type: 'string',
107
+ description: 'Path to the markdown file',
108
+ },
109
+ },
110
+ required: ['path'],
111
+ },
112
+ },
113
+ {
114
+ name: 'md_keyword_search',
115
+ description:
116
+ 'Search markdown documents by keyword search (headings, code blocks, lists, tables).',
117
+ inputSchema: {
118
+ type: 'object',
119
+ properties: {
120
+ heading: {
121
+ type: 'string',
122
+ description: 'Filter by heading pattern (regex)',
123
+ },
124
+ path_filter: {
125
+ type: 'string',
126
+ description: 'Glob pattern to filter files',
127
+ },
128
+ has_code: {
129
+ type: 'boolean',
130
+ description: 'Only sections with code blocks',
131
+ },
132
+ has_list: {
133
+ type: 'boolean',
134
+ description: 'Only sections with lists',
135
+ },
136
+ has_table: {
137
+ type: 'boolean',
138
+ description: 'Only sections with tables',
139
+ },
140
+ limit: {
141
+ type: 'number',
142
+ description: 'Maximum results (default: 20)',
143
+ default: 20,
144
+ },
145
+ },
146
+ },
147
+ },
148
+ {
149
+ name: 'md_index',
150
+ description:
151
+ 'Build or rebuild the index for a directory. Required before using search tools.',
152
+ inputSchema: {
153
+ type: 'object',
154
+ properties: {
155
+ path: {
156
+ type: 'string',
157
+ description: 'Directory to index (default: current directory)',
158
+ default: '.',
159
+ },
160
+ force: {
161
+ type: 'boolean',
162
+ description: 'Force full rebuild (default: false)',
163
+ default: false,
164
+ },
165
+ },
166
+ },
167
+ },
168
+ {
169
+ name: 'md_links',
170
+ description:
171
+ 'Get outgoing links from a markdown file. Shows what files this document references/links to.',
172
+ inputSchema: {
173
+ type: 'object',
174
+ properties: {
175
+ path: {
176
+ type: 'string',
177
+ description: 'Path to the markdown file',
178
+ },
179
+ },
180
+ required: ['path'],
181
+ },
182
+ },
183
+ {
184
+ name: 'md_backlinks',
185
+ description:
186
+ 'Get incoming links to a markdown file. Shows what files reference/link to this document.',
187
+ inputSchema: {
188
+ type: 'object',
189
+ properties: {
190
+ path: {
191
+ type: 'string',
192
+ description: 'Path to the markdown file',
193
+ },
194
+ },
195
+ required: ['path'],
196
+ },
197
+ },
198
+ ]
199
+
200
+ // ============================================================================
201
+ // Tool Handlers
202
+ // ============================================================================
203
+
204
+ const handleMdSearch = async (
205
+ args: Record<string, unknown>,
206
+ rootPath: string,
207
+ ): Promise<CallToolResult> => {
208
+ const query = args.query as string
209
+ const limit = (args.limit as number) ?? 5
210
+ const pathFilter = args.path_filter as string | undefined
211
+ const threshold = (args.threshold as number) ?? 0.35
212
+
213
+ // Note: catchAll is intentional at this MCP boundary layer.
214
+ // MCP protocol requires JSON error responses, so we convert typed errors
215
+ // to { error: message } format for protocol compliance.
216
+ const result = await Effect.runPromise(
217
+ semanticSearch(rootPath, query, {
218
+ limit,
219
+ threshold,
220
+ pathPattern: pathFilter,
221
+ }).pipe(
222
+ Effect.catchAll((e) => Effect.succeed([{ error: e.message }] as const)),
223
+ ),
224
+ )
225
+
226
+ if (Array.isArray(result) && result.length > 0 && 'error' in result[0]) {
227
+ return {
228
+ content: [
229
+ {
230
+ type: 'text',
231
+ text: `Error: ${(result[0] as { error: string }).error}`,
232
+ },
233
+ ],
234
+ isError: true,
235
+ }
236
+ }
237
+
238
+ const formattedResults = (
239
+ result as Array<{
240
+ sectionId: string
241
+ documentPath: string
242
+ heading: string
243
+ similarity: number
244
+ }>
245
+ ).map((r, i) => {
246
+ const similarity = (r.similarity * 100).toFixed(1)
247
+ return `${i + 1}. **${r.heading}** (${similarity}% match)\n ${r.documentPath}`
248
+ })
249
+
250
+ return {
251
+ content: [
252
+ {
253
+ type: 'text',
254
+ text:
255
+ formattedResults.length > 0
256
+ ? `Found ${formattedResults.length} results for "${query}":\n\n${formattedResults.join('\n\n')}`
257
+ : `No results found for "${query}"`,
258
+ },
259
+ ],
260
+ }
261
+ }
262
+
263
+ const handleMdContext = async (
264
+ args: Record<string, unknown>,
265
+ rootPath: string,
266
+ ): Promise<CallToolResult> => {
267
+ const filePath = args.path as string
268
+ const level = (args.level as 'brief' | 'summary' | 'full') ?? 'summary'
269
+ const maxTokens = args.max_tokens as number | undefined
270
+
271
+ const resolvedPath = path.isAbsolute(filePath)
272
+ ? filePath
273
+ : path.join(rootPath, filePath)
274
+
275
+ // Note: catchAll is intentional - MCP boundary converts errors to JSON format
276
+ const result = await Effect.runPromise(
277
+ summarizeFile(resolvedPath, { level, maxTokens }).pipe(
278
+ Effect.catchAll((e) => Effect.succeed({ error: e.message })),
279
+ ),
280
+ )
281
+
282
+ if ('error' in result) {
283
+ return {
284
+ content: [{ type: 'text', text: `Error: ${result.error}` }],
285
+ isError: true,
286
+ }
287
+ }
288
+
289
+ return {
290
+ content: [{ type: 'text', text: formatSummary(result) }],
291
+ }
292
+ }
293
+
294
+ const handleMdStructure = async (
295
+ args: Record<string, unknown>,
296
+ rootPath: string,
297
+ ): Promise<CallToolResult> => {
298
+ const filePath = args.path as string
299
+ const resolvedPath = path.isAbsolute(filePath)
300
+ ? filePath
301
+ : path.join(rootPath, filePath)
302
+
303
+ // Note: catchAll is intentional - MCP boundary converts errors to JSON format
304
+ const result = await Effect.runPromise(
305
+ parseFile(resolvedPath).pipe(
306
+ Effect.catchAll((e) => Effect.succeed({ error: e.message })),
307
+ ),
308
+ )
309
+
310
+ if ('error' in result) {
311
+ return {
312
+ content: [{ type: 'text', text: `Error: ${result.error}` }],
313
+ isError: true,
314
+ }
315
+ }
316
+
317
+ const formatSection = (section: MdSection, depth: number = 0): string => {
318
+ const indent = ' '.repeat(depth)
319
+ const marker = '#'.repeat(section.level)
320
+ const meta: string[] = []
321
+ if (section.metadata.hasCode) meta.push('code')
322
+ if (section.metadata.hasList) meta.push('list')
323
+ if (section.metadata.hasTable) meta.push('table')
324
+ const metaStr = meta.length > 0 ? ` [${meta.join(', ')}]` : ''
325
+
326
+ let output = `${indent}${marker} ${section.heading}${metaStr} (${section.metadata.tokenCount} tokens)\n`
327
+
328
+ for (const child of section.children) {
329
+ output += formatSection(child, depth + 1)
330
+ }
331
+
332
+ return output
333
+ }
334
+
335
+ const structure = result.sections.map((s) => formatSection(s)).join('')
336
+
337
+ return {
338
+ content: [
339
+ {
340
+ type: 'text',
341
+ text: `# ${result.title}\nPath: ${result.path}\nTotal tokens: ${result.metadata.tokenCount}\n\n${structure}`,
342
+ },
343
+ ],
344
+ }
345
+ }
346
+
347
+ const handleMdKeywordSearch = async (
348
+ args: Record<string, unknown>,
349
+ rootPath: string,
350
+ ): Promise<CallToolResult> => {
351
+ const heading = args.heading as string | undefined
352
+ const pathFilter = args.path_filter as string | undefined
353
+ const hasCode = args.has_code as boolean | undefined
354
+ const hasList = args.has_list as boolean | undefined
355
+ const hasTable = args.has_table as boolean | undefined
356
+ const limit = (args.limit as number) ?? 20
357
+
358
+ // Note: catchAll is intentional - MCP boundary converts errors to JSON format
359
+ const result = await Effect.runPromise(
360
+ search(rootPath, {
361
+ heading,
362
+ pathPattern: pathFilter,
363
+ hasCode,
364
+ hasList,
365
+ hasTable,
366
+ limit,
367
+ }).pipe(
368
+ Effect.catchAll((e) => Effect.succeed([{ error: e.message }] as const)),
369
+ ),
370
+ )
371
+
372
+ if (Array.isArray(result) && result.length > 0 && 'error' in result[0]) {
373
+ return {
374
+ content: [
375
+ {
376
+ type: 'text',
377
+ text: `Error: ${(result[0] as { error: string }).error}`,
378
+ },
379
+ ],
380
+ isError: true,
381
+ }
382
+ }
383
+
384
+ const formattedResults = (
385
+ result as Array<{
386
+ section: {
387
+ heading: string
388
+ level: number
389
+ documentPath: string
390
+ tokenCount: number
391
+ hasCode: boolean
392
+ hasList: boolean
393
+ hasTable: boolean
394
+ }
395
+ }>
396
+ ).map((r, i) => {
397
+ const meta: string[] = []
398
+ if (r.section.hasCode) meta.push('code')
399
+ if (r.section.hasList) meta.push('list')
400
+ if (r.section.hasTable) meta.push('table')
401
+ const metaStr = meta.length > 0 ? ` [${meta.join(', ')}]` : ''
402
+
403
+ return `${i + 1}. **${r.section.heading}**${metaStr}\n ${r.section.documentPath} (${r.section.tokenCount} tokens)`
404
+ })
405
+
406
+ return {
407
+ content: [
408
+ {
409
+ type: 'text',
410
+ text:
411
+ formattedResults.length > 0
412
+ ? `Found ${formattedResults.length} sections:\n\n${formattedResults.join('\n\n')}`
413
+ : 'No sections found matching criteria',
414
+ },
415
+ ],
416
+ }
417
+ }
418
+
419
+ const handleMdIndex = async (
420
+ args: Record<string, unknown>,
421
+ rootPath: string,
422
+ ): Promise<CallToolResult> => {
423
+ const indexPath = (args.path as string) ?? '.'
424
+ const force = (args.force as boolean) ?? false
425
+
426
+ const resolvedPath = path.isAbsolute(indexPath)
427
+ ? indexPath
428
+ : path.join(rootPath, indexPath)
429
+
430
+ // Note: catchAll is intentional - MCP boundary converts errors to JSON format
431
+ const result = await Effect.runPromise(
432
+ buildIndex(resolvedPath, { force }).pipe(
433
+ Effect.catchAll((e) => Effect.succeed({ error: e.message })),
434
+ ),
435
+ )
436
+
437
+ if ('error' in result) {
438
+ return {
439
+ content: [{ type: 'text', text: `Error: ${result.error}` }],
440
+ isError: true,
441
+ }
442
+ }
443
+
444
+ return {
445
+ content: [
446
+ {
447
+ type: 'text',
448
+ text: `Indexed ${result.documentsIndexed} documents, ${result.sectionsIndexed} sections, ${result.linksIndexed} links in ${result.duration}ms`,
449
+ },
450
+ ],
451
+ }
452
+ }
453
+
454
+ const handleMdLinks = async (
455
+ args: Record<string, unknown>,
456
+ rootPath: string,
457
+ ): Promise<CallToolResult> => {
458
+ const filePath = args.path as string
459
+ const resolvedPath = path.isAbsolute(filePath)
460
+ ? filePath
461
+ : path.join(rootPath, filePath)
462
+
463
+ // Note: catchAll is intentional - MCP boundary converts errors to JSON format
464
+ const result = await Effect.runPromise(
465
+ getOutgoingLinks(rootPath, resolvedPath).pipe(
466
+ Effect.catchAll((e) => Effect.succeed({ error: e.message })),
467
+ ),
468
+ )
469
+
470
+ if ('error' in result) {
471
+ return {
472
+ content: [{ type: 'text', text: `Error: ${result.error}` }],
473
+ isError: true,
474
+ }
475
+ }
476
+
477
+ const links = result as readonly string[]
478
+ const relativePath = path.relative(rootPath, resolvedPath)
479
+
480
+ return {
481
+ content: [
482
+ {
483
+ type: 'text',
484
+ text:
485
+ links.length > 0
486
+ ? `Outgoing links from ${relativePath}:\n\n${links.map((l) => ` -> ${l}`).join('\n')}\n\nTotal: ${links.length} links`
487
+ : `No outgoing links from ${relativePath}`,
488
+ },
489
+ ],
490
+ }
491
+ }
492
+
493
+ const handleMdBacklinks = async (
494
+ args: Record<string, unknown>,
495
+ rootPath: string,
496
+ ): Promise<CallToolResult> => {
497
+ const filePath = args.path as string
498
+ const resolvedPath = path.isAbsolute(filePath)
499
+ ? filePath
500
+ : path.join(rootPath, filePath)
501
+
502
+ // Note: catchAll is intentional - MCP boundary converts errors to JSON format
503
+ const result = await Effect.runPromise(
504
+ getIncomingLinks(rootPath, resolvedPath).pipe(
505
+ Effect.catchAll((e) => Effect.succeed({ error: e.message })),
506
+ ),
507
+ )
508
+
509
+ if ('error' in result) {
510
+ return {
511
+ content: [{ type: 'text', text: `Error: ${result.error}` }],
512
+ isError: true,
513
+ }
514
+ }
515
+
516
+ const links = result as readonly string[]
517
+ const relativePath = path.relative(rootPath, resolvedPath)
518
+
519
+ return {
520
+ content: [
521
+ {
522
+ type: 'text',
523
+ text:
524
+ links.length > 0
525
+ ? `Incoming links to ${relativePath}:\n\n${links.map((l) => ` <- ${l}`).join('\n')}\n\nTotal: ${links.length} backlinks`
526
+ : `No incoming links to ${relativePath}`,
527
+ },
528
+ ],
529
+ }
530
+ }
531
+
532
+ // ============================================================================
533
+ // MCP Server Setup
534
+ // ============================================================================
535
+
536
+ const createServer = (rootPath: string) => {
537
+ const server = new Server(
538
+ {
539
+ name: 'mdcontext-mcp',
540
+ version: MCP_VERSION,
541
+ },
542
+ {
543
+ capabilities: {
544
+ tools: {},
545
+ },
546
+ },
547
+ )
548
+
549
+ // List available tools
550
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
551
+ tools,
552
+ }))
553
+
554
+ // Handle tool calls
555
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
556
+ const { name, arguments: args } = request.params
557
+
558
+ switch (name) {
559
+ case 'md_search':
560
+ return handleMdSearch(args ?? {}, rootPath)
561
+ case 'md_context':
562
+ return handleMdContext(args ?? {}, rootPath)
563
+ case 'md_structure':
564
+ return handleMdStructure(args ?? {}, rootPath)
565
+ case 'md_keyword_search':
566
+ return handleMdKeywordSearch(args ?? {}, rootPath)
567
+ case 'md_index':
568
+ return handleMdIndex(args ?? {}, rootPath)
569
+ case 'md_links':
570
+ return handleMdLinks(args ?? {}, rootPath)
571
+ case 'md_backlinks':
572
+ return handleMdBacklinks(args ?? {}, rootPath)
573
+ default:
574
+ return {
575
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
576
+ isError: true,
577
+ }
578
+ }
579
+ })
580
+
581
+ return server
582
+ }
583
+
584
+ // ============================================================================
585
+ // Main Entry
586
+ // ============================================================================
587
+
588
+ const main = async () => {
589
+ // Use current working directory as root
590
+ const rootPath = process.cwd()
591
+
592
+ const server = createServer(rootPath)
593
+ const transport = new StdioServerTransport()
594
+
595
+ await server.connect(transport)
596
+
597
+ // Handle graceful shutdown
598
+ process.on('SIGINT', async () => {
599
+ await server.close()
600
+ process.exit(0)
601
+ })
602
+
603
+ process.on('SIGTERM', async () => {
604
+ await server.close()
605
+ process.exit(0)
606
+ })
607
+ }
608
+
609
+ main().catch((error) => {
610
+ console.error('Fatal error:', error)
611
+ process.exit(1)
612
+ })
@@ -0,0 +1 @@
1
+ export * from './parser.js'