@mandujs/mcp 0.9.11 → 0.9.12

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.11",
3
+ "version": "0.9.12",
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.11",
35
+ "@mandujs/core": "^0.9.12",
36
36
  "@modelcontextprotocol/sdk": "^1.25.3"
37
37
  },
38
38
  "engines": {
@@ -2,6 +2,7 @@ import type { Resource } from "@modelcontextprotocol/sdk/types.js";
2
2
  import {
3
3
  loadManifest,
4
4
  getTransactionStatus,
5
+ getWatcher,
5
6
  type GeneratedMap,
6
7
  type SpecLock,
7
8
  } from "@mandujs/core";
@@ -39,6 +40,18 @@ export const resourceDefinitions: Resource[] = [
39
40
  description: "Slot file content for a specific route",
40
41
  mimeType: "text/typescript",
41
42
  },
43
+ {
44
+ uri: "mandu://watch/warnings",
45
+ name: "Watch Warnings",
46
+ description: "Recent file watcher warnings (architecture rule violations)",
47
+ mimeType: "application/json",
48
+ },
49
+ {
50
+ uri: "mandu://watch/status",
51
+ name: "Watch Status",
52
+ description: "File watcher status (active/inactive, uptime, rule count)",
53
+ mimeType: "application/json",
54
+ },
42
55
  ];
43
56
 
44
57
  type ResourceHandler = (params: Record<string, string>) => Promise<unknown>;
@@ -172,5 +185,44 @@ export function resourceHandlers(
172
185
  content,
173
186
  };
174
187
  },
188
+
189
+ "mandu://watch/warnings": async () => {
190
+ const watcher = getWatcher();
191
+ if (!watcher) {
192
+ return { active: false, warnings: [] };
193
+ }
194
+
195
+ const warnings = watcher.getRecentWarnings(50);
196
+ return {
197
+ active: true,
198
+ count: warnings.length,
199
+ warnings: warnings.map((w) => ({
200
+ ruleId: w.ruleId,
201
+ file: w.file,
202
+ message: w.message,
203
+ event: w.event,
204
+ timestamp: w.timestamp.toISOString(),
205
+ })),
206
+ };
207
+ },
208
+
209
+ "mandu://watch/status": async () => {
210
+ const watcher = getWatcher();
211
+ if (!watcher) {
212
+ return { active: false };
213
+ }
214
+
215
+ const status = watcher.getStatus();
216
+ return {
217
+ active: status.active,
218
+ rootDir: status.rootDir,
219
+ fileCount: status.fileCount,
220
+ warningCount: status.recentWarnings.length,
221
+ uptime: status.startedAt
222
+ ? Math.floor((Date.now() - status.startedAt.getTime()) / 1000)
223
+ : 0,
224
+ startedAt: status.startedAt?.toISOString() || null,
225
+ };
226
+ },
175
227
  };
176
228
  }
package/src/server.ts CHANGED
@@ -36,6 +36,7 @@ export class ManduMcpServer {
36
36
  capabilities: {
37
37
  tools: {},
38
38
  resources: {},
39
+ logging: {},
39
40
  },
40
41
  }
41
42
  );
@@ -68,7 +69,7 @@ export class ManduMcpServer {
68
69
  ...slotTools(this.projectRoot),
69
70
  ...hydrationTools(this.projectRoot),
70
71
  ...contractTools(this.projectRoot),
71
- ...brainTools(this.projectRoot),
72
+ ...brainTools(this.projectRoot, this.server),
72
73
  };
73
74
  }
74
75
 
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import type { Tool } from "@modelcontextprotocol/sdk/types.js";
14
+ import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
14
15
  import {
15
16
  loadManifest,
16
17
  runGuardCheck,
@@ -69,6 +70,16 @@ export const brainToolDefinitions: Tool[] = [
69
70
  required: [],
70
71
  },
71
72
  },
73
+ {
74
+ name: "mandu_watch_stop",
75
+ description:
76
+ "Stop file watching and clean up MCP notification subscriptions.",
77
+ inputSchema: {
78
+ type: "object",
79
+ properties: {},
80
+ required: [],
81
+ },
82
+ },
72
83
  // Architecture tools (v0.2)
73
84
  {
74
85
  name: "mandu_check_location",
@@ -126,7 +137,10 @@ export const brainToolDefinitions: Tool[] = [
126
137
  },
127
138
  ];
128
139
 
129
- export function brainTools(projectRoot: string) {
140
+ /** Module-level unsubscribe handle for MCP warning notifications */
141
+ let mcpWarningUnsubscribe: (() => void) | null = null;
142
+
143
+ export function brainTools(projectRoot: string, server?: Server) {
130
144
  const paths = getProjectPaths(projectRoot);
131
145
 
132
146
  return {
@@ -221,11 +235,45 @@ export function brainTools(projectRoot: string) {
221
235
  debounceMs,
222
236
  });
223
237
 
238
+ // Register MCP notification handler
239
+ let notifications = false;
240
+ if (server) {
241
+ // Clean up previous subscription
242
+ if (mcpWarningUnsubscribe) {
243
+ mcpWarningUnsubscribe();
244
+ mcpWarningUnsubscribe = null;
245
+ }
246
+
247
+ mcpWarningUnsubscribe = watcher.onWarning((warning) => {
248
+ // Push logging message (Claude Code receives in real-time)
249
+ server.sendLoggingMessage({
250
+ level: "warning",
251
+ logger: "mandu-watch",
252
+ data: {
253
+ type: "watch_warning",
254
+ ruleId: warning.ruleId,
255
+ file: warning.file,
256
+ message: warning.message,
257
+ event: warning.event,
258
+ timestamp: warning.timestamp.toISOString(),
259
+ },
260
+ }).catch(() => {});
261
+
262
+ // Resource update notification
263
+ server.sendResourceUpdated({
264
+ uri: "mandu://watch/warnings",
265
+ }).catch(() => {});
266
+ });
267
+
268
+ notifications = true;
269
+ }
270
+
224
271
  const status = watcher.getStatus();
225
272
 
226
273
  return {
227
274
  success: true,
228
275
  message: "Watch started successfully",
276
+ notifications: notifications ? "enabled" : "disabled",
229
277
  status: {
230
278
  active: status.active,
231
279
  rootDir: status.rootDir,
@@ -239,7 +287,8 @@ export function brainTools(projectRoot: string) {
239
287
  "CONTRACT_NAMING - Contract 파일 네이밍 규칙",
240
288
  "FORBIDDEN_IMPORT - Generated 파일의 금지된 import 감지",
241
289
  ],
242
- tip: "Watch emits warnings only - it never blocks operations",
290
+ logFile: ".mandu/watch.log",
291
+ tip: "Run `tail -f .mandu/watch.log` in another terminal for real-time warnings.",
243
292
  };
244
293
  } catch (error) {
245
294
  return {
@@ -288,6 +337,28 @@ export function brainTools(projectRoot: string) {
288
337
  }
289
338
  },
290
339
 
340
+ mandu_watch_stop: async () => {
341
+ try {
342
+ // Clean up MCP notification subscription
343
+ if (mcpWarningUnsubscribe) {
344
+ mcpWarningUnsubscribe();
345
+ mcpWarningUnsubscribe = null;
346
+ }
347
+
348
+ stopWatcher();
349
+
350
+ return {
351
+ success: true,
352
+ message: "Watch stopped and notifications cleaned up",
353
+ };
354
+ } catch (error) {
355
+ return {
356
+ error: "Failed to stop watch",
357
+ details: error instanceof Error ? error.message : "Unknown error",
358
+ };
359
+ }
360
+ },
361
+
291
362
  // Architecture tools (v0.2)
292
363
  mandu_check_location: async (args: Record<string, unknown>) => {
293
364
  const { path: filePath, content } = args as {