Skip to content

chore: version packages - #80

Merged
justinwlin merged 1 commit into
mainfrom
changeset-release/main
Aug 17, 2026
Merged

chore: version packages#80
justinwlin merged 1 commit into
mainfrom
changeset-release/main

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@runpod/mcp-server@3.3.0

Minor Changes

  • a62a663: list-gpu-types and get-gpu-type now always send product with their include=AVAILABILITY catalog requests, via a new optional product parameter (POD | CLUSTER | SERVERLESS, default POD). Availability is product-specific — the same GPU can be scarce for Pods and plentiful for Serverless — and the next v2 API release makes product required with availability (400 without it), so this keeps the availability lookups working and lets agents ask for the context they actually deploy to: pass SERVERLESS when picking a GPU for an endpoint, CLUSTER for Instant Clusters. When includeAvailability is false, product is not sent (the API also rejects product without include=AVAILABILITY). The CPU catalog tools never request availability, so they are unaffected.

  • 7074c87: Add an sshPublicKey parameter to create-pod for full SSH access (create-pod should expose SSH terminal access and inject account public keys #73), and fix v2 GPU pod creation failing without an explicit gpuCount.

    The REST API has no SSH switch yet (the console and runpodctl set it via GraphQL startSsh), so Pods created through the MCP got a valid-looking port-22 mapping but no authorized key — direct SSH, SCP, SFTP, and rsync all failed. Passing sshPublicKey now merges the key into the PUBLIC_KEY environment variable and ensures 22/tcp is exposed, which any image honoring the PUBLIC_KEY convention (all runpod/* official images) turns into a running sshd with the key installed. Template deploys extend the template's ports and env rather than replacing them, and an existing PUBLIC_KEY is appended to rather than overwritten.

    The key is validated before the Pod is created, because an unusable value would otherwise produce a Pod that looks SSH-ready and is not: 22/tcp exposed, junk in PUBLIC_KEY, and a reply reporting SSH as configured. Rejected with a 400: a private key in any format (PEM in any case, or a PuTTY .ppk, which contains neither the words "private key" nor a public key line), a file path or SHA256 fingerprint passed instead of the .pub file's contents, a bare base64 blob with no key type, and an empty or whitespace-only value (omit the parameter to create a Pod without SSH). Accepted keys are normalized to one key per line with CRLF stripped, since a stray carriage return corrupts the authorized_keys entry it lands on. Error messages never echo the rejected value, which may itself be secret.

    create-pod also now rejects a gpuCount below 1 or with a fractional part, which the v2 API answers with an opaque 422.

    Separately, the v2 pod-create mapper omitted gpu.count when the caller passed no gpuCount, trusting the spec's documented server-side default of 1. In practice the scheduler matches zero machines without it and every create fails with a misleading "no instances available" error (verified live against both v2 hosts). The mapper now always emits count, defaulting to 1.

  • 9176c78: Clamp long-poll tool budgets on the hosted HTTP server, which runs behind a 60-second Vercel function limit. runsync-endpoint waited 90 seconds by default (300 via wait) and stream-job polled for up to 5 minutes, so for a slow job the function was reaped mid-flight: the caller got a bare 504 and every chunk collected so far was discarded.

    Over HTTP, runsync-endpoint now sends a wait capped at 45000 ms — a job that outlives it comes back as a job ID plus a non-terminal status to poll with get-job-status. stream-job stops after 45 seconds over HTTP and returns what it has with pollingTimedOut: true; /stream drains what it hands out, so calling again resumes rather than replaying. Its HTTP polls also send wait=1000, since the server otherwise holds an empty response for 10 seconds and the budget is only checked between polls. Both tool descriptions are now written per transport, so a caller is told the one budget that applies to them and where to go for more — run-endpoint + get-job-status, or the runtime API directly. The stdio server has no deadline and is unchanged.

  • f20d987: Give every outbound request a client-side deadline — every tool call, the OAuth handshake on the hosted server, and the install wizard's key check. node-fetch applies no timeout of its own, so a Runpod host that accepted the connection and then went silent — a wedged worker, a load balancer holding the socket — left a tool call pending forever, and on the hosted server that ended as a bare 504 when Vercel reaped the function at its 60s limit.

    Requests now abort after 30 seconds with a named RequestTimeoutError naming the API that went quiet, the deadline it was given, and what to do next. runsync-endpoint is the one call that legitimately asks the server to hold a connection open, so it derives its deadline from the wait it requested (the server's own 90-second default when wait is omitted) rather than being truncated. Successful tool output is unchanged.

    On the hosted transport the cap is a single budget for the whole invocation rather than a fresh allowance per request, because several tools make more than one call — get-job-status adds a queued-job diagnosis, deploy-hub-repo and set-endpoint-gpus read before they write, update-endpoint reads the current scaler before patching it — and two full deadlines back to back outlived the platform even with each one bounded. Each request is clamped to what is left, so a stall anywhere in a handler surfaces as the named error instead of a 504. The queued-job diagnosis, which only decorates a status that is already in hand, is bounded at 5 seconds so it cannot spend a budget the reply itself needs.

    stream-job bounds each poll by the wait the server was asked to hold rather than by its whole budget. A deadline set to the budget meant one wedged socket consumed the entire run in a single attempt — 45 seconds on the hosted server, five minutes on stdio — so the loop's retry path never ran and a stall returned nothing at all. Each poll now gets the hold plus a round trip, which both clears a reply already in flight and leaves the budget room to reconnect; a run that ends on the budget reports pollingTimedOut with the last error rather than discarding it.

    The hosted OAuth routes are bounded too. /token polls the flash backend for an approval, and one silent socket there hung the whole handshake until the platform reaped it — the worst place for a blank error, since the user has no credential yet to retry with. Each backend call now has its own 10-second deadline (override with MCP_FLASH_TIMEOUT_MS), never exceeding what is left of a 45-second poll budget, and a stall is reported as a named error naming the operation, the host and the deadline. The runpod-mcp install wizard's key verification gets the same treatment, so it can no longer sit at "Verifying…" indefinitely.

    A timed-out GraphQL read is also no longer described as a possible write. The advice keys off the HTTP method and GraphQL is always POST on the wire, so list-gpu-types timing out used to tell the agent the call may have landed and to "check with the matching list-/get- tool first" — which is the tool that just failed. Only actual mutations carry that warning now.

Patch Changes

  • 2b6480d: Refuse the standalone GET SSE stream with 405. The HTTP server is stateless and never sends server-initiated messages, so the GET "listen" stream has nothing to carry — but the SDK accepted it anyway and held the response open with nothing to send until the platform's maxDuration killed it, at which point every connected client immediately re-opened it. On the hosted deployment that loop was ~1.1M hung requests per day (90% of all traffic), each ending in a 60-second timeout, and it dominated the serverless bill. GET now gets the spec's answer for a server without an SSE stream: 405 Method Not Allowed with an Allow: POST, DELETE header and a JSON-RPC error body, before any auth work — a 401 there would send OAuth clients into a pointless re-auth flow. Clients per the MCP spec treat the 405 as "no server-initiated messages offered" and continue POST-only; tool calls are unaffected.

  • 674fa2c: Fix Claude Code detection and registration in the install wizard on Windows (Bug: Install wizard never detects Claude Code on Windows, even when it's installed and working #56).

    Detection used the POSIX-only command -v claude plus three POSIX install paths, so
    Claude Code was undetectable on a standard Windows install. Windows now probes
    %USERPROFILE%\.local\bin\claude.exe (native installer) and %APPDATA%\npm\claude.cmd
    (global npm), and registration resolves the real entrypoint from the installed package
    manifest instead of running the .cmd shim through cmd.exe. Candidates are
    canonicalized and confined to their standard install roots, a PATH result resolving into
    the current project is rejected, and Claude Code is spawned with PATH, COMSPEC,
    PATHEXT, NODE_OPTIONS, and NODE_PATH sanitized, so nothing a project directory can
    plant receives your API key. Custom npm prefixes and redirected roaming profiles are
    still not auto-detected; when Claude Code is not found, the wizard now prints the exact
    claude mcp command to register or remove the entry yourself.

    Claude Code add and remove are now verified against its own .claude.json rather than
    the CLI exit code, which reports success for writes it never made. Re-running the wizard
    over an existing entry now says the entry was left unchanged instead of printing a bare
    success, since the CLI does not update it and a rotated API key would otherwise look
    applied. An entry shadowed by a local-scope one is called out with the command to clear
    it.

    For the clients that own a JSON config, an edit that would leave the file unparseable —
    or land in a duplicated server block the client does not read — now fails with the
    original file untouched instead of reporting success, and validity is judged by the
    parser each client actually ships (Claude Desktop is strict JSON, Cursor and VS Code
    accept JSONC, Windsurf's tolerance is unverified and disclosed rather than assumed).
    Claude Desktop on Linux and VS Code now honour XDG_CONFIG_HOME instead of writing
    where those clients never read, an empty or relative APPDATA/XDG_CONFIG_HOME no
    longer resolves a config path against the current directory, configs this wizard creates
    are 0600 because they hold a plaintext API key, and a key taken from the environment is
    trimmed.

@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
runpod-mcp Ignored Ignored Preview Aug 14, 2026 10:57pm

Request Review

@github-actions
github-actions Bot force-pushed the changeset-release/main branch 3 times, most recently from da8d1d2 to 9a64ea7 Compare August 7, 2026 19:30
@justinwlin
justinwlin self-requested a review August 7, 2026 19:30
@github-actions
github-actions Bot force-pushed the changeset-release/main branch from 9a64ea7 to 6f965da Compare August 10, 2026 14:28
@github-actions
github-actions Bot force-pushed the changeset-release/main branch from 6f965da to ea0acfe Compare August 14, 2026 22:57
@justinwlin
justinwlin merged commit 4658724 into main Aug 17, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant