@runpod/mcp-server 4.10.1 → 4.11.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/dist/http.mjs CHANGED
@@ -9,9 +9,9 @@ import {
9
9
  requestLogSampleRate,
10
10
  requestProfile,
11
11
  summarizeRpc
12
- } from "./chunk-AKX5ZMCL.mjs";
12
+ } from "./chunk-4M7QDHK4.mjs";
13
13
  import "./chunk-577IYCJT.mjs";
14
- import "./chunk-FQJFKWUV.mjs";
14
+ import "./chunk-24UOECYY.mjs";
15
15
  export {
16
16
  bodyIsCheckable,
17
17
  defaultCredentialChecker,
@@ -24,6 +24,7 @@ __export(profile_exports, {
24
24
  DIRECTORY_TOOLS: () => DIRECTORY_TOOLS,
25
25
  directoryIncludes: () => directoryIncludes,
26
26
  directoryOmittedInputs: () => directoryOmittedInputs,
27
+ omittedInputsIn: () => omittedInputsIn,
27
28
  profileForPath: () => profileForPath
28
29
  });
29
30
  module.exports = __toCommonJS(profile_exports);
@@ -61,6 +62,8 @@ var DIRECTORY_TOOLS = {
61
62
  "deploy-hub-repo": IN,
62
63
  "detach-cost-center-resource": IN,
63
64
  "endpoint-health": IN,
65
+ // env can carry tokens (HF_TOKEN for a model download), so the directory omits it.
66
+ "exec-pod": { include: true, omitInputs: ["body.env"] },
64
67
  "get-capacity": IN,
65
68
  "get-cluster": IN,
66
69
  "get-cost-center": IN,
@@ -180,6 +183,21 @@ function directoryOmittedInputs(name) {
180
183
  const decision = DIRECTORY_TOOLS[name];
181
184
  return decision?.include ? decision.omitInputs ?? [] : [];
182
185
  }
186
+ function omittedInputsIn(omit, args) {
187
+ return omit.filter((key) => {
188
+ const [parent, child] = key.split(".");
189
+ if (child === void 0) return parent in args;
190
+ let value = args[parent];
191
+ if (typeof value === "string") {
192
+ try {
193
+ value = JSON.parse(value);
194
+ } catch {
195
+ return false;
196
+ }
197
+ }
198
+ return !!value && typeof value === "object" && child in value;
199
+ });
200
+ }
183
201
  function profileForPath(pathname) {
184
202
  return pathname.replace(/\/+$/, "") === DIRECTORY_PATH ? "directory" : "default";
185
203
  }
@@ -189,6 +207,7 @@ function profileForPath(pathname) {
189
207
  DIRECTORY_TOOLS,
190
208
  directoryIncludes,
191
209
  directoryOmittedInputs,
210
+ omittedInputsIn,
192
211
  profileForPath
193
212
  });
194
213
  //# sourceMappingURL=profile.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/specgen/profile.ts"],"sourcesContent":["// Which tool surface a connection gets. `default` is the full surface every\n// client has always had. `directory` is what app directories (Anthropic's\n// connector directory, OpenAI's plugin directory) list: their policies forbid\n// tool descriptions that instruct the model, steer it toward other tools or\n// external instructions, or collect conversation data beyond the tool's job.\n// The directory profile is served only at DIRECTORY_PATH; nothing about the\n// default surface changes.\nexport type ServerProfile = 'default' | 'directory';\n\nexport const DIRECTORY_PATH = '/directory';\n\n/** Whether a tool is listed on the directory profile. Every tool the server\n * can serve must have an entry, so a new tool (a spec regeneration, a new\n * curated tool) fails tests/specgen-directory.test.ts until someone decides.\n * The rules for deciding are in specgen/DIRECTORY_POLICY.md.\n * `omitInputs` hides top-level arguments the directory must not ask for (a\n * credential, for example); a call that passes one is refused. */\nexport type DirectoryDecision =\n | { include: true; omitInputs?: readonly string[] }\n | { include: false; reason: string };\n\nconst IN = { include: true } as const;\nconst CREDENTIAL_MANAGEMENT =\n 'Manages stored credentials; OpenAI restricts plugins that handle access credentials, so the directory leaves the whole credential family out.';\nconst CREDENTIAL_INPUT =\n 'Takes a secret or password as input; directories forbid collecting credentials.';\nconst ALP =\n 'Collects conversation-derived data beyond the task; the directory profile never serves it.';\n\nexport const DIRECTORY_TOOLS: Readonly<Record<string, DirectoryDecision>> = {\n 'add-organization-group-member': IN,\n 'attach-cost-center-resource': IN,\n 'cancel-job': IN,\n 'create-cluster': IN,\n 'create-cost-center': IN,\n 'create-delegation': IN,\n 'create-endpoint': IN,\n 'create-global-volume': IN,\n 'create-network-volume': IN,\n 'create-organization-group': IN,\n 'create-organization-invite': IN,\n 'create-organization-role': IN,\n 'create-pod': IN,\n 'create-template': IN,\n 'delete-cluster': IN,\n 'delete-cost-center': IN,\n 'delete-endpoint': IN,\n 'delete-global-volume': IN,\n 'delete-network-volume': IN,\n 'delete-organization-group': IN,\n 'delete-organization-invite': IN,\n 'delete-organization-role': IN,\n 'delete-organization-user': IN,\n 'delete-pod': IN,\n 'delete-template': IN,\n 'deploy-hub-repo': IN,\n 'detach-cost-center-resource': IN,\n 'endpoint-health': IN,\n 'get-capacity': IN,\n 'get-cluster': IN,\n 'get-cost-center': IN,\n 'get-cpu-type': IN,\n 'get-data-center': IN,\n 'get-endpoint': IN,\n 'get-endpoint-build': IN,\n 'get-endpoint-cold-start-metrics': IN,\n 'get-endpoint-request-metrics': IN,\n 'get-endpoint-worker-metrics': IN,\n 'get-global-volume': IN,\n 'get-gpu-type': IN,\n 'get-job-status': IN,\n 'get-network-volume': IN,\n 'get-organization': IN,\n 'get-organization-group': IN,\n 'get-organization-invite': IN,\n 'get-organization-role': IN,\n 'get-organization-role-capabilities': IN,\n 'get-organization-user': IN,\n 'get-pod': IN,\n 'get-ssh-keys': IN,\n 'get-template': IN,\n 'list-audit-logs': IN,\n 'list-billing': IN,\n 'list-cluster-billing': IN,\n 'list-cluster-pods': IN,\n 'list-clusters': IN,\n 'list-cost-center-resources': IN,\n 'list-cost-centers': IN,\n 'list-cpu-types': IN,\n 'list-data-centers': IN,\n 'list-delegations': IN,\n 'list-endpoint-billing': IN,\n 'list-endpoint-builds': IN,\n 'list-endpoint-releases': IN,\n 'list-endpoint-workers': IN,\n 'list-endpoints': IN,\n 'list-global-volumes': IN,\n 'list-gpu-types': IN,\n 'list-hub-repos': IN,\n 'list-network-volume-billing': IN,\n 'list-network-volumes': IN,\n 'list-organization-group-members': IN,\n 'list-organization-groups': IN,\n 'list-organization-invites': IN,\n 'list-organization-roles': IN,\n 'list-organization-users': IN,\n 'list-pod-billing': IN,\n 'list-pods': IN,\n 'list-public-endpoints': IN,\n 'list-public-templates': IN,\n 'list-serverless-billing': IN,\n 'list-templates': IN,\n 'lookup-concept': IN,\n 'purge-endpoint-queue': IN,\n 'remove-organization-group-member': IN,\n 'retry-job': IN,\n 'revoke-delegation': IN,\n 'search-concepts': IN,\n 'search-docs': IN,\n 'set-endpoint-gpus': IN,\n 'stream-job': IN,\n 'stream-pod-logs': IN,\n 'stream-worker-logs': IN,\n 'update-cluster': IN,\n 'update-cost-center': IN,\n 'update-endpoint': IN,\n 'update-global-volume': IN,\n 'update-network-volume': IN,\n 'update-organization-group': IN,\n 'update-organization-role': IN,\n 'update-organization-user': IN,\n 'update-pod': IN,\n 'update-ssh-keys': IN,\n 'update-template': IN,\n 'run-endpoint': { include: true, omitInputs: ['s3Config'] },\n 'runsync-endpoint': { include: true, omitInputs: ['s3Config'] },\n 'pod-action': {\n include: false,\n reason:\n 'Takes the transition as an argument; OpenAI asks for one tool per operation, so the directory serves start-pod, stop-pod and restart-pod (terminate is delete-pod).',\n },\n 'start-pod': IN,\n 'stop-pod': IN,\n 'restart-pod': IN,\n 'list-guides': {\n include: false,\n reason: 'Serves playbooks that instruct the model how to behave.',\n },\n 'read-guide': {\n include: false,\n reason: 'Serves playbooks that instruct the model how to behave.',\n },\n 'describe-tool': {\n include: false,\n reason:\n 'Fetches schemas the directory already lists in full; directories reject schema fetching.',\n },\n 'create-secret': { include: false, reason: CREDENTIAL_INPUT },\n 'get-secret': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'list-secrets': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'delete-secret': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'get-registry': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'list-registries': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'delete-registry': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'update-secret': { include: false, reason: CREDENTIAL_INPUT },\n 'create-registry': { include: false, reason: CREDENTIAL_INPUT },\n report_feedback: { include: false, reason: ALP },\n report_how_user_found_runpod: { include: false, reason: ALP },\n save_to_journal: { include: false, reason: ALP },\n read_journal: { include: false, reason: ALP },\n ask_question: { include: false, reason: ALP },\n};\n\n/** Served on the directory profile only with an explicit include. */\nexport function directoryIncludes(name: string): boolean {\n return DIRECTORY_TOOLS[name]?.include === true;\n}\n\nexport function directoryOmittedInputs(name: string): readonly string[] {\n const decision = DIRECTORY_TOOLS[name];\n return decision?.include ? (decision.omitInputs ?? []) : [];\n}\n\nexport function profileForPath(pathname: string): ServerProfile {\n return pathname.replace(/\\/+$/, '') === DIRECTORY_PATH\n ? 'directory'\n : 'default';\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/specgen/profile.ts"],"sourcesContent":["// Which tool surface a connection gets. `default` is the full surface every\n// client has always had. `directory` is what app directories (Anthropic's\n// connector directory, OpenAI's plugin directory) list: their policies forbid\n// tool descriptions that instruct the model, steer it toward other tools or\n// external instructions, or collect conversation data beyond the tool's job.\n// The directory profile is served only at DIRECTORY_PATH; nothing about the\n// default surface changes.\nexport type ServerProfile = 'default' | 'directory';\n\nexport const DIRECTORY_PATH = '/directory';\n\n/** Whether a tool is listed on the directory profile. Every tool the server\n * can serve must have an entry, so a new tool (a spec regeneration, a new\n * curated tool) fails tests/specgen-directory.test.ts until someone decides.\n * The rules for deciding are in specgen/DIRECTORY_POLICY.md.\n * `omitInputs` hides arguments the directory must not ask for (a credential,\n * for example); a call that passes one is refused. A key is a top-level\n * argument (`s3Config`) or one level inside an object argument (`body.env`). */\nexport type DirectoryDecision =\n | { include: true; omitInputs?: readonly string[] }\n | { include: false; reason: string };\n\nconst IN = { include: true } as const;\nconst CREDENTIAL_MANAGEMENT =\n 'Manages stored credentials; OpenAI restricts plugins that handle access credentials, so the directory leaves the whole credential family out.';\nconst CREDENTIAL_INPUT =\n 'Takes a secret or password as input; directories forbid collecting credentials.';\nconst ALP =\n 'Collects conversation-derived data beyond the task; the directory profile never serves it.';\n\nexport const DIRECTORY_TOOLS: Readonly<Record<string, DirectoryDecision>> = {\n 'add-organization-group-member': IN,\n 'attach-cost-center-resource': IN,\n 'cancel-job': IN,\n 'create-cluster': IN,\n 'create-cost-center': IN,\n 'create-delegation': IN,\n 'create-endpoint': IN,\n 'create-global-volume': IN,\n 'create-network-volume': IN,\n 'create-organization-group': IN,\n 'create-organization-invite': IN,\n 'create-organization-role': IN,\n 'create-pod': IN,\n 'create-template': IN,\n 'delete-cluster': IN,\n 'delete-cost-center': IN,\n 'delete-endpoint': IN,\n 'delete-global-volume': IN,\n 'delete-network-volume': IN,\n 'delete-organization-group': IN,\n 'delete-organization-invite': IN,\n 'delete-organization-role': IN,\n 'delete-organization-user': IN,\n 'delete-pod': IN,\n 'delete-template': IN,\n 'deploy-hub-repo': IN,\n 'detach-cost-center-resource': IN,\n 'endpoint-health': IN,\n // env can carry tokens (HF_TOKEN for a model download), so the directory omits it.\n 'exec-pod': { include: true, omitInputs: ['body.env'] },\n 'get-capacity': IN,\n 'get-cluster': IN,\n 'get-cost-center': IN,\n 'get-cpu-type': IN,\n 'get-data-center': IN,\n 'get-endpoint': IN,\n 'get-endpoint-build': IN,\n 'get-endpoint-cold-start-metrics': IN,\n 'get-endpoint-request-metrics': IN,\n 'get-endpoint-worker-metrics': IN,\n 'get-global-volume': IN,\n 'get-gpu-type': IN,\n 'get-job-status': IN,\n 'get-network-volume': IN,\n 'get-organization': IN,\n 'get-organization-group': IN,\n 'get-organization-invite': IN,\n 'get-organization-role': IN,\n 'get-organization-role-capabilities': IN,\n 'get-organization-user': IN,\n 'get-pod': IN,\n 'get-ssh-keys': IN,\n 'get-template': IN,\n 'list-audit-logs': IN,\n 'list-billing': IN,\n 'list-cluster-billing': IN,\n 'list-cluster-pods': IN,\n 'list-clusters': IN,\n 'list-cost-center-resources': IN,\n 'list-cost-centers': IN,\n 'list-cpu-types': IN,\n 'list-data-centers': IN,\n 'list-delegations': IN,\n 'list-endpoint-billing': IN,\n 'list-endpoint-builds': IN,\n 'list-endpoint-releases': IN,\n 'list-endpoint-workers': IN,\n 'list-endpoints': IN,\n 'list-global-volumes': IN,\n 'list-gpu-types': IN,\n 'list-hub-repos': IN,\n 'list-network-volume-billing': IN,\n 'list-network-volumes': IN,\n 'list-organization-group-members': IN,\n 'list-organization-groups': IN,\n 'list-organization-invites': IN,\n 'list-organization-roles': IN,\n 'list-organization-users': IN,\n 'list-pod-billing': IN,\n 'list-pods': IN,\n 'list-public-endpoints': IN,\n 'list-public-templates': IN,\n 'list-serverless-billing': IN,\n 'list-templates': IN,\n 'lookup-concept': IN,\n 'purge-endpoint-queue': IN,\n 'remove-organization-group-member': IN,\n 'retry-job': IN,\n 'revoke-delegation': IN,\n 'search-concepts': IN,\n 'search-docs': IN,\n 'set-endpoint-gpus': IN,\n 'stream-job': IN,\n 'stream-pod-logs': IN,\n 'stream-worker-logs': IN,\n 'update-cluster': IN,\n 'update-cost-center': IN,\n 'update-endpoint': IN,\n 'update-global-volume': IN,\n 'update-network-volume': IN,\n 'update-organization-group': IN,\n 'update-organization-role': IN,\n 'update-organization-user': IN,\n 'update-pod': IN,\n 'update-ssh-keys': IN,\n 'update-template': IN,\n 'run-endpoint': { include: true, omitInputs: ['s3Config'] },\n 'runsync-endpoint': { include: true, omitInputs: ['s3Config'] },\n 'pod-action': {\n include: false,\n reason:\n 'Takes the transition as an argument; OpenAI asks for one tool per operation, so the directory serves start-pod, stop-pod and restart-pod (terminate is delete-pod).',\n },\n 'start-pod': IN,\n 'stop-pod': IN,\n 'restart-pod': IN,\n 'list-guides': {\n include: false,\n reason: 'Serves playbooks that instruct the model how to behave.',\n },\n 'read-guide': {\n include: false,\n reason: 'Serves playbooks that instruct the model how to behave.',\n },\n 'describe-tool': {\n include: false,\n reason:\n 'Fetches schemas the directory already lists in full; directories reject schema fetching.',\n },\n 'create-secret': { include: false, reason: CREDENTIAL_INPUT },\n 'get-secret': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'list-secrets': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'delete-secret': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'get-registry': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'list-registries': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'delete-registry': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'update-secret': { include: false, reason: CREDENTIAL_INPUT },\n 'create-registry': { include: false, reason: CREDENTIAL_INPUT },\n report_feedback: { include: false, reason: ALP },\n report_how_user_found_runpod: { include: false, reason: ALP },\n save_to_journal: { include: false, reason: ALP },\n read_journal: { include: false, reason: ALP },\n ask_question: { include: false, reason: ALP },\n};\n\n/** Served on the directory profile only with an explicit include. */\nexport function directoryIncludes(name: string): boolean {\n return DIRECTORY_TOOLS[name]?.include === true;\n}\n\nexport function directoryOmittedInputs(name: string): readonly string[] {\n const decision = DIRECTORY_TOOLS[name];\n return decision?.include ? (decision.omitInputs ?? []) : [];\n}\n\n/** The omitted inputs (see DirectoryDecision) that a call's arguments set. */\nexport function omittedInputsIn(\n omit: readonly string[],\n args: Record<string, unknown>\n): string[] {\n return omit.filter((key) => {\n const [parent, child] = key.split('.');\n if (child === undefined) return parent in args;\n let value = args[parent];\n // Dispatch parses a body sent as a JSON string, so check its parsed form;\n // a string that is not JSON fails in dispatch instead.\n if (typeof value === 'string') {\n try {\n value = JSON.parse(value);\n } catch {\n return false;\n }\n }\n return !!value && typeof value === 'object' && child in value;\n });\n}\n\nexport function profileForPath(pathname: string): ServerProfile {\n return pathname.replace(/\\/+$/, '') === DIRECTORY_PATH\n ? 'directory'\n : 'default';\n}\n"],"mappings":"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","names":[]}
@@ -4,8 +4,9 @@ declare const DIRECTORY_PATH = "/directory";
4
4
  * can serve must have an entry, so a new tool (a spec regeneration, a new
5
5
  * curated tool) fails tests/specgen-directory.test.ts until someone decides.
6
6
  * The rules for deciding are in specgen/DIRECTORY_POLICY.md.
7
- * `omitInputs` hides top-level arguments the directory must not ask for (a
8
- * credential, for example); a call that passes one is refused. */
7
+ * `omitInputs` hides arguments the directory must not ask for (a credential,
8
+ * for example); a call that passes one is refused. A key is a top-level
9
+ * argument (`s3Config`) or one level inside an object argument (`body.env`). */
9
10
  type DirectoryDecision = {
10
11
  include: true;
11
12
  omitInputs?: readonly string[];
@@ -17,6 +18,8 @@ declare const DIRECTORY_TOOLS: Readonly<Record<string, DirectoryDecision>>;
17
18
  /** Served on the directory profile only with an explicit include. */
18
19
  declare function directoryIncludes(name: string): boolean;
19
20
  declare function directoryOmittedInputs(name: string): readonly string[];
21
+ /** The omitted inputs (see DirectoryDecision) that a call's arguments set. */
22
+ declare function omittedInputsIn(omit: readonly string[], args: Record<string, unknown>): string[];
20
23
  declare function profileForPath(pathname: string): ServerProfile;
21
24
 
22
- export { DIRECTORY_PATH, DIRECTORY_TOOLS, type DirectoryDecision, type ServerProfile, directoryIncludes, directoryOmittedInputs, profileForPath };
25
+ export { DIRECTORY_PATH, DIRECTORY_TOOLS, type DirectoryDecision, type ServerProfile, directoryIncludes, directoryOmittedInputs, omittedInputsIn, profileForPath };
@@ -4,8 +4,9 @@ declare const DIRECTORY_PATH = "/directory";
4
4
  * can serve must have an entry, so a new tool (a spec regeneration, a new
5
5
  * curated tool) fails tests/specgen-directory.test.ts until someone decides.
6
6
  * The rules for deciding are in specgen/DIRECTORY_POLICY.md.
7
- * `omitInputs` hides top-level arguments the directory must not ask for (a
8
- * credential, for example); a call that passes one is refused. */
7
+ * `omitInputs` hides arguments the directory must not ask for (a credential,
8
+ * for example); a call that passes one is refused. A key is a top-level
9
+ * argument (`s3Config`) or one level inside an object argument (`body.env`). */
9
10
  type DirectoryDecision = {
10
11
  include: true;
11
12
  omitInputs?: readonly string[];
@@ -17,6 +18,8 @@ declare const DIRECTORY_TOOLS: Readonly<Record<string, DirectoryDecision>>;
17
18
  /** Served on the directory profile only with an explicit include. */
18
19
  declare function directoryIncludes(name: string): boolean;
19
20
  declare function directoryOmittedInputs(name: string): readonly string[];
21
+ /** The omitted inputs (see DirectoryDecision) that a call's arguments set. */
22
+ declare function omittedInputsIn(omit: readonly string[], args: Record<string, unknown>): string[];
20
23
  declare function profileForPath(pathname: string): ServerProfile;
21
24
 
22
- export { DIRECTORY_PATH, DIRECTORY_TOOLS, type DirectoryDecision, type ServerProfile, directoryIncludes, directoryOmittedInputs, profileForPath };
25
+ export { DIRECTORY_PATH, DIRECTORY_TOOLS, type DirectoryDecision, type ServerProfile, directoryIncludes, directoryOmittedInputs, omittedInputsIn, profileForPath };
@@ -3,13 +3,15 @@ import {
3
3
  DIRECTORY_TOOLS,
4
4
  directoryIncludes,
5
5
  directoryOmittedInputs,
6
+ omittedInputsIn,
6
7
  profileForPath
7
- } from "../chunk-FQJFKWUV.mjs";
8
+ } from "../chunk-24UOECYY.mjs";
8
9
  export {
9
10
  DIRECTORY_PATH,
10
11
  DIRECTORY_TOOLS,
11
12
  directoryIncludes,
12
13
  directoryOmittedInputs,
14
+ omittedInputsIn,
13
15
  profileForPath
14
16
  };
15
17
  //# sourceMappingURL=profile.mjs.map
package/dist/stdio.cjs CHANGED
@@ -1183,7 +1183,7 @@ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
1183
1183
 
1184
1184
  // src/server.ts
1185
1185
  var SERVER_NAME = "Runpod API Server";
1186
- var SERVER_VERSION = true ? "4.10.1" : "dev";
1186
+ var SERVER_VERSION = true ? "4.11.0" : "dev";
1187
1187
 
1188
1188
  // src/specgen/server.ts
1189
1189
  var import_server3 = require("@modelcontextprotocol/sdk/server/index.js");
@@ -1409,25 +1409,16 @@ async function dispatchGeneratedTool(client, tool, args) {
1409
1409
  const value = args[param.name];
1410
1410
  query[param.name] = param.explode === false && Array.isArray(value) ? value.join(",") : value;
1411
1411
  }
1412
- let requestBody = args.body;
1413
- if (tool.hasBody && typeof requestBody === "string") {
1414
- try {
1415
- requestBody = JSON.parse(requestBody);
1416
- } catch {
1417
- return {
1418
- ok: false,
1419
- status: 400,
1420
- payload: {
1421
- error: "The body argument arrived as a string that is not valid JSON. Send body as an object."
1422
- }
1423
- };
1424
- }
1425
- }
1426
- if (tool.hasBody && requestBody !== void 0) {
1427
- const properties = tool.inputSchema.properties ?? {};
1428
- const defs = tool.inputSchema.$defs ?? {};
1429
- requestBody = coerceScalars(requestBody, properties.body, defs);
1430
- }
1412
+ const sent = tool.hasBody ? bodyAsSent(tool, args.body) : { body: args.body };
1413
+ if (!sent)
1414
+ return {
1415
+ ok: false,
1416
+ status: 400,
1417
+ payload: {
1418
+ error: "The body argument arrived as a string that is not valid JSON. Send body as an object."
1419
+ }
1420
+ };
1421
+ const requestBody = sent.body;
1431
1422
  const verbs = client;
1432
1423
  const { data, error, response } = await verbs[tool.method](tool.path, {
1433
1424
  params: { path: pathParams, query },
@@ -1440,6 +1431,32 @@ async function dispatchGeneratedTool(client, tool, args) {
1440
1431
  payload: data ?? { status: response.status }
1441
1432
  };
1442
1433
  }
1434
+ function bodyAsSent(tool, body) {
1435
+ let parsed = body;
1436
+ if (typeof parsed === "string") {
1437
+ try {
1438
+ parsed = JSON.parse(parsed);
1439
+ } catch {
1440
+ return null;
1441
+ }
1442
+ }
1443
+ if (parsed === void 0) return { body: parsed };
1444
+ const properties = tool.inputSchema.properties ?? {};
1445
+ const defs = tool.inputSchema.$defs ?? {};
1446
+ return { body: coerceScalars(parsed, properties.body, defs) };
1447
+ }
1448
+ function argsAsSent(tool, args) {
1449
+ if (!tool.hasBody || args.body === void 0) return args;
1450
+ const sent = bodyAsSent(tool, args.body);
1451
+ return sent ? { ...args, body: sent.body } : null;
1452
+ }
1453
+ function omittedEmptyQueryArg(tool) {
1454
+ const required = new Set(tool.inputSchema.required ?? []);
1455
+ const query = new Set(
1456
+ tool.params.filter((p2) => p2.location === "query").map((p2) => p2.name)
1457
+ );
1458
+ return (name, value) => query.has(name) && !required.has(name) && isEmptyValue(value);
1459
+ }
1443
1460
  function isEmptyValue(value) {
1444
1461
  return value === null || value === "" || Array.isArray(value) && !value.length;
1445
1462
  }
@@ -3876,6 +3893,109 @@ var generatedTools = [
3876
3893
  }
3877
3894
  }
3878
3895
  },
3896
+ {
3897
+ name: "exec-pod",
3898
+ title: "Run a command in a Pod",
3899
+ operationId: "execPod",
3900
+ description: `Run one command inside a running Pod's container and get stdout, stderr and the exit code back. No SSH key, public IP or exposed port is needed. command is an argv array run without a shell: pass ["sh", "-c", "..."] for pipes, redirects, globs or &&. One call waits at most timeoutSeconds (1-25, default 20). timedOut true means the call stopped waiting, NOT that the command stopped: it keeps running in the container. For anything longer (model downloads, pip installs, builds), start it in the background and log to a file, e.g. ["sh", "-c", "nohup wget -c -O /workspace/m.safetensors URL > /tmp/dl.log 2>&1 &"], then check progress with later calls (ls -la, du -h, tail /tmp/dl.log). stdout and stderr are each capped at 1 MiB (truncated true). Right after a create, the Pod can answer 404 "Pod not found" for about 10 seconds even with the right id, then 409 while its container starts: retry in about 10 seconds, a few times at most. A Pod that is not running answers 409. A host not yet updated for exec answers 503. Reaches only Pods owned by the API key's own account. Commands can change or delete data in the Pod: run write commands only for what the user asked.`,
3901
+ directoryDescription: "Run one command inside a running Pod's container and return stdout, stderr and the exit code. The command is an argv array run without a shell. One call waits at most timeoutSeconds (1-25, default 20); a command still running then keeps running in the container, and the result says timedOut. stdout and stderr are each capped at 1 MiB. A Pod that is not running answers 409, and a host not yet updated for exec answers 503. Reaches only Pods owned by the caller's own account. The command can change or delete data in the Pod.",
3902
+ method: "POST",
3903
+ path: "/v2/pods/{id}/exec",
3904
+ params: [
3905
+ {
3906
+ name: "id",
3907
+ location: "path"
3908
+ }
3909
+ ],
3910
+ hasBody: true,
3911
+ annotations: {
3912
+ readOnlyHint: false,
3913
+ destructiveHint: true,
3914
+ idempotentHint: false,
3915
+ openWorldHint: true
3916
+ },
3917
+ inputSchema: {
3918
+ type: "object",
3919
+ properties: {
3920
+ id: {
3921
+ type: "string",
3922
+ description: "Pod identifier"
3923
+ },
3924
+ body: {
3925
+ $ref: "#/$defs/PodExecRequest",
3926
+ type: "object"
3927
+ }
3928
+ },
3929
+ required: ["id", "body"],
3930
+ $defs: {
3931
+ PodExecRequest: {
3932
+ type: "object",
3933
+ additionalProperties: false,
3934
+ required: ["command"],
3935
+ properties: {
3936
+ command: {
3937
+ type: "array",
3938
+ minItems: 1,
3939
+ maxItems: 256,
3940
+ prefixItems: [
3941
+ {
3942
+ type: "string",
3943
+ minLength: 1,
3944
+ maxLength: 4096,
3945
+ pattern: "^[^\\x00]*[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\\x00][^\\x00]*$"
3946
+ }
3947
+ ],
3948
+ items: {
3949
+ type: "string",
3950
+ maxLength: 4096,
3951
+ pattern: "^[^\\x00]*$"
3952
+ },
3953
+ description: 'Command to run, as an argv array of 1-256 arguments of at most 4096 characters each. It is run directly, never through a shell; for pipes, redirects or globbing pass `["sh", "-c", "..."]`.',
3954
+ examples: [
3955
+ ["nvidia-smi", "--query-gpu=name,memory.used", "--format=csv"]
3956
+ ]
3957
+ },
3958
+ timeoutSeconds: {
3959
+ type: "integer",
3960
+ minimum: 1,
3961
+ maximum: 25,
3962
+ default: 20,
3963
+ description: "How long the call waits for the command to finish, in seconds. A command still running at this point returns with `timedOut: true`.",
3964
+ examples: [10]
3965
+ },
3966
+ cwd: {
3967
+ type: "string",
3968
+ pattern: "^/[^\\x00-\\x1f\\x7f]*$",
3969
+ maxLength: 1024,
3970
+ description: "Absolute working directory for the command, at most 1024 characters and without control characters. Defaults to the image's `WORKDIR`.",
3971
+ examples: ["/workspace"]
3972
+ },
3973
+ env: {
3974
+ type: "object",
3975
+ maxProperties: 64,
3976
+ propertyNames: {
3977
+ type: "string",
3978
+ minLength: 1,
3979
+ maxLength: 256,
3980
+ pattern: "^[^=\\x00]+$"
3981
+ },
3982
+ additionalProperties: {
3983
+ type: "string",
3984
+ maxLength: 16384,
3985
+ pattern: "^[^\\x00]*$"
3986
+ },
3987
+ description: "Up to 64 environment variables added to the container's environment for this command only. A name is 1-256 characters and must not contain `=` or a NUL character; a value is at most 16384 characters and must not contain a NUL character.",
3988
+ examples: [
3989
+ {
3990
+ HF_HOME: "/workspace/hf"
3991
+ }
3992
+ ]
3993
+ }
3994
+ }
3995
+ }
3996
+ }
3997
+ }
3998
+ },
3879
3999
  {
3880
4000
  name: "get-cluster",
3881
4001
  title: "Get cluster",
@@ -7938,6 +8058,122 @@ var generatedTools = [
7938
8058
  }
7939
8059
  ];
7940
8060
 
8061
+ // src/specgen/validate.ts
8062
+ var import__ = require("ajv/dist/2020.js");
8063
+ function schemaValidationMode(value = process.env.MCP_SCHEMA_VALIDATION) {
8064
+ const mode = value?.trim().toLowerCase();
8065
+ return mode === "off" || mode === "enforce" ? mode : "shadow";
8066
+ }
8067
+ var ajv = new import__.Ajv2020({
8068
+ strict: false,
8069
+ allErrors: true,
8070
+ validateFormats: false
8071
+ });
8072
+ var compiled = /* @__PURE__ */ new WeakMap();
8073
+ function validatorFor(schema) {
8074
+ let validate = compiled.get(schema);
8075
+ if (!validate) {
8076
+ validate = ajv.compile(structuredClone(schema));
8077
+ compiled.set(schema, validate);
8078
+ }
8079
+ return validate;
8080
+ }
8081
+ var MAX_NULL_PASSES = 8;
8082
+ function isNullTypeError(error, data) {
8083
+ return error.keyword === "type" && error.instancePath !== "" && valueAt(data, error.instancePath) === null;
8084
+ }
8085
+ function pointerSegments(pointer) {
8086
+ return pointer.split("/").slice(1).map((s) => s.replace(/~1/g, "/").replace(/~0/g, "~"));
8087
+ }
8088
+ function valueAt(data, pointer) {
8089
+ let node = data;
8090
+ for (const key of pointerSegments(pointer)) {
8091
+ if (node === null || typeof node !== "object") return void 0;
8092
+ node = node[key];
8093
+ }
8094
+ return node;
8095
+ }
8096
+ function removeAt(data, pointer) {
8097
+ const segments = pointerSegments(pointer);
8098
+ const last = segments.pop();
8099
+ const parent = valueAt(data, segments.length ? `/${segments.join("/")}` : "");
8100
+ if (last === void 0 || parent === null || typeof parent !== "object")
8101
+ return false;
8102
+ if (Array.isArray(parent)) return false;
8103
+ delete parent[last];
8104
+ return true;
8105
+ }
8106
+ var WRAPPER_KEYWORDS = /* @__PURE__ */ new Set(["if"]);
8107
+ function toViolation(error) {
8108
+ const base = pointerSegments(error.instancePath);
8109
+ let path2 = base.join(".");
8110
+ let message = error.message ?? "is invalid";
8111
+ const params = error.params;
8112
+ if (error.keyword === "required" && typeof params.missingProperty === "string") {
8113
+ path2 = [...base, params.missingProperty].join(".");
8114
+ message = "is required";
8115
+ } else if (error.keyword === "additionalProperties" && typeof params.additionalProperty === "string") {
8116
+ path2 = [...base, params.additionalProperty].join(".");
8117
+ message = "is not an accepted argument here";
8118
+ } else if (error.keyword === "pattern") {
8119
+ message = "is not in an accepted format";
8120
+ }
8121
+ const allowed = error.keyword === "enum" ? params.allowedValues : error.keyword === "const" ? [params.allowedValue] : void 0;
8122
+ return {
8123
+ path: path2,
8124
+ keyword: error.keyword,
8125
+ message,
8126
+ ...allowed ? { allowed } : {},
8127
+ schemaPath: error.schemaPath
8128
+ };
8129
+ }
8130
+ function schemaViolations(schema, args, omitted = () => false) {
8131
+ const validate = validatorFor(schema);
8132
+ const data = structuredClone(
8133
+ Object.fromEntries(
8134
+ Object.entries(args).filter(([name, value]) => !omitted(name, value))
8135
+ )
8136
+ );
8137
+ for (let pass = 0; ; pass++) {
8138
+ if (validate(data)) return [];
8139
+ const errors = validate.errors ?? [];
8140
+ let removed = false;
8141
+ if (pass < MAX_NULL_PASSES) {
8142
+ for (const error of errors.filter((e) => isNullTypeError(e, data)))
8143
+ removed = removeAt(data, error.instancePath) || removed;
8144
+ }
8145
+ if (!removed) {
8146
+ return errors.filter((e) => !WRAPPER_KEYWORDS.has(e.keyword)).map(toViolation);
8147
+ }
8148
+ }
8149
+ }
8150
+ var INVALID_ARGUMENTS_HINT = "Fix the listed fields and call again.";
8151
+ function invalidArgumentsPayload(violations) {
8152
+ return {
8153
+ error: "Invalid arguments",
8154
+ code: "invalid_arguments",
8155
+ fields: violations.map(({ path: path2, message, allowed }) => ({
8156
+ path: path2,
8157
+ problem: message,
8158
+ ...allowed ? { allowed } : {}
8159
+ })),
8160
+ hint: INVALID_ARGUMENTS_HINT
8161
+ };
8162
+ }
8163
+ function logSchemaViolations(tool, mode, violations) {
8164
+ console.error(
8165
+ "schema_violation",
8166
+ JSON.stringify({
8167
+ tool,
8168
+ mode,
8169
+ violations: violations.map(({ keyword, schemaPath }) => ({
8170
+ keyword,
8171
+ schemaPath
8172
+ }))
8173
+ })
8174
+ );
8175
+ }
8176
+
7941
8177
  // src/specgen/tools/annotations.ts
7942
8178
  var readOnly = {
7943
8179
  readOnlyHint: true,
@@ -8151,6 +8387,8 @@ var DIRECTORY_TOOLS = {
8151
8387
  "deploy-hub-repo": IN,
8152
8388
  "detach-cost-center-resource": IN,
8153
8389
  "endpoint-health": IN,
8390
+ // env can carry tokens (HF_TOKEN for a model download), so the directory omits it.
8391
+ "exec-pod": { include: true, omitInputs: ["body.env"] },
8154
8392
  "get-capacity": IN,
8155
8393
  "get-cluster": IN,
8156
8394
  "get-cost-center": IN,
@@ -8270,6 +8508,21 @@ function directoryOmittedInputs(name) {
8270
8508
  const decision = DIRECTORY_TOOLS[name];
8271
8509
  return decision?.include ? decision.omitInputs ?? [] : [];
8272
8510
  }
8511
+ function omittedInputsIn(omit, args) {
8512
+ return omit.filter((key) => {
8513
+ const [parent, child] = key.split(".");
8514
+ if (child === void 0) return parent in args;
8515
+ let value = args[parent];
8516
+ if (typeof value === "string") {
8517
+ try {
8518
+ value = JSON.parse(value);
8519
+ } catch {
8520
+ return false;
8521
+ }
8522
+ }
8523
+ return !!value && typeof value === "object" && child in value;
8524
+ });
8525
+ }
8273
8526
 
8274
8527
  // src/specgen/redact.ts
8275
8528
  var REDACTED = "[redacted]";
@@ -11839,13 +12092,40 @@ function withoutCredentialExamples(schema) {
11839
12092
  };
11840
12093
  return strip(schema);
11841
12094
  }
11842
- function withoutInputs(schema, omit) {
11843
- if (!omit.length) return schema;
12095
+ function withoutKeys(schema, omit) {
11844
12096
  const properties = { ...schema.properties ?? {} };
11845
12097
  for (const key of omit) delete properties[key];
11846
12098
  const required = Array.isArray(schema.required) ? schema.required.filter((key) => !omit.includes(key)) : schema.required;
11847
12099
  return { ...schema, properties, ...required ? { required } : {} };
11848
12100
  }
12101
+ function withoutInputs(schema, omit) {
12102
+ if (!omit.length) return schema;
12103
+ let out = withoutKeys(
12104
+ schema,
12105
+ omit.filter((key) => !key.includes("."))
12106
+ );
12107
+ for (const key of omit.filter((k) => k.includes("."))) {
12108
+ const [parent, child] = key.split(".");
12109
+ const properties = out.properties ?? {};
12110
+ const node = properties[parent];
12111
+ if (!node) continue;
12112
+ const ref = typeof node.$ref === "string" ? node.$ref : void 0;
12113
+ const defName = ref?.startsWith("#/$defs/") ? ref.slice(8) : void 0;
12114
+ const defs = out.$defs ?? {};
12115
+ if (defName && defs[defName]) {
12116
+ out = {
12117
+ ...out,
12118
+ $defs: { ...defs, [defName]: withoutKeys(defs[defName], [child]) }
12119
+ };
12120
+ } else {
12121
+ out = {
12122
+ ...out,
12123
+ properties: { ...properties, [parent]: withoutKeys(node, [child]) }
12124
+ };
12125
+ }
12126
+ }
12127
+ return out;
12128
+ }
11849
12129
  function callShape(schema) {
11850
12130
  const defs = schema.$defs ?? {};
11851
12131
  const resolve2 = (node) => {
@@ -12809,6 +13089,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
12809
13089
  const caller = callerId(ctx.apiKey);
12810
13090
  const profile = opts.profile ?? "default";
12811
13091
  const directory = profile === "directory";
13092
+ const schemaMode = schemaValidationMode();
12812
13093
  const alp = directory ? void 0 : opts.alp;
12813
13094
  const servedGeneratedTools = directory ? generatedTools.filter((tool) => directoryIncludes(tool.name)) : generatedTools;
12814
13095
  const servedCuratedTools = [
@@ -12915,7 +13196,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
12915
13196
  (tool) => tool.name === request.params.name
12916
13197
  );
12917
13198
  const curatedArgs = curated ? withoutNullOptionals(curated.inputSchema, args) : args;
12918
- const omitted = directory ? directoryOmittedInputs(request.params.name).filter((key) => key in args) : [];
13199
+ const omitted = directory ? omittedInputsIn(directoryOmittedInputs(request.params.name), args) : [];
12919
13200
  const rejected = omitted.length ? {
12920
13201
  ok: false,
12921
13202
  status: 400,
@@ -12925,8 +13206,26 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
12925
13206
  } : curated && !curated.lenientArguments ? validateArguments(
12926
13207
  curated.inputSchema,
12927
13208
  curatedArgs
12928
- ) : null;
12929
- const result = rejected ?? await runTool(
13209
+ ) : generated ? validateArguments(generated.inputSchema, args) : null;
13210
+ let schemaRejected = null;
13211
+ if (!rejected && schemaMode !== "off") {
13212
+ const sent = generated ? argsAsSent(generated, args) : null;
13213
+ const violations = curated ? curated.lenientArguments ? [] : schemaViolations(curated.inputSchema, curatedArgs) : generated && sent ? schemaViolations(
13214
+ generated.inputSchema,
13215
+ sent,
13216
+ omittedEmptyQueryArg(generated)
13217
+ ) : [];
13218
+ if (violations.length) {
13219
+ logSchemaViolations(request.params.name, schemaMode, violations);
13220
+ if (schemaMode === "enforce")
13221
+ schemaRejected = {
13222
+ ok: false,
13223
+ status: 400,
13224
+ payload: invalidArgumentsPayload(violations)
13225
+ };
13226
+ }
13227
+ }
13228
+ const result = rejected ?? schemaRejected ?? await runTool(
12930
13229
  () => curated ? curated.handler(ctx, curatedArgs) : dispatchTool(generated, request.params.name, args)
12931
13230
  );
12932
13231
  logToolCall({