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 |
|---|---|---|---|
|
str |
required |
Server name (appears in MCP discovery and server info) |
|
str |
|
Server version |
``server.run()`` parameters:
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
str |
|
Host to bind to |
|
int |
|
Port to listen on |
|
str |
|
URL prefix for proxy environments (e.g. |
@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 |
|---|---|---|---|
|
str |
function name |
Tool name |
|
str |
docstring |
Tool description |
|
set |
|
Tags for categorization |
|
dict |
|
Metadata dictionary |
|
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 |
|---|---|---|---|
|
str |
function name |
Tool name |
|
str |
docstring |
Tool description |
|
set |
|
Tags for categorization |
|
dict |
|
Metadata dictionary |
|
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 |
|---|---|---|---|
|
str |
required |
Resource URI (e.g. |
|
str |
function name |
Resource name |
|
str |
docstring |
Resource description |
|
str |
|
MIME type of content |
|
set |
|
Tags for categorization |
|
dict |
|
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 |
|---|---|---|---|
|
str |
function name |
Prompt name |
|
str |
docstring |
Prompt description |
|
set |
|
Tags for categorization |
|
dict |
|
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 |
|---|---|
|
Log a debug-level message |
|
Log an info-level message |
|
Log a warning-level message |
|
Log an error-level message |
|
Report progress to connected clients |
|
Request form-mode user input through a client that declared the |
|
Request URL-mode elicitation for sensitive or out-of-band flows |
|
Request LLM sampling from a client that declared the |
|
Request roots from a client that declared the |
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.