@mandujs/mcp 0.9.16 → 0.9.17

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.9.16",
3
+ "version": "0.9.17",
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.9.16",
35
+ "@mandujs/core": "^0.9.17",
36
36
  "@modelcontextprotocol/sdk": "^1.25.3"
37
37
  },
38
38
  "engines": {
package/src/server.ts CHANGED
@@ -20,6 +20,7 @@ import { contractTools, contractToolDefinitions } from "./tools/contract.js";
20
20
  import { brainTools, brainToolDefinitions } from "./tools/brain.js";
21
21
  import { resourceHandlers, resourceDefinitions } from "./resources/handlers.js";
22
22
  import { findProjectRoot } from "./utils/project.js";
23
+ import { applyWarningInjection } from "./utils/withWarnings.js";
23
24
  import { ActivityMonitor } from "./activity-monitor.js";
24
25
  import { startWatcher } from "../../core/src/index.js";
25
26
 
@@ -64,7 +65,7 @@ export class ManduMcpServer {
64
65
  }
65
66
 
66
67
  private getAllToolHandlers(): Record<string, (args: Record<string, unknown>) => Promise<unknown>> {
67
- return {
68
+ const handlers = {
68
69
  ...specTools(this.projectRoot),
69
70
  ...generateTools(this.projectRoot),
70
71
  ...transactionTools(this.projectRoot),
@@ -75,6 +76,8 @@ export class ManduMcpServer {
75
76
  ...contractTools(this.projectRoot),
76
77
  ...brainTools(this.projectRoot, this.server, this.monitor),
77
78
  };
79
+
80
+ return applyWarningInjection(handlers);
78
81
  }
79
82
 
80
83
  private registerToolHandlers(): void {
@@ -318,8 +318,8 @@ export function hydrationTools(projectRoot: string) {
318
318
  };
319
319
  }
320
320
 
321
- // Create client slot file
322
- const clientModulePath = `spec/slots/${routeId}.client.ts`;
321
+ // Create client slot file in apps/web/components/ (not spec/slots/)
322
+ const clientModulePath = `apps/web/components/${routeId}.client.tsx`;
323
323
  const clientFilePath = path.join(projectRoot, clientModulePath);
324
324
 
325
325
  // Check if file already exists
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Watcher Warning Injection for Mutation Tools
3
+ *
4
+ * Mutation 도구(write_slot, add_route, generate 등) 실행 후
5
+ * watcher 경고를 자동으로 응답에 포함시킨다.
6
+ *
7
+ * MCP notification이 Claude Code에 전달되지 않는 문제를 해결.
8
+ */
9
+
10
+ import { getWatcher } from "../../../core/src/index.js";
11
+
12
+ const MUTATION_TOOLS = new Set([
13
+ "mandu_write_slot",
14
+ "mandu_add_route",
15
+ "mandu_update_route",
16
+ "mandu_delete_route",
17
+ "mandu_generate",
18
+ "mandu_build",
19
+ "mandu_commit",
20
+ "mandu_add_client_slot",
21
+ "mandu_set_hydration",
22
+ "mandu_create_contract",
23
+ "mandu_update_route_contract",
24
+ "mandu_sync_contract_slot",
25
+ ]);
26
+
27
+ /** watcher debounce(300ms) + 여유분 */
28
+ const WARNING_WAIT_MS = 400;
29
+
30
+ type ToolHandler = (args: Record<string, unknown>) => Promise<unknown>;
31
+
32
+ /**
33
+ * Mutation 도구 핸들러를 감싸서, 실행 후 발생한 watcher 경고를
34
+ * 응답 객체의 `_warnings` 필드에 자동 포함시킨다.
35
+ */
36
+ export function applyWarningInjection(
37
+ handlers: Record<string, ToolHandler>
38
+ ): Record<string, ToolHandler> {
39
+ const wrapped: Record<string, ToolHandler> = {};
40
+
41
+ for (const [name, handler] of Object.entries(handlers)) {
42
+ if (MUTATION_TOOLS.has(name)) {
43
+ wrapped[name] = wrapWithWarnings(handler);
44
+ } else {
45
+ wrapped[name] = handler;
46
+ }
47
+ }
48
+
49
+ return wrapped;
50
+ }
51
+
52
+ function wrapWithWarnings(handler: ToolHandler): ToolHandler {
53
+ return async (args: Record<string, unknown>) => {
54
+ const watcher = getWatcher();
55
+ const beforeCount = watcher?.getRecentWarnings(100).length ?? 0;
56
+
57
+ const result = await handler(args);
58
+
59
+ // watcher debounce 대기
60
+ await new Promise((resolve) => setTimeout(resolve, WARNING_WAIT_MS));
61
+
62
+ const allWarnings = watcher?.getRecentWarnings(100) ?? [];
63
+ const newWarnings = allWarnings.slice(beforeCount);
64
+
65
+ if (
66
+ newWarnings.length > 0 &&
67
+ typeof result === "object" &&
68
+ result !== null
69
+ ) {
70
+ return {
71
+ ...(result as Record<string, unknown>),
72
+ _warnings: newWarnings.map((w) => ({
73
+ ruleId: w.ruleId,
74
+ file: w.file,
75
+ message: w.message,
76
+ level: w.level ?? "warn",
77
+ })),
78
+ };
79
+ }
80
+
81
+ return result;
82
+ };
83
+ }