Skip to content

Replace FastMCP with toolregistry-server: multi-protocol, profiles, policy tags#63

Open
Oaklight wants to merge 2 commits into
UXARRAY:mainfrom
Oaklight:port-toolregistry-server
Open

Replace FastMCP with toolregistry-server: multi-protocol, profiles, policy tags#63
Oaklight wants to merge 2 commits into
UXARRAY:mainfrom
Oaklight:port-toolregistry-server

Conversation

@Oaklight

Copy link
Copy Markdown
Collaborator

Summary

Replace the FastMCP-based server.py with a toolregistry + toolregistry-server implementation. Same tool functions, same Claude Desktop experience by default, but the server now supports:

  • Multi-transport MCP (stdio / SSE / streamable HTTP)
  • Optional OpenAPI / REST surface (pip install uxarray-mcp[openapi])
  • Namespace grouping (session/, hpc/, io/, prompt/, compute/, ...)
  • Two profiles: conservative core default and opt-in deferred-full with BM25 discovery
  • Policy tags on every tool from day one (READ_ONLY, FILE_SYSTEM, NETWORK, SLOW, experimental, stateful)
  • Admin panel for live tool management (enable/disable/inspect)

fastmcp is no longer a dependency. No tool implementation code (tools/, domain/, remote/) was modified — provenance, HPC dispatch, session/workflow state, and fallback semantics are all unchanged.

What changed

File Change
src/uxarray_mcp/registry.py NEWbuild_registry(profile=...) with namespace plan, policy tags, BM25 search hints, prompt-as-tool wiring
src/uxarray_mcp/server.py Rewritemake_registry(), make_mcp_server(), run(transport=...) replacing FastMCP
src/uxarray_mcp/__init__.py Export make_registry/make_mcp_server instead of mcp
src/uxarray_mcp/cli.py serve gains --profile, --transport, --host, --port
pyproject.toml fastmcptoolregistry + toolregistry-server[mcp]; added [openapi] optional extra
tests/test_server.py Rewrite — 18 tests covering profile shape, tags, prompts, live call, MCP server construction
tests/test_vector_calc.py 2 tests adapted from from server import mcp to make_registry()
AGENTS.md Full rewrite for new architecture
CHANGELOG.md Added [Unreleased] section
CONTRIBUTING.md fastmcptoolregistry
conda/recipe/meta.yaml Python 3.12, fastmcptoolregistry deps
docs/conf.py Removed fastmcp autodoc mock

Two profiles

core (default, 27 tools visible)

(top level)  11  — original gateway tools (get_capabilities, analyze_dataset, ...)
session/      8  — create_session, register_dataset, get/reset_session_state, ...
hpc/          4  — endpoint_status, get/set_execution_mode, validate_hpc_setup
io/           1  — list_datasets (read-only, no front-door equivalent)
prompt/       3  — first_look, vorticity_analysis, hpc_diagnose

export_to_netcdf/export_to_csv are reachable in core via run_analysis(operation="export", ...).

deferred-full (58 loaded, 28 visible + 30 deferred)

Core set stays visible. 30 raw implementation tools (compute/, shape/, inspect/, plot/, io/, agent/) loaded with defer=True. Agents find them via discover_tools (BM25 search). Operators promote from admin panel.

Prompt → tool conversion

The 3 @mcp.prompt() decorators are now regular tools under prompt/ namespace. They return the same instruction text as before — the LLM calls prompt-first_look(path="...") and gets a multi-step analysis plan to execute.

Backward compatibility

  • uxarray-mcp serve still starts MCP stdio with the same default tool surface
  • Claude Desktop mcpServers snippets work unchanged
  • python -m uxarray_mcp still defaults to serve
  • All 54 tool functions in uxarray_mcp.tools behave identically

New capabilities are opt-in:

uxarray-mcp serve --profile deferred-full
uxarray-mcp serve --transport sse --port 8001

Test results

301 passed, 3 failed (pre-existing upstream shapely bug), 5 skipped

The 3 failures (test_plot_mesh_healpix, test_plot_mesh_healpix_custom_size, test_analyze_dataset_includes_plots_by_default) reproduce identically on upstream main — they are a shapely GEOSException unrelated to this PR.

Design decisions (discussed via Slack)

  • Core surface = current front-door + control/status + list_datasets + prompts per maintainer spec
  • run_scientific_agent stays deferred/experimental until explicitly promoted
  • export_to_* stays behind run_analysis dispatcher in core; direct calls in deferred
  • Policy tags assigned based on function signatures (use_remoteNETWORK) and explicit overrides
  • discover_tools default top_k controlled by the calling LLM; BM25 search hints added for domain synonyms (e.g. "vorticity" → calculate_curl)

How to try

pip install -e .
uxarray-mcp serve --help
# or:
python -c "from uxarray_mcp.server import make_registry; r = make_registry(); print(r.list_tools())"

Swap the server engine from FastMCP to toolregistry + toolregistry-server,
enabling multi-transport MCP (stdio/SSE/HTTP), optional OpenAPI/REST,
namespace grouping, two-profile tool surface (core / deferred-full),
policy tags, and BM25 discovery.

No tool implementation code (tools/, domain/, remote/) is modified.
Provenance, HPC dispatch, session/workflow state, and fallback semantics
are all preserved.

Closes #2
@Oaklight Oaklight requested a review from rajeeja June 15, 2026 07:43
@rajeeja

rajeeja commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Hey Peng, thanks so much for putting this together. Really nice that this removes the fastmcp dependency and goes to the Anthropic MCP SDK directly — cleaner dependency chain overall. Ran it locally end-to-end: 304 tests pass, both packages install cleanly from PyPI, admin panel defaults to localhost, and discover_tools is working nicely. Few things before we merge:

One thing that needs a decision:
The namespace prefixes show up on the MCP wire — Claude sees session-create_session, hpc-endpoint_status etc. instead of the flat names from before. For the 11 gateway tools that's fine (they're already flat), but the session/hpc tools are new names from the client's perspective. Does toolregistry have a way to keep namespaces internally but expose flat names over MCP? Or is the intent that clients just adapt to the prefixed names?

Smaller things:

  • This feels like a 0.2.0 bump given we're replacing the server engine — what do you think?
  • discover_tools("compute vorticity wind curl") returns prompt-vorticity_analysis at rank 1 and compute-calculate_curl at rank 2. The compute tool should win that query — probably just needs a BM25 hint tweak in registry.py.
  • README and docs/remote-hpc.md still mention fastmcp in a couple spots — happy to clean that up in a follow-up if easier.
  • How are you thinking about toolregistry-server longer term? If something needs patching on our end are you the right person to ping, or should we plan to fork it eventually?

@rljacob

rljacob commented Jun 15, 2026

Copy link
Copy Markdown
Member

Is this targeted to work only with claude code?

- Fix discover_tools ranking: enrich search hints for compute tools,
  reword prompt-tool docstrings to avoid keyword overlap with the
  actual computation tools they describe.
- Bump version to 0.2.0 (pyproject.toml + __init__.py).
- Clean remaining FastMCP references in docs/architecture.md and
  code comments in execution_control.py / remote_tools.py.
@Oaklight

Copy link
Copy Markdown
Collaborator Author

Thanks for running it end-to-end and the detailed feedback. Pushed a follow-up commit addressing everything below.

Namespace prefixes on the wire:

Keeping the prefixed names (session-create_session, hpc-endpoint_status, etc.) is intentional — they give LLMs clearer context about what a tool does, and the admin panel / policy filters use the namespace to group and gate tools. That said, if a specific deployment needs flat names, toolregistry supports registering without a namespace — just pass namespace=None in registry.py. See: https://toolregistry.readthedocs.io/en/latest/usage/tool_management/

For now I'd leave the prefixes as-is and see if any client actually has trouble with them. LLMs handle hpc-endpoint_status just fine — it's arguably more self-documenting than bare endpoint_status.

Version bump:

Bumped to 0.2.0 in both pyproject.toml and __init__.py.

BM25 ranking fix:

Good catch. The issue was that prompt-vorticity_analysis had "vorticity", "wind", and "curl" all over its name + docstring, while calculate_curl's search hints were too narrow. Fixed two things:

  • Enriched _SEARCH_HINTS for calculate_curl (and other compute tools) with the domain terms users actually type
  • Rewrote the vorticity_analysis prompt tool's docstring to describe what it is (a plan generator) rather than echoing the same computation keywords

Before: prompt-vorticity_analysis rank 1 (26.3) > compute-calculate_curl rank 2 (24.3)
After: compute-calculate_curl rank 1 (26.1) > prompt-vorticity_analysis rank 4 (7.5)

Docs cleanup:

Cleaned up remaining fastmcp references in docs/architecture.md, execution_control.py, and remote_tools.py comments.

toolregistry-server maintenance:

I maintain the toolregistry ecosystem (toolregistry, toolregistry-server, toolregistry-hub) including PyPI releases and Docker images. For any issues or patches, open an issue or PR on the relevant repo — happy to discuss and review. No need to plan for a fork.

@Oaklight

Oaklight commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

Is this targeted to work only with claude code?

Hey @rljacob Robert, good to see you here!

Not Claude-only — toolregistry-server is designed to expose the same tool set over multiple protocols from one process:

  • MCP (stdio / SSE / streamable HTTP) — used by Claude Code, Codex CLI, OpenCode, and other MCP-compatible agent platforms
  • OpenAPI / REST — used by cloud services, custom agent environments, chat UIs, or anything that can send HTTP requests (pip install uxarray-mcp[openapi])

Users pick whichever protocol fits their stack. The tool implementations, provenance, and HPC dispatch are shared across all transports.

@Oaklight Oaklight closed this Jun 16, 2026
@Oaklight Oaklight reopened this Jun 16, 2026
@rajeeja rajeeja requested a review from rljacob June 16, 2026 04:27
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.

3 participants