caplets 0.12.7 → 0.13.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.
Files changed (3) hide show
  1. package/README.md +17 -3
  2. package/dist/index.js +505 -105
  3. package/package.json +8 -6
package/README.md CHANGED
@@ -68,6 +68,18 @@ Connect Caplets to any MCP client:
68
68
  Ask your agent to use Caplets. It will see a compact capability list first, then inspect
69
69
  only the backend it needs.
70
70
 
71
+ You can also invoke configured Caplets directly from the CLI for agent-friendly scripts and smoke tests:
72
+
73
+ ```sh
74
+ caplets get-caplet context7
75
+ caplets list-tools context7
76
+ caplets get-tool context7.resolve-library-id
77
+ caplets call-tool context7.resolve-library-id --args '{"libraryName":"react"}'
78
+ caplets call-tool context7.resolve-library-id --args '{"libraryName":"react"}' --field result.id --format json
79
+ ```
80
+
81
+ Direct CLI operation commands print Markdown summaries by default. Add `--format plain` for plain text or `--format json` for machine-readable JSON (`md` is accepted as an alias for `markdown`). If a downstream tool returns `isError: true`, Caplets still exits with status code 1.
82
+
71
83
  ## Agent Plugins
72
84
 
73
85
  Use Caplets as a normal MCP server everywhere, or install a native agent integration when
@@ -610,7 +622,10 @@ shell snippets.
610
622
  "description": "Fetch status for one service.",
611
623
  "inputSchema": {
612
624
  "type": "object",
613
- "properties": { "service": { "type": "string" }, "verbose": { "type": "boolean" } },
625
+ "properties": {
626
+ "service": { "type": "string" },
627
+ "verbose": { "type": "boolean" }
628
+ },
614
629
  "required": ["service"]
615
630
  },
616
631
  "query": { "verbose": "$input.verbose" }
@@ -693,7 +708,7 @@ an existing destination file.
693
708
 
694
709
  Use `capletSets` to expose another Caplets collection as nested Caplets. Each child Caplet appears
695
710
  as one downstream tool and supports the full Caplets operation set: `get_caplet`, `check_backend`,
696
- `check_mcp_server`, `list_tools`, `search_tools`, `get_tool`, and `call_tool`.
711
+ `list_tools`, `search_tools`, `get_tool`, and `call_tool`.
697
712
 
698
713
  ```json
699
714
  {
@@ -891,7 +906,6 @@ Available operations:
891
906
 
892
907
  - `get_caplet`: return the configured capability card without starting the downstream server.
893
908
  - `check_backend`: verify the selected backend, whether MCP, OpenAPI, GraphQL, HTTP, CLI, or nested Caplets.
894
- - `check_mcp_server`: start or connect to an MCP server and verify its tool list.
895
909
  - `list_tools`: return compact downstream tool metadata.
896
910
  - `search_tools`: search downstream tool names and descriptions within this Caplet.
897
911
  - `get_tool`: return full metadata for one exact downstream tool.
package/dist/index.js CHANGED
@@ -180,7 +180,7 @@ const allowsEval = /* @__PURE__ */ cached(() => {
180
180
  return false;
181
181
  }
182
182
  });
183
- function isPlainObject$7(o) {
183
+ function isPlainObject$8(o) {
184
184
  if (isObject(o) === false) return false;
185
185
  const ctor = o.constructor;
186
186
  if (ctor === void 0) return true;
@@ -191,7 +191,7 @@ function isPlainObject$7(o) {
191
191
  return true;
192
192
  }
193
193
  function shallowClone(o) {
194
- if (isPlainObject$7(o)) return { ...o };
194
+ if (isPlainObject$8(o)) return { ...o };
195
195
  if (Array.isArray(o)) return [...o];
196
196
  if (o instanceof Map) return new Map(o);
197
197
  if (o instanceof Set) return new Set(o);
@@ -274,7 +274,7 @@ function omit(schema, mask) {
274
274
  }));
275
275
  }
276
276
  function extend(schema, shape) {
277
- if (!isPlainObject$7(shape)) throw new Error("Invalid input to extend: expected a plain object");
277
+ if (!isPlainObject$8(shape)) throw new Error("Invalid input to extend: expected a plain object");
278
278
  const checks = schema._zod.def.checks;
279
279
  if (checks && checks.length > 0) {
280
280
  const existingShape = schema._zod.def.shape;
@@ -290,7 +290,7 @@ function extend(schema, shape) {
290
290
  } }));
291
291
  }
292
292
  function safeExtend(schema, shape) {
293
- if (!isPlainObject$7(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
293
+ if (!isPlainObject$8(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
294
294
  return clone(schema, mergeDefs(schema._zod.def, { get shape() {
295
295
  const _shape = {
296
296
  ...schema._zod.def.shape,
@@ -1904,7 +1904,7 @@ function mergeValues$1(a, b) {
1904
1904
  valid: true,
1905
1905
  data: a
1906
1906
  };
1907
- if (isPlainObject$7(a) && isPlainObject$7(b)) {
1907
+ if (isPlainObject$8(a) && isPlainObject$8(b)) {
1908
1908
  const bKeys = Object.keys(b);
1909
1909
  const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
1910
1910
  const newObj = {
@@ -1980,7 +1980,7 @@ const $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
1980
1980
  $ZodType.init(inst, def);
1981
1981
  inst._zod.parse = (payload, ctx) => {
1982
1982
  const input = payload.value;
1983
- if (!isPlainObject$7(input)) {
1983
+ if (!isPlainObject$8(input)) {
1984
1984
  payload.issues.push({
1985
1985
  expected: "record",
1986
1986
  code: "invalid_type",
@@ -12268,7 +12268,7 @@ var Protocol = class {
12268
12268
  };
12269
12269
  }
12270
12270
  };
12271
- function isPlainObject$6(value) {
12271
+ function isPlainObject$7(value) {
12272
12272
  return value !== null && typeof value === "object" && !Array.isArray(value);
12273
12273
  }
12274
12274
  function mergeCapabilities(base, additional) {
@@ -12278,7 +12278,7 @@ function mergeCapabilities(base, additional) {
12278
12278
  const addValue = additional[k];
12279
12279
  if (addValue === void 0) continue;
12280
12280
  const baseValue = result[k];
12281
- if (isPlainObject$6(baseValue) && isPlainObject$6(addValue)) result[k] = {
12281
+ if (isPlainObject$7(baseValue) && isPlainObject$7(addValue)) result[k] = {
12282
12282
  ...baseValue,
12283
12283
  ...addValue
12284
12284
  };
@@ -19930,7 +19930,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
19930
19930
  } };
19931
19931
  //#endregion
19932
19932
  //#region ../core/package.json
19933
- var version$1 = "0.13.1";
19933
+ var version$1 = "0.14.0";
19934
19934
  //#endregion
19935
19935
  //#region ../../node_modules/.pnpm/unist-util-stringify-position@4.0.0/node_modules/unist-util-stringify-position/lib/index.js
19936
19936
  /**
@@ -27877,19 +27877,19 @@ function loadCapletFilesWithPaths(root) {
27877
27877
  if (servers[candidate.id] || openapiEndpoints[candidate.id] || graphqlEndpoints[candidate.id] || httpApis[candidate.id] || cliTools[candidate.id] || capletSets[candidate.id]) throw new CapletsError("CONFIG_INVALID", `Duplicate Caplet ID ${candidate.id} under ${root}`);
27878
27878
  paths[candidate.id] = candidate.path;
27879
27879
  const config = readCapletFile(candidate.path);
27880
- if (isPlainObject$5(config) && config.backend === "openapi") {
27880
+ if (isPlainObject$6(config) && config.backend === "openapi") {
27881
27881
  const { backend: _backend, ...endpoint } = config;
27882
27882
  openapiEndpoints[candidate.id] = endpoint;
27883
- } else if (isPlainObject$5(config) && config.backend === "graphql") {
27883
+ } else if (isPlainObject$6(config) && config.backend === "graphql") {
27884
27884
  const { backend: _backend, ...endpoint } = config;
27885
27885
  graphqlEndpoints[candidate.id] = endpoint;
27886
- } else if (isPlainObject$5(config) && config.backend === "http") {
27886
+ } else if (isPlainObject$6(config) && config.backend === "http") {
27887
27887
  const { backend: _backend, ...endpoint } = config;
27888
27888
  httpApis[candidate.id] = endpoint;
27889
- } else if (isPlainObject$5(config) && config.backend === "cli") {
27889
+ } else if (isPlainObject$6(config) && config.backend === "cli") {
27890
27890
  const { backend: _backend, ...endpoint } = config;
27891
27891
  cliTools[candidate.id] = endpoint;
27892
- } else if (isPlainObject$5(config) && config.backend === "caplets") {
27892
+ } else if (isPlainObject$6(config) && config.backend === "caplets") {
27893
27893
  const { backend: _backend, ...endpoint } = config;
27894
27894
  capletSets[candidate.id] = endpoint;
27895
27895
  } else servers[candidate.id] = config;
@@ -28043,7 +28043,7 @@ function parseFrontmatter(text, path) {
28043
28043
  value: text
28044
28044
  });
28045
28045
  matter(file, { strip: true });
28046
- if (!isPlainObject$5(file.data.matter) || Object.keys(file.data.matter).length === 0) throw new Error("empty frontmatter");
28046
+ if (!isPlainObject$6(file.data.matter) || Object.keys(file.data.matter).length === 0) throw new Error("empty frontmatter");
28047
28047
  return {
28048
28048
  frontmatter: file.data.matter,
28049
28049
  body: String(file)
@@ -28052,7 +28052,7 @@ function parseFrontmatter(text, path) {
28052
28052
  throw new CapletsError("CONFIG_INVALID", `Caplet file at ${path} has invalid YAML frontmatter`, redactSecrets(error));
28053
28053
  }
28054
28054
  }
28055
- function isPlainObject$5(value) {
28055
+ function isPlainObject$6(value) {
28056
28056
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
28057
28057
  }
28058
28058
  function validateCapletId(id, path) {
@@ -28673,7 +28673,7 @@ function normalizeLocalPaths(input, baseDir) {
28673
28673
  }
28674
28674
  function normalizeEndpointPaths(endpoints, baseDir, normalize) {
28675
28675
  if (!endpoints) return;
28676
- return Object.fromEntries(Object.entries(endpoints).map(([id, endpoint]) => [id, isPlainObject$4(endpoint) ? normalize(endpoint, baseDir) : endpoint]));
28676
+ return Object.fromEntries(Object.entries(endpoints).map(([id, endpoint]) => [id, isPlainObject$5(endpoint) ? normalize(endpoint, baseDir) : endpoint]));
28677
28677
  }
28678
28678
  function normalizeOpenApiPath(endpoint, baseDir) {
28679
28679
  return {
@@ -28682,7 +28682,7 @@ function normalizeOpenApiPath(endpoint, baseDir) {
28682
28682
  };
28683
28683
  }
28684
28684
  function normalizeGraphQlPath(endpoint, baseDir) {
28685
- const operations = isPlainObject$4(endpoint.operations) ? Object.fromEntries(Object.entries(endpoint.operations).map(([name, operation]) => [name, isPlainObject$4(operation) ? {
28685
+ const operations = isPlainObject$5(endpoint.operations) ? Object.fromEntries(Object.entries(endpoint.operations).map(([name, operation]) => [name, isPlainObject$5(operation) ? {
28686
28686
  ...operation,
28687
28687
  documentPath: normalizeLocalPath(operation.documentPath, baseDir)
28688
28688
  } : operation])) : endpoint.operations;
@@ -28693,7 +28693,7 @@ function normalizeGraphQlPath(endpoint, baseDir) {
28693
28693
  };
28694
28694
  }
28695
28695
  function normalizeCliToolsPaths(endpoint, baseDir) {
28696
- const actions = isPlainObject$4(endpoint.actions) ? Object.fromEntries(Object.entries(endpoint.actions).map(([name, action]) => [name, isPlainObject$4(action) ? {
28696
+ const actions = isPlainObject$5(endpoint.actions) ? Object.fromEntries(Object.entries(endpoint.actions).map(([name, action]) => [name, isPlainObject$5(action) ? {
28697
28697
  ...action,
28698
28698
  cwd: normalizeLocalPath(action.cwd, baseDir)
28699
28699
  } : action])) : endpoint.actions;
@@ -28896,7 +28896,7 @@ function isPublicMetadataPath(path) {
28896
28896
  if (path.length < 3 || path[0] !== "mcpServers" && path[0] !== "openapiEndpoints" && path[0] !== "graphqlEndpoints" && path[0] !== "httpApis" && path[0] !== "cliTools" && path[0] !== "capletSets") return false;
28897
28897
  return NON_INTERPOLATED_SERVER_FIELDS.has(path[2] ?? "");
28898
28898
  }
28899
- function isPlainObject$4(value) {
28899
+ function isPlainObject$5(value) {
28900
28900
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
28901
28901
  }
28902
28902
  function hasEnvReference(value) {
@@ -29066,9 +29066,9 @@ function validateInput(action, input) {
29066
29066
  if (!schema) return;
29067
29067
  const required = Array.isArray(schema.required) ? schema.required : [];
29068
29068
  for (const key of required) if (typeof key === "string" && (input[key] === void 0 || input[key] === null)) throw new CapletsError("REQUEST_INVALID", `CLI tool ${action.name} requires input ${key}`);
29069
- const properties = isPlainObject$3(schema.properties) ? schema.properties : {};
29069
+ const properties = isPlainObject$4(schema.properties) ? schema.properties : {};
29070
29070
  for (const [key, property] of Object.entries(properties)) {
29071
- if (input[key] === void 0 || !isPlainObject$3(property) || typeof property.type !== "string") continue;
29071
+ if (input[key] === void 0 || !isPlainObject$4(property) || typeof property.type !== "string") continue;
29072
29072
  if (!matchesJsonType(input[key], property.type)) throw new CapletsError("REQUEST_INVALID", `CLI tool ${action.name} input ${key} must be ${property.type}`);
29073
29073
  }
29074
29074
  }
@@ -29078,7 +29078,7 @@ function matchesJsonType(value, type) {
29078
29078
  case "number":
29079
29079
  case "integer": return typeof value === "number" && (type === "number" || Number.isInteger(value));
29080
29080
  case "boolean": return typeof value === "boolean";
29081
- case "object": return isPlainObject$3(value);
29081
+ case "object": return isPlainObject$4(value);
29082
29082
  case "array": return Array.isArray(value);
29083
29083
  case "null": return value === null;
29084
29084
  default: return true;
@@ -29169,7 +29169,7 @@ function isExecutable(path) {
29169
29169
  function isAbortError$1(error) {
29170
29170
  return error instanceof Error && error.name === "AbortError";
29171
29171
  }
29172
- function isPlainObject$3(value) {
29172
+ function isPlainObject$4(value) {
29173
29173
  return value !== null && typeof value === "object" && !Array.isArray(value);
29174
29174
  }
29175
29175
  //#endregion
@@ -48425,7 +48425,7 @@ function resolveMapping(mapping, input) {
48425
48425
  function resolveMappingToRecord(mapping, input, name) {
48426
48426
  if (mapping === void 0) return {};
48427
48427
  const resolved = resolveMapping(mapping, input);
48428
- if (!isPlainObject$2(resolved)) throw new CapletsError("REQUEST_INVALID", `HTTP action ${name} mapping must resolve to an object`);
48428
+ if (!isPlainObject$3(resolved)) throw new CapletsError("REQUEST_INVALID", `HTTP action ${name} mapping must resolve to an object`);
48429
48429
  return resolved;
48430
48430
  }
48431
48431
  function valueAtPath(input, path) {
@@ -48520,9 +48520,9 @@ function buildActionUrl(base, actionPath, options = {}) {
48520
48520
  return baseUrl;
48521
48521
  }
48522
48522
  function asRecord$1(value) {
48523
- return isPlainObject$2(value) ? value : {};
48523
+ return isPlainObject$3(value) ? value : {};
48524
48524
  }
48525
- function isPlainObject$2(value) {
48525
+ function isPlainObject$3(value) {
48526
48526
  return value !== null && typeof value === "object" && !Array.isArray(value);
48527
48527
  }
48528
48528
  //#endregion
@@ -58455,14 +58455,12 @@ function openApiCacheKey(endpoint) {
58455
58455
  //#endregion
58456
58456
  //#region ../core/src/capability-description.ts
58457
58457
  function capabilityDescription(server) {
58458
- const backendName = server.backend === "mcp" ? "MCP server" : server.backend === "openapi" ? "OpenAPI endpoint" : server.backend === "graphql" ? "GraphQL endpoint" : server.backend === "http" ? "HTTP API" : server.backend === "cli" ? "CLI tools" : server.backend === "caplets" ? "nested Caplets" : "backend";
58459
- const checkOperation = server.backend === "mcp" ? "check_mcp_server" : "check_backend";
58460
58458
  const hint = [
58461
- `Use this Caplet to inspect and call tools from its ${backendName} backend.`,
58459
+ `Use this Caplet to inspect and call tools from its ${server.backend === "mcp" ? "MCP server" : server.backend === "openapi" ? "OpenAPI endpoint" : server.backend === "graphql" ? "GraphQL endpoint" : server.backend === "http" ? "HTTP API" : server.backend === "cli" ? "CLI tools" : server.backend === "caplets" ? "nested Caplets" : "backend"} backend.`,
58462
58460
  "",
58463
58461
  "Recommended flow:",
58464
58462
  "- Read the full Caplet card: {\"operation\":\"get_caplet\"}",
58465
- `- Check the backend: {"operation":"${checkOperation}"}`,
58463
+ "- Check the backend: {\"operation\":\"check_backend\"}",
58466
58464
  "- Discover tools: {\"operation\":\"list_tools\"} or {\"operation\":\"search_tools\",\"query\":\"<what you need>\"}",
58467
58465
  "- Read one tool schema: {\"operation\":\"get_tool\",\"tool\":\"<tool name>\"}",
58468
58466
  "- Invoke one downstream tool: {\"operation\":\"call_tool\",\"tool\":\"<tool name>\",\"arguments\":{...}}",
@@ -58599,7 +58597,6 @@ function graphQlSource(server) {
58599
58597
  const operations = [
58600
58598
  "get_caplet",
58601
58599
  "check_backend",
58602
- "check_mcp_server",
58603
58600
  "list_tools",
58604
58601
  "search_tools",
58605
58602
  "get_tool",
@@ -58608,7 +58605,7 @@ const operations = [
58608
58605
  const generatedToolInputDescriptions = {
58609
58606
  operation: [
58610
58607
  "Caplets wrapper operation to perform for this configured Caplet backend.",
58611
- "Use get_caplet to read the full Caplet card, check_backend to check any backend, check_mcp_server to check an MCP backend, list_tools or search_tools to discover downstream tools, get_tool to read a downstream input schema, and call_tool to run one downstream tool, operation, action, CLI command, or child Caplet.",
58608
+ "Use get_caplet to read the full Caplet card, check_backend to check backend availability, list_tools or search_tools to discover downstream tools, get_tool to read a downstream input schema, and call_tool to run one downstream tool, operation, action, CLI command, or child Caplet.",
58612
58609
  "For call_tool, pass downstream inputs only inside the top-level \"arguments\" object."
58613
58610
  ].join(" "),
58614
58611
  query: "Required only for search_tools. Example: {\"operation\":\"search_tools\",\"query\":\"web search\",\"limit\":5}. Do not use query for call_tool; put downstream query values under arguments.query.",
@@ -58661,7 +58658,7 @@ function generatedToolInputJsonSchema() {
58661
58658
  //#region ../core/src/field-selection.ts
58662
58659
  function projectStructuredContent(value, outputSchema, fields) {
58663
58660
  validateFieldSelection(outputSchema, fields);
58664
- if (!isPlainObject$1(value)) throwInvalid("Field selection requires object structured content");
58661
+ if (!isPlainObject$2(value)) throwInvalid("Field selection requires object structured content");
58665
58662
  const result = createJsonObject();
58666
58663
  for (const field of fields) {
58667
58664
  const projected = projectPath(value, outputSchema, field.split("."));
@@ -58670,7 +58667,7 @@ function projectStructuredContent(value, outputSchema, fields) {
58670
58667
  return result;
58671
58668
  }
58672
58669
  function validateFieldSelection(outputSchema, fields) {
58673
- if (!isPlainObject$1(outputSchema)) throwInvalid("Field selection requires an output schema");
58670
+ if (!isPlainObject$2(outputSchema)) throwInvalid("Field selection requires an output schema");
58674
58671
  if (!Array.isArray(fields) || fields.some((field) => typeof field !== "string")) throwInvalid("Field selection requires an array of field paths");
58675
58672
  for (const field of fields) validateSchemaPath(outputSchema, field.split("."), field);
58676
58673
  }
@@ -58694,7 +58691,7 @@ function projectPath(value, schema, path) {
58694
58691
  return value.map((item) => projectPath(item, itemSchema, path) ?? {});
58695
58692
  }
58696
58693
  const segment = path[0];
58697
- if (!isPlainObject$1(value) || !Object.prototype.hasOwnProperty.call(value, segment)) return;
58694
+ if (!isPlainObject$2(value) || !Object.prototype.hasOwnProperty.call(value, segment)) return;
58698
58695
  const rest = path.slice(1);
58699
58696
  const propertySchema = getSchemaProperty(schema, segment);
58700
58697
  const projected = projectPath(value[segment], propertySchema, rest);
@@ -58706,24 +58703,24 @@ function pruneToSchema(value, schema) {
58706
58703
  const itemSchema = arrayItemSchema(schema);
58707
58704
  return value.map((item) => pruneToSchema(item, itemSchema));
58708
58705
  }
58709
- if (!isPlainObject$1(value)) return cloneJsonValue(value);
58710
- const properties = isPlainObject$1(schema) ? schema.properties : void 0;
58711
- if (!isPlainObject$1(properties)) return cloneJsonValue(value);
58706
+ if (!isPlainObject$2(value)) return cloneJsonValue(value);
58707
+ const properties = isPlainObject$2(schema) ? schema.properties : void 0;
58708
+ if (!isPlainObject$2(properties)) return cloneJsonValue(value);
58712
58709
  const result = createJsonObject();
58713
58710
  for (const [key, nestedSchema] of Object.entries(properties)) if (isSupportedSegment(key) && Object.prototype.hasOwnProperty.call(value, key)) result[key] = pruneToSchema(value[key], nestedSchema);
58714
58711
  return result;
58715
58712
  }
58716
58713
  function getSchemaProperty(schema, segment) {
58717
- const properties = isPlainObject$1(schema) ? schema.properties : void 0;
58714
+ const properties = isPlainObject$2(schema) ? schema.properties : void 0;
58718
58715
  if (!properties || !Object.prototype.hasOwnProperty.call(properties, segment)) return;
58719
58716
  return properties[segment];
58720
58717
  }
58721
58718
  function arrayItemSchema(schema) {
58722
- if (!isPlainObject$1(schema) || Array.isArray(schema.items)) return;
58719
+ if (!isPlainObject$2(schema) || Array.isArray(schema.items)) return;
58723
58720
  return schema.items;
58724
58721
  }
58725
58722
  function mergeValue(target, value) {
58726
- if (!isPlainObject$1(value)) return;
58723
+ if (!isPlainObject$2(value)) return;
58727
58724
  for (const [key, nested] of Object.entries(value)) {
58728
58725
  if (!isSupportedSegment(key)) continue;
58729
58726
  target[key] = mergeNested(target[key], nested);
@@ -58732,7 +58729,7 @@ function mergeValue(target, value) {
58732
58729
  function mergeNested(existing, next) {
58733
58730
  if (next === void 0) return existing;
58734
58731
  if (Array.isArray(existing) && Array.isArray(next)) return Array.from({ length: Math.max(existing.length, next.length) }, (_, index) => mergeNested(existing[index], next[index]));
58735
- if (isPlainObject$1(existing) && isPlainObject$1(next)) {
58732
+ if (isPlainObject$2(existing) && isPlainObject$2(next)) {
58736
58733
  const merged = Object.assign(createJsonObject(), existing);
58737
58734
  mergeValue(merged, next);
58738
58735
  return merged;
@@ -58742,7 +58739,7 @@ function mergeNested(existing, next) {
58742
58739
  function isSupportedSegment(segment) {
58743
58740
  return segment !== "" && segment !== "*" && segment !== "__proto__" && segment !== "prototype" && segment !== "constructor" && !/^\d+$/.test(segment);
58744
58741
  }
58745
- function isPlainObject$1(value) {
58742
+ function isPlainObject$2(value) {
58746
58743
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
58747
58744
  }
58748
58745
  function createJsonObject() {
@@ -58750,7 +58747,7 @@ function createJsonObject() {
58750
58747
  }
58751
58748
  function cloneJsonValue(value) {
58752
58749
  if (Array.isArray(value)) return value.map(cloneJsonValue);
58753
- if (isPlainObject$1(value)) {
58750
+ if (isPlainObject$2(value)) {
58754
58751
  const result = createJsonObject();
58755
58752
  for (const [key, nested] of Object.entries(value)) if (isSupportedSegment(key)) result[key] = cloneJsonValue(nested);
58756
58753
  return result;
@@ -58773,9 +58770,6 @@ async function handleServerTool(server, request, registry, downstream, openapi,
58773
58770
  switch (parsed.operation) {
58774
58771
  case "get_caplet": return jsonResult(registry.detail(server));
58775
58772
  case "check_backend": return jsonResult(await backendFor(server, downstream, openapi, graphql, http, cli, caplets).check(server));
58776
- case "check_mcp_server":
58777
- if (server.backend !== "mcp") throw new CapletsError("REQUEST_INVALID", "check_mcp_server is only valid for MCP-backed Caplets; use check_backend");
58778
- return jsonResult(await downstream.checkServer(server));
58779
58773
  case "list_tools": {
58780
58774
  const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets);
58781
58775
  const tools = await backend.listTools(server);
@@ -58826,7 +58820,6 @@ function validateOperationRequest(request, maxSearchLimit) {
58826
58820
  switch (value.operation) {
58827
58821
  case "get_caplet":
58828
58822
  case "check_backend":
58829
- case "check_mcp_server":
58830
58823
  case "list_tools":
58831
58824
  allowed([]);
58832
58825
  return { operation: value.operation };
@@ -58856,7 +58849,7 @@ function validateOperationRequest(request, maxSearchLimit) {
58856
58849
  "fields"
58857
58850
  ]);
58858
58851
  if (!value.tool) throw new CapletsError("REQUEST_INVALID", "call_tool requires tool");
58859
- if (!isPlainObject(value.arguments)) throw new CapletsError("REQUEST_INVALID", "call_tool.arguments must be a JSON object");
58852
+ if (!isPlainObject$1(value.arguments)) throw new CapletsError("REQUEST_INVALID", "call_tool.arguments must be a JSON object");
58860
58853
  return value.fields === void 0 ? {
58861
58854
  operation: "call_tool",
58862
58855
  tool: value.tool,
@@ -58877,7 +58870,7 @@ function jsonResult(value) {
58877
58870
  return {
58878
58871
  content: [{
58879
58872
  type: "text",
58880
- text: JSON.stringify(value, null, 2)
58873
+ text: "Result available in structuredContent.result."
58881
58874
  }],
58882
58875
  structuredContent: { result: value }
58883
58876
  };
@@ -58885,7 +58878,7 @@ function jsonResult(value) {
58885
58878
  function projectCallToolResult(result, outputSchema, fields) {
58886
58879
  if (result.isError === true) return result;
58887
58880
  const structuredContent = result.structuredContent;
58888
- if (!isPlainObject(structuredContent)) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Field selection requires the downstream tool to return object structuredContent");
58881
+ if (!isPlainObject$1(structuredContent)) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Field selection requires the downstream tool to return object structuredContent");
58889
58882
  const projected = projectStructuredContent(structuredContent, outputSchema, fields);
58890
58883
  return {
58891
58884
  ...result,
@@ -58896,7 +58889,7 @@ function projectCallToolResult(result, outputSchema, fields) {
58896
58889
  structuredContent: projected
58897
58890
  };
58898
58891
  }
58899
- function isPlainObject(value) {
58892
+ function isPlainObject$1(value) {
58900
58893
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
58901
58894
  }
58902
58895
  function backendFor(server, downstream, openapi, graphql, http, cli, caplets) {
@@ -62992,7 +62985,7 @@ async function loginAuth(serverId, options) {
62992
62985
  };
62993
62986
  if (server.backend === "mcp") await runOAuthFlow(server, flowOptions);
62994
62987
  else await runGenericOAuthFlow(server, flowOptions);
62995
- options.writeOut(`Authenticated ${serverId}\n`);
62988
+ options.writeOut(`Authenticated \`${serverId}\`.\n`);
62996
62989
  } catch (error) {
62997
62990
  options.writeErr(`${JSON.stringify(toSafeError(error, "AUTH_FAILED"), null, 2)}\n`);
62998
62991
  process.exitCode = 1;
@@ -63000,25 +62993,47 @@ async function loginAuth(serverId, options) {
63000
62993
  }
63001
62994
  function logoutAuth(serverId, options) {
63002
62995
  assertLoginTarget(findAuthTarget(serverId, loadConfig(options.configPath)), serverId);
63003
- if (deleteTokenBundle(serverId, options.authDir)) options.writeOut(`Deleted OAuth credentials for ${serverId}\n`);
63004
- else options.writeOut(`No OAuth credentials found for ${serverId}\n`);
62996
+ if (deleteTokenBundle(serverId, options.authDir)) options.writeOut(`Deleted OAuth credentials for \`${serverId}\`.\n`);
62997
+ else options.writeOut(`No OAuth credentials found for \`${serverId}\`.\n`);
63005
62998
  }
63006
62999
  function listAuth(options) {
63007
63000
  const servers = authTargets(loadConfig(options.configPath)).sort((left, right) => left.server.localeCompare(right.server));
63001
+ const format = options.format ?? "plain";
63002
+ if (format === "json") {
63003
+ const rows = servers.map((server) => {
63004
+ const bundle = readTokenBundle(server.server, options.authDir);
63005
+ const status = !bundle ? "missing" : isTokenBundleExpired(bundle) ? "expired" : "authenticated";
63006
+ return {
63007
+ server: server.server,
63008
+ status,
63009
+ ...bundle?.expiresAt ? { expiresAt: bundle.expiresAt } : {},
63010
+ ...bundle?.scope ? { scope: bundle.scope } : {}
63011
+ };
63012
+ });
63013
+ options.writeOut(`${JSON.stringify(rows, null, 2)}\n`);
63014
+ return;
63015
+ }
63008
63016
  if (servers.length === 0) {
63009
- options.writeOut("No configured remote OAuth servers found.\n");
63017
+ options.writeOut(format === "markdown" ? "## OAuth credentials\n\nNo configured remote OAuth servers found.\n" : "No configured remote OAuth servers found.\n");
63010
63018
  return;
63011
63019
  }
63020
+ if (format === "markdown") options.writeOut("## OAuth credentials\n\n");
63021
+ else options.writeOut("OAuth credentials\n\n");
63012
63022
  for (const server of servers) {
63013
63023
  const bundle = readTokenBundle(server.server, options.authDir);
63014
63024
  const status = !bundle ? "missing" : isTokenBundleExpired(bundle) ? "expired" : "authenticated";
63025
+ const details = [bundle?.expiresAt ? `expires ${bundle.expiresAt}` : void 0, bundle?.scope ? `scope ${bundle.scope}` : void 0].filter(Boolean).join("; ");
63026
+ if (format === "markdown") {
63027
+ options.writeOut(`- \`${server.server}\` — ${status}${details ? ` (${details})` : ""}\n`);
63028
+ continue;
63029
+ }
63015
63030
  options.writeOut([
63016
63031
  server.server,
63017
- status,
63018
- bundle?.expiresAt ? `expires ${bundle.expiresAt}` : void 0,
63019
- bundle?.scope ? `scope ${bundle.scope}` : void 0
63020
- ].filter(Boolean).join(" "));
63021
- options.writeOut("\n");
63032
+ ` Status: ${status}`,
63033
+ ...bundle?.expiresAt ? [` Expires: ${bundle.expiresAt}`] : [],
63034
+ ...bundle?.scope ? [` Scope: ${bundle.scope}`] : []
63035
+ ].join("\n"));
63036
+ options.writeOut("\n\n");
63022
63037
  }
63023
63038
  }
63024
63039
  function findAuthTarget(serverId, config = loadConfig()) {
@@ -63118,24 +63133,49 @@ function allCaplets(config) {
63118
63133
  ...Object.values(config.cliTools)
63119
63134
  ];
63120
63135
  }
63121
- function formatCapletList(rows) {
63136
+ function formatCapletList(rows, format = "plain") {
63137
+ return format === "markdown" ? formatCapletListMarkdown(rows) : formatCapletListPlain(rows);
63138
+ }
63139
+ function formatCapletListMarkdown(rows) {
63140
+ if (rows.length === 0) return "## Configured Caplets\n\nNo configured Caplets found.\n";
63141
+ const heading = [
63142
+ "## Configured Caplets",
63143
+ "",
63144
+ `${rows.length} ${rows.length === 1 ? "Caplet" : "Caplets"} shown.`,
63145
+ ""
63146
+ ];
63147
+ const entries = rows.flatMap((row) => [
63148
+ `- \`${row.server}\` — ${row.name}`,
63149
+ ` - Backend: ${row.backend}`,
63150
+ ` - Status: ${row.status}`,
63151
+ ` - Source: ${row.source}`,
63152
+ ...row.disabled ? [" - Disabled: true"] : [],
63153
+ ...row.path ? [` - Path: ${row.path}`] : []
63154
+ ]);
63155
+ const warnings = rows.flatMap((row) => row.shadows.map((shadow) => `Warning: ${formatSourceKind(row.source)} Caplet ${row.server} shadows ${formatSourceKind(shadow.kind)} Caplet at ${shadow.path}`));
63156
+ if (warnings.length === 0) return `${[...heading, ...entries].join("\n")}\n`;
63157
+ return `${[
63158
+ ...heading,
63159
+ ...entries,
63160
+ "",
63161
+ "Warnings:",
63162
+ ...warnings.map((warning) => `- ${warning}`)
63163
+ ].join("\n")}\n`;
63164
+ }
63165
+ function formatCapletListPlain(rows) {
63122
63166
  if (rows.length === 0) return "No configured Caplets found.\n";
63123
- const table = formatTable([[
63124
- "server",
63125
- "backend",
63126
- "status",
63127
- "source",
63128
- "name"
63129
- ], ...rows.map((row) => [
63167
+ const entries = rows.map((row) => [
63130
63168
  row.server,
63131
- row.backend,
63132
- row.status,
63133
- row.source,
63134
- row.name
63135
- ])]);
63169
+ ` Name: ${row.name}`,
63170
+ ` Backend: ${row.backend}`,
63171
+ ` Status: ${row.status}`,
63172
+ ` Source: ${row.source}`,
63173
+ ...row.disabled ? [" Disabled: true"] : [],
63174
+ ...row.path ? [` Path: ${row.path}`] : []
63175
+ ].join("\n")).join("\n\n");
63136
63176
  const warnings = rows.flatMap((row) => row.shadows.map((shadow) => `Warning: ${formatSourceKind(row.source)} Caplet ${row.server} shadows ${formatSourceKind(shadow.kind)} Caplet at ${shadow.path}`));
63137
- if (warnings.length === 0) return `${table}\n`;
63138
- return `${table}\n${warnings.join("\n")}\n`;
63177
+ if (warnings.length === 0) return `Configured Caplets (${rows.length})\n\n${entries}\n`;
63178
+ return `Configured Caplets (${rows.length})\n\n${entries}\n\n${warnings.join("\n")}\n`;
63139
63179
  }
63140
63180
  function formatSourceKind(kind) {
63141
63181
  if (kind.startsWith("project")) return "project";
@@ -63155,28 +63195,35 @@ function resolveCliConfigPaths(envConfigPath, authDir) {
63155
63195
  envConfig: envConfigPath ?? null
63156
63196
  };
63157
63197
  }
63158
- function formatConfigPaths(paths) {
63198
+ function formatConfigPaths(paths, format = "plain") {
63199
+ if (format === "markdown") return formatConfigPathsMarkdown(paths);
63200
+ return formatConfigPathsPlain(paths);
63201
+ }
63202
+ function formatConfigPathsMarkdown(paths) {
63159
63203
  return [
63160
- `userConfig: ${paths.userConfig}`,
63161
- `projectConfig: ${paths.projectConfig}`,
63162
- `userRoot: ${paths.userRoot}`,
63163
- `stateRoot: ${paths.stateRoot}`,
63164
- `projectRoot: ${paths.projectRoot}`,
63165
- `authDir: ${paths.authDir}`,
63166
- `envConfig: ${paths.envConfig ?? "unset"}`
63204
+ "## Caplets paths",
63205
+ "",
63206
+ `- User config: ${paths.userConfig}`,
63207
+ `- Project config: ${paths.projectConfig}`,
63208
+ `- User Caplets root: ${paths.userRoot}`,
63209
+ `- State root: ${paths.stateRoot}`,
63210
+ `- Project Caplets root: ${paths.projectRoot}`,
63211
+ `- Auth directory: ${paths.authDir}`,
63212
+ `- CAPLETS_CONFIG: ${paths.envConfig ?? "unset"}`
63167
63213
  ].join("\n") + "\n";
63168
63214
  }
63169
- function formatTable(rows) {
63170
- const firstRow = rows[0];
63171
- if (!firstRow) return "";
63172
- const widths = firstRow.map((_, column) => Math.max(...rows.map((row) => row[column]?.length ?? 0)));
63173
- return rows.map((row) => formatTableRow(row, widths)).join("\n");
63174
- }
63175
- function formatTableRow(row, widths) {
63176
- return row.map((value, column) => {
63177
- if (column === row.length - 1) return value;
63178
- return value.padEnd((widths[column] ?? 0) + 2);
63179
- }).join("").trimEnd();
63215
+ function formatConfigPathsPlain(paths) {
63216
+ return [
63217
+ "Caplets paths",
63218
+ "",
63219
+ `User config: ${paths.userConfig}`,
63220
+ `Project config: ${paths.projectConfig}`,
63221
+ `User root: ${paths.userRoot}`,
63222
+ `State root: ${paths.stateRoot}`,
63223
+ `Project root: ${paths.projectRoot}`,
63224
+ `Auth directory: ${paths.authDir}`,
63225
+ `CAPLETS_CONFIG: ${paths.envConfig ?? "unset"}`
63226
+ ].join("\n") + "\n";
63180
63227
  }
63181
63228
  //#endregion
63182
63229
  //#region ../core/src/cli/install.ts
@@ -63425,7 +63472,7 @@ async function runCli(args, io = {}) {
63425
63472
  ]);
63426
63473
  } catch (error) {
63427
63474
  if (error instanceof CommanderError) {
63428
- if (error.code === "commander.helpDisplayed" || error.code === "commander.version") return;
63475
+ if (error.code === "commander.helpDisplayed" || error.code === "commander.version" || error.message === "(outputHelp)") return;
63429
63476
  throw new CapletsError("REQUEST_INVALID", error.message);
63430
63477
  }
63431
63478
  throw error;
@@ -63434,6 +63481,9 @@ async function runCli(args, io = {}) {
63434
63481
  function createProgram(io = {}) {
63435
63482
  const writeOut = io.writeOut ?? ((value) => process.stdout.write(value));
63436
63483
  const writeErr = io.writeErr ?? ((value) => process.stderr.write(value));
63484
+ const setExitCode = io.setExitCode ?? ((code) => {
63485
+ process.exitCode = code;
63486
+ });
63437
63487
  const program = new Command();
63438
63488
  program.name("caplets").description("Progressive-disclosure gateway for MCP servers.").version(io.version ?? version$1).exitOverride().configureOutput({
63439
63489
  writeOut,
@@ -63447,13 +63497,13 @@ function createProgram(io = {}) {
63447
63497
  force: Boolean(options.force)
63448
63498
  })}\n`);
63449
63499
  });
63450
- program.command("list").description("List configured Caplets.").option("--all", "include disabled Caplets").option("--json", "print JSON output").action((options) => {
63500
+ program.command("list").description("List configured Caplets.").option("--all", "include disabled Caplets").option("--json", "print JSON output").option("--format <format>", "output format: plain, markdown, md, or json", parseOutputFormat).action((options) => {
63451
63501
  const rows = listCaplets(loadConfigWithSources(envConfigPath()), { includeDisabled: Boolean(options.all) });
63452
- if (options.json) {
63502
+ if (options.json || options.format === "json") {
63453
63503
  writeOut(`${JSON.stringify(rows, null, 2)}\n`);
63454
63504
  return;
63455
63505
  }
63456
- writeOut(formatCapletList(rows));
63506
+ writeOut(formatCapletList(rows, options.format ?? "plain"));
63457
63507
  });
63458
63508
  program.command("install").description("Install Caplets from a repo's caplets directory.").argument("<repo>", "local repo path, Git URL, or GitHub owner/repo").argument("[caplets...]", "optional Caplet IDs to install").option("-g, --global", "install to the user Caplets root").option("--force", "overwrite installed Caplets").action((repo, capletIds, options) => {
63459
63509
  const result = installCaplets(repo, {
@@ -63499,17 +63549,88 @@ function createProgram(io = {}) {
63499
63549
  destinationRoot: addDestinationRoot(options)
63500
63550
  }));
63501
63551
  });
63552
+ program.command("get-caplet").description("Print a configured Caplet card.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
63553
+ await executeOperation(caplet, { operation: "get_caplet" }, {
63554
+ writeOut,
63555
+ writeErr,
63556
+ setExitCode,
63557
+ authDir: io.authDir,
63558
+ format: options.format
63559
+ });
63560
+ });
63561
+ program.command("check-backend").description("Check backend availability for a configured Caplet.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
63562
+ await executeOperation(caplet, { operation: "check_backend" }, {
63563
+ writeOut,
63564
+ writeErr,
63565
+ setExitCode,
63566
+ authDir: io.authDir,
63567
+ format: options.format
63568
+ });
63569
+ });
63570
+ program.command("list-tools").description("List downstream tools for a configured Caplet.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
63571
+ await executeOperation(caplet, { operation: "list_tools" }, {
63572
+ writeOut,
63573
+ writeErr,
63574
+ setExitCode,
63575
+ authDir: io.authDir,
63576
+ format: options.format
63577
+ });
63578
+ });
63579
+ program.command("search-tools").description("Search downstream tools for a configured Caplet.").argument("<caplet>", "configured Caplet ID").argument("<query>", "search query").option("--limit <n>", "maximum number of tools to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, query, options) => {
63580
+ await executeOperation(caplet, options.limit === void 0 ? {
63581
+ operation: "search_tools",
63582
+ query
63583
+ } : {
63584
+ operation: "search_tools",
63585
+ query,
63586
+ limit: options.limit
63587
+ }, {
63588
+ writeOut,
63589
+ writeErr,
63590
+ setExitCode,
63591
+ authDir: io.authDir,
63592
+ format: options.format
63593
+ });
63594
+ });
63595
+ program.command("get-tool").description("Print one downstream tool schema.").argument("<caplet.tool>", "qualified target, split on the first dot").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
63596
+ const { caplet, tool } = parseQualifiedTarget(target);
63597
+ await executeOperation(caplet, {
63598
+ operation: "get_tool",
63599
+ tool
63600
+ }, {
63601
+ writeOut,
63602
+ writeErr,
63603
+ setExitCode,
63604
+ authDir: io.authDir,
63605
+ format: options.format
63606
+ });
63607
+ });
63608
+ program.command("call-tool").description("Call one downstream tool.").argument("<caplet.tool>", "qualified target, split on the first dot").option("--args <json-object>", "JSON object of downstream tool arguments").option("--field <path>", "project a field from structured output", collect, []).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
63609
+ const { caplet, tool } = parseQualifiedTarget(target);
63610
+ await executeOperation(caplet, {
63611
+ operation: "call_tool",
63612
+ tool,
63613
+ arguments: parseCallToolArgs(options.args),
63614
+ ...options.field && options.field.length > 0 ? { fields: options.field } : {}
63615
+ }, {
63616
+ writeOut,
63617
+ writeErr,
63618
+ setExitCode,
63619
+ authDir: io.authDir,
63620
+ format: options.format
63621
+ });
63622
+ });
63502
63623
  const config = program.command("config").description("Inspect Caplets config locations.");
63503
63624
  config.command("path").description("Print the effective user config path.").action(() => {
63504
63625
  writeOut(`${resolveConfigPath(envConfigPath())}\n`);
63505
63626
  });
63506
- config.command("paths").description("Print resolved Caplets config, root, and auth paths.").option("--json", "print JSON output").action((options) => {
63627
+ config.command("paths").description("Print resolved Caplets config, root, and auth paths.").option("--json", "print JSON output").option("--format <format>", "output format: plain, markdown, md, or json", parseOutputFormat).action((options) => {
63507
63628
  const paths = resolveCliConfigPaths(envConfigPath(), io.authDir);
63508
- if (options.json) {
63629
+ if (options.json || options.format === "json") {
63509
63630
  writeOut(`${JSON.stringify(paths, null, 2)}\n`);
63510
63631
  return;
63511
63632
  }
63512
- writeOut(formatConfigPaths(paths));
63633
+ writeOut(formatConfigPaths(paths, options.format ?? "plain"));
63513
63634
  });
63514
63635
  const auth = program.command("auth").description("Manage OAuth credentials for remote servers.");
63515
63636
  auth.command("login").description("Authenticate a configured remote OAuth server.").argument("<server>", "configured server ID").option("--no-open", "print the authorization URL without opening a browser").action(async (serverId, options) => {
@@ -63530,10 +63651,11 @@ function createProgram(io = {}) {
63530
63651
  ...io.authDir ? { authDir: io.authDir } : {}
63531
63652
  });
63532
63653
  });
63533
- auth.command("list").description("List servers with stored OAuth credentials.").action(() => {
63654
+ auth.command("list").description("List servers with stored OAuth credentials.").option("--json", "print JSON output").option("--format <format>", "output format: plain, markdown, md, or json", parseOutputFormat).action((options) => {
63534
63655
  const configPath = envConfigPath();
63535
63656
  listAuth({
63536
63657
  writeOut,
63658
+ format: options.json || options.format === "json" ? "json" : options.format ?? "plain",
63537
63659
  ...configPath ? { configPath } : {},
63538
63660
  ...io.authDir ? { authDir: io.authDir } : {}
63539
63661
  });
@@ -63547,6 +63669,284 @@ function collect(value, previous) {
63547
63669
  previous.push(value);
63548
63670
  return previous;
63549
63671
  }
63672
+ function parsePositiveInteger(value) {
63673
+ const parsed = Number(value);
63674
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new CapletsError("REQUEST_INVALID", `Expected a positive integer, got ${value}`);
63675
+ return parsed;
63676
+ }
63677
+ function parseOutputFormat(value) {
63678
+ switch (value.toLocaleLowerCase()) {
63679
+ case "markdown":
63680
+ case "md": return "markdown";
63681
+ case "plain": return "plain";
63682
+ case "json": return "json";
63683
+ default: throw new CapletsError("REQUEST_INVALID", `Expected output format markdown, md, plain, or json; got ${value}`);
63684
+ }
63685
+ }
63686
+ function parseQualifiedTarget(target) {
63687
+ const dot = target.indexOf(".");
63688
+ if (dot <= 0 || dot === target.length - 1) throw new CapletsError("REQUEST_INVALID", "Expected qualified target in the form <caplet>.<tool>");
63689
+ return {
63690
+ caplet: target.slice(0, dot),
63691
+ tool: target.slice(dot + 1)
63692
+ };
63693
+ }
63694
+ function parseCallToolArgs(value) {
63695
+ if (value === void 0) return {};
63696
+ let parsed;
63697
+ try {
63698
+ parsed = JSON.parse(value);
63699
+ } catch (error) {
63700
+ throw new CapletsError("REQUEST_INVALID", "call-tool --args must be valid JSON", error);
63701
+ }
63702
+ if (!isPlainObject(parsed)) throw new CapletsError("REQUEST_INVALID", "call-tool --args must be a JSON object");
63703
+ return parsed;
63704
+ }
63705
+ function isPlainObject(value) {
63706
+ return value !== null && typeof value === "object" && !Array.isArray(value);
63707
+ }
63708
+ async function executeOperation(caplet, request, io) {
63709
+ const configPath = envConfigPath();
63710
+ const engine = new CapletsEngine({
63711
+ ...configPath ? { configPath } : {},
63712
+ ...io.authDir ? { authDir: io.authDir } : {},
63713
+ watch: false,
63714
+ writeErr: io.writeErr
63715
+ });
63716
+ try {
63717
+ const result = await engine.execute(caplet, request);
63718
+ const output = cliOutputForOperation(result, {
63719
+ ...request,
63720
+ caplet
63721
+ }, io.format ?? "markdown");
63722
+ io.writeOut(typeof output === "string" ? `${output}\n` : `${JSON.stringify(output, null, 2)}\n`);
63723
+ if (isPlainObject(result) && result.isError === true) io.setExitCode(1);
63724
+ } finally {
63725
+ await engine.close();
63726
+ }
63727
+ }
63728
+ function cliOutputForOperation(result, request, format) {
63729
+ if (format === "json" || !isPlainObject(result)) return jsonPayloadForOperation(result, request.operation);
63730
+ return format === "markdown" ? markdownSummaryForOperation(result, request) : plainSummaryForOperation(result, request);
63731
+ }
63732
+ function jsonPayloadForOperation(result, operation) {
63733
+ if (operation === "call_tool" || !isPlainObject(result)) return result;
63734
+ const structuredContent = result.structuredContent;
63735
+ if (!isPlainObject(structuredContent) || !("result" in structuredContent)) return result;
63736
+ return structuredContent.result;
63737
+ }
63738
+ function markdownSummaryForOperation(result, request) {
63739
+ const operation = request.operation;
63740
+ const payload = jsonPayloadForOperation(result, operation);
63741
+ if (!isPlainObject(payload)) return String(payload);
63742
+ switch (operation) {
63743
+ case "get_caplet": return [
63744
+ `## Caplet \`${String(payload.caplet ?? "unknown")}\``,
63745
+ "",
63746
+ `**Name:** ${String(payload.name ?? "Unnamed")}`,
63747
+ `**Description:** ${String(payload.description ?? "No description.")}`,
63748
+ payload.backend ? `**Backend:** ${backendType(payload.backend)}` : void 0,
63749
+ "",
63750
+ "Next:",
63751
+ `- List tools: \`caplets list-tools ${String(payload.caplet ?? "<caplet>")}\``,
63752
+ `- Search tools: \`caplets search-tools ${String(payload.caplet ?? "<caplet>")} <query>\``
63753
+ ].filter((line) => line !== void 0).join("\n");
63754
+ case "check_backend": return [
63755
+ `## Backend \`${String(payload.server ?? "caplet")}\``,
63756
+ "",
63757
+ `- Status: ${String(payload.status ?? "unknown")}`,
63758
+ typeof payload.toolCount === "number" ? `- Tools: ${payload.toolCount}` : void 0,
63759
+ typeof payload.elapsedMs === "number" ? `- Elapsed: ${payload.elapsedMs}ms` : void 0,
63760
+ "",
63761
+ "Next:",
63762
+ `- List tools: \`caplets list-tools ${String(payload.server ?? "<caplet>")}\``
63763
+ ].filter((line) => line !== void 0).join("\n");
63764
+ case "list_tools": {
63765
+ const tools = Array.isArray(payload.tools) ? payload.tools : [];
63766
+ return [
63767
+ `## Tools for \`${String(payload.server ?? "caplet")}\``,
63768
+ "",
63769
+ `${tools.length} ${tools.length === 1 ? "tool" : "tools"} found.`,
63770
+ "",
63771
+ ...formatToolLines(tools, "markdown"),
63772
+ "",
63773
+ "Next:",
63774
+ `- Inspect a tool: \`caplets get-tool ${String(payload.server ?? "<caplet>")}.<tool>\``,
63775
+ `- Call a tool: \`caplets call-tool ${String(payload.server ?? "<caplet>")}.<tool> --args '{...}'\``,
63776
+ "- Machine output: add `--format json`"
63777
+ ].join("\n");
63778
+ }
63779
+ case "search_tools": {
63780
+ const tools = Array.isArray(payload.tools) ? payload.tools : [];
63781
+ return [
63782
+ `## Matches for ${JSON.stringify(String(payload.query ?? ""))} in \`${String(payload.server ?? "caplet")}\``,
63783
+ "",
63784
+ `${tools.length} ${tools.length === 1 ? "match" : "matches"} found.`,
63785
+ "",
63786
+ ...formatToolLines(tools, "markdown"),
63787
+ "",
63788
+ "Next:",
63789
+ tools.length > 0 ? `- Inspect the first match: \`caplets get-tool ${String(payload.server ?? "<caplet>")}.${firstToolName(tools) ?? "<tool>"}\`` : `- Try a broader query or list tools: \`caplets list-tools ${String(payload.server ?? "<caplet>")}\``
63790
+ ].join("\n");
63791
+ }
63792
+ case "get_tool": {
63793
+ const tool = isPlainObject(payload.tool) ? payload.tool : {};
63794
+ const target = `${String(payload.server ?? "<caplet>")}.${String(tool.name ?? "<tool>")}`;
63795
+ return [
63796
+ `## Tool \`${target}\``,
63797
+ "",
63798
+ tool.description ? compactDescription(String(tool.description)) : void 0,
63799
+ "",
63800
+ "Input:",
63801
+ `- ${schemaSummary(tool.inputSchema)}`,
63802
+ "",
63803
+ "Output:",
63804
+ `- ${tool.outputSchema ? schemaSummary(tool.outputSchema) : "not declared"}`,
63805
+ "",
63806
+ "Next:",
63807
+ `- Call: \`caplets call-tool ${target} --args '{...}'\``,
63808
+ "- Full schema: add `--format json`"
63809
+ ].filter((line) => line !== void 0).join("\n");
63810
+ }
63811
+ case "call_tool": return [
63812
+ `## Call \`${`${String(request.caplet ?? "<caplet>")}.${String(request.tool ?? "unknown")}`}\``,
63813
+ "",
63814
+ `- Status: ${payload.isError === true ? "failed" : "succeeded"}`,
63815
+ callStatusLine(payload) ? `- ${callStatusLine(payload)}` : void 0,
63816
+ `- Result: ${summarizeCallResult(payload)}`,
63817
+ "",
63818
+ "Use `--format json` to inspect the full structured result."
63819
+ ].filter((line) => line !== void 0).join("\n");
63820
+ default: return JSON.stringify(payload, null, 2);
63821
+ }
63822
+ }
63823
+ function plainSummaryForOperation(result, request) {
63824
+ const operation = request.operation;
63825
+ const payload = jsonPayloadForOperation(result, operation);
63826
+ if (!isPlainObject(payload)) return String(payload);
63827
+ switch (operation) {
63828
+ case "get_caplet": return [
63829
+ `Caplet: ${String(payload.caplet ?? "unknown")}`,
63830
+ `Name: ${String(payload.name ?? "Unnamed")}`,
63831
+ `Description: ${String(payload.description ?? "No description.")}`,
63832
+ payload.backend ? `Backend: ${backendType(payload.backend)}` : void 0,
63833
+ `Next: caplets list-tools ${String(payload.caplet ?? "<caplet>")} or caplets search-tools ${String(payload.caplet ?? "<caplet>")} <query>`
63834
+ ].filter((line) => Boolean(line)).join("\n");
63835
+ case "check_backend": return [
63836
+ `Backend: ${String(payload.server ?? "caplet")} is ${String(payload.status ?? "unknown")}`,
63837
+ typeof payload.toolCount === "number" ? `Tools: ${payload.toolCount}` : void 0,
63838
+ typeof payload.elapsedMs === "number" ? `Elapsed: ${payload.elapsedMs}ms` : void 0,
63839
+ `Next: caplets list-tools ${String(payload.server ?? "<caplet>")}`
63840
+ ].filter((line) => Boolean(line)).join("\n");
63841
+ case "list_tools": {
63842
+ const tools = Array.isArray(payload.tools) ? payload.tools : [];
63843
+ return [
63844
+ `Tools for ${String(payload.server ?? "caplet")} (${tools.length}):`,
63845
+ ...formatToolLines(tools, "plain"),
63846
+ `Next: caplets get-tool ${String(payload.server ?? "<caplet>")}.<tool> or caplets call-tool ${String(payload.server ?? "<caplet>")}.<tool> --args '{...}'`
63847
+ ].join("\n");
63848
+ }
63849
+ case "search_tools": {
63850
+ const tools = Array.isArray(payload.tools) ? payload.tools : [];
63851
+ return [
63852
+ `Matches for ${JSON.stringify(String(payload.query ?? ""))} in ${String(payload.server ?? "caplet")} (${tools.length}):`,
63853
+ ...formatToolLines(tools, "plain"),
63854
+ tools.length > 0 ? `Next: caplets get-tool ${String(payload.server ?? "<caplet>")}.${firstToolName(tools) ?? "<tool>"}` : `Next: try caplets list-tools ${String(payload.server ?? "<caplet>")} or a broader query.`
63855
+ ].join("\n");
63856
+ }
63857
+ case "get_tool": {
63858
+ const tool = isPlainObject(payload.tool) ? payload.tool : {};
63859
+ const target = `${String(payload.server ?? "<caplet>")}.${String(tool.name ?? "<tool>")}`;
63860
+ return [
63861
+ `Tool: ${target}`,
63862
+ tool.description ? `Description: ${compactDescription(String(tool.description))}` : void 0,
63863
+ `Input: ${schemaSummary(tool.inputSchema)}`,
63864
+ `Output: ${tool.outputSchema ? schemaSummary(tool.outputSchema) : "not declared"}`,
63865
+ `Next: caplets call-tool ${target} --args '{...}'`,
63866
+ "Use --format json to inspect full schemas and descriptions."
63867
+ ].filter((line) => Boolean(line)).join("\n");
63868
+ }
63869
+ case "call_tool": return [
63870
+ `Call ${`${String(request.caplet ?? "<caplet>")}.${String(request.tool ?? "unknown")}`} ${payload.isError === true ? "failed" : "succeeded"}.`,
63871
+ callStatusLine(payload),
63872
+ `Result: ${summarizeCallResult(payload)}`,
63873
+ "Use --format json to inspect the full structured result."
63874
+ ].filter((line) => Boolean(line)).join("\n");
63875
+ default: return JSON.stringify(payload, null, 2);
63876
+ }
63877
+ }
63878
+ function formatToolLines(tools, format) {
63879
+ if (tools.length === 0) return ["- none"];
63880
+ return tools.map((tool) => {
63881
+ if (!isPlainObject(tool)) return `- ${String(tool)}`;
63882
+ const name = String(tool.tool ?? tool.name ?? "unknown");
63883
+ const displayName = format === "markdown" ? `\`${name}\`` : name;
63884
+ const flags = [tool.hasInputSchema ? "input" : void 0, tool.hasOutputSchema ? "output" : void 0].filter(Boolean).join(", ");
63885
+ return `- ${displayName}${flags ? ` (${flags})` : ""}${tool.description ? ` — ${compactDescription(String(tool.description))}` : ""}`;
63886
+ });
63887
+ }
63888
+ function compactDescription(value) {
63889
+ const firstParagraph = value.trim().split(/\n\s*\n/u)[0] ?? "";
63890
+ const collapsed = (firstParagraph.match(/^.*?(?:[.!?](?=\s|$)|$)/u)?.[0] ?? firstParagraph).replace(/\s+/gu, " ").trim();
63891
+ return collapsed.length > 140 ? `${collapsed.slice(0, 137).trimEnd()}...` : collapsed;
63892
+ }
63893
+ function firstToolName(tools) {
63894
+ const first = tools[0];
63895
+ return isPlainObject(first) && typeof first.tool === "string" ? first.tool : void 0;
63896
+ }
63897
+ function backendType(value) {
63898
+ return isPlainObject(value) && typeof value.type === "string" ? value.type : "unknown";
63899
+ }
63900
+ function callStatusLine(payload) {
63901
+ const structured = isPlainObject(payload.structuredContent) ? payload.structuredContent : payload;
63902
+ return typeof structured.exitCode === "number" ? `Exit code: ${structured.exitCode}` : void 0;
63903
+ }
63904
+ function summarizeCallResult(payload) {
63905
+ const structured = isPlainObject(payload.structuredContent) ? payload.structuredContent : payload;
63906
+ const preview = previewValue(preferredPreviewValue(structured));
63907
+ if (preview) return preview;
63908
+ const keys = Object.keys(structured).filter((key) => key !== "elapsedMs");
63909
+ return keys.length > 0 ? `structured keys: ${keys.join(", ")}` : "no structured content";
63910
+ }
63911
+ function preferredPreviewValue(value) {
63912
+ if (!isPlainObject(value)) return value;
63913
+ if ("result" in value) return value.result;
63914
+ if ("json" in value) return value.json;
63915
+ if (typeof value.text === "string") return value.text;
63916
+ if (typeof value.stdout === "string" && value.stdout.trim()) return value.stdout.trim();
63917
+ return value;
63918
+ }
63919
+ function previewValue(value) {
63920
+ if (typeof value === "string") return truncatePreview(value);
63921
+ if (typeof value === "number" || typeof value === "boolean" || value === null) return String(value);
63922
+ if (Array.isArray(value)) return truncatePreview(JSON.stringify(value));
63923
+ if (isPlainObject(value)) {
63924
+ const entries = Object.entries(value).slice(0, 4);
63925
+ if (entries.length === 0) return "empty object";
63926
+ return truncatePreview(entries.map(([key, entryValue]) => `${key}: ${previewScalar(entryValue)}`).join(", "));
63927
+ }
63928
+ }
63929
+ function previewScalar(value) {
63930
+ if (typeof value === "string") return JSON.stringify(truncatePreview(value, 80));
63931
+ if (typeof value === "number" || typeof value === "boolean" || value === null) return String(value);
63932
+ if (Array.isArray(value)) return `[${value.length} item${value.length === 1 ? "" : "s"}]`;
63933
+ if (isPlainObject(value)) return `{${Object.keys(value).slice(0, 3).join(", ")}${Object.keys(value).length > 3 ? ", ..." : ""}}`;
63934
+ return typeof value;
63935
+ }
63936
+ function truncatePreview(value, maxLength = 180) {
63937
+ const collapsed = value.replace(/\s+/gu, " ").trim();
63938
+ return collapsed.length > maxLength ? `${collapsed.slice(0, maxLength - 3).trimEnd()}...` : collapsed;
63939
+ }
63940
+ function schemaSummary(schema) {
63941
+ if (!isPlainObject(schema)) return "not declared";
63942
+ const properties = isPlainObject(schema.properties) ? Object.keys(schema.properties) : [];
63943
+ const required = Array.isArray(schema.required) ? schema.required.filter((value) => typeof value === "string") : [];
63944
+ return [
63945
+ typeof schema.type === "string" ? `type ${schema.type}` : void 0,
63946
+ properties.length > 0 ? `properties ${properties.join(", ")}` : "no declared properties",
63947
+ required.length > 0 ? `required ${required.join(", ")}` : "no required fields"
63948
+ ].filter((part) => Boolean(part)).join("; ");
63949
+ }
63550
63950
  function addDestinationRoot(options) {
63551
63951
  return options.global ? resolveCapletsRoot(resolveConfigPath(envConfigPath())) : resolveProjectCapletsRoot();
63552
63952
  }
@@ -63559,7 +63959,7 @@ function writeAddResult(writeOut, label, result) {
63559
63959
  }
63560
63960
  //#endregion
63561
63961
  //#region package.json
63562
- var version = "0.12.7";
63962
+ var version = "0.13.0";
63563
63963
  //#endregion
63564
63964
  //#region src/index.ts
63565
63965
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caplets",
3
- "version": "0.12.7",
3
+ "version": "0.13.0",
4
4
  "description": "Progressive disclosure gateway CLI for MCP servers and native Caplets adapters.",
5
5
  "keywords": [
6
6
  "caplets",
@@ -33,21 +33,23 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "@modelcontextprotocol/sdk": "^1.29.0",
37
- "@caplets/core": "0.13.1"
36
+ "@modelcontextprotocol/sdk": "^1.29.0"
38
37
  },
39
38
  "devDependencies": {
40
39
  "@types/node": "^25.7.0",
40
+ "@typescript/native-preview": "7.0.0-dev.20260515.1",
41
41
  "rolldown": "^1.0.0",
42
42
  "typescript": "^6.0.3",
43
- "vitest": "^4.1.6"
43
+ "vitest": "^4.1.6",
44
+ "@caplets/core": "0.14.0"
44
45
  },
45
46
  "engines": {
46
47
  "node": ">=22"
47
48
  },
48
49
  "scripts": {
49
50
  "build": "rm -rf dist && rolldown -c",
50
- "typecheck": "tsc --noEmit",
51
- "test": "vitest run --passWithNoTests"
51
+ "build:watch": "rm -rf dist && rolldown -c --watch",
52
+ "typecheck": "tsgo --noEmit",
53
+ "test": "vitest run"
52
54
  }
53
55
  }