MCP Server (Hoziron as an MCP server)

Hoziron exposes its own capabilities — agents, competencies, skills, integrations, workflows, audit log — as an MCP (Model Context Protocol) server, over rmcp's streamable-HTTP transport. This is the opposite direction from reference/cli/integration.md, which covers Hoziron acting as an MCP client connecting to external integration servers (Guidewire, Salesforce, etc.). This page is about pointing an external MCP client (Claude Desktop, an orchestrator, another agent framework) at Hoziron itself.

Implemented in crates/surfaces/hoziron-mcp, mounted by hoziron-server when the mcp surface is enabled (--surfaces mcp or [surfaces.mcp] in config.toml — see config-toml.md). By default it mounts on the api surface's listener at base path /mcp.

⚠ Authentication is not enforced

The crate's own doc comments claim "all inbound MCP calls are authenticated via Bearer token (same as REST API)." This is not true. build_surface always constructs the MCP session via HozironMcpServer::anonymous(...), unconditionally — auth::authenticate_mcp_caller exists and is fully implemented, but has zero call sites anywhere in the crate. Every MCP caller runs as AuthContext::anonymous(), regardless of require_auth/auth_mode/key_store/oidc_validator config. Do not rely on the MCP surface being access-controlled — treat exposing /mcp the same as exposing an unauthenticated endpoint (bind it to a trusted network only, or don't enable the surface on a public listener).

Configuration is not wired to config.toml

The McpServerConfig doc comment claims it's loaded from a [mcp_server] section of config.toml. There is no mcp_server field on PlatformConfig, and hoziron-server builds McpServerConfig from scratch with hardcoded defaults (enabled: true, plus whatever listen_addr/base_path it resolves) rather than from the loaded config file. Practically, this means tool_routes, rate_limit_rpm, require_auth, tokenize_responses, session_keep_alive_secs, and max_body_size cannot be configured by an operator — they're always hardcoded defaults regardless of what you put in config.toml.

Tools

Five tools, registered via #[tool_router]:

ToolDescriptionParameters
hoziron_registryBrowse installed capabilities (agents, competencies, skills, integrations, workflows) and their statuscategory (optional: agents|competencies|skills|integrations|workflows|all), name_filter (optional substring match)
hoziron_auditQuery the audit log filtered by category, actor, or time rangelimit (default 50, max 500), category (one of agent_lifecycle, model_interaction, tool_invocation, data_access, trust_enforcement, system_event, package_lifecycle, pii, routing), actor (exact match), since (ISO-8601)
hoziron_agentInvoke any agent by name/IDagent (name or UUID), message, idempotency_key (optional)
hoziron_workflowList, start, or check status of workflow runsaction (list|start|status), workflow (required for start), plus a run input payload for start
hoziron_fnolSubmit a first notice of loss, routed to a config-designated agentnotice_text, policy_number (optional), claimant_name (optional), date_of_loss (optional, ISO-8601)

hoziron_fnol resolves its target agent via [mcp_server.tool_routes] — but per the config caveat above, that route table is always empty in a shipped hoziron-server, so this tool has no way to be pointed at a real agent.

Manifest

manifest.rs builds a versioned tool listing (build_manifest()), but it is not served over HTTP — only the base MCP path itself is mounted as a route (Router::new().nest_service(&base_path, service)); there's no separate GET endpoint serving the manifest JSON. Use the MCP protocol's own tools/list call against the base path for tool schemas instead.

PII tokenization for untrusted callers

Responses may be tokenized before being returned, depending on caller trust (is_trusted_caller — only Admin/Service roles are treated as trusted). Since every caller is anonymous (see the auth caveat above), every MCP response is tokenized as if from an untrusted caller — plan for that when integrating a client against this surface.

Rate limiting

Per-process token bucket (via governor), not per-caller — there is no real caller identity to key on.


Related: