nn-mcp-stdio
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nn-mcp-stdiobuild a simple server with one tool"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
nn-mcp-stdio -- No nonsense stdio MCP server framework
Build a Model Context Protocol server
(protocol 2025-11-25) by writing plain async def handlers and decorating
them. You annotate the arguments; the server derives the tool's inputSchema,
validates every call strictly, and hands your handler natural keyword
arguments. No pydantic, no code generation, no config.
Goals
A stdio MCP server, specification-conformant.
Strict validation. Arguments are checked against the generated JSON Schema with
jsonschema-- and never coerced.{"n": "3"}for anintis an error, not a silent3.Annotations and docstrings are the source of truth -- one obvious place for each fact, no duplication to keep in sync.
Related MCP server: Easy MCP Server
Non-Goals
HTTP, websocket (stdio only).
Dependency injection.
Storage layers.
Install
pip install nn-mcp-stdioRuntime dependencies: nn-mcp-types (MCP dataclasses + schema generation),
jsonschema (validation), and aiojobs (the handler scheduler).
Implementation overview
The server is a small asyncio pipeline: a single reader parses each stdin
line and dispatches by message shape; an aiojobs scheduler runs handlers
concurrently and in isolation; each handler enqueues its reply on an outbound
queue that a single writer drains to stdout. Logging goes to stderr, so it
never corrupts the protocol stream.
A tool wires three pieces of nn-mcp-types together: your handler's
signature becomes a synthesised dataclass (parameters become fields,
defaults and Annotated metadata carried through); that dataclass becomes the
tool's inputSchema (JSON Schema 2020-12); the handler's docstring
becomes the tool description and its name the tool name. On tools/call the
arguments dict is validated against the schema, reconstructed into the typed
dataclass, and the handler is called with natural kwargs.
Example
One server exposing a tool, two fixed resources (a literal and a file), a dynamic resource read on demand, and a URI-template resource:
import asyncio
import json
import pathlib
import typing
from nn_mcp_stdio import Context, Server
from nn_mcp_types import resources
from nn_mcp_types.content import TextResourceContents
from nn_mcp_types.schema import SchemaAnnotation
server = Server(name="demo", version="0.1.0")
@server.tool()
async def crop(
url: typing.Annotated[str, SchemaAnnotation(description="Image URL")],
width: typing.Annotated[
int, SchemaAnnotation(description="Target width (px)", minimum=1)
] = 800,
ctx: Context = None,
) -> str:
"""Crop the image at `url` to `width` pixels."""
await ctx.info(f"cropping {url} to {width}px", logger="crop")
return f"cropped {url} to {width}px"
# A literal resource: contents fixed in code, served on every read.
server.add_resource_from_literal(
resources.Resource(
uri="config://service",
name="config",
title="The service configuration",
mime_type="application/json",
),
json.dumps({"debug": True}),
)
# A file resource: read lazily on each read, so edits are reflected.
server.add_resource_from_path(
resources.Resource(uri="file:///README.md", name="readme"),
pathlib.Path("README.md"),
describe_contents=lambda path, data: ("text/markdown", TextResourceContents),
)
# A dynamic resource: a reader computes the contents on each read.
@server.resource(
resources.Resource(
uri="clock://now",
name="clock",
mime_type="text/plain",
)
)
async def clock() -> str:
import datetime
return datetime.datetime.now().isoformat()
# A resource template: a URI shape read on demand. The client expands the
# RFC 6570 template; a read routes back here with `{name}` extracted.
@server.resource_template(
resources.ResourceTemplate(
uri_template="greeting://{name}",
name="greeting",
mime_type="text/plain",
)
)
async def greeting(name: str) -> str:
return f"Hello, {name}!"
asyncio.run(server.run())Documentation
Guides to using each building block effectively:
Tools -- annotated handlers, per-argument constraints, structured output, return types, and errors.
Resources -- dynamic readers, the fixed literal/file registrations, and URI-template resources.
Context -- logging and progress back to the client mid-call.
Other handlers -- registering raw requests and notifications with
@server.request/@server.notification.
This server cannot be deployed
Maintenance
Related MCP Connectors
An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform
MCP Spec Compliance MCP — audits any MCP server.json against the official Model Context Protocol
Primarily to be used as a template repository for developing MCP servers with FastMCP in Python, P…
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Related MCP Servers
- AlicenseBqualityDmaintenanceA Python library to build MCP servers with decorators, auto-generating JSON Schema from type hints and including built-in filesystem and HTTP servers.3MIT
- AlicenseNot gradedqualityDmaintenanceA simple toolkit for creating MCP servers with stdio and SSE transport, auto-validating tools via Pydantic.MIT
- AlicenseNot gradedqualityDmaintenanceEnables creating MCP (Model Context Protocol) servers with zero boilerplate, full TypeScript support, and multiple transports (stdio and HTTP).5 npm1MIT
- AlicenseNot gradedqualityBmaintenanceEnables building lightweight MCP servers where Rust handles transport, routing, and schema validation while Python defines tool behavior using type hints, with support for stdio and HTTP transports.MIT