Skip to main content

SchemaRouter

SchemaRouter

Schema-aware planning and execution for LLM tool ecosystems.

English · 한국어

CI Docs License: MIT

SchemaRouter compiles a natural-language request plus a registered capability catalog into a small, typed, auditable execution plan.

It goes beyond Query -> Tool routing:

Query
  -> Tool
  -> Endpoint
  -> Parameters
  -> Response fields
  -> Evidence / policy
  -> Schema validation
  -> Execute

SchemaRouter is intentionally narrower than LangChain or LangGraph. It is designed to sit at the tool-schema boundary between an agent and structured capability sources such as OpenAPI, MCP, OPTIMADE, Python callables, and third-party adapter protocols.

Status: 0.4.0 is the current public non-prerelease release. Install it from PyPI with pip install schemarouter. SchemaRouter remains pre-1.0, so deliberate compatibility changes may still occur in later 0.x minor releases under the documented versioning policy.

Why

As an agent gains more tools, choosing the tool is only one part of the problem. The runtime also needs to know:

  • which operation inside that tool is relevant;
  • which parameters are declared and valid;
  • which response fields should be retained;
  • whether the operation is read-only, mutating, destructive, or unclassified;
  • whether the schema changed after planning;
  • whether the raw tool result actually satisfies the declared contract.

SchemaRouter makes those decisions explicit.

Quickstart

from pydantic import BaseModel

from schemarouter import PlanRequest, SchemaRouter, schema_tool


class Weather(BaseModel):
    city: str
    temperature: float


@schema_tool(read_only=True)
def current_weather(city: str) -> Weather:
    return Weather(city=city, temperature=20.5)


router = SchemaRouter()
router.add_callable(current_weather)

results = router.invoke(
    PlanRequest(
        query="city temperature",
        arguments={"city": "Seoul"},
    )
)

print(results[0].data)

The same execution vocabulary works across capability sources:

router.invoke(request)
await router.ainvoke(request)

router.batch(requests)
await router.abatch(requests)

router.stream(request)
router.astream(request)
router.astream_events(request)

Bring your schema

OpenAPI

router = await SchemaRouter.from_url(
    "https://api.example.com/openapi.json",
    kind="openapi",
)

Cross-document OpenAPI $ref fetching stays off by default. Trusted callers can opt into bounded same-origin resolution:

router = await SchemaRouter.from_url(
    "https://api.example.com/openapi.json",
    kind="openapi",
    openapi_external_refs=True,
)

OPTIMADE

router = await SchemaRouter.from_url(
    "https://www.crystallography.net/cod/optimade",
    kind="optimade",
)

OPTIMADE entry schemas are discovered from /info/<entry_type>. Planned fields are translated into the protocol's response_fields query parameter before execution.

MCP

pip install "schemarouter[mcp]"
router = await SchemaRouter.from_url(
    "http://localhost:8000/mcp",
    kind="mcp",
)

Python

router.add_callable(my_typed_function)

Human-readable API docs

proposal = await router.inspect_url(
    "https://docs.example.com/api",
    model=documentation_model,
)

router.approve_proposal(
    proposal,
    base_url="https://api.example.com",
)

Human-readable documentation never becomes executable automatically. It first becomes an evidence-grounded proposal and then requires explicit approval.

Core guarantees

  • Schema-constrained planning — unknown tools, endpoints, parameters, and fields cannot become executable calls.
  • Runtime JSON Schema validation — validate arguments before invocation and raw output before projection.
  • Bounded nested projection — declared logical fields may map to explicit nested object paths without allowing model-produced JSONPath or undeclared field traversal.
  • Schema and binding drift detection — stale plans and stale transports fail closed.
  • Local execution authority — remote metadata and model output cannot grant mutation or destructive permissions.
  • Credential separation — schema-fetch credentials and runtime credentials stay in different channels; authenticated MCP keeps secrets in the trusted transport boundary.
  • Bounded OpenAPI external refs — cross-document $ref loading is explicit opt-in, confined to the entry-document origin, and bounded by redirect/depth/document/byte limits.
  • Read-only retries by default — contract violations are never retried.
  • Per-call approval and execution budgets — trusted local callbacks and deterministic call, attempt, remote, time, quota, and cost-unit limits fail closed.
  • Trusted execution hooks — ordered sync/async before/after hooks receive detached snapshots, cannot transform calls/results, and fail closed without turning hook failures into tool retries.
  • OpenAPI compatibility reporting — partial/unsupported constructs are machine-readable instead of silently reinterpreted.
  • Redacted runtime events by default — payload tracing is opt-in.
  • Replayable persistent traces — SQLiteRunTraceStore can persist validated event streams and replay them later without re-running planners, network calls, or tools.
  • Persistent/pluggable registry — use the built-in transactional SQLiteRegistry or inject a custom implementation of the public ToolRegistry protocol. Persistent catalog state never serializes trusted invokers or credentials.
  • Pluggable source adapters — AdapterRegistry lets structured protocols compile into the same ToolSpec / EndpointSpec execution model; installed entry-point plugins require an explicit allowlist before import.
  • Optional OpenTelemetry export — redacted runtime events can become parented run/tool spans without exporting payload values.

With LangChain

pip install "schemarouter[langchain]"
from schemarouter.integrations import to_langchain_tools

tools = to_langchain_tools(router)

Execution still flows through SchemaRouter's policy, fingerprint, input, and output validation.

With LangGraph

pip install "schemarouter[langgraph]"
from schemarouter.integrations import to_langgraph_node

builder.add_node("schema_router", to_langgraph_node(router))

The node supports sync/async StateGraph execution and returns checkpoint-friendly partial state updates while SchemaRouter retains planning, policy, and validated execution authority.

With LlamaIndex

Install the packaged LlamaIndex bridge:

pip install "schemarouter[llamaindex]"
from schemarouter.integrations import to_llamaindex_tools

tools = to_llamaindex_tools(router)

LlamaIndex remains the agent/workflow layer; SchemaRouter retains schema identity, validation, and endpoint execution.

Experimental bounded decisions

SchemaRouter includes an optional bounded DecisionBackend for tool/endpoint and output-field selection. It is off by default and cannot invent executable schema members.

from schemarouter import DecisionPolicy, SchemaPlanner
from schemarouter.integrations import JevDecisionBackend

planner = SchemaPlanner(
    registry,
    decision_backend=JevDecisionBackend(min_confidence=0.65),
    decision_policy=DecisionPolicy(
        enabled=True,
        endpoint_selection=True,
        fallback="deterministic",
    ),
)

A provider-neutral local embedding backend is also available without adding an embedding library to SchemaRouter's dependencies:

from schemarouter import EmbeddingDecisionBackend

backend = EmbeddingDecisionBackend(
    embed_batch,
    min_similarity=0.35,
    min_margin=0.05,
)

The callable can wrap a local SentenceTransformers/FastEmbed-style encoder or an application-owned embedding service. SchemaRouter computes cosine ranking locally and can abstain on weak or ambiguous matches.

Jev / TypeSafe System One is optional:

pip install "schemarouter[jev]"
export TYPESAFE_API_KEY="..."

The provider receives only bounded decision inputs. Unknown option IDs fail closed, low-confidence valid choices can abstain, and deterministic fallback remains available. Jev is never enabled just because the package or an API key exists.

A local Ollama model can also serve as a bounded decision backend without an additional Python SDK:

from schemarouter.integrations import OllamaDecisionBackend

backend = OllamaDecisionBackend("your-installed-model")

Ollama structured output constrains the finite option IDs, and SchemaRouter revalidates the result locally. No local model is enabled automatically.

Bounded field selection is independently opt-in:

policy = DecisionPolicy(
    enabled=True,
    field_selection=True,
    fallback="deterministic",
)

Only declared non-identifier fields are offered to the backend. Identifier fields are always preserved locally, and invalid/abstaining provider output falls back to deterministic projection.

Evidence sufficiency is independently opt-in as a conservative gate:

policy = DecisionPolicy(
    enabled=True,
    evidence_sufficiency=True,
    fallback="deterministic",
)

Requested provenance/license/unit/source-type requirements must first be satisfied by local schema metadata. The backend then receives only evidence:sufficient / evidence:insufficient and can veto a locally sufficient call, but it cannot upgrade missing evidence or grant execution authority.

Decision benchmark

Run the deterministic baseline:

python scripts/benchmark_decision_routing.py

Compare an embedding backend through a local callable:

python scripts/benchmark_decision_routing.py \
  --corpus benchmarks/decision-routing-v1.json \
  --embedding-callable my_embeddings:embed_batch

Compare Jev when credentials are available:

TYPESAFE_API_KEY="..." python scripts/benchmark_decision_routing.py --jev

Compare an installed local Ollama model:

python scripts/benchmark_decision_routing.py --ollama-model your-installed-model

The harness includes a checked-in 144-case multilingual/adversarial corpus and reports routing accuracy, invalid-plan rate, abstentions/fallbacks, category accuracy, p50/p95 latency, token usage, errors, and optional cost estimates. A provider-neutral ModelQueryAnalyzer callable can also be supplied with --model-callable module:function; embedding encoders use --embedding-callable module:function.

python scripts/benchmark_decision_routing.py \
  --corpus benchmarks/decision-routing-v1.json \
  --json-out artifacts/decision-benchmark.json \
  --csv-out artifacts/decision-benchmark.csv

Documentation

Full documentation is organized as a framework manual rather than embedded in this README:

Build the docs locally with:

pip install -e ".[docs]"
mkdocs serve

Development

python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
ruff check .
pytest -q -m "not mcp_integration"
python examples/quickstart.py
python scripts/benchmark_decision_routing.py

# Type-check the complete packaged surface, including optional integrations.
pip install -e ".[dev,mcp,langchain,langgraph,llamaindex,jev,otel]"
pyright
pytest -q --cov=schemarouter --cov-branch --cov-report=term-missing

Optional integration suites are isolated from the core package:

pip install -e ".[dev,mcp]"
pytest -q tests/test_mcp_integration.py

pip install -e ".[dev,langchain]"
pytest -q tests/test_langchain_integration.py

pip install -e ".[dev,llamaindex]"
pytest -q tests/test_llamaindex_integration.py

pip install -e ".[dev,jev]"
pytest -q tests/test_jev_integration.py

pip install -e ".[dev,otel]"
pytest -q tests/test_opentelemetry_integration.py

Project scope

SchemaRouter does not implement another chat abstraction, graph runtime, model-provider layer, memory system, or checkpoint store. Those belong in surrounding agent frameworks.

Its scope is:

Natural-language request -> typed tool execution plan -> validated execution.

Research

SchemaRouter originated from SchemaRouter: Field-Aware Tool Routing for Efficient Heterogeneous Agentic RAG. The framework keeps the research idea while removing harness assumptions such as one endpoint per tool and fixture-only execution.

License

MIT © 2026 Yong-eun Cho

Release files for schemarouter 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for schemarouter 0.4.0
File Size Uploaded
schemarouter-0.4.0.tar.gz 205.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for schemarouter 0.4.0
File Interpreter ABI Platform
schemarouter-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 293.4 kB

Release files / schemarouter-0.4.0.tar.gz

Download URL schemarouter-0.4.0.tar.gz
Size 205.4 kB
Tags Source
SHA-256 checksum
How to use checksums
cd00938ed74b0e319f709d9848272a2c687f2a1235ebf695c4a4aab1965395e5
BLAKE2b-256 checksum
How to use checksums
8f9a6d7dff430b2467120194479c68b683f1b251bab9ecad251ae6a8982db754
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / schemarouter-0.4.0-py3-none-any.whl

Download URL schemarouter-0.4.0-py3-none-any.whl
Size 88.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9dcc5303e408e96f754dafd853850a7bbe3aef57d73a1626f3ea48def08d0bd2
BLAKE2b-256 checksum
How to use checksums
7ec1e927c35f1efdf6bb0ef8b85432cc2dfc34b7dbe53b47e7c98a623c499b34
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release history Release notifications | RSS feed

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

This release

0.4.0 This release

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page