Skip to content

Commit 43c8fe4

Browse files
committed
Better launch, tool handling
1 parent 9eaf84d commit 43c8fe4

File tree

3 files changed

+273
-99
lines changed

3 files changed

+273
-99
lines changed

docs/MCP.md

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,107 @@ The response is a `CallToolResult` event whose `content` array contains a single
8787

8888
Error cases are mapped to `CallToolError` with `is_error = true`.
8989

90-
## 5. Limitations & future work
90+
## 5. Example clients
91+
92+
### Rust
93+
94+
```rust
95+
use anyhow::Result;
96+
use rust_mcp_sdk::{
97+
mcp_client::client_runtime,
98+
schema::{
99+
CallToolRequestParams, ClientCapabilities, CreateMessageRequest,
100+
Implementation, InitializeRequestParams, Message, LATEST_PROTOCOL_VERSION,
101+
},
102+
ClientSseTransport, ClientSseTransportOptions,
103+
};
104+
105+
struct Handler;
106+
#[async_trait::async_trait]
107+
impl rust_mcp_sdk::mcp_client::ClientHandler for Handler {}
108+
109+
#[tokio::main]
110+
async fn main() -> Result<()> {
111+
let transport = ClientSseTransport::new(
112+
"http://localhost:4321/mcp/stream",
113+
ClientSseTransportOptions::default(),
114+
)?;
115+
116+
let details = InitializeRequestParams {
117+
capabilities: ClientCapabilities::default(),
118+
client_info: Implementation { name: "mcp-client".into(), version: "0.1".into() },
119+
protocol_version: LATEST_PROTOCOL_VERSION.into(),
120+
};
121+
122+
let client = client_runtime::create_client(details, transport, Handler);
123+
client.clone().start().await?;
124+
125+
let req = CreateMessageRequest {
126+
model: "mistralai/Mistral-7B-Instruct-v0.3".into(),
127+
messages: vec![Message::user("Explain Rust ownership.")],
128+
..Default::default()
129+
};
130+
131+
let result = client
132+
.call_tool(CallToolRequestParams::new("chat", req.into()))
133+
.await?;
134+
135+
println!("{}", result.content[0].as_text_content()?.text);
136+
client.shut_down().await?;
137+
Ok(())
138+
}
139+
```
140+
141+
### Python
142+
143+
```py
144+
import json
145+
import requests
146+
from sseclient import SSEClient
147+
148+
payload = {
149+
"kind": "callToolRequest",
150+
"id": "123",
151+
"params": {
152+
"name": "chat",
153+
"arguments": {
154+
"model": "mistralai/Mistral-7B-Instruct-v0.3",
155+
"messages": [
156+
{"role": "user", "content": "Explain Rust ownership."}
157+
],
158+
},
159+
},
160+
}
161+
162+
resp = requests.post(
163+
"http://localhost:4321/mcp/stream",
164+
headers={"Content-Type": "application/json"},
165+
data=json.dumps(payload),
166+
stream=True,
167+
)
168+
for event in SSEClient(resp):
169+
print(event.data)
170+
```
171+
172+
### HTTP
173+
174+
```bash
175+
curl -N -X POST http://localhost:4321/mcp/stream \
176+
-H 'Content-Type: application/json' \
177+
-d '{
178+
"kind": "callToolRequest",
179+
"id": "123",
180+
"params": {
181+
"name": "chat",
182+
"arguments": {
183+
"model": "mistralai/Mistral-7B-Instruct-v0.3",
184+
"messages": [{"role": "user", "content": "Explain Rust ownership."}]
185+
}
186+
}
187+
}'
188+
```
189+
190+
## 6. Limitations & future work
91191

92192
• Only synchronous, single-shot requests are supported right now.
93193
• Streaming responses (`partialCallToolResult`) are not yet implemented.

mistralrs-server/src/main.rs

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use anyhow::Result;
22
use clap::Parser;
33
use mistralrs_core::{initialize_logging, ModelSelected, TokenSource};
44
use rust_mcp_sdk::schema::LATEST_PROTOCOL_VERSION;
5+
use tokio::join;
56
use tracing::info;
67

78
use mistralrs_server_core::{
@@ -26,7 +27,7 @@ struct Args {
2627

2728
/// Port to serve on.
2829
#[arg(short, long)]
29-
port: Option<String>,
30+
port: Option<u16>,
3031

3132
/// Log all responses and requests to this file
3233
#[clap(long, short)]
@@ -191,45 +192,54 @@ async fn main() -> Result<()> {
191192
return Ok(());
192193
}
193194

194-
// Needs to be after the .build call as that is where the daemon waits.
195-
let setting_server = if !args.interactive_mode {
196-
let port = args.port.expect("Interactive mode was not specified, so expected port to be specified. Perhaps you forgot `-i` or `--port`?");
197-
let ip = args
198-
.serve_ip
199-
.clone()
200-
.unwrap_or_else(|| "0.0.0.0".to_string());
201-
202-
// Create listener early to validate address before model loading
203-
let listener = tokio::net::TcpListener::bind(format!("{ip}:{port}")).await?;
204-
Some((listener, ip, port))
205-
} else {
206-
None
207-
};
195+
if !args.interactive_mode && args.port.is_none() && args.mcp_port.is_none() {
196+
anyhow::bail!("Interactive mode was not specified, so expected port to be specified. Perhaps you forgot `-i` or `--port` or `--mcp-port`?")
197+
}
208198

209-
if let Some(port) = args.mcp_port {
199+
let mcp_port = if let Some(port) = args.mcp_port {
210200
let host = args
211201
.serve_ip
212202
.clone()
213203
.unwrap_or_else(|| "0.0.0.0".to_string());
214204
info!("MCP server listening on http://{host}:{port}.");
215205
info!("MCP protocol version is {}.", LATEST_PROTOCOL_VERSION);
216206
let mcp_server = mcp_server::create_mcp_server(mistralrs.clone(), host, port);
207+
217208
tokio::spawn(async move {
218209
if let Err(e) = mcp_server.start().await {
219210
eprintln!("MCP server error: {e}");
220211
}
221-
});
222-
}
212+
})
213+
} else {
214+
tokio::spawn(async {})
215+
};
223216

224-
let app = MistralRsServerRouterBuilder::new()
225-
.with_mistralrs(mistralrs)
226-
.build()
227-
.await?;
217+
let oai_port = if let Some(port) = args.port {
218+
let ip = args
219+
.serve_ip
220+
.clone()
221+
.unwrap_or_else(|| "0.0.0.0".to_string());
222+
223+
// Create listener early to validate address before model loading
224+
let listener = tokio::net::TcpListener::bind(format!("{ip}:{port}")).await?;
225+
226+
let app = MistralRsServerRouterBuilder::new()
227+
.with_mistralrs(mistralrs)
228+
.build()
229+
.await?;
228230

229-
if let Some((listener, ip, port)) = setting_server {
230231
info!("OpenAI-compatible server listening on http://{ip}:{port}.");
231-
axum::serve(listener, app).await?;
232+
233+
tokio::spawn(async move {
234+
if let Err(e) = axum::serve(listener, app).await {
235+
eprintln!("OpenAI server error: {e}");
236+
}
237+
})
238+
} else {
239+
tokio::spawn(async {})
232240
};
233241

242+
let (_, _) = join!(oai_port, mcp_port);
243+
234244
Ok(())
235245
}

0 commit comments

Comments
 (0)