Skip to content

MCP Protocol

Baponi is a native Model Context Protocol (MCP) server that exposes sandboxed code execution and file access as tools. Any MCP-compatible client (Claude Desktop, Claude Code, Cursor, Windsurf, or custom agents) can connect with a URL and API key. No SDK installation, no local process management, no Docker containers.

Endpoint: POST https://api.baponi.ai/mcp Protocol version: 2025-11-25 Transport: Streamable HTTP (JSON-RPC 2.0 over POST, Server-Sent Events over GET) Auth: Authorization: Bearer sk-us-YOUR_API_KEY

Configure your MCP client with the Baponi endpoint URL and an API key. Create API keys in the admin console under API Keys.

Add to your claude_desktop_config.json:

{
"mcpServers": {
"baponi": {
"url": "https://api.baponi.ai/mcp",
"headers": {
"Authorization": "Bearer sk-us-YOUR_API_KEY"
}
}
}
}

Config file location:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

After saving, restart Claude Desktop. Baponi tools appear in the tool picker (hammer icon).

Baponi implements the MCP Streamable HTTP transport defined in the MCP specification:

HTTP MethodContent-TypePurpose
POSTapplication/jsonSend JSON-RPC requests and receive responses
GETtext/event-streamOpen an SSE stream for server-initiated messages

All requests go to a single endpoint: https://api.baponi.ai/mcp. There is no separate SSE URL.

Unlike many MCP servers, Baponi does not use Mcp-Session-Id headers or maintain server-side session state. This is intentional:

  • No session IDs or reconnection tokens. Every request stands alone.
  • No initialize requirement. The server responds to tools/list and tools/call without prior initialization. Sending initialize is accepted and returns capabilities, but it is not a prerequisite.
  • No sticky routing. Requests can hit any backend pod. Scaling, deploys, and pod restarts never break connections.
  • No session cleanup. Nothing to expire, nothing to garbage-collect.

This means MCP clients that expect session negotiation still work (the initialize handshake succeeds), but clients that skip it and go straight to tools/list or tools/call also work.

Negotiate protocol version and discover server capabilities. MCP clients typically send this as the first request. On Baponi, it is accepted but not required. The response is the same regardless of prior state.

Request:

{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": {
"name": "my-agent",
"version": "1.0"
}
}
}

Response:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-11-25",
"capabilities": {
"tools": {
"listChanged": false
}
},
"serverInfo": {
"name": "baponi",
"version": "0.1.0"
},
"instructions": "Baponi is a sandboxed code execution platform for AI agents. Use sandbox_execute to run Python, Node.js, or Bash code. Use files_download to retrieve files produced by executions."
}
}

Fields in result:

FieldDescription
protocolVersionThe MCP spec version the server implements (2025-11-25)
capabilities.tools.listChangedWhether the server sends notifications/tools/list_changed. Always false. Tool list changes require a new tools/list call.
serverInfo.nameServer identifier (baponi)
serverInfo.versionServer version
instructionsHuman-readable description of the server’s purpose, surfaced to LLMs by MCP clients

After receiving the initialize response, MCP clients send this notification to signal readiness. Baponi accepts and acknowledges it, but takes no action (stateless design).

{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}

No response is returned (JSON-RPC notifications have no id).

Discover the tools available to your API key. The response depends on your organization’s configuration. Tools can be enabled or disabled per-organization in the admin console.

Request:

{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}

Response:

{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "sandbox_execute",
"description": "Execute code in a sandboxed environment",
"inputSchema": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Code to execute"
},
"language": {
"type": "string",
"enum": ["python", "node", "bash"],
"description": "Programming language. Defaults to the sandbox's configured language if omitted."
},
"timeout": {
"type": "integer",
"minimum": 1,
"maximum": 3600,
"description": "Execution timeout in seconds. Defaults to sandbox configuration."
},
"thread_id": {
"type": "string",
"description": "Persistent thread ID. Files written to /home/baponi persist across calls with the same thread_id."
},
"metadata": {
"type": "object",
"description": "Arbitrary key-value metadata attached to the execution record."
},
"env_vars": {
"type": "object",
"additionalProperties": { "type": "string" },
"description": "Environment variables injected into the sandbox. Keys must be uppercase letters, digits, and underscores, starting with a letter."
}
},
"required": ["code"]
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": false,
"idempotentHint": false
}
},
{
"name": "files_download",
"description": "Download file content from a thread or volume",
"inputSchema": {
"type": "object",
"properties": {
"source": {
"type": "string",
"enum": ["thread", "volume"],
"description": "Where to read the file from."
},
"id": {
"type": "string",
"description": "The thread_id or volume name."
},
"path": {
"type": "string",
"description": "File path relative to the storage root."
}
},
"required": ["source", "id", "path"]
},
"annotations": {
"readOnlyHint": true,
"destructiveHint": false,
"idempotentHint": true
}
}
]
}
}

Each tool includes MCP annotations that help clients decide how to present and approve tool calls:

Annotationsandbox_executefiles_downloadMeaning
readOnlyHintfalsetrueWhether the tool only reads data without side effects
destructiveHintfalsefalseWhether the tool may delete or overwrite external data
idempotentHintfalsetrueWhether repeated calls with the same arguments produce the same result

Not all tools appear for every API key:

  • sandbox_execute is always available when code execution is enabled for the organization.
  • files_download appears only when the API key’s sandbox has storage configured (a storage connection or volume).
  • Tools disabled in the admin console Tool Configuration settings are excluded from the tools/list response entirely.

Execute code in an isolated sandbox. Each execution gets its own multi-layer sandbox with dedicated CPU, RAM, filesystem, and network namespace. Code runs to completion and the result is returned.

Request:

{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "sandbox_execute",
"arguments": {
"code": "import sys; print(f'Python {sys.version}')",
"language": "python"
}
}
}

Response (success):

{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "## Execution Result\n\n**stdout:**\n```\nPython 3.14.0a1 (main, Oct 15 2025, 08:00:00)\n```\n\n**Exit code:** 0"
}
],
"isError": false
}
}

Response (execution error, code ran but returned non-zero exit code):

{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "## Execution Result\n\n**stderr:**\n```\nTraceback (most recent call last):\n File \"<stdin>\", line 1, in <module>\nNameError: name 'undefined_var' is not defined\n```\n\n**Exit code:** 1"
}
],
"isError": true
}
}
ParameterTypeRequiredDescription
codestringYesSource code to execute. Max 1 MB.
languagestringNo"python", "node", or "bash". Defaults to the sandbox’s configured default language.
timeoutintegerNoExecution timeout in seconds (1-3600). Defaults to sandbox configuration. Plan limits apply: Free tier max 60s, Pro max 3600s.
thread_idstringNoAssociates the execution with a persistent thread. Files written to /home/baponi are preserved across executions sharing the same thread_id. Omit for ephemeral executions where no files need to persist.
metadataobjectNoArbitrary JSON metadata attached to the execution record. Useful for correlating executions with your application’s data model. Appears in audit logs and execution history.
env_varsobjectNoEnvironment variables injected into the sandbox for this execution. Keys must be uppercase letters, digits, and underscores (starting with a letter). Merged with sandbox-level and API key-level env vars (request takes highest precedence). See environment variables for limits.

When you pass a thread_id, the sandbox mounts persistent storage at /home/baponi. Files written there are preserved and available to subsequent executions with the same thread_id. This enables multi-step workflows:

// Step 1: Generate a file
{
"method": "tools/call",
"params": {
"name": "sandbox_execute",
"arguments": {
"code": "import json; data = {'results': [1,2,3]}; open('/home/baponi/data.json','w').write(json.dumps(data))",
"language": "python",
"thread_id": "analysis-session-42"
}
}
}
// Step 2: Read and process the file (same thread_id)
{
"method": "tools/call",
"params": {
"name": "sandbox_execute",
"arguments": {
"code": "import json; data = json.load(open('/home/baponi/data.json')); print(sum(data['results']))",
"language": "python",
"thread_id": "analysis-session-42"
}
}
}

Download a file from a thread’s persistent storage or a managed volume. Useful for retrieving artifacts produced by code executions: charts, CSVs, processed data, generated images.

Request:

{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "files_download",
"arguments": {
"source": "thread",
"id": "analysis-session-42",
"path": "data.json"
}
}
}

Response (text file):

{
"jsonrpc": "2.0",
"id": 4,
"result": {
"content": [
{
"type": "text",
"text": "{\"results\": [1, 2, 3]}"
}
],
"isError": false
}
}

Response (image file: PNG, JPG, GIF, WebP):

{
"jsonrpc": "2.0",
"id": 4,
"result": {
"content": [
{
"type": "image",
"data": "iVBORw0KGgoAAAANSUhEUgAA...",
"mimeType": "image/png"
}
],
"isError": false
}
}
ParameterTypeRequiredDescription
sourcestringYes"thread" for thread persistent storage, "volume" for managed volumes.
idstringYesThe thread_id (if source is "thread") or volume name (if source is "volume").
pathstringYesFile path relative to the storage root. Example: "output/chart.png".
File typeMCP content typeBehavior
Text files (.txt, .json, .csv, .py, .md, etc.)TextContentReturned inline as text
Images (.png, .jpg, .gif, .webp)ImageContentReturned base64-encoded with MIME type
Files larger than 1 MiBErrorReturns an error with instructions to use the REST Files API for large file downloads
Binary files (non-text, non-image)ErrorReturns an error with instructions to use the REST Files API

Protocol-level errors use standard JSON-RPC error codes. These indicate the request itself was invalid, not that code execution failed.

{
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32601,
"message": "Method not found: tools/unknown"
}
}
CodeNameWhen it occurs
-32700Parse errorRequest body is not valid JSON
-32600Invalid requestMissing required JSON-RPC fields (jsonrpc, method)
-32601Method not foundUnknown method name (e.g., tools/unknown)
-32602Invalid paramsMethod parameters fail validation (e.g., missing code in sandbox_execute)
-32603Internal errorServer-side failure. Retry with exponential backoff.

Invalid or missing API keys return HTTP 401 Unauthorized before JSON-RPC processing:

HTTP/1.1 401 Unauthorized
Content-Type: application/json
{"error": "unauthorized", "message": "Invalid or revoked API key."}

After 3 failed authentication attempts from the same IP within 60 seconds, subsequent requests are rate-limited with HTTP 429.

Baponi distinguishes between two kinds of errors:

  • Execution errors (code ran but failed): Returned as a normal tools/call result with isError: true. The LLM sees the stderr and exit code, and can decide to fix and retry.
  • Protocol errors (request was invalid): Returned as a JSON-RPC error object. The client should fix the request format.

This separation is intentional. LLMs are effective at recovering from execution errors when they can see the error output. Protocol errors indicate a client bug that needs human attention.

HeaderRequiredDescription
AuthorizationYesBearer sk-us-YOUR_API_KEY. API key created in the admin console.
Content-TypeYes (POST)application/json for POST requests.
AcceptNoapplication/json or text/event-stream. Defaults to application/json.
Mcp-Protocol-VersionNo2025-11-25. Validated if present, but not required. The server accepts requests without this header.

This example shows a full MCP session with curl: initialize, list tools, execute code, and download a file.

1. Initialize (optional):

Terminal window
curl -s -X POST https://api.baponi.ai/mcp \
-H "Authorization: Bearer sk-us-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": {"name": "curl-example", "version": "1.0"}
}
}'

2. List available tools:

Terminal window
curl -s -X POST https://api.baponi.ai/mcp \
-H "Authorization: Bearer sk-us-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}'

3. Execute code with a persistent thread:

Terminal window
curl -s -X POST https://api.baponi.ai/mcp \
-H "Authorization: Bearer sk-us-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "sandbox_execute",
"arguments": {
"code": "import csv\nwith open(\"/home/baponi/report.csv\", \"w\") as f:\n w = csv.writer(f)\n w.writerow([\"metric\", \"value\"])\n w.writerow([\"latency_p50\", \"12ms\"])\n w.writerow([\"latency_p99\", \"45ms\"])\nprint(\"Report written.\")",
"language": "python",
"thread_id": "demo-thread-1"
}
}
}'

4. Download the generated file:

Terminal window
curl -s -X POST https://api.baponi.ai/mcp \
-H "Authorization: Bearer sk-us-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "files_download",
"arguments": {
"source": "thread",
"id": "demo-thread-1",
"path": "report.csv"
}
}
}'

Baponi exposes the same execution capabilities through both MCP and REST. Choose based on your integration pattern:

MCP (/mcp)REST (/v1/sandbox/execute)
Best forMCP-native clients (Claude, Cursor, IDEs)Direct HTTP integration, custom agents
ProtocolJSON-RPC 2.0Standard REST
Tool discoveryBuilt-in via tools/listRead the API docs
File downloadInline via files_download tool (text and images up to 1 MiB)Signed URLs via Files API (any size)
StreamingSSE via GETNot available on execute endpoint
AuthSame API keysSame API keys
Sandbox configSame, determined by API keySame, determined by API key

Both surfaces are backed by the same execution infrastructure. Latency, isolation, and security guarantees are identical.

Do I need to run a local MCP server process?

Section titled “Do I need to run a local MCP server process?”

No. Baponi’s MCP server runs in the cloud. You configure your MCP client with a URL. No Docker, no npx, no local process. This is the Streamable HTTP transport, not the stdio transport.

What happens if the server pod restarts during my session?

Section titled “What happens if the server pod restarts during my session?”

Nothing. Baponi’s MCP implementation is stateless. Your next request is routed to any available pod and authenticated independently. There is no session to lose.

Can I use both MCP and REST in the same application?

Section titled “Can I use both MCP and REST in the same application?”

Yes. Both use the same API keys, the same sandboxes, and the same thread storage. An execution started via MCP’s sandbox_execute produces files that are accessible via the REST Files API, and vice versa.

Why does tools/list only show sandbox_execute?

Section titled “Why does tools/list only show sandbox_execute?”

The files_download tool only appears when your API key’s sandbox has storage configured (a storage connection or volume). Configure storage in the admin console under your sandbox settings.

The maximum request body size is 1 MB, matching the code size limit for sandbox_execute. File downloads via files_download are limited to 1 MiB inline. For larger files, use the REST Files API which provides signed URLs for direct download.

Does Baponi support MCP resources or prompts?

Section titled “Does Baponi support MCP resources or prompts?”

Baponi currently implements the MCP tools capability only. Resources and prompts are not exposed. Tool discovery via tools/list is the primary integration surface.