Skip to Content

Transport Modes

Transports define how your server communicates with AI clients. Choose based on your deployment:

TransportUse case
stdioClaude Desktop, VS Code, local tools
HTTPWeb deployments, cloud hosting, multiple clients

stdio is the default. The SDK auto-detects HTTP when you specify host/port.

stdio Transport

Communicates via standard input/output. Claude Desktop spawns your server as a subprocess.

When to Use

  • Claude Desktop integration
  • VS Code extensions
  • Command-line
  • Local development

Usage

TypeScript
import { MCPApp } from 'arcade-mcp'; import { z } from 'zod'; const app = new MCPApp({ name: 'my-server' }); app.tool('ping', { input: z.object({}), handler: () => 'pong', }); app.run({ transport: 'stdio' });
Terminal
bun run server.ts

Claude Desktop Configuration

Recommended: Use the Arcade CLI

Terminal
arcade configure claude --host local

This configures Claude Desktop to run your server as a stdio subprocess.

Manual configuration:

Edit Claude Desktop’s config file:

PlatformPath
macOS~/Library/Application Support/Claude/claude_desktop_config.json
Windows%APPDATA%\Claude\claude_desktop_config.json
Linux~/.config/Claude/claude_desktop_config.json
JSON
{ "mcpServers": { "my-tools": { "command": "bun", "args": ["run", "/path/to/server.ts"], "cwd": "/path/to/your/tools" } } }

With stdio, all stdout is protocol data. Use console.error() for logging, never console.log().

HTTP Transport

REST API with Server-Sent Events (SSE) for streaming. Built on Elysia.

When to Use

  • Web applications
  • Cloud deployments (Railway, Fly.io, etc.)
  • Multiple concurrent clients
  • Behind load balancers

Usage

TypeScript
import { MCPApp } from 'arcade-mcp'; import { z } from 'zod'; const app = new MCPApp({ name: 'my-server' }); app.tool('ping', { input: z.object({}), handler: () => 'pong', }); app.run({ transport: 'http', host: '0.0.0.0', port: 8080 });

Endpoints

EndpointMethodDescription
/worker/healthGETHealth check (returns 200 OK)
/mcpGETSSE stream for server-initiated messages
/mcpPOSTSend JSON-RPC message
/mcpDELETETerminate session (when using session IDs)

Response modes: POST requests may receive either application/json (single response) or text/event-stream (SSE stream). The SDK handles both automatically. Clients should include Accept: application/json, text/event-stream in requests.

API Documentation (Optional)

Add Elysia’s OpenAPI plugin for interactive API docs:

Terminal
bun add @elysiajs/openapi
TypeScript
import { openapi } from '@elysiajs/openapi'; app.elysia.use(openapi({ path: '/docs' }));

Scalar UI will be available at /docs (default: /swagger). See the Elysia OpenAPI docs .

Development Mode

Enable hot reload for faster development:

TypeScript
app.run({ transport: 'http', host: '127.0.0.1', port: 8000, reload: true, });

When reload: true, the server restarts automatically when you save files.

TLS / HTTPS

For production, use a reverse proxy (nginx, Caddy) for TLS termination. This is the recommended approach:

NGINX
# nginx example server { listen 443 ssl; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; location / { proxy_pass http://127.0.0.1:8000; } }

For advanced TLS configuration, use MCPServer with a custom HTTPTransport.

Docker

DOCKERFILE
FROM oven/bun:1 WORKDIR /app COPY package.json bun.lock ./ RUN bun install --frozen-lockfile COPY . . EXPOSE 8000 CMD ["bun", "run", "server.ts"]
Terminal
docker build -t my-mcp-server . docker run -p 8000:8000 -e ARCADE_API_KEY=arc_... my-mcp-server

Environment Variables

Terminal
MCP_SERVER_NAME="My MCP Server" MCP_SERVER_VERSION="1.0.0" MCP_LOG_LEVEL=DEBUG MCP_HTTP_HOST=0.0.0.0 MCP_HTTP_PORT=8080

Access with Bun.env:

TypeScript
const port = Number(Bun.env.MCP_HTTP_PORT) || 8000;

Security

stdio

  • Runs in the security of the parent process
  • No network exposure
  • Safe for local use

HTTP

HTTP transport exposes network endpoints. For production:

RequirementSolution
Use HTTPSConfigure TLS or use a reverse proxy
Enable authenticationUse Arcade auth or custom middleware
Rate limitingAdd rate limiting middleware
FirewallNever bind to 0.0.0.0 without firewall rules

CORS

For browser clients, configure CORS:

TypeScript
import { MCPApp } from 'arcade-mcp'; const app = new MCPApp({ name: 'my-server', cors: { origin: ['https://myapp.com'], credentials: true, allowedHeaders: ['Content-Type', 'Authorization'], methods: ['GET', 'POST'], }, });

CORS options follow the Elysia CORS plugin  format.

Advanced Transport Features

Custom Middleware (HTTP)

Add custom middleware to HTTP transports via the underlying Elysia instance:

TypeScript
import { MCPApp } from 'arcade-mcp'; const app = new MCPApp({ name: 'my-server' }); // Add custom response headers app.elysia.onAfterHandle(({ set }) => { set.headers['X-Custom-Header'] = 'value'; }); // Log all requests app.elysia.onRequest(({ request }) => { console.log(`${request.method} ${request.url}`); });

Transport Events

Listen to transport lifecycle events:

TypeScript
app.elysia.onStart(({ server }) => { console.log(`Server running at ${server?.url}`); }); app.elysia.onStop(() => { console.log('Server shutting down...'); });

Multiple Transports

Run the same server on multiple transports:

TypeScript
import { MCPApp } from 'arcade-mcp'; import { z } from 'zod'; const app = new MCPApp({ name: 'multi-transport' }); app.tool('ping', { input: z.object({}), handler: () => 'pong', }); // Check command line args const transport = Bun.argv[2] === 'stdio' ? 'stdio' : 'http'; app.run({ transport, host: '0.0.0.0', port: 8000, });
Terminal
# Run with HTTP bun run server.ts # Run with stdio (for Claude Desktop) bun run server.ts stdio
Last updated on

Transport Modes - Arcade MCP TypeScript Reference | Arcade Docs