@mandujs/mcp 0.37.2 → 0.37.4
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/index.ts +5 -4
- package/src/profiles.ts +50 -27
- package/src/resources/skills/guides.ts +49 -40
- package/src/resources/skills/loader.ts +8 -6
- package/src/resources/skills/mandu-agent-workflow/SKILL.md +124 -0
- package/src/resources/skills/mandu-agent-workflow/metadata.json +7 -0
- package/src/resources/skills/mandu-composition/SKILL.md +47 -7
- package/src/resources/skills/mandu-deployment/SKILL.md +53 -10
- package/src/resources/skills/mandu-fs-routes/SKILL.md +47 -7
- package/src/resources/skills/mandu-guard/SKILL.md +58 -25
- package/src/resources/skills/mandu-hydration/SKILL.md +67 -19
- package/src/resources/skills/mandu-hydration/rules/hydration-island-setup.md +54 -7
- package/src/resources/skills/mandu-hydration/rules/hydration-priority-visible.md +60 -37
- package/src/resources/skills/mandu-performance/SKILL.md +47 -7
- package/src/resources/skills/mandu-security/SKILL.md +47 -7
- package/src/resources/skills/mandu-slot/SKILL.md +48 -8
- package/src/resources/skills/mandu-styling/SKILL.md +52 -10
- package/src/resources/skills/mandu-testing/SKILL.md +55 -22
- package/src/resources/skills/mandu-ui/SKILL.md +52 -10
- package/src/resources/skills/recipes.ts +28 -19
- package/src/server.ts +3 -4
- package/src/tools/agent.ts +443 -0
- package/src/tools/guard.ts +162 -68
- package/src/tools/index.ts +8 -5
package/src/tools/guard.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import { type ManduError } from "@mandujs/core/error";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import {
|
|
4
|
+
checkDirectory,
|
|
5
|
+
getDefaultFsRoutesGuardPolicy,
|
|
6
|
+
validateAndReport,
|
|
7
|
+
loadManifest,
|
|
8
|
+
runGuardCheck,
|
|
9
|
+
runAutoCorrect,
|
|
10
|
+
type GeneratedMap,
|
|
8
11
|
// Self-Healing Guard imports
|
|
9
12
|
checkWithHealing,
|
|
10
13
|
healAll,
|
|
@@ -12,16 +15,19 @@ import {
|
|
|
12
15
|
// Follow-up E — type-aware lint bridge
|
|
13
16
|
runTsgolint,
|
|
14
17
|
type GuardConfig,
|
|
15
|
-
type ViolationType,
|
|
16
|
-
type GuardPreset,
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
type ViolationType,
|
|
19
|
+
type GuardPreset,
|
|
20
|
+
type Violation,
|
|
21
|
+
} from "@mandujs/core";
|
|
22
|
+
import { getProjectPaths, readJsonFile, readConfig } from "../utils/project.js";
|
|
23
|
+
import fs from "fs/promises";
|
|
24
|
+
import path from "path";
|
|
19
25
|
|
|
20
26
|
export const guardToolDefinitions: Tool[] = [
|
|
21
27
|
{
|
|
22
|
-
name: "mandu.guard.check",
|
|
23
|
-
description:
|
|
24
|
-
"Run
|
|
28
|
+
name: "mandu.guard.check",
|
|
29
|
+
description:
|
|
30
|
+
"Run the same architecture guard used by `mandu guard`, plus legacy spec/generated/slot checks. Set typeAware=true to additionally run `oxlint --type-aware` (tsgolint) and merge its results.",
|
|
25
31
|
annotations: {
|
|
26
32
|
readOnlyHint: true,
|
|
27
33
|
},
|
|
@@ -120,8 +126,37 @@ export const guardToolDefinitions: Tool[] = [
|
|
|
120
126
|
},
|
|
121
127
|
];
|
|
122
128
|
|
|
123
|
-
export function guardTools(projectRoot: string) {
|
|
124
|
-
const paths = getProjectPaths(projectRoot);
|
|
129
|
+
export function guardTools(projectRoot: string) {
|
|
130
|
+
const paths = getProjectPaths(projectRoot);
|
|
131
|
+
|
|
132
|
+
const pathExists = async (candidate: string): Promise<boolean> => {
|
|
133
|
+
try {
|
|
134
|
+
await fs.access(candidate);
|
|
135
|
+
return true;
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const summarizeArchitectureViolation = (violation: Violation) => ({
|
|
142
|
+
ruleId: violation.ruleName,
|
|
143
|
+
type: violation.type,
|
|
144
|
+
file: path.relative(projectRoot, violation.filePath).replace(/\\/g, "/") || violation.filePath,
|
|
145
|
+
line: violation.line,
|
|
146
|
+
column: violation.column,
|
|
147
|
+
message: violation.ruleDescription,
|
|
148
|
+
suggestion: violation.suggestions[0],
|
|
149
|
+
fromLayer: violation.fromLayer,
|
|
150
|
+
toLayer: violation.toLayer,
|
|
151
|
+
importStatement: violation.importStatement,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const summarizeLegacyViolation = (v: Awaited<ReturnType<typeof runGuardCheck>>["violations"][number]) => ({
|
|
155
|
+
ruleId: v.ruleId,
|
|
156
|
+
file: v.file,
|
|
157
|
+
message: v.message,
|
|
158
|
+
suggestion: v.suggestion,
|
|
159
|
+
});
|
|
125
160
|
|
|
126
161
|
const handlers: Record<string, (args: Record<string, unknown>) => Promise<unknown>> = {
|
|
127
162
|
"mandu.guard.check": async (args: Record<string, unknown>) => {
|
|
@@ -137,25 +172,44 @@ export function guardTools(projectRoot: string) {
|
|
|
137
172
|
error: "Failed to load manifest",
|
|
138
173
|
details: manifestResult.errors,
|
|
139
174
|
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const projectConfig = await validateAndReport(projectRoot);
|
|
178
|
+
const guardConfigFromFile = (projectConfig?.guard ?? {}) as GuardConfig;
|
|
179
|
+
const preset = guardConfigFromFile.preset ?? "mandu";
|
|
180
|
+
const enableFsRoutes = await pathExists(paths.appDir);
|
|
181
|
+
|
|
182
|
+
const architectureReport = await checkDirectory(
|
|
183
|
+
{
|
|
184
|
+
preset,
|
|
185
|
+
srcDir: guardConfigFromFile.srcDir ?? "src",
|
|
186
|
+
exclude: guardConfigFromFile.exclude,
|
|
187
|
+
fsRoutes: getDefaultFsRoutesGuardPolicy(enableFsRoutes),
|
|
188
|
+
},
|
|
189
|
+
projectRoot
|
|
190
|
+
);
|
|
191
|
+
const architecturePassed = architectureReport.bySeverity.error === 0;
|
|
192
|
+
const architectureViolations = architectureReport.violations.map(
|
|
193
|
+
summarizeArchitectureViolation
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Run guard check
|
|
197
|
+
const checkResult = await runGuardCheck(manifestResult.data, projectRoot);
|
|
144
198
|
|
|
145
199
|
// Follow-up E — resolve type-aware defaulting from config, then
|
|
146
200
|
// run the bridge when enabled. Result envelope is always included
|
|
147
201
|
// in the tool response so MCP clients have a single stable shape.
|
|
148
|
-
let
|
|
149
|
-
try {
|
|
150
|
-
|
|
151
|
-
} catch {
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
const typeAwareCfg = (
|
|
155
|
-
|
|
156
|
-
| { typeAware?: Record<string, unknown> }
|
|
157
|
-
| undefined
|
|
158
|
-
)?.typeAware;
|
|
202
|
+
let rawProjectConfig: Awaited<ReturnType<typeof readConfig>> | undefined;
|
|
203
|
+
try {
|
|
204
|
+
rawProjectConfig = projectConfig ?? await readConfig(projectRoot);
|
|
205
|
+
} catch {
|
|
206
|
+
rawProjectConfig = projectConfig ?? undefined;
|
|
207
|
+
}
|
|
208
|
+
const typeAwareCfg = (
|
|
209
|
+
rawProjectConfig?.guard as
|
|
210
|
+
| { typeAware?: Record<string, unknown> }
|
|
211
|
+
| undefined
|
|
212
|
+
)?.typeAware;
|
|
159
213
|
const typeAwareEnabled =
|
|
160
214
|
typeAwareArg !== undefined ? typeAwareArg : typeAwareCfg !== undefined;
|
|
161
215
|
|
|
@@ -175,21 +229,40 @@ export function guardTools(projectRoot: string) {
|
|
|
175
229
|
skipped: bridge.skipped,
|
|
176
230
|
summary: bridge.summary,
|
|
177
231
|
violations: bridge.violations,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const typeAwareViolations = typeAwareResponse
|
|
236
|
+
? typeAwareResponse.violations as Array<{ severity: string }>
|
|
237
|
+
: [];
|
|
238
|
+
const typeAwareErrorCount = typeAwareViolations.filter(
|
|
239
|
+
(v) => v.severity === "error",
|
|
240
|
+
).length;
|
|
241
|
+
const legacyViolations = checkResult.violations.map(summarizeLegacyViolation);
|
|
242
|
+
const combinedViolations = [
|
|
243
|
+
...architectureViolations,
|
|
244
|
+
...legacyViolations,
|
|
245
|
+
];
|
|
246
|
+
const allPassed = checkResult.passed && architecturePassed && typeAwareErrorCount === 0;
|
|
247
|
+
const blockingViolationCount = combinedViolations.length + typeAwareErrorCount;
|
|
248
|
+
|
|
249
|
+
if (allPassed) {
|
|
250
|
+
return {
|
|
251
|
+
passed: true,
|
|
252
|
+
violations: [],
|
|
253
|
+
message: "All guard checks passed",
|
|
254
|
+
architecture: {
|
|
255
|
+
passed: true,
|
|
256
|
+
totalViolations: architectureReport.totalViolations,
|
|
257
|
+
bySeverity: architectureReport.bySeverity,
|
|
258
|
+
},
|
|
259
|
+
legacy: {
|
|
260
|
+
passed: true,
|
|
261
|
+
violations: 0,
|
|
262
|
+
},
|
|
263
|
+
relatedSkills: ["mandu-guard-guide", "mandu-debug"],
|
|
264
|
+
...(typeAwareResponse ? { typeAware: typeAwareResponse } : {}),
|
|
265
|
+
};
|
|
193
266
|
}
|
|
194
267
|
|
|
195
268
|
// If auto-correct requested and there are violations
|
|
@@ -200,33 +273,54 @@ export function guardTools(projectRoot: string) {
|
|
|
200
273
|
projectRoot
|
|
201
274
|
);
|
|
202
275
|
|
|
203
|
-
return {
|
|
204
|
-
passed:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
276
|
+
return {
|
|
277
|
+
passed:
|
|
278
|
+
autoCorrectResult.fixed &&
|
|
279
|
+
architecturePassed &&
|
|
280
|
+
typeAwareErrorCount === 0,
|
|
281
|
+
violations: [
|
|
282
|
+
...architectureViolations,
|
|
283
|
+
...autoCorrectResult.remainingViolations.map(summarizeLegacyViolation),
|
|
284
|
+
],
|
|
285
|
+
autoCorrect: {
|
|
286
|
+
attempted: true,
|
|
287
|
+
fixed: autoCorrectResult.fixed,
|
|
209
288
|
steps: autoCorrectResult.steps,
|
|
210
289
|
retriedCount: autoCorrectResult.retriedCount,
|
|
211
290
|
rolledBack: autoCorrectResult.rolledBack,
|
|
212
|
-
changeId: autoCorrectResult.changeId,
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
291
|
+
changeId: autoCorrectResult.changeId,
|
|
292
|
+
},
|
|
293
|
+
architecture: {
|
|
294
|
+
passed: architecturePassed,
|
|
295
|
+
totalViolations: architectureReport.totalViolations,
|
|
296
|
+
bySeverity: architectureReport.bySeverity,
|
|
297
|
+
violations: architectureViolations,
|
|
298
|
+
},
|
|
299
|
+
legacy: {
|
|
300
|
+
passed: autoCorrectResult.fixed,
|
|
301
|
+
violations: autoCorrectResult.remainingViolations.length,
|
|
302
|
+
},
|
|
303
|
+
...(typeAwareResponse ? { typeAware: typeAwareResponse } : {}),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
passed: false,
|
|
309
|
+
violations: combinedViolations,
|
|
310
|
+
message: `Found ${blockingViolationCount} violation(s)`,
|
|
311
|
+
architecture: {
|
|
312
|
+
passed: architecturePassed,
|
|
313
|
+
totalViolations: architectureReport.totalViolations,
|
|
314
|
+
bySeverity: architectureReport.bySeverity,
|
|
315
|
+
violations: architectureViolations,
|
|
316
|
+
},
|
|
317
|
+
legacy: {
|
|
318
|
+
passed: checkResult.passed,
|
|
319
|
+
violations: legacyViolations.length,
|
|
320
|
+
},
|
|
321
|
+
tip: "Use autoCorrect: true to attempt automatic fixes",
|
|
322
|
+
relatedSkills: ["mandu-guard-guide", "mandu-debug"],
|
|
323
|
+
...(typeAwareResponse ? { typeAware: typeAwareResponse } : {}),
|
|
230
324
|
};
|
|
231
325
|
},
|
|
232
326
|
|
package/src/tools/index.ts
CHANGED
|
@@ -11,8 +11,9 @@ import { mcpToolRegistry } from "../registry/mcp-tool-registry.js";
|
|
|
11
11
|
import { moduleToPlugins } from "../adapters/tool-adapter.js";
|
|
12
12
|
import { type McpProfile, getProfileCategories } from "../profiles.js";
|
|
13
13
|
|
|
14
|
-
// 도구 모듈 export
|
|
15
|
-
export {
|
|
14
|
+
// 도구 모듈 export
|
|
15
|
+
export { agentTools, agentToolDefinitions } from "./agent.js";
|
|
16
|
+
export { specTools, specToolDefinitions } from "./spec.js";
|
|
16
17
|
export { generateTools, generateToolDefinitions } from "./generate.js";
|
|
17
18
|
export { transactionTools, transactionToolDefinitions } from "./transaction.js";
|
|
18
19
|
export { historyTools, historyToolDefinitions } from "./history.js";
|
|
@@ -85,8 +86,9 @@ export {
|
|
|
85
86
|
extractContractToolDefinitions,
|
|
86
87
|
} from "./extract-contract.js";
|
|
87
88
|
|
|
88
|
-
// 도구 모듈 import (등록용)
|
|
89
|
-
import {
|
|
89
|
+
// 도구 모듈 import (등록용)
|
|
90
|
+
import { agentTools, agentToolDefinitions } from "./agent.js";
|
|
91
|
+
import { specTools, specToolDefinitions } from "./spec.js";
|
|
90
92
|
import { generateTools, generateToolDefinitions } from "./generate.js";
|
|
91
93
|
import { transactionTools, transactionToolDefinitions } from "./transaction.js";
|
|
92
94
|
import { historyTools, historyToolDefinitions } from "./history.js";
|
|
@@ -189,7 +191,8 @@ export interface ToolModule {
|
|
|
189
191
|
* 빌트인 도구 모듈 목록
|
|
190
192
|
*/
|
|
191
193
|
export const TOOL_MODULES: ToolModule[] = [
|
|
192
|
-
{ category: "
|
|
194
|
+
{ category: "agent", definitions: agentToolDefinitions, handlers: agentTools },
|
|
195
|
+
{ category: "spec", definitions: specToolDefinitions, handlers: specTools },
|
|
193
196
|
{ category: "generate", definitions: generateToolDefinitions, handlers: generateTools },
|
|
194
197
|
{ category: "transaction", definitions: transactionToolDefinitions, handlers: transactionTools },
|
|
195
198
|
{ category: "history", definitions: historyToolDefinitions, handlers: historyTools },
|