Companion demo for the Toolset Versioning SEP draft and the Python SDK draft reference on branch feature/toolset-versioning.
A Streamable HTTP MCP server publishes three agent-facing core-ops Toolset versions plus a separate pagination-demo family for exercising toolsets/list paging. The demo targets MCP protocol revision 2026-07-28: clients discover server support through server/discover, advertise io.modelcontextprotocol/toolsets in per-request capabilities, and pass an exact toolset pin on tools/list / tools/call. A CLI agent pins core-ops@1.1.0 and drives tools through a local OpenAI-compatible LLM (Ollama by default).
server/server.py # MCPServer + Toolsets over Streamable HTTP
client/verify.py # No-LLM pin assertions
client/agent.py # CLI agent (Ollama / OpenAI-compatible)
tests/test_server.py # In-memory server integration tests
- uv
- Sibling checkout of the SDK at
../python-sdkon branchfeature/toolset-versioning - For the agent: Ollama with a tools-capable model, e.g.
ollama pull llama3.2
From this repo root:
uv syncpyproject.toml depends on mcp via an editable path to ../python-sdk.
Terminal 1 — server
uv run python -m server.serverListens at http://127.0.0.1:8000/mcp.
Terminal 2 — verify (no LLM)
uv run python -m client.verifyAsserts unpinned vs pinned membership, that ToolD under 1.1.0 returns tool_not_in_toolset, and that an unknown pin returns unknown_toolset on tools/call.
In-memory server tests
uv run --frozen pytestThe tests traverse the separate pagination-demo Toolset family across pages.
They do not change or exercise the Toolset pinned by the CLI agent.
Terminal 2 — agent
uv run python -m client.agentThen try: get me a content list. The agent should call ToolA, ToolB, and ToolC only (never ToolD under the pin).
| Version | Tools |
|---|---|
core-ops@1.0.0 |
ToolA, ToolB |
core-ops@1.1.0 (agent pin) |
ToolA, ToolB, ToolC |
core-ops@2.0.0 |
ToolB, ToolC, ToolD |
The server also publishes four experimental pagination-demo versions containing
ToolA. With the demo's page size of three, that family spans two toolsets/list
pages without changing the agent-facing core-ops surface.
Unpinned tools/list still returns ToolA–ToolD.
| Variable | Default |
|---|---|
MCP_URL |
http://127.0.0.1:8000/mcp |
TOOLSET_NAME |
core-ops |
TOOLSET_VERSION |
1.1.0 |
OPENAI_BASE_URL |
http://127.0.0.1:11434/v1 |
OPENAI_API_KEY |
ollama |
MODEL |
llama3.2 |
Point OPENAI_BASE_URL / MODEL at any OpenAI-compatible chat+tools endpoint.
This demo shows the extension wire behavior that SEP reviewers may be interested in:
- Discover server support through
server/discoverand advertise the extension in each extension-dependent request toolsets/listdiscovery- Exact pin on
tools/list/tools/call - Protocol errors for non-members (
tool_not_in_toolset) and unknown pins (unknown_toolset) - Concurrent immutable versions (
1.0.0/1.1.0/2.0.0) on one server - Opaque cursor traversal over filtered
toolsets/listresults