caplets 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -11
- package/dist/index.js +325 -79
- package/package.json +7 -2
- package/schemas/caplet.schema.json +32 -0
- package/schemas/caplets-config.schema.json +40 -0
package/README.md
CHANGED
|
@@ -11,6 +11,52 @@ or call that backend's underlying tools or operations.
|
|
|
11
11
|
This keeps the initial MCP tool list small, makes tool selection easier, and avoids
|
|
12
12
|
flattened tool-name collisions across servers.
|
|
13
13
|
|
|
14
|
+
## Why It Matters
|
|
15
|
+
|
|
16
|
+
Large MCP setups make agents worse before they make them better. If every downstream
|
|
17
|
+
server exposes every tool up front, the model starts with a noisy flat list, duplicate
|
|
18
|
+
tool names, and a bigger context surface before it knows which capability matters.
|
|
19
|
+
|
|
20
|
+
Caplets turns that flat tool wall into progressive disclosure: one capability card first,
|
|
21
|
+
then scoped discovery only after the agent chooses the relevant domain.
|
|
22
|
+
|
|
23
|
+
## Benchmark Results
|
|
24
|
+
|
|
25
|
+
In Caplets' reproducible coding-agent benchmark, the same three mock MCP servers are
|
|
26
|
+
exposed two ways: direct flat MCP aggregation versus Caplets progressive disclosure.
|
|
27
|
+
|
|
28
|
+
| Initial Agent Surface | Direct Flat MCP | Caplets | Reduction |
|
|
29
|
+
| ------------------------- | ----------------: | -----------: | ------------: |
|
|
30
|
+
| Visible tools | 106 | 3 | 97.2% fewer |
|
|
31
|
+
| Serialized MCP payload | 32,090 bytes | 8,358 bytes | 74.0% smaller |
|
|
32
|
+
| Approx. context surface | 8,023 tokens | 2,090 tokens | 5,933 fewer |
|
|
33
|
+
| Top-level name collisions | 3 duplicate names | 0 | eliminated |
|
|
34
|
+
|
|
35
|
+
The important part: Caplets does not remove access to the downstream tools. It hides
|
|
36
|
+
them behind scoped discovery operations like `search_tools`, `get_tool`, and `call_tool`,
|
|
37
|
+
so the agent sees less up front while still being able to reach the same capabilities.
|
|
38
|
+
|
|
39
|
+
A local OpenCode live benchmark also completed the full benchmark matrix successfully:
|
|
40
|
+
|
|
41
|
+
| Agent | Mode | Tasks Passed |
|
|
42
|
+
| ------------------------------ | --------------- | -----------: |
|
|
43
|
+
| OpenCode `openai/gpt-5.5-fast` | Direct flat MCP | 2/2 |
|
|
44
|
+
| OpenCode `openai/gpt-5.5-fast` | Caplets | 2/2 |
|
|
45
|
+
|
|
46
|
+
Live results are intentionally not committed as product claims because they depend on
|
|
47
|
+
local agent CLIs, credentials, models, providers, and agent behavior. The deterministic
|
|
48
|
+
surface benchmark is the reproducible claim.
|
|
49
|
+
|
|
50
|
+
See [`docs/benchmarks/coding-agent.md`](docs/benchmarks/coding-agent.md) for methodology,
|
|
51
|
+
limitations, and reproduction commands.
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
pnpm benchmark
|
|
55
|
+
pnpm benchmark:check
|
|
56
|
+
pnpm build
|
|
57
|
+
CAPLETS_BENCH_LIVE=1 pnpm benchmark:live:opencode -- --model openai/gpt-5.5-fast
|
|
58
|
+
```
|
|
59
|
+
|
|
14
60
|
## Inspiration
|
|
15
61
|
|
|
16
62
|
Caplets is a mashup of two ideas that work well separately but leave a gap together:
|
|
@@ -28,7 +74,7 @@ the agent chooses that server and asks to search, list, inspect, or call them.
|
|
|
28
74
|
|
|
29
75
|
## What It Does
|
|
30
76
|
|
|
31
|
-
- Reads downstream MCP server definitions, native OpenAPI endpoint definitions, native GraphQL endpoint definitions, and explicit HTTP API action definitions from
|
|
77
|
+
- Reads downstream MCP server definitions, native OpenAPI endpoint definitions, native GraphQL endpoint definitions, and explicit HTTP API action definitions from the user config file.
|
|
32
78
|
- Registers one generated MCP tool for each enabled MCP server, OpenAPI endpoint, GraphQL endpoint, or HTTP API.
|
|
33
79
|
- Uses the configured server ID as the generated tool name.
|
|
34
80
|
- Uses the configured `name` and `description` as the capability card shown to agents.
|
|
@@ -59,7 +105,7 @@ pnpm build
|
|
|
59
105
|
|
|
60
106
|
## Configure
|
|
61
107
|
|
|
62
|
-
Create a starter
|
|
108
|
+
Create a starter user config at `${XDG_CONFIG_HOME:-~/.config}/caplets/config.json` on Unix-like platforms or `%APPDATA%\caplets\config.json` on Windows:
|
|
63
109
|
|
|
64
110
|
```sh
|
|
65
111
|
caplets init
|
|
@@ -143,7 +189,7 @@ you want Caplets to expose:
|
|
|
143
189
|
}
|
|
144
190
|
```
|
|
145
191
|
|
|
146
|
-
The default config path can be overridden with `CAPLETS_CONFIG`:
|
|
192
|
+
The default config path is `${XDG_CONFIG_HOME:-~/.config}/caplets/config.json` on Unix-like platforms and `%APPDATA%\caplets\config.json` on Windows. It can be overridden with `CAPLETS_CONFIG`:
|
|
147
193
|
|
|
148
194
|
```sh
|
|
149
195
|
CAPLETS_CONFIG=/path/to/config.json caplets init
|
|
@@ -279,11 +325,13 @@ caplets install spiritledsoftware/caplets github linear
|
|
|
279
325
|
```
|
|
280
326
|
|
|
281
327
|
`caplets install` accepts a GitHub `owner/repo` shorthand, a Git URL, or a local repository path.
|
|
282
|
-
It installs into your user Caplets root, which is
|
|
283
|
-
of `CAPLETS_CONFIG` when that environment variable is set.
|
|
284
|
-
unless `--force` is passed.
|
|
328
|
+
It installs into your user Caplets root, which is `${XDG_CONFIG_HOME:-~/.config}/caplets` on Unix-like platforms,
|
|
329
|
+
`%APPDATA%\caplets` on Windows, or the parent directory of `CAPLETS_CONFIG` when that environment variable is set.
|
|
330
|
+
Existing Caplets are not overwritten unless `--force` is passed.
|
|
331
|
+
|
|
332
|
+
On Unix-like platforms, relative `XDG_CONFIG_HOME` and `XDG_STATE_HOME` values are ignored.
|
|
285
333
|
|
|
286
|
-
Caplets always loads user Caplet files from
|
|
334
|
+
Caplets always loads user Caplet files from the user Caplets root. Project `./.caplets/config.json`
|
|
287
335
|
is still loaded as project config, but project Markdown Caplet files are executable
|
|
288
336
|
configuration and are ignored unless explicitly trusted:
|
|
289
337
|
|
|
@@ -511,10 +559,11 @@ For headless terminals:
|
|
|
511
559
|
caplets auth login <server> --no-open
|
|
512
560
|
```
|
|
513
561
|
|
|
514
|
-
OAuth/OIDC tokens are stored under
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
562
|
+
OAuth/OIDC tokens are stored under `${XDG_STATE_HOME:-~/.local/state}/caplets/auth/<server>.json`
|
|
563
|
+
on Unix-like platforms and `%LOCALAPPDATA%\caplets\auth\<server>.json` on Windows.
|
|
564
|
+
Token files use owner-only file permissions where the platform supports them. Caplets supports
|
|
565
|
+
well-known OAuth/OIDC discovery and dynamic client registration when advertised. When a token expires,
|
|
566
|
+
run `caplets auth login <server>` again.
|
|
518
567
|
|
|
519
568
|
To inspect or remove stored OAuth credentials:
|
|
520
569
|
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import minproc, { stdin, stdout, default as process$1 } from "node:process";
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
|
-
import minpath, { basename, dirname, extname, isAbsolute, join, parse, relative, resolve,
|
|
5
|
+
import minpath, { basename, dirname, extname, isAbsolute, join, parse, posix, relative, resolve, win32 } from "node:path";
|
|
6
6
|
import { accessSync, chmodSync, constants, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
|
|
7
7
|
import { createInterface } from "node:readline/promises";
|
|
8
8
|
import { createServer } from "node:http";
|
|
@@ -180,7 +180,7 @@ const allowsEval = /* @__PURE__ */ cached(() => {
|
|
|
180
180
|
return false;
|
|
181
181
|
}
|
|
182
182
|
});
|
|
183
|
-
function isPlainObject$
|
|
183
|
+
function isPlainObject$6(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$5(o) {
|
|
|
191
191
|
return true;
|
|
192
192
|
}
|
|
193
193
|
function shallowClone(o) {
|
|
194
|
-
if (isPlainObject$
|
|
194
|
+
if (isPlainObject$6(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$
|
|
277
|
+
if (!isPlainObject$6(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$
|
|
293
|
+
if (!isPlainObject$6(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$
|
|
1907
|
+
if (isPlainObject$6(a) && isPlainObject$6(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$
|
|
1983
|
+
if (!isPlainObject$6(input)) {
|
|
1984
1984
|
payload.issues.push({
|
|
1985
1985
|
expected: "record",
|
|
1986
1986
|
code: "invalid_type",
|
|
@@ -9619,7 +9619,7 @@ const { program, createCommand, createArgument, createOption, CommanderError, In
|
|
|
9619
9619
|
})))(), 1)).default;
|
|
9620
9620
|
//#endregion
|
|
9621
9621
|
//#region package.json
|
|
9622
|
-
var version = "0.
|
|
9622
|
+
var version = "0.10.0";
|
|
9623
9623
|
//#endregion
|
|
9624
9624
|
//#region node_modules/.pnpm/pkce-challenge@5.0.1/node_modules/pkce-challenge/dist/index.node.js
|
|
9625
9625
|
let crypto;
|
|
@@ -10658,6 +10658,41 @@ async function registerClient(authorizationServerUrl, { metadata, clientMetadata
|
|
|
10658
10658
|
return OAuthClientInformationFullSchema.parse(await response.json());
|
|
10659
10659
|
}
|
|
10660
10660
|
//#endregion
|
|
10661
|
+
//#region src/config/paths.ts
|
|
10662
|
+
function defaultConfigBaseDir(env = process.env, home = homedir(), platform = process.platform) {
|
|
10663
|
+
if (platform === "win32") return env.APPDATA && win32.isAbsolute(env.APPDATA) ? env.APPDATA : win32.join(home, "AppData", "Roaming");
|
|
10664
|
+
return env.XDG_CONFIG_HOME && posix.isAbsolute(env.XDG_CONFIG_HOME) ? env.XDG_CONFIG_HOME : posix.join(home, ".config");
|
|
10665
|
+
}
|
|
10666
|
+
function defaultStateBaseDir(env = process.env, home = homedir(), platform = process.platform) {
|
|
10667
|
+
if (platform === "win32") return env.LOCALAPPDATA && win32.isAbsolute(env.LOCALAPPDATA) ? env.LOCALAPPDATA : win32.join(home, "AppData", "Local");
|
|
10668
|
+
return env.XDG_STATE_HOME && posix.isAbsolute(env.XDG_STATE_HOME) ? env.XDG_STATE_HOME : posix.join(home, ".local", "state");
|
|
10669
|
+
}
|
|
10670
|
+
function defaultConfigPath(env = process.env, home = homedir(), platform = process.platform) {
|
|
10671
|
+
return (platform === "win32" ? win32.join : posix.join)(defaultConfigBaseDir(env, home, platform), "caplets", "config.json");
|
|
10672
|
+
}
|
|
10673
|
+
function defaultAuthDir(env = process.env, home = homedir(), platform = process.platform) {
|
|
10674
|
+
return (platform === "win32" ? win32.join : posix.join)(defaultStateBaseDir(env, home, platform), "caplets", "auth");
|
|
10675
|
+
}
|
|
10676
|
+
const DEFAULT_CONFIG_PATH = defaultConfigPath();
|
|
10677
|
+
const DEFAULT_AUTH_DIR = defaultAuthDir();
|
|
10678
|
+
const PROJECT_CONFIG_FILE = join(".caplets", "config.json");
|
|
10679
|
+
const TRUST_PROJECT_CAPLETS_ENV = "CAPLETS_TRUST_PROJECT_CAPLETS";
|
|
10680
|
+
function resolveConfigPath(path) {
|
|
10681
|
+
return path ?? DEFAULT_CONFIG_PATH;
|
|
10682
|
+
}
|
|
10683
|
+
function resolveProjectConfigPath(cwd = process.cwd()) {
|
|
10684
|
+
return join(cwd, PROJECT_CONFIG_FILE);
|
|
10685
|
+
}
|
|
10686
|
+
function resolveCapletsRoot(configPath = resolveConfigPath()) {
|
|
10687
|
+
return dirname(configPath);
|
|
10688
|
+
}
|
|
10689
|
+
function resolveProjectCapletsRoot(cwd = process.cwd()) {
|
|
10690
|
+
return join(cwd, ".caplets");
|
|
10691
|
+
}
|
|
10692
|
+
function isTrustedEnvEnabled(value) {
|
|
10693
|
+
return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
|
|
10694
|
+
}
|
|
10695
|
+
//#endregion
|
|
10661
10696
|
//#region src/errors.ts
|
|
10662
10697
|
var CapletsError = class extends Error {
|
|
10663
10698
|
code;
|
|
@@ -10708,11 +10743,12 @@ function errorResult(error, fallback) {
|
|
|
10708
10743
|
}
|
|
10709
10744
|
//#endregion
|
|
10710
10745
|
//#region src/auth/store.ts
|
|
10711
|
-
function authStorePath(server, authDir =
|
|
10746
|
+
function authStorePath(server, authDir = DEFAULT_AUTH_DIR) {
|
|
10712
10747
|
if (!server || server.includes("/") || server.includes("\\") || server.includes("..")) throw new CapletsError("REQUEST_INVALID", `Invalid auth store server name ${server}`);
|
|
10713
10748
|
const authRoot = resolve(authDir);
|
|
10714
10749
|
const candidate = resolve(authRoot, `${server}.json`);
|
|
10715
|
-
|
|
10750
|
+
const relativePath = relative(authRoot, candidate);
|
|
10751
|
+
if (relativePath && !relativePath.startsWith("..") && !isAbsolute(relativePath)) return candidate;
|
|
10716
10752
|
throw new CapletsError("REQUEST_INVALID", `Invalid auth store server name ${server}`);
|
|
10717
10753
|
}
|
|
10718
10754
|
function readTokenBundle(server, authDir) {
|
|
@@ -10780,11 +10816,13 @@ var FileOAuthProvider = class {
|
|
|
10780
10816
|
verifier = base64url(randomBytes(32));
|
|
10781
10817
|
stateValue = base64url(randomBytes(24));
|
|
10782
10818
|
clientInfo;
|
|
10819
|
+
clientMetadataUrl;
|
|
10783
10820
|
constructor(server, redirectUrl, onRedirect, authDir) {
|
|
10784
10821
|
this.server = server;
|
|
10785
10822
|
this.redirectUrl = redirectUrl;
|
|
10786
10823
|
this.onRedirect = onRedirect;
|
|
10787
10824
|
this.authDir = authDir;
|
|
10825
|
+
if ((this.server.auth?.type === "oauth2" || this.server.auth?.type === "oidc") && this.server.auth.clientMetadataUrl) this.clientMetadataUrl = this.server.auth.clientMetadataUrl;
|
|
10788
10826
|
}
|
|
10789
10827
|
get clientMetadata() {
|
|
10790
10828
|
return {
|
|
@@ -10884,10 +10922,19 @@ async function runOAuthFlow(server, options = {}) {
|
|
|
10884
10922
|
authorizationCode: completion.code,
|
|
10885
10923
|
...scope ? { scope } : {}
|
|
10886
10924
|
});
|
|
10925
|
+
} catch (error) {
|
|
10926
|
+
throw normalizeMcpOAuthError(server, error);
|
|
10887
10927
|
} finally {
|
|
10888
10928
|
await callback.close();
|
|
10889
10929
|
}
|
|
10890
10930
|
}
|
|
10931
|
+
function normalizeMcpOAuthError(server, error) {
|
|
10932
|
+
if ((server.auth?.type === "oauth2" || server.auth?.type === "oidc") && !server.auth.clientId && !server.auth.clientMetadataUrl && error instanceof Error && error.message.includes("does not support dynamic client registration")) return new CapletsError("AUTH_FAILED", "OAuth is not available for this server without a host-specific OAuth app or PAT auth", {
|
|
10933
|
+
server: server.server,
|
|
10934
|
+
nextAction: "configure_bearer_auth_or_host_oauth_app"
|
|
10935
|
+
});
|
|
10936
|
+
return error;
|
|
10937
|
+
}
|
|
10891
10938
|
async function runGenericOAuthFlow(target, options = {}) {
|
|
10892
10939
|
if (target.auth?.type !== "oauth2" && target.auth?.type !== "oidc") throw new CapletsError("REQUEST_INVALID", `${target.server} is not configured for OAuth`);
|
|
10893
10940
|
const authConfig = target.auth;
|
|
@@ -11057,6 +11104,11 @@ async function resolveGenericClient(target, authConfig, metadata, redirectUri, a
|
|
|
11057
11104
|
...authConfig.clientSecret ? { clientSecret: authConfig.clientSecret } : {},
|
|
11058
11105
|
dynamic: false
|
|
11059
11106
|
};
|
|
11107
|
+
if (authConfig.clientMetadataUrl) return {
|
|
11108
|
+
clientId: authConfig.clientMetadataUrl,
|
|
11109
|
+
...authConfig.clientSecret ? { clientSecret: authConfig.clientSecret } : {},
|
|
11110
|
+
dynamic: false
|
|
11111
|
+
};
|
|
11060
11112
|
if (!metadata.registration_endpoint) throw new CapletsError("AUTH_FAILED", "OAuth clientId is required without dynamic registration", { server: target.server });
|
|
11061
11113
|
const response = await fetchJson(metadata.registration_endpoint, target.requestTimeoutMs, {
|
|
11062
11114
|
method: "POST",
|
|
@@ -11125,8 +11177,9 @@ function assertAllowedAuthUrl(value, label, allowLoopbackHttp = false) {
|
|
|
11125
11177
|
throw new CapletsError("AUTH_FAILED", `${label} must use https except loopback development URLs`);
|
|
11126
11178
|
}
|
|
11127
11179
|
function assertTokenBundleMatchesTarget(bundle, target, authConfig) {
|
|
11180
|
+
const configuredClientId = authConfig.clientId ?? authConfig.clientMetadataUrl;
|
|
11128
11181
|
const expectedOrigin = protectedResourceOrigin(target, authConfig);
|
|
11129
|
-
if (bundle.authType !== authConfig.type || expectedOrigin && bundle.protectedResourceOrigin !== expectedOrigin ||
|
|
11182
|
+
if (bundle.authType !== authConfig.type || expectedOrigin && bundle.protectedResourceOrigin !== expectedOrigin || configuredClientId && bundle.clientId !== configuredClientId || authConfig.issuer && bundle.issuer !== authConfig.issuer) throw new CapletsError("AUTH_REQUIRED", `OAuth credentials for ${target.server} do not match the configured backend`, {
|
|
11130
11183
|
server: target.server,
|
|
11131
11184
|
backend: target.backend,
|
|
11132
11185
|
authType: authConfig.type,
|
|
@@ -18762,6 +18815,7 @@ const capletRemoteAuthSchema = discriminatedUnion("type", [
|
|
|
18762
18815
|
resourceMetadataUrl: string().min(1).optional(),
|
|
18763
18816
|
authorizationServerMetadataUrl: string().min(1).optional(),
|
|
18764
18817
|
openidConfigurationUrl: string().min(1).optional(),
|
|
18818
|
+
clientMetadataUrl: string().min(1).optional(),
|
|
18765
18819
|
clientId: string().min(1).optional(),
|
|
18766
18820
|
clientSecret: string().min(1).optional(),
|
|
18767
18821
|
scopes: array(string().min(1)).optional(),
|
|
@@ -18775,6 +18829,7 @@ const capletRemoteAuthSchema = discriminatedUnion("type", [
|
|
|
18775
18829
|
resourceMetadataUrl: string().min(1).optional(),
|
|
18776
18830
|
authorizationServerMetadataUrl: string().min(1).optional(),
|
|
18777
18831
|
openidConfigurationUrl: string().min(1).optional(),
|
|
18832
|
+
clientMetadataUrl: string().min(1).optional(),
|
|
18778
18833
|
clientId: string().min(1).optional(),
|
|
18779
18834
|
clientSecret: string().min(1).optional(),
|
|
18780
18835
|
scopes: array(string().min(1)).optional(),
|
|
@@ -18799,6 +18854,7 @@ const capletEndpointAuthSchema = discriminatedUnion("type", [
|
|
|
18799
18854
|
resourceMetadataUrl: string().min(1).optional(),
|
|
18800
18855
|
authorizationServerMetadataUrl: string().min(1).optional(),
|
|
18801
18856
|
openidConfigurationUrl: string().min(1).optional(),
|
|
18857
|
+
clientMetadataUrl: string().min(1).optional(),
|
|
18802
18858
|
clientId: string().min(1).optional(),
|
|
18803
18859
|
clientSecret: string().min(1).optional(),
|
|
18804
18860
|
scopes: array(string().min(1)).optional(),
|
|
@@ -18812,6 +18868,7 @@ const capletEndpointAuthSchema = discriminatedUnion("type", [
|
|
|
18812
18868
|
resourceMetadataUrl: string().min(1).optional(),
|
|
18813
18869
|
authorizationServerMetadataUrl: string().min(1).optional(),
|
|
18814
18870
|
openidConfigurationUrl: string().min(1).optional(),
|
|
18871
|
+
clientMetadataUrl: string().min(1).optional(),
|
|
18815
18872
|
clientId: string().min(1).optional(),
|
|
18816
18873
|
clientSecret: string().min(1).optional(),
|
|
18817
18874
|
scopes: array(string().min(1)).optional(),
|
|
@@ -18855,10 +18912,11 @@ const capletMcpServerSchema = object$1({
|
|
|
18855
18912
|
path: ["url"],
|
|
18856
18913
|
message: "remote url must use https except loopback development urls"
|
|
18857
18914
|
});
|
|
18858
|
-
if (server.auth?.type === "oauth2") for (const field of [
|
|
18915
|
+
if (server.auth?.type === "oauth2" || server.auth?.type === "oidc") for (const field of [
|
|
18859
18916
|
"authorizationUrl",
|
|
18860
18917
|
"tokenUrl",
|
|
18861
18918
|
"issuer",
|
|
18919
|
+
"clientMetadataUrl",
|
|
18862
18920
|
"redirectUri"
|
|
18863
18921
|
]) {
|
|
18864
18922
|
const value = server.auth[field];
|
|
@@ -19015,13 +19073,13 @@ function loadCapletFiles(root) {
|
|
|
19015
19073
|
for (const candidate of discoverCapletFiles(root)) {
|
|
19016
19074
|
if (servers[candidate.id] || openapiEndpoints[candidate.id] || graphqlEndpoints[candidate.id] || httpApis[candidate.id]) throw new CapletsError("CONFIG_INVALID", `Duplicate Caplet ID ${candidate.id} under ${root}`);
|
|
19017
19075
|
const config = readCapletFile(candidate.path);
|
|
19018
|
-
if (isPlainObject$
|
|
19076
|
+
if (isPlainObject$5(config) && config.backend === "openapi") {
|
|
19019
19077
|
const { backend: _backend, ...endpoint } = config;
|
|
19020
19078
|
openapiEndpoints[candidate.id] = endpoint;
|
|
19021
|
-
} else if (isPlainObject$
|
|
19079
|
+
} else if (isPlainObject$5(config) && config.backend === "graphql") {
|
|
19022
19080
|
const { backend: _backend, ...endpoint } = config;
|
|
19023
19081
|
graphqlEndpoints[candidate.id] = endpoint;
|
|
19024
|
-
} else if (isPlainObject$
|
|
19082
|
+
} else if (isPlainObject$5(config) && config.backend === "http") {
|
|
19025
19083
|
const { backend: _backend, ...endpoint } = config;
|
|
19026
19084
|
httpApis[candidate.id] = endpoint;
|
|
19027
19085
|
} else servers[candidate.id] = config;
|
|
@@ -19142,7 +19200,7 @@ function parseFrontmatter(text, path) {
|
|
|
19142
19200
|
value: text
|
|
19143
19201
|
});
|
|
19144
19202
|
matter(file, { strip: true });
|
|
19145
|
-
if (!isPlainObject$
|
|
19203
|
+
if (!isPlainObject$5(file.data.matter) || Object.keys(file.data.matter).length === 0) throw new Error("empty frontmatter");
|
|
19146
19204
|
return {
|
|
19147
19205
|
frontmatter: file.data.matter,
|
|
19148
19206
|
body: String(file)
|
|
@@ -19151,7 +19209,7 @@ function parseFrontmatter(text, path) {
|
|
|
19151
19209
|
throw new CapletsError("CONFIG_INVALID", `Caplet file at ${path} has invalid YAML frontmatter`, redactSecrets(error));
|
|
19152
19210
|
}
|
|
19153
19211
|
}
|
|
19154
|
-
function isPlainObject$
|
|
19212
|
+
function isPlainObject$5(value) {
|
|
19155
19213
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
19156
19214
|
}
|
|
19157
19215
|
function validateCapletId(id, path) {
|
|
@@ -19161,27 +19219,6 @@ function hasEnvReference$1(value) {
|
|
|
19161
19219
|
return /\$\{[A-Za-z_][A-Za-z0-9_]*\}|\$env:[A-Za-z_][A-Za-z0-9_]*/.test(value);
|
|
19162
19220
|
}
|
|
19163
19221
|
//#endregion
|
|
19164
|
-
//#region src/config/paths.ts
|
|
19165
|
-
const DEFAULT_CONFIG_PATH = join(homedir(), ".caplets", "config.json");
|
|
19166
|
-
const DEFAULT_AUTH_DIR = join(homedir(), ".caplets", "auth");
|
|
19167
|
-
const PROJECT_CONFIG_FILE = join(".caplets", "config.json");
|
|
19168
|
-
const TRUST_PROJECT_CAPLETS_ENV = "CAPLETS_TRUST_PROJECT_CAPLETS";
|
|
19169
|
-
function resolveConfigPath(path) {
|
|
19170
|
-
return path ?? DEFAULT_CONFIG_PATH;
|
|
19171
|
-
}
|
|
19172
|
-
function resolveProjectConfigPath(cwd = process.cwd()) {
|
|
19173
|
-
return join(cwd, PROJECT_CONFIG_FILE);
|
|
19174
|
-
}
|
|
19175
|
-
function resolveCapletsRoot(configPath = resolveConfigPath()) {
|
|
19176
|
-
return dirname(configPath);
|
|
19177
|
-
}
|
|
19178
|
-
function resolveProjectCapletsRoot(cwd = process.cwd()) {
|
|
19179
|
-
return join(cwd, ".caplets");
|
|
19180
|
-
}
|
|
19181
|
-
function isTrustedEnvEnabled(value) {
|
|
19182
|
-
return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
|
|
19183
|
-
}
|
|
19184
|
-
//#endregion
|
|
19185
19222
|
//#region src/config.ts
|
|
19186
19223
|
const NON_INTERPOLATED_SERVER_FIELDS = new Set([
|
|
19187
19224
|
"name",
|
|
@@ -19207,6 +19244,7 @@ const remoteAuthSchema = discriminatedUnion("type", [
|
|
|
19207
19244
|
resourceMetadataUrl: string().url().optional(),
|
|
19208
19245
|
authorizationServerMetadataUrl: string().url().optional(),
|
|
19209
19246
|
openidConfigurationUrl: string().url().optional(),
|
|
19247
|
+
clientMetadataUrl: string().url().optional(),
|
|
19210
19248
|
clientId: string().min(1).optional(),
|
|
19211
19249
|
clientSecret: string().min(1).optional(),
|
|
19212
19250
|
scopes: array(string().min(1)).optional(),
|
|
@@ -19220,6 +19258,7 @@ const remoteAuthSchema = discriminatedUnion("type", [
|
|
|
19220
19258
|
resourceMetadataUrl: string().url().optional(),
|
|
19221
19259
|
authorizationServerMetadataUrl: string().url().optional(),
|
|
19222
19260
|
openidConfigurationUrl: string().url().optional(),
|
|
19261
|
+
clientMetadataUrl: string().url().optional(),
|
|
19223
19262
|
clientId: string().min(1).optional(),
|
|
19224
19263
|
clientSecret: string().min(1).optional(),
|
|
19225
19264
|
scopes: array(string().min(1)).optional(),
|
|
@@ -19234,6 +19273,7 @@ const oauthLikeAuthSchema = union([object$1({
|
|
|
19234
19273
|
resourceMetadataUrl: string().url().optional(),
|
|
19235
19274
|
authorizationServerMetadataUrl: string().url().optional(),
|
|
19236
19275
|
openidConfigurationUrl: string().url().optional(),
|
|
19276
|
+
clientMetadataUrl: string().url().optional(),
|
|
19237
19277
|
clientId: string().min(1).optional(),
|
|
19238
19278
|
clientSecret: string().min(1).optional(),
|
|
19239
19279
|
scopes: array(string().min(1)).optional(),
|
|
@@ -19246,6 +19286,7 @@ const oauthLikeAuthSchema = union([object$1({
|
|
|
19246
19286
|
resourceMetadataUrl: string().url().optional(),
|
|
19247
19287
|
authorizationServerMetadataUrl: string().url().optional(),
|
|
19248
19288
|
openidConfigurationUrl: string().url().optional(),
|
|
19289
|
+
clientMetadataUrl: string().url().optional(),
|
|
19249
19290
|
clientId: string().min(1).optional(),
|
|
19250
19291
|
clientSecret: string().min(1).optional(),
|
|
19251
19292
|
scopes: array(string().min(1)).optional(),
|
|
@@ -19345,6 +19386,7 @@ const httpActionSchema = object$1({
|
|
|
19345
19386
|
path: string().min(1).regex(/^\//, "HTTP action path must start with /").describe("URL path appended to the HTTP API baseUrl.").refine((value) => !value.startsWith("//"), "HTTP action path must not start with //").refine((value) => !isUrl(value), "HTTP action path must be a URL path, not a URL"),
|
|
19346
19387
|
description: string().min(1).optional().describe("Action capability description."),
|
|
19347
19388
|
inputSchema: record(string(), unknown()).optional().describe("JSON Schema for call_tool arguments."),
|
|
19389
|
+
outputSchema: record(string(), unknown()).optional().describe("JSON Schema for structuredContent returned by this action."),
|
|
19348
19390
|
query: httpScalarMappingSchema.optional().describe("Query parameter mapping."),
|
|
19349
19391
|
headers: httpScalarMappingSchema.optional().describe("Request header mapping."),
|
|
19350
19392
|
jsonBody: unknown().optional().describe("JSON request body mapping.")
|
|
@@ -19609,7 +19651,7 @@ function normalizeLocalPaths(input, baseDir) {
|
|
|
19609
19651
|
}
|
|
19610
19652
|
function normalizeEndpointPaths(endpoints, baseDir, normalize) {
|
|
19611
19653
|
if (!endpoints) return;
|
|
19612
|
-
return Object.fromEntries(Object.entries(endpoints).map(([id, endpoint]) => [id, isPlainObject$
|
|
19654
|
+
return Object.fromEntries(Object.entries(endpoints).map(([id, endpoint]) => [id, isPlainObject$4(endpoint) ? normalize(endpoint, baseDir) : endpoint]));
|
|
19613
19655
|
}
|
|
19614
19656
|
function normalizeOpenApiPath(endpoint, baseDir) {
|
|
19615
19657
|
return {
|
|
@@ -19618,7 +19660,7 @@ function normalizeOpenApiPath(endpoint, baseDir) {
|
|
|
19618
19660
|
};
|
|
19619
19661
|
}
|
|
19620
19662
|
function normalizeGraphQlPath(endpoint, baseDir) {
|
|
19621
|
-
const operations = isPlainObject$
|
|
19663
|
+
const operations = isPlainObject$4(endpoint.operations) ? Object.fromEntries(Object.entries(endpoint.operations).map(([name, operation]) => [name, isPlainObject$4(operation) ? {
|
|
19622
19664
|
...operation,
|
|
19623
19665
|
documentPath: normalizeLocalPath(operation.documentPath, baseDir)
|
|
19624
19666
|
} : operation])) : endpoint.operations;
|
|
@@ -19737,7 +19779,7 @@ function isPublicMetadataPath(path) {
|
|
|
19737
19779
|
if (path.length < 3 || path[0] !== "mcpServers" && path[0] !== "openapiEndpoints" && path[0] !== "graphqlEndpoints" && path[0] !== "httpApis") return false;
|
|
19738
19780
|
return NON_INTERPOLATED_SERVER_FIELDS.has(path[2] ?? "");
|
|
19739
19781
|
}
|
|
19740
|
-
function isPlainObject$
|
|
19782
|
+
function isPlainObject$4(value) {
|
|
19741
19783
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
19742
19784
|
}
|
|
19743
19785
|
function hasEnvReference(value) {
|
|
@@ -19896,12 +19938,14 @@ function formatCapletList(rows) {
|
|
|
19896
19938
|
}
|
|
19897
19939
|
function resolveCliConfigPaths(envConfigPath, authDir) {
|
|
19898
19940
|
const configPath = resolveConfigPath(envConfigPath);
|
|
19941
|
+
const effectiveAuthDir = authDir ?? DEFAULT_AUTH_DIR;
|
|
19899
19942
|
return {
|
|
19900
19943
|
userConfig: configPath,
|
|
19901
19944
|
projectConfig: resolveProjectConfigPath(),
|
|
19902
19945
|
userRoot: resolveCapletsRoot(configPath),
|
|
19946
|
+
stateRoot: dirname(effectiveAuthDir),
|
|
19903
19947
|
projectRoot: resolveProjectCapletsRoot(),
|
|
19904
|
-
authDir:
|
|
19948
|
+
authDir: effectiveAuthDir,
|
|
19905
19949
|
envConfig: envConfigPath ?? null,
|
|
19906
19950
|
projectCapletsTrusted: isTrustedProjectCapletsEnabled()
|
|
19907
19951
|
};
|
|
@@ -19911,6 +19955,7 @@ function formatConfigPaths(paths) {
|
|
|
19911
19955
|
`userConfig: ${paths.userConfig}`,
|
|
19912
19956
|
`projectConfig: ${paths.projectConfig}`,
|
|
19913
19957
|
`userRoot: ${paths.userRoot}`,
|
|
19958
|
+
`stateRoot: ${paths.stateRoot}`,
|
|
19914
19959
|
`projectRoot: ${paths.projectRoot}`,
|
|
19915
19960
|
`authDir: ${paths.authDir}`,
|
|
19916
19961
|
`envConfig: ${paths.envConfig ?? "unset"}`,
|
|
@@ -25820,7 +25865,7 @@ var Protocol = class {
|
|
|
25820
25865
|
};
|
|
25821
25866
|
}
|
|
25822
25867
|
};
|
|
25823
|
-
function isPlainObject$
|
|
25868
|
+
function isPlainObject$3(value) {
|
|
25824
25869
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
25825
25870
|
}
|
|
25826
25871
|
function mergeCapabilities(base, additional) {
|
|
@@ -25830,7 +25875,7 @@ function mergeCapabilities(base, additional) {
|
|
|
25830
25875
|
const addValue = additional[k];
|
|
25831
25876
|
if (addValue === void 0) continue;
|
|
25832
25877
|
const baseValue = result[k];
|
|
25833
|
-
if (isPlainObject$
|
|
25878
|
+
if (isPlainObject$3(baseValue) && isPlainObject$3(addValue)) result[k] = {
|
|
25834
25879
|
...baseValue,
|
|
25835
25880
|
...addValue
|
|
25836
25881
|
};
|
|
@@ -35726,7 +35771,8 @@ var DownstreamManager = class {
|
|
|
35726
35771
|
tool: tool.name,
|
|
35727
35772
|
...tool.description ? { description: tool.description } : {},
|
|
35728
35773
|
...tool.annotations ? { annotations: tool.annotations } : {},
|
|
35729
|
-
hasInputSchema: Boolean(tool.inputSchema)
|
|
35774
|
+
hasInputSchema: Boolean(tool.inputSchema),
|
|
35775
|
+
hasOutputSchema: Boolean(tool.outputSchema)
|
|
35730
35776
|
};
|
|
35731
35777
|
}
|
|
35732
35778
|
search(server, tools, query, limit) {
|
|
@@ -50705,7 +50751,8 @@ var GraphQLManager = class {
|
|
|
50705
50751
|
tool: tool.name,
|
|
50706
50752
|
...tool.description ? { description: tool.description } : {},
|
|
50707
50753
|
...tool.annotations ? { annotations: tool.annotations } : {},
|
|
50708
|
-
hasInputSchema: Boolean(tool.inputSchema)
|
|
50754
|
+
hasInputSchema: Boolean(tool.inputSchema),
|
|
50755
|
+
hasOutputSchema: Boolean(tool.outputSchema)
|
|
50709
50756
|
};
|
|
50710
50757
|
}
|
|
50711
50758
|
search(endpoint, tools, query, limit) {
|
|
@@ -51103,7 +51150,8 @@ var HttpActionManager = class {
|
|
|
51103
51150
|
tool: tool.name,
|
|
51104
51151
|
...tool.description ? { description: tool.description } : {},
|
|
51105
51152
|
...tool.annotations ? { annotations: tool.annotations } : {},
|
|
51106
|
-
hasInputSchema: Boolean(tool.inputSchema)
|
|
51153
|
+
hasInputSchema: Boolean(tool.inputSchema),
|
|
51154
|
+
hasOutputSchema: Boolean(tool.outputSchema)
|
|
51107
51155
|
};
|
|
51108
51156
|
}
|
|
51109
51157
|
search(api, tools, query, limit) {
|
|
@@ -51115,6 +51163,7 @@ var HttpActionManager = class {
|
|
|
51115
51163
|
name: operation.name,
|
|
51116
51164
|
...operation.description ? { description: operation.description } : {},
|
|
51117
51165
|
inputSchema: operation.inputSchema ?? DEFAULT_INPUT_SCHEMA,
|
|
51166
|
+
...operation.outputSchema ? { outputSchema: operation.outputSchema } : {},
|
|
51118
51167
|
annotations: {
|
|
51119
51168
|
readOnlyHint: operation.method === "GET",
|
|
51120
51169
|
destructiveHint: operation.method === "DELETE"
|
|
@@ -51186,7 +51235,7 @@ function resolveMapping(mapping, input) {
|
|
|
51186
51235
|
function resolveMappingToRecord(mapping, input, name) {
|
|
51187
51236
|
if (mapping === void 0) return {};
|
|
51188
51237
|
const resolved = resolveMapping(mapping, input);
|
|
51189
|
-
if (!isPlainObject$
|
|
51238
|
+
if (!isPlainObject$2(resolved)) throw new CapletsError("REQUEST_INVALID", `HTTP action ${name} mapping must resolve to an object`);
|
|
51190
51239
|
return resolved;
|
|
51191
51240
|
}
|
|
51192
51241
|
function valueAtPath(input, path) {
|
|
@@ -51281,9 +51330,9 @@ function buildActionUrl(base, actionPath, options = {}) {
|
|
|
51281
51330
|
return baseUrl;
|
|
51282
51331
|
}
|
|
51283
51332
|
function asRecord$1(value) {
|
|
51284
|
-
return isPlainObject$
|
|
51333
|
+
return isPlainObject$2(value) ? value : {};
|
|
51285
51334
|
}
|
|
51286
|
-
function isPlainObject$
|
|
51335
|
+
function isPlainObject$2(value) {
|
|
51287
51336
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
51288
51337
|
}
|
|
51289
51338
|
//#endregion
|
|
@@ -60897,7 +60946,8 @@ var OpenApiManager = class {
|
|
|
60897
60946
|
tool: tool.name,
|
|
60898
60947
|
...tool.description ? { description: tool.description } : {},
|
|
60899
60948
|
...tool.annotations ? { annotations: tool.annotations } : {},
|
|
60900
|
-
hasInputSchema: Boolean(tool.inputSchema)
|
|
60949
|
+
hasInputSchema: Boolean(tool.inputSchema),
|
|
60950
|
+
hasOutputSchema: Boolean(tool.outputSchema)
|
|
60901
60951
|
};
|
|
60902
60952
|
}
|
|
60903
60953
|
search(endpoint, tools, query, limit) {
|
|
@@ -60940,6 +60990,7 @@ var OpenApiManager = class {
|
|
|
60940
60990
|
name: operation.name,
|
|
60941
60991
|
...operation.summary || operation.description ? { description: operation.summary ?? operation.description } : {},
|
|
60942
60992
|
inputSchema: operation.inputSchema,
|
|
60993
|
+
...operation.outputSchema ? { outputSchema: operation.outputSchema } : {},
|
|
60943
60994
|
annotations: {
|
|
60944
60995
|
readOnlyHint: operation.method === "get" || operation.method === "head",
|
|
60945
60996
|
destructiveHint: operation.method === "delete"
|
|
@@ -60975,6 +61026,7 @@ function extractOperations(endpoint, document) {
|
|
|
60975
61026
|
seen.add(name);
|
|
60976
61027
|
const parameters = [...inheritedParameters, ...Array.isArray(operation.parameters) ? operation.parameters : []];
|
|
60977
61028
|
const requestBody = requestBodyFor(operation);
|
|
61029
|
+
const outputSchema = outputSchemaFor(operation);
|
|
60978
61030
|
const baseUrl = endpoint.baseUrl ?? firstServerUrl(document);
|
|
60979
61031
|
validateOperationBaseUrl(endpoint, baseUrl);
|
|
60980
61032
|
operations.push({
|
|
@@ -60984,6 +61036,7 @@ function extractOperations(endpoint, document) {
|
|
|
60984
61036
|
...typeof operation.summary === "string" ? { summary: operation.summary } : {},
|
|
60985
61037
|
...typeof operation.description === "string" ? { description: operation.description } : {},
|
|
60986
61038
|
inputSchema: inputSchemaFor(parameters, requestBody),
|
|
61039
|
+
...outputSchema ? { outputSchema } : {},
|
|
60987
61040
|
...requestBody?.contentType ? { requestBodyContentType: requestBody.contentType } : {},
|
|
60988
61041
|
...baseUrl ? { baseUrl } : {}
|
|
60989
61042
|
});
|
|
@@ -61004,6 +61057,53 @@ function requestBodyFor(operation) {
|
|
|
61004
61057
|
contentType
|
|
61005
61058
|
};
|
|
61006
61059
|
}
|
|
61060
|
+
function outputSchemaFor(operation) {
|
|
61061
|
+
const responses = operation.responses;
|
|
61062
|
+
if (!responses || typeof responses !== "object") return;
|
|
61063
|
+
const schemas = [];
|
|
61064
|
+
for (const [status, response] of Object.entries(responses)) {
|
|
61065
|
+
if (!/^2\d\d$/.test(status) || !response || typeof response !== "object") continue;
|
|
61066
|
+
const content = response.content;
|
|
61067
|
+
if (!content || typeof content !== "object") continue;
|
|
61068
|
+
const contentType = JSON_CONTENT_TYPES.find((candidate) => content[candidate]);
|
|
61069
|
+
if (!contentType) continue;
|
|
61070
|
+
const schema = actualSchema(content[contentType]?.schema);
|
|
61071
|
+
if (!schema) continue;
|
|
61072
|
+
schemas.push(schema);
|
|
61073
|
+
}
|
|
61074
|
+
if (schemas.length === 0) return;
|
|
61075
|
+
const firstSchema = schemas[0];
|
|
61076
|
+
if (schemas.slice(1).some((schema) => JSON.stringify(schema) !== JSON.stringify(firstSchema))) return;
|
|
61077
|
+
return structuredOutputSchema(firstSchema);
|
|
61078
|
+
}
|
|
61079
|
+
function actualSchema(value) {
|
|
61080
|
+
rejectExternalRefs(value);
|
|
61081
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
61082
|
+
const schema = value;
|
|
61083
|
+
return typeof schema.$ref === "string" ? void 0 : schema;
|
|
61084
|
+
}
|
|
61085
|
+
function structuredOutputSchema(bodySchema) {
|
|
61086
|
+
return {
|
|
61087
|
+
type: "object",
|
|
61088
|
+
additionalProperties: false,
|
|
61089
|
+
required: [
|
|
61090
|
+
"status",
|
|
61091
|
+
"statusText",
|
|
61092
|
+
"headers"
|
|
61093
|
+
],
|
|
61094
|
+
properties: {
|
|
61095
|
+
status: { type: "number" },
|
|
61096
|
+
statusText: { type: "string" },
|
|
61097
|
+
headers: {
|
|
61098
|
+
type: "object",
|
|
61099
|
+
additionalProperties: false,
|
|
61100
|
+
required: ["content-type"],
|
|
61101
|
+
properties: { "content-type": { type: "string" } }
|
|
61102
|
+
},
|
|
61103
|
+
body: bodySchema
|
|
61104
|
+
}
|
|
61105
|
+
};
|
|
61106
|
+
}
|
|
61007
61107
|
function inputSchemaFor(parameters, requestBody) {
|
|
61008
61108
|
const schema = {
|
|
61009
61109
|
type: "object",
|
|
@@ -61163,6 +61263,26 @@ function openApiCacheKey(endpoint) {
|
|
|
61163
61263
|
});
|
|
61164
61264
|
}
|
|
61165
61265
|
//#endregion
|
|
61266
|
+
//#region src/capability-description.mjs
|
|
61267
|
+
function capabilityDescription(server) {
|
|
61268
|
+
const backendName = server.backend === "mcp" ? "MCP server" : server.backend === "openapi" ? "OpenAPI endpoint" : server.backend === "graphql" ? "GraphQL endpoint" : "HTTP API";
|
|
61269
|
+
const checkOperation = server.backend === "mcp" ? "check_mcp_server" : "check_backend";
|
|
61270
|
+
const hint = [
|
|
61271
|
+
`Use this Caplet to inspect and call tools from its ${backendName} backend.`,
|
|
61272
|
+
"",
|
|
61273
|
+
"Recommended flow:",
|
|
61274
|
+
"- Read the full Caplet card: {\"operation\":\"get_caplet\"}",
|
|
61275
|
+
`- Check the backend: {"operation":"${checkOperation}"}`,
|
|
61276
|
+
"- Discover tools: {\"operation\":\"list_tools\"} or {\"operation\":\"search_tools\",\"query\":\"<what you need>\"}",
|
|
61277
|
+
"- Read one tool schema: {\"operation\":\"get_tool\",\"tool\":\"<tool name>\"}",
|
|
61278
|
+
"- Invoke one downstream tool: {\"operation\":\"call_tool\",\"tool\":\"<tool name>\",\"arguments\":{...}}",
|
|
61279
|
+
"",
|
|
61280
|
+
"Important: Do not put downstream arguments at the top level; put them inside \"arguments\".",
|
|
61281
|
+
"After get_tool shows outputSchema (non-GraphQL), call_tool may use fields: [\"path.to.field\"]."
|
|
61282
|
+
].join("\n");
|
|
61283
|
+
return `${server.name}\n\n${server.description}\n\n${hint}`;
|
|
61284
|
+
}
|
|
61285
|
+
//#endregion
|
|
61166
61286
|
//#region src/registry.ts
|
|
61167
61287
|
var ServerRegistry = class {
|
|
61168
61288
|
config;
|
|
@@ -61230,23 +61350,6 @@ var ServerRegistry = class {
|
|
|
61230
61350
|
];
|
|
61231
61351
|
}
|
|
61232
61352
|
};
|
|
61233
|
-
function capabilityDescription(server) {
|
|
61234
|
-
const backendName = server.backend === "mcp" ? "MCP server" : server.backend === "openapi" ? "OpenAPI endpoint" : server.backend === "graphql" ? "GraphQL endpoint" : "HTTP API";
|
|
61235
|
-
const checkOperation = server.backend === "mcp" ? "check_mcp_server" : "check_backend";
|
|
61236
|
-
const hint = [
|
|
61237
|
-
`Use this Caplet to inspect and call tools from its ${backendName} backend.`,
|
|
61238
|
-
"",
|
|
61239
|
-
"Recommended flow:",
|
|
61240
|
-
"- Read the full Caplet card: {\"operation\":\"get_caplet\"}",
|
|
61241
|
-
`- Check the backend: {"operation":"${checkOperation}"}`,
|
|
61242
|
-
"- Discover tools: {\"operation\":\"list_tools\"} or {\"operation\":\"search_tools\",\"query\":\"<what you need>\"}",
|
|
61243
|
-
"- Read one tool schema: {\"operation\":\"get_tool\",\"tool\":\"<tool name>\"}",
|
|
61244
|
-
"- Invoke one downstream tool: {\"operation\":\"call_tool\",\"tool\":\"<tool name>\",\"arguments\":{...}}",
|
|
61245
|
-
"",
|
|
61246
|
-
"Important: call_tool requires a top-level \"arguments\" JSON object containing the downstream tool inputs. Do not put downstream arguments at the top level of this wrapper request."
|
|
61247
|
-
].join("\n");
|
|
61248
|
-
return `${server.name}\n\n${server.description}\n\n${hint}`;
|
|
61249
|
-
}
|
|
61250
61353
|
function backendDetail(server) {
|
|
61251
61354
|
if (server.backend === "openapi") return {
|
|
61252
61355
|
type: "openapi",
|
|
@@ -61284,7 +61387,110 @@ function graphQlSource(server) {
|
|
|
61284
61387
|
return "introspection";
|
|
61285
61388
|
}
|
|
61286
61389
|
//#endregion
|
|
61287
|
-
//#region src/
|
|
61390
|
+
//#region src/field-selection.ts
|
|
61391
|
+
function projectStructuredContent(value, outputSchema, fields) {
|
|
61392
|
+
validateFieldSelection(outputSchema, fields);
|
|
61393
|
+
if (!isPlainObject$1(value)) throwInvalid("Field selection requires object structured content");
|
|
61394
|
+
const result = createJsonObject();
|
|
61395
|
+
for (const field of fields) {
|
|
61396
|
+
const projected = projectPath(value, outputSchema, field.split("."));
|
|
61397
|
+
if (projected !== void 0) mergeValue(result, projected);
|
|
61398
|
+
}
|
|
61399
|
+
return result;
|
|
61400
|
+
}
|
|
61401
|
+
function validateFieldSelection(outputSchema, fields) {
|
|
61402
|
+
if (!isPlainObject$1(outputSchema)) throwInvalid("Field selection requires an output schema");
|
|
61403
|
+
if (!Array.isArray(fields) || fields.some((field) => typeof field !== "string")) throwInvalid("Field selection requires an array of field paths");
|
|
61404
|
+
for (const field of fields) validateSchemaPath(outputSchema, field.split("."), field);
|
|
61405
|
+
}
|
|
61406
|
+
function validateSchemaPath(schema, path, field) {
|
|
61407
|
+
let current = schema;
|
|
61408
|
+
for (const segment of path) {
|
|
61409
|
+
if (!isSupportedSegment(segment)) throwInvalid(`Unsupported field selection path: ${field}`);
|
|
61410
|
+
if (current?.type === "array") current = Array.isArray(current.items) ? void 0 : current.items;
|
|
61411
|
+
current = getOwnSchemaProperty(current?.properties, segment);
|
|
61412
|
+
if (!current) throwInvalid(`Field is not allowed by output schema: ${field}`);
|
|
61413
|
+
}
|
|
61414
|
+
}
|
|
61415
|
+
function getOwnSchemaProperty(properties, segment) {
|
|
61416
|
+
if (!properties || !Object.prototype.hasOwnProperty.call(properties, segment)) return;
|
|
61417
|
+
return properties[segment];
|
|
61418
|
+
}
|
|
61419
|
+
function projectPath(value, schema, path) {
|
|
61420
|
+
if (path.length === 0) return pruneToSchema(value, schema);
|
|
61421
|
+
if (Array.isArray(value)) {
|
|
61422
|
+
const itemSchema = arrayItemSchema(schema);
|
|
61423
|
+
return value.map((item) => projectPath(item, itemSchema, path) ?? {});
|
|
61424
|
+
}
|
|
61425
|
+
const segment = path[0];
|
|
61426
|
+
if (!isPlainObject$1(value) || !Object.prototype.hasOwnProperty.call(value, segment)) return;
|
|
61427
|
+
const rest = path.slice(1);
|
|
61428
|
+
const propertySchema = getSchemaProperty(schema, segment);
|
|
61429
|
+
const projected = projectPath(value[segment], propertySchema, rest);
|
|
61430
|
+
if (projected === void 0) return;
|
|
61431
|
+
return { [segment]: projected };
|
|
61432
|
+
}
|
|
61433
|
+
function pruneToSchema(value, schema) {
|
|
61434
|
+
if (Array.isArray(value)) {
|
|
61435
|
+
const itemSchema = arrayItemSchema(schema);
|
|
61436
|
+
return value.map((item) => pruneToSchema(item, itemSchema));
|
|
61437
|
+
}
|
|
61438
|
+
if (!isPlainObject$1(value)) return cloneJsonValue(value);
|
|
61439
|
+
const properties = isPlainObject$1(schema) ? schema.properties : void 0;
|
|
61440
|
+
if (!isPlainObject$1(properties)) return cloneJsonValue(value);
|
|
61441
|
+
const result = createJsonObject();
|
|
61442
|
+
for (const [key, nestedSchema] of Object.entries(properties)) if (isSupportedSegment(key) && Object.prototype.hasOwnProperty.call(value, key)) result[key] = pruneToSchema(value[key], nestedSchema);
|
|
61443
|
+
return result;
|
|
61444
|
+
}
|
|
61445
|
+
function getSchemaProperty(schema, segment) {
|
|
61446
|
+
const properties = isPlainObject$1(schema) ? schema.properties : void 0;
|
|
61447
|
+
if (!properties || !Object.prototype.hasOwnProperty.call(properties, segment)) return;
|
|
61448
|
+
return properties[segment];
|
|
61449
|
+
}
|
|
61450
|
+
function arrayItemSchema(schema) {
|
|
61451
|
+
if (!isPlainObject$1(schema) || Array.isArray(schema.items)) return;
|
|
61452
|
+
return schema.items;
|
|
61453
|
+
}
|
|
61454
|
+
function mergeValue(target, value) {
|
|
61455
|
+
if (!isPlainObject$1(value)) return;
|
|
61456
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
61457
|
+
if (!isSupportedSegment(key)) continue;
|
|
61458
|
+
target[key] = mergeNested(target[key], nested);
|
|
61459
|
+
}
|
|
61460
|
+
}
|
|
61461
|
+
function mergeNested(existing, next) {
|
|
61462
|
+
if (next === void 0) return existing;
|
|
61463
|
+
if (Array.isArray(existing) && Array.isArray(next)) return Array.from({ length: Math.max(existing.length, next.length) }, (_, index) => mergeNested(existing[index], next[index]));
|
|
61464
|
+
if (isPlainObject$1(existing) && isPlainObject$1(next)) {
|
|
61465
|
+
const merged = Object.assign(createJsonObject(), existing);
|
|
61466
|
+
mergeValue(merged, next);
|
|
61467
|
+
return merged;
|
|
61468
|
+
}
|
|
61469
|
+
return next;
|
|
61470
|
+
}
|
|
61471
|
+
function isSupportedSegment(segment) {
|
|
61472
|
+
return segment !== "" && segment !== "*" && segment !== "__proto__" && segment !== "prototype" && segment !== "constructor" && !/^\d+$/.test(segment);
|
|
61473
|
+
}
|
|
61474
|
+
function isPlainObject$1(value) {
|
|
61475
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
61476
|
+
}
|
|
61477
|
+
function createJsonObject() {
|
|
61478
|
+
return Object.create(null);
|
|
61479
|
+
}
|
|
61480
|
+
function cloneJsonValue(value) {
|
|
61481
|
+
if (Array.isArray(value)) return value.map(cloneJsonValue);
|
|
61482
|
+
if (isPlainObject$1(value)) {
|
|
61483
|
+
const result = createJsonObject();
|
|
61484
|
+
for (const [key, nested] of Object.entries(value)) if (isSupportedSegment(key)) result[key] = cloneJsonValue(nested);
|
|
61485
|
+
return result;
|
|
61486
|
+
}
|
|
61487
|
+
return value;
|
|
61488
|
+
}
|
|
61489
|
+
function throwInvalid(message) {
|
|
61490
|
+
throw new CapletsError("REQUEST_INVALID", message);
|
|
61491
|
+
}
|
|
61492
|
+
//#endregion
|
|
61493
|
+
//#region src/generated-tool-input-schema.mjs
|
|
61288
61494
|
const operations = [
|
|
61289
61495
|
"get_caplet",
|
|
61290
61496
|
"check_backend",
|
|
@@ -61294,16 +61500,25 @@ const operations = [
|
|
|
61294
61500
|
"get_tool",
|
|
61295
61501
|
"call_tool"
|
|
61296
61502
|
];
|
|
61297
|
-
const
|
|
61298
|
-
operation:
|
|
61503
|
+
const generatedToolInputDescriptions = {
|
|
61504
|
+
operation: [
|
|
61299
61505
|
"Caplets wrapper operation to perform for this configured Caplet backend.",
|
|
61300
61506
|
"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 or OpenAPI operation.",
|
|
61301
61507
|
"For call_tool, pass downstream inputs only inside the top-level \"arguments\" object."
|
|
61302
|
-
].join(" ")
|
|
61303
|
-
query:
|
|
61304
|
-
limit:
|
|
61305
|
-
tool:
|
|
61306
|
-
arguments:
|
|
61508
|
+
].join(" "),
|
|
61509
|
+
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.",
|
|
61510
|
+
limit: "Optional only for search_tools; defaults to the configured search limit. For downstream result limits, use call_tool.arguments with the downstream schema field name.",
|
|
61511
|
+
tool: "Exact downstream tool name for get_tool or call_tool. Example: {\"operation\":\"get_tool\",\"tool\":\"web_search_exa\"} before calling it.",
|
|
61512
|
+
arguments: "Required JSON object only for call_tool. Put every downstream tool input inside this object. Example: {\"operation\":\"call_tool\",\"tool\":\"web_search_exa\",\"arguments\":{\"query\":\"latest MCP docs\",\"numResults\":3}}. Do not send downstream inputs as top-level query, limit, url, path, or other fields.",
|
|
61513
|
+
fields: "Optional for call_tool after get_tool shows outputSchema on a non-GraphQL tool. Example: fields: [\"path.to.field\"]."
|
|
61514
|
+
};
|
|
61515
|
+
const generatedToolInputSchema = object$1({
|
|
61516
|
+
operation: _enum(operations).describe(generatedToolInputDescriptions.operation),
|
|
61517
|
+
query: string().optional().describe(generatedToolInputDescriptions.query),
|
|
61518
|
+
limit: number$1().int().positive().optional().describe(generatedToolInputDescriptions.limit),
|
|
61519
|
+
tool: string().optional().describe(generatedToolInputDescriptions.tool),
|
|
61520
|
+
arguments: record(string(), unknown()).optional().describe(generatedToolInputDescriptions.arguments),
|
|
61521
|
+
fields: array(string().min(1)).min(1).optional().describe(generatedToolInputDescriptions.fields)
|
|
61307
61522
|
}).strict();
|
|
61308
61523
|
async function handleServerTool(server, request, registry, downstream, openapi, graphql, http) {
|
|
61309
61524
|
const parsed = validateOperationRequest(request, registry.config.options.maxSearchLimit);
|
|
@@ -61338,7 +61553,15 @@ async function handleServerTool(server, request, registry, downstream, openapi,
|
|
|
61338
61553
|
tool
|
|
61339
61554
|
});
|
|
61340
61555
|
}
|
|
61341
|
-
case "call_tool":
|
|
61556
|
+
case "call_tool": {
|
|
61557
|
+
const backend = backendFor(server, downstream, openapi, graphql, http);
|
|
61558
|
+
if (parsed.fields === void 0) return backend.callTool(server, parsed.tool, parsed.arguments);
|
|
61559
|
+
if (server.backend === "graphql") throw new CapletsError("REQUEST_INVALID", "call_tool.fields is not supported for GraphQL-backed Caplets; select fields in the GraphQL operation document instead");
|
|
61560
|
+
const tool = await backend.getTool(server, parsed.tool);
|
|
61561
|
+
if (!tool.outputSchema) throw new CapletsError("REQUEST_INVALID", "Field selection requires an output schema");
|
|
61562
|
+
validateFieldSelection(tool.outputSchema, parsed.fields);
|
|
61563
|
+
return projectCallToolResult(await backend.callTool(server, parsed.tool, parsed.arguments), tool.outputSchema, parsed.fields);
|
|
61564
|
+
}
|
|
61342
61565
|
}
|
|
61343
61566
|
}
|
|
61344
61567
|
function validateOperationRequest(request, maxSearchLimit) {
|
|
@@ -61379,13 +61602,22 @@ function validateOperationRequest(request, maxSearchLimit) {
|
|
|
61379
61602
|
tool: value.tool
|
|
61380
61603
|
};
|
|
61381
61604
|
case "call_tool":
|
|
61382
|
-
allowed([
|
|
61605
|
+
allowed([
|
|
61606
|
+
"tool",
|
|
61607
|
+
"arguments",
|
|
61608
|
+
"fields"
|
|
61609
|
+
]);
|
|
61383
61610
|
if (!value.tool) throw new CapletsError("REQUEST_INVALID", "call_tool requires tool");
|
|
61384
61611
|
if (!isPlainObject(value.arguments)) throw new CapletsError("REQUEST_INVALID", "call_tool.arguments must be a JSON object");
|
|
61385
|
-
return {
|
|
61612
|
+
return value.fields === void 0 ? {
|
|
61386
61613
|
operation: "call_tool",
|
|
61387
61614
|
tool: value.tool,
|
|
61388
61615
|
arguments: value.arguments
|
|
61616
|
+
} : {
|
|
61617
|
+
operation: "call_tool",
|
|
61618
|
+
tool: value.tool,
|
|
61619
|
+
arguments: value.arguments,
|
|
61620
|
+
fields: value.fields
|
|
61389
61621
|
};
|
|
61390
61622
|
}
|
|
61391
61623
|
}
|
|
@@ -61398,6 +61630,20 @@ function jsonResult(value) {
|
|
|
61398
61630
|
structuredContent: { result: value }
|
|
61399
61631
|
};
|
|
61400
61632
|
}
|
|
61633
|
+
function projectCallToolResult(result, outputSchema, fields) {
|
|
61634
|
+
if (result.isError === true) return result;
|
|
61635
|
+
const structuredContent = result.structuredContent;
|
|
61636
|
+
if (!isPlainObject(structuredContent)) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Field selection requires the downstream tool to return object structuredContent");
|
|
61637
|
+
const projected = projectStructuredContent(structuredContent, outputSchema, fields);
|
|
61638
|
+
return {
|
|
61639
|
+
...result,
|
|
61640
|
+
content: [{
|
|
61641
|
+
type: "text",
|
|
61642
|
+
text: JSON.stringify(projected, null, 2)
|
|
61643
|
+
}],
|
|
61644
|
+
structuredContent: projected
|
|
61645
|
+
};
|
|
61646
|
+
}
|
|
61401
61647
|
function isPlainObject(value) {
|
|
61402
61648
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
61403
61649
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "caplets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Progressive disclosure gateway for MCP servers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"caplets",
|
|
@@ -67,6 +67,11 @@
|
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "rolldown -c",
|
|
69
69
|
"build:watch": "rolldown -c --watch",
|
|
70
|
+
"benchmark": "node benchmarks/run-deterministic.mjs",
|
|
71
|
+
"benchmark:check": "node benchmarks/run-deterministic.mjs --check",
|
|
72
|
+
"benchmark:live": "node benchmarks/run-live.mjs",
|
|
73
|
+
"benchmark:live:opencode": "node benchmarks/run-live.mjs --agent opencode",
|
|
74
|
+
"benchmark:live:pi": "node benchmarks/run-live.mjs --agent pi",
|
|
70
75
|
"changeset": "changeset",
|
|
71
76
|
"dev": "node ./scripts/dev.mjs",
|
|
72
77
|
"format": "oxfmt .",
|
|
@@ -78,7 +83,7 @@
|
|
|
78
83
|
"schema:generate": "rolldown -c rolldown.schema.config.ts && node dist-schema/generate-config-schema.js && rm -rf dist-schema",
|
|
79
84
|
"typecheck": "tsc --noEmit",
|
|
80
85
|
"test": "vitest run",
|
|
81
|
-
"verify": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm schema:check && pnpm test && pnpm build",
|
|
86
|
+
"verify": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm schema:check && pnpm test && pnpm benchmark:check && pnpm build",
|
|
82
87
|
"version-packages": "changeset version"
|
|
83
88
|
}
|
|
84
89
|
}
|
|
@@ -149,6 +149,10 @@
|
|
|
149
149
|
"type": "string",
|
|
150
150
|
"minLength": 1
|
|
151
151
|
},
|
|
152
|
+
"clientMetadataUrl": {
|
|
153
|
+
"type": "string",
|
|
154
|
+
"minLength": 1
|
|
155
|
+
},
|
|
152
156
|
"clientId": {
|
|
153
157
|
"type": "string",
|
|
154
158
|
"minLength": 1
|
|
@@ -203,6 +207,10 @@
|
|
|
203
207
|
"type": "string",
|
|
204
208
|
"minLength": 1
|
|
205
209
|
},
|
|
210
|
+
"clientMetadataUrl": {
|
|
211
|
+
"type": "string",
|
|
212
|
+
"minLength": 1
|
|
213
|
+
},
|
|
206
214
|
"clientId": {
|
|
207
215
|
"type": "string",
|
|
208
216
|
"minLength": 1
|
|
@@ -353,6 +361,10 @@
|
|
|
353
361
|
"type": "string",
|
|
354
362
|
"minLength": 1
|
|
355
363
|
},
|
|
364
|
+
"clientMetadataUrl": {
|
|
365
|
+
"type": "string",
|
|
366
|
+
"minLength": 1
|
|
367
|
+
},
|
|
356
368
|
"clientId": {
|
|
357
369
|
"type": "string",
|
|
358
370
|
"minLength": 1
|
|
@@ -407,6 +419,10 @@
|
|
|
407
419
|
"type": "string",
|
|
408
420
|
"minLength": 1
|
|
409
421
|
},
|
|
422
|
+
"clientMetadataUrl": {
|
|
423
|
+
"type": "string",
|
|
424
|
+
"minLength": 1
|
|
425
|
+
},
|
|
410
426
|
"clientId": {
|
|
411
427
|
"type": "string",
|
|
412
428
|
"minLength": 1
|
|
@@ -591,6 +607,10 @@
|
|
|
591
607
|
"type": "string",
|
|
592
608
|
"minLength": 1
|
|
593
609
|
},
|
|
610
|
+
"clientMetadataUrl": {
|
|
611
|
+
"type": "string",
|
|
612
|
+
"minLength": 1
|
|
613
|
+
},
|
|
594
614
|
"clientId": {
|
|
595
615
|
"type": "string",
|
|
596
616
|
"minLength": 1
|
|
@@ -645,6 +665,10 @@
|
|
|
645
665
|
"type": "string",
|
|
646
666
|
"minLength": 1
|
|
647
667
|
},
|
|
668
|
+
"clientMetadataUrl": {
|
|
669
|
+
"type": "string",
|
|
670
|
+
"minLength": 1
|
|
671
|
+
},
|
|
648
672
|
"clientId": {
|
|
649
673
|
"type": "string",
|
|
650
674
|
"minLength": 1
|
|
@@ -788,6 +812,10 @@
|
|
|
788
812
|
"type": "string",
|
|
789
813
|
"minLength": 1
|
|
790
814
|
},
|
|
815
|
+
"clientMetadataUrl": {
|
|
816
|
+
"type": "string",
|
|
817
|
+
"minLength": 1
|
|
818
|
+
},
|
|
791
819
|
"clientId": {
|
|
792
820
|
"type": "string",
|
|
793
821
|
"minLength": 1
|
|
@@ -842,6 +870,10 @@
|
|
|
842
870
|
"type": "string",
|
|
843
871
|
"minLength": 1
|
|
844
872
|
},
|
|
873
|
+
"clientMetadataUrl": {
|
|
874
|
+
"type": "string",
|
|
875
|
+
"minLength": 1
|
|
876
|
+
},
|
|
845
877
|
"clientId": {
|
|
846
878
|
"type": "string",
|
|
847
879
|
"minLength": 1
|
|
@@ -168,6 +168,10 @@
|
|
|
168
168
|
"type": "string",
|
|
169
169
|
"format": "uri"
|
|
170
170
|
},
|
|
171
|
+
"clientMetadataUrl": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"format": "uri"
|
|
174
|
+
},
|
|
171
175
|
"clientId": {
|
|
172
176
|
"type": "string",
|
|
173
177
|
"minLength": 1
|
|
@@ -222,6 +226,10 @@
|
|
|
222
226
|
"type": "string",
|
|
223
227
|
"format": "uri"
|
|
224
228
|
},
|
|
229
|
+
"clientMetadataUrl": {
|
|
230
|
+
"type": "string",
|
|
231
|
+
"format": "uri"
|
|
232
|
+
},
|
|
225
233
|
"clientId": {
|
|
226
234
|
"type": "string",
|
|
227
235
|
"minLength": 1
|
|
@@ -403,6 +411,10 @@
|
|
|
403
411
|
"type": "string",
|
|
404
412
|
"format": "uri"
|
|
405
413
|
},
|
|
414
|
+
"clientMetadataUrl": {
|
|
415
|
+
"type": "string",
|
|
416
|
+
"format": "uri"
|
|
417
|
+
},
|
|
406
418
|
"clientId": {
|
|
407
419
|
"type": "string",
|
|
408
420
|
"minLength": 1
|
|
@@ -457,6 +469,10 @@
|
|
|
457
469
|
"type": "string",
|
|
458
470
|
"format": "uri"
|
|
459
471
|
},
|
|
472
|
+
"clientMetadataUrl": {
|
|
473
|
+
"type": "string",
|
|
474
|
+
"format": "uri"
|
|
475
|
+
},
|
|
460
476
|
"clientId": {
|
|
461
477
|
"type": "string",
|
|
462
478
|
"minLength": 1
|
|
@@ -670,6 +686,10 @@
|
|
|
670
686
|
"type": "string",
|
|
671
687
|
"format": "uri"
|
|
672
688
|
},
|
|
689
|
+
"clientMetadataUrl": {
|
|
690
|
+
"type": "string",
|
|
691
|
+
"format": "uri"
|
|
692
|
+
},
|
|
673
693
|
"clientId": {
|
|
674
694
|
"type": "string",
|
|
675
695
|
"minLength": 1
|
|
@@ -724,6 +744,10 @@
|
|
|
724
744
|
"type": "string",
|
|
725
745
|
"format": "uri"
|
|
726
746
|
},
|
|
747
|
+
"clientMetadataUrl": {
|
|
748
|
+
"type": "string",
|
|
749
|
+
"format": "uri"
|
|
750
|
+
},
|
|
727
751
|
"clientId": {
|
|
728
752
|
"type": "string",
|
|
729
753
|
"minLength": 1
|
|
@@ -896,6 +920,10 @@
|
|
|
896
920
|
"type": "string",
|
|
897
921
|
"format": "uri"
|
|
898
922
|
},
|
|
923
|
+
"clientMetadataUrl": {
|
|
924
|
+
"type": "string",
|
|
925
|
+
"format": "uri"
|
|
926
|
+
},
|
|
899
927
|
"clientId": {
|
|
900
928
|
"type": "string",
|
|
901
929
|
"minLength": 1
|
|
@@ -950,6 +978,10 @@
|
|
|
950
978
|
"type": "string",
|
|
951
979
|
"format": "uri"
|
|
952
980
|
},
|
|
981
|
+
"clientMetadataUrl": {
|
|
982
|
+
"type": "string",
|
|
983
|
+
"format": "uri"
|
|
984
|
+
},
|
|
953
985
|
"clientId": {
|
|
954
986
|
"type": "string",
|
|
955
987
|
"minLength": 1
|
|
@@ -1009,6 +1041,14 @@
|
|
|
1009
1041
|
},
|
|
1010
1042
|
"additionalProperties": {}
|
|
1011
1043
|
},
|
|
1044
|
+
"outputSchema": {
|
|
1045
|
+
"description": "JSON Schema for structuredContent returned by this action.",
|
|
1046
|
+
"type": "object",
|
|
1047
|
+
"propertyNames": {
|
|
1048
|
+
"type": "string"
|
|
1049
|
+
},
|
|
1050
|
+
"additionalProperties": {}
|
|
1051
|
+
},
|
|
1012
1052
|
"query": {
|
|
1013
1053
|
"description": "Query parameter mapping.",
|
|
1014
1054
|
"type": "object",
|