@mandujs/mcp 0.18.8 → 0.18.10
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 +0 -1
- package/package.json +42 -42
- package/src/activity-monitor.ts +9 -8
- package/src/adapters/tool-adapter.ts +2 -0
- package/src/executor/error-handler.ts +268 -250
- package/src/index.ts +8 -0
- package/src/new-resources.ts +119 -0
- package/src/profiles.ts +34 -0
- package/src/prompts.ts +104 -0
- package/src/resources/handlers.ts +0 -23
- package/src/server.ts +78 -5
- package/src/tools/ate.ts +28 -0
- package/src/tools/brain.ts +56 -24
- package/src/tools/component.ts +194 -185
- package/src/tools/composite.ts +440 -0
- package/src/tools/contract.ts +58 -58
- package/src/tools/decisions.ts +270 -0
- package/src/tools/generate.ts +23 -21
- package/src/tools/guard.ts +32 -708
- package/src/tools/history.ts +24 -7
- package/src/tools/hydration.ts +40 -13
- package/src/tools/index.ts +28 -2
- package/src/tools/kitchen.ts +107 -0
- package/src/tools/negotiate.ts +263 -0
- package/src/tools/project.ts +464 -382
- package/src/tools/resource.ts +19 -2
- package/src/tools/runtime.ts +533 -508
- package/src/tools/seo.ts +446 -417
- package/src/tools/slot-validation.ts +200 -0
- package/src/tools/slot.ts +20 -21
- package/src/tools/spec.ts +45 -43
- package/src/tools/transaction.ts +55 -13
- package/src/tx-lock.ts +73 -0
- package/src/utils/project.ts +48 -9
- package/src/utils/runtime-control.ts +52 -0
- package/src/utils/withWarnings.ts +2 -1
package/src/index.ts
CHANGED
|
@@ -83,6 +83,14 @@ export {
|
|
|
83
83
|
getToolsSummary,
|
|
84
84
|
} from "./tools/index.js";
|
|
85
85
|
|
|
86
|
+
// Profile exports
|
|
87
|
+
export {
|
|
88
|
+
type McpProfile,
|
|
89
|
+
PROFILE_CATEGORIES,
|
|
90
|
+
getProfileCategories,
|
|
91
|
+
isValidProfile,
|
|
92
|
+
} from "./profiles.js";
|
|
93
|
+
|
|
86
94
|
// CLI entry point
|
|
87
95
|
import { startServer } from "./server.js";
|
|
88
96
|
import path from "path";
|
|
@@ -0,0 +1,119 @@
|
|
|
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 { getDevServerState } from "./tools/project.js";
|
|
12
|
+
|
|
13
|
+
export const manduResourceDefinitions: Resource[] = [
|
|
14
|
+
{
|
|
15
|
+
uri: "mandu://routes",
|
|
16
|
+
name: "Route Manifest",
|
|
17
|
+
description: "Current project route manifest (JSON)",
|
|
18
|
+
mimeType: "application/json",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
uri: "mandu://config",
|
|
22
|
+
name: "Mandu Config",
|
|
23
|
+
description: "Parsed mandu.config.ts settings",
|
|
24
|
+
mimeType: "application/json",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
uri: "mandu://errors",
|
|
28
|
+
name: "Recent Errors",
|
|
29
|
+
description: "Recent build and runtime errors",
|
|
30
|
+
mimeType: "application/json",
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
type ResourceReadResult = { uri: string; mimeType: string; text: string };
|
|
35
|
+
type ResourceHandler = () => Promise<ResourceReadResult>;
|
|
36
|
+
|
|
37
|
+
function jsonResult(uri: string, data: unknown): ResourceReadResult {
|
|
38
|
+
return { uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function manduResourceHandlers(projectRoot: string): Record<string, ResourceHandler> {
|
|
42
|
+
const manifestPath = path.join(projectRoot, ".mandu", "routes.manifest.json");
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
"mandu://routes": async () => {
|
|
46
|
+
const result = await loadManifest(manifestPath);
|
|
47
|
+
if (!result.success || !result.data) {
|
|
48
|
+
return jsonResult("mandu://routes", {
|
|
49
|
+
error: "Failed to load route manifest",
|
|
50
|
+
details: result.errors,
|
|
51
|
+
hint: "Run 'mandu generate' or 'mandu dev' to create the manifest.",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return jsonResult("mandu://routes", {
|
|
55
|
+
version: result.data.version,
|
|
56
|
+
routeCount: result.data.routes.length,
|
|
57
|
+
routes: result.data.routes.map((r) => ({
|
|
58
|
+
id: r.id, pattern: r.pattern, kind: r.kind, module: r.module,
|
|
59
|
+
slotModule: r.slotModule ?? null, clientModule: r.clientModule ?? null,
|
|
60
|
+
})),
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
"mandu://config": async () => {
|
|
65
|
+
try {
|
|
66
|
+
const config = await loadManduConfig(projectRoot);
|
|
67
|
+
return jsonResult("mandu://config", {
|
|
68
|
+
server: config.server ?? {},
|
|
69
|
+
guard: config.guard ?? {},
|
|
70
|
+
build: config.build ?? {},
|
|
71
|
+
dev: config.dev ?? {},
|
|
72
|
+
fsRoutes: config.fsRoutes ?? {},
|
|
73
|
+
seo: config.seo ?? {},
|
|
74
|
+
});
|
|
75
|
+
} catch {
|
|
76
|
+
const raw = await readConfig(projectRoot);
|
|
77
|
+
return jsonResult("mandu://config", raw ?? {
|
|
78
|
+
error: "No mandu.config.ts/js/json found",
|
|
79
|
+
hint: "Create a mandu.config.ts in the project root.",
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
"mandu://errors": async () => {
|
|
85
|
+
const errors: unknown[] = [];
|
|
86
|
+
|
|
87
|
+
// Try Kitchen DevTools error log from running dev server
|
|
88
|
+
let port: number | undefined;
|
|
89
|
+
const serverState = getDevServerState();
|
|
90
|
+
if (serverState) {
|
|
91
|
+
for (const line of serverState.output) {
|
|
92
|
+
const m = line.match(/https?:\/\/localhost:(\d+)/);
|
|
93
|
+
if (m) port = parseInt(m[1], 10);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (port) {
|
|
97
|
+
try {
|
|
98
|
+
const res = await fetch(`http://localhost:${port}/__kitchen/api/errors`);
|
|
99
|
+
if (res.ok) {
|
|
100
|
+
const body = (await res.json()) as { errors: unknown[] };
|
|
101
|
+
if (body.errors?.length) errors.push(...body.errors);
|
|
102
|
+
}
|
|
103
|
+
} catch { /* dev server not reachable */ }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Read local error log file
|
|
107
|
+
const loggedErrors = await readJsonFile<unknown[]>(
|
|
108
|
+
path.join(projectRoot, ".mandu", "errors.json"),
|
|
109
|
+
);
|
|
110
|
+
if (Array.isArray(loggedErrors)) errors.push(...loggedErrors);
|
|
111
|
+
|
|
112
|
+
return jsonResult("mandu://errors", {
|
|
113
|
+
count: errors.length,
|
|
114
|
+
errors,
|
|
115
|
+
source: port ? "kitchen+log" : "log",
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
package/src/profiles.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Profiles
|
|
3
|
+
*
|
|
4
|
+
* Controls how many tool categories are exposed to AI agents.
|
|
5
|
+
* - minimal: Core scaffolding tools only (~15 tools)
|
|
6
|
+
* - standard: Common development workflow (~40 tools)
|
|
7
|
+
* - full: All categories, no filtering (default)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type McpProfile = "minimal" | "standard" | "full";
|
|
11
|
+
|
|
12
|
+
export const PROFILE_CATEGORIES: Record<McpProfile, string[] | null> = {
|
|
13
|
+
minimal: ["spec", "project", "guard", "generate"],
|
|
14
|
+
standard: [
|
|
15
|
+
"spec", "project", "guard", "generate",
|
|
16
|
+
"contract", "slot", "hydration", "seo",
|
|
17
|
+
"component", "kitchen", "composite",
|
|
18
|
+
],
|
|
19
|
+
full: null,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Returns allowed category names for a profile, or null if all categories are allowed.
|
|
24
|
+
*/
|
|
25
|
+
export function getProfileCategories(profile: McpProfile): string[] | null {
|
|
26
|
+
return PROFILE_CATEGORIES[profile] ?? null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Type guard for valid profile strings.
|
|
31
|
+
*/
|
|
32
|
+
export function isValidProfile(value: string): value is McpProfile {
|
|
33
|
+
return value === "minimal" || value === "standard" || value === "full";
|
|
34
|
+
}
|
package/src/prompts.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Prompts for Mandu Framework
|
|
3
|
+
*
|
|
4
|
+
* Conversation templates that guide agents through common Mandu workflows.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Prompt, GetPromptResult } from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
+
|
|
9
|
+
export const manduPrompts: Prompt[] = [
|
|
10
|
+
{
|
|
11
|
+
name: "new-feature",
|
|
12
|
+
description: "Guide creating a new feature with routes, contracts, and islands",
|
|
13
|
+
arguments: [
|
|
14
|
+
{ name: "description", description: "Feature description", required: true },
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "debug",
|
|
19
|
+
description: "Diagnose and fix errors in the Mandu project",
|
|
20
|
+
arguments: [
|
|
21
|
+
{ name: "symptom", description: "Error message or symptom", required: false },
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "add-crud",
|
|
26
|
+
description: "Create a complete CRUD API with contracts and tests",
|
|
27
|
+
arguments: [
|
|
28
|
+
{ name: "resource", description: "Resource name (e.g., 'products')", required: true },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
function msg(text: string): GetPromptResult {
|
|
34
|
+
return { messages: [{ role: "user", content: { type: "text", text } }] };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function capitalize(s: string): string {
|
|
38
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type PromptHandler = (args: Record<string, string>) => GetPromptResult;
|
|
42
|
+
|
|
43
|
+
const promptHandlers: Record<string, PromptHandler> = {
|
|
44
|
+
"new-feature": (args) => msg(`Create a new feature: ${args.description}
|
|
45
|
+
|
|
46
|
+
Follow these steps using Mandu MCP tools:
|
|
47
|
+
|
|
48
|
+
1. Read current route manifest: Resource mandu://routes
|
|
49
|
+
2. Read project config: Resource mandu://config
|
|
50
|
+
3. Negotiate the feature spec: Tool mandu.negotiate
|
|
51
|
+
4. Generate scaffold: Tool mandu.negotiate.scaffold
|
|
52
|
+
5. If client interactivity needed, create island: Tool mandu_create_island
|
|
53
|
+
Use the declarative pattern: island('visible', Component)
|
|
54
|
+
6. If data requirements exist, create slot: Tool mandu_create_slot
|
|
55
|
+
Slots are server-side data loaders that run before render
|
|
56
|
+
7. If API is exposed, define contract: Tool mandu_create_contract
|
|
57
|
+
Contracts are Zod schemas for validation and OpenAPI generation
|
|
58
|
+
8. Validate architecture: Tool mandu_guard_check
|
|
59
|
+
9. Run brain doctor: Tool mandu_brain_diagnose`),
|
|
60
|
+
|
|
61
|
+
"debug": (args) => msg(`${args.symptom ? `Diagnose this error: ${args.symptom}` : "Diagnose errors in the Mandu project"}
|
|
62
|
+
|
|
63
|
+
Follow these diagnostic steps using Mandu MCP tools:
|
|
64
|
+
|
|
65
|
+
1. Check client-side errors: Tool mandu.kitchen.errors
|
|
66
|
+
2. Check recent build/runtime errors: Resource mandu://errors
|
|
67
|
+
3. Check architecture rule violations: Resource mandu://watch/warnings
|
|
68
|
+
4. Run brain doctor for structural analysis: Tool mandu_brain_diagnose
|
|
69
|
+
5. Verify route manifest: Resource mandu://routes
|
|
70
|
+
6. Inspect specific route slot: Resource mandu://slots/{routeId}
|
|
71
|
+
7. Run guard check: Tool mandu_guard_check
|
|
72
|
+
|
|
73
|
+
After identifying root cause, fix and re-run checks to confirm.`),
|
|
74
|
+
|
|
75
|
+
"add-crud": (args) => {
|
|
76
|
+
const r = args.resource;
|
|
77
|
+
const R = capitalize(r);
|
|
78
|
+
return msg(`Create a complete CRUD API for the '${r}' resource.
|
|
79
|
+
|
|
80
|
+
Follow these steps using Mandu MCP tools:
|
|
81
|
+
|
|
82
|
+
1. Read project config: Resource mandu://config
|
|
83
|
+
2. Create API routes: Tool mandu.negotiate
|
|
84
|
+
- GET/POST for /api/${r}, GET/PUT/DELETE for /api/${r}/[id]
|
|
85
|
+
3. Define contracts: Tool mandu_create_contract
|
|
86
|
+
- Create${R}Schema, Update${R}Schema, ${R}ResponseSchema
|
|
87
|
+
4. Generate scaffold: Tool mandu.negotiate.scaffold
|
|
88
|
+
5. Create data slot for list page: Tool mandu_create_slot
|
|
89
|
+
6. Validate: Tool mandu_guard_check + Tool mandu_brain_diagnose
|
|
90
|
+
7. Verify routes: Resource mandu://routes`);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get the prompt handler result for a given prompt name and arguments.
|
|
96
|
+
*/
|
|
97
|
+
export function getPromptResult(
|
|
98
|
+
name: string,
|
|
99
|
+
args: Record<string, string>,
|
|
100
|
+
): GetPromptResult | null {
|
|
101
|
+
const handler = promptHandlers[name];
|
|
102
|
+
if (!handler) return null;
|
|
103
|
+
return handler(args);
|
|
104
|
+
}
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
getTransactionStatus,
|
|
5
5
|
getWatcher,
|
|
6
6
|
type GeneratedMap,
|
|
7
|
-
type SpecLock,
|
|
8
7
|
} from "@mandujs/core";
|
|
9
8
|
import { getProjectPaths, readJsonFile } from "../utils/project.js";
|
|
10
9
|
import {
|
|
@@ -26,12 +25,6 @@ export const resourceDefinitions: Resource[] = [
|
|
|
26
25
|
description: "Current routes.manifest.json content",
|
|
27
26
|
mimeType: "application/json",
|
|
28
27
|
},
|
|
29
|
-
{
|
|
30
|
-
uri: "mandu://spec/lock",
|
|
31
|
-
name: "Spec Lock",
|
|
32
|
-
description: "Current spec.lock.json with hash verification",
|
|
33
|
-
mimeType: "application/json",
|
|
34
|
-
},
|
|
35
28
|
{
|
|
36
29
|
uri: "mandu://generated/map",
|
|
37
30
|
name: "Generated Map",
|
|
@@ -181,22 +174,6 @@ export function resourceHandlers(
|
|
|
181
174
|
};
|
|
182
175
|
},
|
|
183
176
|
|
|
184
|
-
"mandu://spec/lock": async () => {
|
|
185
|
-
const lock = await readJsonFile<SpecLock>(paths.lockPath);
|
|
186
|
-
if (!lock) {
|
|
187
|
-
return {
|
|
188
|
-
exists: false,
|
|
189
|
-
message: "spec.lock.json not found",
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
exists: true,
|
|
195
|
-
routesHash: lock.routesHash,
|
|
196
|
-
updatedAt: lock.updatedAt,
|
|
197
|
-
};
|
|
198
|
-
},
|
|
199
|
-
|
|
200
177
|
"mandu://generated/map": async () => {
|
|
201
178
|
const generatedMap = await readJsonFile<GeneratedMap>(paths.generatedMapPath);
|
|
202
179
|
if (!generatedMap) {
|
package/src/server.ts
CHANGED
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
ListToolsRequestSchema,
|
|
17
17
|
ListResourcesRequestSchema,
|
|
18
18
|
ReadResourceRequestSchema,
|
|
19
|
+
ListPromptsRequestSchema,
|
|
20
|
+
GetPromptRequestSchema,
|
|
19
21
|
type CallToolResult,
|
|
20
22
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
21
23
|
|
|
@@ -38,11 +40,18 @@ import { mcpHookRegistry, registerDefaultMcpHooks, type McpToolContext } from ".
|
|
|
38
40
|
// DNA-006: 설정 핫 리로드
|
|
39
41
|
import { startMcpConfigWatcher, type McpConfigWatcher } from "./hooks/config-watcher.js";
|
|
40
42
|
|
|
43
|
+
// Prompts
|
|
44
|
+
import { manduPrompts, getPromptResult } from "./prompts.js";
|
|
45
|
+
|
|
46
|
+
// New top-level resources
|
|
47
|
+
import { manduResourceDefinitions, manduResourceHandlers } from "./new-resources.js";
|
|
48
|
+
|
|
41
49
|
// 기존 컴포넌트
|
|
42
50
|
import { resourceHandlers, resourceDefinitions } from "./resources/handlers.js";
|
|
43
51
|
import { findProjectRoot } from "./utils/project.js";
|
|
44
52
|
import { applyWarningInjection } from "./utils/withWarnings.js";
|
|
45
53
|
import { ActivityMonitor } from "./activity-monitor.js";
|
|
54
|
+
import { type McpProfile, isValidProfile } from "./profiles.js";
|
|
46
55
|
|
|
47
56
|
/**
|
|
48
57
|
* MCP 서버 버전
|
|
@@ -61,11 +70,16 @@ export class ManduMcpServer {
|
|
|
61
70
|
private config?: ManduConfig;
|
|
62
71
|
private configWatcher?: McpConfigWatcher;
|
|
63
72
|
private toolExecutor: ToolExecutor;
|
|
73
|
+
private profile: McpProfile;
|
|
64
74
|
|
|
65
75
|
constructor(projectRoot: string) {
|
|
66
76
|
this.projectRoot = projectRoot;
|
|
67
77
|
this.monitor = new ActivityMonitor(projectRoot);
|
|
68
78
|
|
|
79
|
+
// Resolve profile from environment variable (default: "full")
|
|
80
|
+
const envProfile = process.env.MANDU_MCP_PROFILE ?? "full";
|
|
81
|
+
this.profile = isValidProfile(envProfile) ? envProfile : "full";
|
|
82
|
+
|
|
69
83
|
// MCP Server 초기화
|
|
70
84
|
this.server = new Server(
|
|
71
85
|
{
|
|
@@ -76,13 +90,16 @@ export class ManduMcpServer {
|
|
|
76
90
|
capabilities: {
|
|
77
91
|
tools: {},
|
|
78
92
|
resources: {},
|
|
93
|
+
prompts: {},
|
|
79
94
|
logging: {},
|
|
80
95
|
},
|
|
81
96
|
}
|
|
82
97
|
);
|
|
83
98
|
|
|
84
99
|
// DNA-001: 플러그인 기반 도구 등록
|
|
85
|
-
registerBuiltinTools(projectRoot, this.server, this.monitor
|
|
100
|
+
registerBuiltinTools(projectRoot, this.server, this.monitor, {
|
|
101
|
+
profile: this.profile,
|
|
102
|
+
});
|
|
86
103
|
|
|
87
104
|
// DNA-008: 로깅 통합
|
|
88
105
|
setupMcpLogging({ consoleOutput: false });
|
|
@@ -100,6 +117,7 @@ export class ManduMcpServer {
|
|
|
100
117
|
// 핸들러 등록
|
|
101
118
|
this.registerToolHandlers();
|
|
102
119
|
this.registerResourceHandlers();
|
|
120
|
+
this.registerPromptHandlers();
|
|
103
121
|
}
|
|
104
122
|
|
|
105
123
|
/**
|
|
@@ -124,21 +142,75 @@ export class ManduMcpServer {
|
|
|
124
142
|
});
|
|
125
143
|
}
|
|
126
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Prompt handler registration
|
|
147
|
+
*/
|
|
148
|
+
private registerPromptHandlers(): void {
|
|
149
|
+
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
150
|
+
return { prompts: manduPrompts };
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
154
|
+
const { name, arguments: args } = request.params;
|
|
155
|
+
const result = getPromptResult(name, args ?? {});
|
|
156
|
+
|
|
157
|
+
if (!result) {
|
|
158
|
+
return {
|
|
159
|
+
messages: [
|
|
160
|
+
{
|
|
161
|
+
role: "user" as const,
|
|
162
|
+
content: {
|
|
163
|
+
type: "text" as const,
|
|
164
|
+
text: `Unknown prompt: ${name}. Available prompts: ${manduPrompts.map((p) => p.name).join(", ")}`,
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return result;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
127
175
|
/**
|
|
128
176
|
* 리소스 핸들러 등록 (기존 유지)
|
|
129
177
|
*/
|
|
130
178
|
private registerResourceHandlers(): void {
|
|
131
179
|
const handlers = resourceHandlers(this.projectRoot);
|
|
180
|
+
const newHandlers = manduResourceHandlers(this.projectRoot);
|
|
181
|
+
|
|
182
|
+
// Merge resource definitions (new top-level + existing)
|
|
183
|
+
const allResources = [...manduResourceDefinitions, ...resourceDefinitions];
|
|
132
184
|
|
|
133
185
|
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
134
|
-
return {
|
|
135
|
-
resources: resourceDefinitions,
|
|
136
|
-
};
|
|
186
|
+
return { resources: allResources };
|
|
137
187
|
});
|
|
138
188
|
|
|
139
189
|
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
140
190
|
const { uri } = request.params;
|
|
141
191
|
|
|
192
|
+
// Check new top-level resource handlers first
|
|
193
|
+
const newHandler = newHandlers[uri];
|
|
194
|
+
if (newHandler) {
|
|
195
|
+
try {
|
|
196
|
+
const result = await newHandler();
|
|
197
|
+
return { contents: [result] };
|
|
198
|
+
} catch (error) {
|
|
199
|
+
return {
|
|
200
|
+
contents: [
|
|
201
|
+
{
|
|
202
|
+
uri,
|
|
203
|
+
mimeType: "application/json",
|
|
204
|
+
text: JSON.stringify({
|
|
205
|
+
error: error instanceof Error ? error.message : String(error),
|
|
206
|
+
}),
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Fall through to existing resource handlers
|
|
142
214
|
const handler = handlers[uri];
|
|
143
215
|
if (!handler) {
|
|
144
216
|
// 동적 리소스 패턴 매칭
|
|
@@ -244,7 +316,7 @@ export class ManduMcpServer {
|
|
|
244
316
|
warning.message
|
|
245
317
|
);
|
|
246
318
|
|
|
247
|
-
//
|
|
319
|
+
// MCP 클라이언트에 알림
|
|
248
320
|
this.server.sendLoggingMessage({
|
|
249
321
|
level: "warning",
|
|
250
322
|
logger: "mandu-watch",
|
|
@@ -270,6 +342,7 @@ export class ManduMcpServer {
|
|
|
270
342
|
const summary = getToolsSummary();
|
|
271
343
|
console.error(`Mandu MCP Server v${MCP_VERSION} running`);
|
|
272
344
|
console.error(` Project: ${this.projectRoot}`);
|
|
345
|
+
console.error(` Profile: ${this.profile}`);
|
|
273
346
|
console.error(` Tools: ${summary.total} (${summary.categories.join(", ")})`);
|
|
274
347
|
}
|
|
275
348
|
|
package/src/tools/ate.ts
CHANGED
|
@@ -15,6 +15,9 @@ import type { OracleLevel } from "@mandujs/ate";
|
|
|
15
15
|
export const ateToolDefinitions: Tool[] = [
|
|
16
16
|
{
|
|
17
17
|
name: "mandu.ate.extract",
|
|
18
|
+
annotations: {
|
|
19
|
+
readOnlyHint: false,
|
|
20
|
+
},
|
|
18
21
|
description:
|
|
19
22
|
"ATE Step 1 — Extract: Statically analyze the Mandu project's AST to build an interaction graph of routes, slots, contracts, and data flow. " +
|
|
20
23
|
"Identifies all testable interactions without running the server. " +
|
|
@@ -39,6 +42,9 @@ export const ateToolDefinitions: Tool[] = [
|
|
|
39
42
|
},
|
|
40
43
|
{
|
|
41
44
|
name: "mandu.ate.generate",
|
|
45
|
+
annotations: {
|
|
46
|
+
readOnlyHint: false,
|
|
47
|
+
},
|
|
42
48
|
description:
|
|
43
49
|
"ATE Step 2 — Generate: Create Playwright test scenarios from the interaction graph produced by mandu.ate.extract. " +
|
|
44
50
|
"Oracle level controls assertion depth: " +
|
|
@@ -67,6 +73,9 @@ export const ateToolDefinitions: Tool[] = [
|
|
|
67
73
|
},
|
|
68
74
|
{
|
|
69
75
|
name: "mandu.ate.run",
|
|
76
|
+
annotations: {
|
|
77
|
+
readOnlyHint: false,
|
|
78
|
+
},
|
|
70
79
|
description:
|
|
71
80
|
"ATE Step 3 — Run: Execute the generated Playwright specs against a running Mandu dev server. " +
|
|
72
81
|
"Collects test artifacts (screenshots, traces, results) in .mandu/ate/runs/{runId}/. " +
|
|
@@ -93,6 +102,9 @@ export const ateToolDefinitions: Tool[] = [
|
|
|
93
102
|
},
|
|
94
103
|
{
|
|
95
104
|
name: "mandu.ate.report",
|
|
105
|
+
annotations: {
|
|
106
|
+
readOnlyHint: true,
|
|
107
|
+
},
|
|
96
108
|
description:
|
|
97
109
|
"ATE Step 4 — Report: Generate a test report from run artifacts. " +
|
|
98
110
|
"Produces pass/fail summary, coverage by route, and failure details. " +
|
|
@@ -132,6 +144,9 @@ export const ateToolDefinitions: Tool[] = [
|
|
|
132
144
|
},
|
|
133
145
|
{
|
|
134
146
|
name: "mandu.ate.heal",
|
|
147
|
+
annotations: {
|
|
148
|
+
readOnlyHint: true,
|
|
149
|
+
},
|
|
135
150
|
description:
|
|
136
151
|
"ATE Step 5 — Heal: Analyze test failures from a run and generate safe diff suggestions for fixing the code. " +
|
|
137
152
|
"Classifies failures by root cause (schema mismatch, missing handler, wrong status, selector stale, etc.) " +
|
|
@@ -149,6 +164,9 @@ export const ateToolDefinitions: Tool[] = [
|
|
|
149
164
|
},
|
|
150
165
|
{
|
|
151
166
|
name: "mandu.ate.impact",
|
|
167
|
+
annotations: {
|
|
168
|
+
readOnlyHint: true,
|
|
169
|
+
},
|
|
152
170
|
description:
|
|
153
171
|
"ATE Optimization — Impact Analysis: Calculate the minimal subset of routes affected by changed files using git diff. " +
|
|
154
172
|
"Avoids running the full test suite when only part of the codebase changed. " +
|
|
@@ -166,6 +184,9 @@ export const ateToolDefinitions: Tool[] = [
|
|
|
166
184
|
},
|
|
167
185
|
{
|
|
168
186
|
name: "mandu.ate.auto_pipeline",
|
|
187
|
+
annotations: {
|
|
188
|
+
readOnlyHint: false,
|
|
189
|
+
},
|
|
169
190
|
description:
|
|
170
191
|
"ATE Full Pipeline — Run the complete ATE cycle in one call: " +
|
|
171
192
|
"Extract AST → Generate Playwright specs → Run tests → Create report → Suggest heals. " +
|
|
@@ -202,6 +223,9 @@ export const ateToolDefinitions: Tool[] = [
|
|
|
202
223
|
},
|
|
203
224
|
{
|
|
204
225
|
name: "mandu.ate.feedback",
|
|
226
|
+
annotations: {
|
|
227
|
+
readOnlyHint: true,
|
|
228
|
+
},
|
|
205
229
|
description:
|
|
206
230
|
"ATE Feedback — Evaluate heal suggestions from a failed run and classify which fixes are safe to auto-apply. " +
|
|
207
231
|
"Safe-to-auto-apply: selector-map updates (CSS selector changes). " +
|
|
@@ -222,6 +246,10 @@ export const ateToolDefinitions: Tool[] = [
|
|
|
222
246
|
},
|
|
223
247
|
{
|
|
224
248
|
name: "mandu.ate.apply_heal",
|
|
249
|
+
annotations: {
|
|
250
|
+
destructiveHint: true,
|
|
251
|
+
readOnlyHint: false,
|
|
252
|
+
},
|
|
225
253
|
description:
|
|
226
254
|
"ATE Apply — Apply a specific heal suggestion diff to the codebase. " +
|
|
227
255
|
"Always creates a backup snapshot first (use mandu_rollback to undo). " +
|