* feat: implement MCP elicitation support for interactive user input Adds comprehensive elicitation functionality according to MCP 2025-06-18 specification: Core Features: - ElicitationAction enum (Accept, Decline, Cancel) - CreateElicitationRequestParam and CreateElicitationResult structures - Protocol version V_2025_06_18 with elicitation methods - Full JSON-RPC integration with method constants Capabilities Integration: - ElicitationCapability with schema validation support - ClientCapabilities builder pattern integration - enable_elicitation() and enable_elicitation_schema_validation() methods Handler Support: - create_elicitation method in ClientHandler and ServerHandler traits - Integration with existing request/response union types - Async/await compatible implementation Service Layer: - Basic create_elicitation method via macro expansion - Four convenience methods for common scenarios: * elicit_confirmation() - yes/no questions * elicit_text_input() - string input with optional requirements * elicit_choice() - selection from multiple options * elicit_structured_input() - complex data via JSON Schema Comprehensive Testing: - 11 test cases covering all functionality aspects - JSON serialization/deserialization validation - MCP specification compliance verification - Error handling and edge cases - Performance benchmarks - Capabilities integration tests All tests pass and code follows project standards. * feat: add typed elicitation API with enhanced error handling - Add new 'elicitation' feature that depends on 'client' and 'schemars' - Implement elicit<T>() method for type-safe elicitation with automatic schema generation - Remove convenience methods (elicit_confirmation, elicit_text_input, elicit_choice) - Add ElicitationError enum with detailed error variants: - Service: underlying service errors - UserDeclined: user cancelled or declined request - ParseError: response parsing failed with context - NoContent: no response content provided - Update documentation with comprehensive examples and error handling - Add comprehensive tests for typed elicitation and error handling * fix: correct elicitation direction to comply with MCP 2025-06-18 - Remove CreateElicitationRequest from ClientRequest - clients cannot initiate elicitation - Move elicit methods from client to server - servers now request user input - Add comprehensive direction tests verifying Server→Client→Server flow - Maintain CreateElicitationResult in ClientResult for proper responses - Update handlers to reflect correct message routing - Add elicitation feature flag for typed schema generation Fixes elicitation direction to match specification where servers request interactive user input from clients, not the reverse. * feat: add elicitation capability checking for server methods - Add supports_elicitation() method to check client capabilities - Add CapabilityNotSupported error variant to ElicitationError - Update elicit_structured_input() to check capabilities before execution - Update elicit<T>() method to check capabilities before execution - Add comprehensive tests for capability checking functionality - Tests verify that servers check client capabilities before sending elicitation requests - Ensures compliance with MCP 2025-06-18 specification requirement * fix: json rpc message schema * fix: doc tests * fix: cargo nightly fmt checks * fix: clippy * refactor: separate elicitation methods into dedicated impl block for RoleServer - Move (supports_elicitation, elicit_structured_input, elicit) to separate impl block - Move ElicitationError definition to elicitation methods section - Keep base methods (create_message, list_roots, notify_*) in main impl block with macro - Add section comments to distinguish general and elicitation-specific methods * revert: rollback LATEST protocol version to V_2025_03_26 * fix: remove protocol version assertions - Remove assertions for V_2025_06_18 protocol version * fix: fmt checks * feat: add timeout support for elicitation methods - Add peer_req_with_timeout macro variants for timeout-enabled methods - Implement create_elicitation_with_timeout() method - Implement elicit_with_timeout() for typed elicitation with timeout - Refactor elicit() to use elicit_with_timeout() internally - Add 8 comprehensive timeout tests covering validation, error handling, and realistic scenarios - Fix elicitation feature dependencies in Cargo.toml - Add proper feature gates for elicitation-specific code * feat: add timeout validation to prevent DoS attacks - Add InvalidTimeout error variant for comprehensive validation - Implement validate_timeout function with security limits (1ms-300s) - Integrate validation into peer_req_with_timeout macros - Add comprehensive security tests for timeout validation - Prevent DoS attacks through unreasonable timeout values * feat: separate UserDeclined and UserCancelled elicitation errors According to MCP specification and PR feedback, decline and cancel actions should be handled differently: - UserDeclined: explicit user rejection (clicked "Decline", "No", etc.) - UserCancelled: dismissal without explicit choice (closed dialog, Escape, etc.) Changes: - Split ElicitationError::UserDeclined into two distinct error types - Update error handling logic to map each ElicitationAction correctly - Improve documentation with proper action semantics - Add comprehensive tests for new error types and action mapping - Update examples to demonstrate proper error handling This provides better error granularity allowing servers to handle explicit declines vs cancellations appropriately as per MCP spec. * feat: add compile-time type safety for elicitation methods Add ElicitationSafe trait and elicit_safe\! macro to ensure elicit<T>() methods are only used with types that generate appropriate JSON object schemas, addressing type safety concerns from PR feedback. Features: - ElicitationSafe marker trait for compile-time constraints - elicit_safe\! macro for opt-in type safety declaration - Updated elicit<T> and elicit_with_timeout<T> to require ElicitationSafe bound - Comprehensive documentation with examples and rationale - Full test coverage for new type safety features This prevents common mistakes like: - elicit::<String>() - primitives not suitable for object schemas - elicit::<Vec<i32>>() - arrays don't match client expectations Breaking change: Existing code must add elicit_safe\!(TypeName) declarations for types used with elicit methods. This is an intentional safety improvement. * Revert "feat: add timeout validation to prevent DoS attacks" This reverts commit 829624212b4fb55ec32566329af7ec195c987ef5. * fix: correct doctest example in elicit_safe macro documentation - Remove invalid async/await usage in doctest example - Comment out the actual usage line to show intent without compilation errors - Maintain clear documentation of the macro's purpose and usage * refactor: remove redundant elicitation direction tests - Remove test_elicitation_not_in_client_request (duplicated functionality) - Remove redundant ServerRequest match in test_elicitation_direction_server_to_client - Direction compliance is already verified by the remaining comprehensive test - Reduces test fragility and maintenance burden * feat: add elicitation example with user name collection - Add elicitation server example demonstrating real MCP usage - Implement greet_user tool with context.peer.elicit::<T>() API - Show type-safe elicitation with elicit_safe! macro - Include reset_name tool and MCP Inspector instructions - Update examples documentation and dependencies * fix: add Default impl to ElicitationServer for clippy Resolves clippy::new_without_default warning by implementing Default trait for ElicitationServer struct.
209 lines
5.2 KiB
TOML
209 lines
5.2 KiB
TOML
[package]
|
|
name = "rmcp"
|
|
license = { workspace = true }
|
|
version = { workspace = true }
|
|
edition = { workspace = true }
|
|
repository = { workspace = true }
|
|
homepage = { workspace = true }
|
|
readme = { workspace = true }
|
|
description = "Rust SDK for Model Context Protocol"
|
|
documentation = "https://docs.rs/rmcp"
|
|
|
|
[package.metadata.docs.rs]
|
|
all-features = true
|
|
rustdoc-args = ["--cfg", "docsrs"]
|
|
|
|
[dependencies]
|
|
serde = { version = "1.0", features = ["derive", "rc"] }
|
|
serde_json = "1.0"
|
|
thiserror = "2"
|
|
tokio = { version = "1", features = ["sync", "macros", "rt", "time"] }
|
|
futures = "0.3"
|
|
tracing = { version = "0.1" }
|
|
tokio-util = { version = "0.7" }
|
|
pin-project-lite = "0.2"
|
|
paste = { version = "1", optional = true }
|
|
|
|
# oauth2 support
|
|
oauth2 = { version = "5.0", optional = true }
|
|
|
|
# for auto generate schema
|
|
schemars = { version = "1.0", optional = true, features = ["chrono04"] }
|
|
|
|
# for image encoding
|
|
base64 = { version = "0.22", optional = true }
|
|
|
|
# for SSE client
|
|
reqwest = { version = "0.12", default-features = false, features = [
|
|
"json",
|
|
"stream",
|
|
], optional = true }
|
|
|
|
sse-stream = { version = "0.2", optional = true }
|
|
|
|
http = { version = "1", optional = true }
|
|
url = { version = "2.4", optional = true }
|
|
|
|
# For tower compatibility
|
|
tower-service = { version = "0.3", optional = true }
|
|
|
|
# for child process transport
|
|
process-wrap = { version = "8.2", features = ["tokio1"], optional = true }
|
|
|
|
# for ws transport
|
|
# tokio-tungstenite ={ version = "0.26", optional = true }
|
|
|
|
# for http-server transport
|
|
axum = { version = "0.8", features = [], optional = true }
|
|
rand = { version = "0.9", optional = true }
|
|
tokio-stream = { version = "0.1", optional = true }
|
|
uuid = { version = "1", features = ["v4"], optional = true }
|
|
http-body = { version = "1", optional = true }
|
|
http-body-util = { version = "0.1", optional = true }
|
|
bytes = { version = "1", optional = true }
|
|
# macro
|
|
rmcp-macros = { version = "0.2.1", workspace = true, optional = true }
|
|
[target.'cfg(not(all(target_family = "wasm", target_os = "unknown")))'.dependencies]
|
|
chrono = { version = "0.4.38", features = ["serde"] }
|
|
|
|
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
|
|
chrono = { version = "0.4.38", default-features = false, features = [
|
|
"serde",
|
|
"clock",
|
|
"std",
|
|
"oldtime",
|
|
] }
|
|
|
|
[features]
|
|
default = ["base64", "macros", "server"]
|
|
client = ["dep:tokio-stream"]
|
|
server = ["transport-async-rw", "dep:schemars"]
|
|
macros = ["dep:rmcp-macros", "dep:paste"]
|
|
elicitation = []
|
|
|
|
# reqwest http client
|
|
__reqwest = ["dep:reqwest"]
|
|
|
|
reqwest = ["__reqwest", "reqwest?/rustls-tls"]
|
|
|
|
reqwest-tls-no-provider = ["__reqwest", "reqwest?/rustls-tls-no-provider"]
|
|
|
|
server-side-http = [
|
|
"uuid",
|
|
"dep:rand",
|
|
"dep:tokio-stream",
|
|
"dep:http",
|
|
"dep:http-body",
|
|
"dep:http-body-util",
|
|
"dep:bytes",
|
|
"dep:sse-stream",
|
|
"tower",
|
|
]
|
|
# SSE client
|
|
client-side-sse = ["dep:sse-stream", "dep:http"]
|
|
|
|
transport-sse-client = ["client-side-sse", "transport-worker"]
|
|
|
|
transport-worker = ["dep:tokio-stream"]
|
|
|
|
|
|
# Streamable HTTP client
|
|
transport-streamable-http-client = ["client-side-sse", "transport-worker"]
|
|
|
|
|
|
transport-async-rw = ["tokio/io-util", "tokio-util/codec"]
|
|
transport-io = ["transport-async-rw", "tokio/io-std"]
|
|
transport-child-process = [
|
|
"transport-async-rw",
|
|
"tokio/process",
|
|
"dep:process-wrap",
|
|
]
|
|
transport-sse-server = [
|
|
"transport-async-rw",
|
|
"transport-worker",
|
|
"server-side-http",
|
|
"dep:axum",
|
|
]
|
|
transport-streamable-http-server = [
|
|
"transport-streamable-http-server-session",
|
|
"server-side-http",
|
|
]
|
|
transport-streamable-http-server-session = [
|
|
"transport-async-rw",
|
|
"dep:tokio-stream",
|
|
]
|
|
# transport-ws = ["transport-io", "dep:tokio-tungstenite"]
|
|
tower = ["dep:tower-service"]
|
|
auth = ["dep:oauth2", "__reqwest", "dep:url"]
|
|
schemars = ["dep:schemars"]
|
|
|
|
[dev-dependencies]
|
|
tokio = { version = "1", features = ["full"] }
|
|
schemars = { version = "1.0", features = ["chrono04"] }
|
|
|
|
anyhow = "1.0"
|
|
tracing-subscriber = { version = "0.3", features = [
|
|
"env-filter",
|
|
"std",
|
|
"fmt",
|
|
] }
|
|
async-trait = "0.1"
|
|
[[test]]
|
|
name = "test_tool_macros"
|
|
required-features = ["server", "client"]
|
|
path = "tests/test_tool_macros.rs"
|
|
|
|
[[test]]
|
|
name = "test_with_python"
|
|
required-features = [
|
|
"reqwest",
|
|
"server",
|
|
"client",
|
|
"transport-sse-server",
|
|
"transport-sse-client",
|
|
"transport-child-process",
|
|
]
|
|
path = "tests/test_with_python.rs"
|
|
|
|
[[test]]
|
|
name = "test_with_js"
|
|
required-features = [
|
|
"server",
|
|
"client",
|
|
"transport-sse-server",
|
|
"transport-child-process",
|
|
"transport-streamable-http-server",
|
|
"transport-streamable-http-client",
|
|
"__reqwest",
|
|
]
|
|
path = "tests/test_with_js.rs"
|
|
|
|
[[test]]
|
|
name = "test_notification"
|
|
required-features = ["server", "client"]
|
|
path = "tests/test_notification.rs"
|
|
|
|
[[test]]
|
|
name = "test_logging"
|
|
required-features = ["server", "client"]
|
|
path = "tests/test_logging.rs"
|
|
|
|
[[test]]
|
|
name = "test_message_protocol"
|
|
required-features = ["client"]
|
|
path = "tests/test_message_protocol.rs"
|
|
|
|
[[test]]
|
|
name = "test_message_schema"
|
|
required-features = ["server", "client", "schemars"]
|
|
path = "tests/test_message_schema.rs"
|
|
|
|
[[test]]
|
|
name = "test_progress_subscriber"
|
|
required-features = ["server", "client", "macros"]
|
|
path = "tests/test_progress_subscriber.rs"
|
|
|
|
[[test]]
|
|
name = "test_elicitation"
|
|
required-features = ["elicitation", "client", "server"]
|
|
path = "tests/test_elicitation.rs"
|