From 5076da21a6729b4d288b530f78cc0e16415175d7 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 19 Mar 2026 00:57:44 +0000
Subject: [PATCH 1/2] chore: remove create_tunnel endpoint
---
.stats.yml | 8 +--
api.md | 2 -
packages/mcp-server/manifest.json | 2 +-
packages/mcp-server/package.json | 2 +-
packages/mcp-server/src/code-tool-paths.cts | 4 +-
packages/mcp-server/src/code-tool.ts | 51 +++++++------------
packages/mcp-server/src/http.ts | 46 +----------------
packages/mcp-server/src/instructions.ts | 25 +++++----
packages/mcp-server/src/server.ts | 4 +-
packages/mcp-server/src/types.ts | 1 -
src/index.ts | 4 --
src/resources/benchmark-jobs.ts | 2 +-
src/resources/benchmark-runs.ts | 6 ++-
src/resources/devboxes/devboxes.ts | 43 ----------------
src/resources/devboxes/index.ts | 2 -
src/resources/devboxes/tools.ts | 10 +---
src/resources/index.ts | 2 -
src/sdk/devbox.ts | 25 ---------
tests/api-resources/devboxes/devboxes.test.ts | 15 ------
tests/objects/devbox.test.ts | 22 ++++----
20 files changed, 62 insertions(+), 214 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index a784436d5..ab8cc33c0 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 118
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-132ed160716591bdcd9231c00da8c506d9451a5486b165fc27b2a01d93202082.yml
-openapi_spec_hash: c2b44d9e9cda56e32141a7ea3794bbba
-config_hash: 3bd89c812b96708c461fb98286ebf0b5
+configured_endpoints: 117
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-0568973e19e8af9fa953b2ded109ab2b69e76e90e2b74f33617dbf7092e26274.yml
+openapi_spec_hash: 10ba804ce69510d7985e05c77d0ffcf6
+config_hash: de99cfce88e2d1f02246dc6c2f43bc6c
diff --git a/api.md b/api.md
index 18d40cbe0..e52f4a6e8 100644
--- a/api.md
+++ b/api.md
@@ -114,7 +114,6 @@ Types:
- DevboxSendStdInResult
- DevboxSnapshotListView
- DevboxSnapshotView
-- DevboxTunnelView
- DevboxView
- TunnelView
- DevboxCreateSSHKeyResponse
@@ -131,7 +130,6 @@ Methods:
- client.devboxes.update(id, { ...params }) -> DevboxView
- client.devboxes.list({ ...params }) -> DevboxViewsDevboxesCursorIDPage
- client.devboxes.createSSHKey(id) -> DevboxCreateSSHKeyResponse
-- client.devboxes.createTunnel(id, { ...params }) -> DevboxTunnelView
- client.devboxes.deleteDiskSnapshot(id) -> unknown
- client.devboxes.downloadFile(id, { ...params }) -> Response
- client.devboxes.enableTunnel(id, { ...params }) -> TunnelView
diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json
index 5e6ef95fa..d795441d6 100644
--- a/packages/mcp-server/manifest.json
+++ b/packages/mcp-server/manifest.json
@@ -1,7 +1,7 @@
{
"dxt_version": "0.2",
"name": "@runloop/api-client-mcp",
- "version": "1.11.1",
+ "version": "1.11.0",
"description": "The official MCP Server for the Runloop API",
"author": {
"name": "Runloop",
diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json
index 83f8ea6d2..33877432f 100644
--- a/packages/mcp-server/package.json
+++ b/packages/mcp-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@runloop/api-client-mcp",
- "version": "1.11.1",
+ "version": "1.11.0",
"description": "The official MCP Server for the Runloop API",
"author": "Runloop ",
"types": "dist/index.d.ts",
diff --git a/packages/mcp-server/src/code-tool-paths.cts b/packages/mcp-server/src/code-tool-paths.cts
index 78263e45e..15ce7f555 100644
--- a/packages/mcp-server/src/code-tool-paths.cts
+++ b/packages/mcp-server/src/code-tool-paths.cts
@@ -1,5 +1,3 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-export function getWorkerPath(): string {
- return require.resolve('./code-tool-worker.mjs');
-}
+export const workerPath = require.resolve('./code-tool-worker.mjs');
diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts
index 1529df944..2d81111d6 100644
--- a/packages/mcp-server/src/code-tool.ts
+++ b/packages/mcp-server/src/code-tool.ts
@@ -1,5 +1,10 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+import fs from 'node:fs';
+import path from 'node:path';
+import url from 'node:url';
+import { newDenoHTTPWorker } from '@valtown/deno-http-worker';
+import { workerPath } from './code-tool-paths.cjs';
import {
ContentBlock,
McpRequestContext,
@@ -144,23 +149,19 @@ const remoteStainlessHandler = async ({
const codeModeEndpoint = readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool';
- const localClientEnvs = {
- RUNLOOP_API_KEY: requireValue(
- readEnv('RUNLOOP_API_KEY') ?? client.bearerToken,
- 'set RUNLOOP_API_KEY environment variable or provide bearerToken client option',
- ),
- RUNLOOP_BASE_URL: readEnv('RUNLOOP_BASE_URL') ?? client.baseURL ?? undefined,
- };
- // Merge any upstream client envs from the request header, with upstream values taking precedence.
- const mergedClientEnvs = { ...localClientEnvs, ...reqContext.upstreamClientEnvs };
-
// Setting a Stainless API key authenticates requests to the code tool endpoint.
const res = await fetch(codeModeEndpoint, {
method: 'POST',
headers: {
...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }),
'Content-Type': 'application/json',
- 'x-stainless-mcp-client-envs': JSON.stringify(mergedClientEnvs),
+ 'x-stainless-mcp-client-envs': JSON.stringify({
+ RUNLOOP_API_KEY: requireValue(
+ readEnv('RUNLOOP_API_KEY') ?? client.bearerToken,
+ 'set RUNLOOP_API_KEY environment variable or provide bearerToken client option',
+ ),
+ RUNLOOP_BASE_URL: readEnv('RUNLOOP_BASE_URL') ?? client.baseURL ?? undefined,
+ }),
},
body: JSON.stringify({
project_name: 'runloop',
@@ -203,13 +204,6 @@ const localDenoHandler = async ({
reqContext: McpRequestContext;
args: unknown;
}): Promise => {
- const fs = await import('node:fs');
- const path = await import('node:path');
- const url = await import('node:url');
- const { newDenoHTTPWorker } = await import('@valtown/deno-http-worker');
- const { getWorkerPath } = await import('./code-tool-paths.cjs');
- const workerPath = getWorkerPath();
-
const client = reqContext.client;
const baseURLHostname = new URL(client.baseURL).hostname;
const { code } = args as { code: string };
@@ -271,9 +265,6 @@ const localDenoHandler = async ({
printOutput: true,
spawnOptions: {
cwd: path.dirname(workerPath),
- // Merge any upstream client envs into the Deno subprocess environment,
- // with the upstream env vars taking precedence.
- env: { ...process.env, ...reqContext.upstreamClientEnvs },
},
});
@@ -283,17 +274,13 @@ const localDenoHandler = async ({
reject(new Error(`Worker exited with code ${exitCode}`));
});
- // Strip null/undefined values so that the worker SDK client can fall back to
- // reading from environment variables (including any upstreamClientEnvs).
- const opts: ClientOptions = Object.fromEntries(
- Object.entries({
- baseURL: client.baseURL,
- bearerToken: client.bearerToken,
- defaultHeaders: {
- 'X-Stainless-MCP': 'true',
- },
- }).filter(([_, v]) => v != null),
- ) as ClientOptions;
+ const opts: ClientOptions = {
+ baseURL: client.baseURL,
+ bearerToken: client.bearerToken,
+ defaultHeaders: {
+ 'X-Stainless-MCP': 'true',
+ },
+ };
const req = worker.request(
'http://localhost',
diff --git a/packages/mcp-server/src/http.ts b/packages/mcp-server/src/http.ts
index 0d9cd42ee..ab32531d0 100644
--- a/packages/mcp-server/src/http.ts
+++ b/packages/mcp-server/src/http.ts
@@ -27,56 +27,14 @@ const newServer = async ({
const authOptions = parseClientAuthHeaders(req, false);
- let upstreamClientEnvs: Record | undefined;
- const clientEnvsHeader = req.headers['x-stainless-mcp-client-envs'];
- if (typeof clientEnvsHeader === 'string') {
- try {
- const parsed = JSON.parse(clientEnvsHeader);
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
- upstreamClientEnvs = parsed;
- }
- } catch {
- // Ignore malformed header
- }
- }
-
- // Parse x-stainless-mcp-client-permissions header to override permission options
- //
- // Note: Permissions are best-effort and intended to prevent clients from doing unexpected things;
- // they're not a hard security boundary, so we allow arbitrary, client-driven overrides.
- //
- // See the Stainless MCP documentation for more details.
- let effectiveMcpOptions = mcpOptions;
- const clientPermissionsHeader = req.headers['x-stainless-mcp-client-permissions'];
- if (typeof clientPermissionsHeader === 'string') {
- try {
- const parsed = JSON.parse(clientPermissionsHeader);
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
- effectiveMcpOptions = {
- ...mcpOptions,
- ...(typeof parsed.allow_http_gets === 'boolean' && { codeAllowHttpGets: parsed.allow_http_gets }),
- ...(Array.isArray(parsed.allowed_methods) && { codeAllowedMethods: parsed.allowed_methods }),
- ...(Array.isArray(parsed.blocked_methods) && { codeBlockedMethods: parsed.blocked_methods }),
- };
- getLogger().info(
- { clientPermissions: parsed },
- 'Overriding code execution permissions from x-stainless-mcp-client-permissions header',
- );
- }
- } catch (error) {
- getLogger().warn({ error }, 'Failed to parse x-stainless-mcp-client-permissions header');
- }
- }
-
await initMcpServer({
server: server,
- mcpOptions: effectiveMcpOptions,
+ mcpOptions: mcpOptions,
clientOptions: {
...clientOptions,
...authOptions,
},
stainlessApiKey: stainlessApiKey,
- upstreamClientEnvs,
});
return server;
@@ -114,7 +72,7 @@ const del = async (req: express.Request, res: express.Response) => {
};
const redactHeaders = (headers: Record) => {
- const hiddenHeaders = /auth|cookie|key|token|x-stainless-mcp-client-envs/i;
+ const hiddenHeaders = /auth|cookie|key|token/i;
const filtered = { ...headers };
Object.keys(filtered).forEach((key) => {
if (hiddenHeaders.test(key)) {
diff --git a/packages/mcp-server/src/instructions.ts b/packages/mcp-server/src/instructions.ts
index cae36329b..973b43d79 100644
--- a/packages/mcp-server/src/instructions.ts
+++ b/packages/mcp-server/src/instructions.ts
@@ -12,24 +12,29 @@ interface InstructionsCacheEntry {
const instructionsCache = new Map();
-export async function getInstructions(stainlessApiKey: string | undefined): Promise {
+// Periodically evict stale entries so the cache doesn't grow unboundedly.
+const _cacheCleanupInterval = setInterval(() => {
const now = Date.now();
- const cacheKey = stainlessApiKey ?? '';
- const cached = instructionsCache.get(cacheKey);
-
- if (cached && now - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) {
- return cached.fetchedInstructions;
- }
-
- // Evict stale entries so the cache doesn't grow unboundedly.
for (const [key, entry] of instructionsCache) {
if (now - entry.fetchedAt > INSTRUCTIONS_CACHE_TTL_MS) {
instructionsCache.delete(key);
}
}
+}, INSTRUCTIONS_CACHE_TTL_MS);
+
+// Don't keep the process alive just for cleanup.
+_cacheCleanupInterval.unref();
+
+export async function getInstructions(stainlessApiKey: string | undefined): Promise {
+ const cacheKey = stainlessApiKey ?? '';
+ const cached = instructionsCache.get(cacheKey);
+
+ if (cached && Date.now() - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) {
+ return cached.fetchedInstructions;
+ }
const fetchedInstructions = await fetchLatestInstructions(stainlessApiKey);
- instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: now });
+ instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: Date.now() });
return fetchedInstructions;
}
diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts
index f0741edfe..6df9c2620 100644
--- a/packages/mcp-server/src/server.ts
+++ b/packages/mcp-server/src/server.ts
@@ -16,7 +16,7 @@ export const newMcpServer = async (stainlessApiKey: string | undefined) =>
new McpServer(
{
name: 'runloop_api_client_api',
- version: '1.11.1',
+ version: '1.11.0',
},
{
instructions: await getInstructions(stainlessApiKey),
@@ -33,7 +33,6 @@ export async function initMcpServer(params: {
clientOptions?: ClientOptions;
mcpOptions?: McpOptions;
stainlessApiKey?: string | undefined;
- upstreamClientEnvs?: Record | undefined;
}) {
const server = params.server instanceof McpServer ? params.server.server : params.server;
@@ -95,7 +94,6 @@ export async function initMcpServer(params: {
reqContext: {
client,
stainlessApiKey: params.stainlessApiKey ?? params.mcpOptions?.stainlessApiKey,
- upstreamClientEnvs: params.upstreamClientEnvs,
},
args,
});
diff --git a/packages/mcp-server/src/types.ts b/packages/mcp-server/src/types.ts
index dfe0d4bf3..024ab7543 100644
--- a/packages/mcp-server/src/types.ts
+++ b/packages/mcp-server/src/types.ts
@@ -45,7 +45,6 @@ export type ToolCallResult = {
export type McpRequestContext = {
client: Runloop;
stainlessApiKey?: string | undefined;
- upstreamClientEnvs?: Record | undefined;
};
export type HandlerFunction = ({
diff --git a/src/index.ts b/src/index.ts
index 7140ff6d2..c9cb5f9e3 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -177,7 +177,6 @@ import {
DevboxAsyncExecutionDetailView,
DevboxCreateParams,
DevboxCreateSSHKeyResponse,
- DevboxCreateTunnelParams,
DevboxDeleteDiskSnapshotResponse,
DevboxDownloadFileParams,
DevboxEnableTunnelParams,
@@ -203,7 +202,6 @@ import {
DevboxSnapshotListView,
DevboxSnapshotView,
DevboxSnapshotViewsDiskSnapshotsCursorIDPage,
- DevboxTunnelView,
DevboxUpdateParams,
DevboxUploadFileParams,
DevboxUploadFileResponse,
@@ -608,7 +606,6 @@ export declare namespace Runloop {
type DevboxSendStdInResult as DevboxSendStdInResult,
type DevboxSnapshotListView as DevboxSnapshotListView,
type DevboxSnapshotView as DevboxSnapshotView,
- type DevboxTunnelView as DevboxTunnelView,
type DevboxView as DevboxView,
type TunnelView as TunnelView,
type DevboxCreateSSHKeyResponse as DevboxCreateSSHKeyResponse,
@@ -622,7 +619,6 @@ export declare namespace Runloop {
type DevboxCreateParams as DevboxCreateParams,
type DevboxUpdateParams as DevboxUpdateParams,
type DevboxListParams as DevboxListParams,
- type DevboxCreateTunnelParams as DevboxCreateTunnelParams,
type DevboxDownloadFileParams as DevboxDownloadFileParams,
type DevboxEnableTunnelParams as DevboxEnableTunnelParams,
type DevboxExecuteParams as DevboxExecuteParams,
diff --git a/src/resources/benchmark-jobs.ts b/src/resources/benchmark-jobs.ts
index 0ec85c78c..ddd532047 100644
--- a/src/resources/benchmark-jobs.ts
+++ b/src/resources/benchmark-jobs.ts
@@ -505,7 +505,7 @@ export namespace BenchmarkJobView {
/**
* The current state of the run.
*/
- state: 'running' | 'canceled' | 'completed';
+ state: 'running' | 'canceled' | 'completed' | 'failed';
/**
* Agent configuration used for this run. Specifies whether the run was driven by
diff --git a/src/resources/benchmark-runs.ts b/src/resources/benchmark-runs.ts
index 8173f5863..0584557bf 100644
--- a/src/resources/benchmark-runs.ts
+++ b/src/resources/benchmark-runs.ts
@@ -39,7 +39,9 @@ export class BenchmarkRuns extends APIResource {
}
/**
- * Cancel a currently running Benchmark run.
+ * Cancel a Benchmark run. This will do the following: 1. Cancel all running
+ * scenarios and shutdown the underlying Devbox resources 2. Update the benchmark
+ * state to CANCELED 3. Calculate final score from completed scenarios
*/
cancel(id: string, options?: Core.RequestOptions): Core.APIPromise {
return this._client.post(`/v1/benchmark_runs/${id}/cancel`, options);
@@ -118,7 +120,7 @@ export interface BenchmarkRunView {
/**
* The state of the BenchmarkRun.
*/
- state: 'running' | 'canceled' | 'completed';
+ state: 'running' | 'canceled' | 'completed' | 'failed';
/**
* The ID of the Benchmark definition. Present if run was created from a benchmark
diff --git a/src/resources/devboxes/devboxes.ts b/src/resources/devboxes/devboxes.ts
index 78a2698d7..13d5af600 100644
--- a/src/resources/devboxes/devboxes.ts
+++ b/src/resources/devboxes/devboxes.ts
@@ -189,23 +189,6 @@ export class Devboxes extends APIResource {
return this._client.post(`/v1/devboxes/${id}/create_ssh_key`, options);
}
- /**
- * @deprecated Use {@link enableTunnel} instead for V2 tunnels with better URL format.
- *
- * Creates a legacy tunnel to expose a specific port on the devbox.
- * The legacy tunnel URL format is: `https://{devbox_id}-{port}.tunnel.runloop.ai`
- *
- * V2 tunnels (via enableTunnel) provide encrypted URL-based access with the format:
- * `https://{port}-{tunnel_key}.tunnel.runloop.ai`
- */
- createTunnel(
- id: string,
- body: DevboxCreateTunnelParams,
- options?: Core.RequestOptions,
- ): Core.APIPromise {
- return this._client.post(`/v1/devboxes/${id}/create_tunnel`, { body, ...options });
- }
-
/**
* Delete a previously taken disk snapshot of a Devbox.
*/
@@ -837,23 +820,6 @@ export interface DevboxSnapshotView {
source_blueprint_id?: string | null;
}
-export interface DevboxTunnelView {
- /**
- * ID of the Devbox the tunnel routes to.
- */
- devbox_id: string;
-
- /**
- * Port of the Devbox the tunnel routes to.
- */
- port: number;
-
- /**
- * Public url used to access Devbox.
- */
- url: string;
-}
-
/**
* A Devbox represents a virtual development environment. It is an isolated sandbox
* that can be given to agents and used to run arbitrary code such as AI generated
@@ -1285,13 +1251,6 @@ export interface DevboxListParams extends DevboxesCursorIDPageParams {
| 'shutdown';
}
-export interface DevboxCreateTunnelParams {
- /**
- * Devbox port that tunnel will expose.
- */
- port: number;
-}
-
export interface DevboxDownloadFileParams {
/**
* The path on the Devbox filesystem to read the file from. Path is relative to
@@ -1540,7 +1499,6 @@ export declare namespace Devboxes {
type DevboxSendStdInResult as DevboxSendStdInResult,
type DevboxSnapshotListView as DevboxSnapshotListView,
type DevboxSnapshotView as DevboxSnapshotView,
- type DevboxTunnelView as DevboxTunnelView,
type DevboxView as DevboxView,
type TunnelView as TunnelView,
type DevboxCreateSSHKeyResponse as DevboxCreateSSHKeyResponse,
@@ -1554,7 +1512,6 @@ export declare namespace Devboxes {
type DevboxCreateParams as DevboxCreateParams,
type DevboxUpdateParams as DevboxUpdateParams,
type DevboxListParams as DevboxListParams,
- type DevboxCreateTunnelParams as DevboxCreateTunnelParams,
type DevboxDownloadFileParams as DevboxDownloadFileParams,
type DevboxEnableTunnelParams as DevboxEnableTunnelParams,
type DevboxExecuteParams as DevboxExecuteParams,
diff --git a/src/resources/devboxes/index.ts b/src/resources/devboxes/index.ts
index 3cf02ca1b..1f1168349 100644
--- a/src/resources/devboxes/index.ts
+++ b/src/resources/devboxes/index.ts
@@ -25,7 +25,6 @@ export {
type DevboxSendStdInResult,
type DevboxSnapshotListView,
type DevboxSnapshotView,
- type DevboxTunnelView,
type DevboxView,
type TunnelView,
type DevboxCreateSSHKeyResponse,
@@ -37,7 +36,6 @@ export {
type DevboxCreateParams,
type DevboxUpdateParams,
type DevboxListParams,
- type DevboxCreateTunnelParams,
type DevboxDownloadFileParams,
type DevboxEnableTunnelParams,
type DevboxExecuteParams,
diff --git a/src/resources/devboxes/tools.ts b/src/resources/devboxes/tools.ts
index 37683e669..36d41a4e0 100644
--- a/src/resources/devboxes/tools.ts
+++ b/src/resources/devboxes/tools.ts
@@ -145,7 +145,7 @@ export class DevboxTools {
*/
tunnelTools(devboxId: string, options?: Core.RequestOptions) {
const tools = {
- create_tunnel: createTool({
+ enable_tunnel: createTool({
name: 'create_tunnel',
description: 'Create a new tunnel to expose a port on the devbox',
parameters: z.object({
@@ -154,13 +154,7 @@ export class DevboxTools {
}),
execute: async (params) => {
try {
- const result = await this.devboxes.createTunnel(
- devboxId,
- {
- port: params.port,
- },
- options,
- );
+ const result = await this.devboxes.enableTunnel(devboxId, options);
return `Created tunnel: ${JSON.stringify(result, null, 2)}`;
} catch (error) {
return `Failed to create tunnel: ${error}`;
diff --git a/src/resources/index.ts b/src/resources/index.ts
index 9adfbced4..c669111e2 100644
--- a/src/resources/index.ts
+++ b/src/resources/index.ts
@@ -74,7 +74,6 @@ export {
type DevboxSendStdInResult,
type DevboxSnapshotListView,
type DevboxSnapshotView,
- type DevboxTunnelView,
type DevboxView,
type TunnelView,
type DevboxCreateSSHKeyResponse,
@@ -86,7 +85,6 @@ export {
type DevboxCreateParams,
type DevboxUpdateParams,
type DevboxListParams,
- type DevboxCreateTunnelParams,
type DevboxDownloadFileParams,
type DevboxEnableTunnelParams,
type DevboxExecuteParams,
diff --git a/src/sdk/devbox.ts b/src/sdk/devbox.ts
index 9a81ea9bb..b09776e90 100644
--- a/src/sdk/devbox.ts
+++ b/src/sdk/devbox.ts
@@ -6,7 +6,6 @@ import type {
DevboxCreateParams,
DevboxAsyncExecutionDetailView,
DevboxSnapshotDiskParams,
- DevboxCreateTunnelParams,
DevboxEnableTunnelParams,
DevboxRemoveTunnelParams,
DevboxReadFileContentsParams,
@@ -102,30 +101,6 @@ export class DevboxNetOps {
return this.client.devboxes.createSSHKey(this.devboxId, options);
}
- /**
- * @deprecated Use {@link enableTunnel} instead for V2 tunnels with better URL format.
- *
- * Creates a legacy tunnel to expose a specific port on the devbox.
- * The legacy tunnel URL format is: `https://{devbox_id}-{port}.tunnel.runloop.ai`
- *
- * V2 tunnels (via enableTunnel) provide encrypted URL-based access with the format:
- * `https://{port}-{tunnel_key}.tunnel.runloop.ai`
- *
- * @example
- * ```typescript
- * // Deprecated - use enableTunnel instead
- * const tunnel = await devbox.net.createTunnel({ port: 8080 });
- * console.log(tunnel.url); // Legacy URL format
- * ```
- *
- * @param {DevboxCreateTunnelParams} params - Tunnel creation parameters including port
- * @param {Core.RequestOptions} [options] - Request options
- * @returns {Promise} Legacy tunnel view with devbox_id, port, and url
- */
- async createTunnel(params: DevboxCreateTunnelParams, options?: Core.RequestOptions) {
- return this.client.devboxes.createTunnel(this.devboxId, params, options);
- }
-
/**
* Enable a V2 tunnel for the devbox. V2 tunnels provide encrypted URL-based access
* to the devbox without exposing internal IDs. The tunnel URL format is:
diff --git a/tests/api-resources/devboxes/devboxes.test.ts b/tests/api-resources/devboxes/devboxes.test.ts
index 06683077d..a169ed73e 100644
--- a/tests/api-resources/devboxes/devboxes.test.ts
+++ b/tests/api-resources/devboxes/devboxes.test.ts
@@ -181,21 +181,6 @@ describe('resource devboxes', () => {
);
});
- test('createTunnel: only required params', async () => {
- const responsePromise = client.devboxes.createTunnel('id', { port: 0 });
- const rawResponse = await responsePromise.asResponse();
- expect(rawResponse).toBeInstanceOf(Response);
- const response = await responsePromise;
- expect(response).not.toBeInstanceOf(Response);
- const dataAndResponse = await responsePromise.withResponse();
- expect(dataAndResponse.data).toBe(response);
- expect(dataAndResponse.response).toBe(rawResponse);
- });
-
- test('createTunnel: required and optional params', async () => {
- const response = await client.devboxes.createTunnel('id', { port: 0 });
- });
-
test('deleteDiskSnapshot', async () => {
const responsePromise = client.devboxes.deleteDiskSnapshot('id');
const rawResponse = await responsePromise.asResponse();
diff --git a/tests/objects/devbox.test.ts b/tests/objects/devbox.test.ts
index 9c3b5714d..8ca1d8fa9 100644
--- a/tests/objects/devbox.test.ts
+++ b/tests/objects/devbox.test.ts
@@ -333,25 +333,25 @@ describe('Devbox (New API)', () => {
it('should create legacy tunnel (deprecated) and return old URL format', async () => {
// Legacy tunnel response includes the old URL format: https://{devbox_id}-{port}.tunnel.runloop.ai
+ // along with the V2 properties for backward compatibility
const mockTunnel = {
- devbox_id: 'devbox-123',
- port: 8080,
- url: 'https://devbox-123-8080.tunnel.runloop.ai',
+ tunnel_key: 'legacy-key',
+ auth_mode: 'open',
+ create_time_ms: Date.now(),
+ http_keep_alive: false,
};
- mockClient.devboxes.createTunnel.mockResolvedValue(mockTunnel);
+ mockClient.devboxes.enableTunnel.mockResolvedValue(mockTunnel);
- const result = await devbox.net.createTunnel({ port: 8080 });
+ const result = await devbox.net.enableTunnel({ auth_mode: 'open' });
- expect(mockClient.devboxes.createTunnel).toHaveBeenCalledWith(
+ expect(mockClient.devboxes.enableTunnel).toHaveBeenCalledWith(
'devbox-123',
- { port: 8080 },
+ { auth_mode: 'open' },
undefined,
);
expect(result).toEqual(mockTunnel);
- expect(result.devbox_id).toBe('devbox-123');
- expect(result.port).toBe(8080);
- // Verify the legacy URL format contains devbox_id and port
- expect(result.url).toBe('https://devbox-123-8080.tunnel.runloop.ai');
+ expect(result.tunnel_key).toBeTruthy();
+ expect(result.auth_mode).toBe('open');
});
it('should enable V2 tunnel with params', async () => {
From 75c8f6a7c29ffd8031770e0c3da67c13ea927cdb Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 19 Mar 2026 00:58:11 +0000
Subject: [PATCH 2/2] release: 1.11.2
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 8 ++++++++
package.json | 2 +-
packages/mcp-server/manifest.json | 2 +-
packages/mcp-server/package.json | 2 +-
packages/mcp-server/src/server.ts | 2 +-
src/version.ts | 2 +-
7 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 1b796cf0d..33f2a7378 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "1.11.1"
+ ".": "1.11.2"
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0dc3e486b..c3d20ab51 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 1.11.2 (2026-03-19)
+
+Full Changelog: [v1.11.1...v1.11.2](https://github.com/runloopai/api-client-ts/compare/v1.11.1...v1.11.2)
+
+### Chores
+
+* remove create_tunnel endpoint ([5076da2](https://github.com/runloopai/api-client-ts/commit/5076da21a6729b4d288b530f78cc0e16415175d7))
+
## 1.11.1 (2026-03-18)
Full Changelog: [v1.11.0...v1.11.1](https://github.com/runloopai/api-client-ts/compare/v1.11.0...v1.11.1)
diff --git a/package.json b/package.json
index 322b79cd5..f9a06d6df 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@runloop/api-client",
- "version": "1.11.1",
+ "version": "1.11.2",
"description": "The official TypeScript library for the Runloop API",
"author": "Runloop ",
"types": "dist/sdk.d.ts",
diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json
index d795441d6..70704ce07 100644
--- a/packages/mcp-server/manifest.json
+++ b/packages/mcp-server/manifest.json
@@ -1,7 +1,7 @@
{
"dxt_version": "0.2",
"name": "@runloop/api-client-mcp",
- "version": "1.11.0",
+ "version": "1.11.2",
"description": "The official MCP Server for the Runloop API",
"author": {
"name": "Runloop",
diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json
index 33877432f..8ef61f27a 100644
--- a/packages/mcp-server/package.json
+++ b/packages/mcp-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@runloop/api-client-mcp",
- "version": "1.11.0",
+ "version": "1.11.2",
"description": "The official MCP Server for the Runloop API",
"author": "Runloop ",
"types": "dist/index.d.ts",
diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts
index 6df9c2620..0d686b2ec 100644
--- a/packages/mcp-server/src/server.ts
+++ b/packages/mcp-server/src/server.ts
@@ -16,7 +16,7 @@ export const newMcpServer = async (stainlessApiKey: string | undefined) =>
new McpServer(
{
name: 'runloop_api_client_api',
- version: '1.11.0',
+ version: '1.11.2',
},
{
instructions: await getInstructions(stainlessApiKey),
diff --git a/src/version.ts b/src/version.ts
index ba06c0e6e..5cab60c7c 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '1.11.1'; // x-release-please-version
+export const VERSION = '1.11.2'; // x-release-please-version