Skip to content

create rmcp-core crate to isolate implementations from traits and core logic. (step 1) #105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@


[workspace]
members = ["crates/rmcp", "crates/rmcp-macros", "examples/*"]
members = ["crates/rmcp","crates/rmcp-core", "crates/rmcp-macros", "examples/*"]
resolver = "2"

[workspace.dependencies]
rmcp-core = { version = "0.1.5", path = "./crates/rmcp-core" }
rmcp = { version = "0.1.5", path = "./crates/rmcp" }
rmcp-macros = { version = "0.1.5", path = "./crates/rmcp-macros" }

Expand Down
49 changes: 49 additions & 0 deletions crates/rmcp-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[package]
name = "rmcp-core"
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

[dependencies]
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = "1.0"
thiserror = "2"
chrono = { version = "0.4.38", features = ["serde"] }
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 }
# for auto generate schema
schemars = { version = "0.8", optional = true }

# for image encoding
base64 = { version = "0.22", optional = true }

# macro
rmcp-macros = { version = "0.1", workspace = true, optional = true }

[features]
default = ["base64", "macros"]
macros = ["dep:rmcp-macros", "dep:paste"]
schemars = ["dep:schemars"]

[dev-dependencies]
tokio = { version = "1", features = ["full"] }

anyhow = "1.0"
tracing-subscriber = { version = "0.3", features = [
"env-filter",
"std",
"fmt",
] }
async-trait = "0.1"
File renamed without changes.
9 changes: 9 additions & 0 deletions crates/rmcp-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mod error;
pub use error::Error;

pub mod model;
pub use model::*;
#[cfg(feature = "macros")]
pub use paste;
#[cfg(feature = "macros")]
pub use rmcp_macros::tool;
32 changes: 31 additions & 1 deletion crates/rmcp/src/model.rs → crates/rmcp-core/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ macro_rules! object {
};
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy, Eq)]
#[cfg_attr(feature = "server", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct EmptyObject {}

pub trait ConstString: Default {
Expand Down Expand Up @@ -1040,6 +1040,36 @@ impl From<CancelledNotification> for ClientNotification {
}
}

pub trait IntoCallToolResult {
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error>;
}
impl IntoCallToolResult for () {
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
Ok(CallToolResult::success(vec![]))
}
}

impl<T: IntoContents> IntoCallToolResult for T {
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
Ok(CallToolResult::success(self.into_contents()))
}
}

impl IntoCallToolResult for Result<CallToolResult, crate::Error> {
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
self
}
}

impl<T: IntoContents, E: IntoContents> IntoCallToolResult for Result<T, E> {
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
match self {
Ok(value) => Ok(CallToolResult::success(value.into_contents())),
Err(error) => Ok(CallToolResult::error(error.into_contents())),
}
}
}

#[cfg(test)]
mod tests {
use serde_json::json;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub struct RootsCapabilities {
///
/// # Builder
/// ```rust
/// # use rmcp::model::ClientCapabilities;
/// # use rmcp_core::model::ClientCapabilities;
/// let cap = ClientCapabilities::builder()
/// .enable_experimental()
/// .enable_roots()
Expand All @@ -58,7 +58,7 @@ pub struct ClientCapabilities {
///
/// ## Builder
/// ```rust
/// # use rmcp::model::ServerCapabilities;
/// # use rmcp_core::model::ServerCapabilities;
/// let cap = ServerCapabilities::builder()
/// .enable_logging()
/// .enable_experimental()
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext = Extensions::new();
/// assert!(ext.insert(5i32).is_none());
/// assert!(ext.insert(4u8).is_none());
Expand All @@ -79,7 +79,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext = Extensions::new();
/// assert!(ext.get::<i32>().is_none());
/// ext.insert(5i32);
Expand All @@ -98,7 +98,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext = Extensions::new();
/// ext.insert(String::from("Hello"));
/// ext.get_mut::<String>().unwrap().push_str(" World");
Expand All @@ -118,7 +118,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext = Extensions::new();
/// *ext.get_or_insert(1i32) += 2;
///
Expand All @@ -134,7 +134,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext = Extensions::new();
/// *ext.get_or_insert_with(|| 1i32) += 2;
///
Expand All @@ -158,7 +158,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext = Extensions::new();
/// *ext.get_or_insert_default::<i32>() += 2;
///
Expand All @@ -175,7 +175,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext = Extensions::new();
/// ext.insert(5i32);
/// assert_eq!(ext.remove::<i32>(), Some(5i32));
Expand All @@ -193,7 +193,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext = Extensions::new();
/// ext.insert(5i32);
/// ext.clear();
Expand All @@ -212,7 +212,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext = Extensions::new();
/// assert!(ext.is_empty());
/// ext.insert(5i32);
Expand All @@ -228,7 +228,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext = Extensions::new();
/// assert_eq!(ext.len(), 0);
/// ext.insert(5i32);
Expand All @@ -247,7 +247,7 @@ impl Extensions {
/// # Example
///
/// ```
/// # use rmcp::model::Extensions;
/// # use rmcp_core::model::Extensions;
/// let mut ext_a = Extensions::new();
/// ext_a.insert(8u8);
/// ext_a.insert(16u16);
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 5 additions & 4 deletions crates/rmcp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ paste = { version = "1", optional = true }
schemars = { version = "0.8", optional = true }

# for image encoding
base64 = { version = "0.21", optional = true }
base64 = { version = "0.22", optional = true }

# for SSE client
reqwest = { version = "0.12", default-features = false, features = [
Expand All @@ -52,11 +52,12 @@ tokio-stream = { version = "0.1", optional = true }

# macro
rmcp-macros = { version = "0.1", workspace = true, optional = true }
rmcp-core = { version = "0.1", workspace = true, optional = true, features = ["schemars"] }

[features]
default = ["base64", "macros", "server"]
client = []
server = ["transport-async-rw", "dep:schemars"]
default = ["base64", "macros", "server", "dep:rmcp-core"]
client = ["dep:rmcp-core"]
server = ["transport-async-rw", "dep:schemars", "dep:rmcp-core"]
macros = ["dep:rmcp-macros", "dep:paste"]
transport-sse = ["dep:reqwest", "dep:sse-stream", "dep:url"]
transport-async-rw = ["tokio/io-util", "tokio-util/codec"]
Expand Down
2 changes: 1 addition & 1 deletion crates/rmcp/src/handler/client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
error::Error as McpError,
Error as McpError,
model::*,
service::{Peer, RequestContext, RoleClient, Service, ServiceRole},
};
Expand Down
2 changes: 1 addition & 1 deletion crates/rmcp/src/handler/server.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
error::Error as McpError,
Error as McpError,
model::*,
service::{Peer, RequestContext, RoleServer, Service, ServiceRole},
};
Expand Down
32 changes: 1 addition & 31 deletions crates/rmcp/src/handler/server/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use tokio_util::sync::CancellationToken;

use crate::{
RoleServer,
model::{CallToolRequestParam, CallToolResult, ConstString, IntoContents, JsonObject},
model::{CallToolRequestParam, CallToolResult, ConstString, IntoCallToolResult, JsonObject},
service::RequestContext,
};
/// A shortcut for generating a JSON schema for a type.
Expand Down Expand Up @@ -86,30 +86,6 @@ pub trait FromToolCallContextPart<'a, S>: Sized {
) -> Result<(Self, ToolCallContext<'a, S>), crate::Error>;
}

pub trait IntoCallToolResult {
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error>;
}
impl IntoCallToolResult for () {
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
Ok(CallToolResult::success(vec![]))
}
}

impl<T: IntoContents> IntoCallToolResult for T {
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
Ok(CallToolResult::success(self.into_contents()))
}
}

impl<T: IntoContents, E: IntoContents> IntoCallToolResult for Result<T, E> {
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
match self {
Ok(value) => Ok(CallToolResult::success(value.into_contents())),
Err(error) => Ok(CallToolResult::error(error.into_contents())),
}
}
}

pin_project_lite::pin_project! {
#[project = IntoCallToolResultFutProj]
pub enum IntoCallToolResultFut<F, R> {
Expand Down Expand Up @@ -145,12 +121,6 @@ where
}
}

impl IntoCallToolResult for Result<CallToolResult, crate::Error> {
fn into_call_tool_result(self) -> Result<CallToolResult, crate::Error> {
self
}
}

pub trait CallToolHandler<'a, S, A> {
type Fut: Future<Output = Result<CallToolResult, crate::Error>> + Send + 'a;
fn call(self, context: ToolCallContext<'a, S>) -> Self::Fut;
Expand Down
4 changes: 1 addition & 3 deletions crates/rmcp/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
mod error;
pub use error::Error;
pub use rmcp_core::{Error, model};

/// Basic data types in MCP specification
pub mod model;
#[cfg(any(feature = "client", feature = "server"))]
pub mod service;
#[cfg(feature = "client")]
Expand Down
2 changes: 1 addition & 1 deletion crates/rmcp/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use futures::future::BoxFuture;
use thiserror::Error;

use crate::{
error::Error as McpError,
Error as McpError,
model::{
CancelledNotification, CancelledNotificationParam, Extensions, GetExtensions, GetMeta,
JsonRpcBatchRequestItem, JsonRpcBatchResponseItem, JsonRpcError, JsonRpcMessage,
Expand Down
2 changes: 1 addition & 1 deletion crates/rmcp/tests/common/calculator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rmcp::{
ServerHandler,
model::{ServerCapabilities, ServerInfo},
model::{IntoCallToolResult, ServerCapabilities, ServerInfo},
schemars, tool,
};
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
Expand Down
7 changes: 6 additions & 1 deletion crates/rmcp/tests/test_tool_macros.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use std::sync::Arc;

use rmcp::{ServerHandler, handler::server::tool::ToolCallContext, tool};
use rmcp::{
ServerHandler,
handler::server::tool::ToolCallContext,
model::IntoCallToolResult,
tool,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

Expand Down
5 changes: 2 additions & 3 deletions examples/clients/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


[package]
name = "mcp-client-examples"
version = "0.1.5"
Expand All @@ -13,12 +11,13 @@ rmcp = { path = "../../crates/rmcp", features = [
"transport-child-process",
"tower"
] }
rmcp-core = { path = "../../crates/rmcp-core", features = ["macros"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
rand = "0.8"
rand = "0.9"
futures = "0.3"
anyhow = "1.0"

Expand Down
2 changes: 1 addition & 1 deletion examples/clients/src/everything_stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use anyhow::Result;
use rmcp::{
ServiceExt,
model::{CallToolRequestParam, GetPromptRequestParam, ReadResourceRequestParam},
object,
transport::TokioChildProcess,
};
use rmcp_core::object;
use tokio::process::Command;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

Expand Down
Loading