| Crates.io | oxify-mcp |
| lib.rs | oxify-mcp |
| version | 0.1.0 |
| created_at | 2026-01-19 04:52:21.004081+00 |
| updated_at | 2026-01-19 04:52:21.004081+00 |
| description | Model Context Protocol (MCP) server implementation for OxiFY |
| homepage | |
| repository | https://github.com/cool-japan/oxify |
| max_upload_size | |
| id | 2053714 |
| size | 314,707 |
Model Context Protocol (MCP) Implementation for Oxify
A comprehensive Rust implementation of the Model Context Protocol, providing both client and server capabilities with built-in servers for common operations.
The Model Context Protocol (MCP) is a standardized protocol for exposing tools and capabilities to AI models. oxify-mcp provides:
list_tools) with JSON Schema validationcall_tool) with typed parametersCredentialStoredatabase feature)Add oxify-mcp to your Cargo.toml:
[dependencies]
oxify-mcp = { path = "../oxify-mcp" }
# For database support:
oxify-mcp = { path = "../oxify-mcp", features = ["database"] }
use oxify_mcp::{McpRegistry, McpClient, DefaultMcpClient, McpServer};
use oxify_mcp::servers::{FilesystemServer, ShellServer};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a registry
let mut registry = McpRegistry::new();
// Register built-in servers
registry.register_local_server(
"filesystem",
Box::new(FilesystemServer::new("/tmp")?),
).await?;
registry.register_local_server(
"shell",
Box::new(ShellServer::new(vec!["ls", "pwd", "echo"])),
).await?;
// List all available tools
let tools = registry.list_all_tools().await?;
println!("Available tools: {}", tools.len());
// Execute a tool
let result = registry.invoke_tool(
"fs_read",
json!({ "path": "example.txt" }),
).await?;
println!("Result: {}", result);
Ok(())
}
use oxify_mcp::{DefaultMcpClient, McpClient};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect via HTTP
let client = DefaultMcpClient::http("http://localhost:8080/mcp", None)?;
// List tools
let tools = client.list_tools().await?;
// Call a tool
let result = client.call_tool(
"some_tool",
json!({ "param": "value" }),
).await?;
Ok(())
}
Provides secure filesystem operations with sandboxing.
use oxify_mcp::servers::FilesystemServer;
use serde_json::json;
// Create server with base directory
let server = FilesystemServer::new("/home/user/workspace")?;
// Read file
let content = server.call_tool("fs_read", json!({
"path": "example.txt"
})).await?;
// Write file
server.call_tool("fs_write", json!({
"path": "output.txt",
"content": "Hello, world!"
})).await?;
// List directory
let files = server.call_tool("fs_list", json!({
"path": "."
})).await?;
Tools: fs_read, fs_write, fs_list, fs_delete, fs_exists
Security: Prevents path traversal attacks, enforces base directory sandboxing.
Execute shell commands with whitelisting.
use oxify_mcp::servers::ShellServer;
use serde_json::json;
// Create server with allowed commands
let server = ShellServer::new(vec!["ls", "pwd", "git", "npm"]);
// Execute command
let result = server.call_tool("shell_exec", json!({
"command": "ls",
"args": ["-la"],
"working_dir": "/tmp"
})).await?;
// Find command
let path = server.call_tool("shell_which", json!({
"command": "git"
})).await?;
Tools: shell_exec, shell_which
Security: Only whitelisted commands can be executed.
Git repository operations.
use oxify_mcp::servers::GitServer;
use serde_json::json;
let server = GitServer::new();
// Get status
let status = server.call_tool("git_status", json!({
"repo_path": "/path/to/repo"
})).await?;
// Commit changes
server.call_tool("git_commit", json!({
"repo_path": "/path/to/repo",
"message": "feat: add new feature"
})).await?;
Tools: git_status, git_clone, git_add, git_commit, git_push, git_pull, git_log, git_diff, git_branch, git_checkout
HTTP operations and web scraping.
use oxify_mcp::servers::WebServer;
use serde_json::json;
let server = WebServer::new();
// HTTP GET
let html = server.call_tool("http_get", json!({
"url": "https://example.com"
})).await?;
// HTTP POST
server.call_tool("http_post", json!({
"url": "https://api.example.com/data",
"body": "{\"key\": \"value\"}",
"headers": {"Content-Type": "application/json"}
})).await?;
// Web scraping
let text = server.call_tool("web_scrape", json!({
"url": "https://example.com",
"selector": "h1"
})).await?;
Tools: http_get, http_post, web_scrape, web_screenshot (not yet implemented)
PostgreSQL database operations (requires database feature).
use oxify_mcp::servers::{DatabaseServer, DatabaseConfig};
use serde_json::json;
// Create server
let config = DatabaseConfig::postgres("postgres://user:pass@localhost/db")
.with_max_connections(5)
.with_read_only(false);
let server = DatabaseServer::new(config).await?;
// Query
let results = server.call_tool("db_query", json!({
"sql": "SELECT * FROM users WHERE age > 18"
})).await?;
// Execute
server.call_tool("db_execute", json!({
"sql": "INSERT INTO users (name, age) VALUES ('Alice', 25)"
})).await?;
// Transaction
server.call_tool("db_transaction", json!({
"statements": [
{"sql": "UPDATE accounts SET balance = balance - 100 WHERE id = 1"},
{"sql": "UPDATE accounts SET balance = balance + 100 WHERE id = 2"}
]
})).await?;
// Describe table
let schema = server.call_tool("db_describe", json!({
"table": "users"
})).await?;
Tools: db_query, db_execute, db_transaction, db_describe, db_tables
Features:
Expose oxify workflows as MCP tools.
use oxify_mcp::servers::WorkflowServer;
use oxify_model::Workflow;
let mut server = WorkflowServer::new();
// Register workflow
let workflow = Workflow {
id: "sentiment_analysis".to_string(),
name: "Sentiment Analysis".to_string(),
description: Some("Analyze sentiment of text".to_string()),
// ... workflow definition
};
server.register_workflow(workflow, None)?;
// Now "sentiment_analysis" tool is available
let result = server.call_tool("sentiment_analysis", json!({
"text": "I love this product!"
})).await?;
Features:
use oxify_mcp::auth::{ApiKeyAuth, AuthenticatedHttpTransport};
let auth = ApiKeyAuth::new("my-api-key")
.with_prefix("Bearer"); // Optional: "Bearer my-api-key"
let transport = AuthenticatedHttpTransport::new(
"http://localhost:8080/mcp",
auth.into(),
None, // timeout
)?;
let client = DefaultMcpClient::new(Box::new(transport));
use oxify_mcp::auth::{BasicAuth, AuthenticatedHttpTransport};
let auth = BasicAuth::new("username", "password");
let transport = AuthenticatedHttpTransport::new(
"http://localhost:8080/mcp",
auth.into(),
None,
)?;
use oxify_mcp::auth::{BearerAuth, AuthenticatedHttpTransport};
let auth = BearerAuth::new("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");
let transport = AuthenticatedHttpTransport::new(
"http://localhost:8080/mcp",
auth.into(),
None,
)?;
use oxify_mcp::auth::{CustomHeaderAuth, AuthenticatedHttpTransport};
use std::collections::HashMap;
let mut headers = HashMap::new();
headers.insert("X-Custom-Auth".to_string(), "secret123".to_string());
headers.insert("X-API-Version".to_string(), "v1".to_string());
let auth = CustomHeaderAuth::new(headers);
let transport = AuthenticatedHttpTransport::new(
"http://localhost:8080/mcp",
auth.into(),
None,
)?;
use oxify_mcp::auth::{OAuth2Auth, AuthConfig, AuthenticatedHttpTransport};
// Create OAuth2 auth with client credentials
let mut auth = OAuth2Auth::client_credentials(
"https://auth.example.com/oauth/token",
"your-client-id",
"your-client-secret",
).with_scopes(vec!["read".to_string(), "write".to_string()]);
// Request initial token
auth.request_token().await?;
// Create authenticated transport
let config = AuthConfig {
method: AuthMethod::OAuth2(auth),
scopes: Vec::new(),
};
let transport = AuthenticatedHttpTransport::new(
"http://localhost:8080/mcp",
config,
None,
)?;
use oxify_mcp::auth::OAuth2Auth;
let mut auth = OAuth2Auth::authorization_code(
"https://auth.example.com/oauth/token",
"your-client-id",
None, // No client secret (PKCE)
"authorization-code-from-redirect",
)
.with_pkce("code-verifier-123") // PKCE support
.with_scopes(vec!["read".to_string()]);
// Exchange authorization code for tokens
auth.request_token().await?;
use oxify_mcp::auth::OAuth2Auth;
// Create OAuth2 auth with existing tokens
let mut auth = OAuth2Auth::with_tokens(
"https://auth.example.com/oauth/token",
"your-client-id",
Some("your-client-secret".to_string()),
"existing-access-token",
"existing-refresh-token",
);
// Automatically refresh if expired
let refreshed = auth.refresh_if_needed().await?;
if refreshed {
println!("Token was refreshed");
}
// Check if token is expired
if auth.is_token_expired() {
// Token needs refresh
}
use oxify_mcp::auth::{CredentialStore, ApiKeyAuth, BasicAuth};
let mut store = CredentialStore::new();
// Different credentials for each server
store.set_credentials(
"server1",
ApiKeyAuth::new("key1").into(),
);
store.set_credentials(
"server2",
BasicAuth::new("user", "pass").into(),
);
// Use when registering servers
if let Some(creds) = store.get_credentials("server1") {
// Create authenticated transport with creds
}
use oxify_mcp::{McpRegistry, LoadBalancingStrategy};
let mut registry = McpRegistry::new();
registry.set_load_balancing_strategy(LoadBalancingStrategy::RoundRobin);
// Register multiple servers for the same tool
// Calls will be distributed evenly
registry.set_load_balancing_strategy(LoadBalancingStrategy::LeastConnections);
// Routes to server with fewest active connections
use oxify_mcp::{LoadBalancingStrategy, ServerWeight};
registry.set_load_balancing_strategy(LoadBalancingStrategy::Weighted);
// Set server weights (higher = more traffic)
registry.set_server_weight("server1", 3); // 75% of traffic
registry.set_server_weight("server2", 1); // 25% of traffic
// Automatic failover to backup servers
let result = registry.invoke_tool_with_failover(
"my_tool",
json!({"param": "value"}),
).await?;
// If first server fails, tries next available server
// Tag servers by capability
registry.tag_server("server1", "fast");
registry.tag_server("server2", "slow");
// Route to servers with specific tags
let servers = registry.get_servers_by_tag("fast");
Implement the McpServer trait to create custom servers:
use oxify_mcp::{McpServer, Result};
use async_trait::async_trait;
use serde_json::{json, Value};
pub struct MyCustomServer {
// Server state
}
#[async_trait]
impl McpServer for MyCustomServer {
async fn list_tools(&self) -> Result<Vec<Value>> {
Ok(vec![
json!({
"name": "my_tool",
"description": "Does something useful",
"inputSchema": {
"type": "object",
"properties": {
"input": {
"type": "string",
"description": "Input parameter"
}
},
"required": ["input"]
}
})
])
}
async fn call_tool(&self, name: &str, arguments: Value) -> Result<Value> {
match name {
"my_tool" => {
let input = arguments
.get("input")
.and_then(|v| v.as_str())
.ok_or_else(|| {
oxify_mcp::McpError::InvalidArgument(
"input is required".to_string()
)
})?;
// Do something with input
let result = format!("Processed: {}", input);
Ok(json!({ "result": result }))
}
_ => Err(oxify_mcp::McpError::ToolNotFound(name.to_string())),
}
}
}
my_tool, process_data)McpError variantsThe MCP client is integrated with oxify-engine for workflow execution:
use oxify_mcp::MCP_EXECUTOR;
use serde_json::json;
// MCP tools are automatically available in workflows
// Priority: Local servers → HTTP MCP → HTTP fallback
// Register local server
MCP_EXECUTOR.register_local_server(
"my_server",
Box::new(my_server),
).await?;
// Execute tool from workflow
let result = MCP_EXECUTOR.execute_tool(
"fs_read",
json!({"path": "data.txt"}),
).await?;
The MCP registry is fully integrated with oxify-api, providing REST endpoints:
# List registered servers with metrics
GET /api/v1/mcp/servers
# List available tools (optionally filtered by server_id)
POST /api/v1/mcp/tools
{
"server_id": "filesystem" // optional
}
# Invoke a tool
POST /api/v1/mcp/tools/invoke
{
"server_id": "filesystem",
"tool_name": "fs_read",
"parameters": {"path": "example.txt"}
}
# Get registry statistics
GET /api/v1/mcp/stats
See the examples/ directory:
Run an example:
cargo run --example basic_usage
cargo run --example transport_example
cargo run --example api_integration
cargo run --features database --example basic_usage
Run all tests:
# Without database feature
cargo test
# With database feature
cargo test --features database
# Show test output
cargo test -- --nocapture
# Run specific test
cargo test test_fs_read
Test Coverage:
Apache-2.0 - See LICENSE file for details.
Contributions are welcome! Please ensure:
cargo test)cargo check)cargo fmt)cargo clippy)Features:
Security:
Performance:
For more information, see the TODO.md for development roadmap and implementation details.