API Reference ============= MCPServer --------- The main server class. Create an instance, register tools/resources/prompts with decorators, then call ``run()``. .. code-block:: python from nanohubmcp import MCPServer server = MCPServer("my-server", version="1.0.0") server.run(host="0.0.0.0", port=8000, path_prefix="") **Constructor parameters:** .. list-table:: :header-rows: 1 :widths: 15 10 15 60 * - Parameter - Type - Default - Description * - ``name`` - str - required - Server name (appears in MCP discovery and server info) * - ``version`` - str - ``"1.0.0"`` - Server version **``server.run()`` parameters:** .. list-table:: :header-rows: 1 :widths: 15 10 15 60 * - Parameter - Type - Default - Description * - ``host`` - str - ``"0.0.0.0"`` - Host to bind to * - ``port`` - int - ``8000`` - Port to listen on * - ``path_prefix`` - str - ``""`` - URL prefix for proxy environments (e.g. ``/weber/.../``) @server.tool() --------------- Register a function as an MCP tool. The function name becomes the tool name, and the docstring becomes the description. Parameters are auto-detected from the function signature. .. code-block:: python @server.tool() def add(a, b): # type: (float, float) -> float """Add two numbers together.""" return float(a) + float(b) With explicit options: .. code-block:: python @server.tool(name="custom_name", description="Custom description", tags={"math"}) def my_func(a, b): return a + b .. list-table:: :header-rows: 1 :widths: 15 10 15 60 * - Parameter - Type - Default - Description * - ``name`` - str - function name - Tool name * - ``description`` - str - docstring - Tool description * - ``tags`` - set - ``None`` - Tags for categorization * - ``meta`` - dict - ``None`` - Metadata dictionary * - ``input_schema`` - dict - auto-generated - JSON Schema for inputs @server.async_tool() --------------------- Register a long-running function as an async MCP tool. When called, the server returns a ``job_id`` immediately (within milliseconds) instead of blocking the HTTP connection. The actual work runs in a background thread. The client polls for the result using the built-in ``get_job_result`` tool. Use this for any tool that may exceed the reverse proxy timeout (typically 30–60 seconds). .. code-block:: python from nanohubmcp import MCPServer server = MCPServer("my-server") @server.async_tool() def run_simulation(verilog_code, design_name): # type: (str, str) -> str """Run a long RTL-to-GDSII flow. Can take up to 10 minutes.""" # ... long-running work ... return result Calling ``run_simulation`` returns immediately: .. code-block:: json { "status": "running", "job_id": "550e8400-e29b-41d4-a716-446655440000", "message": "Job started. Poll with get_job_result(job_id=\"...\")" } The client then polls with the built-in ``get_job_result`` tool until complete: .. code-block:: json { "status": "done", "job_id": "...", "result": "..." } Or on failure: .. code-block:: json { "status": "error", "job_id": "...", "error": "..." } The decorator accepts the same parameters as ``@server.tool()``: .. list-table:: :header-rows: 1 :widths: 15 10 15 60 * - Parameter - Type - Default - Description * - ``name`` - str - function name - Tool name * - ``description`` - str - docstring - Tool description * - ``tags`` - set - ``None`` - Tags for categorization * - ``meta`` - dict - ``None`` - Metadata dictionary * - ``input_schema`` - dict - auto-generated - JSON Schema for inputs .. note:: Every server automatically registers a built-in ``get_job_result(job_id)`` tool. It is always present in ``tools/list`` and requires no configuration. @server.resource() ------------------- Register a function as an MCP resource. .. code-block:: python @server.resource("config://calculator/settings") def get_settings(): """Get calculator settings.""" return {"precision": 10} With MIME type: .. code-block:: python @server.resource("data://samples/temperatures", mime_type="application/json") def temperature_data(): """Monthly average temperatures.""" return {"data": [2.1, 3.5, 7.2, 12.1]} .. list-table:: :header-rows: 1 :widths: 15 10 15 60 * - Parameter - Type - Default - Description * - ``uri`` - str - required - Resource URI (e.g. ``config://settings``, ``file:///path``) * - ``name`` - str - function name - Resource name * - ``description`` - str - docstring - Resource description * - ``mime_type`` - str - ``None`` - MIME type of content * - ``tags`` - set - ``None`` - Tags for categorization * - ``meta`` - dict - ``None`` - Metadata dictionary @server.prompt() ----------------- Register a function as an MCP prompt template. .. code-block:: python @server.prompt() def calculate(expression): # type: (str) -> list """Generate a calculation prompt.""" return [ { "role": "user", "content": {"type": "text", "text": "Please calculate: {}".format(expression)} } ] .. list-table:: :header-rows: 1 :widths: 15 10 15 60 * - Parameter - Type - Default - Description * - ``name`` - str - function name - Prompt name * - ``description`` - str - docstring - Prompt description * - ``tags`` - set - ``None`` - Tags for categorization * - ``meta`` - dict - ``None`` - Metadata dictionary Context ------- Tools can receive a ``Context`` object for logging, progress reporting, and client interactions such as elicitation. Add a ``ctx`` (or ``context``) parameter as the first argument: .. code-block:: python from nanohubmcp import MCPServer, Context server = MCPServer("my-server") @server.tool() def power(ctx, base, exponent): # type: (Context, float, float) -> float """Raise base to the power of exponent.""" ctx.info("Computing {}^{}".format(base, exponent)) ctx.report_progress(0.5, total=1.0, message="Computing...") return float(base) ** float(exponent) @server.tool() def ask_for_label(ctx): # type: (Context) -> dict """Ask the MCP client to collect a label from the user.""" return ctx.elicit( "Enter a label", { "type": "object", "properties": { "label": {"type": "string", "title": "Label"} }, "required": ["label"] } ) **Context methods:** .. list-table:: :header-rows: 1 :widths: 40 60 * - Method - Description * - ``ctx.debug(msg)`` - Log a debug-level message * - ``ctx.info(msg)`` - Log an info-level message * - ``ctx.warning(msg)`` - Log a warning-level message * - ``ctx.error(msg)`` - Log an error-level message * - ``ctx.report_progress(progress, total=None, message=None)`` - Report progress to connected clients * - ``ctx.elicit(message, requested_schema=None, timeout=60)`` - Request form-mode user input through a client that declared the ``elicitation`` capability * - ``ctx.elicit_url(message, url, elicitation_id=None, timeout=60)`` - Request URL-mode elicitation for sensitive or out-of-band flows * - ``ctx.sample(params, timeout=60)`` - Request LLM sampling from a client that declared the ``sampling`` capability * - ``ctx.list_roots(timeout=60)`` - Request roots from a client that declared the ``roots`` capability .. note:: ``ctx.elicit(...)`` requires the client to initialize with the ``elicitation`` capability. Form-mode elicitation should only be used for non-sensitive data. Use ``ctx.elicit_url(...)`` when the user needs to enter credentials, API keys, tokens, payment details, or other secrets. Return Types ------------ Tool handlers can return: - **Scalar values** (``str``, ``int``, ``float``) — wrapped as ``{"content": [{"type": "text", "text": "..."}], "isError": false}`` - **Dictionaries** — JSON-serialized and wrapped as text content - **``ToolResult``** — returned as-is for full control Resource handlers can return: - **Dictionaries** — JSON-serialized as resource content - **Strings** — returned as text content - **``ResourceResult``** — returned as-is for full control Prompt handlers can return: - **List of message dicts** — used directly as prompt messages - **Strings** — wrapped in a user message - **``PromptResult``** — returned as-is for full control Raising an exception in a tool handler sets ``isError: true`` in the response.