Skip to content

MCP-Bridge Plugins

The mcp-bridge runtime kind lets you register an existing MCP server binary as a Wayland plugin without writing any adapter code. The engine spawns the binary, runs the standard MCP handshake, discovers its tools, and registers them as first-class plugin tools. From the engine’s apply pipeline those tools are indistinguishable from statically-linked plugin tools.

The implementation lives in crates/wcore-plugin-subprocess/src/mcp_bridge.rs.

Drop a plugin.toml in a named subdirectory under the plugin install root:

~/.wayland/plugins/mcp-time/plugin.toml

Minimal manifest for a stdio MCP server binary:

plugin_api_version = "1.0"
[plugin]
name = "mcp-time"
version = "0.1.0"
description = "Wraps mcp-server-time as a Wayland plugin"
[permissions]
register_mcp_server = true
register_tools = true
tool_namespace = "Time"
[runtime]
kind = "mcp-bridge"
[runtime.subprocess]
binary_path = "/usr/local/bin/mcp-server-time"
args = []

The binary_path is resolved relative to the directory containing plugin.toml. An absolute path works too.

Two [permissions] keys are required for any mcp-bridge plugin:

KeyEffect
register_mcp_server = trueThe plugin loader checks this before launching the runner. If absent, the plugin is refused.
register_tools = trueEnables the synthesized tools to be registered into the engine’s tool registry.
tool_namespaceA string prefix. Every synthesized tool is registered as <namespace>::<tool_name>. Required when register_tools = true. Prevents two plugins from claiming the same name.

When the engine loads the plugin, McpBridgePluginRunner::load does the following:

  1. Spawns the binary with env_clear(). Only an explicit allowlist of environment variables is forwarded: PATH, HOME, USER, LANG, TZ, locale vars, TMPDIR, and Windows essentials (SYSTEMROOT, WINDIR, COMSPEC, PATHEXT, USERPROFILE, APPDATA, and others). Variables such as OPENAI_API_KEY, ANTHROPIC_API_KEY, and WAYLAND_VAULT_PASSPHRASE are withheld from the child process.
  2. Sends initialize with protocolVersion: "2025-03-26" and clientInfo.name: "wayland-mcp-bridge".
  3. Sends notifications/initialized (fire-and-forget, required by the MCP spec before further requests).
  4. Sends tools/list and collects the returned tool definitions.
  5. Synthesizes one PluginTool per discovered tool. Each tool’s execute closure routes tools/call back through the same stdio transport.
  6. Returns the LoadedMcpBridgePlugin struct carrying the tool list.

The tool list is folded into the engine’s InitializeOutcome.tools alongside any other plugin tools. No MCP-bridge-specific branch exists in the apply pipeline.

The bridge speaks JSON-RPC 2.0 over newline-delimited JSON on stdin and stdout, which is the canonical MCP-stdio framing. Any binary that implements the MCP stdio transport works without modification.

Per-RPC timeout is 30 seconds. Lines from the server’s stdout are capped at 8 MiB. A server that sends a line longer than that cap without a newline causes the transport to be marked dead, preventing out-of-memory growth.

Each synthesized tool is named <tool_namespace>::<mcp_tool_name> as declared in the manifest. The ScopedToolRegistry on the host side enforces uniqueness: two plugins cannot register the same <namespace>::<name> pair.

Passing environment variables to the server

Section titled “Passing environment variables to the server”

If the MCP server needs credentials or configuration, supply them via env in the [runtime.subprocess] block:

[runtime.subprocess]
binary_path = "/usr/local/bin/mcp-server-github"
args = []
env = { GITHUB_TOKEN = "ghp_xxx" }

Values in env are forwarded in addition to the base allowlist.

When the engine shuts down or the plugin is unloaded, the runner closes stdin (which signals the MCP server to exit per the spec), waits up to 5 seconds for a clean exit, then sends SIGKILL if the process has not exited. The kill_on_drop(true) flag on the spawned Command also reaps the process if the runner is dropped unexpectedly.