Skip to main content

claude_api/messages/
mcp.rs

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