Skip to content
68 changes: 68 additions & 0 deletions rust/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1972,6 +1972,13 @@ pub struct SessionConfig {
/// applied automatically at session creation/resume time. `None` means no
/// explicit value is set and the runtime default takes effect.
pub mcp_oauth_token_storage: Option<String>,
/// URL identifying this host's OAuth client metadata document.
///
/// Authorization servers that support client ID metadata documents can use
/// this URL as the MCP OAuth client ID. When unset, the SDK does not supply
/// a first-party host identity and the runtime uses its generic,
/// session-isolated OAuth client behavior.
pub auth_client_id_metadata_url: Option<String>,
/// Enables runtime discovery of supported configuration. Explicitly supplied
/// configuration takes precedence over discovered values.
pub enable_config_discovery: Option<bool>,
Expand Down Expand Up @@ -2248,6 +2255,10 @@ impl std::fmt::Debug for SessionConfig {
.field("excluded_builtin_agents", &self.excluded_builtin_agents)
.field("mcp_servers", &self.mcp_servers)
.field("mcp_oauth_token_storage", &self.mcp_oauth_token_storage)
.field(
"auth_client_id_metadata_url",
&self.auth_client_id_metadata_url,
)
.field("embedding_cache_storage", &self.embedding_cache_storage)
.field("enable_config_discovery", &self.enable_config_discovery)
.field("skip_embedding_retrieval", &self.skip_embedding_retrieval)
Expand Down Expand Up @@ -2380,6 +2391,7 @@ impl Default for SessionConfig {
excluded_builtin_agents: None,
mcp_servers: None,
mcp_oauth_token_storage: None,
auth_client_id_metadata_url: None,
enable_config_discovery: None,
skip_embedding_retrieval: None,
organization_custom_instructions: None,
Expand Down Expand Up @@ -2538,6 +2550,7 @@ impl SessionConfig {
tool_filter_precedence: "excluded",
mcp_servers: self.mcp_servers,
mcp_oauth_token_storage: self.mcp_oauth_token_storage,
auth_client_id_metadata_url: self.auth_client_id_metadata_url,
embedding_cache_storage: self.embedding_cache_storage,
env_value_mode: "direct",
enable_config_discovery: self.enable_config_discovery,
Expand Down Expand Up @@ -2870,6 +2883,12 @@ impl SessionConfig {
self
}

/// Set the URL identifying this host's OAuth client metadata document.
pub fn with_auth_client_id_metadata_url(mut self, url: impl Into<String>) -> Self {
self.auth_client_id_metadata_url = Some(url.into());
self
}

/// Set embedding cache storage mode.
pub fn with_embedding_cache_storage(
mut self,
Expand Down Expand Up @@ -3298,6 +3317,12 @@ pub struct ResumeSessionConfig {
/// Controls how MCP OAuth tokens are stored for this session.
/// See [`SessionConfig::mcp_oauth_token_storage`] for details.
pub mcp_oauth_token_storage: Option<String>,
/// Re-supply the host OAuth client metadata document URL on resume.
///
/// Set this to the same host identity used when the session was created.
/// When unset, the SDK does not supply a first-party host identity.
/// See [`SessionConfig::auth_client_id_metadata_url`] for details.
pub auth_client_id_metadata_url: Option<String>,
/// Enables runtime discovery of supported configuration. Explicitly supplied
/// configuration takes precedence over discovered values.
pub enable_config_discovery: Option<bool>,
Expand Down Expand Up @@ -3514,6 +3539,10 @@ impl std::fmt::Debug for ResumeSessionConfig {
.field("excluded_builtin_agents", &self.excluded_builtin_agents)
.field("mcp_servers", &self.mcp_servers)
.field("mcp_oauth_token_storage", &self.mcp_oauth_token_storage)
.field(
"auth_client_id_metadata_url",
&self.auth_client_id_metadata_url,
)
.field("embedding_cache_storage", &self.embedding_cache_storage)
.field("enable_config_discovery", &self.enable_config_discovery)
.field("skip_embedding_retrieval", &self.skip_embedding_retrieval)
Expand Down Expand Up @@ -3683,6 +3712,7 @@ impl ResumeSessionConfig {
tool_filter_precedence: "excluded",
mcp_servers: self.mcp_servers,
mcp_oauth_token_storage: self.mcp_oauth_token_storage,
auth_client_id_metadata_url: self.auth_client_id_metadata_url,
embedding_cache_storage: self.embedding_cache_storage,
env_value_mode: "direct",
enable_config_discovery: self.enable_config_discovery,
Expand Down Expand Up @@ -3787,6 +3817,7 @@ impl ResumeSessionConfig {
excluded_builtin_agents: None,
mcp_servers: None,
mcp_oauth_token_storage: None,
auth_client_id_metadata_url: None,
enable_config_discovery: None,
skip_embedding_retrieval: None,
organization_custom_instructions: None,
Expand Down Expand Up @@ -4091,6 +4122,12 @@ impl ResumeSessionConfig {
self
}

/// Set the host OAuth client metadata document URL on resume.
pub fn with_auth_client_id_metadata_url(mut self, url: impl Into<String>) -> Self {
self.auth_client_id_metadata_url = Some(url.into());
self
}

/// Set embedding cache storage mode on resume.
pub fn with_embedding_cache_storage(
mut self,
Expand Down Expand Up @@ -6669,6 +6706,37 @@ mod tests {
assert!(empty_json.get("largeOutput").is_none());
}

#[test]
fn auth_client_id_metadata_url_reaches_create_and_resume_wire_payloads() {
let url = "https://example.com/oauth/client-metadata.json";

let (create_wire, _) = SessionConfig::default()
.with_auth_client_id_metadata_url(url)
.into_wire(None)
.expect("default create has no duplicate handlers");
let create_json = serde_json::to_value(&create_wire).unwrap();
assert_eq!(create_json["authClientIdMetadataUrl"], url);

let (resume_wire, _) = ResumeSessionConfig::new(SessionId::from("sess-1"))
.with_auth_client_id_metadata_url(url)
.into_wire()
.expect("default resume has no duplicate handlers");
let resume_json = serde_json::to_value(&resume_wire).unwrap();
assert_eq!(resume_json["authClientIdMetadataUrl"], url);

let (empty_create_wire, _) = SessionConfig::default()
.into_wire(None)
.expect("default create has no duplicate handlers");
let empty_create_json = serde_json::to_value(&empty_create_wire).unwrap();
assert!(empty_create_json.get("authClientIdMetadataUrl").is_none());

let (empty_resume_wire, _) = ResumeSessionConfig::new(SessionId::from("sess-2"))
.into_wire()
.expect("default resume has no duplicate handlers");
let empty_resume_json = serde_json::to_value(&empty_resume_wire).unwrap();
assert!(empty_resume_json.get("authClientIdMetadataUrl").is_none());
}

#[test]
fn session_config_clones_disabled_mcp_servers() {
let create = SessionConfig::default().with_disabled_mcp_servers(["local-files"]);
Expand Down
4 changes: 4 additions & 0 deletions rust/src/wire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ pub(crate) struct SessionCreateWire {
#[serde(skip_serializing_if = "Option::is_none")]
pub mcp_oauth_token_storage: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_client_id_metadata_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embedding_cache_storage: Option<String>,
pub env_value_mode: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -240,6 +242,8 @@ pub(crate) struct SessionResumeWire {
#[serde(skip_serializing_if = "Option::is_none")]
pub mcp_oauth_token_storage: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_client_id_metadata_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embedding_cache_storage: Option<String>,
pub env_value_mode: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down
Loading