brainos_mcphost/types.rs
1//! Public types for the MCP host: server configs, tool descriptors, and outcomes.
2
3use std::{collections::BTreeMap, path::PathBuf};
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8/// Transport-specific configuration for a mounted MCP server.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(tag = "transport", rename_all = "snake_case")]
11pub enum ServerConfig {
12 /// Local child process speaking MCP JSON-RPC on stdin/stdout.
13 Stdio {
14 command: String,
15 args: Vec<String>,
16 #[serde(default)]
17 env: BTreeMap<String, String>,
18 #[serde(default)]
19 cwd: Option<PathBuf>,
20 },
21
22 /// MCP spec 2025-11-25 Streamable HTTP transport.
23 StreamableHttp {
24 url: String,
25 #[serde(default)]
26 oauth: Option<OAuthConfig>,
27 },
28
29 /// Legacy HTTP+SSE transport. Still spec-required for compatibility.
30 HttpSse {
31 url: String,
32 #[serde(default)]
33 oauth: Option<OAuthConfig>,
34 },
35}
36
37/// OAuth 2.1 + PKCE configuration for HTTP transports.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct OAuthConfig {
40 /// Resource indicator per RFC 8707; reject tokens with mismatched `aud`.
41 pub resource: String,
42 /// Optional pre-registered client id (else fall back to DCR / Client ID
43 /// Metadata Document per MCP spec 2025-11-25).
44 #[serde(default)]
45 pub client_id: Option<String>,
46 /// Optional explicit authorization-server URL (else discovered via PRM).
47 #[serde(default)]
48 pub authorization_server: Option<String>,
49}
50
51/// Server metadata returned from the MCP `initialize` handshake.
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct ServerInfo {
54 pub name: String,
55 pub version: String,
56 pub protocol_version: String,
57}
58
59/// A tool exposed by a mounted server. Mirrors the MCP `Tool` shape plus the
60/// originating server name for routing.
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct ToolDescriptor {
63 pub server: String,
64 pub name: String,
65 /// **UNTRUSTED.** Attacker-controllable text shipped by the remote
66 /// MCP server. The hash-pin layer in [`crate::rmcp_host`] detects
67 /// rug-pull *changes* to this field; callers that surface it to
68 /// the LLM must additionally route it through
69 /// [`intent::sanitization::render_tool_description_for_prompt`]
70 /// before inlining.
71 #[serde(default)]
72 pub description: Option<String>,
73 /// JSON Schema for `arguments`. Rendered as **untrusted** content
74 /// when shown to the model (CVE-2025-54136 / MCPoison mitigation).
75 pub input_schema: serde_json::Value,
76}
77
78/// Outcome of a `tools/call`. Structured so audit/observer can render it
79/// without re-parsing the raw MCP response.
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct CallOutcome {
82 pub server: String,
83 pub tool: String,
84 pub is_error: bool,
85 pub content: serde_json::Value,
86 pub elapsed_ms: u64,
87}
88
89/// In-memory record of a mounted server. The transport-bound `MCPClient`
90/// is attached when a real transport is configured; the bare record
91/// tracks config + handshake data.
92#[derive(Debug, Clone)]
93pub struct MountedServer {
94 pub name: String,
95 pub config: ServerConfig,
96 pub mounted_at: DateTime<Utc>,
97 pub info: Option<ServerInfo>,
98 pub tools: Vec<ToolDescriptor>,
99}
100
101/// Snapshot for `list_servers` / `Intent::List { resource: McpServers }`.
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct ServerStatus {
104 pub name: String,
105 pub mounted_at: DateTime<Utc>,
106 pub tool_count: usize,
107 pub info: Option<ServerInfo>,
108}