Skip to content

Serve tools over MCP

KAOS is MCP-native, so any runtime you build can be served to an AI client — Claude Code, Codex, or your own — with no extra wiring. kaos-mcp is the bridge: it exposes whatever tools you mount on it.

This example registers tools on a runtime and builds the bridge, showing exactly which tools it would serve. (It doesn’t start the long-running server, so it stays offline; in practice you’d call server.run_stdio() or serve over --http.)

Terminal window
uv run examples/serve-over-mcp.py
runtime has 2 tool(s): ['kaos-demo-echo', 'kaos-demo-wordcount']
bridge built; to serve them, call server.run_stdio() (or --http).
would expose over MCP: ['kaos-demo-echo', 'kaos-demo-wordcount']
examples/serve-over-mcp.py
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.13"
# dependencies = ["kaos-mcp>=0.1.3,<0.2", "kaos-core>=0.1.4,<0.2"]
# ///
"""Expose a KAOS runtime's tools over the Model Context Protocol.
`kaos-mcp` is the bridge that serves *any* runtime's tools to an MCP client
(Claude Code, Codex, ...). It holds no tools of its own — it exposes whatever you
mount on it. This example registers tools on a runtime and builds the bridge,
showing exactly which tools it would serve. (It doesn't start the long-running
server, so it stays a one-shot offline example; in practice you'd call
`server.run_stdio()` or `--http`.)
Run it:
uv run examples/serve-over-mcp.py
"""
from __future__ import annotations
from kaos_core import (
KaosRuntime,
ToolAnnotations,
ToolCapability,
ToolCategory,
kaos_tool,
)
from kaos_mcp import KaosMCPServer
@kaos_tool(
name="kaos-demo-echo",
description="Echo the input text.",
category=ToolCategory.DATA,
capability=ToolCapability.TRANSFORM,
annotations=ToolAnnotations(readOnlyHint=True),
auto_register=False,
)
async def echo(text: str) -> str:
return text
@kaos_tool(
name="kaos-demo-wordcount",
description="Count words.",
category=ToolCategory.DATA,
capability=ToolCapability.ANALYZE,
annotations=ToolAnnotations(readOnlyHint=True),
auto_register=False,
)
async def word_count(text: str) -> int:
return len(text.split())
def main() -> list[str]:
runtime = KaosRuntime()
runtime.tools.register_tool(echo)
runtime.tools.register_tool(word_count)
tool_names = runtime.tools.list_tools()
print(f"runtime has {len(tool_names)} tool(s): {tool_names}")
# Mount the runtime on the MCP bridge — these tools become available to any
# MCP client once you run the server.
server = KaosMCPServer(runtime)
print("bridge built; to serve them, call server.run_stdio() (or --http).")
print(f"would expose over MCP: {tool_names}")
assert server is not None
return tool_names
if __name__ == "__main__":
names = main()
assert "kaos-demo-echo" in names
assert "kaos-demo-wordcount" in names
assert len(names) == 2
  • The bridge holds no tools of its own. KaosMCPServer(runtime) exposes your runtime’s tools — load kaos-pdf + kaos-source, the full agent stack, or your own tools, and that’s exactly what the client sees.
  • Two transports. run_stdio() for desktop AI clients that spawn a subprocess; run_streamable_http() (--http) for network serving — which requires an auth token.
  • Tool annotations carry over. A readOnlyHint tool an agent would auto-allow is exposed with that metadata intact, so the consuming client can reason about safety.

The default for AI clients that spawn a subprocess (Claude Code, Codex).

Terminal window
kaos-agents-serve
# or, in code: KaosMCPServer(runtime).run_stdio()

No network exposure — the client launches and talks to it over stdin/stdout. See connect an AI tool.

Each package also ships its own server (kaos-pdf-serve, kaos-agents-serve, …) — those are kaos-mcp mounting that package’s runtime for you. Point a serve command at an MCP client to make KAOS tools available inside it. See the MCP tools reference for what each package exposes.