| Crates.io | thulp-mcp |
| lib.rs | thulp-mcp |
| version | 0.2.0 |
| created_at | 2026-01-15 00:24:39.525178+00 |
| updated_at | 2026-01-15 07:15:37.205641+00 |
| description | MCP (Model Context Protocol) integration for thulp |
| homepage | |
| repository | https://github.com/dirmacs/thulp |
| max_upload_size | |
| id | 2044240 |
| size | 258,873 |
Model Context Protocol (MCP) Integration for Thulp
This crate provides transport implementations for connecting to MCP servers using the Model Context Protocol. It wraps rs-utcp to provide a Thulp-native interface for MCP tool discovery and execution.
ToolDefinitionAdd to your Cargo.toml:
[dependencies]
thulp-mcp = "0.1"
For Ares server support:
[dependencies]
thulp-mcp = { version = "0.1", features = ["ares"] }
Connect to a local MCP server using standard input/output:
use thulp_mcp::McpTransport;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Spawn and connect to a local MCP server
let transport = McpTransport::stdio(
"/path/to/mcp-server", // Server executable path
&["--verbose"], // Arguments
None, // Optional environment variables
).await?;
println!("Connected to MCP server!");
Ok(())
}
Connect to a remote MCP server over HTTPS:
use thulp_mcp::McpTransport;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let transport = McpTransport::https("https://mcp.example.com").await?;
println!("Connected to remote MCP server!");
Ok(())
}
Discover tools provided by the MCP server:
use thulp_mcp::McpTransport;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let transport = McpTransport::stdio(
"/path/to/mcp-server",
&[],
None,
).await?;
// List all available tools
let tools = transport.list_tools().await?;
for tool in &tools {
println!("Tool: {}", tool.name);
println!(" Description: {}", tool.description);
println!(" Parameters:");
for param in &tool.parameters {
println!(" - {}: {:?} (required: {})",
param.name,
param.parameter_type,
param.required
);
}
}
Ok(())
}
Execute an MCP tool with parameters:
use thulp_mcp::McpTransport;
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let transport = McpTransport::stdio(
"/path/to/mcp-server",
&[],
None,
).await?;
// Call a tool with parameters
let result = transport.call_tool(
"search",
json!({
"query": "rust programming",
"max_results": 10
})
).await?;
println!("Tool result: {}", result);
Ok(())
}
use thulp_mcp::McpTransport;
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect to MCP server
let transport = McpTransport::stdio(
"/usr/local/bin/weather-mcp-server",
&["--api-key", "your-api-key"],
None,
).await?;
// Discover available tools
let tools = transport.list_tools().await?;
println!("Available tools: {:?}", tools.iter().map(|t| &t.name).collect::<Vec<_>>());
// Call a tool
let weather = transport.call_tool(
"get_weather",
json!({
"location": "San Francisco, CA",
"units": "metric"
})
).await?;
println!("Weather: {}", weather);
// Disconnect (optional - happens automatically on drop)
transport.disconnect().await?;
Ok(())
}
The thulp-mcp crate automatically converts MCP JSON Schema tool definitions to Thulp's type system:
| MCP JSON Schema Type | Thulp ParameterType |
|---|---|
"string" |
ParameterType::String |
"integer" |
ParameterType::Integer |
"number" |
ParameterType::Number |
"boolean" |
ParameterType::Boolean |
"array" |
ParameterType::Array |
"object" |
ParameterType::Object |
Given this MCP tool schema:
{
"name": "create_file",
"description": "Create a new file",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "File path"
},
"content": {
"type": "string",
"description": "File content"
},
"overwrite": {
"type": "boolean",
"description": "Overwrite if exists"
}
},
"required": ["path", "content"]
}
}
Thulp will parse it as:
ToolDefinition {
name: "create_file",
description: "Create a new file",
parameters: vec![
Parameter {
name: "path",
description: "File path",
parameter_type: ParameterType::String,
required: true,
..
},
Parameter {
name: "content",
description: "File content",
parameter_type: ParameterType::String,
required: true,
..
},
Parameter {
name: "overwrite",
description: "Overwrite if exists",
parameter_type: ParameterType::Boolean,
required: false,
..
},
],
}
The crate provides detailed error types for different failure scenarios:
use thulp_mcp::{McpTransport, McpError};
#[tokio::main]
async fn main() {
let result = McpTransport::stdio("/invalid/path", &[], None).await;
match result {
Ok(transport) => println!("Connected!"),
Err(e) => {
eprintln!("Connection failed: {}", e);
// Handle specific error types
if let Some(io_err) = e.source() {
eprintln!("IO error: {}", io_err);
}
}
}
}
use thulp_mcp::McpTransport;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Connect
let mut transport = McpTransport::stdio("/path/to/server", &[], None).await?;
// 2. Use the transport
let tools = transport.list_tools().await?;
let result = transport.call_tool("some_tool", json!({})).await?;
// 3. Disconnect (optional - happens automatically on drop)
transport.disconnect().await?;
// 4. Reconnect if needed
transport.connect().await?;
Ok(())
}
The crate includes comprehensive tests including edge cases:
# Run all tests
cargo test -p thulp-mcp
# Run with output
cargo test -p thulp-mcp -- --nocapture
# Run specific test
cargo test -p thulp-mcp test_list_tools
McpTransport wraps rs-utcp's transport types:
StdioClientTransport to communicate with child processesSseClientTransport for Server-Sent Events over HTTPSThe crate implements rs-utcp's ToolProvider trait to enable tool discovery:
impl ToolProvider for McpToolProvider {
async fn call(&self, name: &str, args: Value) -> Result<Value> {
// Forward to actual tool implementation
}
}
ToolDefinition::parse_mcp_input_schema handles the conversion from MCP JSON Schema to Thulp parameters. It supports:
required array)rs-utcp v0.3.0aresEnables integration with the Ares MCP server implementation:
[dependencies]
thulp-mcp = { version = "0.1", features = ["ares"] }
This feature:
ares-server as a dependencySee the examples/ directory for more usage examples:
stdio_example.rs: Basic STDIO connectionhttps_example.rs: Remote HTTPS connectiontool_discovery.rs: Tool listing and introspectiontool_execution.rs: Calling tools with validationYou can also run the Thulp examples that demonstrate MCP integration:
# Run the MCP example (requires MCP feature)
cargo run --example mcp --features mcp
See the root examples directory for more comprehensive examples.
// Check server path
let result = McpTransport::stdio("/path/to/server", &[], None).await;
match result {
Err(e) => eprintln!("Failed to start server: {}", e),
Ok(_) => println!("Success!"),
}
// List available tools first
let tools = transport.list_tools().await?;
for tool in tools {
println!("Available: {}", tool.name);
}
// Check tool definition for required parameters
let tools = transport.list_tools().await?;
let tool = tools.iter().find(|t| t.name == "my_tool").unwrap();
for param in &tool.parameters {
if param.required {
println!("Required: {} ({:?})", param.name, param.parameter_type);
}
}
Contributions are welcome! Please ensure:
cargo test -p thulp-mcpcargo fmtcargo clippy -p thulp-mcpLicensed under either of:
at your option.