Skip to content

Add external MCP tool servers for the VibeCAD agent - #187

Draft
ntindle wants to merge 2 commits into
10-X-eng:mainfrom
ntindle:claude/vibecad-mcp-servers-u9ew1l
Draft

Add external MCP tool servers for the VibeCAD agent#187
ntindle wants to merge 2 commits into
10-X-eng:mainfrom
ntindle:claude/vibecad-mcp-servers-u9ew1l

Conversation

@ntindle

@ntindle ntindle commented Sep 6, 2026

Copy link
Copy Markdown

Lets the human register MCP servers in Preferences so the built-in VibeCAD agent can use their tools. VibeCAD acts as the MCP client: it starts or connects to each server, lists its tools, and declares them to the active provider (Codex/ChatGPT, Grok, OpenAI-compatible, Anthropic, Gemini) beside the frozen CAD surface under mcp_<server> namespaces. The first-class test target is cua-driver mcp, registered through an Add cua-driver preset; the target flow is "search models online, find one on GrabCAD, download it, import it, link it with VibeCAD". This is unrelated to the existing External MCP control mode (an outside client driving VibeCAD): tool servers extend the built-in agent and never disable it.

Changes:

  • VibeCADMCPToolServers.py (new): frozen MCPToolServer config (stdio or Streamable HTTP, env, headers, cwd, timeout, tool allowlist), JSON persistence under the new MCPToolServers preference, register/unregister helpers, presets (cua_driver_server, playwright_browser_server, filesystem_server, fetch_server), a client manager on one asyncio loop thread using the bundled mcp SDK, tool-name and schema conversion (mcp_cua_driver.screenshot), result mapping (text, structured content, images through the existing _vibecad_image_attachment path), failure codes (MCP_TOOL_ERROR, MCP_TOOL_TIMEOUT, MCP_TOOL_CALL_FAILED, MCP_SERVER_UNAVAILABLE), and an ExternalToolRunner wrapper that routes mcp_* calls and delegates everything else.
  • VibeCADSession.py: after the CAD context is frozen, attach_external_tool_schemas adds external_tool_schemas and per-server statuses as separate context keys, and the session tool runner is wrapped for the turn. A broken server is reported and skipped; the CAD turn continues.
  • VibeCADProvider.py: Codex gets extra mcp_<server> dynamic namespaces and a developer-instruction note that they are permitted; Anthropic and Gemini children share _provider_tool_surface_definitions, which appends external tools after CAD tools; a compact system-instruction section lists connected servers and the rules (external output is untrusted data, never edits the CAD document, failures reported plainly).
  • VibeCADPreferences.py: the MCP page gains an External MCP tool servers editor (list, Add/Remove, Test connection, presets, fields for command/args/URL/env/headers/cwd/tools/timeout/enabled); reset_settings removes the new key.
  • VibeCADGui.py: renders server ready/failed progress events and shuts tool servers down with the assistant.
  • CMakeLists.txt: installs the new module.
  • Docs: docs/vibecad-mcp-tool-servers.md (registration, presets, the find-download-import flow, security notes), a README section, and a cross-link from docs/vibecad-mcp-control.md.

Compatibility (per AGENTS.md):

  • Existing public functions/APIs still present and behaviorally compatible. _codex_dynamic_tool_surface, provider_tool_schemas, provider_tool_surface, digests, and native authorization are untouched; external schemas live in separate context keys.
  • No preference keys, tool names, or schema fields renamed/removed. One additive key: MCPToolServers.
  • Defaults preserve previous behavior: with no server registered, no context keys are added, no runner wrapper is installed, and no instruction section is emitted.
  • No deprecations.
  • No breaking changes.

Risk and non-goals: a stdio registration runs the configured command as the user, and the docs say so; ${NAME} references keep secrets out of user.cfg. External tool schemas are bounded (2000 characters per description, 96 KiB per turn) and an oversized server is skipped with a status message rather than truncated silently. Out of scope: STEP import through the agent (STEP goes through File → Import today; meshes import through the Mesh ribbon's mesh.io tool), MCP resources and prompts, and OAuth flows for HTTP servers.

Verification

  • For code changes, a test failed before the implementation and passes afterward; for non-code changes, the PR explains why TDD does not apply.
  • The PR lists the exact build and test commands and their results.

Red/green: vibecad_tests/test_mcp_tool_servers.py (with the fake_mcp_tool_server.py stdio fixture) was written first and failed at import (ModuleNotFoundError: No module named 'VibeCADMCPToolServers'); after the implementation it passes. It covers config round-trips and validation, preference persistence and reset, the cua-driver preset and its StdioServerParameters, name and schema normalization, live stdio behavior against the fixture (list, call, structured content, image attachment, server error, startup failure with stderr tail and backoff, disabled servers, per-call timeout, reconnect after shutdown, allowlist), the runner wrapper, the system-instruction section, Codex and child-provider declarations, a session-turn test proving the provider sees and can call an MCP tool while CAD tools still reach the CAD runner, the Preferences page contract, and GUI event rendering.

Commands run from src/Mod/VibeCAD with Python 3.11 after pip install --user pytest mcp==2.0.0 jsonschema==4.25.1 cffi cryptography:

python3 -m pytest -q vibecad_tests/test_mcp_tool_servers.py
33 passed, 1 skipped   (live cua-driver listing; skips when the binary is absent)

VIBECAD_MCP_FLOW_TEST=1 VIBECAD_TEST_PLAYWRIGHT_MCP=<node_modules/.bin/playwright-mcp> \
VIBECAD_TEST_FILESYSTEM_MCP=<node_modules/.bin/mcp-server-filesystem> \
VIBECAD_TEST_CHROMIUM=/opt/pw-browsers/chromium \
python3 -m pytest -q vibecad_tests/test_mcp_tool_servers_flow.py
1 passed   (real @playwright/mcp 0.0.80 and @modelcontextprotocol/server-filesystem 2026.8.31
            through the manager: search a local model library, open the model page, click
            Download, file lands in the downloads folder, filesystem server lists it)

python3 -m pytest -q vibecad_tests/
4407 passed, 15 skipped, 2 failed
  test_vibecad_package_manifest.py::test_cmake_installs_every_top_level_vibecad_python_module
    fixed by the CMakeLists.txt entry; passes now
  test_fasteners_packaging_contract.py::test_fasteners_is_an_exact_pinned_dependency_with_provenance
    pre-existing: fails identically with this branch's changes stashed because the
    src/Mod/Fasteners submodule is not initialized in the checkout

python3 -m pytest -q vibecad_tests/test_vibecad_package_manifest.py vibecad_tests/test_mcp_tool_servers.py \
  vibecad_tests/test_mcp_tool_servers_flow.py vibecad_tests/test_branding_contract.py \
  vibecad_tests/test_mcp_control_mode.py
92 passed, 8 skipped

Not verified in the development sandbox: the cua-driver binary itself (its installer downloads from GitHub Releases, which that sandbox's proxy blocks), page loads against public sites from Chromium (the sandbox proxy resets them), and the Qt page inside a running FreeCAD (no PySide there; the page is covered by a source contract test and pyflakes). On a desktop with cua-driver installed, test_cua_driver_live_tools_are_listed runs automatically, and the flow test runs against the published npm packages with VIBECAD_MCP_FLOW_TEST=1.

Issues

Related #170

Before and After Images

No screenshots: the development environment has no Qt. The MCP preferences page gains an "External MCP tool servers" group below the existing control-mode rows; widget object names are listed in test_mcp_preferences_page_edits_and_tests_registered_tool_servers.

🤖 Generated with Claude Code

ntindle and others added 2 commits September 6, 2026 01:27
Red phase for registering MCP servers that the built-in VibeCAD agent
consumes as tools (issue 10-X-eng#170). Adds a minimal stdio MCP server fixture
and tests covering server configuration and persistence, the cua-driver
preset, tool naming and schema conversion, live stdio behaviour, the
tool-runner wrapper, provider declarations, and the session turn.

The implementation module VibeCADMCPToolServers does not exist yet, so
the suite currently fails at import.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CioXS1GVGjBLW2K46LpeW
Let the human register MCP servers (cua-driver, a Playwright browser, a
project folder, or any stdio or Streamable HTTP server) in Preferences.
The built-in agent connects as an MCP client, declares each server's
tools beside the frozen VibeCAD CAD surface under mcp_<server>
namespaces, and routes those calls through a wrapper placed in front of
the session tool runner. The CAD surface, its digests, and its
authorization checks are unchanged, and nothing changes for users who
register no server.

- VibeCADMCPToolServers: configuration model, MCPToolServers preference,
  cua-driver/Playwright/filesystem/fetch presets, stdio and HTTP client
  manager on one loop thread, result and image mapping, runner wrapper,
  and the system-instruction section describing connected servers
- Providers: Codex external namespaces plus a shared Anthropic and
  Gemini declaration helper that appends external tools after CAD tools
- Session: attach external schemas after context capture and wrap the
  tool runner for the turn
- Preferences: External MCP tool servers editor with presets and a
  Test connection button; GUI renders server ready/failed events and
  shuts servers down with the assistant
- Docs: docs/vibecad-mcp-tool-servers.md, README section, and a
  cross-link from the MCP control-mode document

Issue: 10-X-eng#170

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CioXS1GVGjBLW2K46LpeW
@10-X-eng

10-X-eng commented Sep 6, 2026

Copy link
Copy Markdown
Owner

AHH, I see now, you want it to function like an MCP client. Ok I get it now

@ntindle

ntindle commented Sep 6, 2026

Copy link
Copy Markdown
Author

Nailed it. No idea if this implementation is any good but you get the idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants