@mandujs/mcp 0.19.4 β†’ 0.19.6

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/README.md CHANGED
@@ -34,18 +34,34 @@ Add to your MCP configuration (`.mcp.json` or `.claude.json`):
34
34
  "mcpServers": {
35
35
  "mandu": {
36
36
  "command": "bunx",
37
- "args": ["@mandujs/mcp"],
37
+ "args": ["mandu-mcp"],
38
38
  "cwd": "/path/to/your/project"
39
39
  }
40
40
  }
41
41
  }
42
42
  ```
43
43
 
44
+ > **Note**: Use `mandu-mcp` (not `@mandujs/mcp`) to avoid conflicts with Python's `mcp` CLI on PATH (#174).
45
+
44
46
  ### Direct Execution
45
47
 
46
48
  ```bash
47
49
  cd /path/to/project
48
- bunx @mandujs/mcp
50
+ bunx mandu-mcp
51
+ ```
52
+
53
+ ### Tool Profiles
54
+
55
+ Filter available tools via the `MANDU_MCP_PROFILE` env var:
56
+
57
+ | Profile | Tools | Use Case |
58
+ |---------|-------|----------|
59
+ | `minimal` | ~15 | Read-only operations, safe for autonomous agents |
60
+ | `standard` | ~50 | Default β€” most common operations |
61
+ | `full` | 85+ | All tools including destructive operations |
62
+
63
+ ```bash
64
+ MANDU_MCP_PROFILE=minimal bunx mandu-mcp
49
65
  ```
50
66
 
51
67
  ### Global Mode
@@ -64,7 +80,7 @@ bunx @mandujs/mcp --root /path/to/project
64
80
 
65
81
  ---
66
82
 
67
- ## Tools (35+)
83
+ ## Tools (85+)
68
84
 
69
85
  ### Spec Management
70
86
 
@@ -167,7 +183,7 @@ bunx @mandujs/mcp --root /path/to/project
167
183
  | `mandu_list_changes` | View change history |
168
184
  | `mandu_prune_history` | Clean old snapshots |
169
185
 
170
- ### ATE (Automation Test Engine) πŸ†•
186
+ ### ATE (Automation Test Engine) β€” 9 tools
171
187
 
172
188
  | Tool | Description |
173
189
  |------|-------------|
@@ -177,19 +193,40 @@ bunx @mandujs/mcp --root /path/to/project
177
193
  | `mandu.ate.report` | Generate test summary report |
178
194
  | `mandu.ate.heal` | Auto-suggest fixes for failed tests |
179
195
  | `mandu.ate.impact` | Compute affected routes (subset testing) |
196
+ | `mandu.ate.auto_pipeline` | Run full pipeline (extract β†’ generate β†’ run β†’ report β†’ heal) |
197
+ | `mandu.ate.feedback` | Analyze failures with 7-category classification |
198
+ | `mandu.ate.apply_heal` | Apply heal diffs safely (with backup) |
199
+
200
+ ### Test Selection (Phase 5) β€” 3 tools πŸ†•
201
+
202
+ | Tool | Description |
203
+ |------|-------------|
204
+ | `mandu.test.smart` | Smart test selection from git diff with priority scoring |
205
+ | `mandu.test.coverage` | Detect coverage gaps in interaction graph |
206
+ | `mandu.test.precommit` | Pre-commit hook: should we test before committing? |
207
+
208
+ ### Composite β€” 7 tools
209
+
210
+ | Tool | Description |
211
+ |------|-------------|
212
+ | `mandu.feature.create` | Scaffold full feature (route + contract + slot + island) |
213
+ | `mandu.diagnose` | Multi-aspect project health check |
214
+ | `mandu.island.add` | Add interactive island to route |
215
+ | `mandu.middleware.add` | Add middleware to route |
216
+ | `mandu.test.route` | Quick smoke test for a route |
217
+ | `mandu.deploy.check` | Pre-deploy production readiness check |
218
+ | `mandu.cache.manage` | Manage ISR/SWR cache (list, invalidate, stats) |
180
219
 
181
220
  ---
182
221
 
183
- ## Resources
222
+ ## Resources (4)
184
223
 
185
224
  | URI | Description |
186
225
  |-----|-------------|
187
- | `mandu://spec/manifest` | Current routes.manifest.json |
188
- | `mandu://generated/map` | Generated files mapping |
189
- | `mandu://transaction/active` | Active transaction state |
190
- | `mandu://slots/{routeId}` | Slot file content by route ID |
191
- | `mandu://watch/warnings` | Recent architecture violation warnings |
192
- | `mandu://watch/status` | Watcher status (active, uptime, count) |
226
+ | `mandu://routes` | Current routes manifest |
227
+ | `mandu://config` | Parsed `mandu.config.ts` settings |
228
+ | `mandu://errors` | Recent build and runtime errors |
229
+ | `mandu://activity` | **NEW**: Recent observability events + 5-minute stats from EventBus |
193
230
 
194
231
  ---
195
232
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/mcp",
3
- "version": "0.19.4",
3
+ "version": "0.19.6",
4
4
  "description": "Mandu MCP Server - Agent-native interface for Mandu framework operations",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -34,11 +34,11 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@mandujs/core": "^0.20.0",
38
- "@mandujs/ate": "^0.18.0",
37
+ "@mandujs/core": "^0.22.0",
38
+ "@mandujs/ate": "^0.18.2",
39
39
  "@modelcontextprotocol/sdk": "^1.25.3"
40
40
  },
41
41
  "engines": {
42
- "bun": ">=1.0.0"
42
+ "bun": ">=1.3.12"
43
43
  }
44
44
  }
@@ -277,6 +277,8 @@ export class ActivityMonitor {
277
277
  private summaryTimer: NodeJS.Timeout | null = null;
278
278
  private summaryCounts = { total: 0, info: 0, warn: 0, error: 0 };
279
279
  private lastToolArgs = new Map<string, Record<string, unknown> | null>();
280
+ // Phase 1-3: 도ꡬ 호좜 μ‹œμž‘ μ‹œκ°„ 좔적 (duration κ³„μ‚°μš©)
281
+ private toolStartTimes = new Map<string, number>();
280
282
  // Phase 5-1: μ—μ΄μ „νŠΈ μ„Έμ…˜ 식별 (MCP ν΄λΌμ΄μ–ΈνŠΈλ³„ 좔적)
281
283
  public sessionId: string = crypto.randomUUID();
282
284
 
@@ -382,17 +384,23 @@ export class ActivityMonitor {
382
384
  fingerprint: `tool:error:${name}:${argsStr}`,
383
385
  data: { tool: name, tag, args, argsSummary: argsStr, error },
384
386
  });
385
- // Phase 1-3: MCP 도ꡬ μ—λŸ¬ β†’ EventBus (Phase 5-1: sessionId 포함)
387
+ // Phase 1-3: MCP 도ꡬ μ—λŸ¬ β†’ EventBus (sessionId + duration 포함)
388
+ const startTime = this.toolStartTimes.get(name);
389
+ this.toolStartTimes.delete(name);
386
390
  eventBus.emit({
387
391
  type: "mcp",
388
392
  severity: "error",
389
393
  source: "mcp",
390
394
  message: `${name} ❌ ${error}`,
395
+ duration: startTime ? Date.now() - startTime : undefined,
391
396
  data: { tool: name, args, error, sessionId: this.sessionId },
392
397
  });
393
398
  return;
394
399
  }
395
400
 
401
+ // Phase 1-3: 도ꡬ 호좜 μ‹œμž‘ μ‹œκ°„ 기둝 (logResultμ—μ„œ duration 계산)
402
+ this.toolStartTimes.set(name, Date.now());
403
+
396
404
  this.enqueue({
397
405
  ts: new Date().toISOString(),
398
406
  type: "tool.call",
@@ -400,14 +408,6 @@ export class ActivityMonitor {
400
408
  source: "tool",
401
409
  data: { tool: name, tag, args, argsSummary: argsStr },
402
410
  });
403
- // Phase 1-3: MCP 도ꡬ 호좜 β†’ EventBus (Phase 5-1: sessionId 포함)
404
- eventBus.emit({
405
- type: "mcp",
406
- severity: "info",
407
- source: "mcp",
408
- message: `${name} βœ…`,
409
- data: { tool: name, args, sessionId: this.sessionId },
410
- });
411
411
  }
412
412
 
413
413
  /**
@@ -421,6 +421,18 @@ export class ActivityMonitor {
421
421
  const summary = summarizeResult(result);
422
422
  const tag = TOOL_ICONS[name] || name.replace("mandu_", "").toUpperCase();
423
423
 
424
+ // Phase 1-3: 도ꡬ μ™„λ£Œ μ‹œ duration 계산 및 EventBus emit (Phase 5-1: sessionId 포함)
425
+ const startTime = this.toolStartTimes.get(name);
426
+ this.toolStartTimes.delete(name);
427
+ eventBus.emit({
428
+ type: "mcp",
429
+ severity: "info",
430
+ source: "mcp",
431
+ message: `${name} βœ…`,
432
+ duration: startTime ? Date.now() - startTime : undefined,
433
+ data: { tool: name, args: lastArgs, sessionId: this.sessionId },
434
+ });
435
+
424
436
  if (summary) {
425
437
  this.enqueue({
426
438
  ts: new Date().toISOString(),
package/src/index.ts CHANGED
File without changes
@@ -1,145 +1,157 @@
1
- /**
2
- * MCP Resources for Mandu Framework
3
- *
4
- * Project state data exposed via the MCP resource protocol.
5
- */
6
-
7
- import type { Resource } from "@modelcontextprotocol/sdk/types.js";
8
- import path from "path";
9
- import { readConfig, readJsonFile } from "./utils/project.js";
10
- import { loadManduConfig, loadManifest } from "@mandujs/core";
11
- import { eventBus } from "@mandujs/core/observability";
12
- import { getDevServerState } from "./tools/project.js";
13
-
14
- export const manduResourceDefinitions: Resource[] = [
15
- {
16
- uri: "mandu://routes",
17
- name: "Route Manifest",
18
- description: "Current project route manifest (JSON)",
19
- mimeType: "application/json",
20
- },
21
- {
22
- uri: "mandu://config",
23
- name: "Mandu Config",
24
- description: "Parsed mandu.config.ts settings",
25
- mimeType: "application/json",
26
- },
27
- {
28
- uri: "mandu://errors",
29
- name: "Recent Errors",
30
- description: "Recent build and runtime errors",
31
- mimeType: "application/json",
32
- },
33
- {
34
- uri: "mandu://activity",
35
- name: "Recent Activity",
36
- description: "Recent observability events (HTTP, MCP, Guard) from EventBus + 5-minute stats",
37
- mimeType: "application/json",
38
- },
39
- ];
40
-
41
- type ResourceReadResult = { uri: string; mimeType: string; text: string };
42
- type ResourceHandler = () => Promise<ResourceReadResult>;
43
-
44
- function jsonResult(uri: string, data: unknown): ResourceReadResult {
45
- return { uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) };
46
- }
47
-
48
- export function manduResourceHandlers(projectRoot: string): Record<string, ResourceHandler> {
49
- const manifestPath = path.join(projectRoot, ".mandu", "routes.manifest.json");
50
-
51
- return {
52
- "mandu://routes": async () => {
53
- const result = await loadManifest(manifestPath);
54
- if (!result.success || !result.data) {
55
- return jsonResult("mandu://routes", {
56
- error: "Failed to load route manifest",
57
- details: result.errors,
58
- hint: "Run 'mandu generate' or 'mandu dev' to create the manifest.",
59
- });
60
- }
61
- return jsonResult("mandu://routes", {
62
- version: result.data.version,
63
- routeCount: result.data.routes.length,
64
- routes: result.data.routes.map((r) => ({
65
- id: r.id, pattern: r.pattern, kind: r.kind, module: r.module,
66
- slotModule: r.slotModule ?? null, clientModule: r.clientModule ?? null,
67
- })),
68
- });
69
- },
70
-
71
- "mandu://config": async () => {
72
- try {
73
- const config = await loadManduConfig(projectRoot);
74
- return jsonResult("mandu://config", {
75
- server: config.server ?? {},
76
- guard: config.guard ?? {},
77
- build: config.build ?? {},
78
- dev: config.dev ?? {},
79
- fsRoutes: config.fsRoutes ?? {},
80
- seo: config.seo ?? {},
81
- });
82
- } catch {
83
- const raw = await readConfig(projectRoot);
84
- return jsonResult("mandu://config", raw ?? {
85
- error: "No mandu.config.ts/js/json found",
86
- hint: "Create a mandu.config.ts in the project root.",
87
- });
88
- }
89
- },
90
-
91
- "mandu://activity": async () => {
92
- // Phase 5-3: AI μ—μ΄μ „νŠΈκ°€ EventBus ν™œλ™μ„ 직접 쑰회 κ°€λŠ₯
93
- const recent = eventBus.getRecent(20);
94
- const stats = eventBus.getStats(5 * 60 * 1000); // last 5 minutes
95
- return jsonResult("mandu://activity", {
96
- recent: recent.map((e) => ({
97
- ts: new Date(e.timestamp).toISOString(),
98
- type: e.type,
99
- severity: e.severity,
100
- source: e.source,
101
- message: e.message,
102
- duration: e.duration,
103
- correlationId: e.correlationId,
104
- })),
105
- stats,
106
- windowMs: 5 * 60 * 1000,
107
- });
108
- },
109
-
110
- "mandu://errors": async () => {
111
- const errors: unknown[] = [];
112
-
113
- // Try Kitchen DevTools error log from running dev server
114
- let port: number | undefined;
115
- const serverState = getDevServerState();
116
- if (serverState) {
117
- for (const line of serverState.output) {
118
- const m = line.match(/https?:\/\/localhost:(\d+)/);
119
- if (m) port = parseInt(m[1], 10);
120
- }
121
- }
122
- if (port) {
123
- try {
124
- const res = await fetch(`http://localhost:${port}/__kitchen/api/errors`);
125
- if (res.ok) {
126
- const body = (await res.json()) as { errors: unknown[] };
127
- if (body.errors?.length) errors.push(...body.errors);
128
- }
129
- } catch { /* dev server not reachable */ }
130
- }
131
-
132
- // Read local error log file
133
- const loggedErrors = await readJsonFile<unknown[]>(
134
- path.join(projectRoot, ".mandu", "errors.json"),
135
- );
136
- if (Array.isArray(loggedErrors)) errors.push(...loggedErrors);
137
-
138
- return jsonResult("mandu://errors", {
139
- count: errors.length,
140
- errors,
141
- source: port ? "kitchen+log" : "log",
142
- });
143
- },
144
- };
145
- }
1
+ /**
2
+ * MCP Resources for Mandu Framework
3
+ *
4
+ * Project state data exposed via the MCP resource protocol.
5
+ */
6
+
7
+ import type { Resource } from "@modelcontextprotocol/sdk/types.js";
8
+ import path from "path";
9
+ import { readConfig, readJsonFile } from "./utils/project.js";
10
+ import { loadManduConfig, loadManifest } from "@mandujs/core";
11
+ import { eventBus } from "@mandujs/core/observability";
12
+ import { computeAgentStats } from "@mandujs/core/kitchen";
13
+ import { getDevServerState } from "./tools/project.js";
14
+
15
+ export const manduResourceDefinitions: Resource[] = [
16
+ {
17
+ uri: "mandu://routes",
18
+ name: "Route Manifest",
19
+ description: "Current project route manifest (JSON)",
20
+ mimeType: "application/json",
21
+ },
22
+ {
23
+ uri: "mandu://config",
24
+ name: "Mandu Config",
25
+ description: "Parsed mandu.config.ts settings",
26
+ mimeType: "application/json",
27
+ },
28
+ {
29
+ uri: "mandu://errors",
30
+ name: "Recent Errors",
31
+ description: "Recent build and runtime errors",
32
+ mimeType: "application/json",
33
+ },
34
+ {
35
+ uri: "mandu://activity",
36
+ name: "Recent Activity",
37
+ description: "Recent observability events (HTTP, MCP, Guard) from EventBus + 5-minute stats",
38
+ mimeType: "application/json",
39
+ },
40
+ {
41
+ uri: "mandu://agent-stats",
42
+ name: "Per-Agent Stats",
43
+ description: "MCP tool usage aggregated by sessionId (per-agent observability)",
44
+ mimeType: "application/json",
45
+ },
46
+ ];
47
+
48
+ type ResourceReadResult = { uri: string; mimeType: string; text: string };
49
+ type ResourceHandler = () => Promise<ResourceReadResult>;
50
+
51
+ function jsonResult(uri: string, data: unknown): ResourceReadResult {
52
+ return { uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) };
53
+ }
54
+
55
+ export function manduResourceHandlers(projectRoot: string): Record<string, ResourceHandler> {
56
+ const manifestPath = path.join(projectRoot, ".mandu", "routes.manifest.json");
57
+
58
+ return {
59
+ "mandu://routes": async () => {
60
+ const result = await loadManifest(manifestPath);
61
+ if (!result.success || !result.data) {
62
+ return jsonResult("mandu://routes", {
63
+ error: "Failed to load route manifest",
64
+ details: result.errors,
65
+ hint: "Run 'mandu generate' or 'mandu dev' to create the manifest.",
66
+ });
67
+ }
68
+ return jsonResult("mandu://routes", {
69
+ version: result.data.version,
70
+ routeCount: result.data.routes.length,
71
+ routes: result.data.routes.map((r) => ({
72
+ id: r.id, pattern: r.pattern, kind: r.kind, module: r.module,
73
+ slotModule: r.slotModule ?? null, clientModule: r.clientModule ?? null,
74
+ })),
75
+ });
76
+ },
77
+
78
+ "mandu://config": async () => {
79
+ try {
80
+ const config = await loadManduConfig(projectRoot);
81
+ return jsonResult("mandu://config", {
82
+ server: config.server ?? {},
83
+ guard: config.guard ?? {},
84
+ build: config.build ?? {},
85
+ dev: config.dev ?? {},
86
+ fsRoutes: config.fsRoutes ?? {},
87
+ seo: config.seo ?? {},
88
+ });
89
+ } catch {
90
+ const raw = await readConfig(projectRoot);
91
+ return jsonResult("mandu://config", raw ?? {
92
+ error: "No mandu.config.ts/js/json found",
93
+ hint: "Create a mandu.config.ts in the project root.",
94
+ });
95
+ }
96
+ },
97
+
98
+ "mandu://activity": async () => {
99
+ // Phase 5-3: AI μ—μ΄μ „νŠΈκ°€ EventBus ν™œλ™μ„ 직접 쑰회 κ°€λŠ₯
100
+ const recent = eventBus.getRecent(20);
101
+ const stats = eventBus.getStats(5 * 60 * 1000); // last 5 minutes
102
+ return jsonResult("mandu://activity", {
103
+ recent: recent.map((e) => ({
104
+ ts: new Date(e.timestamp).toISOString(),
105
+ type: e.type,
106
+ severity: e.severity,
107
+ source: e.source,
108
+ message: e.message,
109
+ duration: e.duration,
110
+ correlationId: e.correlationId,
111
+ })),
112
+ stats,
113
+ windowMs: 5 * 60 * 1000,
114
+ });
115
+ },
116
+
117
+ "mandu://agent-stats": async () => {
118
+ // Phase 5-2: per-agent (sessionId) MCP tool usage aggregation
119
+ return jsonResult("mandu://agent-stats", computeAgentStats());
120
+ },
121
+
122
+ "mandu://errors": async () => {
123
+ const errors: unknown[] = [];
124
+
125
+ // Try Kitchen DevTools error log from running dev server
126
+ let port: number | undefined;
127
+ const serverState = getDevServerState();
128
+ if (serverState) {
129
+ for (const line of serverState.output) {
130
+ const m = line.match(/https?:\/\/localhost:(\d+)/);
131
+ if (m) port = parseInt(m[1], 10);
132
+ }
133
+ }
134
+ if (port) {
135
+ try {
136
+ const res = await fetch(`http://localhost:${port}/__kitchen/api/errors`);
137
+ if (res.ok) {
138
+ const body = (await res.json()) as { errors: unknown[] };
139
+ if (body.errors?.length) errors.push(...body.errors);
140
+ }
141
+ } catch { /* dev server not reachable */ }
142
+ }
143
+
144
+ // Read local error log file
145
+ const loggedErrors = await readJsonFile<unknown[]>(
146
+ path.join(projectRoot, ".mandu", "errors.json"),
147
+ );
148
+ if (Array.isArray(loggedErrors)) errors.push(...loggedErrors);
149
+
150
+ return jsonResult("mandu://errors", {
151
+ count: errors.length,
152
+ errors,
153
+ source: port ? "kitchen+log" : "log",
154
+ });
155
+ },
156
+ };
157
+ }