API Reference

MCPServer

The main server class. Create an instance, register tools/resources/prompts with decorators, then call run().

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:

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:

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.

@server.tool()
def add(a, b):
    # type: (float, float) -> float
    """Add two numbers together."""
    return float(a) + float(b)

With explicit options:

@server.tool(name="custom_name", description="Custom description", tags={"math"})
def my_func(a, b):
    return a + b

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).

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:

{
  "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:

{ "status": "done", "job_id": "...", "result": "..." }

Or on failure:

{ "status": "error", "job_id": "...", "error": "..." }

The decorator accepts the same parameters as @server.tool():

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.

@server.resource("config://calculator/settings")
def get_settings():
    """Get calculator settings."""
    return {"precision": 10}

With MIME type:

@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]}

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.

@server.prompt()
def calculate(expression):
    # type: (str) -> list
    """Generate a calculation prompt."""
    return [
        {
            "role": "user",
            "content": {"type": "text", "text": "Please calculate: {}".format(expression)}
        }
    ]

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:

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:

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.