caplets 0.17.2 → 0.17.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.
Files changed (3) hide show
  1. package/README.md +12 -1
  2. package/dist/index.js +78 -16
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -548,7 +548,18 @@ This repository includes polished working examples under [`caplets/`](caplets/):
548
548
  - `linear`: Linear's hosted OAuth MCP endpoint.
549
549
  - `context7`: Context7 documentation lookup through `@upstash/context7-mcp`.
550
550
  - `repo-cli`: Read-oriented repository CLI workflows through `git` and package scripts.
551
- - `github-cli`: Read-oriented GitHub workflows through the `gh` CLI.
551
+ - `ast-grep`: Structural code search, scan, rewrite, rule testing, and scaffolding through the `ast-grep` CLI.
552
+ - `osv`: OSV.dev vulnerability lookups through explicit read-only HTTP actions.
553
+ - `npm`: npm registry operations through npm's published OpenAPI spec URL.
554
+ - `pypi`: PyPI project metadata, release metadata, and Simple API JSON through a curated OpenAPI spec.
555
+ - `deepwiki`: Repository-focused documentation and architecture context through DeepWiki MCP.
556
+ - `sourcegraph`: Cross-repository code search and navigation through Sourcegraph MCP.
557
+ - `playwright`: Headless browser automation for frontend inspection and testing through Playwright MCP.
558
+ - `coding-agent-toolkit`: A CapletSet that bundles high-value coding-agent examples; source children are symlinks to canonical top-level examples and installed copies are materialized as self-contained files/directories.
559
+ - `github-cli`: Pre-existing secondary read-oriented GitHub workflows through the `gh` CLI; prefer the canonical `github` MCP example for the polished GitHub integration.
560
+
561
+ GraphQL is intentionally skipped in this showcase batch so the examples can focus on HTTP,
562
+ OpenAPI, MCP, CLI, and CapletSet coverage without duplicating GitHub or GitLab surfaces.
552
563
 
553
564
  Install every example from a repo's `caplets/` directory into the current project's
554
565
  `./.caplets` directory:
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
- import { accessSync, chmodSync, closeSync, constants, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, watch, writeFileSync, writeSync } from "node:fs";
4
- import minpath, { basename, delimiter, dirname, extname, isAbsolute, join, parse, posix, relative, resolve, win32 } from "node:path";
3
+ import { accessSync, chmodSync, closeSync, constants, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, watch, writeFileSync, writeSync } from "node:fs";
4
+ import minpath, { basename, delimiter, dirname, extname, isAbsolute, join, parse, posix, relative, resolve, sep, win32 } from "node:path";
5
5
  import { execFileSync, spawn } from "node:child_process";
6
6
  import process$1, { stdin, stdout } from "node:process";
7
7
  import { fileURLToPath } from "node:url";
@@ -4709,7 +4709,7 @@ function generatedToolInputJsonSchema() {
4709
4709
  return generatedToolInputJsonSchemaForCaplet({ backend: "tool" });
4710
4710
  }
4711
4711
  //#endregion
4712
- //#region ../core/dist/options-bnsSREid.js
4712
+ //#region ../core/dist/options-j9p3L3r1.js
4713
4713
  var __create = Object.create;
4714
4714
  var __defProp = Object.defineProperty;
4715
4715
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -56439,8 +56439,21 @@ async function loadOpenApiSource(endpoint, authDir) {
56439
56439
  if (endpoint.specPath) return endpoint.specPath;
56440
56440
  if (!endpoint.specUrl) throw new CapletsError("CONFIG_INVALID", `${endpoint.server} is missing OpenAPI spec source`);
56441
56441
  if (!endpoint.baseUrl) throw new CapletsError("CONFIG_INVALID", `${endpoint.server} must configure baseUrl when using remote specUrl`);
56442
- const response = await fetchWithLimit(endpoint.specUrl, endpoint.requestTimeoutMs, shouldSendSpecAuth(endpoint) ? authHeaders(endpoint, authDir) : {});
56443
- return JSON.parse(response);
56442
+ return parseOpenApiSourceText(await fetchWithLimit(endpoint.specUrl, endpoint.requestTimeoutMs, shouldSendSpecAuth(endpoint) ? authHeaders(endpoint, authDir) : {}));
56443
+ }
56444
+ function parseOpenApiSourceText(source) {
56445
+ let parsed;
56446
+ try {
56447
+ parsed = JSON.parse(source);
56448
+ } catch (jsonError) {
56449
+ try {
56450
+ parsed = (0, import_dist$1.parse)(source);
56451
+ } catch {
56452
+ throw jsonError instanceof Error ? jsonError : new Error(String(jsonError));
56453
+ }
56454
+ }
56455
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("OpenAPI source must parse to an object");
56456
+ return parsed;
56444
56457
  }
56445
56458
  function extractOperations(endpoint, document) {
56446
56459
  const operations = [];
@@ -56458,6 +56471,7 @@ function extractOperations(endpoint, document) {
56458
56471
  const requestBody = requestBodyFor(operation);
56459
56472
  const outputSchema = outputSchemaFor(operation);
56460
56473
  const baseUrl = endpoint.baseUrl ?? firstServerUrl(document);
56474
+ const staticHeaders = staticHeaderDefaultsFor(endpoint, parameters);
56461
56475
  validateOperationBaseUrl(endpoint, baseUrl);
56462
56476
  operations.push({
56463
56477
  name,
@@ -56465,15 +56479,32 @@ function extractOperations(endpoint, document) {
56465
56479
  path,
56466
56480
  ...typeof operation.summary === "string" ? { summary: operation.summary } : {},
56467
56481
  ...typeof operation.description === "string" ? { description: operation.description } : {},
56468
- inputSchema: inputSchemaFor(parameters, requestBody),
56482
+ inputSchema: inputSchemaFor(parameters, requestBody, staticHeaders),
56469
56483
  ...outputSchema ? { outputSchema } : {},
56470
56484
  ...requestBody?.contentType ? { requestBodyContentType: requestBody.contentType } : {},
56471
- ...baseUrl ? { baseUrl } : {}
56485
+ ...baseUrl ? { baseUrl } : {},
56486
+ ...Object.keys(staticHeaders).length ? { staticHeaders } : {}
56472
56487
  });
56473
56488
  }
56474
56489
  }
56475
56490
  return operations.sort((left, right) => left.name.localeCompare(right.name));
56476
56491
  }
56492
+ function staticHeaderDefaultsFor(endpoint, parameters) {
56493
+ const configuredHeaderNames = configuredAuthHeaderNames(endpoint);
56494
+ const headers = {};
56495
+ for (const parameter of parameters) {
56496
+ if (parameter?.in !== "header" || typeof parameter.name !== "string") continue;
56497
+ const normalized = parameter.name.toLowerCase();
56498
+ if (configuredHeaderNames.has(normalized) || FORBIDDEN_ARGUMENT_HEADERS.has(normalized) && normalized !== "accept") continue;
56499
+ const defaultValue = parameter.schema?.default;
56500
+ if ([
56501
+ "string",
56502
+ "number",
56503
+ "boolean"
56504
+ ].includes(typeof defaultValue)) headers[parameter.name] = String(defaultValue);
56505
+ }
56506
+ return headers;
56507
+ }
56477
56508
  function requestBodyFor(operation) {
56478
56509
  const requestBody = operation.requestBody;
56479
56510
  if (!requestBody || typeof requestBody !== "object") return;
@@ -56534,19 +56565,20 @@ function structuredOutputSchema(bodySchema) {
56534
56565
  }
56535
56566
  };
56536
56567
  }
56537
- function inputSchemaFor(parameters, requestBody) {
56568
+ function inputSchemaFor(parameters, requestBody, staticHeaders = {}) {
56538
56569
  const schema = {
56539
56570
  type: "object",
56540
56571
  additionalProperties: false,
56541
56572
  properties: {}
56542
56573
  };
56543
56574
  const required = [];
56575
+ const protectedStaticHeaders = new Set(Object.keys(staticHeaders).map((key) => key.toLowerCase()).filter((key) => FORBIDDEN_ARGUMENT_HEADERS.has(key)));
56544
56576
  for (const location of [
56545
56577
  "path",
56546
56578
  "query",
56547
56579
  "header"
56548
56580
  ]) {
56549
- const locationParameters = parameters.filter((parameter) => parameter?.in === location);
56581
+ const locationParameters = parameters.filter((parameter) => parameter?.in === location && !(location === "header" && protectedStaticHeaders.has(parameter.name?.toLowerCase())));
56550
56582
  if (locationParameters.length === 0) continue;
56551
56583
  const nestedRequired = locationParameters.filter((parameter) => parameter.required === true || location === "path").map((parameter) => parameter.name);
56552
56584
  schema.properties[location] = {
@@ -56585,7 +56617,8 @@ function buildRequest(endpoint, operation, args, authDir) {
56585
56617
  for (const [key, value] of Object.entries(asRecord(args.query))) if (value !== void 0 && value !== null) url.searchParams.append(key, serializeHttpValue("query", key, value));
56586
56618
  const headers = new Headers();
56587
56619
  applyAuth(headers, endpoint, authDir);
56588
- const configuredHeaderNames = endpoint.auth.type === "headers" ? new Set(Object.keys(endpoint.auth.headers).map((key) => key.toLowerCase())) : /* @__PURE__ */ new Set();
56620
+ const configuredHeaderNames = configuredAuthHeaderNames(endpoint);
56621
+ for (const [key, value] of Object.entries(operation.staticHeaders ?? {})) if (!headers.has(key) && !configuredHeaderNames.has(key.toLowerCase())) headers.set(key, value);
56589
56622
  for (const [key, value] of Object.entries(asRecord(args.header))) if (value !== void 0 && value !== null) {
56590
56623
  const normalized = key.toLowerCase();
56591
56624
  if (FORBIDDEN_ARGUMENT_HEADERS.has(normalized) || configuredHeaderNames.has(normalized)) throw new CapletsError("REQUEST_INVALID", `Header ${key} cannot be supplied by arguments`);
@@ -56631,6 +56664,9 @@ function asRecord(value) {
56631
56664
  function applyAuth(headers, endpoint, authDir) {
56632
56665
  for (const [key, value] of Object.entries(authHeaders(endpoint, authDir))) headers.set(key, value);
56633
56666
  }
56667
+ function configuredAuthHeaderNames(endpoint) {
56668
+ return endpoint.auth.type === "headers" ? new Set(Object.keys(endpoint.auth.headers).map((key) => key.toLowerCase())) : /* @__PURE__ */ new Set();
56669
+ }
56634
56670
  function authHeaders(endpoint, authDir) {
56635
56671
  switch (endpoint.auth.type) {
56636
56672
  case "none": return {};
@@ -57625,7 +57661,7 @@ var CapletsEngine = class {
57625
57661
  }
57626
57662
  }
57627
57663
  async completeCliWords(words) {
57628
- const { completeCliWords } = await Promise.resolve().then(() => completion_L23s2FGB_exports).then((n) => n.r);
57664
+ const { completeCliWords } = await Promise.resolve().then(() => completion_Cq1z7ci2_exports).then((n) => n.r);
57629
57665
  return await completeCliWords(words, {
57630
57666
  config: this.registry.config,
57631
57667
  managers: {
@@ -57963,8 +57999,8 @@ function hasEnv$1(value) {
57963
57999
  return value !== void 0 && value.trim() !== "";
57964
58000
  }
57965
58001
  //#endregion
57966
- //#region ../core/dist/completion-L23s2FGB.js
57967
- var completion_L23s2FGB_exports = /* @__PURE__ */ __exportAll$1({
58002
+ //#region ../core/dist/completion-Cq1z7ci2.js
58003
+ var completion_Cq1z7ci2_exports = /* @__PURE__ */ __exportAll$1({
57968
58004
  a: () => formatCapletList,
57969
58005
  c: () => resolveCliConfigPaths,
57970
58006
  i: () => trailingSpaceCompletionToken,
@@ -59827,7 +59863,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
59827
59863
  values: [],
59828
59864
  hasMore: false
59829
59865
  } };
59830
- var version$1 = "0.18.2";
59866
+ var version$1 = "0.18.3";
59831
59867
  var CapletsMcpSession = class {
59832
59868
  engine;
59833
59869
  server;
@@ -63626,12 +63662,14 @@ function rejectCrossKindDestinationCollision(plan, destinationRoot) {
63626
63662
  function installPlan(caplet, options) {
63627
63663
  const isDirectory = basename(caplet.path) === "CAPLET.md";
63628
63664
  const sourcePath = isDirectory ? dirname(caplet.path) : caplet.path;
63665
+ const sourceBoundary = dirname(sourcePath);
63629
63666
  const sourcePathRelative = relative(options.repoRoot, sourcePath);
63630
63667
  const destination = isDirectory ? join(options.destinationRoot, caplet.id) : join(options.destinationRoot, `${caplet.id}.md`);
63631
63668
  return {
63632
63669
  id: caplet.id,
63633
63670
  source: `${options.sourceId}#${sourcePathRelative}`,
63634
63671
  sourcePath,
63672
+ sourceBoundary,
63635
63673
  destination,
63636
63674
  kind: isDirectory ? "directory" : "file"
63637
63675
  };
@@ -63693,16 +63731,40 @@ function removeInstallPath(path, label, force) {
63693
63731
  }
63694
63732
  function copyInstallPath(plan) {
63695
63733
  try {
63734
+ if (plan.kind === "directory") {
63735
+ copyDirectoryCaplet(plan.sourcePath, plan.destination, realpathSync(plan.sourceBoundary));
63736
+ return;
63737
+ }
63696
63738
  cpSync(plan.sourcePath, plan.destination, {
63697
- recursive: plan.kind === "directory",
63739
+ recursive: false,
63698
63740
  force: false,
63699
63741
  errorOnExist: true
63700
63742
  });
63701
63743
  } catch (error) {
63744
+ if (error instanceof CapletsError) throw error;
63702
63745
  if (isFsError(error, "EEXIST") || isFsError(error, "EISDIR")) throw new CapletsError("CONFIG_EXISTS", `Caplet ${plan.id} already exists at ${plan.destination}; pass --force to overwrite it`, toSafeError(error));
63703
63746
  throw new CapletsError("CONFIG_INVALID", `Could not install Caplet ${plan.id} to ${plan.destination}`, toSafeError(error));
63704
63747
  }
63705
63748
  }
63749
+ function copyDirectoryCaplet(source, destination, sourceBoundary, seenDirectories = /* @__PURE__ */ new Set()) {
63750
+ const resolvedSource = lstatSync(source).isSymbolicLink() ? resolveDirectoryCapletSymlink(source, sourceBoundary) : source;
63751
+ if (statSync(resolvedSource).isDirectory()) {
63752
+ const realDirectory = realpathSync(resolvedSource);
63753
+ if (seenDirectories.has(realDirectory)) throw new CapletsError("CONFIG_INVALID", `Directory Caplet symlink ${source} creates a copy cycle`);
63754
+ const childSeenDirectories = new Set(seenDirectories);
63755
+ childSeenDirectories.add(realDirectory);
63756
+ mkdirSync(destination);
63757
+ for (const entry of readdirSync(resolvedSource)) copyDirectoryCaplet(join(resolvedSource, entry), join(destination, entry), sourceBoundary, childSeenDirectories);
63758
+ return;
63759
+ }
63760
+ copyFileSync(resolvedSource, destination);
63761
+ }
63762
+ function resolveDirectoryCapletSymlink(source, sourceBoundary) {
63763
+ const target = readlinkSync(source);
63764
+ const resolvedTarget = realpathSync(isAbsolute(target) ? target : resolve(dirname(source), target));
63765
+ if (resolvedTarget !== sourceBoundary && !resolvedTarget.startsWith(`${sourceBoundary}${sep}`)) throw new CapletsError("CONFIG_INVALID", `Directory Caplet symlink ${source} resolves outside source Caplets boundary`);
63766
+ return resolvedTarget;
63767
+ }
63706
63768
  function isFsError(error, code) {
63707
63769
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
63708
63770
  }
@@ -68811,7 +68873,7 @@ function writeAddResult(writeOut, label, result) {
68811
68873
  }
68812
68874
  //#endregion
68813
68875
  //#region package.json
68814
- var version = "0.17.2";
68876
+ var version = "0.17.3";
68815
68877
  //#endregion
68816
68878
  //#region src/index.ts
68817
68879
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caplets",
3
- "version": "0.17.2",
3
+ "version": "0.17.3",
4
4
  "description": "Progressive disclosure gateway CLI for MCP servers and native Caplets adapters.",
5
5
  "keywords": [
6
6
  "caplets",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@modelcontextprotocol/sdk": "^1.29.0",
37
- "@caplets/core": "0.18.2"
37
+ "@caplets/core": "0.18.3"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^25.9.1",