Download mcp-server/server.py from joduor/adaptive-model: direct link, hf CLI and curl.
- Browser
- Download file 3.69 kB
-
https://huggingface.co/joduor/adaptive-model/resolve/main/mcp-server/server.py
- Command line
-
hf download hf://joduor/adaptive-model/mcp-server/server.py
-
curl -L -o server.py https://huggingface.co/joduor/adaptive-model/resolve/main/mcp-server/server.py
3.69 kB
| """ | |
| MCP server β adaptive UI surface (Path B). | |
| Tools return structuredContent that the adaptive.html widget renders | |
| inline in MCP Apps-compatible hosts (ChatGPT, Claude, Goose, VS Code). | |
| Run: | |
| python -m mcp_server.server # stdio (local MCP client) | |
| python mcp-server/server.py --http # SSE on :3100 (remote clients) | |
| """ | |
| import json | |
| import os | |
| import sys | |
| from pathlib import Path | |
| import httpx | |
| from dotenv import load_dotenv | |
| from mcp.server.fastmcp import FastMCP | |
| load_dotenv() | |
| sys.path.insert(0, str(Path(__file__).parent.parent)) | |
| from lib.router import route | |
| HF_ENDPOINT_URL = os.getenv("HF_ENDPOINT_URL", "") | |
| HF_TOKEN = os.getenv("HF_TOKEN", "") | |
| WIDGET_PATH = Path(__file__).parent / "widget" / "adaptive.html" | |
| mcp = FastMCP("adaptive-model", version="1.0.0") | |
| # ββ widget resource βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # MCP Apps hosts load this URI into an iframe when the tool result carries | |
| # a _widget key in structuredContent. | |
| def adaptive_widget() -> str: | |
| return WIDGET_PATH.read_text(encoding="utf-8") | |
| # ββ tools βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async def respond(messages: list[dict], mode: str = "auto") -> dict: | |
| """ | |
| Run the adaptive model for the current conversation turn. | |
| Args: | |
| messages: Full conversation history, e.g. | |
| [{"role": "user", "content": "Show me revenue trends"}] | |
| mode: Adapter override β 'support' | 'analytics' | 'form' | 'auto' | |
| Returns a dict with: | |
| content β text for the model to reason over | |
| structuredContent β ui_spec for the widget to render interactively | |
| """ | |
| resolved = route(messages, None if mode == "auto" else mode) | |
| headers = {"Content-Type": "application/json"} | |
| if HF_TOKEN: | |
| headers["Authorization"] = f"Bearer {HF_TOKEN}" | |
| async with httpx.AsyncClient(timeout=60.0) as client: | |
| r = await client.post( | |
| HF_ENDPOINT_URL, | |
| json={"inputs": {"messages": messages, "mode": resolved}}, | |
| headers=headers, | |
| ) | |
| r.raise_for_status() | |
| out = r.json() | |
| text = out.get("text", "") | |
| ui_spec = out.get("ui_spec") | |
| result: dict = { | |
| "content": [{"type": "text", "text": text}], | |
| } | |
| if ui_spec: | |
| # _widget tells MCP Apps hosts which resource URI to iframe | |
| result["structuredContent"] = {**ui_spec, "_widget": "ui://widget/adaptive.html"} | |
| return result | |
| async def classify_mode(messages: list[dict]) -> dict: | |
| """ | |
| Return which adapter would handle this turn without running inference. | |
| Useful for debugging routing decisions. | |
| """ | |
| adapter = route(messages, None) | |
| return {"adapter": adapter} | |
| # ββ entry-point βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if __name__ == "__main__": | |
| import argparse | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--http", action="store_true", | |
| help="Serve over SSE on port 3100 instead of stdio") | |
| args = parser.parse_args() | |
| if args.http: | |
| mcp.run(transport="sse", host="0.0.0.0", port=3100) | |
| else: | |
| mcp.run(transport="stdio") | |