feat: Added support for specifying the oauth port and client ID in MCP server configs
This commit is contained in:
@@ -132,7 +132,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: None,
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: Some(url.to_string()),
|
||||
headers,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3698,7 +3698,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: None,
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
+7
-1
@@ -158,7 +158,13 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
let url = spec.url.as_deref().expect("validated: remote spec has url");
|
||||
mcp::oauth::run_mcp_oauth_flow(server_name, url, spec.oauth_client_id.as_deref()).await?;
|
||||
mcp::oauth::run_mcp_oauth_flow(
|
||||
server_name,
|
||||
url,
|
||||
spec.oauth.as_ref().and_then(|o| o.client_id.as_deref()),
|
||||
spec.oauth.as_ref().and_then(|o| o.callback_port),
|
||||
)
|
||||
.await?;
|
||||
println!("Authentication saved. '{server_name}' is now available for use.");
|
||||
|
||||
return Ok(());
|
||||
|
||||
+22
-14
@@ -57,6 +57,14 @@ pub(crate) struct McpServersConfig {
|
||||
pub mcp_servers: IndexMap<String, McpServer>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub(crate) struct McpOAuthConfig {
|
||||
#[serde(rename = "clientId", skip_serializing_if = "Option::is_none")]
|
||||
pub client_id: Option<String>,
|
||||
#[serde(rename = "callbackPort", skip_serializing_if = "Option::is_none")]
|
||||
pub callback_port: Option<u16>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub(crate) struct McpServer {
|
||||
@@ -75,7 +83,7 @@ pub(crate) struct McpServer {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub headers: Option<IndexMap<String, String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub oauth_client_id: Option<String>,
|
||||
pub oauth: Option<McpOAuthConfig>,
|
||||
}
|
||||
|
||||
impl McpServer {
|
||||
@@ -110,10 +118,10 @@ impl McpServer {
|
||||
"MCP server '{name}' is missing a \"command\" field (required for stdio transport)"
|
||||
));
|
||||
}
|
||||
if self.url.is_some() || self.headers.is_some() || self.oauth_client_id.is_some() {
|
||||
if self.url.is_some() || self.headers.is_some() || self.oauth.is_some() {
|
||||
return Err(anyhow!(
|
||||
"MCP server '{name}' has type \"stdio\" but also specifies remote fields \
|
||||
(url/headers/oauth_client_id). Remove the remote fields or change the type to \"http\" or \"sse\"."
|
||||
(url/headers/oauth). Remove the remote fields or change the type to \"http\" or \"sse\"."
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -511,7 +519,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: None,
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,7 +532,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: Some(url.to_string()),
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,7 +545,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: Some(url.to_string()),
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,7 +577,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: None,
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
};
|
||||
|
||||
let err = spec.validate("test").unwrap_err();
|
||||
@@ -587,7 +595,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: Some("http://localhost".into()),
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
};
|
||||
|
||||
let err = spec.validate("test").unwrap_err();
|
||||
@@ -607,7 +615,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: None,
|
||||
headers: Some(headers),
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
};
|
||||
|
||||
let err = spec.validate("test").unwrap_err();
|
||||
@@ -632,7 +640,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: None,
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
};
|
||||
|
||||
let err = spec.validate("test").unwrap_err();
|
||||
@@ -650,7 +658,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: Some("http://localhost".into()),
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
};
|
||||
|
||||
let err = spec.validate("test").unwrap_err();
|
||||
@@ -668,7 +676,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: Some("http://localhost".into()),
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
};
|
||||
|
||||
let err = spec.validate("test").unwrap_err();
|
||||
@@ -686,7 +694,7 @@ mod tests {
|
||||
cwd: Some("/tmp".into()),
|
||||
url: Some("http://localhost".into()),
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
};
|
||||
|
||||
let err = spec.validate("test").unwrap_err();
|
||||
@@ -711,7 +719,7 @@ mod tests {
|
||||
cwd: None,
|
||||
url: None,
|
||||
headers: None,
|
||||
oauth_client_id: None,
|
||||
oauth: None,
|
||||
};
|
||||
|
||||
let err = spec.validate("test").unwrap_err();
|
||||
|
||||
+3
-1
@@ -80,10 +80,12 @@ pub async fn run_mcp_oauth_flow(
|
||||
server_name: &str,
|
||||
server_url: &str,
|
||||
configured_client_id: Option<&str>,
|
||||
callback_port: Option<u16>,
|
||||
) -> Result<()> {
|
||||
let metadata = discover_oauth_metadata(server_url).await?;
|
||||
|
||||
let listener = TcpListener::bind("127.0.0.1:0")?;
|
||||
let bind_addr = format!("127.0.0.1:{}", callback_port.unwrap_or(0));
|
||||
let listener = TcpListener::bind(&bind_addr)?;
|
||||
let port = listener.local_addr()?.port();
|
||||
drop(listener);
|
||||
let redirect_uri = format!("http://127.0.0.1:{port}/callback");
|
||||
|
||||
+13
-3
@@ -640,9 +640,19 @@ pub async fn run_repl_command(
|
||||
.url
|
||||
.as_deref()
|
||||
.expect("validated: remote spec has url");
|
||||
let client_id = spec.oauth_client_id.as_deref();
|
||||
mcp::oauth::run_mcp_oauth_flow(server_name, url, client_id)
|
||||
.await?;
|
||||
let client_id = spec
|
||||
.oauth
|
||||
.as_ref()
|
||||
.and_then(|o| o.client_id.as_deref());
|
||||
let callback_port =
|
||||
spec.oauth.as_ref().and_then(|o| o.callback_port);
|
||||
mcp::oauth::run_mcp_oauth_flow(
|
||||
server_name,
|
||||
url,
|
||||
client_id,
|
||||
callback_port,
|
||||
)
|
||||
.await?;
|
||||
println!(
|
||||
"Authentication saved. \
|
||||
Restart Coyote to connect to '{server_name}'."
|
||||
|
||||
Reference in New Issue
Block a user