Transport Modes
Transports define how your server communicates with AI clients. Choose based on your deployment:
| Transport | Use case |
|---|---|
| stdio | Claude Desktop, VS Code, local tools |
| HTTP | Web 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
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' });bun run server.tsClaude Desktop Configuration
Recommended: Use the Arcade CLI
arcade configure claude --host localThis configures Claude Desktop to run your server as a stdio subprocess.
Manual configuration:
Edit Claude Desktop’s config file:
| Platform | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.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
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
| Endpoint | Method | Description |
|---|---|---|
/worker/health | GET | Health check (returns 200 OK) |
/mcp | GET | SSE stream for server-initiated messages |
/mcp | POST | Send JSON-RPC message |
/mcp | DELETE | Terminate 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:
bun add @elysiajs/openapiimport { 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:
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 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
FROM oven/bun:1
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
COPY . .
EXPOSE 8000
CMD ["bun", "run", "server.ts"]docker build -t my-mcp-server .
docker run -p 8000:8000 -e ARCADE_API_KEY=arc_... my-mcp-serverEnvironment Variables
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=8080Access with Bun.env:
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:
| Requirement | Solution |
|---|---|
| Use HTTPS | Configure TLS or use a reverse proxy |
| Enable authentication | Use Arcade auth or custom middleware |
| Rate limiting | Add rate limiting middleware |
| Firewall | Never bind to 0.0.0.0 without firewall rules |
CORS
For browser clients, configure CORS:
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.
Multiple Transports
Run the same server on multiple transports:
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,
});# Run with HTTP
bun run server.ts
# Run with stdio (for Claude Desktop)
bun run server.ts stdio