Skip to content

MCP-native tool architecture, Markdown-only skills registry, and pull progress reporting#3

Open
GVonB wants to merge 8 commits into
cumbof:mainfrom
GVonB:feat/mcp-native-tools
Open

MCP-native tool architecture, Markdown-only skills registry, and pull progress reporting#3
GVonB wants to merge 8 commits into
cumbof:mainfrom
GVonB:feat/mcp-native-tools

Conversation

@GVonB

@GVonB GVonB commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Ports the framework's 25 built-in tools (previously team/tools.py) into 8 in-process MCP servers under team/mcp/builtin/ (workspace, code, memory, beliefs, decisions, federation, expert, web), wired through a new MCP tool bus that both text-mode (fenced-block) and native-mode (LLM function-calling — tool_mode: native already existed pre-migration) agentic loops share.
  • Retires the Python tool-loading half of skills.py (arbitrary local/remote/entry-point-registered Python tool plugins, including its exec()-based and remote-URL loader) in favor of MCP servers — either external (mcp_servers:, stdio/http) or in-process (team.mcp_servers entry-point group).
  • Keeps and narrows the context-injection half: team.skills still exists as an entry-point group, but now resolves only to Markdown context bundles (via a SKILL_FILE-pointing wrapper module), consumed through extra_context: alongside plain relative paths. team forge scaffolds a matching skills/ slot next to servers/, and the generated extension CLI gets a skills subcommand mirroring servers.
  • Verified against the actual published team-core PyPI wheel and this repo: the bundled skills/ example directory never shipped in the package (only the loader module did), and every documented example skill is faithfully preserved under the new architecture — the 3 markdown files are byte-identical in examples/context/, and the 4 Python skills' tool names all reappear in examples/mcp/team_helpers_server.py.
  • Fixes a bus-loop responsiveness regression (built-in tools ran inline on the single bus event loop, so a slow tool stalled every other member's concurrent calls) and a native-mode prompt bug (the text-mode fenced-block protocol was leaking into native-mode system prompts, telling native models to emit blocks the native path never parses).
  • Adds live progress reporting for Docker image pulls and Ollama model pulls (team up/run/test), replacing a silent blocking spinner that was indistinguishable from a hang on large first-time pulls.

Test plan

  • pytest -q -m "not integration" — 912 passed
  • Manually verified team check preflight against a real Docker Desktop instance
  • Manually verified team up/team run against real Ollama containers, confirming progress line updates during image/model pulls
  • Verified tool_mode: native executes real tool calls (vs. text-mode's fenced-block parsing) against llama3.2:3b via a live smoke-test run
  • Forged a fresh extension package end-to-end (team forge, installed in a clean venv, ran its scaffold tests, confirmed team <ext> skills/servers both work)

🤖 Generated with Claude Code

GVonB and others added 8 commits July 7, 2026 08:35
Add team/mcp package with MCPToolBus (background asyncio loop thread,
single manager task owning the AsyncExitStack), MemberToolset (per-member
enablement + output truncation), and ToolInfo. Bump to 0.18.0, Python 3.10,
add mcp>=1.9,<2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
team/mcp/builtin/{code,web,workspace,memory,beliefs,decisions,federation,expert}.py
with BuiltinContext + BUILTIN_SERVERS registry. Typed keyword-arg signatures
replace the old string-body parsing; sandbox helpers move into code.py.
Schema regression test pins all 25 generated schemas. tools.py stays until the
Phase 4 cutover.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Config: MCPServerConfig + mcp_servers, extra_context (replaces Markdown skills),
qualified server/tool pattern validation, config.expand_env(). Remove skills fields.

Cutover: member.py drives both agentic loops through MemberToolset (text mode via
team.mcp.textmode, native mode via toolset schemas) — fixes the native-mode dropped
peers bug. orchestrator.py owns the bus lifecycle, builds per-member BuiltinContext,
connects external mcp_servers (TEAM_WORKSPACE injected), and carries the moved
host-Ollama sandbox security warning. personas.py renders the tool protocol from
ToolInfo. Tests rewired to the bus (conftest fixtures + federation KV shim).

tools.py/skills.py remain until Phase 5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Delete team/tools.py, team/skills.py, tests/test_tools.py, tests/test_skills.py.
Extension scaffolder (cli.py forge) and team/extension.py swap the skills/
slot + team.skills entry-point group for a servers/ slot + team.mcp_servers
group (transport: entry_point). bridge_server capabilities advertise tools +
mcp_servers instead of skills. Rewire test_plugin_system to _resolve_entry_point,
update test_forge/test_extension. Federation tests route through a KV-body shim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace repo skills/ with examples/mcp/team_helpers_server.py (the 4 helper
skills as one stdio FastMCP server) and examples/context/*.md referenced via
extra_context. Add examples/mcp_team.yaml. New tests: test_example_helpers_server
(bus-driven) and test_mcp_external_stdio (real subprocess, reaps child on stop).
Bump _version to 0.18.0. Rewrite README tool/skill chapters + AGENTS.md for the
MCP-native architecture; add CHANGELOG.md with breaking changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… mode

Code review follow-ups:
- Built-in tools ran inline on the single bus event loop (FastMCP does not
  offload sync tool fns), so a slow tool (run_bash, or federation delegate_task
  waiting up to 600s) froze the loop and stalled every other member's concurrent
  tool calls — a regression of the per-member parallelism the old design had. Add
  an @offload decorator (anyio.to_thread) to all 25 built-in tools; functools.wraps
  preserves the FastMCP-generated schema.
- render_system_prompt injected the text-mode fenced-block protocol even for
  native-mode members, telling native models to emit  blocks that the
  native path never parses. Gate the section on tool_mode.
- Bound external MCP server connects (asyncio.wait_for inside the manager task) so
  a server that never completes initialize/list_tools is marked dead instead of
  wedging startup and shutdown; snapshot _conns iteration to avoid a dict-mutation
  race with stop(); treat timeout=0 as a real (not falsy) bound.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`ensure_image()` blocked silently on `client.images.pull()` with no
progress and no timeout, and `OllamaClient.ensure_model()` discarded the
streamed pull events it already received — so a slow or genuinely large
(multi-GB) first-run pull was indistinguishable from a hang.

container.py now pulls via the low-level streaming API and aggregates
layer progress; ollama_client.py forwards pull events instead of dropping
them; member.py/orchestrator.py thread an optional on_progress callback
through prepare()/up(); cli.py's `_up_with_progress` wires it into the
existing status spinner for `up`, `run`, and `test`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Prior investigation against the upstream cumbof/team repo and its published
PyPI wheel confirmed the bundled skills/ examples never shipped in the
package (only the loader did), and every one of upstream's own documented
skills is already faithfully preserved under the new architecture (3
markdown files byte-identical in examples/context/, 4 python skills'
tool names reproduced in examples/mcp/team_helpers_server.py). The one real
gap: the team.skills entry-point registry that let a third-party package
register a reusable, installable context bundle by name had no replacement
- extra_context: only accepted local relative paths.

team/skills.py is a new, much smaller module covering only that gap: a
name -> Markdown file resolver via the team.skills entry-point group (a
registered entry's module sets SKILL_FILE to its bundled .md path). Python
tool-loading, remote-URL fetching, and checksum verification stay retired
in favor of MCP servers. member.py's _load_extra_context() now falls back
to this registry when an extra_context: entry isn't a local file.

extension.py and cli.py's forge scaffold a matching skills/ slot alongside
servers/, with a `skills` subcommand listing a package's registered
Markdown bundles (mirroring `servers` for MCP). Also fixes a pre-existing
cosmetic bug in the servers/skills CLI hints where an escaped \n printed
literally instead of a newline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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