Skip to main content

claude_api/messages/
mcp.rs

1//! MCP server configuration for the `mcp_servers` request field.
2
3use serde::{Deserialize, Serialize};
4
5/// One entry in the `mcp_servers` array on a Messages request.
6///
7/// Currently only the URL form is supported on the wire.
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(tag = "type", rename_all = "snake_case")]
10#[non_exhaustive]
11pub enum McpServerConfig {
12    /// A URL-addressable MCP server.
13    Url {
14        /// MCP endpoint URL.
15        url: String,
16        /// Logical name for this server (used in tool dispatch).
17        name: String,
18        /// Optional bearer token for the MCP server.
19        #[serde(default, skip_serializing_if = "Option::is_none")]
20        authorization_token: Option<String>,
21        /// Per-server tool gating.
22        #[serde(default, skip_serializing_if = "Option::is_none")]
23        tool_configuration: Option<McpToolConfiguration>,
24    },
25}
26
27impl McpServerConfig {
28    /// Convenience constructor for a URL-addressed MCP server.
29    pub fn url(url: impl Into<String>, name: impl Into<String>) -> Self {
30        Self::Url {
31            url: url.into(),
32            name: name.into(),
33            authorization_token: None,
34            tool_configuration: None,
35        }
36    }
37}
38
39/// Per-server tool gating for an [`McpServerConfig::Url`].
40#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
41#[non_exhaustive]
42pub struct McpToolConfiguration {
43    /// Whether the model is allowed to use tools from this MCP server.
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub enabled: Option<bool>,
46    /// If set, restrict the model to this allowlist of tool names.
47    #[serde(default, skip_serializing_if = "Option::is_none")]
48    pub allowed_tools: Option<Vec<String>>,
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use pretty_assertions::assert_eq;
55    use serde_json::json;
56
57    #[test]
58    fn url_minimal_round_trips() {
59        let c = McpServerConfig::url("https://mcp.example", "example");
60        let v = serde_json::to_value(&c).unwrap();
61        assert_eq!(
62            v,
63            json!({"type": "url", "url": "https://mcp.example", "name": "example"})
64        );
65        let parsed: McpServerConfig = serde_json::from_value(v).unwrap();
66        assert_eq!(parsed, c);
67    }
68
69    #[test]
70    fn url_full_round_trips() {
71        let c = McpServerConfig::Url {
72            url: "https://mcp.example".into(),
73            name: "example".into(),
74            authorization_token: Some("Bearer xyz".into()),
75            tool_configuration: Some(McpToolConfiguration {
76                enabled: Some(true),
77                allowed_tools: Some(vec!["search".into(), "fetch".into()]),
78            }),
79        };
80        let v = serde_json::to_value(&c).unwrap();
81        assert_eq!(
82            v,
83            json!({
84                "type": "url",
85                "url": "https://mcp.example",
86                "name": "example",
87                "authorization_token": "Bearer xyz",
88                "tool_configuration": {
89                    "enabled": true,
90                    "allowed_tools": ["search", "fetch"]
91                }
92            })
93        );
94        let parsed: McpServerConfig = serde_json::from_value(v).unwrap();
95        assert_eq!(parsed, c);
96    }
97}