Concept · MCP-native architecture

Built for agents from the first commit.

KAOS speaks the Model Context Protocol (MCP) — Anthropic's open standard for how language-model agents discover and call external tools. Most frameworks bolt MCP on through an adapter layer. KAOS makes MCP the type system: a tool class is an MCP tool, a resource class is an MCP resource, and serializing one to the wire is a single method call. That decision is what keeps the 14 servers and 200+ tools behaving consistently.

The architectural choice

MCP defines a wire format for tools, resources, prompts, content, sampling, and tasks. A typical platform implements its own internal types and writes an "export to MCP" adapter on top. The adapter is always second-class: it lags behind protocol changes, it can't carry tool annotations the internal types didn't anticipate, and it adds another round of serialization at every call.

KAOS turns the protocol into the type system. The base tool class carries the same metadata, parameter schema, and approval-hint annotations the protocol itself defines, and a single method emits the wire envelope FastMCP — the open-source MCP server library KAOS uses — expects. Nothing sits between your class definition and the bytes a Claude Code, Codex, or Cursor client receives.

That choice cascades into a small set of opinions every package follows. Large bodies move through artifact handles rather than inline payloads, so a 50-page contract never floods a tool result. Every runtime carries a virtual file system whose manifest survives restarts. There are no process-global singletons; the runtime is passed in as a constructor argument so tests stay isolated. Module settings extend a typed base class with a documented resolution order from defaults through environment to overrides.

From class definition to wire bytes

The class is the source of truth from your editor down to the bytes on the wire. No adapter layer in between.

KaosTool subclass + metadata parameter_schema to_mcp_dict() protocol-native serializer KaosRuntime tools.register_tool() tools registry FastMCP stdio | HTTP wire

What it looks like in code

A tool an M&A associate would actually use — pull every change-of-control clause from a contract for the diligence flagged-issues table. The metadata, parameter schema, and approval-hint annotations are the same types the MCP specification defines. The serialize call below produces the envelope the protocol expects.

from typing import Any
from kaos_core import KaosRuntime, KaosTool, ToolMetadata, ToolResult
from kaos_core.types.annotations import ToolAnnotations
from kaos_core.types.enums import ToolCapability, ToolCategory
from kaos_core.types.parameters import ParameterSchema
class ExtractChangeOfControlTool(KaosTool):
"""Extract every change-of-control clause from a contract."""
@property
def metadata(self) -> ToolMetadata:
return ToolMetadata(
name="kaos-diligence-change-of-control",
display_name="Extract Change-of-Control Clauses",
description=(
"Find clauses that grant the counterparty a termination right, "
"consent requirement, payment acceleration, or pricing loss "
"on a change of control of the contracting entity. Returns "
"verbatim quotes with citation anchors back to the source."
),
category=ToolCategory.DOCUMENT,
capability=ToolCapability.EXTRACT,
module_name="kaos-diligence",
version="0.1.0",
annotations=ToolAnnotations(
readOnlyHint=True,
idempotentHint=True,
openWorldHint=False,
),
input_schema=[
ParameterSchema(
name="document_uri",
type="string",
description="Artifact handle for the parsed contract.",
required=True,
),
],
)
async def execute(
self, inputs: dict[str, Any], context: Any | None = None
) -> ToolResult:
uri = inputs.get("document_uri", "")
if not uri:
return ToolResult.create_error(
"Missing 'document_uri'. Fix: pass the artifact URI returned "
"by kaos-pdf-parse. Alternative: kaos-core-vfs-list to browse."
)
# ... extractor logic produces a list of clause hits
return ToolResult.create_success(output={"clauses": []})
# Wire it into a runtime and serialize the discovery envelope.
runtime = KaosRuntime()
runtime.tools.register_tool(ExtractChangeOfControlTool())
print(runtime.tools.get_tool("kaos-diligence-change-of-control").metadata.to_mcp_dict())

What the protocol-as-type-system buys

Claude Code, Codex CLI, Gemini CLI, VS Code, and Cursor introspect the tool surface over MCP without any extra configuration. A read-only hint on a tool tells the host's permission policy the call is safe to auto-run; a destructive hint forces a confirmation prompt. Those approval cues ride on the wire envelope, not in a sidecar config file the host has to discover separately.

Resource handles let an agent ask for derived views of a parsed document — a Markdown rendering, a section outline, a single page — by URI rather than by RPC. A pre-shipped set of those URI patterns covers the typed document and tabular artifacts so a client doesn't need to write its own. A small set of workflow prompts surfaces as slash commands in VS Code and interactive prompts in Claude Code, and cost and telemetry events ride the same envelope, so per-tool spend reporting is one aggregator call away.

Read next

The runtime page covers the in-process registry and the MCP server bridge in detail. The document AST concept shows the typed shape every extractor produces. The dependency-layering concept explains why the core package sits below everything else.

On learn-kaos: the-mcp-bridge · serve-over-mcp.