@mortice/mapwright 0.8.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 (166) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/LICENSE +21 -0
  3. package/README.md +262 -0
  4. package/dist/catalogue/canonicalisation.d.ts +36 -0
  5. package/dist/catalogue/canonicalisation.js +262 -0
  6. package/dist/catalogue/config.d.ts +13 -0
  7. package/dist/catalogue/config.js +77 -0
  8. package/dist/catalogue/deterministic.d.ts +3 -0
  9. package/dist/catalogue/deterministic.js +25 -0
  10. package/dist/catalogue/diagnostics.d.ts +2 -0
  11. package/dist/catalogue/diagnostics.js +34 -0
  12. package/dist/catalogue/evidence.d.ts +1 -0
  13. package/dist/catalogue/evidence.js +10 -0
  14. package/dist/catalogue/examples.d.ts +3 -0
  15. package/dist/catalogue/examples.js +20 -0
  16. package/dist/catalogue/identity.d.ts +61 -0
  17. package/dist/catalogue/identity.js +114 -0
  18. package/dist/catalogue/ignore.d.ts +4 -0
  19. package/dist/catalogue/ignore.js +29 -0
  20. package/dist/catalogue/indexer.d.ts +20 -0
  21. package/dist/catalogue/indexer.js +1393 -0
  22. package/dist/catalogue/linkage.d.ts +16 -0
  23. package/dist/catalogue/linkage.js +419 -0
  24. package/dist/catalogue/moduleResolution.d.ts +4 -0
  25. package/dist/catalogue/moduleResolution.js +67 -0
  26. package/dist/catalogue/ordering.d.ts +13 -0
  27. package/dist/catalogue/ordering.js +86 -0
  28. package/dist/catalogue/packages.d.ts +12 -0
  29. package/dist/catalogue/packages.js +475 -0
  30. package/dist/catalogue/pythonReExports.d.ts +7 -0
  31. package/dist/catalogue/pythonReExports.js +113 -0
  32. package/dist/catalogue/resolveRefs.d.ts +40 -0
  33. package/dist/catalogue/resolveRefs.js +284 -0
  34. package/dist/catalogue/scanner.d.ts +8 -0
  35. package/dist/catalogue/scanner.js +99 -0
  36. package/dist/catalogue/schema.d.ts +289 -0
  37. package/dist/catalogue/schema.js +1 -0
  38. package/dist/catalogue/store.d.ts +41 -0
  39. package/dist/catalogue/store.js +209 -0
  40. package/dist/catalogue/templates.d.ts +18 -0
  41. package/dist/catalogue/templates.js +158 -0
  42. package/dist/catalogue/version.d.ts +12 -0
  43. package/dist/catalogue/version.js +30 -0
  44. package/dist/cli/commands/adoptionPlan.d.ts +8 -0
  45. package/dist/cli/commands/adoptionPlan.js +24 -0
  46. package/dist/cli/commands/auditDocs.d.ts +8 -0
  47. package/dist/cli/commands/auditDocs.js +24 -0
  48. package/dist/cli/commands/changeImpact.d.ts +10 -0
  49. package/dist/cli/commands/changeImpact.js +36 -0
  50. package/dist/cli/commands/changes.d.ts +12 -0
  51. package/dist/cli/commands/changes.js +31 -0
  52. package/dist/cli/commands/check.d.ts +9 -0
  53. package/dist/cli/commands/check.js +4 -0
  54. package/dist/cli/commands/compatibility.d.ts +10 -0
  55. package/dist/cli/commands/compatibility.js +43 -0
  56. package/dist/cli/commands/diff.d.ts +31 -0
  57. package/dist/cli/commands/diff.js +118 -0
  58. package/dist/cli/commands/dogfood.d.ts +8 -0
  59. package/dist/cli/commands/dogfood.js +24 -0
  60. package/dist/cli/commands/drift.d.ts +8 -0
  61. package/dist/cli/commands/drift.js +13 -0
  62. package/dist/cli/commands/index.d.ts +9 -0
  63. package/dist/cli/commands/index.js +9 -0
  64. package/dist/cli/commands/indexWorkspace.d.ts +8 -0
  65. package/dist/cli/commands/indexWorkspace.js +23 -0
  66. package/dist/cli/commands/recommendConfig.d.ts +7 -0
  67. package/dist/cli/commands/recommendConfig.js +20 -0
  68. package/dist/cli/commands/schema.d.ts +1 -0
  69. package/dist/cli/commands/schema.js +37 -0
  70. package/dist/cli/commands/template.d.ts +5 -0
  71. package/dist/cli/commands/template.js +44 -0
  72. package/dist/cli/commands/validate.d.ts +9 -0
  73. package/dist/cli/commands/validate.js +85 -0
  74. package/dist/cli/index.d.ts +2 -0
  75. package/dist/cli/index.js +211 -0
  76. package/dist/comparison/artefactValidate.d.ts +19 -0
  77. package/dist/comparison/artefactValidate.js +334 -0
  78. package/dist/comparison/candidates.d.ts +12 -0
  79. package/dist/comparison/candidates.js +391 -0
  80. package/dist/comparison/compare.d.ts +15 -0
  81. package/dist/comparison/compare.js +240 -0
  82. package/dist/comparison/compatibility.d.ts +26 -0
  83. package/dist/comparison/compatibility.js +316 -0
  84. package/dist/comparison/drift.d.ts +34 -0
  85. package/dist/comparison/drift.js +71 -0
  86. package/dist/comparison/identity.d.ts +30 -0
  87. package/dist/comparison/identity.js +73 -0
  88. package/dist/comparison/impact.d.ts +14 -0
  89. package/dist/comparison/impact.js +291 -0
  90. package/dist/comparison/index.d.ts +6 -0
  91. package/dist/comparison/index.js +6 -0
  92. package/dist/comparison/integrity.d.ts +12 -0
  93. package/dist/comparison/integrity.js +199 -0
  94. package/dist/comparison/ordering.d.ts +4 -0
  95. package/dist/comparison/ordering.js +69 -0
  96. package/dist/comparison/projections.d.ts +19 -0
  97. package/dist/comparison/projections.js +185 -0
  98. package/dist/comparison/relationshipPolicy.d.ts +19 -0
  99. package/dist/comparison/relationshipPolicy.js +98 -0
  100. package/dist/comparison/schema.d.ts +178 -0
  101. package/dist/comparison/schema.js +16 -0
  102. package/dist/comparison/store.d.ts +58 -0
  103. package/dist/comparison/store.js +462 -0
  104. package/dist/comparison/validate.d.ts +23 -0
  105. package/dist/comparison/validate.js +137 -0
  106. package/dist/comparison/version.d.ts +9 -0
  107. package/dist/comparison/version.js +11 -0
  108. package/dist/extractors/docTags.d.ts +9 -0
  109. package/dist/extractors/docTags.js +106 -0
  110. package/dist/extractors/generic.d.ts +5 -0
  111. package/dist/extractors/generic.js +3 -0
  112. package/dist/extractors/javascript.d.ts +3 -0
  113. package/dist/extractors/javascript.js +4 -0
  114. package/dist/extractors/markdown.d.ts +7 -0
  115. package/dist/extractors/markdown.js +269 -0
  116. package/dist/extractors/python.d.ts +7 -0
  117. package/dist/extractors/python.js +314 -0
  118. package/dist/extractors/typescript.d.ts +7 -0
  119. package/dist/extractors/typescript.js +387 -0
  120. package/dist/index.d.ts +2 -0
  121. package/dist/index.js +2 -0
  122. package/dist/mcp/resources.d.ts +18 -0
  123. package/dist/mcp/resources.js +57 -0
  124. package/dist/mcp/server.d.ts +2 -0
  125. package/dist/mcp/server.js +95 -0
  126. package/dist/mcp/tools.d.ts +29 -0
  127. package/dist/mcp/tools.js +902 -0
  128. package/dist/queries/adoption.d.ts +62 -0
  129. package/dist/queries/adoption.js +366 -0
  130. package/dist/queries/capabilities.d.ts +45 -0
  131. package/dist/queries/capabilities.js +124 -0
  132. package/dist/queries/changes.d.ts +33 -0
  133. package/dist/queries/changes.js +53 -0
  134. package/dist/queries/dependencies.d.ts +17 -0
  135. package/dist/queries/dependencies.js +94 -0
  136. package/dist/queries/dogfood.d.ts +50 -0
  137. package/dist/queries/dogfood.js +116 -0
  138. package/dist/queries/editingContext.d.ts +84 -0
  139. package/dist/queries/editingContext.js +240 -0
  140. package/dist/queries/examples.d.ts +8 -0
  141. package/dist/queries/examples.js +16 -0
  142. package/dist/queries/files.d.ts +8 -0
  143. package/dist/queries/files.js +14 -0
  144. package/dist/queries/lists.d.ts +94 -0
  145. package/dist/queries/lists.js +184 -0
  146. package/dist/queries/overview.d.ts +2 -0
  147. package/dist/queries/overview.js +8 -0
  148. package/dist/queries/publicApis.d.ts +60 -0
  149. package/dist/queries/publicApis.js +120 -0
  150. package/dist/queries/ranking.d.ts +67 -0
  151. package/dist/queries/ranking.js +387 -0
  152. package/dist/queries/recommendConfig.d.ts +47 -0
  153. package/dist/queries/recommendConfig.js +123 -0
  154. package/dist/queries/relationships.d.ts +5 -0
  155. package/dist/queries/relationships.js +15 -0
  156. package/dist/queries/reuseCandidates.d.ts +34 -0
  157. package/dist/queries/reuseCandidates.js +181 -0
  158. package/dist/queries/symbols.d.ts +40 -0
  159. package/dist/queries/symbols.js +41 -0
  160. package/dist/security/pathPolicy.d.ts +12 -0
  161. package/dist/security/pathPolicy.js +46 -0
  162. package/dist/security/secretPatterns.d.ts +1 -0
  163. package/dist/security/secretPatterns.js +4 -0
  164. package/dist/security/sourceAccess.d.ts +12 -0
  165. package/dist/security/sourceAccess.js +35 -0
  166. package/package.json +60 -0
@@ -0,0 +1,902 @@
1
+ import { z } from "zod";
2
+ import path from "node:path";
3
+ import { indexWorkspace, shouldFailFromDiagnostics } from "../catalogue/indexer.js";
4
+ import { loadConfig } from "../catalogue/config.js";
5
+ import { loadCommittedCatalogue } from "../catalogue/store.js";
6
+ import { searchSymbols, getSymbolContext } from "../queries/symbols.js";
7
+ import { getCapabilityContext } from "../queries/capabilities.js";
8
+ import { getPublicApiContext, searchPublicApis } from "../queries/publicApis.js";
9
+ import { getWorkspaceOverview } from "../queries/overview.js";
10
+ import { getBoundedSourcePreview } from "../security/sourceAccess.js";
11
+ import { getDependencyNeighborhood } from "../queries/dependencies.js";
12
+ import { findReuseCandidates } from "../queries/reuseCandidates.js";
13
+ import { listCapabilitiesPage, listDiagnosticsPage, listExamplesPage, listFilesPage, listPackagesPage, listPublicApisPage, listRelationshipsPage, listSymbolsPage } from "../queries/lists.js";
14
+ import { getAdoptionAudit, getAdoptionPlan } from "../queries/adoption.js";
15
+ import { recommendConfig } from "../queries/recommendConfig.js";
16
+ import { getDocumentationTemplate, listDocumentationTemplateIds, listDocumentationTemplates } from "../catalogue/templates.js";
17
+ import { getDogfoodReport } from "../queries/dogfood.js";
18
+ import { loadCataloguePair } from "../comparison/validate.js";
19
+ import { compareCatalogues } from "../comparison/compare.js";
20
+ import { loadComparisonArtefact, writeComparisonArtefact, assertComparisonPathContained } from "../comparison/store.js";
21
+ import { getChangeImpact } from "../comparison/impact.js";
22
+ import { DEFAULT_COMPARISON_OPTIONS } from "../comparison/schema.js";
23
+ import { getChange, getChangeSummary, getCompatibilityReport, listChanges } from "../queries/changes.js";
24
+ import { checkCatalogueDrift } from "../comparison/drift.js";
25
+ export const CATALOGUE_NOT_LOADED_CODE = "catalogue_not_loaded";
26
+ export const CATALOGUE_NOT_LOADED_MESSAGE = "Run load_committed_catalogue or index_workspace first.";
27
+ export const COMPARISON_NOT_LOADED_CODE = "comparison_not_loaded";
28
+ export const COMPARISON_NOT_LOADED_MESSAGE = "Run compare_catalogues or load_comparison first.";
29
+ export function success(data, warnings = []) {
30
+ return { ok: true, data, warnings, provenance: { source: "mapwright-mcp" } };
31
+ }
32
+ export function failure(code, message) {
33
+ return { ok: false, error: { code, message }, warnings: [], provenance: { source: "mapwright-mcp" } };
34
+ }
35
+ export function catalogueNotLoadedFailure() {
36
+ return failure(CATALOGUE_NOT_LOADED_CODE, CATALOGUE_NOT_LOADED_MESSAGE);
37
+ }
38
+ export function comparisonNotLoadedFailure() {
39
+ return failure(COMPARISON_NOT_LOADED_CODE, COMPARISON_NOT_LOADED_MESSAGE);
40
+ }
41
+ export function catalogueNotLoadedContent() {
42
+ return { content: [{ type: "text", text: JSON.stringify(catalogueNotLoadedFailure()) }] };
43
+ }
44
+ export function registerTools(server, options = {}) {
45
+ let latest = options.initialSnapshot;
46
+ let latestWorkspace = options.initialWorkspace ?? process.cwd();
47
+ let latestLimits = { maxFileSizeBytes: 512000, maxSourcePreviewLines: 120 };
48
+ let activeComparison;
49
+ const setActiveComparison = (next) => {
50
+ activeComparison = next;
51
+ options.onComparisonChange?.(next);
52
+ };
53
+ server.registerTool("load_committed_catalogue", {
54
+ description: "Load a committed .mapwright catalogue snapshot without indexing source.",
55
+ inputSchema: {
56
+ workspaceRoot: z.string(),
57
+ catalogueDir: z.string().default(".mapwright")
58
+ }
59
+ }, async (args) => {
60
+ const workspaceRoot = args.workspaceRoot;
61
+ const { config } = loadConfig({ workspaceRoot });
62
+ const catalogueDir = args.catalogueDir ?? config.catalogueDir;
63
+ const loaded = loadCommittedCatalogue(workspaceRoot, catalogueDir);
64
+ if (!loaded.ok) {
65
+ return {
66
+ content: [
67
+ {
68
+ type: "text",
69
+ text: JSON.stringify(failure(loaded.error.code, loaded.error.message))
70
+ }
71
+ ]
72
+ };
73
+ }
74
+ latestWorkspace = workspaceRoot;
75
+ latestLimits = {
76
+ maxFileSizeBytes: config.limits.maxFileSizeBytes,
77
+ maxSourcePreviewLines: config.limits.maxSourcePreviewLines
78
+ };
79
+ latest = loaded.snapshot;
80
+ options.onSnapshotChange?.(loaded.snapshot, workspaceRoot);
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text: JSON.stringify(success({
86
+ catalogueId: loaded.snapshot.catalogueId,
87
+ stats: loaded.snapshot.stats,
88
+ source: "committed-catalogue",
89
+ schemaVersion: loaded.snapshot.schemaVersion,
90
+ identityScheme: loaded.snapshot.identityScheme,
91
+ generatorVersion: loaded.snapshot.generator.version
92
+ }, loaded.warnings))
93
+ }
94
+ ]
95
+ };
96
+ });
97
+ server.registerTool("index_workspace", {
98
+ description: "Index workspace and write catalogue output.",
99
+ inputSchema: {
100
+ workspaceRoot: z.string(),
101
+ mode: z.enum(["committed", "local", "memory"]).default("committed"),
102
+ writeCache: z.boolean().default(true)
103
+ }
104
+ }, async (args) => {
105
+ const workspaceRoot = args.workspaceRoot;
106
+ latestWorkspace = workspaceRoot;
107
+ const { config } = loadConfig({ workspaceRoot });
108
+ latestLimits = {
109
+ maxFileSizeBytes: config.limits.maxFileSizeBytes,
110
+ maxSourcePreviewLines: config.limits.maxSourcePreviewLines
111
+ };
112
+ const { snapshot } = indexWorkspace({
113
+ workspaceRoot,
114
+ mode: args.mode,
115
+ write: args.writeCache
116
+ });
117
+ latest = snapshot;
118
+ options.onSnapshotChange?.(snapshot, workspaceRoot);
119
+ return { content: [{ type: "text", text: JSON.stringify(success({ catalogueId: snapshot.catalogueId, stats: snapshot.stats })) }] };
120
+ });
121
+ server.registerTool("get_workspace_overview", { description: "Get workspace overview.", inputSchema: {} }, async () => {
122
+ if (!latest)
123
+ return catalogueNotLoadedContent();
124
+ return { content: [{ type: "text", text: JSON.stringify(success(getWorkspaceOverview(latest))) }] };
125
+ });
126
+ server.registerTool("list_files", {
127
+ description: "List indexed files (bounded, deterministically ordered).",
128
+ inputSchema: {
129
+ language: z.string().optional(),
130
+ kind: z.enum(["source", "test", "documentation", "config", "generated", "unknown"]).optional(),
131
+ limit: z.number().int().positive().max(1000).default(100),
132
+ offset: z.number().int().min(0).default(0)
133
+ }
134
+ }, async (args) => {
135
+ if (!latest)
136
+ return catalogueNotLoadedContent();
137
+ return {
138
+ content: [
139
+ {
140
+ type: "text",
141
+ text: JSON.stringify(success(listFilesPage(latest, {
142
+ language: args.language,
143
+ kind: args.kind,
144
+ limit: args.limit,
145
+ offset: args.offset
146
+ })))
147
+ }
148
+ ]
149
+ };
150
+ });
151
+ server.registerTool("list_symbols", {
152
+ description: "List indexed symbols (bounded, deterministically ordered).",
153
+ inputSchema: {
154
+ language: z.string().optional(),
155
+ kind: z
156
+ .enum(["function", "class", "method", "interface", "type", "enum", "constant", "variable", "module", "namespace", "component", "route", "test", "unknown"])
157
+ .optional(),
158
+ packageId: z.string().optional(),
159
+ limit: z.number().int().positive().max(1000).default(100),
160
+ offset: z.number().int().min(0).default(0)
161
+ }
162
+ }, async (args) => {
163
+ if (!latest)
164
+ return catalogueNotLoadedContent();
165
+ return {
166
+ content: [
167
+ {
168
+ type: "text",
169
+ text: JSON.stringify(success(listSymbolsPage(latest, {
170
+ language: args.language,
171
+ kind: args.kind,
172
+ packageId: args.packageId,
173
+ limit: args.limit,
174
+ offset: args.offset
175
+ })))
176
+ }
177
+ ]
178
+ };
179
+ });
180
+ server.registerTool("list_packages", {
181
+ description: "List indexed packages (bounded, deterministically ordered).",
182
+ inputSchema: {
183
+ ecosystem: z.enum(["npm", "python", "unknown"]).optional(),
184
+ limit: z.number().int().positive().max(500).default(50),
185
+ offset: z.number().int().min(0).default(0)
186
+ }
187
+ }, async (args) => {
188
+ if (!latest)
189
+ return catalogueNotLoadedContent();
190
+ return {
191
+ content: [
192
+ {
193
+ type: "text",
194
+ text: JSON.stringify(success(listPackagesPage(latest, {
195
+ ecosystem: args.ecosystem,
196
+ limit: args.limit,
197
+ offset: args.offset
198
+ })))
199
+ }
200
+ ]
201
+ };
202
+ });
203
+ server.registerTool("list_public_apis", {
204
+ description: "List public APIs (bounded, deterministically ordered).",
205
+ inputSchema: {
206
+ stability: z.enum(["stable", "experimental", "deprecated", "internal", "test-only", "unknown"]).optional(),
207
+ packageId: z.string().optional(),
208
+ limit: z.number().int().positive().max(1000).default(100),
209
+ offset: z.number().int().min(0).default(0)
210
+ }
211
+ }, async (args) => {
212
+ if (!latest)
213
+ return catalogueNotLoadedContent();
214
+ return {
215
+ content: [
216
+ {
217
+ type: "text",
218
+ text: JSON.stringify(success(listPublicApisPage(latest, {
219
+ stability: args.stability,
220
+ packageId: args.packageId,
221
+ limit: args.limit,
222
+ offset: args.offset
223
+ })))
224
+ }
225
+ ]
226
+ };
227
+ });
228
+ server.registerTool("list_relationships", {
229
+ description: "List relationships (bounded, deterministically ordered).",
230
+ inputSchema: {
231
+ type: z
232
+ .enum([
233
+ "defines",
234
+ "contains",
235
+ "documents",
236
+ "tests",
237
+ "covers",
238
+ "imports",
239
+ "exports",
240
+ "explicit_coverage",
241
+ "explicit_example",
242
+ "resolved_direct_import",
243
+ "resolved_direct_usage"
244
+ ])
245
+ .optional(),
246
+ limit: z.number().int().positive().max(1000).default(100),
247
+ offset: z.number().int().min(0).default(0)
248
+ }
249
+ }, async (args) => {
250
+ if (!latest)
251
+ return catalogueNotLoadedContent();
252
+ return {
253
+ content: [
254
+ {
255
+ type: "text",
256
+ text: JSON.stringify(success(listRelationshipsPage(latest, {
257
+ type: args.type,
258
+ limit: args.limit,
259
+ offset: args.offset
260
+ })))
261
+ }
262
+ ]
263
+ };
264
+ });
265
+ server.registerTool("list_diagnostics", {
266
+ description: "List diagnostics (bounded, deterministically ordered).",
267
+ inputSchema: {
268
+ severity: z.enum(["info", "warning", "error"]).optional(),
269
+ category: z.enum(["documentation", "indexing", "resolution", "style", "security", "determinism"]).optional(),
270
+ limit: z.number().int().positive().max(500).default(50),
271
+ offset: z.number().int().min(0).default(0)
272
+ }
273
+ }, async (args) => {
274
+ if (!latest)
275
+ return catalogueNotLoadedContent();
276
+ return {
277
+ content: [
278
+ {
279
+ type: "text",
280
+ text: JSON.stringify(success(listDiagnosticsPage(latest, {
281
+ severity: args.severity,
282
+ category: args.category,
283
+ limit: args.limit,
284
+ offset: args.offset
285
+ })))
286
+ }
287
+ ]
288
+ };
289
+ });
290
+ server.registerTool("search_symbols", {
291
+ description: "Search symbols with deterministic ranking and per-result match reasons.",
292
+ inputSchema: {
293
+ query: z.string(),
294
+ kind: z
295
+ .enum(["function", "class", "method", "interface", "type", "enum", "constant", "variable", "module", "namespace", "component", "route", "test", "unknown"])
296
+ .optional(),
297
+ language: z.string().optional(),
298
+ packageId: z.string().optional(),
299
+ publicOnly: z.boolean().optional(),
300
+ capabilityId: z.string().optional(),
301
+ hasExamples: z.boolean().optional(),
302
+ hasTests: z.boolean().optional(),
303
+ limit: z.number().int().positive().max(200).default(20),
304
+ offset: z.number().int().min(0).default(0)
305
+ }
306
+ }, async (args) => {
307
+ if (!latest)
308
+ return catalogueNotLoadedContent();
309
+ return {
310
+ content: [
311
+ {
312
+ type: "text",
313
+ text: JSON.stringify(success(searchSymbols(latest, {
314
+ query: args.query,
315
+ kind: args.kind,
316
+ language: args.language,
317
+ packageId: args.packageId,
318
+ publicOnly: args.publicOnly,
319
+ capabilityId: args.capabilityId,
320
+ hasExamples: args.hasExamples,
321
+ hasTests: args.hasTests,
322
+ limit: args.limit,
323
+ offset: args.offset
324
+ })))
325
+ }
326
+ ]
327
+ };
328
+ });
329
+ server.registerTool("get_symbol_context", {
330
+ description: "Get symbol details and optional source preview.",
331
+ inputSchema: {
332
+ symbolId: z.string(),
333
+ includeSource: z.boolean().default(false),
334
+ includeEditingContext: z.boolean().default(false),
335
+ editingContextLimit: z.number().int().positive().max(25).optional(),
336
+ maxSourceLines: z.number().int().positive().max(200).default(80)
337
+ }
338
+ }, async (args) => {
339
+ if (!latest)
340
+ return catalogueNotLoadedContent();
341
+ const context = getSymbolContext(latest, args.symbolId, {
342
+ includeEditingContext: args.includeEditingContext,
343
+ editingContextLimit: args.editingContextLimit
344
+ });
345
+ if (!context)
346
+ return { content: [{ type: "text", text: JSON.stringify(failure("symbol_not_found", "Symbol not found.")) }] };
347
+ const { symbol, editingContext } = context;
348
+ let sourcePreview;
349
+ const bounded = getBoundedSourcePreview({
350
+ workspaceRoot: latestWorkspace,
351
+ relativePath: symbol.filePath,
352
+ includeSource: args.includeSource,
353
+ maxSourceLines: Math.min(args.maxSourceLines, latestLimits.maxSourcePreviewLines),
354
+ maxFileSizeBytes: latestLimits.maxFileSizeBytes
355
+ });
356
+ if (bounded.allowed) {
357
+ sourcePreview = bounded.source;
358
+ }
359
+ return {
360
+ content: [
361
+ {
362
+ type: "text",
363
+ text: JSON.stringify(success({
364
+ symbol,
365
+ editingContext,
366
+ sourcePreview,
367
+ sourceAccess: bounded.allowed ? "granted" : bounded.reason
368
+ }))
369
+ }
370
+ ]
371
+ };
372
+ });
373
+ server.registerTool("list_capabilities", {
374
+ description: "List capabilities (bounded, deterministically ordered).",
375
+ inputSchema: {
376
+ stability: z.enum(["stable", "experimental", "deprecated", "internal", "test-only", "unknown"]).optional(),
377
+ limit: z.number().int().positive().max(500).default(50),
378
+ offset: z.number().int().min(0).default(0)
379
+ }
380
+ }, async (args) => {
381
+ if (!latest)
382
+ return catalogueNotLoadedContent();
383
+ return {
384
+ content: [
385
+ {
386
+ type: "text",
387
+ text: JSON.stringify(success(listCapabilitiesPage(latest, { stability: args.stability, limit: args.limit, offset: args.offset })))
388
+ }
389
+ ]
390
+ };
391
+ });
392
+ server.registerTool("get_capability_context", {
393
+ description: "Get capability context.",
394
+ inputSchema: {
395
+ capabilityId: z.string(),
396
+ maxPublicApis: z.number().int().positive().max(1000).optional(),
397
+ maxSymbols: z.number().int().positive().max(2000).optional(),
398
+ maxExamples: z.number().int().positive().max(1000).optional(),
399
+ maxTests: z.number().int().positive().max(1000).optional(),
400
+ maxDiagnostics: z.number().int().positive().max(5000).optional()
401
+ }
402
+ }, async (args) => {
403
+ if (!latest)
404
+ return catalogueNotLoadedContent();
405
+ const capability = getCapabilityContext(latest, args.capabilityId, {
406
+ maxPublicApis: args.maxPublicApis,
407
+ maxSymbols: args.maxSymbols,
408
+ maxExamples: args.maxExamples,
409
+ maxTests: args.maxTests,
410
+ maxDiagnostics: args.maxDiagnostics
411
+ });
412
+ if (!capability)
413
+ return { content: [{ type: "text", text: JSON.stringify(failure("capability_not_found", "Capability not found.")) }] };
414
+ return { content: [{ type: "text", text: JSON.stringify(success(capability)) }] };
415
+ });
416
+ server.registerTool("search_public_apis", {
417
+ description: "Search public APIs with deterministic ranking and per-result match reasons.",
418
+ inputSchema: {
419
+ query: z.string(),
420
+ stability: z.enum(["stable", "experimental", "deprecated", "internal", "test-only", "unknown"]).optional(),
421
+ packageId: z.string().optional(),
422
+ language: z.string().optional(),
423
+ capabilityId: z.string().optional(),
424
+ hasExamples: z.boolean().optional(),
425
+ hasTests: z.boolean().optional(),
426
+ limit: z.number().int().positive().max(200).default(20),
427
+ offset: z.number().int().min(0).default(0)
428
+ }
429
+ }, async (args) => {
430
+ if (!latest)
431
+ return catalogueNotLoadedContent();
432
+ return {
433
+ content: [
434
+ {
435
+ type: "text",
436
+ text: JSON.stringify(success(searchPublicApis(latest, {
437
+ query: args.query,
438
+ stability: args.stability,
439
+ packageId: args.packageId,
440
+ language: args.language,
441
+ capabilityId: args.capabilityId,
442
+ hasExamples: args.hasExamples,
443
+ hasTests: args.hasTests,
444
+ limit: args.limit,
445
+ offset: args.offset
446
+ })))
447
+ }
448
+ ]
449
+ };
450
+ });
451
+ server.registerTool("get_public_api_context", {
452
+ description: "Get public API context bundle.",
453
+ inputSchema: {
454
+ publicApiId: z.string(),
455
+ maxCapabilities: z.number().int().positive().max(1000).optional(),
456
+ maxExamples: z.number().int().positive().max(1000).optional(),
457
+ maxTests: z.number().int().positive().max(1000).optional(),
458
+ maxDiagnostics: z.number().int().positive().max(5000).optional(),
459
+ includeEditingContext: z.boolean().default(false),
460
+ editingContextLimit: z.number().int().positive().max(25).optional()
461
+ }
462
+ }, async (args) => {
463
+ if (!latest)
464
+ return catalogueNotLoadedContent();
465
+ const context = getPublicApiContext(latest, args.publicApiId, {
466
+ maxCapabilities: args.maxCapabilities,
467
+ maxExamples: args.maxExamples,
468
+ maxTests: args.maxTests,
469
+ maxDiagnostics: args.maxDiagnostics,
470
+ includeEditingContext: args.includeEditingContext,
471
+ editingContextLimit: args.editingContextLimit
472
+ });
473
+ if (!context)
474
+ return { content: [{ type: "text", text: JSON.stringify(failure("public_api_not_found", "Public API not found.")) }] };
475
+ return { content: [{ type: "text", text: JSON.stringify(success(context)) }] };
476
+ });
477
+ server.registerTool("list_examples", {
478
+ description: "List example records (bounded, deterministically ordered) with optional capability/public API filtering.",
479
+ inputSchema: {
480
+ capabilityId: z.string().optional(),
481
+ publicApiId: z.string().optional(),
482
+ limit: z.number().int().positive().max(500).default(50),
483
+ offset: z.number().int().min(0).default(0)
484
+ }
485
+ }, async (args) => {
486
+ if (!latest)
487
+ return catalogueNotLoadedContent();
488
+ return {
489
+ content: [
490
+ {
491
+ type: "text",
492
+ text: JSON.stringify(success(listExamplesPage(latest, {
493
+ capabilityId: args.capabilityId,
494
+ publicApiId: args.publicApiId,
495
+ limit: args.limit,
496
+ offset: args.offset
497
+ })))
498
+ }
499
+ ]
500
+ };
501
+ });
502
+ server.registerTool("find_reuse_candidates", {
503
+ description: "Find deterministic metadata-only reuse candidates.",
504
+ inputSchema: {
505
+ query: z.string(),
506
+ capabilityId: z.string().optional(),
507
+ packageId: z.string().optional(),
508
+ limit: z.number().int().positive().max(100).default(10)
509
+ }
510
+ }, async (args) => {
511
+ if (!latest)
512
+ return catalogueNotLoadedContent();
513
+ return {
514
+ content: [
515
+ {
516
+ type: "text",
517
+ text: JSON.stringify(success(findReuseCandidates(latest, {
518
+ query: args.query,
519
+ capabilityId: args.capabilityId,
520
+ packageId: args.packageId,
521
+ limit: args.limit
522
+ })))
523
+ }
524
+ ]
525
+ };
526
+ });
527
+ server.registerTool("validate_catalogue", {
528
+ description: "Validate indexed catalogue diagnostics.",
529
+ inputSchema: {
530
+ failOn: z.enum(["info", "warning", "error", "never"]).default("warning")
531
+ }
532
+ }, async (args) => {
533
+ if (!latest)
534
+ return catalogueNotLoadedContent();
535
+ const failed = shouldFailFromDiagnostics(args.failOn, latest.diagnostics);
536
+ return {
537
+ content: [
538
+ {
539
+ type: "text",
540
+ text: JSON.stringify(success({
541
+ valid: !failed,
542
+ diagnostics: latest.diagnostics
543
+ }))
544
+ }
545
+ ]
546
+ };
547
+ });
548
+ server.registerTool("get_dependency_neighborhood", {
549
+ description: "Get dependency neighborhood for a file path.",
550
+ inputSchema: {
551
+ path: z.string(),
552
+ depth: z.number().int().min(0).max(10).default(1),
553
+ direction: z.enum(["imports", "imported_by", "both"]).default("both")
554
+ }
555
+ }, async (args) => {
556
+ if (!latest)
557
+ return catalogueNotLoadedContent();
558
+ return {
559
+ content: [
560
+ {
561
+ type: "text",
562
+ text: JSON.stringify(success(getDependencyNeighborhood(latest, { path: args.path, depth: args.depth, direction: args.direction })))
563
+ }
564
+ ]
565
+ };
566
+ });
567
+ server.registerTool("get_adoption_audit", {
568
+ description: "Get deterministic documentation adoption audit.",
569
+ inputSchema: {
570
+ includeInfo: z.boolean().default(true),
571
+ includeGenerated: z.boolean().default(false),
572
+ maxGaps: z.number().int().min(1).max(5000).default(500)
573
+ }
574
+ }, async (args) => {
575
+ if (!latest)
576
+ return catalogueNotLoadedContent();
577
+ return {
578
+ content: [
579
+ {
580
+ type: "text",
581
+ text: JSON.stringify(success(getAdoptionAudit(latest, {
582
+ includeInfo: args.includeInfo,
583
+ includeGenerated: args.includeGenerated,
584
+ maxGaps: args.maxGaps
585
+ })))
586
+ }
587
+ ]
588
+ };
589
+ });
590
+ server.registerTool("get_adoption_plan", {
591
+ description: "Get grouped adoption recommendations.",
592
+ inputSchema: {
593
+ includeInfo: z.boolean().default(true),
594
+ includeGenerated: z.boolean().default(false),
595
+ maxGaps: z.number().int().min(1).max(5000).default(500)
596
+ }
597
+ }, async (args) => {
598
+ if (!latest)
599
+ return catalogueNotLoadedContent();
600
+ return {
601
+ content: [
602
+ {
603
+ type: "text",
604
+ text: JSON.stringify(success(getAdoptionPlan(latest, {
605
+ includeInfo: args.includeInfo,
606
+ includeGenerated: args.includeGenerated,
607
+ maxGaps: args.maxGaps
608
+ })))
609
+ }
610
+ ]
611
+ };
612
+ });
613
+ server.registerTool("list_documentation_templates", {
614
+ description: "List available static documentation templates.",
615
+ inputSchema: {}
616
+ }, async () => ({
617
+ content: [
618
+ {
619
+ type: "text",
620
+ text: JSON.stringify(success({ templates: listDocumentationTemplates() }))
621
+ }
622
+ ]
623
+ }));
624
+ server.registerTool("get_documentation_template", {
625
+ description: "Get a static documentation template by template ID.",
626
+ inputSchema: {
627
+ templateId: z.string()
628
+ }
629
+ }, async (args) => {
630
+ const available = listDocumentationTemplateIds();
631
+ if (!available.includes(args.templateId)) {
632
+ return {
633
+ content: [
634
+ {
635
+ type: "text",
636
+ text: JSON.stringify(failure("template_not_found", `Unknown template ${args.templateId}.`))
637
+ }
638
+ ]
639
+ };
640
+ }
641
+ return {
642
+ content: [
643
+ {
644
+ type: "text",
645
+ text: JSON.stringify(success(getDocumentationTemplate(args.templateId)))
646
+ }
647
+ ]
648
+ };
649
+ });
650
+ server.registerTool("recommend_config", {
651
+ description: "Recommend read-only exclude patterns for high-noise generated trees currently indexed (advisory; never writes config).",
652
+ inputSchema: {
653
+ limit: z.number().int().positive().max(200).default(50)
654
+ }
655
+ }, async (args) => {
656
+ if (!latest)
657
+ return catalogueNotLoadedContent();
658
+ return {
659
+ content: [
660
+ {
661
+ type: "text",
662
+ text: JSON.stringify(success(recommendConfig(latest, { limit: args.limit })))
663
+ }
664
+ ]
665
+ };
666
+ });
667
+ server.registerTool("get_dogfood_report", {
668
+ description: "Get a compact dogfood quality report for the latest indexed snapshot.",
669
+ inputSchema: {
670
+ maxDiagnostics: z.number().int().min(1).max(200).default(20),
671
+ maxGaps: z.number().int().min(1).max(200).default(20)
672
+ }
673
+ }, async (args) => {
674
+ if (!latest) {
675
+ return catalogueNotLoadedContent();
676
+ }
677
+ return {
678
+ content: [
679
+ {
680
+ type: "text",
681
+ text: JSON.stringify(success(getDogfoodReport(latest, {
682
+ maxDiagnostics: args.maxDiagnostics,
683
+ maxGaps: args.maxGaps
684
+ })))
685
+ }
686
+ ]
687
+ };
688
+ });
689
+ server.registerTool("compare_catalogues", {
690
+ description: "Compare two committed Schema 2 catalogues and optionally write a comparison artefact.",
691
+ inputSchema: {
692
+ baseWorkspaceRoot: z.string(),
693
+ baseCatalogueDir: z.string().default(".mapwright"),
694
+ headWorkspaceRoot: z.string(),
695
+ headCatalogueDir: z.string().default(".mapwright"),
696
+ outputWorkspaceRoot: z.string().optional(),
697
+ writeDir: z.string().optional()
698
+ }
699
+ }, async (args) => {
700
+ const prior = activeComparison;
701
+ const pair = loadCataloguePair({
702
+ baseWorkspaceRoot: args.baseWorkspaceRoot,
703
+ baseCatalogueDir: args.baseCatalogueDir ?? ".mapwright",
704
+ headWorkspaceRoot: args.headWorkspaceRoot,
705
+ headCatalogueDir: args.headCatalogueDir ?? ".mapwright"
706
+ });
707
+ if (!pair.ok) {
708
+ setActiveComparison(prior);
709
+ return { content: [{ type: "text", text: JSON.stringify(failure(pair.error.code, pair.error.message)) }] };
710
+ }
711
+ const compared = compareCatalogues(pair.base, pair.head, DEFAULT_COMPARISON_OPTIONS);
712
+ if (!compared.ok) {
713
+ setActiveComparison(prior);
714
+ return { content: [{ type: "text", text: JSON.stringify(failure(compared.error.code, compared.error.message)) }] };
715
+ }
716
+ if (args.writeDir) {
717
+ const outputWorkspace = args.outputWorkspaceRoot ?? args.baseWorkspaceRoot;
718
+ const containment = assertComparisonPathContained(outputWorkspace, args.writeDir);
719
+ if (containment) {
720
+ setActiveComparison(prior);
721
+ return {
722
+ content: [{ type: "text", text: JSON.stringify(failure(containment.code, containment.message)) }]
723
+ };
724
+ }
725
+ const resolvedWrite = path.isAbsolute(args.writeDir)
726
+ ? path.resolve(args.writeDir)
727
+ : path.resolve(outputWorkspace, args.writeDir);
728
+ const written = writeComparisonArtefact(resolvedWrite, compared.comparison);
729
+ if (!written.ok) {
730
+ setActiveComparison(prior);
731
+ return {
732
+ content: [{ type: "text", text: JSON.stringify(failure(written.error.code, written.error.message)) }]
733
+ };
734
+ }
735
+ }
736
+ setActiveComparison(compared.comparison);
737
+ return {
738
+ content: [
739
+ {
740
+ type: "text",
741
+ text: JSON.stringify(success({
742
+ comparisonId: compared.comparison.manifest.comparisonId,
743
+ counts: compared.comparison.manifest.counts,
744
+ aggregateCompatibility: compared.comparison.manifest.aggregateCompatibility,
745
+ warnings: compared.comparison.warnings ?? []
746
+ }))
747
+ }
748
+ ]
749
+ };
750
+ });
751
+ server.registerTool("load_comparison", {
752
+ description: "Load a written comparison artefact without reading source repositories.",
753
+ inputSchema: {
754
+ workspaceRoot: z.string(),
755
+ comparisonDir: z.string()
756
+ }
757
+ }, async (args) => {
758
+ const prior = activeComparison;
759
+ const containment = assertComparisonPathContained(args.workspaceRoot, args.comparisonDir);
760
+ if (containment) {
761
+ setActiveComparison(prior);
762
+ return {
763
+ content: [{ type: "text", text: JSON.stringify(failure(containment.code, containment.message)) }]
764
+ };
765
+ }
766
+ const resolved = path.isAbsolute(args.comparisonDir)
767
+ ? path.resolve(args.comparisonDir)
768
+ : path.resolve(args.workspaceRoot, args.comparisonDir);
769
+ const loaded = loadComparisonArtefact(resolved);
770
+ if (!loaded.ok) {
771
+ setActiveComparison(prior);
772
+ return {
773
+ content: [{ type: "text", text: JSON.stringify(failure(loaded.error.code, loaded.error.message)) }]
774
+ };
775
+ }
776
+ setActiveComparison(loaded.comparison);
777
+ return {
778
+ content: [
779
+ {
780
+ type: "text",
781
+ text: JSON.stringify(success({
782
+ comparisonId: loaded.comparison.manifest.comparisonId,
783
+ counts: loaded.comparison.manifest.counts,
784
+ aggregateCompatibility: loaded.comparison.manifest.aggregateCompatibility
785
+ }))
786
+ }
787
+ ]
788
+ };
789
+ });
790
+ server.registerTool("get_change_summary", {
791
+ description: "Summarize the active comparison counts and aggregate compatibility.",
792
+ inputSchema: {}
793
+ }, async () => {
794
+ if (!activeComparison) {
795
+ return { content: [{ type: "text", text: JSON.stringify(comparisonNotLoadedFailure()) }] };
796
+ }
797
+ return {
798
+ content: [{ type: "text", text: JSON.stringify(success(getChangeSummary(activeComparison))) }]
799
+ };
800
+ });
801
+ server.registerTool("list_changes", {
802
+ description: "List change records from the active comparison with deterministic ordering and pagination.",
803
+ inputSchema: {
804
+ domain: z.string().optional(),
805
+ status: z.string().optional(),
806
+ offset: z.number().int().min(0).default(0),
807
+ limit: z.number().int().positive().max(200).default(50)
808
+ }
809
+ }, async (args) => {
810
+ if (!activeComparison) {
811
+ return { content: [{ type: "text", text: JSON.stringify(comparisonNotLoadedFailure()) }] };
812
+ }
813
+ return {
814
+ content: [
815
+ {
816
+ type: "text",
817
+ text: JSON.stringify(success(listChanges(activeComparison, {
818
+ domain: args.domain,
819
+ status: args.status,
820
+ offset: args.offset,
821
+ limit: args.limit
822
+ })))
823
+ }
824
+ ]
825
+ };
826
+ });
827
+ server.registerTool("get_change", {
828
+ description: "Get a single change record from the active comparison.",
829
+ inputSchema: {
830
+ changeId: z.string()
831
+ }
832
+ }, async (args) => {
833
+ if (!activeComparison) {
834
+ return { content: [{ type: "text", text: JSON.stringify(comparisonNotLoadedFailure()) }] };
835
+ }
836
+ const change = getChange(activeComparison, args.changeId);
837
+ if (!change) {
838
+ return { content: [{ type: "text", text: JSON.stringify(failure("change_not_found", `Unknown changeId ${args.changeId}`)) }] };
839
+ }
840
+ return { content: [{ type: "text", text: JSON.stringify(success(change)) }] };
841
+ });
842
+ server.registerTool("get_compatibility_report", {
843
+ description: "Return compatibility findings and aggregate classification for the active comparison.",
844
+ inputSchema: {
845
+ offset: z.number().int().min(0).default(0),
846
+ limit: z.number().int().positive().max(200).default(50)
847
+ }
848
+ }, async (args) => {
849
+ if (!activeComparison) {
850
+ return { content: [{ type: "text", text: JSON.stringify(comparisonNotLoadedFailure()) }] };
851
+ }
852
+ return {
853
+ content: [
854
+ {
855
+ type: "text",
856
+ text: JSON.stringify(success(getCompatibilityReport(activeComparison, {
857
+ offset: args.offset,
858
+ limit: args.limit
859
+ })))
860
+ }
861
+ ]
862
+ };
863
+ });
864
+ server.registerTool("get_change_impact", {
865
+ description: "Compute bounded base/head impact for a change using the Model A graph index.",
866
+ inputSchema: {
867
+ changeId: z.string(),
868
+ maxDepth: z.number().int().min(1).max(8).default(3),
869
+ offset: z.number().int().min(0).default(0),
870
+ limit: z.number().int().positive().max(200).default(50)
871
+ }
872
+ }, async (args) => {
873
+ if (!activeComparison) {
874
+ return { content: [{ type: "text", text: JSON.stringify(comparisonNotLoadedFailure()) }] };
875
+ }
876
+ const impact = getChangeImpact(activeComparison, args.changeId, {
877
+ maxDepth: args.maxDepth,
878
+ offset: args.offset,
879
+ limit: args.limit
880
+ });
881
+ if (!impact) {
882
+ return { content: [{ type: "text", text: JSON.stringify(failure("change_not_found", `Unknown changeId ${args.changeId}`)) }] };
883
+ }
884
+ return { content: [{ type: "text", text: JSON.stringify(success(impact)) }] };
885
+ });
886
+ server.registerTool("check_catalogue_drift", {
887
+ description: "Explicitly index the workspace in memory and compare against a committed catalogue without writing files.",
888
+ inputSchema: {
889
+ workspaceRoot: z.string(),
890
+ catalogueDir: z.string().default(".mapwright")
891
+ }
892
+ }, async (args) => {
893
+ const result = checkCatalogueDrift({
894
+ workspaceRoot: args.workspaceRoot,
895
+ catalogueDir: args.catalogueDir ?? ".mapwright"
896
+ });
897
+ if (!result.ok) {
898
+ return { content: [{ type: "text", text: JSON.stringify(failure(result.error.code, result.error.message)) }] };
899
+ }
900
+ return { content: [{ type: "text", text: JSON.stringify(success(result.data)) }] };
901
+ });
902
+ }