Skip to content

Repository files navigation

mcp-toolsets-runtime

PyPI npm

The shared runtime for MCP Toolsets. Both developmentseed/mcp-toolsets and downstream repos generated from it install this package instead of each carrying their own copy of the runtime.

What's in here

One Python distribution (mcp-toolsets-runtime) exposing five top-level modules, plus the view-side JS bridge:

Module What it is
mcp_runtime Discovers a toolset's LangChain tools (TOOLS) and serves them as an MCP server; serves UI views (VIEWS) as ui:// resources; derives server instructions from CREDENTIAL_HEADERS; advertises what each tool publishes into session state, and which parameters a model may not write (NotAuthored). Entry points: mcp-serve (one toolset), mcp-serve-local (several at once, for local dev), mcp-index.
mcp_state Session state for any agent driving MCP tools: the tool_state namespace, StateCaptureMiddleware (moves large payloads out of the transcript), inspect_state (the model reads one on demand), and bind_injected (fills declared parameters from state, and offers @state:<key> handles on the rest). A filled parameter leaves a receipt, so a value the model never saw can still be traced to the tool that published it. Works against unmodified third-party servers. Requires the [state] extra.
mcp_cli Typer CLI to list and call tools on a running MCP service. Entry point: mcp-cli.
mcp_toolset Scaffolds a new toolset in a consumer repo (mcp-toolset new [--with-ui] <name>), wired to this package + the npm view bridge.
mcp_agent Example Chainlit chat agent that discovers MCP servers behind an index URL and drives their tools, with mcp_state wired in (MCP_AGENT_STATE=0 to opt out). Conversations are checkpointed per thread_id — in-process by default, PostgreSQL via MCP_AGENT_CHECKPOINT + the [checkpointing-postgres] extra. Ships the Chainlit host element elements/McpView.jsx. Entry points: mcp-agent, mcp-agent-web. mcp_agent.main (build_agent, run_turn), mcp_agent.streaming (stream_turn, the same turn yielded as it happens) and mcp_agent.host — the UI-framework-free helpers a host of its own needs (view bundles and props, and the tool-step arguments session state filled in) — need the [agent] extra. mcp_agent.web, the Chainlit host, needs [web] on top.
mcp_agent_api The agent over HTTP. mcp_agent_api.events turns one turn into AG-UI events — tokens, tool calls, and the two things AG-UI has no vocabulary for: where each tool's arguments came from and which ui:// view renders its result, both as ACTIVITY_* messages carrying a rendered display line beside their fields. Imports no FastAPI. mcp_agent_api.routes is an APIRouter over a built agent — POST /runs streams that turn as SSE, and four read routes serve what the stream deliberately leaves out: the thread's transcript, its turns with the state each ended holding, a session-state payload in full (?turn=N for the value as it stood then, which the checkpointer has kept all along), and a ui:// view bundle. mcp_agent_api.app closes the stack for a deployment with no application of its own: create_app(build=…) puts a lifespan, a checkpointer, CORS and two health probes around those routes, and a module-level app serves under uvicorn mcp_agent_api.app:app. A sixth route, GET /connections, says what the agent connected to and which credential headers it wants, which is what a client needs before there is a conversation. mcp_agent_api.ui serves the bundled web client (below) beside all of it. Requires the [api] extra.
@developmentseed/mcp-view (js/mcp-view) The view-side ui/* postMessage bridge a toolset UI imports (onData / sendMessage). Published to npm separately.

The toolset plugin contract

mcp_runtime discovers a toolset purely by convention — a <toolset>.tools module exporting:

  • TOOLS — a non-empty list of LangChain tools that return a ToolResult.
  • VIEWS (optional){tool_name: view_id}, with a built bundle at <package>/views/<view_id>.html.
  • CREDENTIAL_HEADERS (optional) — header names the tools read off the transport; used to derive the model-facing auth hint.

Every data key of a ToolResult — every field but message — is a value the tool publishes. An mcp_state client captures each into session state under <toolset>/<tool>/<field> and lets a later tool be pointed at it by that key, so a large value — a geometry, an item collection — moves from the tool that produced it to the tool that needs it without passing through the model. Producer and consumer may be different toolsets on different servers; the key is the only thing they share, which is why a data key is a public name.

A tool may also tag a parameter NotAuthored, which says only that a model must not write the value — no type, nothing for another toolset to agree with. An mcp_state client narrows that parameter until the only thing it accepts is a reference to a value some tool already produced; a client that has never heard of any of this is unaffected.

Keeping a value out of the context is client-side work, so an external MCP host does none of it: served to Claude.ai or ChatGPT, a toolset behaves like any other. Tag for the agents that understand it, and size tool returns for the clients that don't.

Tagging is an accelerator, not a requirement: mcp_state moves values across unmodified third-party MCP servers too, by capturing large returns on size and letting the model point a parameter at one with an @state:<key> handle. What the tag buys is that the parameter leaves the model's schema entirely.

Treat ToolResult, NotAuthored, and the ui/* wire protocol as public API. The state contract, worked through as sequence diagrams — including the trust assumption it rests on — is in docs/SESSION-STATE.md, with a runnable version of the whole thing, against a third-party server included, in examples/session-state/ (uv run python examples/session-state/demo.py — no API key needed). The same machinery on the wire, driven over HTTP by the client the wheel ships, is in examples/agui-events/ — tokens streaming, tool calls and receipts in the order they arrive, and a state panel whose values are a fetch away rather than on the wire.

The bundled web client

[api] installs a page as well as an API. mcp_agent_api.app serves it at the root, so a container running uvicorn mcp_agent_api.app:app is a working chat over the toolsets behind MCP_URL — the transcript, tool calls and receipts as they happen, the session-state panel, and ui:// views in their frames. No Node runs in the image and no front end is copied into the deployment.

What a deployment says about it is text and one colour, read from the environment at startup:

MCP_AGENT_UI_TITLE the name in the header and the browser tab
MCP_AGENT_UI_TAGLINE one line beside it
MCP_AGENT_UI_GREETING the opening paragraph; unset, the page says what GET /connections reports
MCP_AGENT_UI_EXAMPLES questions offered as buttons, one per line (or a JSON array)
MCP_AGENT_UI_ACCENT a CSS colour

Anything structural is a change to the client, whose source is js/agent-ui. It talks to the six routes in mcp_agent_api.routes and nothing else, so a host that mounts create_router into an application of its own serves the same client with mount_ui(app, api="/api"); what forces a fork is diverging from those routes, not from the application around them. create_app(ui=False) turns the page off for a deployment with a front end of its own.

Install

From PyPI — see the badge above for the current release:

# base: runtime + cli (lean, for tool-serving images)
pip install mcp-toolsets-runtime

# session state, for wiring it into an agent of your own
pip install "mcp-toolsets-runtime[state]"

# the agent — build_agent, run_turn, stream_turn and the host helpers
pip install "mcp-toolsets-runtime[agent]"

# the bundled Chainlit web host, on top of the agent
pip install "mcp-toolsets-runtime[web]"

# the agent over HTTP as AG-UI events, plus the web client that renders them
# — an alternative to [web], not a layer
pip install "mcp-toolsets-runtime[api]"

[state], [agent] and [web] are a chain, so name only the outermost you need. [api] sits beside [web] on top of [agent]: a deployment serving the API does not install Chainlit, and one serving the chat does not install AG-UI.

With uv, as a consumer — an ordinary dependency, no source override:

dependencies = ["mcp-toolsets-runtime[web]"]

Imports are unchanged from the old workspace packages: from mcp_runtime.server import build_server, etc. uv.lock pins whatever resolved, so upgrading is uv lock --upgrade-package mcp-toolsets-runtime. The package is pre-1.0, where a minor release may break — bound it at the next minor in your own pyproject.toml if you'd rather take those deliberately.

Consuming this package — the plugin contract, serving toolsets, wiring up UI views (including mcp-agent install-elements and the npm bridge), wiring session state into your own agent, serving that agent over HTTP, and migrating off the in-repo workspace: see docs/CONSUMING.md.

Develop

uv sync --all-extras   # install every extra ([web] included) + dev tools
./scripts/lint         # ruff check + ruff format --check + mypy (config in pyproject)
./scripts/test         # pytest
./scripts/build-js     # both JS packages: the npm view bridge, and the web
                       # client, which builds into src/mcp_agent_api/ui (needs node)

Releases

Versioning and CHANGELOG.md are managed by release-please from Conventional Commits. See CONTRIBUTING.md — in short, your PR title is the changelog entry, and CI fails a PR whose title isn't a valid conventional commit. The Python package and the JS bridge share one version (linked).

About

The runtime packages for executing and discovering MCP Toolsets

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages