@mandujs/mcp 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/mcp",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Mandu MCP Server - Agent-native interface for Mandu framework operations",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -32,7 +32,7 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@mandujs/core": "^0.8.1",
35
+ "@mandujs/core": "^0.9.0",
36
36
  "@modelcontextprotocol/sdk": "^1.25.3"
37
37
  },
38
38
  "engines": {
package/src/server.ts CHANGED
@@ -17,6 +17,7 @@ import { guardTools, guardToolDefinitions } from "./tools/guard.js";
17
17
  import { slotTools, slotToolDefinitions } from "./tools/slot.js";
18
18
  import { hydrationTools, hydrationToolDefinitions } from "./tools/hydration.js";
19
19
  import { contractTools, contractToolDefinitions } from "./tools/contract.js";
20
+ import { brainTools, brainToolDefinitions } from "./tools/brain.js";
20
21
  import { resourceHandlers, resourceDefinitions } from "./resources/handlers.js";
21
22
  import { findProjectRoot } from "./utils/project.js";
22
23
 
@@ -53,6 +54,7 @@ export class ManduMcpServer {
53
54
  ...slotToolDefinitions,
54
55
  ...hydrationToolDefinitions,
55
56
  ...contractToolDefinitions,
57
+ ...brainToolDefinitions,
56
58
  ];
57
59
  }
58
60
 
@@ -66,6 +68,7 @@ export class ManduMcpServer {
66
68
  ...slotTools(this.projectRoot),
67
69
  ...hydrationTools(this.projectRoot),
68
70
  ...contractTools(this.projectRoot),
71
+ ...brainTools(this.projectRoot),
69
72
  };
70
73
  }
71
74
 
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Mandu MCP - Brain Tools
3
+ *
4
+ * MCP tools for Brain functionality:
5
+ * - mandu_doctor: Guard failure analysis + patch suggestions
6
+ * - mandu_watch_start: Start file watching
7
+ * - mandu_watch_status: Get watch status
8
+ */
9
+
10
+ import type { Tool } from "@modelcontextprotocol/sdk/types.js";
11
+ import {
12
+ loadManifest,
13
+ runGuardCheck,
14
+ analyzeViolations,
15
+ generateJsonReport,
16
+ initializeBrain,
17
+ getBrain,
18
+ startWatcher,
19
+ stopWatcher,
20
+ getWatcher,
21
+ generateJsonStatus,
22
+ } from "../../../core/src/index.js";
23
+ import { getProjectPaths } from "../utils/project.js";
24
+
25
+ export const brainToolDefinitions: Tool[] = [
26
+ {
27
+ name: "mandu_doctor",
28
+ description:
29
+ "Analyze Guard failures and suggest patches. Works with or without LLM - template-based analysis is always available.",
30
+ inputSchema: {
31
+ type: "object",
32
+ properties: {
33
+ useLLM: {
34
+ type: "boolean",
35
+ description:
36
+ "Whether to use LLM for enhanced analysis (default: true if available)",
37
+ },
38
+ },
39
+ required: [],
40
+ },
41
+ },
42
+ {
43
+ name: "mandu_watch_start",
44
+ description:
45
+ "Start file watching with architecture rule warnings. Watches for common mistakes and emits warnings (no blocking).",
46
+ inputSchema: {
47
+ type: "object",
48
+ properties: {
49
+ debounceMs: {
50
+ type: "number",
51
+ description: "Debounce delay in milliseconds (default: 300)",
52
+ },
53
+ },
54
+ required: [],
55
+ },
56
+ },
57
+ {
58
+ name: "mandu_watch_status",
59
+ description:
60
+ "Get the current watch status including recent warnings and active rules.",
61
+ inputSchema: {
62
+ type: "object",
63
+ properties: {},
64
+ required: [],
65
+ },
66
+ },
67
+ ];
68
+
69
+ export function brainTools(projectRoot: string) {
70
+ const paths = getProjectPaths(projectRoot);
71
+
72
+ return {
73
+ mandu_doctor: async (args: Record<string, unknown>) => {
74
+ const { useLLM = true } = args as { useLLM?: boolean };
75
+
76
+ try {
77
+ // Initialize Brain
78
+ await initializeBrain();
79
+ const brain = getBrain();
80
+ const llmAvailable = await brain.isLLMAvailable();
81
+
82
+ // Load manifest
83
+ const manifestResult = await loadManifest(paths.manifestPath);
84
+ if (!manifestResult.success || !manifestResult.data) {
85
+ return {
86
+ error: "Failed to load manifest",
87
+ details: manifestResult.errors,
88
+ };
89
+ }
90
+
91
+ // Run guard check
92
+ const checkResult = await runGuardCheck(
93
+ manifestResult.data,
94
+ projectRoot
95
+ );
96
+
97
+ if (checkResult.passed) {
98
+ return {
99
+ passed: true,
100
+ message: "All guard checks passed - no violations found",
101
+ llmAvailable,
102
+ };
103
+ }
104
+
105
+ // Analyze violations
106
+ const analysis = await analyzeViolations(checkResult.violations, {
107
+ useLLM: useLLM && brain.enabled && llmAvailable,
108
+ });
109
+
110
+ return {
111
+ passed: false,
112
+ summary: analysis.summary,
113
+ violationCount: analysis.violations.length,
114
+ violations: analysis.violations.map((v) => ({
115
+ ruleId: v.ruleId,
116
+ file: v.file,
117
+ message: v.message,
118
+ suggestion: v.suggestion,
119
+ line: v.line,
120
+ severity: v.severity || "error",
121
+ })),
122
+ patches: analysis.patches.map((p) => ({
123
+ file: p.file,
124
+ type: p.type,
125
+ description: p.description,
126
+ command: p.command,
127
+ confidence: p.confidence,
128
+ })),
129
+ nextCommand: analysis.nextCommand,
130
+ llmAssisted: analysis.llmAssisted,
131
+ tip: "Run the suggested patches or nextCommand to fix violations",
132
+ };
133
+ } catch (error) {
134
+ return {
135
+ error: "Doctor analysis failed",
136
+ details: error instanceof Error ? error.message : "Unknown error",
137
+ };
138
+ }
139
+ },
140
+
141
+ mandu_watch_start: async (args: Record<string, unknown>) => {
142
+ const { debounceMs } = args as { debounceMs?: number };
143
+
144
+ try {
145
+ // Check if already watching
146
+ const existingWatcher = getWatcher();
147
+ if (existingWatcher) {
148
+ const status = existingWatcher.getStatus();
149
+ if (status.active) {
150
+ return {
151
+ success: false,
152
+ message: "Watch is already running",
153
+ status: JSON.parse(generateJsonStatus(status)),
154
+ };
155
+ }
156
+ }
157
+
158
+ // Start watcher
159
+ const watcher = await startWatcher({
160
+ rootDir: projectRoot,
161
+ debounceMs,
162
+ });
163
+
164
+ const status = watcher.getStatus();
165
+
166
+ return {
167
+ success: true,
168
+ message: "Watch started successfully",
169
+ status: {
170
+ active: status.active,
171
+ rootDir: status.rootDir,
172
+ fileCount: status.fileCount,
173
+ startedAt: status.startedAt?.toISOString(),
174
+ },
175
+ rules: [
176
+ "GENERATED_DIRECT_EDIT - Generated 파일 직접 수정 감지",
177
+ "WRONG_SLOT_LOCATION - 잘못된 위치의 Slot 파일 감지",
178
+ "SLOT_NAMING - Slot 파일 네이밍 규칙",
179
+ "CONTRACT_NAMING - Contract 파일 네이밍 규칙",
180
+ "FORBIDDEN_IMPORT - Generated 파일의 금지된 import 감지",
181
+ ],
182
+ tip: "Watch emits warnings only - it never blocks operations",
183
+ };
184
+ } catch (error) {
185
+ return {
186
+ error: "Failed to start watch",
187
+ details: error instanceof Error ? error.message : "Unknown error",
188
+ };
189
+ }
190
+ },
191
+
192
+ mandu_watch_status: async () => {
193
+ try {
194
+ const watcher = getWatcher();
195
+
196
+ if (!watcher) {
197
+ return {
198
+ active: false,
199
+ message: "Watch is not running",
200
+ tip: "Use mandu_watch_start to begin watching",
201
+ };
202
+ }
203
+
204
+ const status = watcher.getStatus();
205
+
206
+ return {
207
+ active: status.active,
208
+ rootDir: status.rootDir,
209
+ fileCount: status.fileCount,
210
+ startedAt: status.startedAt?.toISOString() || null,
211
+ uptime: status.startedAt
212
+ ? Math.floor((Date.now() - status.startedAt.getTime()) / 1000)
213
+ : 0,
214
+ recentWarnings: status.recentWarnings.map((w) => ({
215
+ ruleId: w.ruleId,
216
+ file: w.file,
217
+ message: w.message,
218
+ event: w.event,
219
+ timestamp: w.timestamp.toISOString(),
220
+ })),
221
+ warningCount: status.recentWarnings.length,
222
+ };
223
+ } catch (error) {
224
+ return {
225
+ error: "Failed to get watch status",
226
+ details: error instanceof Error ? error.message : "Unknown error",
227
+ };
228
+ }
229
+ },
230
+ };
231
+ }
@@ -6,3 +6,4 @@ export { guardTools, guardToolDefinitions } from "./guard.js";
6
6
  export { slotTools, slotToolDefinitions } from "./slot.js";
7
7
  export { hydrationTools, hydrationToolDefinitions } from "./hydration.js";
8
8
  export { contractTools, contractToolDefinitions } from "./contract.js";
9
+ export { brainTools, brainToolDefinitions } from "./brain.js";