@runpod/mcp-server 2.0.0 → 3.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,72 @@
1
1
  # @runpod/mcp-server
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 119f633: Resync the tool surface with the current v2 REST spec.
8
+
9
+ **Breaking:** removes the seven tag tools (`list-tags`, `get-tag`, `create-tag`, `update-tag`,
10
+ `delete-tag`, `attach-tag`, `detach-tag`). `/v2/tags` no longer exists — it returns
11
+ `404 "The requested path was not found."` on both the production and dev hosts — so the tools
12
+ could not succeed for any caller.
13
+
14
+ **New:** three AWS ECR delegation tools (`list-registry-delegations`,
15
+ `create-registry-delegation`, `delete-registry-delegation`) for `/v2/registries/delegations`.
16
+ Unlike `create-container-registry-auth`, this stores no credentials: you register an ECR
17
+ repository ARN and Runpod is granted scoped pull access. v2-only.
18
+
19
+ **New:** `create-network-volume` gains `volumeType` (`STANDARD` | `HIGH_PERFORMANCE`). The
20
+ field was in the spec but no tool exposed it, so high-performance volumes could not be created
21
+ here. Omit it for the data center default. Its documented size range is also corrected to
22
+ 10–4096 GB (was 1–4000).
23
+
24
+ **Enabled:** `list-endpoint-releases` was implemented but left unregistered because
25
+ `GET /v2/serverless/{id}/releases` used to be dev-only. It now returns 200 on production.
26
+
27
+ `create-template` no longer sends `category: NVIDIA` when the caller omits it — v2 made the
28
+ field optional with that same server-side default, so the value is no longer invented
29
+ client-side. An explicit `category` still passes through.
30
+
31
+ ### Minor Changes
32
+
33
+ - fbcf9b7: Add `RUNPOD_AUTHED_GRAPHQL_URL` to override the GraphQL host used by authenticated operations that have no REST equivalent (`deploy-hub-repo`, `set-endpoint-gpus`). These calls send the caller's API key as a Bearer token, so they no longer follow `RUNPOD_PUBLIC_GRAPHQL_URL` — that variable stays the credential-free discovery override, safe to point at a stub.
34
+ - 00e6ec2: Surface dead credentials as a proper HTTP 401 on the HTTP server so OAuth-capable MCP clients re-authenticate automatically.
35
+
36
+ Previously, when a bearer token was revoked mid-session (e.g. an OAuth-minted key), every upstream Runpod call failed with a 401 that the MCP SDK wrapped into a 200 JSON-RPC tool error — the client never saw an HTTP 401, never re-ran its auth flow, and the user was stuck with bare "Unauthorized" tool errors until they manually reconnected. The request handler now pre-flight verifies the bearer (one `myself` GraphQL query, cached ~60s valid / ~30s invalid by token hash, never the raw token) and answers `401` + `WWW-Authenticate` with the protected-resource metadata when the credential is dead. `WWW-Authenticate` is exposed via CORS so browser clients can read it, and a rejected credential carries `error="invalid_token"` to distinguish it from a request that sent none (RFC 6750 §3.1). `ToolContext` gains an optional `onUnauthorized` callback, invoked when an outbound call returns 401, which drops the cached verdict so the next request re-checks.
37
+
38
+ The check is deliberately conservative so it can never reject a working key:
39
+ - It fails **open** on anything indeterminate — auth-backend errors, 5xx, 403 (this host sits behind a WAF, and a block is not a revocation), a slow backend (time-bound), and a GraphQL response carrying an `errors` array (a resolver blip returns `myself: null` for everyone at once; treating that as invalid would 401 every valid key). Failing closed on any of these would make OAuth clients re-run the flow, minting a new API key per attempt — an unrecoverable loop.
40
+ - It runs only for the requests the transport would actually run a tool for: a `POST` whose `Content-Type` the SDK treats as JSON and whose body invokes a method (including inside a batch). Everything else is passed through unchecked.
41
+ - It self-disables (logging `skip_env_mismatch`) when the REST/Serverless hosts and the auth-GraphQL host disagree about environment in either direction, since it would otherwise validate a key against the wrong backend.
42
+ - Request bodies are capped at 4 MB (matching the SDK) on hosts that do not pre-parse them.
43
+ - The verdict cache is safe under load: eviction never disowns an in-flight check, and TTLs use a monotonic clock, so a burst of distinct tokens or a wall-clock adjustment cannot let a since-revoked key linger as `valid`.
44
+
45
+ Set `MCP_SKIP_CREDENTIAL_CHECK=true` to disable the pre-flight entirely.
46
+
47
+ Notes for consumers of the published `./http` export (not only the hosted deployment): the pre-flight adds one outbound `myself` call to the Runpod GraphQL host per checked request, and a new dependency on that host being reachable (it fails open if not). This does **not** close the pre-existing key-validity oracle — a caller who speaks MCP can still learn 401-vs-not for a token, one upstream call each, with no rate limiting; closing that needs rate limiting, which this does not add.
48
+
49
+ - f3b226b: Add a `deploy-hub-repo` tool that deploys a Runpod Hub repo's listed release as a new Serverless endpoint — the same operation as clicking Deploy on the Hub. Identify the repo by `repo` ("owner/name") or `hubReleaseId` (both from `list-hub-repos`); the release supplies the prebuilt image, container disk, CUDA constraints, and env-var defaults, with caller overrides for env vars, GPU pools, worker counts, scaling, and FlashBoot. Required env vars without a default fail fast with an actionable message before anything is created, as do POD listings and releases without a GPU pool when none is provided. Uses the authenticated GraphQL `saveEndpoint` mutation (Hub deploys have no REST home yet), so it behaves identically on v1 and v2. The tool runtime gains a `graphqlAuthed` helper (API-key Bearer + variables) to support it.
50
+ - 725b6cd: Surface realtime GPU availability on the v2 catalog. `list-gpu-types` now requests `?include=AVAILABILITY` by default, so each GPU carries an `availability` summary (HIGH/MEDIUM/LOW/NONE) and results are sorted highest-stock-first. The full catalog is still returned by default — nothing is hidden — so the tool stays complete for discovery and capacity/price surveys. `includeUnavailable` is now an opt-in _hide_: pass `includeUnavailable: false` to drop out-of-stock GPUs and list only currently-deployable ones. A new `includeAvailability` param (default true) can opt out of the stock lookup entirely. `get-gpu-type` returns the per-datacenter availability breakdown (and URL-encodes ids, which contain spaces) so callers can pick a `dataCenterIds` with stock before creating a pod. Falls back gracefully when the backend doesn't populate availability.
51
+
52
+ The v2 path also drops the `"unknown"` sentinel GPU (matching the v1 path) so it doesn't leak into the now-unhidden default list.
53
+
54
+ Behavior change to note: `list-gpu-types` still returns every GPU by default, but results are now sorted highest-stock-first rather than in a stable order. Nothing is hidden by default.
55
+
56
+ - 546a40f: Add a `list-hub-repos` tool for discovering the Runpod Hub catalog (prebuilt Serverless workers and Pod templates such as vLLM, ComfyUI, and Axolotl). Served by the public GraphQL endpoint — no auth required — and available on both v1 and v2. Each result includes the repo metadata (stars, deploys, category, tags) plus the currently listed release with its `hubReleaseId` and prebuilt `imageName`, the two values a Hub deploy is pinned to. Supports `searchTerm`, `category`, `type` (SERVERLESS/POD), and `repoOwner` filters (applied client-side), and an opt-in `includeConfig` that returns the release's parsed hardware/env-var config from `.runpod/hub.json`. Results are sorted most-deployed first and use the shared pagination envelope.
57
+ - e4fabd1: Add a `list-public-endpoints` tool for discovering Runpod Public Endpoints — the managed, pay-per-use model APIs (text, image, video, audio) that require no deployment. Served by the public GraphQL endpoint — no auth required — and available on both v1 and v2. Each result includes the `endpointId` to call with `run-endpoint`/`runsync-endpoint` (or directly at `https://api.runpod.ai/v2/{endpointId}`), plus the model name, modality, owner, and pricing parsed from the catalog metadata. Only live endpoints are listed by default (`includeOffline:true` to include the rest), with `searchTerm`, `modality`, and `owner` filters and the shared pagination envelope.
58
+ - cc0966c: Enable pod and serverless worker log streaming (v2). `stream-pod-logs` (GET /v2/pods/{id}/logs) is now registered — it was previously implemented but disabled while the endpoint was dev-only, and it is now live on prod. A new `stream-worker-logs` tool (GET /v2/serverless/{id}/workers/{workerId}/logs) streams a single worker's logs; get the workerId from `list-endpoint-workers`. Both read a bounded snapshot of the Server-Sent-Events stream (container and/or system) and support `source`, `tail`, `since`, and `maxWaitMs`; both return a 501 notice on the v1 API.
59
+ - 466a9d8: Add a `set-endpoint-gpus` tool that sets which GPUs any Serverless endpoint's workers run on — including pinning specific GPU SKUs, which the REST API cannot express (its pool-level `gpuPoolIds` has no exclusion concept). Callers pass either a raw `gpuIds` string or `pools` plus `excludeGpuTypeIds` (GPU type ids from `list-gpu-types`) and the exclusion string is built automatically; excluding all but one SKU in a pool pins that SKU exactly. Because the GraphQL `saveEndpoint` mutation resets omitted endpoint-level fields to server defaults (verified live), the tool reads the endpoint's current settings first and echoes every field back with only the GPU selection changed — workers, scaling, timeouts, FlashBoot, locations, network volumes, and the template are all preserved.
60
+ - d1f3719: create-pod template deploy improvements. Added a `containerRegistryAuthId` param to `create-pod` (private-image registry credential, a valid v2 ContainerConfig field). When deploying from a `templateId`, the template's registry credential is inherited as a default so a private-image template pulls correctly, `containerRegistryAuthId` overrides it, and passing an empty string opts out entirely (emits `registry: null`, which v2 accepts, clearing the template's credential). The template is fetched from v2 explicitly, so a split per-resource version override (e.g. templates pinned to v1) can't yield a v1-shaped template merged into a v2 pod body. Tool descriptions now state that template fields are applied whole-field as defaults (an explicitly-passed field replaces the template value rather than merging).
61
+ - 0b49b91: Fix two template gaps from the same ticket (API-385 / E-3717).
62
+ 1. `create-template`/`update-template` were dropping the container start command on the v2 REST API. `dockerStartCmd` is now mapped to the v2 template's `args` field (a single string; a multi-element array is space-joined) instead of being discarded, so a template's startup command is persisted. `update-template` accepts `dockerStartCmd` now too. (v2 has no separate entrypoint field, so `dockerEntrypoint` is still not persisted on v2 — documented in the tool.)
63
+ 2. `create-pod` can now deploy from a template. v2 `CreatePodRequest` has no `templateId`, so `create-pod` accepts a `templateId`, fetches the template, and spreads its container config (image, start command, ports, env, disk, volume, registry credential) into the pod body as defaults — any field you also pass explicitly overrides the template. `imageName` is now optional when `templateId` is given. Template-based deploy requires the v2 REST API and (for now) a GPU pod; clear errors are returned for v1, CPU, or when neither image nor template is supplied.
64
+
65
+ ### Patch Changes
66
+
67
+ - e50409d: URL-encode the resource ids interpolated into the log-streaming request paths (`stream-pod-logs`, `stream-worker-logs`) so an id containing a URL-special character can't corrupt the request path. Opaque Runpod ids are unaffected in practice; this is defensive hardening.
68
+ - fb76883: Surface the configured `RUNPOD_REST_VERSION` in the MCP `serverInfo` version, e.g. `2.0.0 [RUNPOD_REST_VERSION=v2]` (or `RUNPOD_REST_VERSION unset (default v2)`). A plain `initialize` handshake now reveals whether a deployment is running v1 or v2 without inspecting the environment, and flags any per-resource overrides.
69
+
3
70
  ## 2.0.0
4
71
 
5
72
  ### Major Changes
@@ -65,7 +132,7 @@
65
132
 
66
133
  ### Minor Changes
67
134
 
68
- - 3e6dedf: Add HTTP transport for hosted MCP server deployments. The package now exports three entrypoints: `@runpod/mcp-server` (stdio, unchanged), `@runpod/mcp-server/http` (streamable HTTP with per-request auth), and `@runpod/mcp-server/tools` (shared tool definitions). This enables deploying the MCP server on Vercel or any HTTP-capable host where each request carries its own RunPod API key via the Authorization header. Existing stdio users are unaffected.
135
+ - 3e6dedf: Add HTTP transport for hosted MCP server deployments. The package now exports three entrypoints: `@runpod/mcp-server` (stdio, unchanged), `@runpod/mcp-server/http` (streamable HTTP with per-request auth), and `@runpod/mcp-server/tools` (shared tool definitions). This enables deploying the MCP server on Vercel or any HTTP-capable host where each request carries its own Runpod API key via the Authorization header. Existing stdio users are unaffected.
69
136
  - 3e6dedf: Add a "Sign in with Runpod" OAuth flow to the hosted HTTP server so it can act as the authorization server for Claude's MCP connector. When `MCP_OAUTH_ENABLED=true`, the server advertises itself in the OAuth discovery metadata and exposes `GET /authorize` and `POST /token`. The flow reuses the Runpod flash auth backend: `/authorize` creates a guest `createFlashAuthRequest` and hands off to the Runpod console for approval, and `/token` polls `flashAuthRequestStatus` and returns the minted Runpod API key as the access token, which the server then forwards to the Runpod API. The backend endpoint and console base URL are configurable via `RUNPOD_GRAPHQL_URL` and `CONSOLE_BASE_URL`.
70
137
  - 3e6dedf: Complete the hosted OAuth flow for real MCP clients. Add an OAuth 2.0 Dynamic Client Registration endpoint (`POST /register`, RFC 7591) and advertise it as `registration_endpoint`, so clients like Claude can register before signing in. Make the Runpod REST and Serverless API hosts configurable via `RUNPOD_REST_API_URL` and `RUNPOD_SERVERLESS_API_URL` so a deployment authenticating with non-production keys can target the matching environment. The console handoff path is `/integrations/mcp/login`.
71
138
  - 3e6dedf: Validate the OAuth `redirect_uri` against an allowlist. Because the authorization code redeems into a real Runpod API key, `/authorize` and `/token` now reject any `redirect_uri` that is not an allowlisted client callback, preventing an attacker from crafting an authorize link that delivers the code (and key) to a host they control. Loopback addresses (`http://localhost` / `127.0.0.1` / `::1`, any port) are always allowed per RFC 8252; the hosted client callbacks default to Claude's, and the list is extendable via `MCP_ALLOWED_REDIRECT_URIS` (comma-separated).
@@ -102,4 +169,4 @@
102
169
 
103
170
  ### Minor Changes
104
171
 
105
- - c49b2cc: Add npx support for running the MCP server directly without installation. Includes bin field in package.json, shebang in build output, and updated README with npx command. Also fixes branding consistency (RunPod -> Runpod) and adds development conventions documentation.
172
+ - c49b2cc: Add npx support for running the MCP server directly without installation. Includes bin field in package.json, shebang in build output, and updated README with npx command. Also fixes branding consistency (Runpod -> Runpod) and adds development conventions documentation.
package/README.md CHANGED
@@ -200,7 +200,9 @@ The token can be either:
200
200
  - a Runpod API key the caller configured manually, or
201
201
  - a Runpod API key obtained through the OAuth "Sign in with Runpod" flow (see below).
202
202
 
203
- An unauthenticated request receives a `401` with a `WWW-Authenticate` header pointing at the protected-resource metadata, which tells an OAuth-capable client (such as Claude) to start the sign-in flow. A caller that brings its own API key as the bearer token never hits that path.
203
+ An unauthenticated request receives a `401` with a `WWW-Authenticate` header pointing at the protected-resource metadata, which tells an OAuth-capable client (such as Claude) to start the sign-in flow.
204
+
205
+ A request whose bearer token is _rejected_ — a revoked key, whether OAuth-minted or supplied by the caller — gets the same `401`, plus `error="invalid_token"` to distinguish it from having sent no credential at all. The token is verified once and the verdict cached briefly. If a credential dies mid-session while a "valid" verdict is still cached, the first call afterward can still surface the upstream 401 as a `200` tool error; that call drops the cached verdict, so the next request re-checks and surfaces a real HTTP `401` that triggers re-authentication. Set `MCP_SKIP_CREDENTIAL_CHECK=true` to disable that verification; it also self-disables when the REST/Serverless hosts and the auth GraphQL host are pointed at different environments, since it would otherwise validate the key against the wrong backend.
204
206
 
205
207
  ### Sign in with Runpod (authorize flow)
206
208
 
@@ -381,17 +383,25 @@ The server can target either the v1 REST API (`rest.runpod.io/v1`) or the newer
381
383
  | `RUNPOD_REST_API_URL` | URL | `https://rest.runpod.io/v1` | v1 base URL. |
382
384
  | `RUNPOD_SERVERLESS_API_URL` | URL | `https://api.runpod.ai/v2` | Serverless runtime base URL. |
383
385
 
386
+ > **⚠️ Migration note — `create-endpoint` / `update-endpoint` changed shape in v2.** Serverless endpoints now use the v2 API (`/v2/serverless`) with an **inline config** instead of a `templateId`. On v2, `create-endpoint` requires `imageName` + `gpuPoolIds` (GPU **pool** names from `list-gpu-types` — the `pool` field, e.g. `AMPERE_80`), plus optional `workersMin`/`workersMax`, `scalerType`/`scalerValue`/`idleTimeout`, `containerDiskInGb`, `env`, `flashboot`, etc. It no longer accepts `templateId`. **If you previously called `create-endpoint` with `{ templateId }` and have no `RUNPOD_REST_VERSION` set, that call now returns a clean `400` after upgrading** — switch to the inline fields, or pin `RUNPOD_REST_VERSION=v1` to keep the legacy template-based model.
387
+
384
388
  Notes:
385
389
 
386
390
  - **Default is v2** — with nothing set, every control-plane resource uses the v2 REST API. Set `RUNPOD_REST_VERSION=v1` to pin the previous v1 behavior.
387
391
  - **Pinning a deployment** — the env var is read once at startup, so set `RUNPOD_REST_VERSION` (`v1` or `v2`) in the host's environment (e.g. a Vercel project env var) and redeploy; no code change. Hosted HTTP honors this default like any other transport.
388
392
  - **What `auto` does:** it probes v2 once at startup and falls back to v1 — but **only on the `stdio` transport** (one process = one key). On hosted HTTP `auto` resolves to **v1**, because a warm instance serves many users and a cached probe verdict could leak across them. `auto` is a stdio-only convenience; on HTTP, rely on the default or pin the version explicitly.
389
393
  - `jobs` (serverless runtime) always uses v1 regardless of the setting — it has no v2 REST home (it targets `api.runpod.ai/v2`, a different service).
390
- - The v2-only tools (`list-cpu-types`, `get-gpu-type`, `restart-pod`) return a clear "v2 only" notice when called under v1.
394
+ - The v2-only tools (`list-cpu-types`, `get-gpu-type`, `restart-pod`, and the three `*-registry-delegation(s)` tools) return a clear "v2 only" notice when called under v1.
395
+ - `create-pod` for a **CPU pod** (`computeType: "CPU"`) on v2 is transparently served by the **v1** API — v2 has no CPU pods yet — and the reply is flagged `_servedBy: "v1"`. Because that fallback hits the **v1 base**, set `RUNPOD_REST_API_URL` to match your environment when running v2 against a non-prod host (otherwise CPU creates land on v1 **prod**). On v2 a create with neither `gpuTypeIds` nor `computeType` is rejected — absence is never silently turned into a CPU pod.
391
396
 
392
- > **⚠️ Migration note — `create-endpoint` / `update-endpoint` changed shape in v2.** Serverless endpoints now use the v2 API (`/v2/serverless`) with an **inline config** instead of a `templateId`. On v2, `create-endpoint` requires `imageName` + `gpuPoolIds` (GPU **pool** names from `list-gpu-types` — the `pool` field, e.g. `AMPERE_80`), plus optional `workersMin`/`workersMax`, `scalerType`/`scalerValue`/`idleTimeout`, `containerDiskInGb`, `env`, `flashboot`, etc. It no longer accepts `templateId`. **If you previously called `create-endpoint` with `{ templateId }` and have no `RUNPOD_REST_VERSION` set, that call now returns a clean `400` after upgrading** — switch to the inline fields, or pin `RUNPOD_REST_VERSION=v1` to keep the legacy template-based model.
397
+ ### Private image pull: credentials vs ECR delegation
393
398
 
394
- - `create-pod` for a **CPU pod** (`computeType: "CPU"`) on v2 is transparently served by the **v1** API — v2 has no CPU pods yet — and the reply is flagged `_servedBy: "v1"`. Because that fallback hits the **v1 base**, set `RUNPOD_REST_API_URL` to match your environment when running v2 against a non-prod host (otherwise CPU creates land on v1 **prod**). On v2 a create with neither `gpuTypeIds` nor `computeType` is rejected — absence is never silently turned into a CPU pod.
399
+ Two ways to let Runpod pull a private image, and they are not interchangeable:
400
+
401
+ - **`create-container-registry-auth`** — stores a username + password/token. Works for any registry (Docker Hub, GHCR, Quay, a self-hosted one). Available on v1 and v2. Reference the resulting id from `create-pod` / `create-endpoint` via `containerRegistryAuthId`.
402
+ - **`create-registry-delegation`** — **AWS ECR only, v2-only, no credentials stored.** You register an ECR repository ARN and Runpod is granted scoped pull access to it; the reply carries a `dockerRegistryUri` plus the resolved repository, tag, and AWS region. Manage with `list-registry-delegations` and revoke with `delete-registry-delegation`.
403
+
404
+ Prefer the delegation for ECR — nothing long-lived is stored on Runpod's side.
395
405
 
396
406
  Example (stdio client config) — v2 against prod:
397
407