stdio transport¶
The default and most common transport. The server reads JSON-RPC messages from stdin and writes responses to stdout. Designed for tight integration with a local MCP host (Claude Desktop, Cline, custom agents).
Run¶
uv run python -m novelai_image_mcp serve
# or explicitly:
MCP_TRANSPORT=stdio uv run python -m novelai_image_mcp serve
The server runs in the foreground. Press Ctrl+C to exit.
Agent configuration¶
Claude Desktop¶
Edit claude_desktop_config.json:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"novelai-image": {
"type": "stdio",
"command": "uv",
"args": [
"run",
"--directory",
"/path/to/NovelAI-Image-MCP",
"python",
"-m",
"novelai_image_mcp",
"serve"
],
"env": {
"NOVELAI_TOKEN": "${input:novelai_token}"
}
}
}
}
${input:novelai_token} resolves to a host-managed secret. For a one-off
test, inline a literal token ("pst-...") instead.
Shorthand: uvx (installed package)¶
{
"mcpServers": {
"novelai-image": {
"command": "uvx",
"args": ["--prerelease=allow", "novelai-image-mcp", "serve"],
"env": { "NOVELAI_TOKEN": "pst-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}
Set NOVELAI_TOKEN in the host shell — uvx inherits the parent env.
Restart Claude Desktop. You’ll see a novelai-image MCP server registered
with 11 tools.
Cline (VS Code)¶
Add to cline_mcp_settings.json:
{
"mcpServers": {
"novelai-image": {
"type": "stdio",
"command": "uv",
"args": [
"run", "--directory", "/path/to/NovelAI-Image-MCP",
"python", "-m", "novelai_image_mcp", "serve"
],
"env": { "NOVELAI_TOKEN": "pst-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" },
"disabled": false,
"autoApprove": []
}
}
}
Custom Python agent¶
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
params = StdioServerParameters(
command="uv",
args=["run", "--directory", "/path/to/NovelAI-Image-MCP",
"python", "-m", "novelai_image_mcp", "serve"],
env={"NOVELAI_TOKEN": "pst-..."},
)
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print([t.name for t in tools])
asyncio.run(main())
Why stdio?¶
No network overhead — no TCP handshake, no HTTP framing.
No port conflicts — the server doesn’t bind to anything.
Process lifecycle is automatic — the agent owns the process; when the agent exits, the server exits.
Stdin/stdout are universally supported across MCP SDKs.
Limitations¶
Single client only. Two agents cannot share one stdio server.
The server’s stderr is captured by the agent as logs. Use
logging(configured to stderr) for diagnostics; never print to stdout (it corrupts the JSON-RPC stream).
Logging¶
The MCP runtime writes all logs to stderr. To see them in a separate terminal:
uv run python -m novelai_image_mcp serve 2>server.log
To forward logs to the agent (e.g. for Claude Desktop’s “MCP logs” panel), configure the runtime’s logging handler per the MCP SDK docs.