@mandujs/mcp 0.13.0 → 0.17.0
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 +102 -7
- package/package.json +3 -2
- package/src/adapters/index.ts +20 -20
- package/src/adapters/monitor-adapter.ts +100 -100
- package/src/adapters/tool-adapter.ts +88 -88
- package/src/executor/error-handler.ts +250 -250
- package/src/executor/index.ts +22 -22
- package/src/executor/tool-executor.ts +148 -148
- package/src/hooks/config-watcher.ts +174 -174
- package/src/hooks/index.ts +23 -23
- package/src/hooks/mcp-hooks.ts +227 -227
- package/src/logging/index.ts +15 -15
- package/src/logging/mcp-transport.ts +134 -134
- package/src/registry/index.ts +13 -13
- package/src/registry/mcp-tool-registry.ts +298 -298
- package/src/resources/skills/guides.ts +1136 -1136
- package/src/resources/skills/index.ts +12 -12
- package/src/resources/skills/loader.ts +218 -218
- package/src/resources/skills/mandu-composition/SKILL.md +91 -91
- package/src/resources/skills/mandu-composition/metadata.json +13 -13
- package/src/resources/skills/mandu-composition/rules/_sections.md +26 -26
- package/src/resources/skills/mandu-composition/rules/_template.md +77 -77
- package/src/resources/skills/mandu-composition/rules/comp-arch-avoid-boolean-props.md +146 -146
- package/src/resources/skills/mandu-composition/rules/comp-arch-compound-components.md +164 -164
- package/src/resources/skills/mandu-composition/rules/comp-island-event.md +161 -161
- package/src/resources/skills/mandu-composition/rules/comp-island-slot-split.md +167 -167
- package/src/resources/skills/mandu-composition/rules/comp-pattern-children.md +149 -149
- package/src/resources/skills/mandu-composition/rules/comp-state-context-interface.md +148 -148
- package/src/resources/skills/mandu-composition/rules/comp-state-lift-state.md +150 -150
- package/src/resources/skills/mandu-deployment/SKILL.md +92 -92
- package/src/resources/skills/mandu-deployment/_sections.md +41 -41
- package/src/resources/skills/mandu-deployment/_template.md +38 -38
- package/src/resources/skills/mandu-deployment/metadata.json +13 -13
- package/src/resources/skills/mandu-deployment/rules/deploy-build-bun.md +109 -109
- package/src/resources/skills/mandu-deployment/rules/deploy-build-output.md +115 -115
- package/src/resources/skills/mandu-deployment/rules/deploy-cicd-github.md +219 -219
- package/src/resources/skills/mandu-deployment/rules/deploy-docker-bun.md +150 -150
- package/src/resources/skills/mandu-deployment/rules/deploy-docker-compose.md +223 -223
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-fly.md +152 -152
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-render.md +179 -179
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-supabase.md +323 -323
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-vercel.md +140 -140
- package/src/resources/skills/mandu-fs-routes/SKILL.md +82 -82
- package/src/resources/skills/mandu-fs-routes/metadata.json +12 -12
- package/src/resources/skills/mandu-fs-routes/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-fs-routes/rules/_template.md +69 -69
- package/src/resources/skills/mandu-fs-routes/rules/routes-api-methods.md +65 -65
- package/src/resources/skills/mandu-fs-routes/rules/routes-dynamic-param.md +93 -93
- package/src/resources/skills/mandu-fs-routes/rules/routes-naming-page.md +55 -55
- package/src/resources/skills/mandu-guard/SKILL.md +129 -129
- package/src/resources/skills/mandu-guard/metadata.json +12 -12
- package/src/resources/skills/mandu-guard/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-guard/rules/_template.md +82 -82
- package/src/resources/skills/mandu-guard/rules/guard-config-rules.md +100 -100
- package/src/resources/skills/mandu-guard/rules/guard-layer-direction.md +76 -76
- package/src/resources/skills/mandu-guard/rules/guard-preset-mandu.md +81 -81
- package/src/resources/skills/mandu-guard/rules/guard-validate-import.md +80 -80
- package/src/resources/skills/mandu-hydration/SKILL.md +91 -91
- package/src/resources/skills/mandu-hydration/metadata.json +12 -12
- package/src/resources/skills/mandu-hydration/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-hydration/rules/_template.md +72 -72
- package/src/resources/skills/mandu-hydration/rules/hydration-data-event.md +109 -109
- package/src/resources/skills/mandu-hydration/rules/hydration-directive-use-client.md +55 -55
- package/src/resources/skills/mandu-hydration/rules/hydration-island-setup.md +113 -113
- package/src/resources/skills/mandu-hydration/rules/hydration-priority-visible.md +68 -68
- package/src/resources/skills/mandu-performance/SKILL.md +85 -85
- package/src/resources/skills/mandu-performance/metadata.json +14 -14
- package/src/resources/skills/mandu-performance/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-performance/rules/_template.md +64 -64
- package/src/resources/skills/mandu-performance/rules/perf-async-defer-await.md +103 -103
- package/src/resources/skills/mandu-performance/rules/perf-async-parallel.md +95 -95
- package/src/resources/skills/mandu-performance/rules/perf-bun-file.md +124 -124
- package/src/resources/skills/mandu-performance/rules/perf-bun-serve.md +125 -125
- package/src/resources/skills/mandu-performance/rules/perf-bundle-imports.md +80 -80
- package/src/resources/skills/mandu-performance/rules/perf-bundle-island-lazy.md +145 -145
- package/src/resources/skills/mandu-performance/rules/perf-cache-react.md +98 -98
- package/src/resources/skills/mandu-performance/rules/perf-render-transitions.md +154 -154
- package/src/resources/skills/mandu-security/SKILL.md +87 -87
- package/src/resources/skills/mandu-security/metadata.json +13 -13
- package/src/resources/skills/mandu-security/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-security/rules/_template.md +74 -74
- package/src/resources/skills/mandu-security/rules/sec-auth-guard.md +127 -127
- package/src/resources/skills/mandu-security/rules/sec-env-management.md +133 -133
- package/src/resources/skills/mandu-security/rules/sec-input-validate.md +148 -148
- package/src/resources/skills/mandu-security/rules/sec-protect-csrf.md +146 -146
- package/src/resources/skills/mandu-security/rules/sec-protect-headers.md +138 -138
- package/src/resources/skills/mandu-slot/SKILL.md +85 -85
- package/src/resources/skills/mandu-slot/metadata.json +12 -12
- package/src/resources/skills/mandu-slot/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-slot/rules/_template.md +63 -63
- package/src/resources/skills/mandu-slot/rules/slot-basic-structure.md +38 -38
- package/src/resources/skills/mandu-slot/rules/slot-ctx-response.md +56 -56
- package/src/resources/skills/mandu-slot/rules/slot-guard-auth.md +59 -59
- package/src/resources/skills/mandu-slot/rules/slot-http-methods.md +64 -64
- package/src/resources/skills/mandu-styling/SKILL.md +154 -154
- package/src/resources/skills/mandu-styling/_sections.md +43 -43
- package/src/resources/skills/mandu-styling/_template.md +32 -32
- package/src/resources/skills/mandu-styling/metadata.json +15 -15
- package/src/resources/skills/mandu-styling/rules/style-component-compound.md +235 -235
- package/src/resources/skills/mandu-styling/rules/style-component-slots.md +255 -255
- package/src/resources/skills/mandu-styling/rules/style-component-tokens.md +205 -205
- package/src/resources/skills/mandu-styling/rules/style-island-animations.md +272 -272
- package/src/resources/skills/mandu-styling/rules/style-island-scoping.md +167 -167
- package/src/resources/skills/mandu-styling/rules/style-island-variants.md +221 -221
- package/src/resources/skills/mandu-styling/rules/style-perf-critical.md +209 -209
- package/src/resources/skills/mandu-styling/rules/style-perf-purge.md +192 -192
- package/src/resources/skills/mandu-styling/rules/style-setup-modules.md +162 -162
- package/src/resources/skills/mandu-styling/rules/style-setup-panda.md +164 -164
- package/src/resources/skills/mandu-styling/rules/style-setup-tailwind.md +170 -170
- package/src/resources/skills/mandu-styling/rules/style-tailwind-v4-gotchas.md +179 -179
- package/src/resources/skills/mandu-styling/rules/style-theme-darkmode.md +229 -229
- package/src/resources/skills/mandu-testing/SKILL.md +99 -99
- package/src/resources/skills/mandu-testing/metadata.json +13 -13
- package/src/resources/skills/mandu-testing/rules/_sections.md +26 -26
- package/src/resources/skills/mandu-testing/rules/_template.md +65 -65
- package/src/resources/skills/mandu-testing/rules/test-component-island.md +195 -195
- package/src/resources/skills/mandu-testing/rules/test-e2e-playwright.md +196 -196
- package/src/resources/skills/mandu-testing/rules/test-mock-fetch.md +219 -219
- package/src/resources/skills/mandu-testing/rules/test-slot-unit.md +192 -192
- package/src/resources/skills/mandu-ui/SKILL.md +117 -117
- package/src/resources/skills/mandu-ui/_sections.md +23 -23
- package/src/resources/skills/mandu-ui/_template.md +32 -32
- package/src/resources/skills/mandu-ui/metadata.json +13 -13
- package/src/resources/skills/mandu-ui/rules/ui-accessibility-aria.md +232 -232
- package/src/resources/skills/mandu-ui/rules/ui-accessibility-focus.md +238 -238
- package/src/resources/skills/mandu-ui/rules/ui-composition-patterns.md +259 -259
- package/src/resources/skills/mandu-ui/rules/ui-island-integration.md +258 -258
- package/src/resources/skills/mandu-ui/rules/ui-radix-patterns.md +213 -213
- package/src/resources/skills/mandu-ui/rules/ui-shadcn-setup.md +209 -209
- package/src/resources/skills/recipes.ts +932 -932
- package/src/tools/ate.ts +219 -0
- package/src/tools/index.ts +4 -1
- package/src/tools/project.ts +334 -334
- package/src/tools/runtime.ts +497 -497
- package/src/tools/seo.ts +417 -417
- package/src/utils/withWarnings.ts +83 -83
|
@@ -1,250 +1,250 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Error Handler
|
|
3
|
-
*
|
|
4
|
-
* DNA-007 에러 추출 시스템 기반 MCP 에러 처리
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
extractErrorInfo,
|
|
9
|
-
classifyError,
|
|
10
|
-
serializeError,
|
|
11
|
-
isRetryableError,
|
|
12
|
-
type ErrorCategory,
|
|
13
|
-
type ExtractedErrorInfo,
|
|
14
|
-
} from "@mandujs/core";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* MCP 에러 응답 타입
|
|
18
|
-
*/
|
|
19
|
-
export interface McpErrorResponse {
|
|
20
|
-
/** 에러 메시지 */
|
|
21
|
-
error: string;
|
|
22
|
-
/** 에러 코드 (있는 경우) */
|
|
23
|
-
code?: string;
|
|
24
|
-
/** 에러 카테고리 (DNA-007) */
|
|
25
|
-
category: ErrorCategory;
|
|
26
|
-
/** 재시도 가능 여부 */
|
|
27
|
-
retryable: boolean;
|
|
28
|
-
/** 추가 컨텍스트 */
|
|
29
|
-
context?: Record<string, unknown>;
|
|
30
|
-
/** 복구 제안 */
|
|
31
|
-
suggestion?: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* MCP 도구 응답 타입
|
|
36
|
-
*/
|
|
37
|
-
export interface McpToolResponse {
|
|
38
|
-
content: Array<{ type: string; text: string }>;
|
|
39
|
-
isError?: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 에러를 MCP 응답 형식으로 변환
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* ```ts
|
|
47
|
-
* try {
|
|
48
|
-
* await tool.execute(args);
|
|
49
|
-
* } catch (err) {
|
|
50
|
-
* const response = formatMcpError(err, "mandu_guard_check");
|
|
51
|
-
* // { error: "...", code: "...", category: "validation", ... }
|
|
52
|
-
* }
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
export function formatMcpError(err: unknown, toolName?: string): McpErrorResponse {
|
|
56
|
-
const info = extractErrorInfo(err);
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
error: info.message,
|
|
60
|
-
code: info.code,
|
|
61
|
-
category: info.category,
|
|
62
|
-
retryable: isRetryableError(err),
|
|
63
|
-
context: {
|
|
64
|
-
...info.context,
|
|
65
|
-
toolName,
|
|
66
|
-
errorName: info.name,
|
|
67
|
-
},
|
|
68
|
-
suggestion: generateSuggestion(info, toolName),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 에러 카테고리 및 도구별 복구 제안 생성
|
|
74
|
-
*/
|
|
75
|
-
function generateSuggestion(info: ExtractedErrorInfo, toolName?: string): string | undefined {
|
|
76
|
-
// 도구별 특화 제안
|
|
77
|
-
if (toolName) {
|
|
78
|
-
const toolSuggestion = getToolSpecificSuggestion(toolName, info);
|
|
79
|
-
if (toolSuggestion) return toolSuggestion;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// 카테고리별 일반 제안
|
|
83
|
-
switch (info.category) {
|
|
84
|
-
case "network":
|
|
85
|
-
return "네트워크 연결을 확인하고 다시 시도해주세요.";
|
|
86
|
-
|
|
87
|
-
case "timeout":
|
|
88
|
-
return "요청 시간이 초과되었습니다. 잠시 후 다시 시도해주세요.";
|
|
89
|
-
|
|
90
|
-
case "auth":
|
|
91
|
-
return "인증 정보를 확인해주세요.";
|
|
92
|
-
|
|
93
|
-
case "validation":
|
|
94
|
-
return "입력 값을 확인해주세요. 필수 파라미터가 누락되었거나 형식이 올바르지 않을 수 있습니다.";
|
|
95
|
-
|
|
96
|
-
case "config":
|
|
97
|
-
return "설정 파일(mandu.config.ts)을 확인해주세요.";
|
|
98
|
-
|
|
99
|
-
case "system":
|
|
100
|
-
if (info.code === "ENOENT") {
|
|
101
|
-
const path = info.context?.path ?? "unknown";
|
|
102
|
-
return `파일 또는 디렉토리를 찾을 수 없습니다: ${path}`;
|
|
103
|
-
}
|
|
104
|
-
if (info.code === "EACCES" || info.code === "EPERM") {
|
|
105
|
-
return "파일 접근 권한을 확인해주세요.";
|
|
106
|
-
}
|
|
107
|
-
return "시스템 리소스를 확인해주세요.";
|
|
108
|
-
|
|
109
|
-
case "external":
|
|
110
|
-
return "외부 서비스에 문제가 있습니다. 잠시 후 다시 시도해주세요.";
|
|
111
|
-
|
|
112
|
-
case "internal":
|
|
113
|
-
return "내부 오류가 발생했습니다. 문제가 지속되면 이슈를 보고해주세요.";
|
|
114
|
-
|
|
115
|
-
default:
|
|
116
|
-
return undefined;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* 도구별 특화된 에러 제안
|
|
122
|
-
*/
|
|
123
|
-
function getToolSpecificSuggestion(toolName: string, info: ExtractedErrorInfo): string | undefined {
|
|
124
|
-
// spec 관련 도구
|
|
125
|
-
if (toolName.startsWith("mandu_") && toolName.includes("route")) {
|
|
126
|
-
if (info.code === "ENOENT") {
|
|
127
|
-
return "routes.manifest.json 파일이 없습니다. `mandu init`을 먼저 실행해주세요.";
|
|
128
|
-
}
|
|
129
|
-
if (info.message.includes("not found")) {
|
|
130
|
-
return "해당 라우트를 찾을 수 없습니다. `mandu_list_routes`로 존재하는 라우트를 확인해주세요.";
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// guard 관련 도구
|
|
135
|
-
if (toolName === "mandu_guard_check") {
|
|
136
|
-
if (info.category === "config") {
|
|
137
|
-
return "Guard 설정을 확인해주세요. mandu.config.ts의 guard 섹션을 검토해주세요.";
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// contract 관련 도구
|
|
142
|
-
if (toolName.includes("contract")) {
|
|
143
|
-
if (info.category === "validation") {
|
|
144
|
-
return "Contract 스키마가 올바른지 확인해주세요. Zod 스키마 문법을 확인해주세요.";
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// generate 관련 도구
|
|
149
|
-
if (toolName === "mandu_generate") {
|
|
150
|
-
if (info.code === "EEXIST") {
|
|
151
|
-
return "파일이 이미 존재합니다. 덮어쓰려면 force 옵션을 사용해주세요.";
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// transaction 관련 도구
|
|
156
|
-
if (toolName.includes("tx") || toolName.includes("transaction")) {
|
|
157
|
-
if (info.message.includes("no active")) {
|
|
158
|
-
return "활성화된 트랜잭션이 없습니다. `mandu_begin`으로 트랜잭션을 시작해주세요.";
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return undefined;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* 도구 실행 결과를 MCP 응답으로 변환
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* ```ts
|
|
170
|
-
* // 성공 응답
|
|
171
|
-
* const response = createToolResponse("mandu_list_routes", { routes: [...] });
|
|
172
|
-
*
|
|
173
|
-
* // 에러 응답
|
|
174
|
-
* const response = createToolResponse("mandu_list_routes", null, new Error("..."));
|
|
175
|
-
* ```
|
|
176
|
-
*/
|
|
177
|
-
export function createToolResponse(
|
|
178
|
-
toolName: string,
|
|
179
|
-
result: unknown,
|
|
180
|
-
error?: unknown
|
|
181
|
-
): McpToolResponse {
|
|
182
|
-
if (error) {
|
|
183
|
-
const errorResponse = formatMcpError(error, toolName);
|
|
184
|
-
return {
|
|
185
|
-
content: [
|
|
186
|
-
{
|
|
187
|
-
type: "text",
|
|
188
|
-
text: JSON.stringify(errorResponse, null, 2),
|
|
189
|
-
},
|
|
190
|
-
],
|
|
191
|
-
isError: true,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
content: [
|
|
197
|
-
{
|
|
198
|
-
type: "text",
|
|
199
|
-
text: JSON.stringify(result, null, 2),
|
|
200
|
-
},
|
|
201
|
-
],
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* 에러 응답인지 확인
|
|
207
|
-
*/
|
|
208
|
-
export function isErrorResponse(response: McpToolResponse): boolean {
|
|
209
|
-
return response.isError === true;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* 에러 응답에서 McpErrorResponse 추출
|
|
214
|
-
*/
|
|
215
|
-
export function extractErrorFromResponse(response: McpToolResponse): McpErrorResponse | null {
|
|
216
|
-
if (!response.isError) return null;
|
|
217
|
-
|
|
218
|
-
try {
|
|
219
|
-
const text = response.content[0]?.text;
|
|
220
|
-
if (!text) return null;
|
|
221
|
-
return JSON.parse(text) as McpErrorResponse;
|
|
222
|
-
} catch {
|
|
223
|
-
return null;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* 에러 로깅 헬퍼
|
|
229
|
-
*/
|
|
230
|
-
export function logToolError(
|
|
231
|
-
toolName: string,
|
|
232
|
-
error: unknown,
|
|
233
|
-
args?: Record<string, unknown>
|
|
234
|
-
): void {
|
|
235
|
-
const info = extractErrorInfo(error);
|
|
236
|
-
|
|
237
|
-
console.error(`[MCP:${toolName}] ${info.category.toUpperCase()}: ${info.message}`);
|
|
238
|
-
|
|
239
|
-
if (info.code) {
|
|
240
|
-
console.error(` Code: ${info.code}`);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (args && Object.keys(args).length > 0) {
|
|
244
|
-
console.error(` Args:`, JSON.stringify(args, null, 2));
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (info.context && Object.keys(info.context).length > 0) {
|
|
248
|
-
console.error(` Context:`, info.context);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* MCP Error Handler
|
|
3
|
+
*
|
|
4
|
+
* DNA-007 에러 추출 시스템 기반 MCP 에러 처리
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
extractErrorInfo,
|
|
9
|
+
classifyError,
|
|
10
|
+
serializeError,
|
|
11
|
+
isRetryableError,
|
|
12
|
+
type ErrorCategory,
|
|
13
|
+
type ExtractedErrorInfo,
|
|
14
|
+
} from "@mandujs/core";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* MCP 에러 응답 타입
|
|
18
|
+
*/
|
|
19
|
+
export interface McpErrorResponse {
|
|
20
|
+
/** 에러 메시지 */
|
|
21
|
+
error: string;
|
|
22
|
+
/** 에러 코드 (있는 경우) */
|
|
23
|
+
code?: string;
|
|
24
|
+
/** 에러 카테고리 (DNA-007) */
|
|
25
|
+
category: ErrorCategory;
|
|
26
|
+
/** 재시도 가능 여부 */
|
|
27
|
+
retryable: boolean;
|
|
28
|
+
/** 추가 컨텍스트 */
|
|
29
|
+
context?: Record<string, unknown>;
|
|
30
|
+
/** 복구 제안 */
|
|
31
|
+
suggestion?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* MCP 도구 응답 타입
|
|
36
|
+
*/
|
|
37
|
+
export interface McpToolResponse {
|
|
38
|
+
content: Array<{ type: string; text: string }>;
|
|
39
|
+
isError?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 에러를 MCP 응답 형식으로 변환
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* try {
|
|
48
|
+
* await tool.execute(args);
|
|
49
|
+
* } catch (err) {
|
|
50
|
+
* const response = formatMcpError(err, "mandu_guard_check");
|
|
51
|
+
* // { error: "...", code: "...", category: "validation", ... }
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function formatMcpError(err: unknown, toolName?: string): McpErrorResponse {
|
|
56
|
+
const info = extractErrorInfo(err);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
error: info.message,
|
|
60
|
+
code: info.code,
|
|
61
|
+
category: info.category,
|
|
62
|
+
retryable: isRetryableError(err),
|
|
63
|
+
context: {
|
|
64
|
+
...info.context,
|
|
65
|
+
toolName,
|
|
66
|
+
errorName: info.name,
|
|
67
|
+
},
|
|
68
|
+
suggestion: generateSuggestion(info, toolName),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 에러 카테고리 및 도구별 복구 제안 생성
|
|
74
|
+
*/
|
|
75
|
+
function generateSuggestion(info: ExtractedErrorInfo, toolName?: string): string | undefined {
|
|
76
|
+
// 도구별 특화 제안
|
|
77
|
+
if (toolName) {
|
|
78
|
+
const toolSuggestion = getToolSpecificSuggestion(toolName, info);
|
|
79
|
+
if (toolSuggestion) return toolSuggestion;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 카테고리별 일반 제안
|
|
83
|
+
switch (info.category) {
|
|
84
|
+
case "network":
|
|
85
|
+
return "네트워크 연결을 확인하고 다시 시도해주세요.";
|
|
86
|
+
|
|
87
|
+
case "timeout":
|
|
88
|
+
return "요청 시간이 초과되었습니다. 잠시 후 다시 시도해주세요.";
|
|
89
|
+
|
|
90
|
+
case "auth":
|
|
91
|
+
return "인증 정보를 확인해주세요.";
|
|
92
|
+
|
|
93
|
+
case "validation":
|
|
94
|
+
return "입력 값을 확인해주세요. 필수 파라미터가 누락되었거나 형식이 올바르지 않을 수 있습니다.";
|
|
95
|
+
|
|
96
|
+
case "config":
|
|
97
|
+
return "설정 파일(mandu.config.ts)을 확인해주세요.";
|
|
98
|
+
|
|
99
|
+
case "system":
|
|
100
|
+
if (info.code === "ENOENT") {
|
|
101
|
+
const path = info.context?.path ?? "unknown";
|
|
102
|
+
return `파일 또는 디렉토리를 찾을 수 없습니다: ${path}`;
|
|
103
|
+
}
|
|
104
|
+
if (info.code === "EACCES" || info.code === "EPERM") {
|
|
105
|
+
return "파일 접근 권한을 확인해주세요.";
|
|
106
|
+
}
|
|
107
|
+
return "시스템 리소스를 확인해주세요.";
|
|
108
|
+
|
|
109
|
+
case "external":
|
|
110
|
+
return "외부 서비스에 문제가 있습니다. 잠시 후 다시 시도해주세요.";
|
|
111
|
+
|
|
112
|
+
case "internal":
|
|
113
|
+
return "내부 오류가 발생했습니다. 문제가 지속되면 이슈를 보고해주세요.";
|
|
114
|
+
|
|
115
|
+
default:
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 도구별 특화된 에러 제안
|
|
122
|
+
*/
|
|
123
|
+
function getToolSpecificSuggestion(toolName: string, info: ExtractedErrorInfo): string | undefined {
|
|
124
|
+
// spec 관련 도구
|
|
125
|
+
if (toolName.startsWith("mandu_") && toolName.includes("route")) {
|
|
126
|
+
if (info.code === "ENOENT") {
|
|
127
|
+
return "routes.manifest.json 파일이 없습니다. `mandu init`을 먼저 실행해주세요.";
|
|
128
|
+
}
|
|
129
|
+
if (info.message.includes("not found")) {
|
|
130
|
+
return "해당 라우트를 찾을 수 없습니다. `mandu_list_routes`로 존재하는 라우트를 확인해주세요.";
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// guard 관련 도구
|
|
135
|
+
if (toolName === "mandu_guard_check") {
|
|
136
|
+
if (info.category === "config") {
|
|
137
|
+
return "Guard 설정을 확인해주세요. mandu.config.ts의 guard 섹션을 검토해주세요.";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// contract 관련 도구
|
|
142
|
+
if (toolName.includes("contract")) {
|
|
143
|
+
if (info.category === "validation") {
|
|
144
|
+
return "Contract 스키마가 올바른지 확인해주세요. Zod 스키마 문법을 확인해주세요.";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// generate 관련 도구
|
|
149
|
+
if (toolName === "mandu_generate") {
|
|
150
|
+
if (info.code === "EEXIST") {
|
|
151
|
+
return "파일이 이미 존재합니다. 덮어쓰려면 force 옵션을 사용해주세요.";
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// transaction 관련 도구
|
|
156
|
+
if (toolName.includes("tx") || toolName.includes("transaction")) {
|
|
157
|
+
if (info.message.includes("no active")) {
|
|
158
|
+
return "활성화된 트랜잭션이 없습니다. `mandu_begin`으로 트랜잭션을 시작해주세요.";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 도구 실행 결과를 MCP 응답으로 변환
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* // 성공 응답
|
|
171
|
+
* const response = createToolResponse("mandu_list_routes", { routes: [...] });
|
|
172
|
+
*
|
|
173
|
+
* // 에러 응답
|
|
174
|
+
* const response = createToolResponse("mandu_list_routes", null, new Error("..."));
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export function createToolResponse(
|
|
178
|
+
toolName: string,
|
|
179
|
+
result: unknown,
|
|
180
|
+
error?: unknown
|
|
181
|
+
): McpToolResponse {
|
|
182
|
+
if (error) {
|
|
183
|
+
const errorResponse = formatMcpError(error, toolName);
|
|
184
|
+
return {
|
|
185
|
+
content: [
|
|
186
|
+
{
|
|
187
|
+
type: "text",
|
|
188
|
+
text: JSON.stringify(errorResponse, null, 2),
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
isError: true,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
content: [
|
|
197
|
+
{
|
|
198
|
+
type: "text",
|
|
199
|
+
text: JSON.stringify(result, null, 2),
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 에러 응답인지 확인
|
|
207
|
+
*/
|
|
208
|
+
export function isErrorResponse(response: McpToolResponse): boolean {
|
|
209
|
+
return response.isError === true;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 에러 응답에서 McpErrorResponse 추출
|
|
214
|
+
*/
|
|
215
|
+
export function extractErrorFromResponse(response: McpToolResponse): McpErrorResponse | null {
|
|
216
|
+
if (!response.isError) return null;
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const text = response.content[0]?.text;
|
|
220
|
+
if (!text) return null;
|
|
221
|
+
return JSON.parse(text) as McpErrorResponse;
|
|
222
|
+
} catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* 에러 로깅 헬퍼
|
|
229
|
+
*/
|
|
230
|
+
export function logToolError(
|
|
231
|
+
toolName: string,
|
|
232
|
+
error: unknown,
|
|
233
|
+
args?: Record<string, unknown>
|
|
234
|
+
): void {
|
|
235
|
+
const info = extractErrorInfo(error);
|
|
236
|
+
|
|
237
|
+
console.error(`[MCP:${toolName}] ${info.category.toUpperCase()}: ${info.message}`);
|
|
238
|
+
|
|
239
|
+
if (info.code) {
|
|
240
|
+
console.error(` Code: ${info.code}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (args && Object.keys(args).length > 0) {
|
|
244
|
+
console.error(` Args:`, JSON.stringify(args, null, 2));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (info.context && Object.keys(info.context).length > 0) {
|
|
248
|
+
console.error(` Context:`, info.context);
|
|
249
|
+
}
|
|
250
|
+
}
|
package/src/executor/index.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Executor
|
|
3
|
-
*
|
|
4
|
-
* 도구 실행 및 에러 처리
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export {
|
|
8
|
-
formatMcpError,
|
|
9
|
-
createToolResponse,
|
|
10
|
-
isErrorResponse,
|
|
11
|
-
extractErrorFromResponse,
|
|
12
|
-
logToolError,
|
|
13
|
-
type McpErrorResponse,
|
|
14
|
-
type McpToolResponse,
|
|
15
|
-
} from "./error-handler.js";
|
|
16
|
-
|
|
17
|
-
export {
|
|
18
|
-
ToolExecutor,
|
|
19
|
-
createToolExecutor,
|
|
20
|
-
type ToolExecutorOptions,
|
|
21
|
-
type ExecutionResult,
|
|
22
|
-
} from "./tool-executor.js";
|
|
1
|
+
/**
|
|
2
|
+
* MCP Executor
|
|
3
|
+
*
|
|
4
|
+
* 도구 실행 및 에러 처리
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
formatMcpError,
|
|
9
|
+
createToolResponse,
|
|
10
|
+
isErrorResponse,
|
|
11
|
+
extractErrorFromResponse,
|
|
12
|
+
logToolError,
|
|
13
|
+
type McpErrorResponse,
|
|
14
|
+
type McpToolResponse,
|
|
15
|
+
} from "./error-handler.js";
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
ToolExecutor,
|
|
19
|
+
createToolExecutor,
|
|
20
|
+
type ToolExecutorOptions,
|
|
21
|
+
type ExecutionResult,
|
|
22
|
+
} from "./tool-executor.js";
|