@mandujs/mcp 0.37.2 → 0.37.3

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.37.2",
3
+ "version": "0.37.3",
4
4
  "description": "Mandu MCP Server - Agent-native interface for Mandu framework operations",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -34,7 +34,7 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@mandujs/core": "^0.54.2",
37
+ "@mandujs/core": "^0.54.3",
38
38
  "@mandujs/ate": "^0.26.1",
39
39
  "@mandujs/skills": "^0.20.1",
40
40
  "@modelcontextprotocol/sdk": "^1.25.3"
@@ -377,21 +377,21 @@ Island Hydration은 페이지의 일부분만 클라이언트에서 인터랙티
377
377
  | \`idle\` | 브라우저 유휴 시 | 비중요 기능 |
378
378
  | \`interaction\` | 사용자 상호작용 시 | 클릭해야 활성화 |
379
379
 
380
- ## Island 만들기
381
-
382
- ### 1. 클라이언트 컴포넌트 작성
383
-
384
- \`\`\`tsx
385
- // app/counter/client.tsx
386
-
387
- "use client";
388
-
389
- import { useState } from "react";
390
-
391
- export default function Counter({ initial = 0 }: { initial?: number }) {
392
- const [count, setCount] = useState(initial);
393
-
394
- return (
380
+ ## Inline client region 만들기
381
+
382
+ ### 1. 클라이언트 컴포넌트 작성
383
+
384
+ \`\`\`tsx
385
+ // app/counter/client.tsx
386
+
387
+ "use client";
388
+
389
+ import { useState } from "react";
390
+
391
+ export function Counter({ initial = 0 }: { initial?: number }) {
392
+ const [count, setCount] = useState(initial);
393
+
394
+ return (
395
395
  <div>
396
396
  <p>Count: {count}</p>
397
397
  <button onClick={() => setCount(c => c - 1)}>-</button>
@@ -399,34 +399,43 @@ export default function Counter({ initial = 0 }: { initial?: number }) {
399
399
  </div>
400
400
  );
401
401
  }
402
- \`\`\`
403
-
404
- ### 2. 페이지에서 사용
405
-
406
- \`\`\`tsx
407
- // app/counter/page.tsx
408
-
409
- import Counter from "./client";
410
-
411
- export default function CounterPage() {
412
- return (
402
+ \`\`\`
403
+
404
+ ### 2. 서버 페이지에서 partial로 사용
405
+
406
+ \`\`\`tsx
407
+ // app/counter/page.tsx
408
+
409
+ import { partial } from "@mandujs/core/client";
410
+ import { Counter } from "./client";
411
+
412
+ const CounterPartial = partial({
413
+ component: Counter,
414
+ priority: "visible",
415
+ });
416
+
417
+ export default function CounterPage() {
418
+ return (
413
419
  <div>
414
420
  <h1>Counter Demo</h1>
415
421
  <p>이 텍스트는 정적 HTML입니다.</p>
416
-
417
- {/* 이 부분만 hydration됩니다 */}
418
- <Counter initial={10} />
419
- </div>
420
- );
421
- }
422
- \`\`\`
423
-
424
- ## Mandu.island() API
425
-
426
- 고급 Island 패턴을 위한 API:
427
-
428
- \`\`\`typescript
429
- // spec/slots/todos.client.ts
422
+
423
+ {/* 이 부분만 hydration됩니다 */}
424
+ <CounterPartial.Render initial={10} />
425
+ </div>
426
+ );
427
+ }
428
+ \`\`\`
429
+
430
+ ## Mandu.island() API
431
+
432
+ 고급 page-level Island 패턴을 위한 API입니다. \`Mandu.island()\`는 단일
433
+ 정의 객체만 받습니다. \`island("visible", Component)\` 형태는 지원하지
434
+ 않으며, 서버 페이지 안에 inline으로 렌더링할 영역은 \`partial()\`을
435
+ 사용하세요.
436
+
437
+ \`\`\`typescript
438
+ // spec/slots/todos.client.ts
430
439
 
431
440
  import { Mandu } from "@mandujs/core/client";
432
441
  import { useState, useCallback } from "react";
@@ -57,17 +57,25 @@ Reference these guidelines when:
57
57
  - `hydration-data-server` - Access server data with useServerData
58
58
  - `hydration-data-event` - Communicate between Islands with useIslandEvent
59
59
 
60
- ## Hydration Strategies
61
-
62
- | Strategy | Description | Use Case |
63
- |----------|-------------|----------|
64
- | `none` | No JavaScript | Pure static pages |
65
- | `island` | Partial hydration (default) | Static + interactive mix |
66
- | `full` | Full hydration | SPA-style pages |
67
-
68
- ## Client Hooks
69
-
70
- ```typescript
60
+ ## Hydration Strategies
61
+
62
+ | Strategy | Description | Use Case |
63
+ |----------|-------------|----------|
64
+ | `none` | No JavaScript | Pure static pages |
65
+ | `island` | Partial hydration (default) | Static + interactive mix |
66
+ | `full` | Full hydration | SPA-style pages |
67
+
68
+ ## Runtime API Constraints
69
+
70
+ - `island()` / `Mandu.island()` takes one definition object: `island({ setup, render })`.
71
+ - Do not call `island("visible", Component)`; use `wrapComponent(Component)` for a simple page-level island wrapper.
72
+ - Islands are page-level client bundles. Do not render them as inline JSX like `<MyIsland />`.
73
+ - For an embedded interactive region inside a server page, use `partial()`: put it in `*.partial.tsx`, export `partial({ id, component })`, and render the returned `.Render` component from the server page.
74
+ - A server page that renders partials must opt into hydration, for example `export const hydration = { strategy: "island", priority: "visible", preload: false }`.
75
+
76
+ ## Client Hooks
77
+
78
+ ```typescript
71
79
  import {
72
80
  useServerData,
73
81
  useHydrated,
@@ -5,13 +5,60 @@ impactDescription: Proper Island structure
5
5
  tags: hydration, island, setup
6
6
  ---
7
7
 
8
- ## Use Mandu.island() with Setup Function
9
-
10
- For complex Islands, use `Mandu.island()` API to separate state logic from rendering.
11
-
12
- **Incorrect (mixed concerns):**
13
-
14
- ```tsx
8
+ ## Use Mandu.island() with Setup Function
9
+
10
+ For complex Islands, use `Mandu.island()` API to separate state logic from rendering.
11
+ `Mandu.island()` takes a single definition object. The shorthand
12
+ `island("visible", Component)` is not a supported Mandu runtime API.
13
+
14
+ Islands are page-level client bundles. They are discovered and rendered by
15
+ the framework wrapper, not embedded directly as inline JSX. Use `partial()`
16
+ when a server page needs an inline interactive region.
17
+
18
+ ```tsx
19
+ // Simple page-level island wrapper
20
+ "use client";
21
+
22
+ import { wrapComponent } from "@mandujs/core/client";
23
+
24
+ function Counter() {
25
+ return <button>Count</button>;
26
+ }
27
+
28
+ export default wrapComponent(Counter);
29
+ ```
30
+
31
+ ```tsx
32
+ // Inline client region inside a server-rendered page
33
+ // app/Counter.partial.tsx
34
+ import { partial } from "@mandujs/core/client";
35
+ import { Counter } from "./counter.client";
36
+
37
+ export default partial({
38
+ id: "Counter",
39
+ component: Counter,
40
+ priority: "visible",
41
+ });
42
+ ```
43
+
44
+ ```tsx
45
+ // app/page.tsx
46
+ import CounterPartial from "./Counter.partial";
47
+
48
+ export const hydration = {
49
+ strategy: "island",
50
+ priority: "visible",
51
+ preload: false,
52
+ };
53
+
54
+ export default function Page() {
55
+ return <CounterPartial.Render initial={0} />;
56
+ }
57
+ ```
58
+
59
+ **Incorrect (mixed concerns):**
60
+
61
+ ```tsx
15
62
  "use client";
16
63
 
17
64
  export default function TodoList({ initialTodos }) {
@@ -18,43 +18,66 @@ Set hydration priority based on when the component needs to be interactive.
18
18
  | `idle` | Browser idle | Non-critical features (analytics widgets) |
19
19
  | `interaction` | User action | Click-to-activate components |
20
20
 
21
- ## Examples
22
-
23
- ### Immediate: Always-visible interactions
24
-
25
- ```tsx
26
- // Header with navigation - needs to work immediately
27
- <Island priority="immediate">
28
- <HeaderNav />
29
- </Island>
30
- ```
31
-
32
- ### Visible: Below-fold content (default)
33
-
34
- ```tsx
35
- // Comments section - load when scrolled into view
36
- <Island priority="visible">
37
- <CommentsSection postId={postId} />
38
- </Island>
39
- ```
40
-
41
- ### Idle: Background features
42
-
43
- ```tsx
44
- // Chat widget - can wait until browser is idle
45
- <Island priority="idle">
46
- <ChatWidget />
47
- </Island>
48
- ```
49
-
50
- ### Interaction: On-demand activation
51
-
52
- ```tsx
53
- // Video player - only hydrate when user clicks play
54
- <Island priority="interaction">
55
- <VideoPlayer videoId={videoId} />
56
- </Island>
57
- ```
21
+ ## Examples
22
+
23
+ Inline client regions live in `*.partial.tsx` files and use
24
+ `partial({ id, component, priority })`, then render the returned `.Render`
25
+ component from the server page. The page must export a non-`none` hydration
26
+ config.
27
+
28
+ ### Immediate: Always-visible interactions
29
+
30
+ ```tsx
31
+ // Header with navigation - needs to work immediately
32
+ import { partial } from "@mandujs/core/client";
33
+
34
+ const HeaderNavPartial = partial({
35
+ id: "HeaderNav",
36
+ component: HeaderNav,
37
+ priority: "immediate",
38
+ });
39
+
40
+ <HeaderNavPartial.Render />
41
+ ```
42
+
43
+ ### Visible: Below-fold content (default)
44
+
45
+ ```tsx
46
+ // Comments section - load when scrolled into view
47
+ const CommentsPartial = partial({
48
+ id: "Comments",
49
+ component: CommentsSection,
50
+ priority: "visible",
51
+ });
52
+
53
+ <CommentsPartial.Render postId={postId} />
54
+ ```
55
+
56
+ ### Idle: Background features
57
+
58
+ ```tsx
59
+ // Chat widget - can wait until browser is idle
60
+ const ChatPartial = partial({
61
+ id: "Chat",
62
+ component: ChatWidget,
63
+ priority: "idle",
64
+ });
65
+
66
+ <ChatPartial.Render />
67
+ ```
68
+
69
+ ### Interaction: On-demand activation
70
+
71
+ ```tsx
72
+ // Video player - only hydrate when user clicks play
73
+ const VideoPartial = partial({
74
+ id: "Video",
75
+ component: VideoPlayer,
76
+ priority: "interaction",
77
+ });
78
+
79
+ <VideoPartial.Render videoId={videoId} />
80
+ ```
58
81
 
59
82
  ## Performance Impact
60
83
 
@@ -469,24 +469,30 @@ export default function Counter({ initial = 0, step = 1 }: CounterProps) {
469
469
  }
470
470
  \`\`\`
471
471
 
472
- ## Step 2: 페이지에서 사용
473
-
474
- \`\`\`tsx
475
- // app/counter/page.tsx
476
-
477
- import Counter from "./client";
478
-
479
- export default function CounterPage() {
480
- return (
472
+ ## Step 2: 서버 페이지에서 partial로 사용
473
+
474
+ \`\`\`tsx
475
+ // app/counter/page.tsx
476
+
477
+ import { partial } from "@mandujs/core/client";
478
+ import Counter from "./client";
479
+
480
+ const CounterPartial = partial({
481
+ component: Counter,
482
+ priority: "visible",
483
+ });
484
+
485
+ export default function CounterPage() {
486
+ return (
481
487
  <div style={{ padding: "20px" }}>
482
488
  <h1>Counter Demo</h1>
483
489
  <p>아래 카운터는 클라이언트에서 hydration됩니다.</p>
484
-
485
- {/* Island 컴포넌트 */}
486
- <Counter initial={10} step={5} />
487
-
488
- <p style={{ marginTop: "20px", color: "#666" }}>
489
- 이 텍스트는 정적 HTML입니다.
490
+
491
+ {/* Inline client region */}
492
+ <CounterPartial.Render initial={10} step={5} />
493
+
494
+ <p style={{ marginTop: "20px", color: "#666" }}>
495
+ 이 텍스트는 정적 HTML입니다.
490
496
  </p>
491
497
  </div>
492
498
  );
@@ -618,10 +624,13 @@ export default function UserList() {
618
624
 
619
625
  ## Mandu.island() API (고급)
620
626
 
621
- 서버 데이터와 클라이언트 상태를 분리하려면:
622
-
623
- \`\`\`typescript
624
- // spec/slots/todos.client.ts
627
+ 서버 데이터와 클라이언트 상태를 분리하는 page-level Island가 필요하면
628
+ \`Mandu.island({ setup, render })\`를 사용합니다. \`island("visible",
629
+ Component)\`는 지원하지 않습니다. 서버 페이지 안에 inline으로 렌더링할
630
+ 영역은 \`partial()\`을 사용하세요.
631
+
632
+ \`\`\`typescript
633
+ // spec/slots/todos.client.ts
625
634
 
626
635
  import { Mandu } from "@mandujs/core/client";
627
636
  import { useState } from "react";
@@ -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
- loadManifest,
5
- runGuardCheck,
6
- runAutoCorrect,
7
- type GeneratedMap,
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
- } from "@mandujs/core";
18
- import { getProjectPaths, readJsonFile, readConfig } from "../utils/project.js";
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 guard checks to validate spec integrity, generated files, and slot files. Set typeAware=true to additionally run `oxlint --type-aware` (tsgolint) and merge its results.",
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
- // Run guard check
143
- const checkResult = await runGuardCheck(manifestResult.data, projectRoot);
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 projectConfig: Awaited<ReturnType<typeof readConfig>> | undefined;
149
- try {
150
- projectConfig = await readConfig(projectRoot);
151
- } catch {
152
- projectConfig = undefined;
153
- }
154
- const typeAwareCfg = (
155
- projectConfig?.guard as
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
- if (checkResult.passed) {
182
- return {
183
- passed: typeAwareResponse
184
- ? (typeAwareResponse.violations as Array<{ severity: string }>).filter(
185
- (v) => v.severity === "error",
186
- ).length === 0
187
- : true,
188
- violations: [],
189
- message: "All guard checks passed",
190
- relatedSkills: ["mandu-guard-guide", "mandu-debug"],
191
- ...(typeAwareResponse ? { typeAware: typeAwareResponse } : {}),
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: autoCorrectResult.fixed,
205
- violations: autoCorrectResult.remainingViolations,
206
- autoCorrect: {
207
- attempted: true,
208
- fixed: autoCorrectResult.fixed,
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
- ...(typeAwareResponse ? { typeAware: typeAwareResponse } : {}),
215
- };
216
- }
217
-
218
- return {
219
- passed: false,
220
- violations: checkResult.violations.map((v) => ({
221
- ruleId: v.ruleId,
222
- file: v.file,
223
- message: v.message,
224
- suggestion: v.suggestion,
225
- })),
226
- message: `Found ${checkResult.violations.length} violation(s)`,
227
- tip: "Use autoCorrect: true to attempt automatic fixes",
228
- relatedSkills: ["mandu-guard-guide", "mandu-debug"],
229
- ...(typeAwareResponse ? { typeAware: typeAwareResponse } : {}),
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