pi-modal-mcp
Enables MCP-aware agents to drive Modal serverless compute, including GPU workers and headless Chromium for web browsing with screenshots.
README
pi-modal-mcp
Drive Modal serverless compute (GPU workers + headless Chromium) from any MCP-aware agent. Zero local dependencies for the remote path; a tiny stdio shim for stdio-only clients like pi.
Three pieces, one system:
agent (pi / Claude Desktop / Cursor / your Linux box)
│
│ MCP (stdio shim OR streamable HTTP)
▼
Modal frontend ── FastAPI + MCP-over-HTTP
│ (.remote.aio fan-out)
├─▶ run_model containers (CPU workers, parallel)
└─▶ browse_page container (Playwright + Chromium → PNG)
What's inside
| Path | What |
|---|---|
modal-frontend/app.py |
Modal app: FastAPI REST endpoints and a Modal-hosted MCP-over-HTTP server (stateless streamable HTTP). Deploys Playwright for screenshots. |
modal-mcp/modal_mcp.py |
Stdio MCP shim. Bridges stdio-only MCP clients (e.g. pi) to the Modal REST endpoints. Returns screenshots as MCP image content. |
mcp-runtime/index.ts |
Pi extension: a native MCP runtime for pi. Reads ~/.pi/agent/mcp.json, spawns stdio MCP servers, registers their tools as pi tools. |
mcp-runtime/mcp.json.example |
Example pi MCP config. |
Tools exposed
| Tool | Does |
|---|---|
modal_ping |
Health-check the Modal frontend. |
modal_models |
Fan out N parallel CPU workers on Modal. Returns JSON: concurrent count, elapsed seconds, per-worker results. |
modal_browse |
Open a URL in headless Chromium inside a Modal container. Returns a PNG screenshot (MCP image content). |
modal_swarm |
Run a prompt across N parallel OSS model workers on Modal GPUs (a model swarm). Returns JSON: {concurrent, elapsed, results:[{model, completion, elapsed}]}. Models are HuggingFace IDs (default Salesforce/codegen-350M-mono), cached in a shared Modal Volume. |
Deploy the Modal app
Requires the Modal CLI authenticated.
cd modal-frontend
modal deploy app.py
You'll get two URLs:
web: https://YOUR-WORKSPACE--pi-frontend-web.modal.run (REST: /api/*)
mcp_web: https://YOUR-WORKSPACE--pi-frontend-mcp-web.modal.run/mcp (MCP streamable HTTP)
Why stateless?
Modal load-balances each HTTP request across containers, so stateful MCP
sessions (which require session affinity) break. The app sets
stateless_http=True and disables DNS-rebinding host checks so any host can
reach it. For a private deployment, tighten transport_security.allowed_hosts.
Use it
Remote MCP client (Claude Desktop / Cursor / your Linux box)
{
"mcpServers": {
"modal": {
"url": "https://YOUR-WORKSPACE--pi-frontend-mcp-web.modal.run/mcp"
}
}
}
Or the mcp Python client:
pip install mcp
python3 - <<'PY'
import asyncio
from mcp.client.streamable_http import streamablehttp_client
from mcp.client.session import ClientSession
async def main():
async with streamablehttp_client("https://YOUR-WORKSPACE--pi-frontend-mcp-web.modal.run/mcp") as (r,w,_):
async with ClientSession(r,w) as s:
await s.initialize()
print([t.name for t in (await s.list_tools()).tools])
res = await s.call_tool("modal_browse", {"url": "example.com"})
print([getattr(c,'type',None) for c in res.content])
asyncio.run(main())
PY
pi (native remote transport, no shim)
The mcp-runtime extension now speaks the remote (streamable-HTTP) MCP
transport directly, so pi can hit the Modal-hosted MCP server with zero
local component.
- Install the extension:
mkdir -p ~/.pi/agent/extensions/mcp-runtime
cp mcp-runtime/index.ts mcp-runtime/package.json ~/.pi/agent/extensions/mcp-runtime/
cd ~/.pi/agent/extensions/mcp-runtime && npm install
- Add
~/.pi/agent/mcp.json(replace workspace + URL):
{
"mcpServers": {
"modal": {
"url": "https://YOUR-WORKSPACE--pi-frontend-mcp-web.modal.run/mcp",
"transport": "streamable-http"
}
}
}
- Restart pi (or
/reload), then/mcp listto seemcp_modal_modal_ping,mcp_modal_modal_models,mcp_modal_modal_browsetagged[remote].
pi (stdio fallback) via the shim
For MCP servers that only expose a stdio interface (or for air-gapped use),
the modal-mcp shim still works:
- Install the shim on PATH:
cp modal-mcp/run.sh /usr/local/bin/modal-mcp # or anywhere on PATH
chmod +x /usr/local/bin/modal-mcp
- Add
~/.pi/agent/mcp.jsonusing thecommandform instead ofurl:
{
"mcpServers": {
"modal": {
"command": "modal-mcp",
"args": [],
"env": {
"MODAL_FRONTEND": "https://YOUR-WORKSPACE--pi-frontend-web.modal.run"
}
}
}
}
/mcp listshows the tools tagged[stdio].
Why stateless + 405 GET?
Modal load-balances each HTTP request across containers, so stateful MCP
sessions (which require session affinity) break. The app sets
stateless_http=True and disables DNS-rebinding host checks so any host can
reach it.
A second subtlety: the MCP Node SDK's streamable-HTTP client, on start(),
first tries GET /mcp to open a listening SSE stream; if the server returns
200 and holds it open, the client blocks waiting for events that a stateless
server never delivers. A stateless server has no stream to offer, so the app
wraps the MCP ASGI app in a middleware that returns 405 for GET /mcp,
telling strict clients to skip the GET stream and use POST-inline responses.
This makes the endpoint compatible with the Node SDK, the Python SDK, Claude
Desktop, and Cursor alike.
For a private deployment, tighten transport_security.allowed_hosts.
Transports
The pi mcp-runtime extension supports both backends from one config:
- stdio —
{ "command": "...", "args": [...] }spawns a local process. - remote —
{ "url": "https://...", "transport": "streamable-http" }talks to a remote MCP-over-HTTP server directly."transport": "sse"selects the legacy SSE client. Optional"headers": { ... }for auth.
Remote transport means pi needs zero local component for cloud-hosted MCP
servers — no shim, no npx/Python process to spawn.
AI-native IDE (model swarm)
The app serves a web IDE at /ide on the web URL: a Monaco editor whose
"Run swarm" button fans the current buffer across N parallel OSS model workers
on Modal GPUs and shows each completion in a side panel. This is the
self-hosted, OSS-brain equivalent of a cloud coding agent — local UI,
remote GPUs, no API keys.
https://YOUR-WORKSPACE--pi-frontend-web.modal.run/ide
Models default to Salesforce/codegen-350M-mono (small, fast, public) and are
cached in a shared Modal Volume so cold starts after the first only pay the
model-load cost. Pass any HuggingFace ID in the models field; gated models
need a secrets=[modal.Secret.from_name("huggingface-secret")] on llm_worker.
How many models can I run simultaneously?
Modal scales each function horizontally — there's no fixed concurrency cap.
You can fan out hundreds of concurrent .remote() calls; Modal spins up
containers on demand. What actually bounds you:
- Concurrent containers quota per workspace (free tiers are low; extra calls queue, they don't fail).
- GPU availability for
gpu=...functions. - Cold starts — first container of a function takes a few seconds; warm containers are ~instant.
- Your fan-out shape —
asyncio.gather(*[fn.remote.aio(x) for x in items])issues all calls concurrently.
License
MIT
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。