Skip to main content

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}