sha256:929c802b67123774c08bcf93c0957d2476f50364474343d1e08f0450907fe30e
Last pushed
26 days by simonferquel
Type
Sandbox Kit
Manifest digest
sha256:929c802b67123774c08bcf93c0957d2476f50364474343d1e08f0450907fe30e
schemaVersion: "2"
kind: sandbox
name: pi-agent-mcp
displayName: Pi (MCP Gateway)
description: 'Minimal terminal coding agent with extensible tools, skills, and TUI -- a personal-preferences kit derived from docker/sbx-kits-contrib''s `pi`: bakes in pi-mcp-adapter (pi has no MCP client of its own) and self-registers the sandbox''s hosted MCP gateway (when one is reserved) into pi-mcp-adapter''s own ~/.pi/agent/mcp.json, so pi discovers gateway-provided tools automatically with no manual setup step.'
sandbox:
image: docker.io/simonferquel/pi-agent-mcp-base:latest
entrypoint:
- pi
agentInstructions:
filename: AGENTS.md
permissions:
network:
allow:
- api.anthropic.com
- registry.npmjs.org
- platform.claude.com:443
credentials:
- service: anthropic
apiKey:
name: ANTHROPIC_API_KEY
proxyManaged: true
inject:
- domain: api.anthropic.com
header: x-api-key
format: '%s'
oauth:
tokenEndpoint:
host: platform.claude.com
path: /v1/oauth/token
resourceHosts:
- api.anthropic.com
sentinels:
accessToken: sk-ant-oat01-proxy-managed
refreshToken: sk-ant-ort01-proxy-managed
credentialFile:
path: ~/.pi/agent/auth.json
structure:
anthropic:
access: '{{.AccessToken}}'
expires: '{{.ExpiresAt}}'
refresh: '{{.RefreshToken}}'
type: oauth
setup:
install:
- command: if [ -n "${HTTP_PROXY:-}" ]; then npm config set proxy="$HTTP_PROXY" https-proxy="${HTTPS_PROXY:-$HTTP_PROXY}"; fi
user: "1000"
description: Point npm at the sandbox proxy in ~/.npmrc so pi's runtime npm use (`pi install npm:...`, `pi update`, startup package fetches) works in exec contexts that do not inherit the proxy environment variables
startup:
- command:
- sh
- -c
- |
set -e
[ -n "${MCP_GATEWAY_URL:-}" ] || exit 0
MCP_GATEWAY_URL="$MCP_GATEWAY_URL" MCP_SENTINEL_TOKEN_NAME="${MCP_SENTINEL_TOKEN_NAME:-mcp-gateway}" node <<'NODEEOF'
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const gatewayUrl = process.env.MCP_GATEWAY_URL;
const tokenName = process.env.MCP_SENTINEL_TOKEN_NAME;
const configPath = path.join(os.homedir(), '.pi', 'agent', 'mcp.json');
let config = {};
try {
if (fs.existsSync(configPath)) {
const raw = fs.readFileSync(configPath, 'utf8');
const parsed = raw.trim() ? JSON.parse(raw) : {};
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
config = parsed;
}
}
} catch (err) {
// Fail safe: an unreadable/corrupt file is left untouched rather
// than clobbered; the next sandbox start retries naturally.
process.stderr.write('pi-agent-mcp-kit: mcp.json unreadable, skipping gateway registration: ' + err.message + '\n');
process.exit(0);
}
if (typeof config.mcpServers !== 'object' || config.mcpServers === null || Array.isArray(config.mcpServers)) {
config.mcpServers = {};
}
config.mcpServers['mcp-gateway'] = {
url: gatewayUrl,
headers: { Authorization: 'Bearer ' + tokenName },
lifecycle: 'eager',
};
fs.mkdirSync(path.dirname(configPath), { recursive: true });
const tmpPath = configPath + '.tmp';
fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n');
fs.renameSync(tmpPath, configPath);
NODEEOF
user: "1000"
description: Register the sandbox's hosted MCP gateway (when reserved) as a remote MCP server in pi-mcp-adapter's ~/.pi/agent/mcp.json; no-op when no gateway is reserved