Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/v1alpha1/sandboxset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const (

AnnotationLock = InternalPrefix + "lock"
AnnotationOwner = InternalPrefix + "owner"
AnnotationMCPSessionID = InternalPrefix + "mcp-session-id"
AnnotationClaimTime = InternalPrefix + "claim-timestamp"
AnnotationRestoreFrom = InternalPrefix + "restore-from"
AnnotationInitRuntimeRequest = InternalPrefix + "init-runtime-request"
Expand Down
65 changes: 65 additions & 0 deletions cmd/sandbox-manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ limitations under the License.
package main

import (
"context"
"flag"
"net/http" // Added for pprof server
_ "net/http/pprof" // Added to register pprof handlers
"os"
"strings"
"time"

"github.com/google/uuid"
"github.com/spf13/pflag"
Expand All @@ -35,6 +37,7 @@ import (
"github.com/openkruise/agents/pkg/servers/e2b"
"github.com/openkruise/agents/pkg/servers/e2b/keys"
"github.com/openkruise/agents/pkg/servers/e2b/models"
"github.com/openkruise/agents/pkg/servers/mcp"
"github.com/openkruise/agents/pkg/utils"
utilfeature "github.com/openkruise/agents/pkg/utils/feature"
)
Expand Down Expand Up @@ -68,6 +71,12 @@ func main() {
var e2bKeyStorage string
var e2bKeyStorageDisableAutoMigrate bool

// Define variables for MCP server configuration
var mcpEnabled bool
var mcpPort int
var mcpSandboxTTL int
var mcpSessionSyncPort int

utilfeature.DefaultMutableFeatureGate.AddFlag(pflag.CommandLine)

// Register the new pprof flags
Expand Down Expand Up @@ -96,6 +105,12 @@ func main() {
pflag.BoolVar(&e2bKeyStorageDisableAutoMigrate, "e2b-key-storage-disable-schema-auto-update", false,
"Disable schema auto-migration for DB-Based key storage like mysql; when enabled, schema changes are skipped but admin team/key bootstrap still runs")

// Register MCP server configuration flags
pflag.BoolVar(&mcpEnabled, "mcp-enabled", false, "Enable MCP server")
pflag.IntVar(&mcpPort, "mcp-port", 8082, "MCP server port")
pflag.IntVar(&mcpSandboxTTL, "mcp-sandbox-ttl", 300, "MCP sandbox TTL in seconds")
pflag.IntVar(&mcpSessionSyncPort, "mcp-session-sync-port", 7790, "MCP session sync port")

opts := zap.Options{
Development: false,
}
Expand Down Expand Up @@ -176,6 +191,18 @@ func main() {
klog.Fatalf("--e2b-key-storage must be 'secret' or 'mysql'")
}
}
// Validate MCP server configuration
if mcpPort <= 0 || mcpPort > 65535 {
klog.Fatalf("--mcp-port must be between 1 and 65535")
}

if mcpSandboxTTL <= 0 {
klog.Fatalf("--mcp-sandbox-ttl must be greater than 0")
}

if mcpSessionSyncPort <= 0 || mcpSessionSyncPort > 65535 {
klog.Fatalf("--mcp-session-sync-port must be between 1 and 65535")
}

// Initialize Kubernetes client and config
clientConfig, err := clients.NewRestConfig(float32(kubeClientQPS), kubeClientBurst)
Expand Down Expand Up @@ -206,6 +233,44 @@ func main() {
if err != nil {
klog.Fatalf("Failed to start sandbox controller: %v", err)
}

// Create MCP Server before Run() to register SessionEventHandler before Informer starts
var mcpServer *mcp.MCPServer
if mcpEnabled {
klog.Info("MCP Server enabled, creating...")
mcpConfig := mcp.DefaultServerConfig()
mcpConfig.Port = mcpPort
mcpConfig.SandboxTTL = time.Second * time.Duration(mcpSandboxTTL)
mcpConfig.SessionSyncPort = mcpSessionSyncPort

mcpServer = mcp.NewMCPServer(
mcpConfig,
sandboxController.GetManager(),
sandboxController.GetKeys(),
sandboxController.GetManager().GetPeersManager(),
)
klog.Info("MCP Server created, SessionEventHandler registered")
}

// Start MCP Server HTTP service
if mcpServer != nil {
if err := mcpServer.Run(sandboxCtx); err != nil {
klog.Fatalf("Failed to run MCP server: %v", err)
}
klog.InfoS("MCP server started successfully", "port", mcpPort, "sandboxTTL", time.Second*time.Duration(mcpSandboxTTL))
}

<-sandboxCtx.Done()

// Stop MCP Server if running
if mcpServer != nil {
klog.Info("Stopping MCP server...")
stopCtx, stopCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer stopCancel()
if err := mcpServer.Stop(stopCtx); err != nil {
klog.ErrorS(err, "Failed to stop MCP server gracefully")
}
}

klog.Info("Sandbox controller stopped")
}
243 changes: 243 additions & 0 deletions docs/best-practices/use-mcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
# Using OpenKruise Agents Sandbox via MCP Protocol

The sandbox-manager component of OpenKruise Agents supports MCP (Model Context Protocol) as an alternative interface to E2B protocol for AI agent integrations.

## Overview

MCP Server provides a standardized protocol interface for AI agents to execute code and commands in sandbox environments. It runs alongside the E2B API server and shares the same authentication and sandbox management infrastructure.

```text
┌─────────────┐ ┌─────────────┐
│ E2B API │ │ MCP API │ <-- External Protocol Interfaces
└──────┬──────┘ └──────┬──────┘
│ │
└────────┬─────────┘
┌───────────────┐
│ SandboxManager│ <-- Unified Management Layer
└───────┬───────┘
┌───────────────┐
│ Sandbox CRs │ <-- Kubernetes Resources
└───────────────┘
```

### Available Tools

| Tool Name | Description |
|----------------|----------------------------------------------------------------------------|
| `run_code` | Execute code in a persistent sandbox with Jupyter Notebook semantics |
| `run_code_once`| Execute code in a one-time sandbox (auto-cleanup after execution) |
| `run_command` | Execute shell commands in the sandbox environment |

## Enabling MCP Server

MCP Server is disabled by default. To enable it, configure the following flags in sandbox-manager:

| Flag | Description | Default Value |
|---------------------------|------------------------------------------|---------------|
| `--mcp-enabled` | Enable MCP Server | `false` |
| `--mcp-port` | Port for MCP HTTP endpoint | `8082` |
| `--mcp-sandbox-ttl` | Sandbox TTL in seconds | `300` |
| `--mcp-session-sync-port` | Port for session peer synchronization | `7790` |

### Configuration Example

1. Add flags to sandbox-manager Deployment args:

```yaml
args:
- --mcp-enabled=true
- --mcp-port=8082
- --mcp-sandbox-ttl=300
- --mcp-session-sync-port=7790
```

2. Add container port to sandbox-manager Deployment:

```yaml
ports:
- containerPort: 8082
name: http-mcp
```

3. Add service port to sandbox-manager Service (required for Service-based access):

```yaml
ports:
- port: 8082
targetPort: 8082
protocol: TCP
name: http-mcp
```

## Authentication

MCP Server uses HTTP header authentication, sharing the same API key system with E2B:

- **Header**: `E2B-API-KEY`
- **Value**: Same API key used for E2B API (`E2B_API_KEY`)

If `E2B_ENABLE_AUTH` is set to `false`, authentication is disabled and anonymous access is allowed.

## Session Configuration Headers

MCP Server supports optional HTTP headers for per-request session configuration:

| Header | Description | Default Value |
|------------------------|--------------------------------------------------|--------------------------------|
| `X-Template` | Sandbox template name (SandboxSet name) | Server default |
| `X-Sandbox-TTL` | Sandbox TTL in seconds | `--mcp-sandbox-ttl` value |
| `X-Execution-Timeout` | Code/command execution timeout in seconds | 60 |

### Usage Example

```python
headers = {
"E2B-API-KEY": "<your-api-key>",
"X-Template": "code-interpreter", # Use specific SandboxSet
"X-Sandbox-TTL": "600", # 10 minutes TTL
"X-Execution-Timeout": "120" # 2 minutes timeout
}
```

## Endpoint

The default MCP endpoint path is `/mcp`. Full endpoint URL:

```
http://<sandbox-manager-host>:<MCP_SERVER_PORT>/mcp
```

## Client Integration Methods

### 1. Direct HTTP Access (In-Cluster)

For clients running in the same Kubernetes cluster:

```python
import httpx
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def run_code_in_sandbox():
# Configure MCP client
mcp_url = "http://sandbox-manager.sandbox-system.svc.cluster.local:8082/mcp"
headers = {"E2B-API-KEY": "<your-api-key>"}

async with streamablehttp_client(mcp_url, headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()

# Call run_code tool
result = await session.call_tool("run_code", {
"code": "print('Hello from sandbox!')",
"language": "python"
})
print(result)
```

### 2. Port Forward to Local Machine

1. Port forward MCP server:
```shell
kubectl port-forward services/sandbox-manager 8082:8082 -n sandbox-system
```

2. Connect from local client:
```python
mcp_url = "http://localhost:8082/mcp"
headers = {"E2B-API-KEY": "<your-api-key>"}
```

### 3. External Access via Ingress

Configure Ingress to expose MCP endpoint externally. Ensure proper TLS and authentication.

## Tool Usage Examples

### run_code

Execute code with persistent session state:

```json
{
"code": "x = 10\nprint(f'x = {x}')",
"language": "python"
}
```

Response:
```json
{
"logs": {
"stdout": [
"x = 10\n"
],
"stderr": []
},
"results": [],
"sandbox_id": "default--sandbox-abc123",
"execution_count": 3
}
```

### run_code_once

Execute code in a disposable sandbox:

```json
{
"code": "import os\nprint(os.getcwd())",
"language": "python"
}
```

### run_command

Execute shell commands:

```json
{
"cmd": "ls -la /workspace"
}

```

Response:
```json
{
"stdout": "...",
"stderr": "",
"exit_code": 0,
"sandbox_id": "default--sandbox-abc123"
}
```

## Session Management

MCP Server automatically manages sandbox lifecycle:

- **Session Binding**: Each MCP session is bound to a dedicated sandbox
- **Auto-Provisioning**: Sandbox is claimed from SandboxSet pool on first tool call
- **TTL Management**: Sandbox is automatically released after `--mcp-sandbox-ttl` idle time
- **Peer Sync**: Sessions are synchronized across sandbox-manager replicas

## Comparison with E2B API

| Feature | E2B API | MCP Protocol |
|----------------------|--------------------------------|--------------------------------|
| Protocol | REST HTTP | MCP over HTTP (Streamable) |
| Session State | Manual sandbox management | Automatic session-sandbox binding |
| Authentication | `E2B-API-KEY` header | `E2B-API-KEY` header |
| Use Case | Direct sandbox control | AI agent tool integration |
| Sandbox Lifecycle | Explicit create/delete | Auto-provisioned per session |

## Health Check

MCP Server provides a health check endpoint:

```shell
curl http://<sandbox-manager-host>:8082/health
# Response: OK
```
Loading
Loading