Math Circuit Studio
An MCP server that provides interactive 2D/3D math plotting, embedded CircuitJS circuit demonstrations, and locally rendered Manim slide videos for ChatGPT/Codex.
README
Math Circuit Studio
A small, self-hosted MCP App for ChatGPT/Codex that can:
- draw interactive 2D function plots, 3D surfaces, and parametric 3D curves;
- embed a pinned CircuitJS1 editor for explanatory circuits;
- render short Manim MP4s from a constrained slide/equation schema.
It has no database, queue, S3 bucket, CDN, or external simulation API. One Docker container runs the MCP server, hosts every browser asset, and invokes Manim locally. Generated videos and CircuitJS export files live in a named Docker volume and expire automatically.
CircuitJS is used as an interactive teaching canvas. This project intentionally does not expose SPICE, arbitrary Manim Python, or electrical design verification.
Architecture
flowchart LR
C["ChatGPT or an MCP client"] -->|"Streamable HTTP /mcp"| A["Node MCP server"]
A --> W["Unified in-chat widget"]
W --> P["Plotly + mathjs lazy-loaded for graphs"]
W --> J["Bundled CircuitJS iframe"]
A --> M["Constrained Manim templates"]
M --> V["Local artifacts volume"]
W --> V
Tools
| Tool | Intended input | Result |
|---|---|---|
plot_math |
mathjs expressions and finite ranges | interactive 2D/3D Plotly view |
show_circuit |
CircuitJS Export as Text content | embedded CircuitJS canvas |
render_manim_explanation |
1–8 text/LaTeX slides | locally rendered MP4 player |
All three tools use the same MCP App resource at
ui://math-circuit-studio/viewer-v2.html. Every structured tool result has a matching
output schema. The resource declares only the deployed origin in its CSP; there are
no public CDN dependencies.
Run locally with Docker
Requirements: Docker Compose v2 and roughly 4 GB RAM for building CircuitJS and rendering Manim.
cp .env.example .env
# For local use, set PUBLIC_ORIGIN=http://localhost:3000 in .env
docker compose -f compose.yaml -f compose.local.yaml up --build
Check http://localhost:3000/readyz (dependency readiness) or /healthz (process
liveness). The MCP endpoint is
http://localhost:3000/mcp and the bundled CircuitJS page is
http://localhost:3000/circuitjs/circuitjs.html.
The first image build compiles the pinned CircuitJS GWT application, so it is much slower than subsequent cached builds.
Deploy with Dokploy
-
Create a Docker Compose application from this repository and select
compose.yaml. -
In Dokploy's Environment tab, set at least:
PUBLIC_ORIGIN=https://visuals.your-domain.example WIDGET_DOMAIN=https://visuals.your-domain.example MEMORY_LIMIT=4G CPU_LIMIT=2.0 -
In Domains, add the public hostname to service
app, container port3000, and enable HTTPS. Do not add Traefik labels to the Compose file; Dokploy manages them from this domain configuration. -
Deploy, then verify
https://visuals.your-domain.example/readyzandhttps://visuals.your-domain.example/circuitjs/circuitjs.html. -
Add
https://visuals.your-domain.example/mcpas the remote MCP server in ChatGPT developer mode.
Dokploy writes dashboard environment values to .env; this Compose file references
them explicitly with ${VARIABLE} interpolation. It uses only a named volume, never
an absolute host bind mount, so redeploys remain portable on a single Dokploy node.
Do not run multiple replicas: the render-concurrency guard is process-local and the artifact volume is local. This is intentionally a simple single-instance service.
Configuration
| Variable | Default | Purpose |
|---|---|---|
PUBLIC_ORIGIN |
http://localhost:3000 |
Exact external origin used in iframe/video URLs and widget CSP |
WIDGET_DOMAIN |
PUBLIC_ORIGIN |
Dedicated HTTPS origin declared to the ChatGPT widget host |
MANIM_ENABLED |
true |
Enables the Manim tool |
MANIM_TIMEOUT_MS |
75000 |
Hard timeout for one render |
MANIM_MAX_CONCURRENT |
1 |
Simultaneous render limit |
ARTIFACT_TTL_HOURS |
72 |
Local MP4 and CircuitJS export retention |
MCP_BEARER_TOKEN |
empty | Optional static bearer protection for clients that can send it |
MEMORY_LIMIT |
4G |
Runtime Compose memory limit |
CPU_LIMIT |
2.0 |
Runtime Compose CPU limit |
CIRCUITJS_REF |
pinned SHA | Reproducible CircuitJS source revision |
PUBLIC_ORIGIN must be the external HTTPS origin in ChatGPT. Set WIDGET_DOMAIN to
that same dedicated origin for plugin submission. A localhost value in a remote
deployment will make nested CircuitJS frames and video URLs point at the wrong machine.
ChatGPT integration notes
The server uses Streamable HTTP at /mcp, serves an MCP App HTML resource with the
text/html;profile=mcp-app MIME type, and returns structured content for the widget.
The CircuitJS editor is a nested same-origin frame, so the resource CSP includes the
configured origin in frameDomains. Circuit export text is loaded from a short,
content-addressed /circuit-files/... URL instead of being placed in a potentially
oversized query string.
The default deployment has no authentication to keep first-time self-hosting simple.
That means anyone who can reach /mcp can request renders. For an internet-facing
production deployment, put the endpoint behind an OAuth-capable gateway compatible
with your MCP client, or use MCP_BEARER_TOKEN with a client that can attach a static
Authorization header. Keep the video and CircuitJS routes publicly readable so the
sandboxed widget can load them.
Local development
Node 22+ is required.
npm ci
npm run check
npm test
MANIM_ENABLED=false PUBLIC_ORIGIN=http://localhost:3000 npm start
The host build does not install Manim or compile CircuitJS; those are supplied by the Docker image. The normal test suite disables Manim and verifies MCP schemas, metadata, input rejection, graph results, concurrent circuit storage, and widget resources. The container test additionally exercises a real Manim/LaTeX render and cache hit.
The included .mcp.json points Codex at http://localhost:3000/mcp, so start the
Compose application before using the repo-local plugin configuration. For a remote
instance, change that URL to the deployed HTTPS endpoint.
Operational and safety boundaries
- Manim source is generated only by server-owned templates. User/model content is inserted as escaped string literals; arbitrary Python is not accepted. LaTeX is restricted to a math-oriented command and environment allowlist before TeX runs.
- Graph expressions are parsed and restricted to numeric operators, selected math functions, constants, and the variables expected by each graph mode. Assignment, object access, imports, and function definitions are rejected.
- The container runs as
manimuser, drops Linux capabilities, uses a read-only root filesystem, and writes only to/tmpplus the artifact volume. - Manim jobs are synchronous, cached, timeout-bound, and deliberately limited. On timeout or shutdown, the complete Manim/ffmpeg/LaTeX process group is terminated. This avoids Redis and a worker queue, but it is intended for short explanations rather than production video jobs.
- CircuitJS browser behavior is useful for intuition and demos, not component sizing, compliance, safety analysis, or sign-off.
CircuitJS license
CircuitJS1 is GPL-2.0 licensed. The Docker image exposes the exact license at
/licenses/circuitjs1-GPL-2.0.txt and the complete pinned source used to build it at
/licenses/circuitjs1-source.tar.gz. Review THIRD_PARTY_NOTICES.md before
redistributing an image, especially if you modify CircuitJS.
推荐服务器
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 模型以安全和受控的方式获取实时的网络信息。