3 releases
| 0.1.2 | Oct 3, 2025 |
|---|---|
| 0.1.1 | Oct 3, 2025 |
| 0.1.0 | Oct 2, 2025 |
#369 in HTTP server
2,529 downloads per month
Used in 3 crates
180KB
4K
SLoC
pforge-runtime
Core runtime for building high-performance Model Context Protocol (MCP) servers with pforge.
Features
- Type-Safe Handlers: Define MCP tools with full Rust type safety
- Multiple Handler Types: Native Rust, CLI wrappers, HTTP proxies, and pipelines
- State Management: Built-in persistent state with memory and Sled backends
- Middleware System: Composable middleware for logging, validation, recovery, and metrics
- Resource Management: URI-based resource handling with template matching
- Prompt Templates: Mustache-style templating for dynamic prompts
- Circuit Breaker: Fault tolerance with configurable circuit breaking
- Retry Policies: Exponential backoff with jitter
- Error Tracking: Automatic error classification and monitoring
Performance
Built on pmcp (Pragmatic AI Labs MCP SDK):
- < 1μs hot handler dispatch (O(1) lookup with FxHash)
- > 100K req/s sequential throughput
- > 500K req/s concurrent throughput (8-core)
- < 512KB memory baseline
- < 256B memory per tool
Installation
cargo add pforge-runtime
Quick Example
use pforge_runtime::prelude::*;
use serde_json::{json, Value};
#[derive(Default)]
struct CalculatorHandler;
#[async_trait::async_trait]
impl Handler for CalculatorHandler {
async fn handle(&self, params: Value) -> Result<Value> {
let a = params["a"].as_i64().ok_or(Error::Validation("Missing 'a'".into()))?;
let b = params["b"].as_i64().ok_or(Error::Validation("Missing 'b'".into()))?;
let op = params["op"].as_str().ok_or(Error::Validation("Missing 'op'".into()))?;
let result = match op {
"add" => a + b,
"sub" => a - b,
"mul" => a * b,
"div" if b != 0 => a / b,
_ => return Err(Error::Validation("Invalid operation".into())),
};
Ok(json!({ "result": result }))
}
}
#[tokio::main]
async fn main() -> Result<()> {
let mut registry = HandlerRegistry::new();
registry.register("calculator", Arc::new(CalculatorHandler::default()));
// Use the registry...
Ok(())
}
Handler Types
Native Handlers
Pure Rust handlers compiled into your binary:
#[async_trait::async_trait]
impl Handler for MyHandler {
async fn handle(&self, params: Value) -> Result<Value> {
// Your logic here
Ok(json!({"status": "success"}))
}
}
CLI Handlers
Wrap command-line tools as MCP tools:
- type: cli
name: git_status
description: "Check git status"
command: git
args: ["status", "--short"]
HTTP Handlers
Proxy HTTP endpoints as MCP tools:
- type: http
name: fetch_user
description: "Fetch user data"
endpoint: "https://api.example.com/users/{id}"
method: GET
Pipeline Handlers
Chain multiple tools together:
- type: pipeline
name: process_data
description: "Fetch and transform data"
steps:
- tool: fetch_data
- tool: transform
- tool: validate
Middleware
Build composable middleware stacks:
use pforge_runtime::{MiddlewareChain, LoggingMiddleware, ValidationMiddleware, RecoveryMiddleware};
let mut chain = MiddlewareChain::new();
// Add logging
chain.add(Arc::new(LoggingMiddleware::new("my-server")));
// Add validation
chain.add(Arc::new(ValidationMiddleware::new(vec!["input".to_string()])));
// Add recovery with circuit breaker
chain.add(Arc::new(RecoveryMiddleware::new()
.with_circuit_breaker(CircuitBreakerConfig {
failure_threshold: 5,
timeout: Duration::from_secs(60),
success_threshold: 2,
})));
// Execute through middleware
let result = chain.execute(params, |req| async move {
// Handler logic
Ok(json!({"output": req["input"]}))
}).await?;
State Management
Persistent state for your handlers:
use pforge_runtime::{StateManager, MemoryStateManager};
let state = MemoryStateManager::new();
// Set a value
state.set("user:123", b"Alice".to_vec(), None).await?;
// Get a value
if let Some(value) = state.get("user:123").await? {
println!("User: {}", String::from_utf8_lossy(&value));
}
// Delete a value
state.delete("user:123").await?;
Circuit Breaker
Prevent cascading failures:
use pforge_runtime::{CircuitBreaker, CircuitBreakerConfig};
let cb = CircuitBreaker::new(CircuitBreakerConfig {
failure_threshold: 5, // Open after 5 failures
timeout: Duration::from_secs(60), // Try again after 60s
success_threshold: 2, // Close after 2 successes
});
let result = cb.call(|| async {
// Potentially failing operation
risky_operation().await
}).await?;
Retry Policies
Resilient execution with exponential backoff:
use pforge_runtime::{retry_with_policy, RetryPolicy};
let policy = RetryPolicy::new(3)
.with_backoff(Duration::from_millis(100), Duration::from_secs(5))
.with_jitter(true);
let result = retry_with_policy(&policy, || async {
unstable_operation().await
}).await?;
Documentation
License
MIT
Dependencies
~20–40MB
~544K SLoC