@praneeth_54/agentdoctor 1.0.0 → 1.1.1

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 (208) hide show
  1. package/CHANGELOG.md +60 -5
  2. package/README.md +462 -251
  3. package/dist/cli/commands/brain-mcp.d.ts +9 -0
  4. package/dist/cli/commands/brain-mcp.js +38 -0
  5. package/dist/cli/program.js +13 -0
  6. package/dist/constants.d.ts +1 -1
  7. package/dist/constants.js +1 -1
  8. package/dist/core/understanding/architecture/index.d.ts +4 -0
  9. package/dist/core/understanding/architecture/index.js +3 -0
  10. package/dist/core/understanding/architecture/infer.d.ts +6 -0
  11. package/dist/core/understanding/architecture/infer.js +39 -0
  12. package/dist/core/understanding/architecture/models.d.ts +23 -0
  13. package/dist/core/understanding/architecture/models.js +127 -0
  14. package/dist/core/understanding/architecture/patterns/distribution.d.ts +2 -0
  15. package/dist/core/understanding/architecture/patterns/distribution.js +115 -0
  16. package/dist/core/understanding/architecture/patterns/index.d.ts +2 -0
  17. package/dist/core/understanding/architecture/patterns/index.js +8 -0
  18. package/dist/core/understanding/architecture/patterns/layered.d.ts +2 -0
  19. package/dist/core/understanding/architecture/patterns/layered.js +279 -0
  20. package/dist/core/understanding/architecture/patterns/structural.d.ts +2 -0
  21. package/dist/core/understanding/architecture/patterns/structural.js +197 -0
  22. package/dist/core/understanding/architecture/rules.d.ts +4 -0
  23. package/dist/core/understanding/architecture/rules.js +6 -0
  24. package/dist/core/understanding/architecture/types.d.ts +52 -0
  25. package/dist/core/understanding/architecture/types.js +1 -0
  26. package/dist/core/understanding/brain/build.d.ts +13 -0
  27. package/dist/core/understanding/brain/build.js +158 -0
  28. package/dist/core/understanding/brain/claims/index.d.ts +3 -0
  29. package/dist/core/understanding/brain/claims/index.js +2 -0
  30. package/dist/core/understanding/brain/claims/lifecycle.d.ts +16 -0
  31. package/dist/core/understanding/brain/claims/lifecycle.js +78 -0
  32. package/dist/core/understanding/brain/claims/types.d.ts +32 -0
  33. package/dist/core/understanding/brain/claims/types.js +25 -0
  34. package/dist/core/understanding/brain/components/index.d.ts +17 -0
  35. package/dist/core/understanding/brain/components/index.js +97 -0
  36. package/dist/core/understanding/brain/components/types.d.ts +16 -0
  37. package/dist/core/understanding/brain/components/types.js +8 -0
  38. package/dist/core/understanding/brain/confidence.d.ts +19 -0
  39. package/dist/core/understanding/brain/confidence.js +39 -0
  40. package/dist/core/understanding/brain/contract.d.ts +17 -0
  41. package/dist/core/understanding/brain/contract.js +100 -0
  42. package/dist/core/understanding/brain/contradictions/index.d.ts +12 -0
  43. package/dist/core/understanding/brain/contradictions/index.js +57 -0
  44. package/dist/core/understanding/brain/contradictions/types.d.ts +12 -0
  45. package/dist/core/understanding/brain/contradictions/types.js +8 -0
  46. package/dist/core/understanding/brain/delta.d.ts +25 -0
  47. package/dist/core/understanding/brain/delta.js +116 -0
  48. package/dist/core/understanding/brain/evidence/index.d.ts +3 -0
  49. package/dist/core/understanding/brain/evidence/index.js +2 -0
  50. package/dist/core/understanding/brain/evidence/redact.d.ts +4 -0
  51. package/dist/core/understanding/brain/evidence/redact.js +36 -0
  52. package/dist/core/understanding/brain/evidence/types.d.ts +34 -0
  53. package/dist/core/understanding/brain/evidence/types.js +35 -0
  54. package/dist/core/understanding/brain/explain.d.ts +17 -0
  55. package/dist/core/understanding/brain/explain.js +33 -0
  56. package/dist/core/understanding/brain/index.d.ts +28 -0
  57. package/dist/core/understanding/brain/index.js +16 -0
  58. package/dist/core/understanding/brain/migrate.d.ts +21 -0
  59. package/dist/core/understanding/brain/migrate.js +47 -0
  60. package/dist/core/understanding/brain/query.d.ts +60 -0
  61. package/dist/core/understanding/brain/query.js +92 -0
  62. package/dist/core/understanding/brain/security.d.ts +3 -0
  63. package/dist/core/understanding/brain/security.js +43 -0
  64. package/dist/core/understanding/brain/storage/index.d.ts +2 -0
  65. package/dist/core/understanding/brain/storage/index.js +1 -0
  66. package/dist/core/understanding/brain/storage/store.d.ts +53 -0
  67. package/dist/core/understanding/brain/storage/store.js +305 -0
  68. package/dist/core/understanding/brain/trace.d.ts +24 -0
  69. package/dist/core/understanding/brain/trace.js +207 -0
  70. package/dist/core/understanding/brain/types.d.ts +32 -0
  71. package/dist/core/understanding/brain/types.js +8 -0
  72. package/dist/core/understanding/brain/version.d.ts +13 -0
  73. package/dist/core/understanding/brain/version.js +7 -0
  74. package/dist/core/understanding/delta/compare.d.ts +26 -0
  75. package/dist/core/understanding/delta/compare.js +74 -0
  76. package/dist/core/understanding/delta/index.d.ts +2 -0
  77. package/dist/core/understanding/delta/index.js +1 -0
  78. package/dist/core/understanding/dependencies/discover.d.ts +7 -0
  79. package/dist/core/understanding/dependencies/discover.js +550 -0
  80. package/dist/core/understanding/dependencies/extract.d.ts +10 -0
  81. package/dist/core/understanding/dependencies/extract.js +189 -0
  82. package/dist/core/understanding/dependencies/index.d.ts +4 -0
  83. package/dist/core/understanding/dependencies/index.js +3 -0
  84. package/dist/core/understanding/dependencies/models.d.ts +12 -0
  85. package/dist/core/understanding/dependencies/models.js +125 -0
  86. package/dist/core/understanding/dependencies/types.d.ts +27 -0
  87. package/dist/core/understanding/dependencies/types.js +1 -0
  88. package/dist/core/understanding/domain/discover.d.ts +14 -0
  89. package/dist/core/understanding/domain/discover.js +90 -0
  90. package/dist/core/understanding/domain/index.d.ts +2 -0
  91. package/dist/core/understanding/domain/index.js +1 -0
  92. package/dist/core/understanding/entrypoints/discover.d.ts +6 -0
  93. package/dist/core/understanding/entrypoints/discover.js +62 -0
  94. package/dist/core/understanding/entrypoints/extract.d.ts +19 -0
  95. package/dist/core/understanding/entrypoints/extract.js +43 -0
  96. package/dist/core/understanding/entrypoints/index.d.ts +4 -0
  97. package/dist/core/understanding/entrypoints/index.js +3 -0
  98. package/dist/core/understanding/entrypoints/models.d.ts +26 -0
  99. package/dist/core/understanding/entrypoints/models.js +216 -0
  100. package/dist/core/understanding/entrypoints/types.d.ts +20 -0
  101. package/dist/core/understanding/entrypoints/types.js +1 -0
  102. package/dist/core/understanding/index.d.ts +32 -0
  103. package/dist/core/understanding/index.js +18 -0
  104. package/dist/core/understanding/mind/build.d.ts +16 -0
  105. package/dist/core/understanding/mind/build.js +134 -0
  106. package/dist/core/understanding/mind/index.d.ts +6 -0
  107. package/dist/core/understanding/mind/index.js +3 -0
  108. package/dist/core/understanding/mind/query.d.ts +33 -0
  109. package/dist/core/understanding/mind/query.js +39 -0
  110. package/dist/core/understanding/mind/types.d.ts +24 -0
  111. package/dist/core/understanding/mind/types.js +7 -0
  112. package/dist/core/understanding/model/builder.d.ts +9 -0
  113. package/dist/core/understanding/model/builder.js +209 -0
  114. package/dist/core/understanding/model/ids.d.ts +7 -0
  115. package/dist/core/understanding/model/ids.js +48 -0
  116. package/dist/core/understanding/model/index.d.ts +7 -0
  117. package/dist/core/understanding/model/index.js +6 -0
  118. package/dist/core/understanding/model/schema.d.ts +12 -0
  119. package/dist/core/understanding/model/schema.js +33 -0
  120. package/dist/core/understanding/model/serializer.d.ts +9 -0
  121. package/dist/core/understanding/model/serializer.js +46 -0
  122. package/dist/core/understanding/model/types.d.ts +110 -0
  123. package/dist/core/understanding/model/types.js +1 -0
  124. package/dist/core/understanding/model/validator.d.ts +5 -0
  125. package/dist/core/understanding/model/validator.js +126 -0
  126. package/dist/core/understanding/model/version.d.ts +9 -0
  127. package/dist/core/understanding/model/version.js +7 -0
  128. package/dist/core/understanding/ownership/discover.d.ts +12 -0
  129. package/dist/core/understanding/ownership/discover.js +242 -0
  130. package/dist/core/understanding/ownership/index.d.ts +2 -0
  131. package/dist/core/understanding/ownership/index.js +1 -0
  132. package/dist/core/understanding/ownership/types.d.ts +23 -0
  133. package/dist/core/understanding/ownership/types.js +1 -0
  134. package/dist/core/understanding/query/engine.d.ts +24 -0
  135. package/dist/core/understanding/query/engine.js +35 -0
  136. package/dist/core/understanding/query/errors.d.ts +13 -0
  137. package/dist/core/understanding/query/errors.js +26 -0
  138. package/dist/core/understanding/query/executor.d.ts +8 -0
  139. package/dist/core/understanding/query/executor.js +34 -0
  140. package/dist/core/understanding/query/index.d.ts +7 -0
  141. package/dist/core/understanding/query/index.js +5 -0
  142. package/dist/core/understanding/query/models.d.ts +102 -0
  143. package/dist/core/understanding/query/models.js +1 -0
  144. package/dist/core/understanding/query/query.d.ts +68 -0
  145. package/dist/core/understanding/query/query.js +145 -0
  146. package/dist/core/understanding/query/registry.d.ts +8 -0
  147. package/dist/core/understanding/query/registry.js +340 -0
  148. package/dist/core/understanding/query/types.d.ts +63 -0
  149. package/dist/core/understanding/query/types.js +1 -0
  150. package/dist/core/understanding/relationships/discover.d.ts +11 -0
  151. package/dist/core/understanding/relationships/discover.js +517 -0
  152. package/dist/core/understanding/relationships/extract.d.ts +14 -0
  153. package/dist/core/understanding/relationships/extract.js +165 -0
  154. package/dist/core/understanding/relationships/index.d.ts +4 -0
  155. package/dist/core/understanding/relationships/index.js +3 -0
  156. package/dist/core/understanding/relationships/models.d.ts +21 -0
  157. package/dist/core/understanding/relationships/models.js +185 -0
  158. package/dist/core/understanding/relationships/types.d.ts +42 -0
  159. package/dist/core/understanding/relationships/types.js +1 -0
  160. package/dist/core/understanding/risks/discover.d.ts +8 -0
  161. package/dist/core/understanding/risks/discover.js +155 -0
  162. package/dist/core/understanding/risks/index.d.ts +2 -0
  163. package/dist/core/understanding/risks/index.js +1 -0
  164. package/dist/core/understanding/risks/types.d.ts +26 -0
  165. package/dist/core/understanding/risks/types.js +1 -0
  166. package/dist/core/understanding/shared/domain-lexicon.d.ts +9 -0
  167. package/dist/core/understanding/shared/domain-lexicon.js +107 -0
  168. package/dist/core/understanding/shared/index.d.ts +2 -0
  169. package/dist/core/understanding/shared/index.js +2 -0
  170. package/dist/core/understanding/shared/tokens.d.ts +7 -0
  171. package/dist/core/understanding/shared/tokens.js +141 -0
  172. package/dist/core/understanding/snapshot/identity.d.ts +24 -0
  173. package/dist/core/understanding/snapshot/identity.js +79 -0
  174. package/dist/core/understanding/snapshot/index.d.ts +2 -0
  175. package/dist/core/understanding/snapshot/index.js +1 -0
  176. package/dist/core/understanding/types/index.d.ts +22 -0
  177. package/dist/core/understanding/types/index.js +1 -0
  178. package/dist/core/understanding/understand/formatter.d.ts +10 -0
  179. package/dist/core/understanding/understand/formatter.js +115 -0
  180. package/dist/core/understanding/understand/index.d.ts +4 -0
  181. package/dist/core/understanding/understand/index.js +3 -0
  182. package/dist/core/understanding/understand/service.d.ts +17 -0
  183. package/dist/core/understanding/understand/service.js +76 -0
  184. package/dist/core/understanding/understand/summary.d.ts +14 -0
  185. package/dist/core/understanding/understand/summary.js +39 -0
  186. package/dist/core/understanding/understand/types.d.ts +31 -0
  187. package/dist/core/understanding/understand/types.js +1 -0
  188. package/dist/mcp/brain/compile.d.ts +9 -0
  189. package/dist/mcp/brain/compile.js +42 -0
  190. package/dist/mcp/brain/errors.d.ts +12 -0
  191. package/dist/mcp/brain/errors.js +23 -0
  192. package/dist/mcp/brain/index.d.ts +11 -0
  193. package/dist/mcp/brain/index.js +8 -0
  194. package/dist/mcp/brain/provenance.d.ts +29 -0
  195. package/dist/mcp/brain/provenance.js +59 -0
  196. package/dist/mcp/brain/schemas.d.ts +13 -0
  197. package/dist/mcp/brain/schemas.js +105 -0
  198. package/dist/mcp/brain/security/root.d.ts +6 -0
  199. package/dist/mcp/brain/security/root.js +66 -0
  200. package/dist/mcp/brain/server.d.ts +12 -0
  201. package/dist/mcp/brain/server.js +46 -0
  202. package/dist/mcp/brain/session.d.ts +33 -0
  203. package/dist/mcp/brain/session.js +117 -0
  204. package/dist/mcp/brain/tools/handlers.d.ts +13 -0
  205. package/dist/mcp/brain/tools/handlers.js +483 -0
  206. package/dist/mcp/brain/tools/registry.d.ts +9 -0
  207. package/dist/mcp/brain/tools/registry.js +191 -0
  208. package/package.json +11 -4
@@ -0,0 +1,483 @@
1
+ import { averageBrainConfidence, buildBrainDelta, createBrainQueryEngine, explainClaim, PROJECT_BRAIN_LIMITATIONS, redactEvidenceList, traceBrain, } from "../../../core/understanding/brain/index.js";
2
+ import { BrainMcpError } from "../errors.js";
3
+ import { wrapProvenance } from "../provenance.js";
4
+ import { asObject, parseClaimStatus, requireString, resolveQueryType, resolveTraceMode, } from "../schemas.js";
5
+ function optionalString(args, field) {
6
+ const value = args[field];
7
+ if (value === undefined || value === null) {
8
+ return undefined;
9
+ }
10
+ return requireString(value, field);
11
+ }
12
+ export async function handleBrainOverview(session) {
13
+ const brain = session.getBrain();
14
+ const activeClaims = brain.claims.filter((c) => c.status === "ACTIVE");
15
+ const contradicted = brain.claims.filter((c) => c.status === "CONTRADICTED");
16
+ const confidences = [
17
+ ...activeClaims.map((c) => c.confidence),
18
+ ...brain.components.map((c) => c.confidence),
19
+ ...brain.risks.risks.map((r) => r.confidence),
20
+ ];
21
+ return wrapProvenance({
22
+ brain,
23
+ evidenceIds: brain.evidence
24
+ .map((e) => e.id)
25
+ .slice()
26
+ .sort((a, b) => a.localeCompare(b)),
27
+ confidence: averageBrainConfidence(confidences),
28
+ metadata: { tool: "brain_overview" },
29
+ result: {
30
+ repository: {
31
+ projectName: brain.metadata.projectName,
32
+ brainId: brain.metadata.brainId,
33
+ root: session.getProjectRoot(),
34
+ },
35
+ snapshotId: brain.snapshot.id,
36
+ schemaVersion: brain.metadata.schemaVersion,
37
+ modelVersion: brain.model.metadata.schemaVersion,
38
+ domains: brain.model.domains
39
+ .map((d) => d.name)
40
+ .slice()
41
+ .sort((a, b) => a.localeCompare(b)),
42
+ entrypoints: brain.model.entrypoints
43
+ .map((e) => ({ file: e.file, framework: e.framework, confidence: e.confidence }))
44
+ .slice()
45
+ .sort((a, b) => a.file.localeCompare(b.file)),
46
+ components: brain.components
47
+ .map((c) => ({ id: c.id, name: c.name, type: c.type, path: c.path }))
48
+ .slice()
49
+ .sort((a, b) => a.path.localeCompare(b.path)),
50
+ architecture: brain.model.architectures
51
+ .map((a) => ({
52
+ pattern: a.pattern,
53
+ confidence: a.confidence,
54
+ matchedRules: a.matchedRules,
55
+ }))
56
+ .slice()
57
+ .sort((a, b) => a.pattern.localeCompare(b.pattern)),
58
+ ownershipSummary: {
59
+ matchCount: brain.ownership.ownerships.length,
60
+ unknowns: brain.ownership.unknowns.slice().sort((a, b) => a.localeCompare(b)),
61
+ },
62
+ riskSummary: {
63
+ riskKind: "change-danger",
64
+ notVulnerabilityScanner: true,
65
+ count: brain.risks.risks.length,
66
+ byKind: Object.fromEntries([...brain.risks.risks]
67
+ .reduce((map, risk) => {
68
+ map.set(risk.kind, (map.get(risk.kind) ?? 0) + 1);
69
+ return map;
70
+ }, new Map())
71
+ .entries()),
72
+ },
73
+ activeClaimCount: activeClaims.length,
74
+ contradictedClaimCount: contradicted.length,
75
+ contradictionCount: brain.contradictions.length,
76
+ unknownCount: brain.unknowns.length,
77
+ limitations: [...brain.limitations, ...PROJECT_BRAIN_LIMITATIONS],
78
+ confidenceSummary: {
79
+ contract: brain.confidenceContract,
80
+ average: averageBrainConfidence(confidences),
81
+ },
82
+ },
83
+ });
84
+ }
85
+ export async function handleBrainQuery(session, rawArgs) {
86
+ const args = asObject(rawArgs);
87
+ const typeInput = requireString(args.type ?? args.queryType ?? args.intent, "type");
88
+ const queryType = resolveQueryType(typeInput);
89
+ if (queryType === "ListChanges") {
90
+ throw new BrainMcpError("unsupported_query", "ListChanges requires an in-memory previous brain; use brain_delta with snapshot ids instead");
91
+ }
92
+ let query;
93
+ if (queryType === "Impact" || queryType === "BlastRadius") {
94
+ const target = requireString(args.target, "target");
95
+ query = { type: queryType, target };
96
+ }
97
+ else if (queryType === "ListClaims") {
98
+ const status = parseClaimStatus(optionalString(args, "status"));
99
+ query = status ? { type: "ListClaims", status } : { type: "ListClaims" };
100
+ }
101
+ else {
102
+ query = {
103
+ type: queryType,
104
+ };
105
+ }
106
+ const brain = session.getBrain();
107
+ const response = createBrainQueryEngine(brain).execute(query);
108
+ return wrapProvenance({
109
+ brain,
110
+ result: response.result,
111
+ evidenceIds: response.evidenceIds,
112
+ confidence: response.confidence,
113
+ metadata: {
114
+ tool: "brain_query",
115
+ queryType: response.metadata.queryType,
116
+ brainId: response.metadata.brainId,
117
+ },
118
+ });
119
+ }
120
+ export async function handleBrainExplain(session, rawArgs) {
121
+ const args = asObject(rawArgs);
122
+ const claimId = requireString(args.claimId ?? args.id, "claimId");
123
+ const brain = session.getBrain();
124
+ const explanation = explainClaim(brain, claimId);
125
+ if (!explanation) {
126
+ throw new BrainMcpError("not_found", `claim not found: ${claimId}`);
127
+ }
128
+ return wrapProvenance({
129
+ brain,
130
+ result: {
131
+ claim: explanation.claim,
132
+ status: explanation.status,
133
+ confidence: explanation.confidence,
134
+ evidence: redactEvidenceList(explanation.supportingEvidence),
135
+ contradictions: explanation.contradictions,
136
+ invalidation: explanation.invalidationState,
137
+ unknowns: explanation.unknowns,
138
+ },
139
+ evidenceIds: explanation.supportingEvidence.map((e) => e.id),
140
+ confidence: explanation.confidence,
141
+ claimStatus: explanation.status,
142
+ metadata: { tool: "brain_explain", claimId },
143
+ });
144
+ }
145
+ export async function handleBrainTrace(session, rawArgs) {
146
+ const args = asObject(rawArgs);
147
+ const target = requireString(args.target ?? args.root, "target");
148
+ const mode = resolveTraceMode(optionalString(args, "mode"));
149
+ const brain = session.getBrain();
150
+ const trace = traceBrain(brain, target, mode);
151
+ const evidenceIds = [
152
+ ...new Set(trace.edges.flatMap((edge) => [...edge.evidence])),
153
+ ].sort((a, b) => a.localeCompare(b));
154
+ const truncated = trace.edges.length >= 5000;
155
+ return wrapProvenance({
156
+ brain,
157
+ result: {
158
+ root: trace.root,
159
+ mode,
160
+ nodes: trace.nodes,
161
+ edges: trace.edges,
162
+ cycles: trace.cycles,
163
+ caps: { maxDepth: 25, maxEdges: 5000 },
164
+ truncated: truncated || trace.edges.length >= 5000,
165
+ confidence: trace.confidence,
166
+ },
167
+ evidenceIds,
168
+ confidence: trace.confidence,
169
+ metadata: { tool: "brain_trace", mode, target },
170
+ });
171
+ }
172
+ export async function handleBrainClaims(session, rawArgs) {
173
+ const args = asObject(rawArgs);
174
+ const status = parseClaimStatus(optionalString(args, "status"));
175
+ const includeHistorical = args.includeHistorical === true;
176
+ const brain = session.getBrain();
177
+ let claims = [...brain.claims];
178
+ if (status) {
179
+ claims = claims.filter((c) => c.status === status);
180
+ }
181
+ else if (!includeHistorical) {
182
+ // Truth filter: default excludes INVALIDATED / SUPERSEDED
183
+ claims = claims.filter((c) => c.status === "ACTIVE" || c.status === "CONTRADICTED");
184
+ }
185
+ claims.sort((a, b) => a.id.localeCompare(b.id));
186
+ return wrapProvenance({
187
+ brain,
188
+ result: {
189
+ claims: claims.map((c) => ({
190
+ id: c.id,
191
+ subject: c.subject,
192
+ predicate: c.predicate,
193
+ object: c.object,
194
+ status: c.status,
195
+ confidence: c.confidence,
196
+ evidenceIds: c.evidenceIds,
197
+ snapshotId: c.snapshotId,
198
+ contradictionIds: c.contradictionIds,
199
+ invalidatedAt: c.invalidatedAt,
200
+ supersededBy: c.supersededBy,
201
+ })),
202
+ truthFilter: status
203
+ ? `status=${status}`
204
+ : includeHistorical
205
+ ? "all-statuses"
206
+ : "ACTIVE+CONTRADICTED",
207
+ },
208
+ evidenceIds: claims.flatMap((c) => c.evidenceIds),
209
+ confidence: averageBrainConfidence(claims.map((c) => c.confidence)),
210
+ metadata: { tool: "brain_claims", includeHistorical, status: status ?? null },
211
+ });
212
+ }
213
+ export async function handleBrainEvidence(session, rawArgs) {
214
+ const args = asObject(rawArgs);
215
+ const evidenceId = optionalString(args, "evidenceId") ?? optionalString(args, "id");
216
+ const brain = session.getBrain();
217
+ let evidence = [...redactEvidenceList(brain.evidence)];
218
+ if (evidenceId) {
219
+ evidence = evidence.filter((e) => e.id === evidenceId);
220
+ if (evidence.length === 0) {
221
+ throw new BrainMcpError("not_found", `evidence not found: ${evidenceId}`);
222
+ }
223
+ }
224
+ evidence = [...evidence].sort((a, b) => a.id.localeCompare(b.id));
225
+ const claimLinks = brain.claims
226
+ .filter((c) => c.evidenceIds.some((id) => evidence.some((e) => e.id === id)))
227
+ .map((c) => ({
228
+ claimId: c.id,
229
+ status: c.status,
230
+ evidenceIds: c.evidenceIds.filter((id) => evidence.some((e) => e.id === id)),
231
+ }))
232
+ .sort((a, b) => a.claimId.localeCompare(b.claimId));
233
+ return wrapProvenance({
234
+ brain,
235
+ result: {
236
+ evidence: evidence.map((e) => ({
237
+ id: e.id,
238
+ kind: e.kind,
239
+ epistemics: e.epistemics,
240
+ locator: e.locator,
241
+ source: e.source,
242
+ symbol: e.symbol,
243
+ redaction: e.redaction,
244
+ })),
245
+ claimRelationships: claimLinks,
246
+ redacted: true,
247
+ },
248
+ evidenceIds: evidence.map((e) => e.id),
249
+ confidence: 1,
250
+ metadata: { tool: "brain_evidence" },
251
+ });
252
+ }
253
+ export async function handleBrainOwnership(session, rawArgs) {
254
+ const args = asObject(rawArgs);
255
+ const filePath = optionalString(args, "path") ?? optionalString(args, "filePath");
256
+ const brain = session.getBrain();
257
+ let ownerships = [...brain.ownership.ownerships];
258
+ if (filePath) {
259
+ ownerships = ownerships.filter((o) => o.path === filePath ||
260
+ o.path.endsWith(`/${filePath}`) ||
261
+ filePath.endsWith(o.path) ||
262
+ o.path.includes(filePath));
263
+ }
264
+ ownerships.sort((a, b) => a.path.localeCompare(b.path));
265
+ const result = filePath && ownerships.length === 0
266
+ ? {
267
+ path: filePath,
268
+ ownership: "UNKNOWN",
269
+ matches: [],
270
+ unknowns: brain.ownership.unknowns,
271
+ sourcesAllowed: ["CODEOWNERS", "MAINTAINERS", "package-metadata"],
272
+ note: "Ownership is UNKNOWN without explicit CODEOWNERS / MAINTAINERS / package evidence",
273
+ }
274
+ : {
275
+ matches: ownerships,
276
+ unknowns: brain.ownership.unknowns.slice().sort((a, b) => a.localeCompare(b)),
277
+ sourcesAllowed: ["CODEOWNERS", "MAINTAINERS", "package-metadata"],
278
+ inferenceForbidden: [
279
+ "git-blame",
280
+ "cwd-guessing",
281
+ "author-guessing",
282
+ "commit-frequency",
283
+ ],
284
+ };
285
+ return wrapProvenance({
286
+ brain,
287
+ result,
288
+ evidenceIds: [],
289
+ confidence: averageBrainConfidence(ownerships.map((o) => o.confidence)),
290
+ metadata: { tool: "brain_ownership", path: filePath ?? null },
291
+ });
292
+ }
293
+ export async function handleBrainRisk(session, rawArgs) {
294
+ const args = asObject(rawArgs);
295
+ const kind = optionalString(args, "kind");
296
+ const brain = session.getBrain();
297
+ let risks = [...brain.risks.risks];
298
+ if (kind) {
299
+ risks = risks.filter((r) => r.kind === kind);
300
+ }
301
+ risks.sort((a, b) => {
302
+ const byKind = a.kind.localeCompare(b.kind);
303
+ if (byKind !== 0)
304
+ return byKind;
305
+ return a.target.localeCompare(b.target);
306
+ });
307
+ return wrapProvenance({
308
+ brain,
309
+ result: {
310
+ riskKind: "change-danger",
311
+ notVulnerabilityScanner: true,
312
+ distinction: {
313
+ changeRisk: "Project Brain change-danger (entrypoint centrality, coupling, ownership clarity, architecture conflict)",
314
+ securityVulnerability: "AgentDoctor Safety Scan findings — not produced by this tool",
315
+ },
316
+ risks: risks.map((r) => ({
317
+ kind: r.kind,
318
+ severity: r.severity,
319
+ target: r.target,
320
+ rationale: r.rationale,
321
+ confidence: r.confidence,
322
+ evidence: r.evidence,
323
+ })),
324
+ unknowns: brain.risks.unknowns.slice().sort((a, b) => a.localeCompare(b)),
325
+ },
326
+ evidenceIds: [],
327
+ confidence: averageBrainConfidence(risks.map((r) => r.confidence)),
328
+ metadata: { tool: "brain_risk", kind: kind ?? null },
329
+ });
330
+ }
331
+ export async function handleBrainDelta(session, rawArgs) {
332
+ const args = asObject(rawArgs);
333
+ const fromSnapshot = requireString(args.fromSnapshot ?? args.from, "fromSnapshot");
334
+ const toSnapshot = optionalString(args, "toSnapshot") ??
335
+ optionalString(args, "to") ??
336
+ session.getBrain().snapshot.id;
337
+ const store = session.getStore();
338
+ let before;
339
+ let after;
340
+ try {
341
+ before = await store.loadSnapshot(fromSnapshot);
342
+ after =
343
+ toSnapshot === session.getBrain().snapshot.id
344
+ ? session.getBrain()
345
+ : await store.loadSnapshot(toSnapshot);
346
+ }
347
+ catch (error) {
348
+ const message = error instanceof Error ? error.message : "failed to load snapshots";
349
+ throw new BrainMcpError("brain_corrupt", message);
350
+ }
351
+ const delta = buildBrainDelta(before, after);
352
+ return wrapProvenance({
353
+ brain: after,
354
+ result: {
355
+ fromSnapshot: delta.beforeSnapshotId,
356
+ toSnapshot: delta.afterSnapshotId,
357
+ added: delta.addedClaimIds,
358
+ removed: delta.removedClaimIds,
359
+ changed: delta.changedClaimIds,
360
+ invalidatedClaims: delta.invalidatedClaimIds,
361
+ newClaims: delta.addedClaimIds,
362
+ supersededClaims: delta.supersededClaimIds,
363
+ contradictedClaims: delta.contradictedClaimIds,
364
+ contradictions: {
365
+ added: delta.newContradictionIds,
366
+ resolved: delta.resolvedContradictionIds,
367
+ },
368
+ components: {
369
+ added: delta.addedComponentIds,
370
+ removed: delta.removedComponentIds,
371
+ },
372
+ ownershipChanges: delta.ownershipChanges,
373
+ riskChanges: delta.riskChanges,
374
+ dependencyChanges: delta.dependencyChanges,
375
+ summary: delta.summary,
376
+ },
377
+ evidenceIds: [],
378
+ confidence: 1,
379
+ metadata: {
380
+ tool: "brain_delta",
381
+ fromSnapshot,
382
+ toSnapshot,
383
+ readOnly: true,
384
+ },
385
+ });
386
+ }
387
+ export async function handleBrainSnapshot(session, rawArgs) {
388
+ const args = asObject(rawArgs);
389
+ const action = (optionalString(args, "action") ?? "current").toLowerCase();
390
+ if (action === "rebuild") {
391
+ const brain = await session.rebuild();
392
+ return wrapProvenance({
393
+ brain,
394
+ result: {
395
+ action: "rebuild",
396
+ snapshot: {
397
+ id: brain.snapshot.id,
398
+ contentHash: brain.snapshot.contentHash,
399
+ createdAt: brain.snapshot.generatedAt,
400
+ },
401
+ },
402
+ evidenceIds: [],
403
+ confidence: 1,
404
+ metadata: { tool: "brain_snapshot", action },
405
+ });
406
+ }
407
+ if (action === "history") {
408
+ const brain = session.getBrain();
409
+ const snapshots = await session.listSnapshots();
410
+ const sorted = [...snapshots].sort((a, b) => a.id.localeCompare(b.id));
411
+ return wrapProvenance({
412
+ brain,
413
+ result: {
414
+ action: "history",
415
+ latestSnapshotId: brain.snapshot.id,
416
+ snapshots: sorted.map((s) => ({
417
+ id: s.id,
418
+ contentHash: s.contentHash,
419
+ createdAt: s.createdAt,
420
+ checksum: s.checksum,
421
+ projectName: s.projectName,
422
+ brainId: s.brainId,
423
+ })),
424
+ },
425
+ evidenceIds: [],
426
+ confidence: 1,
427
+ metadata: { tool: "brain_snapshot", action },
428
+ });
429
+ }
430
+ if (action === "compare") {
431
+ const leftId = requireString(args.leftId ?? args.fromSnapshot, "leftId");
432
+ const rightId = requireString(args.rightId ?? args.toSnapshot, "rightId");
433
+ const comparison = await session.compareSnapshots(leftId, rightId);
434
+ const brain = session.getBrain();
435
+ return wrapProvenance({
436
+ brain,
437
+ result: { action: "compare", comparison },
438
+ evidenceIds: [],
439
+ confidence: 1,
440
+ metadata: { tool: "brain_snapshot", action },
441
+ });
442
+ }
443
+ if (action === "load") {
444
+ const snapshotId = requireString(args.snapshotId ?? args.id, "snapshotId");
445
+ const brain = await session.loadSnapshot(snapshotId);
446
+ return wrapProvenance({
447
+ brain,
448
+ result: {
449
+ action: "load",
450
+ snapshot: {
451
+ id: brain.snapshot.id,
452
+ contentHash: brain.snapshot.contentHash,
453
+ createdAt: brain.snapshot.generatedAt,
454
+ },
455
+ },
456
+ evidenceIds: [],
457
+ confidence: 1,
458
+ metadata: { tool: "brain_snapshot", action },
459
+ });
460
+ }
461
+ if (action === "current" || action === "latest" || action === "metadata") {
462
+ const brain = session.getBrain();
463
+ return wrapProvenance({
464
+ brain,
465
+ result: {
466
+ action: "current",
467
+ snapshot: {
468
+ id: brain.snapshot.id,
469
+ schemaVersion: brain.metadata.schemaVersion,
470
+ contentHash: brain.snapshot.contentHash,
471
+ createdAt: brain.snapshot.generatedAt,
472
+ brainId: brain.metadata.brainId,
473
+ projectName: brain.metadata.projectName,
474
+ },
475
+ storeRoot: session.getStore().root,
476
+ },
477
+ evidenceIds: [],
478
+ confidence: 1,
479
+ metadata: { tool: "brain_snapshot", action: "current" },
480
+ });
481
+ }
482
+ throw new BrainMcpError("invalid_argument", `unsupported brain_snapshot action "${action}"; use current|history|compare|load|rebuild`);
483
+ }
@@ -0,0 +1,9 @@
1
+ import type { Tool } from "@modelcontextprotocol/sdk/types.js";
2
+ import type { BrainMcpSession } from "../session.js";
3
+ export declare const BRAIN_MCP_TOOL_NAMES: readonly ["brain_overview", "brain_query", "brain_explain", "brain_trace", "brain_claims", "brain_evidence", "brain_ownership", "brain_risk", "brain_delta", "brain_snapshot"];
4
+ export type BrainMcpToolName = (typeof BRAIN_MCP_TOOL_NAMES)[number];
5
+ export declare function listBrainMcpTools(): Tool[];
6
+ export declare function invokeBrainMcpTool(session: BrainMcpSession, name: string, args: unknown): Promise<{
7
+ structured: unknown;
8
+ isError: boolean;
9
+ }>;
@@ -0,0 +1,191 @@
1
+ import { toToolErrorPayload } from "../errors.js";
2
+ import { handleBrainClaims, handleBrainDelta, handleBrainEvidence, handleBrainExplain, handleBrainOverview, handleBrainOwnership, handleBrainQuery, handleBrainRisk, handleBrainSnapshot, handleBrainTrace, } from "./handlers.js";
3
+ export const BRAIN_MCP_TOOL_NAMES = [
4
+ "brain_overview",
5
+ "brain_query",
6
+ "brain_explain",
7
+ "brain_trace",
8
+ "brain_claims",
9
+ "brain_evidence",
10
+ "brain_ownership",
11
+ "brain_risk",
12
+ "brain_delta",
13
+ "brain_snapshot",
14
+ ];
15
+ const emptyObjectSchema = {
16
+ type: "object",
17
+ properties: {},
18
+ additionalProperties: false,
19
+ };
20
+ export function listBrainMcpTools() {
21
+ return [
22
+ {
23
+ name: "brain_overview",
24
+ description: "READ: Compact Project Brain overview (domains, entrypoints, components, ownership/risk summaries, claim counts) with provenance. Not a file search.",
25
+ inputSchema: emptyObjectSchema,
26
+ },
27
+ {
28
+ name: "brain_query",
29
+ description: "READ: Typed BrainQueryEngine query only (ProjectSummary, ListEntrypoints, ListRisks, Impact, …). No arbitrary code/shell. Does not invent facts.",
30
+ inputSchema: {
31
+ type: "object",
32
+ properties: {
33
+ type: {
34
+ type: "string",
35
+ description: "Query type or alias (e.g. ProjectSummary, find_risks, Impact)",
36
+ },
37
+ target: {
38
+ type: "string",
39
+ description: "Required for Impact / BlastRadius",
40
+ },
41
+ status: {
42
+ type: "string",
43
+ description: "Optional claim status filter for ListClaims",
44
+ },
45
+ },
46
+ required: ["type"],
47
+ additionalProperties: false,
48
+ },
49
+ },
50
+ {
51
+ name: "brain_explain",
52
+ description: "READ: Evidence-backed explainClaim for a claim id. Returns claim, status, confidence, evidence, contradictions. Never invents reasons.",
53
+ inputSchema: {
54
+ type: "object",
55
+ properties: {
56
+ claimId: { type: "string", description: "Claim id" },
57
+ },
58
+ required: ["claimId"],
59
+ additionalProperties: false,
60
+ },
61
+ },
62
+ {
63
+ name: "brain_trace",
64
+ description: "READ: Deterministic capped traceBrain (max depth 25, max edges 5000). Modes: dependencies, dependents, entrypoint-downstream, blast-radius, domain-modules.",
65
+ inputSchema: {
66
+ type: "object",
67
+ properties: {
68
+ target: { type: "string" },
69
+ mode: { type: "string" },
70
+ },
71
+ required: ["target"],
72
+ additionalProperties: false,
73
+ },
74
+ },
75
+ {
76
+ name: "brain_claims",
77
+ description: "READ: List claims with lifecycle status. Default truth filter: ACTIVE + CONTRADICTED only (excludes INVALIDATED/SUPERSEDED unless requested).",
78
+ inputSchema: {
79
+ type: "object",
80
+ properties: {
81
+ status: {
82
+ type: "string",
83
+ description: "ACTIVE | INVALIDATED | SUPERSEDED | CONTRADICTED",
84
+ },
85
+ includeHistorical: {
86
+ type: "boolean",
87
+ description: "When true without status, include INVALIDATED and SUPERSEDED",
88
+ },
89
+ },
90
+ additionalProperties: false,
91
+ },
92
+ },
93
+ {
94
+ name: "brain_evidence",
95
+ description: "READ: Typed evidence records after Brain redaction. Never returns secret plaintext. Optional evidenceId filter.",
96
+ inputSchema: {
97
+ type: "object",
98
+ properties: {
99
+ evidenceId: { type: "string" },
100
+ },
101
+ additionalProperties: false,
102
+ },
103
+ },
104
+ {
105
+ name: "brain_ownership",
106
+ description: "READ: Ownership from explicit CODEOWNERS / MAINTAINERS / package metadata only. Missing → UNKNOWN. No git-blame.",
107
+ inputSchema: {
108
+ type: "object",
109
+ properties: {
110
+ path: { type: "string" },
111
+ },
112
+ additionalProperties: false,
113
+ },
114
+ },
115
+ {
116
+ name: "brain_risk",
117
+ description: "READ: Change-danger risk only (not SAST/vulnerabilities): critical entrypoint, centrality, coupling, ownership clarity, architecture conflict.",
118
+ inputSchema: {
119
+ type: "object",
120
+ properties: {
121
+ kind: { type: "string" },
122
+ },
123
+ additionalProperties: false,
124
+ },
125
+ },
126
+ {
127
+ name: "brain_delta",
128
+ description: "READ: Compare two snapshots without mutating. Returns claim/component/ownership/risk deltas between fromSnapshot and toSnapshot.",
129
+ inputSchema: {
130
+ type: "object",
131
+ properties: {
132
+ fromSnapshot: { type: "string" },
133
+ toSnapshot: { type: "string" },
134
+ },
135
+ required: ["fromSnapshot"],
136
+ additionalProperties: false,
137
+ },
138
+ },
139
+ {
140
+ name: "brain_snapshot",
141
+ description: "Snapshot ops. READ: current|history|compare|load. CONTROLLED WRITE: rebuild writes only under <root>/.agentdoctor/project-brain/ (never source/agent configs; refuses divergent overwrite).",
142
+ inputSchema: {
143
+ type: "object",
144
+ properties: {
145
+ action: { type: "string" },
146
+ snapshotId: { type: "string" },
147
+ leftId: { type: "string" },
148
+ rightId: { type: "string" },
149
+ },
150
+ additionalProperties: false,
151
+ },
152
+ },
153
+ ];
154
+ }
155
+ export async function invokeBrainMcpTool(session, name, args) {
156
+ try {
157
+ switch (name) {
158
+ case "brain_overview":
159
+ return { structured: await handleBrainOverview(session), isError: false };
160
+ case "brain_query":
161
+ return { structured: await handleBrainQuery(session, args), isError: false };
162
+ case "brain_explain":
163
+ return { structured: await handleBrainExplain(session, args), isError: false };
164
+ case "brain_trace":
165
+ return { structured: await handleBrainTrace(session, args), isError: false };
166
+ case "brain_claims":
167
+ return { structured: await handleBrainClaims(session, args), isError: false };
168
+ case "brain_evidence":
169
+ return { structured: await handleBrainEvidence(session, args), isError: false };
170
+ case "brain_ownership":
171
+ return { structured: await handleBrainOwnership(session, args), isError: false };
172
+ case "brain_risk":
173
+ return { structured: await handleBrainRisk(session, args), isError: false };
174
+ case "brain_delta":
175
+ return { structured: await handleBrainDelta(session, args), isError: false };
176
+ case "brain_snapshot":
177
+ return { structured: await handleBrainSnapshot(session, args), isError: false };
178
+ default:
179
+ return {
180
+ structured: {
181
+ ok: false,
182
+ error: { code: "invalid_argument", message: `unknown tool: ${name}` },
183
+ },
184
+ isError: true,
185
+ };
186
+ }
187
+ }
188
+ catch (error) {
189
+ return { structured: toToolErrorPayload(error), isError: true };
190
+ }
191
+ }