Skip to content

Drop stdio MCP; keep process spawning and eval out of the bundle - #467

Merged
Leo310 merged 7 commits into
mainfrom
feat/drop-stdio-mcp
Sep 5, 2026
Merged

Drop stdio MCP; keep process spawning and eval out of the bundle#467
Leo310 merged 7 commits into
mainfrom
feat/drop-stdio-mcp

Conversation

@Leo310

@Leo310 Leo310 commented Sep 5, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to #465 for the two behaviour findings that came from bundled dependencies rather than our code: Shell Execution (child_process) and Dynamic Code Execution (new Function). After this PR the production bundle contains no child_process, spawn, execFile, eval( or new Function at all (verified by grepping build/prod/main.js), and it is ~140 kB smaller.

MCP is HTTP-only

  • The server modal loses the transport picker and the command / arguments / environment fields; pasting a stdio config into it is refused with an explanation instead of silently importing.
  • MCPTransportType is "http" only; MCPStdioServerConfig is gone. The agent editor, data store and AgentManager.loadMCPTools drop their stdio branches (MCP now loads identically on desktop and mobile).
  • Data migration v12 → v13 removes any stored stdio server (with a unit test).

Build-time module shims (vite.config.tssrc/lib/shims/)

A small shimModules Vite plugin matches on resolved module paths and swaps four dependency modules:

  • @modelcontextprotocol/sdk/client/stdio.js → throws if ever constructed (removes child_process + cross-spawn).
  • @modelcontextprotocol/sdk/validation/ajv-provider.js → the SDK's own @cfworker/json-schema provider, which interprets schemas instead of compiling them with new Function. Tool-result validation still runs.
  • @anthropic-ai/sdk/tools/agent-toolset/node.mjs → a stub. This is the SDK's local bash/grep/skills toolset; its worker environment imports it dynamically, so Vite inlined it even though nothing here calls it. It was the last child_process user.
  • Pixi's five code-generator modules (unsafeEvalSupported, createUboSyncFunction, generateShaderSyncCode, generateUniformsSync, generateParticleUpdateFunction) → stubs; pixiRenderer.ts imports pixi.js/unsafe-eval, Pixi's supported eval-free implementation, so the stubs are never reached.
  • The one remaining literal child_process.spawn was a doc string in @langchain/mcp-adapters' stdio option schema; the plugin rewords it in that module only.

Docs to update (site repo)

agents/mcp.md and help/troubleshooting.md on smartsecondbrain.dev still describe the stdio transport and its mobile caveat; both need trimming to HTTP-only.

Test plan

  • bun run check, format, lint, test (1652 passing), bun run build
  • Bundle grep: 0 × child_process / spawn( / execFile / eval( / new Function
  • Live (slot vault): smart graph renders on the eval-free Pixi path, no console errors
  • Live: add / edit / test-connect an HTTP MCP server; open an agent that previously had a stdio server and confirm it is gone after migration

@greptile-apps

greptile-apps Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR removes stdio MCP support and keeps process-spawning and dynamic-code dependencies out of the production bundle.

  • Restricts stored and runtime MCP configuration to HTTP and migrates existing stdio entries away.
  • Replaces dependency modules that introduce process spawning or generated code with explicit build-time shims.
  • Uses Pixi’s supported eval-free rendering path.
  • Adds production-bundle checks that fail the build when required shims stop matching or forbidden capabilities reappear.
  • Updates MCP configuration UI, storage conversion, types, and migration tests accordingly.

Confidence Score: 5/5

The PR appears safe to merge; no outstanding blocking or non-blocking code issue remains.

No new actionable defects were established. The previous SSE finding was correctly withdrawn, the bundle-shim invariant is now enforced during production builds, and the credential-probe and save-gating findings were fully addressed in the current code.

Important Files Changed

Filename Overview
vite.config.ts Adds resolved-module shimming and production bundle validation for process-spawning and dynamic-code patterns.
src/components/modal/MCPServerModal.svelte Converts MCP setup to HTTP-only configuration with guarded automatic connection probing.
src/stores/dataMigrations.ts Adds schema migration v13 to remove persisted stdio MCP servers.
src/agent/AgentManager.ts Removes the platform-specific stdio branch and initializes MCP uniformly with HTTP server configurations.
src/components/graph/pixiRenderer.ts Installs Pixi’s eval-free implementation before renderer creation.
src/lib/shims/mcpJsonSchemaValidator.ts Replaces AJV’s generated-code validator with the SDK’s interpreted CFWorker provider.
src/types/plugin.ts Narrows the persisted MCP configuration model to HTTP transport only.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Stored agent MCP configuration] --> B[v12 to v13 migration]
  B --> C{Transport}
  C -->|stdio| D[Remove server entry]
  C -->|HTTP| E[Keep server entry]
  E --> F[AgentManager]
  F --> G[HTTP MCP client]
  H[Vite dependency resolution] --> I[stdio transport shim]
  H --> J[CFWorker schema validator shim]
  H --> K[Anthropic toolset shim]
  H --> L[Pixi no-eval shims]
  I --> M[Production bundle]
  J --> M
  K --> M
  L --> M
  M --> N[Forbidden-pattern validation]
Loading

Reviews (8): Last reviewed commit: "MCP server modal: let untouched-connecti..." | Re-trigger Greptile

Comment thread src/components/modal/MCPServerModal.svelte
Comment thread vite.config.ts
Comment thread src/components/modal/MCPServerModal.svelte Outdated
Comment thread src/components/modal/MCPServerModal.svelte Outdated
MCP servers are HTTP-only now: the transport picker, command/args/env
fields and stdio branches are gone from the server modal, agent editor,
data store and types, and a v13 migration drops any stored stdio server.

A resolved-path shim plugin in vite.config.ts then swaps the dependency
modules that would still put shell access or dynamic code execution in
the bundle: the MCP SDK's stdio transport (child_process), its ajv
validator (new Function; replaced by the SDK's own cfworker provider),
the Anthropic SDK's local agent toolset (bash/grep/skills via
child_process, inlined through a dynamic import nothing here calls) and
Pixi's code generators (pixi.js/unsafe-eval provides the runtime). The
production bundle contains no child_process, spawn, execFile, eval or
new Function, and is ~140 kB smaller.
…pattern returns

Every shim pattern must match at least one resolved module, and the
production chunk is scanned for child_process / spawn( / execFile /
new Function( / eval( after minification. A dependency moving one of
the shimmed files now breaks the build instead of quietly restoring
the capability.
Core's .modal-button-container top margin stacked on the body's flex gap
(and its bottom padding on the modal's own), leaving a hole between
Test connection and the buttons. The flex gap now provides the spacing.
Mirrors the provider setup modal: no Test connection button. The URL
and headers are probed (debounced) as they change, the verdict — idle,
checking, connected with the tool count, or the failure cause — sits in
the footer between Delete and Cancel/Save, and the discovered tools are
listed once the probe connects. A result is only shown while the form
still matches the config it was taken for. The URL field is bound so
the probe tracks typing rather than waiting for blur.
…ep the confirm buttons together

Probing per keystroke would send stored headers (a bearer token, say)
to every host a half-typed URL happens to name, so the connection is
now checked when the URL or headers field is left, and while editing a
server it is held — with an inline "check now" — whenever the URL
points at a different origin than the saved one and headers are set.
Saving still sends them; the hold covers only the moment before.

The footer status takes a zero flex basis and Cancel/Save sit in a
non-shrinking group, so a long verdict wraps its own text instead of
pushing the confirm pair onto a second line.
Same rule as the provider modal's Done: the confirm button is plain and
disabled until the probe reports connected, so a server can't be saved
on a URL that was never reached, and a held cross-origin probe has to be
released explicitly before saving.
…r is down

A rename of an existing server must not need a live probe of a host that
may be unreachable. Save stays gated on a validated connection whenever
the URL or headers differ from the saved ones; if they are untouched, the
saved config is what gets written back, so nothing new is being trusted.
@Leo310
Leo310 force-pushed the feat/drop-stdio-mcp branch from 179766e to c5469f8 Compare September 5, 2026 14:22
@Leo310
Leo310 merged commit 775eb33 into main Sep 5, 2026
3 checks passed
@Leo310
Leo310 deleted the feat/drop-stdio-mcp branch September 5, 2026 14:25
Leo310 added a commit that referenced this pull request Sep 5, 2026
The Obsidian plugin-review pass (#465) and the removal of the stdio MCP
transport (#467). A minor bump, not a patch: a transport is gone (stored
stdio servers are dropped by the v13 data migration) and the MCP server
modal now checks the connection automatically and gates saving on it.

minAppVersion is unchanged at 1.11.4. Nothing in this release touched
PROVIDER_TEMPLATES, src/skills/defaults/, BUILT_IN_TOOL_IDS or
CURATED_PLUGIN_INTEGRATIONS, so the site's enumerable facts need no
refresh; the MCP docs were updated separately (s2b-dev/site#1).
Leo310 added a commit that referenced this pull request Sep 5, 2026
Re-cut of 2.1.0 as a patch release: nothing was added — the plugin-review
pass (#465) and the removal of the stdio MCP transport (#467) are cleanup
and a removal, not a feature. The 2.1.0 tag and release were deleted;
versions.json carries 2.0.3 in its place. minAppVersion unchanged at
1.11.4; site enumerable facts unchanged.
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.

1 participant