Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions crates/mcp-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,13 @@ tower = { version = "0.4", features = ["timeout", "util"] }
tower-service = "0.3"
rand = "0.8"
nix = { version = "0.30.1", features = ["process", "signal"] }
# OAuth dependencies
axum = { version = "0.8", features = ["query"] }
base64 = "0.22"
sha2 = "0.10"
chrono = { version = "0.4", features = ["serde"] }
nanoid = "0.4"
webbrowser = "1.0"
serde_urlencoded = "0.7"

[dev-dependencies]
64 changes: 64 additions & 0 deletions crates/mcp-client/examples/test_auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use anyhow::Result;
use mcp_client::client::{ClientCapabilities, ClientInfo, McpClient, McpClientTrait};
use mcp_client::transport::{StreamableHttpTransport, Transport};
use std::collections::HashMap;
use std::time::Duration;
use tracing_subscriber::EnvFilter;

#[tokio::main]
async fn main() -> Result<()> {
// Initialize logging
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::from_default_env()
.add_directive("mcp_client=debug".parse().unwrap())
.add_directive("eventsource_client=info".parse().unwrap()),
)
.init();

println!("Testing Streamable HTTP transport with OAuth 2.0 authentication...");

// Create the Streamable HTTP transport for any MCP service that supports OAuth
// This example uses a hypothetical MCP endpoint - replace with actual service
let mcp_endpoint =
std::env::var("MCP_ENDPOINT").unwrap_or_else(|_| "https://example.com/mcp".to_string());

println!("Using MCP endpoint: {}", mcp_endpoint);

let transport = StreamableHttpTransport::new(&mcp_endpoint, HashMap::new());

// Start transport
let handle = transport.start().await?;

// Create client
let mut client = McpClient::connect(handle, Duration::from_secs(30)).await?;
println!("Client created with Streamable HTTP transport\n");

// Initialize - this will trigger the OAuth flow if authentication is needed
// The implementation now includes:
// - RFC 8707 Resource Parameter support for proper token audience binding
// - Proper OAuth 2.0 discovery with multiple fallback paths
// - Dynamic client registration (RFC 7591)
// - PKCE for security (RFC 7636)
// - MCP-Protocol-Version header as required by the specification
let server_info = client
.initialize(
ClientInfo {
name: "streamable-http-auth-test".into(),
version: "1.0.0".into(),
},
ClientCapabilities::default(),
)
.await?;

println!("Connected to server: {server_info:?}\n");
println!("OAuth 2.0 authentication test completed successfully!");
println!("\nKey improvements implemented:");
println!("✓ RFC 8707 Resource Parameter implementation");
println!("✓ MCP-Protocol-Version header support");
println!("✓ Enhanced OAuth discovery with multiple fallback paths");
println!("✓ Proper canonical resource URI generation");
println!("✓ Full compliance with MCP Authorization specification");

Ok(())
}
5 changes: 5 additions & 0 deletions crates/mcp-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
pub mod client;
pub mod oauth;
pub mod service;
pub mod transport;

#[cfg(test)]
mod oauth_tests;

pub use client::{ClientCapabilities, ClientInfo, Error, McpClient, McpClientTrait};
pub use oauth::{authenticate_service, ServiceConfig};
pub use service::McpService;
pub use transport::{
SseTransport, StdioTransport, StreamableHttpTransport, Transport, TransportHandle,
Expand Down
Loading