Move whole rmcp crate to offcial rust sdk (#44)
* get rid of async-trait * move rmcp to official-rust-sdk * replace master with main * update readme, change license * fix typo * Update README.md Co-authored-by: Jefry Dewangga <jefrydco@gmail.com> * fix typos --------- Co-authored-by: = <=> Co-authored-by: Jefry Dewangga <jefrydco@gmail.com>
This commit is contained in:
parent
31493cfbcc
commit
0be6067326
5 changed files with 8676 additions and 4466 deletions
27
examples/wasi/Cargo.toml
Normal file
27
examples/wasi/Cargo.toml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
[package]
|
||||
name = "wasi"
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
description.workspace = true
|
||||
keywords.workspace = true
|
||||
homepage.workspace = true
|
||||
categories.workspace = true
|
||||
readme.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
wasi = { version = "0.14.2+wasi-0.2.4"}
|
||||
tokio = { version = "1", features = ["rt", "io-util", "sync", "macros", "time"] }
|
||||
rmcp= { path = "../../crates/rmcp", features = ["server", "macros"] }
|
||||
serde = { version = "1", features = ["derive"]}
|
||||
tracing-subscriber = { version = "0.3", features = [
|
||||
"env-filter",
|
||||
"std",
|
||||
"fmt",
|
||||
] }
|
||||
tracing = "0.1"
|
||||
4
examples/wasi/README.md
Normal file
4
examples/wasi/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
```sh
|
||||
cargo build -p wasi --target wasm32-wasip2
|
||||
npx @modelcontextprotocol/inspector wasmtime target/wasm32-wasip2/debug/wasi.wasm
|
||||
```
|
||||
2
examples/wasi/config.toml
Normal file
2
examples/wasi/config.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[build]
|
||||
target = "wasm32-wasip2"
|
||||
46
examples/wasi/src/calculator.rs
Normal file
46
examples/wasi/src/calculator.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
use rmcp::{
|
||||
ServerHandler,
|
||||
model::{ServerCapabilities, ServerInfo},
|
||||
schemars, tool, tool_box,
|
||||
};
|
||||
|
||||
#[derive(Debug, rmcp::serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct SumRequest {
|
||||
#[schemars(description = "the left hand side number")]
|
||||
pub a: i32,
|
||||
pub b: i32,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Calculator;
|
||||
impl Calculator {
|
||||
#[tool(description = "Calculate the sum of two numbers")]
|
||||
fn sum(&self, #[tool(aggr)] SumRequest { a, b }: SumRequest) -> String {
|
||||
(a + b).to_string()
|
||||
}
|
||||
|
||||
#[tool(description = "Calculate the sum of two numbers")]
|
||||
fn sub(
|
||||
&self,
|
||||
#[tool(param)]
|
||||
#[schemars(description = "the left hand side number")]
|
||||
a: i32,
|
||||
#[tool(param)]
|
||||
#[schemars(description = "the left hand side number")]
|
||||
b: i32,
|
||||
) -> String {
|
||||
(a - b).to_string()
|
||||
}
|
||||
|
||||
tool_box!(Calculator { sum, sub });
|
||||
}
|
||||
|
||||
impl ServerHandler for Calculator {
|
||||
tool_box!(@derive);
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo {
|
||||
instructions: Some("A simple calculator".into()),
|
||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
121
examples/wasi/src/lib.rs
Normal file
121
examples/wasi/src/lib.rs
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
pub mod calculator;
|
||||
use std::task::{Poll, Waker};
|
||||
|
||||
use rmcp::ServiceExt;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use wasi::{
|
||||
cli::{
|
||||
stdin::{InputStream, get_stdin},
|
||||
stdout::{OutputStream, get_stdout},
|
||||
},
|
||||
io::streams::Pollable,
|
||||
};
|
||||
|
||||
pub fn wasi_io() -> (AsyncInputStream, AsyncOutputStream) {
|
||||
let input = AsyncInputStream { inner: get_stdin() };
|
||||
let output = AsyncOutputStream {
|
||||
inner: get_stdout(),
|
||||
};
|
||||
(input, output)
|
||||
}
|
||||
|
||||
pub struct AsyncInputStream {
|
||||
inner: InputStream,
|
||||
}
|
||||
|
||||
impl AsyncRead for AsyncInputStream {
|
||||
fn poll_read(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &mut tokio::io::ReadBuf<'_>,
|
||||
) -> std::task::Poll<std::io::Result<()>> {
|
||||
let bytes = self
|
||||
.inner
|
||||
.read(buf.remaining() as u64)
|
||||
.map_err(std::io::Error::other)?;
|
||||
if bytes.is_empty() {
|
||||
let pollable = self.inner.subscribe();
|
||||
let waker = cx.waker().clone();
|
||||
runtime_poll(waker, pollable);
|
||||
return Poll::Pending;
|
||||
}
|
||||
buf.put_slice(&bytes);
|
||||
std::task::Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AsyncOutputStream {
|
||||
inner: OutputStream,
|
||||
}
|
||||
fn runtime_poll(waker: Waker, pollable: Pollable) {
|
||||
tokio::task::spawn(async move {
|
||||
loop {
|
||||
if pollable.ready() {
|
||||
waker.wake();
|
||||
break;
|
||||
} else {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
impl AsyncWrite for AsyncOutputStream {
|
||||
fn poll_write(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, std::io::Error>> {
|
||||
let writable_len = self.inner.check_write().map_err(std::io::Error::other)?;
|
||||
if writable_len == 0 {
|
||||
let pollable = self.inner.subscribe();
|
||||
let waker = cx.waker().clone();
|
||||
runtime_poll(waker, pollable);
|
||||
return Poll::Pending;
|
||||
}
|
||||
let bytes_to_write = buf.len().min(writable_len as usize);
|
||||
self.inner
|
||||
.write(&buf[0..bytes_to_write])
|
||||
.map_err(std::io::Error::other)?;
|
||||
Poll::Ready(Ok(bytes_to_write))
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<Result<(), std::io::Error>> {
|
||||
self.inner.flush().map_err(std::io::Error::other)?;
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<Result<(), std::io::Error>> {
|
||||
self.poll_flush(cx)
|
||||
}
|
||||
}
|
||||
|
||||
struct TokioCliRunner;
|
||||
|
||||
impl wasi::exports::cli::run::Guest for TokioCliRunner {
|
||||
fn run() -> Result<(), ()> {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
rt.block_on(async move {
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
EnvFilter::from_default_env().add_directive(tracing::Level::DEBUG.into()),
|
||||
)
|
||||
.with_writer(std::io::stderr)
|
||||
.with_ansi(false)
|
||||
.init();
|
||||
let server = calculator::Calculator.serve(wasi_io()).await.unwrap();
|
||||
server.waiting().await.unwrap();
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
wasi::cli::command::export!(TokioCliRunner);
|
||||
Loading…
Reference in a new issue