Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ which provide:
Code, and any MCP-compatible client.
- **OpenAPI / REST** (optional extra) for curl, OpenAI Assistants,
Anthropic Messages API, Gemini, LangChain, plain scripts.
- **Python API** — `from uxarray_mcp.server import make_registry`.
- **Python API** — `from uxarray_mcp.app import make_registry`.

It supports local execution and optional remote execution on HPC clusters via
[Globus Compute](https://globus-compute.readthedocs.io/).
Expand Down
2 changes: 1 addition & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,6 @@ State
Server
------

.. automodule:: uxarray_mcp.server
.. automodule:: uxarray_mcp.app
:members:
:undoc-members:
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ Edit the config:
"run",
"python",
"-m",
"uxarray_mcp.server"
"uxarray_mcp"
]
}
}
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ license = { file = "LICENSE" }
# support and broaden requires-python back to >=3.11.
requires-python = ">=3.12,<3.13"
dependencies = [
"toolregistry-server[mcp]>=0.3.3",
"toolregistry-server[mcp]>=0.4.0",
"holoviews>=1.19.0",
"matplotlib>=3.9.0",
"pyyaml>=6.0",
Expand All @@ -34,7 +34,7 @@ dependencies = [

[project.optional-dependencies]
openapi = [
"toolregistry-server[openapi]>=0.3.3",
"toolregistry-server[openapi]>=0.4.0",
]
hpc = [
"academy-py>=0.3.1",
Expand Down
3 changes: 1 addition & 2 deletions src/uxarray_mcp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""UXarray MCP Server - AI tools for unstructured mesh analysis."""

from uxarray_mcp.server import make_mcp_server, make_registry
from uxarray_mcp.tools import inspect_mesh

__all__ = ["make_mcp_server", "make_registry", "inspect_mesh"]
__all__ = ["inspect_mesh"]
__version__ = "0.2.0"
77 changes: 77 additions & 0 deletions src/uxarray_mcp/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""UXarray application — subclass of toolregistry_server.App.

Provides :class:`UXarrayApp`, the central server application that builds
the UXarray tool registry and dispatches to protocol adapters (MCP, OpenAPI).

Identity (product name, version, description) flows automatically to
MCP server name, OpenAPI title, and CLI banner.

Example::

from uxarray_mcp.app import UXarrayApp

app = UXarrayApp()
app.serve_mcp(transport="stdio", profile="core")
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from toolregistry_server import ServerIdentity
from toolregistry_server.app import App

from . import __version__
from .registry import Profile

if TYPE_CHECKING:
from toolregistry import ToolRegistry

UXARRAY_IDENTITY = ServerIdentity(
name="UXarray MCP",
version=__version__,
description="Mesh analysis tools for AI agents",
)


class UXarrayApp(App):
"""UXarray-specific server application.

Overrides :meth:`prepare_registry` to build the UXarray tool
registry with profile-based tool surface selection.
"""

def __init__(self, identity: ServerIdentity | None = None) -> None:
super().__init__(identity=identity or UXARRAY_IDENTITY)

def prepare_registry(self, **kwargs) -> ToolRegistry:
"""Build the UXarray tool registry.

Keyword Args:
profile: Tool surface profile (``"core"`` or
``"deferred-full"``). Defaults to ``"core"``.
"""
from .registry import build_registry

profile = kwargs.get("profile", "core")
return build_registry(profile=profile)


# ---------------------------------------------------------------------------
# Convenience helpers for tests and scripts
# ---------------------------------------------------------------------------


def make_registry(*, profile: Profile = "core") -> ToolRegistry:
"""Build the tool registry for the requested profile."""
return UXarrayApp().prepare_registry(profile=profile)


def make_mcp_server(*, profile: Profile = "core"):
"""Build a configured MCP server ready for any transport."""
from toolregistry_server.adapters.mcp import route_table_to_mcp_server
from toolregistry_server.route_table import RouteTable

registry = make_registry(profile=profile)
route_table = RouteTable(registry)
return route_table_to_mcp_server(route_table, name="UXarray MCP")
43 changes: 39 additions & 4 deletions src/uxarray_mcp/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,29 @@ def _ensure_hpc_block(data: dict[str, Any]) -> dict[str, Any]:

def cmd_serve(args: argparse.Namespace) -> int:
"""Run the MCP server."""
from uxarray_mcp.server import run
from uxarray_mcp.app import UXarrayApp

run(
profile=getattr(args, "profile", "core"),
transport=getattr(args, "transport", "stdio"),
app = UXarrayApp()
transport = getattr(args, "transport", "stdio")
mcp_transport = "streamable-http" if transport == "http" else transport
app.serve_mcp(
transport=mcp_transport,
host=getattr(args, "host", "127.0.0.1"),
port=getattr(args, "port", 8001),
profile=getattr(args, "profile", "core"),
)
return 0


def cmd_openapi(args: argparse.Namespace) -> int:
"""Run the OpenAPI/REST server."""
from uxarray_mcp.app import UXarrayApp

app = UXarrayApp()
app.serve_openapi(
host=getattr(args, "host", "0.0.0.0"),
port=getattr(args, "port", 8000),
profile=getattr(args, "profile", "core"),
)
return 0

Expand Down Expand Up @@ -285,6 +301,7 @@ def build_parser() -> argparse.ArgumentParser:
)
sub = p.add_subparsers(dest="command", required=True)

# --- serve (MCP) ---
serve = sub.add_parser("serve", help="Run the MCP server.")
serve.add_argument(
"--profile",
Expand All @@ -306,6 +323,24 @@ def build_parser() -> argparse.ArgumentParser:
serve.add_argument("--port", type=int, default=8001, help="Port for SSE/HTTP.")
serve.set_defaults(func=cmd_serve)

# --- openapi ---
openapi = sub.add_parser("openapi", help="Run the OpenAPI/REST server.")
openapi.add_argument(
"--profile",
choices=("core", "deferred-full"),
default="core",
help=(
"core: gateway + control + list_datasets + prompts (~27 tools). "
"deferred-full: also load 30 raw tools as deferred, gated "
"behind discover_tools / admin promotion."
),
)
openapi.add_argument(
"--host", default="0.0.0.0", help="Bind host (default: 0.0.0.0)."
)
openapi.add_argument("--port", type=int, default=8000, help="Port (default: 8000).")
openapi.set_defaults(func=cmd_openapi)

setup = sub.add_parser("setup", help="Write a starter user config.")
setup.add_argument("--path", default=None, help="Override target path.")
setup.add_argument(
Expand Down
92 changes: 0 additions & 92 deletions src/uxarray_mcp/server.py

This file was deleted.

6 changes: 3 additions & 3 deletions tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

These tests replace the previous FastMCP-based assertions. They
exercise ``uxarray_mcp.registry.build_registry`` and
``uxarray_mcp.server.make_registry`` to confirm the tool surface
``uxarray_mcp.app.make_registry`` to confirm the tool surface
matches the agreed design spec.
"""

Expand All @@ -13,6 +13,7 @@

import pytest

from uxarray_mcp.app import make_mcp_server, make_registry
from uxarray_mcp.registry import (
_CONTROL_TOOLS,
_CORE_EXTRA_TOOLS,
Expand All @@ -21,7 +22,6 @@
FRONTDOOR_NAMES,
build_registry,
)
from uxarray_mcp.server import make_mcp_server, make_registry

EXPECTED_FRONTDOOR = 11
EXPECTED_CONTROL = 12 # 8 session + 4 hpc
Expand Down Expand Up @@ -298,4 +298,4 @@ def test_live_call_through_registry():
async def test_mcp_server_constructs():
"""make_mcp_server() returns a working MCP Server object."""
server = make_mcp_server(profile="core")
assert server.name == "uxarray-mcp-server"
assert server.name == "UXarray MCP"
4 changes: 2 additions & 2 deletions tests/test_vector_calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ def test_accepts_use_remote_endpoint_session_params(self):

def test_vector_calc_operations_available_through_run_analysis():
"""run_analysis must advertise vector calc operations in its description."""
from uxarray_mcp.server import make_registry
from uxarray_mcp.app import make_registry

registry = make_registry(profile="core")
tool = registry.get_tool("run_analysis")
Expand All @@ -343,7 +343,7 @@ def test_vector_calc_operations_available_through_run_analysis():

def test_prompts_registered_as_tools():
"""Former @mcp.prompt() decorators are now prompt/ namespace tools."""
from uxarray_mcp.server import make_registry
from uxarray_mcp.app import make_registry

registry = make_registry(profile="core")
tools = registry.list_tools()
Expand Down
Loading