@runpod/mcp-server 1.2.0 → 1.3.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,34 @@
1
1
  # @runpod/mcp-server
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 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.
8
+ - 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`.
9
+ - 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`.
10
+ - 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).
11
+ - 3e6dedf: Address hosted OAuth review feedback:
12
+ - Remove the `MCP_OAUTH_ENABLED` flag — the hosted HTTP server always advertises the OAuth sign-in challenge. Callers that bring their own Runpod API key as the bearer token are unaffected (they never hit the challenge).
13
+ - Default `CONSOLE_BASE_URL` to the production console (`https://console.runpod.io`) so a deploy that forgets to set it doesn't silently redirect users to localhost.
14
+ - `/token` now polls for most of the function budget (~45s) instead of returning a non-retryable `authorization_pending` after ~10s, avoiding a dead-end when console→backend approval propagation lags.
15
+ - Caller-tracking falls back to the inbound HTTP `User-Agent` on the stateless HTTP transport, where the per-request server never sees the MCP `initialize` `clientInfo` (previously reported `client=unknown`).
16
+ - Stop advertising PKCE (`S256`) since it isn't enforced server-side; security rests on the `redirect_uri` allowlist and the single-use flash approval.
17
+ - Default the minted key name to `runpod-mcp` (`RUNPOD_API_KEY_NAME`), so hosted keys are identifiable/revocable. Set `RUNPOD_API_KEY_NAME=""` to suppress it for a backend that has not shipped the `apiKeyName` argument.
18
+
19
+ - 3e6dedf: Simplify hosted HTTP auth to pure token passthrough. The server now forwards the caller's Bearer token directly to the Runpod API and holds no credential of its own — there is no shared server-side key and no token translation. When `MCP_OAUTH_ENABLED=true`, unauthenticated requests return a `WWW-Authenticate` challenge so OAuth-capable clients start the "Sign in with Runpod" flow. Removes the `RUNPOD_HTTP_SHARED_API_KEY` environment variable and the `jose` dependency.
20
+
21
+ ### Patch Changes
22
+
23
+ - 3e6dedf: Support naming the minted Runpod API key in the OAuth flow. Set `RUNPOD_API_KEY_NAME` (e.g. `runpod-mcp`) to pass `apiKeyName` through to the flash backend's `createFlashAuthRequest`, so the key shows up under that name in the user's dashboard. It is omitted by default for compatibility with backends that don't yet support the argument.
24
+ - 3e6dedf: Address low-severity review nits on the hosted HTTP / OAuth path:
25
+ - Make the public discovery GraphQL host configurable via `RUNPOD_PUBLIC_GRAPHQL_URL` (was hardcoded to prod, so a dev deploy hit prod GraphQL).
26
+ - Report the real package version in the hosted server's outbound `User-Agent` (read from package.json at runtime, since tsup's build-time define doesn't run when Vercel compiles `api/index.ts`).
27
+ - Gate OAuth request-id / handoff-URL logging behind `MCP_VERBOSE_LOGS` (request ids are live, single-use auth codes).
28
+ - `stream-job` now surfaces the most recent polling error in its timeout payload instead of discarding it.
29
+ - Pass the parsed request body to the MCP transport explicitly; dispose the per-request server/transport on response close; guard `getBaseUrl` against a missing `Host`; handle a `Buffer` request body in the token endpoint.
30
+ - Single CORS source of truth (handler, not `vercel.json`); stop serving the whole repo as static assets; add an `oauth-e2e` npm script and `engines.pnpm`; fix docs that misattributed the OAuth routes and listed the new env vars.
31
+
3
32
  ## 1.2.0
4
33
 
5
34
  ### Minor Changes
package/README.md CHANGED
@@ -1,46 +1,48 @@
1
1
  # Runpod MCP server
2
+
2
3
  [![smithery badge](https://smithery.ai/badge/@runpod/runpod-mcp-ts)](https://smithery.ai/server/@runpod/runpod-mcp-ts)
3
4
 
4
- This Model Context Protocol (MCP) server lets you manage Runpod infrastructure through any MCP-compatible client. It provides tools for working with Pods, Serverless endpoints, templates, network volumes, and container registry authentications.
5
+ This is the official Runpod Model Context Protocol (MCP) server, published to npm as `@runpod/mcp-server`.
5
6
 
6
- ## Quick start
7
+ It supports two deployment modes:
8
+
9
+ - Local `stdio` for Claude Desktop, Claude Code, Cursor, VS Code, and other MCP clients that launch a local process. The caller sets `RUNPOD_API_KEY` in the environment.
10
+ - Hosted Streamable HTTP for Vercel or other HTTP-capable platforms. Each request carries its own `Authorization: Bearer <token>`, which the server forwards directly to the Runpod API. The token can be a Runpod API key or one obtained through the OAuth "Sign in with Runpod" flow.
7
11
 
8
- ### Requirements
12
+ The server never holds a credential of its own and never shares one across users.
13
+
14
+ ## Requirements
9
15
 
10
16
  - Node.js 18 or higher.
11
- - A Runpod account and API key ([get your API key](https://www.runpod.io/console/user/settings)).
17
+ - A Runpod account and API key: https://www.runpod.io/console/user/settings
12
18
 
13
- ### Running with npx
19
+ ## Quick start
14
20
 
15
- You can run the server directly without installation:
21
+ ### Run locally with `npx`
16
22
 
17
23
  ```bash
18
- RUNPOD_API_KEY=YOUR_API_KEY npx @runpod/mcp-server@latest
24
+ RUNPOD_API_KEY=YOUR_API_KEY npx -y @runpod/mcp-server@latest
19
25
  ```
20
26
 
21
- ### Installing via Smithery
22
-
23
- To install for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@runpod/runpod-mcp-ts):
27
+ ### Install via Smithery
24
28
 
25
29
  ```bash
26
30
  npx -y @smithery/cli install @runpod/runpod-mcp-ts --client claude
27
31
  ```
28
32
 
29
- ## Setting up with your client
33
+ ## Local MCP clients
30
34
 
31
- Most MCP clients use a JSON configuration file with the same general structure. The examples below show the npx approach (recommended for most users) and the local build approach (for development). Replace `YOUR_API_KEY` with your actual Runpod API key.
35
+ Local MCP clients should use the default package entrypoint, which is the `stdio` server.
32
36
 
33
37
  ### Claude Code
34
38
 
35
- Add the MCP server globally so it's available across all your projects:
36
-
37
39
  ```bash
38
40
  claude mcp add runpod -s user \
39
41
  -e RUNPOD_API_KEY=YOUR_API_KEY \
40
42
  -- npx -y @runpod/mcp-server@latest
41
43
  ```
42
44
 
43
- Or add it to a specific project (creates a `.mcp.json` file you can commit):
45
+ For a project-local server:
44
46
 
45
47
  ```bash
46
48
  claude mcp add runpod -s project \
@@ -48,11 +50,19 @@ claude mcp add runpod -s project \
48
50
  -- npx -y @runpod/mcp-server@latest
49
51
  ```
50
52
 
51
- Verify the server is connected with `claude mcp list`. If you're in an active session, type `/mcp` to reconnect without restarting.
53
+ Verify with `claude mcp list`. In an active session, use `/mcp` to reconnect.
52
54
 
53
55
  ### Claude Desktop
54
56
 
55
- Edit the config file at `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
57
+ Local MCP servers in Claude Desktop still use `claude_desktop_config.json`.
58
+
59
+ macOS:
60
+
61
+ `~/Library/Application Support/Claude/claude_desktop_config.json`
62
+
63
+ Windows:
64
+
65
+ `%APPDATA%\\Claude\\claude_desktop_config.json`
56
66
 
57
67
  ```json
58
68
  {
@@ -72,7 +82,7 @@ Restart Claude Desktop after saving.
72
82
 
73
83
  ### Cursor
74
84
 
75
- Add the following to `.cursor/mcp.json` in your project directory, or `~/.cursor/mcp.json` for global access:
85
+ Add this to `.cursor/mcp.json` in your project or `~/.cursor/mcp.json` globally:
76
86
 
77
87
  ```json
78
88
  {
@@ -88,73 +98,105 @@ Add the following to `.cursor/mcp.json` in your project directory, or `~/.cursor
88
98
  }
89
99
  ```
90
100
 
91
- ### Windsurf
101
+ ### VS Code, Windsurf, Cline, JetBrains, and other local clients
92
102
 
93
- Open Windsurf settings (Cmd+Shift+P → "Open Windsurf Settings"), navigate to the Cascade section, and enable MCP. Then edit `~/.codeium/windsurf/mcp_config.json`:
103
+ Use the same pattern:
94
104
 
95
- ```json
96
- {
97
- "mcpServers": {
98
- "runpod": {
99
- "command": "npx",
100
- "args": ["-y", "@runpod/mcp-server@latest"],
101
- "env": {
102
- "RUNPOD_API_KEY": "YOUR_API_KEY"
103
- }
104
- }
105
- }
106
- }
107
- ```
105
+ - command: `npx`
106
+ - args: `["-y", "@runpod/mcp-server@latest"]`
107
+ - env: `RUNPOD_API_KEY=YOUR_API_KEY`
108
108
 
109
- ### VS Code (GitHub Copilot)
109
+ For a broader list of MCP clients, see https://modelcontextprotocol.io/clients
110
110
 
111
- MCP works in VS Code's agent mode (requires VS Code 1.101+). Add the following to `.vscode/mcp.json` in your workspace:
111
+ ## Hosted HTTP deployment
112
112
 
113
- ```json
114
- {
115
- "mcpServers": {
116
- "runpod": {
117
- "command": "npx",
118
- "args": ["-y", "@runpod/mcp-server@latest"],
119
- "env": {
120
- "RUNPOD_API_KEY": "YOUR_API_KEY"
121
- }
122
- }
123
- }
124
- }
125
- ```
113
+ The package also exports a Streamable HTTP entrypoint at `@runpod/mcp-server/http`, and this repo includes a Vercel function in `api/index.ts`.
126
114
 
127
- Click the "Start" button next to the server entry to connect.
115
+ The hosted transport is stateless:
128
116
 
129
- ### Cline
117
+ - Each request creates a fresh MCP server instance.
118
+ - The caller must send `Authorization: Bearer <token>`.
119
+ - No credentials are cached or shared server-side.
130
120
 
131
- Open Cline in VS Code, click the hamburger menu (☰), and go to MCP Servers. You can add servers through the marketplace or manually configure in Cline's settings using the same JSON structure shown above.
121
+ ### Hosted auth
132
122
 
133
- ### JetBrains IDEs
123
+ The server forwards the caller's Bearer token directly to the Runpod API as the credential for that request. There is no server-side or shared key.
134
124
 
135
- Create a `mcp.json` file at `~/.junie/mcp.json` (global) or `.junie/mcp/` in your project:
125
+ The token can be either:
136
126
 
137
- ```json
138
- {
139
- "mcpServers": {
140
- "runpod": {
141
- "command": "npx",
142
- "args": ["-y", "@runpod/mcp-server@latest"],
143
- "env": {
144
- "RUNPOD_API_KEY": "YOUR_API_KEY"
145
- }
146
- }
147
- }
148
- }
127
+ - a Runpod API key the caller configured manually, or
128
+ - a Runpod API key obtained through the OAuth "Sign in with Runpod" flow (see below).
129
+
130
+ 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.
131
+
132
+ ### Sign in with Runpod (authorize flow)
133
+
134
+ In OAuth mode the hosted server is itself the authorization server for Claude's connector. It advertises itself in `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`, pointing `authorization_endpoint` at its own `GET /authorize`, `token_endpoint` at its own `POST /token`, and `registration_endpoint` at its own `POST /register`.
135
+
136
+ The flow reuses the Runpod flash auth backend and mints a real Runpod API key:
137
+
138
+ 1. The client registers via `POST /register` (OAuth Dynamic Client Registration, RFC 7591) to obtain a `client_id`. This is a public client; no client secret is used.
139
+ 2. `GET /authorize` calls the guest `createFlashAuthRequest` mutation to get a request id (which serves as the OAuth authorization code), then `302`-redirects the browser to the console handoff page (`/integrations/mcp/login`), carrying the request id plus the client's `redirect_uri` and `state`.
140
+ 3. The user logs in and approves the request in the console. On approval the backend mints a Runpod API key for the request.
141
+ 4. The console returns the browser to the client's `redirect_uri` with `code=<request id>`.
142
+ 5. `POST /token` polls the guest `flashAuthRequestStatus` query for that id; once `APPROVED`, it returns the minted `apiKey` as the `access_token`. The client then sends that key as its bearer token on every MCP request, and the server forwards it to the Runpod API.
143
+
144
+ This flow uses these environment variables:
145
+
146
+ - `RUNPOD_GRAPHQL_URL`: flash auth backend endpoint (default `https://api.runpod.io/graphql`).
147
+ - `CONSOLE_BASE_URL`: base URL of the console that hosts the handoff login page (default `https://console.runpod.io`).
148
+ - `RUNPOD_REST_API_URL` / `RUNPOD_SERVERLESS_API_URL`: override the REST and Serverless API hosts so a deployment authenticating with non-production keys can target the matching environment.
149
+ - `RUNPOD_API_KEY_NAME`: name for the minted key as shown in the user's dashboard. Defaults to `runpod-mcp`. Set it to `""` to omit the name for a backend that does not support the `apiKeyName` argument (such backends reject the request when it is sent).
150
+ - `MCP_ALLOWED_REDIRECT_URIS`: comma-separated extra `redirect_uri` values to allow, in addition to the built-in Claude callbacks. Loopback addresses (`localhost`/`127.0.0.1`/`::1`, any port) are always allowed. `/authorize` and `/token` reject any `redirect_uri` not on this list, since the authorization code redeems into a real API key.
151
+
152
+ You can verify the entire flow end to end with `MCP_SERVER_URL=<deployment-url> npx tsx scripts/oauth-e2e.ts` (the harness uses a loopback callback).
153
+
154
+ Notes and current limitations:
155
+
156
+ - PKCE is not supported: `code_challenge` is neither advertised nor enforced (the flash flow has no place to bind it). Security relies on the `redirect_uri` allowlist and the single-use, short-lived flash approval.
157
+ - The minted key is named via `RUNPOD_API_KEY_NAME` (default `runpod-mcp`), which requires a flash backend that supports the `apiKeyName` argument. Against a backend without it, set `RUNPOD_API_KEY_NAME=""`.
158
+
159
+ ### Vercel
160
+
161
+ This repo already contains `vercel.json` and the Vercel handler.
162
+
163
+ Deploy with the Vercel CLI:
164
+
165
+ ```bash
166
+ vercel
167
+ vercel --prod
168
+ ```
169
+
170
+ After deploy, test the endpoint:
171
+
172
+ ```bash
173
+ curl -i https://YOUR-DEPLOYMENT.vercel.app/
174
+ ```
175
+
176
+ Unauthenticated requests should return `401`.
177
+
178
+ ### Hosted smoke test
179
+
180
+ ```bash
181
+ MCP_SERVER_URL=https://YOUR-DEPLOYMENT.vercel.app \
182
+ RUNPOD_API_KEY=YOUR_API_KEY \
183
+ pnpm smoke:http
149
184
  ```
150
185
 
151
- ### Other clients
186
+ This validates:
187
+
188
+ - MCP initialization.
189
+ - `tools/list`.
190
+ - A public GraphQL-backed tool call.
191
+ - An authenticated REST-backed tool call.
192
+
193
+ ## Claude Desktop and hosted MCP
152
194
 
153
- This server uses stdio transport and works with any MCP-compatible client. The configuration pattern is the same across all clients — point the command to `npx` with `@runpod/mcp-server@latest` as the argument, and set `RUNPOD_API_KEY` in the environment. For a full list of MCP clients, see the [official MCP clients page](https://modelcontextprotocol.io/clients).
195
+ Use the local `stdio` integration for Claude Desktop today.
154
196
 
155
- ## Using a local build
197
+ For remote clients such as Claude's connector, deploy the hosted HTTP server. The client then runs the OAuth "Sign in with Runpod" flow, and the resulting Runpod API key is forwarded to the Runpod API on each request.
156
198
 
157
- If you want to run from a local clone of the repo (for development or to test unreleased changes):
199
+ ## Local development
158
200
 
159
201
  ```bash
160
202
  git clone https://github.com/runpod/runpod-mcp.git
@@ -163,28 +205,42 @@ pnpm install
163
205
  pnpm build
164
206
  ```
165
207
 
166
- Then replace the `command` and `args` in any of the configurations above with:
208
+ Run the local build directly:
209
+
210
+ ```bash
211
+ RUNPOD_API_KEY=YOUR_API_KEY node dist/stdio.mjs
212
+ ```
213
+
214
+ Or point a client at your local build:
167
215
 
168
216
  ```json
169
217
  {
170
218
  "command": "node",
171
- "args": ["/absolute/path/to/runpod-mcp/dist/index.mjs"]
219
+ "args": ["/absolute/path/to/runpod-mcp/dist/stdio.mjs"],
220
+ "env": {
221
+ "RUNPOD_API_KEY": "YOUR_API_KEY"
222
+ }
172
223
  }
173
224
  ```
174
225
 
175
- After making changes to the source, re-run `pnpm build` and restart or reconnect the MCP server for changes to take effect.
226
+ ### Local smoke test
227
+
228
+ ```bash
229
+ RUNPOD_API_KEY=YOUR_API_KEY pnpm build
230
+ RUNPOD_API_KEY=YOUR_API_KEY pnpm smoke:stdio
231
+ ```
176
232
 
177
233
  ## Usage examples
178
234
 
179
235
  ### List all Pods
180
236
 
181
- ```
237
+ ```text
182
238
  Can you list all my Runpod Pods?
183
239
  ```
184
240
 
185
241
  ### Create a new Pod
186
242
 
187
- ```
243
+ ```text
188
244
  Create a new Runpod Pod with the following specifications:
189
245
  - Name: test-pod
190
246
  - Image: runpod/pytorch:2.1.0-py3.10-cuda11.8.0-devel-ubuntu22.04
@@ -194,7 +250,7 @@ Create a new Runpod Pod with the following specifications:
194
250
 
195
251
  ### Create a Serverless endpoint
196
252
 
197
- ```
253
+ ```text
198
254
  Create a Runpod Serverless endpoint with the following configuration:
199
255
  - Name: my-endpoint
200
256
  - Template ID: 30zmvf89kd
@@ -204,36 +260,41 @@ Create a Runpod Serverless endpoint with the following configuration:
204
260
 
205
261
  ## Contributing
206
262
 
207
- To get started with local development, clone the repo and build:
263
+ The source is now split by responsibility:
264
+
265
+ - `src/stdio.ts`: local `stdio` entrypoint.
266
+ - `src/http.ts`: bearer-token extraction and the per-request MCP session for the Streamable HTTP transport.
267
+ - `src/tools.ts`: all Runpod MCP tools.
268
+ - `src/server.ts`: shared server metadata and construction.
269
+ - `api/index.ts`: Vercel adapter and the OAuth authorization-server routes (`/.well-known/*`, `/register`, `/authorize`, `/token`).
270
+
271
+ After changes:
208
272
 
209
273
  ```bash
210
- git clone https://github.com/runpod/runpod-mcp.git
211
- cd runpod-mcp
212
- pnpm install
274
+ pnpm type-check
275
+ pnpm lint
213
276
  pnpm build
214
277
  ```
215
278
 
216
- After making changes, rebuild with `pnpm build`. In Claude Code, type `/mcp` to reconnect to the updated server without restarting your session. You can also use `pnpm build:watch` for auto-rebuilding during development.
217
-
218
- All tools live in `src/index.ts`. The server uses two backends: the REST API (`runpodRequest()`) for authenticated CRUD operations, and the GraphQL API (`graphqlRequest()`) for public read-only queries like GPU types and data centers. Follow existing patterns when adding new tools — kebab-case names, Zod schemas with `.describe()`, and JSON stringified responses.
219
-
220
- This project uses [changesets](https://github.com/changesets/changesets) for versioning and npm publishing. Every PR with user-facing changes needs a changeset file at `.changeset/DESCRIPTIVE_NAME.md`:
279
+ For transport validation:
221
280
 
222
- ```markdown
223
- ---
224
- "@runpod/mcp-server": minor
225
- ---
226
-
227
- Description of what changed and why.
281
+ ```bash
282
+ pnpm smoke:stdio
283
+ pnpm smoke:http
228
284
  ```
229
285
 
230
- Use `patch` for bug fixes, `minor` for new tools or features, and `major` for breaking changes. The `.changeset/` directory is gitignored, so use `git add -f` to stage changeset files.
286
+ This project uses [changesets](https://github.com/changesets/changesets) for versioning and npm publishing. Every PR with user-facing changes needs a changeset file at `.changeset/DESCRIPTIVE_NAME.md`.
231
287
 
232
- See [CLAUDE.md](./CLAUDE.md) for the full development guide including architecture details, tool conventions, and known issues.
288
+ See `CLAUDE.md` and `docs/context.md` for contributor guidance.
233
289
 
234
290
  ## Security considerations
235
291
 
236
- This server requires your Runpod API key, which grants full access to your Runpod account. Never share your API key. Be cautious about what operations you perform, and consider setting up a separate API key with limited permissions. Do not use this in a production environment without proper security measures.
292
+ This server acts with the full permissions of the supplied `RUNPOD_API_KEY`.
293
+
294
+ - Never share your API key.
295
+ - Be deliberate about destructive tools.
296
+ - Treat hosted deployments as sensitive infrastructure.
297
+ - Each request authenticates with its own caller-supplied token, which is forwarded to the Runpod API and never persisted server-side.
237
298
 
238
299
  ## License
239
300