@mandujs/mcp 0.8.1 → 0.9.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.
- package/package.json +2 -2
- package/src/server.ts +3 -0
- package/src/tools/brain.ts +231 -0
- package/src/tools/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mandujs/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
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.
|
|
35
|
+
"@mandujs/core": "^0.9.1",
|
|
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
|
+
}
|
package/src/tools/index.ts
CHANGED
|
@@ -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";
|