Multi-model MCP routing fabric for local and cloud LLMs with automatic fallback. Routes tasks across Ollama, llama.cpp, Gemini, Claude, and OpenAI-compatible endpoints using an Intent -> Plan -> Execute loop. Runs as an MCP server, desktop GUI (PySide6), or AuraGrid managed service.
MCP Client / GUI / AuraGrid MAS
|
FastMCP Server (server.py)
|
MCP Tool Layer (mcp_tools.py)
|-- route_task (IPE loop + active analyzer delegation)
|-- generate_code (multi-step code gen with review)
|-- local_inference (privacy-preserving local-only execution)
|-- rag_status (AuraXLM retrieval integration status)
|-- sovereignty_status (policy + recent verdicts)
|-- speculative_* (drafter/verifier orchestration tools)
|-- monologue_* (recursive reasoning control + trace)
|-- list_intents (intent discovery: built-in + analyzer-declared)
|-- catalog tools (unified artifact CRUD)
|-- analyzer tools (active analyzer management)
|-- session tools (opt-in stateful conversations)
|-- grid tools (opt-in remote MCP service calls)
|
IntentRegistry (intent_registry.py)
|-- IntentDefinition (name, description, target_role, source, priority)
|-- build_intent_registry() (factory: built-in + active analyzer intents)
|
ComputeFabric (fabric.py)
|-- ConfigLoader (auraconfig.yaml read/write)
|-- ProviderCatalog (provider discovery + lifecycle)
|-- TriageRouter (complexity-based role selection)
|-- SovereigntyGate (prompt classification -> open/local/block)
|-- RagEnrichment (AuraXLM MCP context retrieval)
|-- UsageStore (token accounting)
|-- BudgetManager (cost guardrails)
|-- PrivacyAuditor (PII detection)
|
Provider Layer
|-- Ollama (local HTTP)
|-- llamacpp-server (local HTTP)
|-- llamacpp (embedded, requires [local])
|-- openapi (OpenAI-compatible HTTP)
|-- MCPProvider (wraps external MCP server packages)
- Unified artifact catalog. Models, services, and analyzers share a single typed registry (
catalogsection in YAML). Legacymodelsentries are surfaced askind: modelartifacts transparently. - Route analyzers as first-class primitives. Analyzers control routing decisions. A built-in
aurarouter-defaultwraps the IPE pipeline. Remote analyzers (e.g., AuraXLM) can take over via MCP callback. Fallback to built-in on failure. - Provider separation. Built-in providers (Ollama, llama.cpp, OpenAPI) ship with the core package. Cloud providers (Claude, Gemini) are external MCP server packages connected via
MCPProvider. - Atomic config persistence.
ConfigLoader.save()writes to a temp file then renames. No partial writes. - Convention-over-configuration MCP tools. Each tool can be individually enabled/disabled in
mcp.tools. Sensible defaults in_MCP_TOOL_DEFAULTS.
src/aurarouter/
__init__.py
server.py # FastMCP server factory (create_mcp_server)
config.py # ConfigLoader — YAML read/write, catalog CRUD, active analyzer
fabric.py # ComputeFabric — model execution with fallback chains, routing advisors
routing.py # Intent analysis, plan generation, review loop
rag_enrichment.py # AuraXLM-backed context retrieval and prompt enrichment
sovereignty.py # SovereigntyGate, verdicts, local-only / blocked routing
speculative.py # SpeculativeOrchestrator and speculative session model
notional.py # Notional response + correction event protocol
monologue.py # Recursive multi-expert reasoning blackboard orchestration
sanitizer.py # Response sanitization + sovereignty audit emission
mcp_tools.py # MCP tool implementations (stateless functions)
catalog_model.py # CatalogArtifact, ArtifactKind domain model (incl. supported_intents)
intent_registry.py # IntentRegistry, IntentDefinition, build_intent_registry()
analyzer_schema.py # validate_analyzer_spec(), AnalyzerSpecValidation
analyzers.py # Built-in analyzer factory (create_default_analyzer)
contracts/
auracode.py # AURACODE_INTENTS, create_auracode_analyzer_spec()
auraxlm.py # AURAXLM_ANALYZER_SPEC, ANALYZE_ROUTE_PARAMS
migration.py # Config migration: old format -> catalog format
cli.py # Click CLI (model, route, config, catalog, migrate-config, gui)
catalog.py # ProviderCatalog — provider discovery and lifecycle
semantic_verbs.py # Synonym resolution for tag-to-role auto-join
tuning.py # GGUF metadata extraction, auto-tune
_logging.py # Structured logging setup
providers/
protocol.py # ProviderProtocol ABC
ollama.py # Ollama provider
llamacpp_server.py # llama.cpp HTTP server provider
llamacpp_embedded.py # Embedded llama.cpp (optional dep)
openapi.py # OpenAI-compatible provider
mcp_provider.py # MCPProvider — wraps external MCP servers
template/ # Starter template for custom providers
savings/
triage.py # TriageRouter — complexity-based role selection
pricing.py # CostEngine, PricingCatalog, ModelPrice
usage_store.py # SQLite-backed token accounting
budget.py # BudgetManager — per-period cost limits
privacy.py # PrivacyAuditor, PrivacyStore, PII pattern matching
sessions/
manager.py # SessionManager — stateful multi-turn conversations
mcp_client/
registry.py # McpClientRegistry — grid service client management
models/
file_storage.py # FileModelStorage — local GGUF file registry
auragrid/
mas_host.py # AuraGrid MAS host entry point
gui/
app.py # PySide6 desktop GUI entry point
Three artifact kinds managed through a single catalog section in auraconfig.yaml:
class ArtifactKind(str, Enum):
MODEL = "model"
SERVICE = "service"
ANALYZER = "analyzer"
@dataclass
class CatalogArtifact:
artifact_id: str
kind: ArtifactKind
display_name: str
description: str = ""
provider: str = ""
version: str = ""
tags: list[str] = field(default_factory=list)
capabilities: list[str] = field(default_factory=list)
supported_intents: list[str] = field(default_factory=list) # Intent eligibility (models)
status: str = "registered"
spec: dict[str, Any] = field(default_factory=dict) # Kind-specific fieldsto_dict()serializes; spec fields merge at top level (flat YAML).from_dict(artifact_id, data)deserializes from YAML config dict.is_remoteproperty: True ifspec["mcp_endpoint"]is set.
Central registry of all known intents (built-in + analyzer-declared).
@dataclass
class IntentDefinition:
name: str # e.g., "SIMPLE_CODE", "sar_processing"
description: str # One-line description for the classifier prompt
target_role: str # Role to route to (e.g., "coding", "reasoning")
source: str # "builtin" | analyzer_id that declared it
priority: int = 0 # Higher = preferred on conflict (builtin=0, analyzer=10)| Method | Description |
|---|---|
register(intent) |
Register a single intent (higher priority wins on conflict). |
unregister_by_source(source) |
Remove all intents from a source (never removes built-in). |
register_from_role_bindings(analyzer_id, bindings) |
Convert role_bindings dict to intents. |
get_all() |
All registered IntentDefinitions. |
get_by_name(name) |
Look up by name. Returns None if unknown. |
get_intent_names() |
All registered intent names. |
resolve_role(intent_name) |
Return target role for an intent, or None. |
build_classifier_choices() |
Formatted string of intent choices for classifier prompt. |
Built-in intents: DIRECT (coding), SIMPLE_CODE (coding), COMPLEX_REASONING (reasoning).
build_intent_registry(config) factory: creates a registry with built-in intents, then adds intents from the active analyzer's role_bindings.
@dataclass
class AnalyzerSpecValidation:
valid: bool
warnings: list[str]
errors: list[str]
declared_intents: list[str]validate_analyzer_spec(spec, available_roles=None) checks required fields, role_bindings keys/values, mcp_endpoint URL, capabilities list. Warn-only for backwards compatibility.
Invalid or unusable MCP endpoint values are treated as validation errors. Compatibility issues such as weak role binding metadata remain warnings.
| Method | Description |
|---|---|
catalog_get(artifact_id) |
Look up by ID. Falls back to models section (legacy). |
catalog_list(kind=None) |
All artifact IDs. Merges catalog + legacy models. |
catalog_set(artifact_id, data) |
Write artifact to config["catalog"]. |
catalog_remove(artifact_id) |
Remove from config["catalog"]. Returns bool. |
catalog_query(kind, tags, capabilities, provider) |
Filtered query. All filter params optional. Results enriched with artifact_id. |
get_active_analyzer() |
Read system.active_analyzer. Returns str or None. |
set_active_analyzer(analyzer_id) |
Write or clear system.active_analyzer. |
create_default_analyzer() returns a CatalogArtifact with:
artifact_id:"aurarouter-default"kind:ANALYZERanalyzer_kind:"intent_triage"(in spec)role_bindings:{simple_code: coding, complex_reasoning: reasoning, review: reviewer}(in spec)capabilities:[code, reasoning, review, planning]
Auto-registered in catalog on server startup if not already present.
migrate_config(config_data) transforms old-format configs:
- Adds
catalogsection if missing. - Converts
grid_services.endpointsto catalog service entries. - Sets
system.active_analyzerto"aurarouter-default"if missing. - Never modifies
modelsorgrid_servicessections.
migrate_config_file(path, dry_run=False) reads YAML, migrates, writes back.
CLI: aurarouter migrate-config [--dry-run]
route_task(task, context, format)-- IPE loop; consults active analyzer firstlocal_inference(prompt, context)-- Local-only executiongenerate_code(task_description, file_context, language)-- Multi-step code gencompare_models(prompt, models)-- Side-by-side model comparisonlist_models()-- All configured models as JSON
rag_status()-- RAG enrichment configuration and recent retrieval statssovereignty_status()-- Sovereignty enforcement configuration and recent verdictsspeculative_execute()/speculative_status()-- Drafter/verifier orchestration and session inspectionmonologue_execute()/monologue_status()/monologue_trace()-- Recursive reasoning workflow control
aurarouter.assets.list-- Physical GGUF filesaurarouter.assets.register-- Register local GGUFaurarouter.assets.register_remote-- Register remote endpointaurarouter.assets.unregister-- Remove model from routing
aurarouter.catalog.list(kind?)-- List artifacts (model/service/analyzer)aurarouter.catalog.get(artifact_id)-- Get single artifactaurarouter.catalog.register(artifact_id, kind, display_name, ...)-- Register artifactaurarouter.catalog.remove(artifact_id)-- Remove artifact
aurarouter.analyzer.set_active(analyzer_id?)-- Set/clear active analyzeraurarouter.analyzer.get_active()-- Get active analyzer ID
list_intents()-- All available intents (built-in + analyzer-declared) with target roles and sources
create_session,session_message,session_status,list_sessions,delete_session
list_grid_services,list_remote_tools,call_remote_tool
system:
log_level: INFO # DEBUG, INFO, WARNING, ERROR
default_timeout: 120.0 # Seconds
active_analyzer: aurarouter-default # Catalog artifact ID of active route analyzer
models: # Legacy model definitions (still supported)
model_id:
provider: ollama | llamacpp-server | llamacpp | openapi
endpoint: http://... # For HTTP providers
model_path: /path/to.gguf # For llamacpp
model_name: ...
tags: [coding, local]
capabilities: [code, chat]
cost_per_1m_input: 0.0
cost_per_1m_output: 0.0
hosting_tier: on-prem | cloud | dedicated-tenant
parameters:
temperature: 0.1
num_ctx: 4096 # Ollama
n_ctx: 4096 # llama.cpp
roles: # Ordered fallback chains
router: [model_a]
reasoning: [model_b, model_a]
coding: [model_a]
reviewer: [model_a] # Optional — enables review loop
execution:
max_review_iterations: 3 # 0 = disable review loop
catalog: # Unified artifact catalog
artifact_id:
kind: model | service | analyzer
display_name: ...
description: ...
provider: ...
version: ...
tags: [...]
capabilities: [...]
status: registered # Default
supported_intents: [...] # Model only: intents this model is suited for
# Kind-specific spec fields merged at top level:
# Analyzer: analyzer_kind, role_bindings, mcp_endpoint, mcp_tool_name
# Service: endpoint, protocol, auto_sync_models, health_check
xlm: # AuraXLM integration (opt-in)
endpoint: http://xlm-host:8080 # AuraXLM MCP endpoint
features:
prompt_augmentation: true # Prepend RAG context via auraxlm.query
usage_reporting: false # Report token usage back to XLM
savings: # Opt-in cost/privacy subsystem
enabled: false
db_path: ...
triage:
enabled: false
budget:
enabled: false
privacy:
enabled: true
feedback: # Routing outcome feedback loop
enabled: false
db_path: ~/.auracore/aurarouter/feedback.db
sessions:
enabled: false
store_path: ...
grid_services:
endpoints:
- name: ...
url: http://...
auto_sync_models: true
mcp:
tools:
tool_name:
enabled: true | false
provider_catalog:
auto_start_entrypoints: true
manual:
- name: gemini
endpoint: http://localhost:9001
auto_start: true
semantic_verbs: # Custom tag-to-role synonym mappings
coding: [code, program, develop]Cloud providers are distributed as separate MCP server packages, discovered via the aurarouter.providers entry-point group:
| Package | Provider | Install | Entry Point |
|---|---|---|---|
aurarouter-claude |
Anthropic Claude (Opus 4, Sonnet 4, Haiku 4.5) | pip install aurarouter-claude |
claude = "aurarouter_claude" |
aurarouter-gemini |
Google Gemini (2.5 Pro, 2.5 Flash, 2.0 Flash) | pip install aurarouter-gemini |
gemini = "aurarouter_gemini" |
Both packages register under the aurarouter.providers entry-point group. ProviderCatalog.discover() finds them automatically when installed. Each exposes a get_provider_metadata() callable that returns a ProviderMetadata-compatible object.
Provider count: 4 built-in + 2 external MCP = 6 providers total.
- Python 3.12+, src-layout.
- Apache-2.0 license (open source under AuraCore Dynamics).
mcp[cli],httpx,pyyaml,PySide6are core deps. Everything else is optional.- MCP tool functions in
mcp_tools.pyare stateless — they takeComputeFabric/ConfigLoaderand return strings. - Tool registration and wiring happens in
server.py(create_mcp_server). ConfigLoaderis the single source of truth for all YAML config access. No directconfig["key"]outside of it.- Providers implement
ProviderProtocol. - New intents flow through the
IntentRegistry: declared via analyzerrole_bindings, registered bybuild_intent_registry(), resolved byresolve_role(). - Test with
pytest tests/ -v --tb=short. Comprehensive test suite with 130+ tests.
# Full suite
pytest tests/ -v --tb=short
# Catalog tests specifically
pytest tests/test_catalog*.py -v --tb=short
# Config and migration
pytest tests/test_config*.py tests/test_migration*.py -v --tb=short
# MCP tools
pytest tests/test_mcp_tools*.py -v --tb=short
# XLM integration
pytest tests/test_xlm_integration.py -v --tb=short
# Feedback loop
pytest tests/test_feedback.py -v --tb=short
# Cross-project integration
pytest tests/test_cross_project_integration.py -v --tb=shortAll catalog, config, and migration tests are self-contained with no external dependencies.