argsbarg 7.1.2 → 7.1.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.md +7 -7
  3. package/docs/README.md +1 -1
  4. package/docs/cli-program.md +1 -1
  5. package/docs/configure.md +2 -2
  6. package/docs/mcp.md +20 -0
  7. package/docs/output-schema.md +6 -0
  8. package/examples/full-example/AGENTS.md +1 -1
  9. package/examples/full-example/bun.lock +83 -1
  10. package/examples/full-example/justfile +5 -3
  11. package/examples/full-example-json/AGENTS.md +1 -1
  12. package/examples/full-example-json/README.md +1 -0
  13. package/examples/full-example-json/bun.lock +83 -1
  14. package/examples/full-example-json/docs/cli-schema.json +284 -9
  15. package/examples/full-example-json/docs/cli.md +236 -18
  16. package/examples/full-example-json/docs/http.md +1 -0
  17. package/examples/full-example-json/docs/mcp.md +18 -0
  18. package/examples/full-example-json/docs/openapi.json +155 -0
  19. package/examples/full-example-json/justfile +5 -3
  20. package/examples/full-example-json/src/commands/shape-area/__generated__/ShapeAreaInputSchema.json +59 -0
  21. package/examples/full-example-json/src/commands/shape-area/__generated__/index.ts +5 -0
  22. package/examples/full-example-json/src/commands/shape-area/command.test.ts +34 -0
  23. package/examples/full-example-json/src/commands/shape-area/command.ts +31 -0
  24. package/examples/full-example-json/src/commands/shape-area/types.ts +28 -0
  25. package/examples/full-example-json/src/program.ts +2 -1
  26. package/examples/mcp-plugin/.claude-plugin/plugin.json +7 -1
  27. package/examples/mcp-plugin/.cursor-plugin/plugin.json +7 -1
  28. package/examples/mcp-plugin/AGENTS.md +12 -1
  29. package/examples/mcp-plugin/README.md +18 -9
  30. package/examples/mcp-plugin/bun.lock +83 -1
  31. package/examples/mcp-plugin/dist/mcp-plugin.mjs +385 -0
  32. package/examples/mcp-plugin/justfile +17 -12
  33. package/examples/mcp-plugin/package.json +2 -5
  34. package/examples/mcp-plugin/scripts/release.ts +17 -30
  35. package/examples/mcp-plugin/src/index.ts +0 -1
  36. package/package.json +1 -1
  37. package/src/cli-tool/create.test.ts +14 -0
  38. package/src/cli-tool/create.ts +9 -0
  39. package/src/cli-tool/main.ts +1 -1
  40. package/src/cli-tool/schemagen/run.ts +41 -2
  41. package/src/cli-tool/schemagen/schemagen.test.ts +87 -2
  42. package/src/config/validate.ts +13 -31
  43. package/src/core/json-pointer.ts +46 -0
  44. package/src/core/validate.ts +64 -1
  45. package/src/headless/tool-call.test.ts +74 -2
  46. package/src/headless/tool-call.ts +44 -22
  47. package/src/http/schema-deref.ts +1 -23
  48. package/src/mcp/tools.test.ts +137 -3
  49. package/src/mcp/tools.ts +50 -5
  50. package/examples/mcp-plugin/.mcp.json +0 -6
  51. package/examples/mcp-plugin/bunfig.toml +0 -3
  52. package/examples/mcp-plugin/mcp.json +0 -8
  53. package/examples/mcp-plugin/scripts/mcp.mjs +0 -11106
  54. package/examples/mcp-plugin/src/commands/render-json/__generated__/RenderJsonInputSchema.json +0 -15
  55. package/examples/mcp-plugin/src/commands/render-json/__generated__/index.ts +0 -5
  56. package/examples/mcp-plugin/src/commands/status/__generated__/StatusJsonOutputSchema.json +0 -15
  57. package/examples/mcp-plugin/src/commands/status/__generated__/index.ts +0 -5
  58. package/examples/mcp-plugin/src/commands/workspaces/__generated__/WorkspaceNameInputSchema.json +0 -15
  59. package/examples/mcp-plugin/src/commands/workspaces/__generated__/index.ts +0 -5
@@ -1,7 +1,15 @@
1
- /* Unit tests for shared headless error text (MCP and HTTP). */
1
+ /* Unit tests for shared headless error text (MCP and HTTP) and wrapped MCP tool calls. */
2
2
 
3
3
  import { describe, expect, test } from "bun:test";
4
- import { type HeadlessToolCallFailure, headlessFailureMcpMessage, headlessFailureToHttpResponse } from "./tool-call.ts";
4
+ import { Cli, type CliContext } from "../index.ts";
5
+ import { collectMcpTools } from "../mcp/tools.ts";
6
+ import { requireMcpTool, testProgram } from "../test/fixtures.ts";
7
+ import {
8
+ executeHeadlessToolCall,
9
+ type HeadlessToolCallFailure,
10
+ headlessFailureMcpMessage,
11
+ headlessFailureToHttpResponse,
12
+ } from "./tool-call.ts";
5
13
 
6
14
  /** Builds a failed headless result with the given error message. */
7
15
  function invokeFailure(
@@ -30,3 +38,67 @@ describe("headless error text", () => {
30
38
  expect(body.error).toBe(multiline);
31
39
  });
32
40
  });
41
+
42
+ describe("wrapped MCP tools", () => {
43
+ /** Union input leaf that echoes its inputs; output is an array so structuredContent gets wrapped too. */
44
+ const program = testProgram({
45
+ key: "wrapcall",
46
+ description: "Wrapped call demo.",
47
+ mcpServer: { enabled: true },
48
+ commands: [
49
+ {
50
+ key: "edit",
51
+ description: "Edit.",
52
+ kind: "document",
53
+ inputSchema: {
54
+ anyOf: [{ $ref: "#/definitions/Append" }, { $ref: "#/definitions/Replace" }],
55
+ definitions: {
56
+ Append: {
57
+ type: "object",
58
+ properties: { kind: { const: "append" }, text: { type: "string" } },
59
+ required: ["kind", "text"],
60
+ additionalProperties: false,
61
+ },
62
+ Replace: {
63
+ type: "object",
64
+ properties: { kind: { const: "replace" }, find: { type: "string" }, text: { type: "string" } },
65
+ required: ["kind", "find", "text"],
66
+ additionalProperties: false,
67
+ },
68
+ },
69
+ },
70
+ outputSchema: { type: "array" },
71
+ handler: (ctx: CliContext) => [ctx.inputs],
72
+ },
73
+ ],
74
+ });
75
+ const cli = new Cli(program);
76
+ const tool = requireMcpTool(collectMcpTools(program), "edit");
77
+
78
+ test("unwraps input and wraps structuredContent under result", async () => {
79
+ const result = await executeHeadlessToolCall(cli, tool, { input: { kind: "append", text: "hi" } }, "mcp");
80
+ expect(result.ok).toBe(true);
81
+ if (result.ok) {
82
+ expect(result.mcpResult.structuredContent).toEqual({ result: [{ kind: "append", text: "hi" }] });
83
+ }
84
+ });
85
+
86
+ test("still validates the exact union after unwrapping", async () => {
87
+ const result = await executeHeadlessToolCall(
88
+ cli,
89
+ tool,
90
+ { input: { kind: "append", text: "hi", find: "x" } },
91
+ "mcp",
92
+ );
93
+ expect(result.ok).toBe(false);
94
+ });
95
+
96
+ test("rejects arguments that are not wrapped", async () => {
97
+ const result = await executeHeadlessToolCall(cli, tool, { kind: "append", text: "hi" }, "mcp");
98
+ expect(result.ok).toBe(false);
99
+ if (!result.ok) {
100
+ expect(result.kind).toBe("argv");
101
+ expect(result.message).toContain('single "input" object property');
102
+ }
103
+ });
104
+ });
@@ -10,7 +10,13 @@ import { apiErrorResponse, apiSuccessResponse, stripAnsi } from "../http/result.
10
10
  import { type HttpRouteDef, httpRequestToArgv } from "../http/routes.ts";
11
11
  import { obscureUnexpectedClientMessage } from "../log/emitter.ts";
12
12
  import { buildToolCallSuccessFromResponse } from "../mcp/result.ts";
13
- import { collectMcpTools, type McpToolDef, mcpToolCallToArgv } from "../mcp/tools.ts";
13
+ import {
14
+ collectMcpTools,
15
+ MCP_INPUT_WRAPPER_KEY,
16
+ MCP_OUTPUT_WRAPPER_KEY,
17
+ type McpToolDef,
18
+ mcpToolCallToArgv,
19
+ } from "../mcp/tools.ts";
14
20
  import type { Cli, CliInvokeResult } from "../runtime/cli.ts";
15
21
 
16
22
  /** Outcome of resolving a tool name against the program schema. */
@@ -86,8 +92,31 @@ function noResponseFailure(result: CliInvokeResult): HeadlessToolCallFailure {
86
92
  };
87
93
  }
88
94
 
95
+ /** Pre-invoke argument failure (bad shape or argv conversion error). */
96
+ function argvFailure(
97
+ /** Client-facing error message. */
98
+ message: string,
99
+ ): HeadlessToolCallFailure {
100
+ return { ok: false, kind: "argv", message, exitCode: 1, stdout: "", stderr: "", failureKind: "validation" };
101
+ }
102
+
103
+ /** Returns the leaf input from wrapped tool arguments (`{ input: {...} }`), or `undefined` when malformed. */
104
+ function unwrapToolArgs(
105
+ /** Raw tools/call arguments. */
106
+ args: Record<string, unknown>,
107
+ ): Record<string, unknown> | undefined {
108
+ const inner = args[MCP_INPUT_WRAPPER_KEY];
109
+ const onlyWrapperKey = Object.keys(args).every((key) => key === MCP_INPUT_WRAPPER_KEY);
110
+ if (!onlyWrapperKey || typeof inner !== "object" || inner === null || Array.isArray(inner)) {
111
+ return undefined;
112
+ }
113
+ return inner as Record<string, unknown>;
114
+ }
115
+
89
116
  /**
90
117
  * Converts flat tool arguments to argv and invokes the leaf handler headlessly.
118
+ * Wrapped tools (see `wrapMcpRootSchema`) receive `{ input: {...} }`, unwrapped here, and return
119
+ * `structuredContent` wrapped as `{ result: ... }` to match their `outputSchema`.
91
120
  */
92
121
  export async function executeHeadlessToolCall(
93
122
  cli: Cli,
@@ -96,20 +125,19 @@ export async function executeHeadlessToolCall(
96
125
  invocation: CliInvocation,
97
126
  mcp?: { rpcMethod: string; toolName?: string; requestId: string },
98
127
  ): Promise<HeadlessToolCallResult> {
99
- const argvResult = mcpToolCallToArgv(cli.program, tool, args);
128
+ const leafArgs = tool.inputWrapped ? unwrapToolArgs(args) : args;
129
+ if (leafArgs === undefined) {
130
+ return argvFailure(
131
+ `Tool arguments must be an object with a single "${MCP_INPUT_WRAPPER_KEY}" object property (see inputSchema)`,
132
+ );
133
+ }
134
+
135
+ const argvResult = mcpToolCallToArgv(cli.program, tool, leafArgs);
100
136
  if ("error" in argvResult) {
101
- return {
102
- ok: false,
103
- kind: "argv",
104
- message: argvResult.error,
105
- exitCode: 1,
106
- stdout: "",
107
- stderr: "",
108
- failureKind: "validation",
109
- };
137
+ return argvFailure(argvResult.error);
110
138
  }
111
139
 
112
- const invokeResult = await cli.invoke(argvResult, { invocation, toolArgs: args, mcp });
140
+ const invokeResult = await cli.invoke(argvResult, { invocation, toolArgs: leafArgs, mcp });
113
141
  if (invokeResult.kind === "help") {
114
142
  return invokeFailure(invokeResult);
115
143
  }
@@ -119,7 +147,9 @@ export async function executeHeadlessToolCall(
119
147
  return {
120
148
  ok: true,
121
149
  response: invokeResult.response,
122
- mcpResult,
150
+ mcpResult: tool.outputWrapped
151
+ ? { ...mcpResult, structuredContent: { [MCP_OUTPUT_WRAPPER_KEY]: mcpResult.structuredContent } }
152
+ : mcpResult,
123
153
  };
124
154
  }
125
155
 
@@ -143,15 +173,7 @@ export async function executeHttpRouteCall(
143
173
  ): Promise<HeadlessToolCallResult> {
144
174
  const argvResult = httpRequestToArgv(cli.program, route, pathParams, query, body);
145
175
  if ("error" in argvResult) {
146
- return {
147
- ok: false,
148
- kind: "argv",
149
- message: argvResult.error,
150
- exitCode: 1,
151
- stdout: "",
152
- stderr: "",
153
- failureKind: "validation",
154
- };
176
+ return argvFailure(argvResult.error);
155
177
  }
156
178
 
157
179
  const toolArgs = { ...body, ...query, ...pathParams };
@@ -2,34 +2,12 @@
2
2
  Inline JSON Schema $ref dereferencing for OpenAPI embedding.
3
3
  */
4
4
 
5
- function decodeJsonPointerSegment(segment: string): string {
6
- return segment.replace(/~1/g, "/").replace(/~0/g, "~");
7
- }
5
+ import { resolveJsonPointer } from "../core/json-pointer.ts";
8
6
 
9
7
  function isPlainObject(value: unknown): value is Record<string, unknown> {
10
8
  return value !== null && typeof value === "object" && !Array.isArray(value);
11
9
  }
12
10
 
13
- /** Resolves a same-document JSON Pointer (`#/definitions/Foo`). */
14
- function resolveJsonPointer(root: Record<string, unknown>, ref: string): unknown {
15
- if (!ref.startsWith("#/")) {
16
- return undefined;
17
- }
18
- const segments = ref
19
- .slice(2)
20
- .split("/")
21
- .filter((segment) => segment.length > 0)
22
- .map(decodeJsonPointerSegment);
23
- let current: unknown = root;
24
- for (const segment of segments) {
25
- if (!isPlainObject(current)) {
26
- return undefined;
27
- }
28
- current = current[segment];
29
- }
30
- return current;
31
- }
32
-
33
11
  function derefValue(value: unknown, root: Record<string, unknown>, resolving: Set<string>): unknown {
34
12
  if (Array.isArray(value)) {
35
13
  return value.map((item) => derefValue(item, root, resolving));
@@ -1,13 +1,21 @@
1
1
  /*
2
- Tests for mcp/tools module: MCP tool derivation, size reporting, and per-leaf MCP-only notes.
2
+ Tests for mcp/tools module: MCP tool derivation, size reporting, per-leaf MCP-only notes,
3
+ object-root schema wrapping, and the MCP tool schema startup check.
3
4
  */
4
5
 
5
6
  import { describe, expect, test } from "bun:test";
6
7
  import { cliPresentationRoot } from "../builtins/presentation.ts";
7
8
  import { CliOptionKind } from "../core/types.ts";
9
+ import { cliValidateProgram } from "../core/validate.ts";
8
10
  import { cliHelpRender } from "../help.ts";
9
- import { testProgram } from "../test/fixtures.ts";
10
- import { collectMcpTools, DEFAULT_MCP_SIZE_LIMITS, mcpSizeReport } from "./tools.ts";
11
+ import { requireMcpTool, testProgram } from "../test/fixtures.ts";
12
+ import {
13
+ collectMcpTools,
14
+ DEFAULT_MCP_SIZE_LIMITS,
15
+ MCP_INPUT_WRAPPER_KEY,
16
+ mcpSizeReport,
17
+ wrapMcpRootSchema,
18
+ } from "./tools.ts";
11
19
 
12
20
  describe("mcpSizeReport", () => {
13
21
  test("measures description and pretty definition against a hand-built JSON.stringify", () => {
@@ -156,3 +164,129 @@ describe("mcpTool.notes", () => {
156
164
  expect(help).toContain("Original CLI notes.");
157
165
  });
158
166
  });
167
+
168
+ /** Discriminated-union input: `anyOf` root with `$ref` branches, as ts-json-schema-generator writes it. */
169
+ const unionInputSchema: Record<string, unknown> = {
170
+ $schema: "http://json-schema.org/draft-07/schema#",
171
+ description: "Edit operation.",
172
+ anyOf: [{ $ref: "#/definitions/Append" }, { $ref: "#/definitions/Replace" }],
173
+ definitions: {
174
+ Append: {
175
+ type: "object",
176
+ properties: { kind: { const: "append" }, text: { type: "string" } },
177
+ required: ["kind", "text"],
178
+ additionalProperties: false,
179
+ },
180
+ Replace: {
181
+ type: "object",
182
+ properties: { kind: { const: "replace" }, find: { type: "string" }, text: { type: "string" } },
183
+ required: ["kind", "find", "text"],
184
+ additionalProperties: false,
185
+ },
186
+ },
187
+ };
188
+
189
+ describe("MCP object-root wrapping", () => {
190
+ test("object-rooted schemas pass through unchanged", () => {
191
+ const schema = { type: "object", properties: { a: { type: "string" } } };
192
+ expect(wrapMcpRootSchema(schema, MCP_INPUT_WRAPPER_KEY)).toEqual({ schema, wrapped: false });
193
+ });
194
+
195
+ test("union roots wrap under input with $schema and definitions moved to the new root", () => {
196
+ const { schema, wrapped } = wrapMcpRootSchema(unionInputSchema, MCP_INPUT_WRAPPER_KEY);
197
+ expect(wrapped).toBe(true);
198
+ expect(schema).toEqual({
199
+ $schema: unionInputSchema.$schema,
200
+ type: "object",
201
+ properties: { input: { description: "Edit operation.", anyOf: unionInputSchema.anyOf } },
202
+ required: ["input"],
203
+ additionalProperties: false,
204
+ definitions: unionInputSchema.definitions,
205
+ });
206
+ });
207
+
208
+ test("collectMcpTools wraps non-object input and output schemas and flags them", () => {
209
+ const program = testProgram({
210
+ key: "wrapapp",
211
+ description: "Wrap demo.",
212
+ mcpServer: { enabled: true },
213
+ commands: [
214
+ {
215
+ key: "edit",
216
+ description: "Edit.",
217
+ kind: "document",
218
+ inputSchema: unionInputSchema,
219
+ outputSchema: { type: "array", items: { type: "string" } },
220
+ handler: () => [],
221
+ },
222
+ { key: "plain", description: "Plain.", handler: () => {} },
223
+ ],
224
+ });
225
+ const tools = collectMcpTools(program);
226
+ const edit = requireMcpTool(tools, "edit");
227
+ expect(edit.inputWrapped).toBe(true);
228
+ expect(edit.inputSchema.type).toBe("object");
229
+ expect(edit.outputWrapped).toBe(true);
230
+ expect(edit.outputSchema).toEqual({
231
+ type: "object",
232
+ properties: { result: { type: "array", items: { type: "string" } } },
233
+ required: ["result"],
234
+ additionalProperties: false,
235
+ });
236
+ const plain = requireMcpTool(tools, "plain");
237
+ expect(plain.inputWrapped).toBe(false);
238
+ expect(plain.outputWrapped).toBe(false);
239
+ });
240
+ });
241
+
242
+ describe("MCP tool schema startup check", () => {
243
+ /** Program with one document leaf using `inputSchema`, MCP on or off. */
244
+ function schemaProgram(inputSchema: Record<string, unknown>, mcpEnabled: boolean) {
245
+ return testProgram({
246
+ key: "checkapp",
247
+ description: "Check demo.",
248
+ ...(mcpEnabled ? { mcpServer: { enabled: true } } : {}),
249
+ commands: [{ key: "run", description: "Run.", kind: "document", inputSchema, handler: () => {} }],
250
+ });
251
+ }
252
+
253
+ test("accepts wrapped schemas whose definitions still resolve", () => {
254
+ expect(() => cliValidateProgram(schemaProgram(unionInputSchema, true))).not.toThrow();
255
+ });
256
+
257
+ test("rejects an unresolved local $ref when MCP is enabled", () => {
258
+ const dangling = { $ref: "#/definitions/Missing", definitions: {} };
259
+ expect(() => cliValidateProgram(schemaProgram(dangling, true))).toThrow(
260
+ 'MCP tool "run" inputSchema has an unresolved $ref: #/definitions/Missing',
261
+ );
262
+ });
263
+
264
+ test('rejects $ref "#" in a wrapped schema', () => {
265
+ const recursive = { anyOf: [{ type: "object" }, { type: "array", items: { $ref: "#" } }] };
266
+ expect(() => cliValidateProgram(schemaProgram(recursive, true))).toThrow('uses $ref "#"');
267
+ });
268
+
269
+ test("skips the check when MCP is disabled", () => {
270
+ const dangling = { $ref: "#/definitions/Missing", definitions: {} };
271
+ expect(() => cliValidateProgram(schemaProgram(dangling, false))).not.toThrow();
272
+ });
273
+
274
+ test("skips MCP-hidden leaves", () => {
275
+ const program = testProgram({
276
+ key: "checkapp",
277
+ description: "Check demo.",
278
+ mcpServer: { enabled: true },
279
+ commands: [
280
+ {
281
+ key: "run",
282
+ description: "Run.",
283
+ kind: "document",
284
+ inputSchema: { $ref: "#/definitions/Missing", definitions: {} },
285
+ mcpTool: { hidden: true },
286
+ handler: () => {},
287
+ },
288
+ ],
289
+ });
290
+ expect(() => cliValidateProgram(program)).not.toThrow();
291
+ });
292
+ });
package/src/mcp/tools.ts CHANGED
@@ -49,10 +49,51 @@ export interface McpToolDef {
49
49
  path: string[];
50
50
  /** Leaf command node. */
51
51
  leaf: CliLeaf;
52
- /** JSON Schema for tools/call arguments. */
52
+ /** JSON Schema for tools/call arguments (wrapped under {@link MCP_INPUT_WRAPPER_KEY} when not object-rooted). */
53
53
  inputSchema: Record<string, unknown>;
54
- /** JSON Schema for structured tool results when set on the leaf `mcpTool`. */
54
+ /** True when {@link inputSchema} wraps the leaf schema; tools/call arguments are unwrapped before invoke. */
55
+ inputWrapped: boolean;
56
+ /** JSON Schema for structured tool results (wrapped under {@link MCP_OUTPUT_WRAPPER_KEY} when not object-rooted). */
55
57
  outputSchema?: Record<string, unknown>;
58
+ /** True when {@link outputSchema} wraps the leaf schema; `structuredContent` is wrapped to match. */
59
+ outputWrapped: boolean;
60
+ }
61
+
62
+ /** Property holding the leaf input when an MCP `inputSchema` is wrapped to get an object root. */
63
+ export const MCP_INPUT_WRAPPER_KEY = "input";
64
+
65
+ /** Property holding the leaf result when an MCP `outputSchema` is wrapped to get an object root. */
66
+ export const MCP_OUTPUT_WRAPPER_KEY = "result";
67
+
68
+ /**
69
+ * MCP requires `type: "object"` at the root of tool input and output schemas. Returns object-rooted
70
+ * schemas unchanged; wraps anything else (e.g. a discriminated-union `anyOf` root) as a single
71
+ * required property, moving `$schema`, `$id`, `definitions`, and `$defs` up so `#/definitions/…`
72
+ * references still resolve.
73
+ */
74
+ export function wrapMcpRootSchema(
75
+ /** Leaf input or output schema. */
76
+ schema: Record<string, unknown>,
77
+ /** Wrapper property name ({@link MCP_INPUT_WRAPPER_KEY} or {@link MCP_OUTPUT_WRAPPER_KEY}). */
78
+ key: string,
79
+ ): { schema: Record<string, unknown>; wrapped: boolean } {
80
+ if (schema.type === "object") {
81
+ return { schema, wrapped: false };
82
+ }
83
+ const { $schema, $id, definitions, $defs, ...inner } = schema;
84
+ return {
85
+ schema: {
86
+ ...($schema === undefined ? {} : { $schema }),
87
+ ...($id === undefined ? {} : { $id }),
88
+ type: "object",
89
+ properties: { [key]: inner },
90
+ required: [key],
91
+ additionalProperties: false,
92
+ ...(definitions === undefined ? {} : { definitions }),
93
+ ...($defs === undefined ? {} : { $defs }),
94
+ },
95
+ wrapped: true,
96
+ };
56
97
  }
57
98
 
58
99
  /** Builds MCP tool description: "{cli path} — {description}". */
@@ -163,14 +204,18 @@ export function collectMcpTools(root: CliProgram): McpToolDef[] {
163
204
  if (isMcpHidden(cmd)) {
164
205
  return;
165
206
  }
166
- const outputSchema = leafOutputSchema(cmd);
207
+ const input = wrapMcpRootSchema(buildLeafInputSchema(cmd), MCP_INPUT_WRAPPER_KEY);
208
+ const leafOutput = leafOutputSchema(cmd);
209
+ const output = leafOutput === undefined ? undefined : wrapMcpRootSchema(leafOutput, MCP_OUTPUT_WRAPPER_KEY);
167
210
  out.push({
168
211
  name: mcpToolName(root, path),
169
212
  description: resolveToolDescription(root, path, cmd),
170
213
  path,
171
214
  leaf: cmd,
172
- inputSchema: buildLeafInputSchema(cmd),
173
- ...(outputSchema === undefined ? {} : { outputSchema }),
215
+ inputSchema: input.schema,
216
+ inputWrapped: input.wrapped,
217
+ ...(output === undefined ? {} : { outputSchema: output.schema }),
218
+ outputWrapped: output?.wrapped ?? false,
174
219
  });
175
220
  return;
176
221
  }
@@ -1,6 +0,0 @@
1
- {
2
- "mcp-plugin": {
3
- "command": "node",
4
- "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/mcp.mjs", "mcp"]
5
- }
6
- }
@@ -1,3 +0,0 @@
1
- [install]
2
- # fetch package.json deps on first `bun src/index.ts` without manual `bun install`.
3
- auto = "fallback"
@@ -1,8 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "mcp-plugin": {
4
- "command": "node",
5
- "args": ["${CURSOR_PLUGIN_ROOT}/scripts/mcp.mjs", "mcp"]
6
- }
7
- }
8
- }