SchemaRouter is a typed capability routing, planning, and governed execution layer for AI agents across APIs, tools, and data systems. It is not a general-purpose agent framework, model router, graph runtime, database proxy, or MCP replacement.
The core is designed around one principle:
model output and remote schemas may describe capabilities, but only trusted local code grants execution authority.
That authority runs in both directions. Trusted local code may also declare the result contract of a capability SchemaRouter imported on its behalf — output fields, semantic IDs, units, normalization and measurement qualifiers — without losing the execution binding and without handling an invoker. It may not change execution identity or validation shape. See Registry and schema identity.
RAG stands for Retrieval-Augmented Generation: generation is conditioned on information retrieved from external sources. SchemaRouter does not implement that complete architecture or the generation step.
Architecturally, it can occupy a structured retrieval/execution boundary inside a RAG or agent system: adapters parse APIs/tools into endpoint/field contracts, the registry/index organizes the capability surface, and routing selects a bounded executable subset that can return the requested external data.
The registry can be viewed as a logical capability graph. A graph database is not required, and embeddings are not authority. Datatype, unit, qualifier, policy and side-effect semantics come from trusted registered contracts.
See Structured retrieval and execution for RAG and agents.
flowchart LR
subgraph P["Plan"]
direction TB
A1["request"] --> A2["QueryAnalyzer"]
A2 --> A3["semantic data need"]
A3 --> A4["required logical fields"]
A4 --> A5["candidate provider / access path / endpoint"]
A5 --> A6["parameter + field plan + bounded fallbacks"]
end
subgraph C["Check"]
direction TB
B1["schema/tool fingerprints"] --> B2["availability + execution policy"]
B2 --> B3["JSON Schema input validation"]
end
subgraph E["Execute"]
direction TB
C1["server-side field projection when explicitly supported"] --> C2["trusted invoker"]
C2 --> C3["JSON Schema output validation"]
C3 --> C4["final local field projection"]
C4 --> C5["minimal ToolResult"]
end
P --> C --> E
schemarouter.models typed tool / endpoint / plan contracts
schemarouter.capability_contracts provider-neutral typed capability/effect/precondition contracts
schemarouter.capability_graph indexed/incremental dependency graph + bounded SCC cycle analysis
schemarouter.capability_snapshot content-addressed graph snapshots + versioned document envelope
schemarouter.capability_publication atomic validated successor snapshot publication
schemarouter.capability_artifact portable versioned graph artifacts + migration/integrity checks
schemarouter.capability_decision_trace privacy-safe aggregation of existing decision explanations
schemarouter.provider_profiles provider identity -> declared protocol/SDK access methods
schemarouter.registry versioned namespaced catalog + optional SQLite persistence
schemarouter.planner exact-recall candidate indexing + deterministic scoring + recall-first projection
schemarouter.analyzers optional model-assisted intent extraction
schemarouter.validation JSON Schema runtime validation
schemarouter.policy trusted local side-effect + approval authority
schemarouter.runs run configuration, retry policy, budgets, typed lifecycle events
schemarouter.traces validated append-only run-event persistence + non-executing replay
schemarouter.hooks trusted snapshot-only before/after execution middleware
schemarouter.health explicit read-only health probes + bounded background monitoring
schemarouter.executor plan, binding, schema, policy, availability and hook enforcement
schemarouter.adapters adapter contracts + Python/SDK/OpenAPI/MCP/OPTIMADE/GraphQL/OData/OpenRPC implementations
schemarouter.ingestion AdapterRegistry dispatch, safe source loading, registry binding
schemarouter.proposals evidence-grounded HTML documentation proposals
schemarouter.integrations optional LangChain/LlamaIndex/System One/Laya/OpenTelemetry integrations
schemarouter.decision_plugins explicit third-party bounded decision-backend discovery/loading
schemarouter.runtime high-level registration/retrieval/invoke/batch/stream facade
A semantic query -> tool router is not enough differentiation. SchemaRouter makes tool,
endpoint, parameter, output field, evidence requirements, schema identity, execution authority,
and result projection explicit contracts.
The research artifact conventionally used one search endpoint per tool. Production OpenAPI and MCP
servers expose many operations, so ToolSpec owns multiple first-class EndpointSpec
objects.
Large registries should not require scoring every endpoint on every request, but approximate prefilters can silently remove valid tools. SchemaRouter indexes every input that can produce a positive deterministic score under the current scorer and still runs the unchanged scoring function on the resulting candidates. The index is cached by registry version and can be disabled for exhaustive parity checks.
SchemaRouter is field-first, not merely tool-first. The planner should first identify the smallest declared logical data surface that can answer the request, then choose a route capable of supplying that surface. This reduces upstream bytes/latency and keeps unrelated values out of downstream LLM context.
The research results also showed that over-aggressive projection can remove answer-critical information, so the boundary is explicit:
- clear query-to-field match -> select the matched fields plus required identifiers;
- explicit server projection support -> push only those planned fields into the upstream request;
- raw response -> validate before projection;
- downstream result -> retain only planned logical fields;
- ambiguous field intent -> preserve declared fields rather than pretending one field is sufficient.
FieldSpec.path can map a bounded logical field ID onto a nested object path without exposing
arbitrary JSONPath syntax to planning.
Every endpoint and tool has a canonical schema fingerprint. A plan compiled against an older endpoint is rejected. A bound invoker tied to an older tool contract is also rejected after the registry changes.
ModelQueryAnalyzer returns a structured proposal, not an executable command. Unknown tools,
endpoints, parameters, fields, and extra JSON keys are rejected or removed. Explicit caller
arguments override model-produced values.
Descriptions from remote schemas are passed to models only as untrusted data and cannot extend the registry or execution authority.
MCP annotations and OpenAPI descriptions never grant permissions. Sensitive runtime credentials stay outside model-visible schemas.
For OpenAPI, Authorization, Cookie, Host, proxy authorization, connection framing, and
other sensitive runtime headers cannot be supplied through tool arguments.
schema_headers are used only for OpenAPI schema retrieval. trusted_headers are injected only
by the runtime invoker. OpenAPI document redirects are followed manually and only within the
original origin, preventing schema-fetch credentials from crossing origins. Cross-document
$ref retrieval is disabled by default; when explicitly enabled, it reuses schema headers only for
same-origin referenced documents and stays within redirect/depth/document/byte limits.
A cross-origin servers entry is treated as descriptive, not executable authority. SchemaRouter
imports the schema but leaves it unbound until trusted local code supplies base_url or calls
bind_openapi().
Runtime HTTP redirects are disabled and endpoint paths are constrained to the approved origin/base path.
The executor validates the actual argument object against JSON Schema immediately before
invocation. Type, enum, range, required-property, pattern, and other supported constraints
cannot be bypassed by manually constructing a ToolCall.
Raw structured tool output is validated before projection, so field projection cannot hide an invalid response.
The default ExecutionPolicy blocks known remote mutations and destructive operations. It also
blocks MCP operations whose side effects are not trusted locally because MCP annotations remain
untrusted.
Local application code may explicitly opt into:
allow_mutations=Trueallow_destructive=Trueallow_unclassified_remote=True
A model or remote schema cannot set these flags.
inspect_url() creates a non-executable SchemaProposal. Every accepted endpoint, parameter,
and field must cite an exact quote found in the fetched document. Script/style content is removed
before model analysis.
approve_proposal() is a separate authority transition with grounding thresholds, explicit API
base URL, and mutation opt-in. Runtime execution policy is an independent second gate.
v0.2 introduces AdapterRegistry. OpenAPI, OPTIMADE, MCP, and future structured protocols compile
into the same ToolSpec / EndpointSpec model. The planner does not branch on protocol type.
Call-aware invokers may receive the validated ToolCall when a protocol needs selected fields at
transport time. The executor still owns schema, policy, retry, and binding-drift enforcement.
DecisionBackend receives a finite set of locally generated option IDs. Unknown IDs, duplicate
selections, out-of-range/non-finite scores, and malformed results fail closed. Jev / TypeSafe System
One is an optional provider adapter; low-confidence valid choices may abstain and deterministic
fallback stays locally controlled.
Decision providers never construct ToolCall objects and never receive execution credentials or
authority. For bounded field selection, providers receive only declared non-identifier output
fields; identifier fields are preserved locally and cannot be removed by the provider. For evidence
sufficiency, local schema metadata must already satisfy requested provenance/license/unit/source-type
requirements before the provider is consulted; the provider can then only preserve or veto the
call and cannot upgrade missing evidence. Jev additionally does not receive
DecisionOption.metadata.
Schema and documentation fetches were already bounded, and OPTIMADE runtime execution used a bounded
reader. OpenAPI runtime execution now uses the same posture: responses are streamed and capped at
16 MiB by default, checking both declared Content-Length and bytes actually received before
JSON/text decoding.
OpenAPI parsing success does not imply perfect semantic fidelity, so imported OpenAPI tools
carry a machine-readable compatibility report that marks partial or unsupported constructs. Same-
origin cross-document references can be explicitly bundled under bounded limits; unresolved external
references, $id rebasing, non-JSON-Pointer anchors, composition, cookie parameters, non-JSON
bodies, callbacks, webhooks, and server variables remain visible rather than guessed.
MCP authentication belongs to the trusted HTTP transport/client boundary. Credentials embedded in MCP URLs are rejected, protocol-controlled headers cannot be overridden, and custom OAuth/mTLS/ gateway behavior is injected as a trusted client factory rather than represented in tool schemas.
Local ExecutionPolicy grants category-level authority. Optional trusted approval callbacks gate
individual calls after schema/policy validation and fail closed on missing, negative, or exceptional
decisions.
One logical call may produce multiple real invoker attempts, so budgets count logical calls, total attempts, remote attempts, wall-clock execution, per-tool quotas, and application-defined cost units separately. Retries consume attempt/remote/cost budget before invocation.
The OpenTelemetry integration consumes typed RunEvents but exports only structural attributes. Argument/result values, RunConfig metadata, tags, and exception messages are omitted.
Plugin metadata can be discovered without import. Entry-point loading requires an explicit non-empty allowlist so installed packages are never auto-executed merely because they are discoverable.
Execution hooks run only after schema/policy/approval validation and receive detached snapshots. Before hooks may veto by failing but cannot mutate the executable call. After hooks receive only the validated, projected result and cannot mutate the result returned to the caller. Non-None returns are rejected, hook errors fail closed, and after-hook failures are never retried as tool failures.
Because sync/async before hooks may wait while local state changes, SchemaRouter refreshes current schema and binding state after hooks complete before invocation.
Persistent traces store validated RunEvent envelopes. Replay returns detached historical events
only and never invokes the planner, executor, network, or tool bindings. Sequence gaps, identity
mismatches, timestamp regressions, corrupt stored JSON, and events appended after a terminal event
fail closed.
The trace database preserves the privacy level of the source event stream: default redacted events
remain structural, while an explicit include_payloads=True choice persists payload-bearing data
and creates an application-managed sensitive-data store.
Exact fingerprints remain the execution boundary, but a bare mismatch is operationally opaque.
compare_endpoint_specs() and compare_tool_specs() therefore classify trusted snapshot changes
as identical, compatible, breaking, or security-review changes. The classifier is conservative
for JSON Schema.
Compatibility reports are diagnostic only. They never permit a stale ToolCall or invoker binding
to execute without replanning/rebinding against the current fingerprint.
The original allow_mutations / allow_destructive switches remain safe defaults, but production
applications may need narrower authority. Ordered local PolicyRule values can allow, deny, or
require approval for a bounded tool.endpoint pattern and optional side-effect predicates.
Rules are trusted application configuration. Remote schemas, descriptions, decision backends, and model output cannot create or alter them.
Each planned call may carry a structured PlanExplanation containing deterministic score
components, field-retention reasons, ignored undeclared arguments, and whether a bounded decision
backend selected the candidate.
These are locally observable routing facts. SchemaRouter does not expose or attempt to reconstruct private model reasoning.
27. Descriptive metadata must not become hidden execution authority
Adversarial review found that adapter/runtime behavior can accidentally depend on values stored in
ordinary metadata, while fingerprints exclude that bag. If execution or policy
reads such a value, the runtime meaning can change without producing schema/binding drift.
SchemaRouter separates:
- ordinary
metadata: descriptive/inspection data only; EndpointSpec.execution_metadata: fingerprinted endpoint runtime semantics;ToolSpec.execution_metadata: fingerprinted transport/binding identity;ToolSpec.remote: fingerprinted local/remote authority classification.
Built-in adapters mirror some values into ordinary metadata for backward-compatible inspection, but runtime code reads the fingerprinted contract fields. Legacy persisted built-in metadata is migrated into those fields during model validation. Schema/discovery provenance URLs remain descriptive when they do not determine invocation; only actual runtime targets belong in the execution contract. Credential-bearing runtime URLs are rejected rather than persisted.
Planner-generated ToolCall values also pin the current tool fingerprint, so changing transport
origin or local/remote classification invalidates an already-compiled plan even after a trusted
rebind.
parallel_read_only is limited to flat plans whose calls all preflight successfully and have
read_only is True. Schema, binding, and policy validation happen before tasks are launched, and
all tasks share the same run budget and concurrency bound.
Dependencies, branching, checkpointing, write coordination, compensation, and DAG semantics remain outside the core and belong to surrounding orchestration frameworks.
One logical provider can expose multiple transport/access contracts, and a request may also have a semantically compatible alternative provider. Availability fallback is useful, but open-ended runtime search would reintroduce agent/workflow semantics and can silently change provenance.
SchemaRouter treats provider redundancy as a bounded execution contract:
ToolSpec.provideridentifies the logical information provider;ToolSpec.access_modeidentifies one access path;- planning may precompile
FallbackRoutealternatives only when explicitly requested; - same-provider paths are ordered before cross-provider candidates;
- every alternative has its own schema/tool fingerprint, arguments, field projection and evidence;
- automatic fallback is limited to explicitly read-only calls;
- runtime fallback occurs only after
InvocationUnavailableError, after normal same-route retry; - validation, policy, approval, stale-state and deterministic 4xx/application failures never cause fallback;
- the complete fallback chain is preflighted before the primary invocation.
Field aliases are the local semantic bridge when access paths expose different names. If semantic compatibility cannot be proven from local contracts, SchemaRouter omits the fallback instead of asking a model to guess.
Passive transport failures can make a route temporarily unattractive, but one outage must not permanently remove a capability, so SchemaRouter uses bounded cooldown state: after timeout, connection failure, HTTP 429, or transient 5xx exhaustion, an access path can be skipped for a finite interval and automatically becomes eligible again when the cooldown expires.
Applications that need faster recovery can register trusted local health probes for explicitly
read-only access paths. The optional background AccessHealthMonitor runs only those registered
callbacks, marks failed probes temporarily unavailable, and immediately reopens a route when a
probe succeeds. It never invents health requests from remote metadata and never turns arbitrary
data retrieval into an implicit probe.
Local result projection alone protects LLM context, but it does not reduce provider bandwidth or
latency if the upstream API still returns a full record. ServerProjectionSpec makes
server-side field selection a fingerprinted endpoint contract.
A trusted adapter may map planned logical fields to a declared query selector such as
fields=... or OPTIMADE response_fields=.... Generic OpenAPI support does not infer this
semantics from a parameter name. If no trusted projection contract exists, SchemaRouter still
performs raw-output validation and final local projection.
- A plan cannot call an unregistered tool or endpoint.
- A plan cannot pass undeclared parameters.
- Required parameters are recomputed at execution; a forged plan cannot suppress them.
- Arguments must satisfy the current endpoint input JSON Schema.
- Requested projection fields must be declared logical field IDs from the current endpoint; nested
wire paths come only from trusted
FieldSpec.pathmetadata. - Raw tool output must satisfy the current endpoint output JSON Schema before projection.
- A stale endpoint fingerprint cannot execute.
- A stale invoker binding cannot execute after tool replacement.
- Remote metadata cannot grant authorization.
- Known remote mutations/destructive operations require local policy opt-in.
- Unclassified remote MCP operations require local policy opt-in.
- Schema-fetch credentials cannot cross an origin redirect or an explicitly enabled external-ref fetch boundary.
- Cross-origin OpenAPI server declarations and external-ref targets require explicit local authority; external-ref targets are same-origin only in the built-in resolver.
- Runtime API secrets are not model-visible tool parameters.
- Ambiguous output selection favors recall over aggressive pruning.
- Automatic retries apply only to endpoints trusted as read-only unless local code opts in.
Built-in OpenAPI/OPTIMADE transports fail fast on known non-transient HTTP and deterministic
response-contract failures; trusted custom invokers can raise
NonRetryableInvocationErrorto opt a failure out of the retry loop. - Run-event arguments and result payloads are redacted unless payload tracing is explicitly enabled.
- Optional framework integrations call back through the same executor boundary rather than bypassing policy or validation.
- Optional decision providers can select only locally offered option IDs and cannot grant execution authority.
- OpenAPI runtime responses are bounded before decoding, including when Content-Length is absent or misleading.
- MCP runtime credentials remain inside trusted transport configuration and are never planner-visible.
- Calls requiring local approval fail closed if approval is absent, denied, or errors.
- Execution budgets are checked before each logical call and real invoker attempt; async approval callbacks, execution hooks, invocations, and retry backoff are bounded by the remaining wall-clock budget. Synchronous trusted callbacks are checked immediately after returning.
- Adapter plugins are never auto-imported from discovery alone.
- OpenTelemetry export omits payload values and exception messages.
- OpenAPI compatibility limitations are surfaced explicitly rather than silently guessed.
- Candidate indexing may reduce scorer work but must preserve exhaustive deterministic planner recall.
- Execution hooks receive detached snapshots and cannot transform calls or results.
- Hook failures fail closed and never create additional tool invocation attempts.
- Schema compatibility analysis never bypasses exact plan/binding fingerprint validation.
- Fine-grained policy rules exist only in trusted local configuration and cannot be supplied by models or remote capability metadata.
- Structured planning explanations contain deterministic/runtime-visible signals only, not model chain-of-thought.
- Parallel execution requires every call to preflight as explicitly read-only and shares one run budget across concurrent calls.
- Ordinary descriptive metadata cannot grant policy authority or alter built-in transport semantics; execution-affecting values live in fingerprinted contract fields.
- Planner-generated calls pin both endpoint and tool fingerprints, and remote/runtime-sensitive legacy calls without a tool fingerprint fail closed.
- Automatic provider/access fallback is precompiled, read-only, budgeted, and triggered only by an explicit invocation-unavailable marker; it never bypasses validation/policy/approval.
- Same-provider access paths precede cross-provider fallbacks, and every fallback retains its own schema/tool fingerprint and evidence contract.
- Inspection/dashboard provenance never exposes URL userinfo, query strings, or fragments. Runtime target identity remains fingerprinted; schema/document provenance is sanitized before model-visible or persisted descriptive state retains it.
- Availability cooldown is finite; registered trusted read-only health probes may reopen a path early, but no model/remote schema can set health state or define a probe.
- Server-side field projection is used only when declared by a trusted fingerprinted
ServerProjectionSpec; final local projection remains enforced after raw validation. - Availability fallback may change provider/access path but must not broaden the logical field need that the plan was compiled to answer.
- Provider-first registration may resolve known access methods but cannot auto-install SDKs, persist credentials, or grant execution authority.
- State-conditioned corrective re-retrieval operates only over the same host-visible/available capability surface and preserves the stable stateless retrieval facade.
- Capability-graph indexing and incremental rebuilds are search/update optimizations only; the canonical compatibility comparator remains authoritative, and compatibility-context changes require a full rebuild.
- Capability snapshot publication is atomic: readers observe a complete predecessor or complete successor, never a partially rebuilt graph. Runtime health is not part of immutable snapshot identity.
- Capability artifact/snapshot migrations are explicit, versioned, and fail closed on corrupt or unknown future formats; migrations cannot reconstruct secrets, invokers, or execution authority.
- Decision traces may aggregate only host-visible structured results and must not reveal hidden inventory, rank scores, payloads, credentials, or chain-of-thought, nor may they alter policy or execution decisions.
- trusted local classification for individual MCP tool side effects and richer MCP retry semantics;
- OpenAPI
$id/anchor-aware resolution and richer composition-aware planning/execution; - non-object request-body ergonomics and typed array-element projection if justified;
- organization-specific policy/approval and license/provenance extensions;
- compensation, transactions, and distributed execution;
- distributed/remote registry implementations beyond the built-in SQLite persistence;
- multi-page and client-rendered documentation crawling;
- additional trusted trace/export sinks;
- dated live-provider benchmark evidence and compatibility dashboards.
These are extension layers. They should not weaken the core fail-closed contracts above.