Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
turbomcp-wasm
WebAssembly bindings for TurboMCP - MCP client and server for browsers, edge environments, and WASI runtimes.
Write Once, Run Everywhere
TurboMCP v3 supports portable MCP handlers that can run on both native platforms (Linux, macOS, Windows) and WASM/edge environments with zero code changes. Write your McpHandler implementation once, then deploy to any target:
use *;
;
// Native: Use run_tcp(), run_http(), run_websocket(), or serve() (stdio)
// WASM: Use WasmHandlerExt trait for Cloudflare Workers
WASM Deployment with WasmHandlerExt
Any McpHandler can be used directly in Cloudflare Workers via the WasmHandlerExt extension trait:
use WasmHandlerExt;
use *;
async
This enables sharing business logic between native servers and edge deployments without code duplication.
Features
Client (Browser & WASI)
- Browser Support: Full MCP client using Fetch API and WebSocket
- Type-Safe: All MCP types available in JavaScript/TypeScript
- Async/Await: Modern Promise-based API
- Small Binary: Optimized for minimal bundle size (~50-200KB)
Server (wasm-server feature)
- Edge MCP Servers: Build MCP servers running on Cloudflare Workers and other edge platforms
- Ergonomic API: Just write async functions - inspired by axum's IntoResponse pattern
- Type-Safe Handlers: Automatic JSON schema generation from Rust types
- Idiomatic Error Handling: Full
?operator support - Zero Tokio: Uses wasm-bindgen-futures for async, no tokio runtime needed
- Full MCP Protocol: Tools, resources, prompts, and all standard MCP methods
Installation
Client (NPM)
Server (Rust)
[]
= { = "3.0", = false, = ["wasm-server"] }
= "0.7"
= { = "1.0", = ["derive"] }
= "1.0"
= { = "0.3", = ["wasm_js"] }
Client Usage
Browser (ES Modules)
import init from 'turbomcp-wasm';
.;
TypeScript
import init, { McpClient, Tool, Resource, Content } from 'turbomcp-wasm';
async function main(): Promise<void> {
await init();
const client = new McpClient("https://api.example.com/mcp");
await client.initialize();
const tools: Tool[] = await client.listTools();
const resources: Resource[] = await client.listResources();
}
Server Usage (Cloudflare Workers)
Basic Server - Ergonomic API
use *;
use *;
use Deserialize;
// Just write async functions - return any type implementing IntoToolResponse!
async
async
async
With Error Handling
use *;
// Use Result for error handling - errors automatically become tool errors
async
// Use the ? operator for automatic error propagation
async
Parameter Descriptions
Add descriptions to tool parameters using doc comments or schemars attributes:
use Deserialize;
use JsonSchema;
// Option 1: Doc comments (preferred)
// Option 2: schemars attribute
These descriptions appear in the JSON schema and help LLMs understand how to use your tools correctly.
Return Type Flexibility
use *;
// Return String
async
// Return JSON
async
// Return Result with automatic error handling
async
// Return ToolResult for full control
async
// Return () for empty success
async
// Return Option
async
With Authentication (Cloudflare Access)
use *;
use CloudflareAccessAuthenticator;
use *;
async
Important: Always use worker::Env to retrieve secrets at runtime. Never hardcode credentials in your code.
Secret Management Best Practices
use *;
use *;
async
Configure secrets in wrangler.toml:
[]
= "https://db.example.com"
= "your-team"
= "your-aud-tag"
# Secrets should be set via wrangler CLI, not in config:
# wrangler secret put API_KEY
Security
TurboMCP WASM implements defense-in-depth security measures to protect against common JWT and authentication vulnerabilities.
JWT Security Features
Algorithm Confusion Attack Prevention:
- Algorithm whitelist is mandatory - tokens are rejected if no algorithms are configured
- Key-type validation ensures RSA keys can only be used with RS* algorithms, EC keys with ES* algorithms
- The
"none"algorithm is always rejected
use ;
// ✅ CORRECT: Always use JwtConfig::new() for secure defaults
let config = new // Defaults to [RS256, ES256]
.issuer
.audience;
// ✅ CORRECT: Or explicitly specify algorithms
let config = new
.algorithms;
// ❌ WRONG: Never create config with empty algorithms
// The Default trait is NOT implemented to prevent this mistake
JWKS Security:
- JWKS URLs must use HTTPS (HTTP is rejected to prevent MITM attacks)
- Localhost URLs are allowed for development
- Use
allow_insecure_http()only for testing (never in production)
use JwksCache;
// ✅ CORRECT: HTTPS URL
let cache = new;
// ✅ OK: Localhost for development
let cache = new;
// ⚠️ DANGER: Only for testing!
let cache = new
.allow_insecure_http;
Claim Validation:
- Expiration (
exp) validation is enabled by default - Not-before (
nbf) validation is enabled by default - Issuer (
iss) and audience (aud) validation when configured - 60-second clock skew leeway (configurable)
Request Security
- Maximum request body size: 1MB (DoS protection)
- POST-only enforcement for JSON-RPC
- Content-Type validation
- Strict JSON-RPC 2.0 compliance
CORS Security
TurboMCP implements secure CORS handling to prevent credentials from being exposed to arbitrary origins:
- Origin Echo: The request
Originheader is echoed back inAccess-Control-Allow-Origininstead of using* - Vary Header:
Vary: Originis automatically added when origin-specific responses are returned (required for proper caching) - Non-Browser Fallback: Only falls back to
*when no Origin header is present (e.g., curl, Postman)
This approach:
- Prevents credentials leakage to malicious sites
- Enables proper CORS preflight validation
- Works correctly with CDN caching
# Browser request with Origin header
Request:
Origin: https://app.example.com
Response:
Access-Control-Allow-Origin: https://app.example.com
Vary: Origin
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Request-ID
Access-Control-Max-Age: 86400
# Non-browser request (no Origin header)
Response:
Access-Control-Allow-Origin: *
# (No Vary header when using wildcard)
OAuth and Token Protection
TurboMCP supports multiple authentication patterns for protecting MCP servers:
1. Cloudflare Access (Recommended for Production)
Cloudflare Access provides enterprise-grade zero-trust authentication with automatic key rotation:
use CloudflareAccessAuthenticator;
let auth = new;
let protected = server.with_auth;
2. Custom JWT Validation
For self-hosted OAuth/OIDC providers:
use ;
// Configure JWT validation
let config = new
.algorithms
.issuer
.audience;
// Set up JWKS caching for signature verification
let jwks = new;
// Create validator
let validator = new;
3. Bearer Token (Development Only)
For simple API key authentication during development:
async
⚠️ Warning: Simple Bearer tokens lack rotation and are vulnerable to theft. Use OAuth/OIDC for production.
Cloudflare Access Integration
When using Cloudflare Access, the CloudflareAccessAuthenticator enforces additional security:
- Only RS256 algorithm is allowed
- JWKS fetched from Cloudflare's official endpoint
- Validates
Cf-Access-Jwt-Assertionheader (or falls back toAuthorization: Bearer)
use CloudflareAccessAuthenticator;
// CloudflareAccessAuthenticator automatically:
// - Uses HTTPS JWKS endpoint
// - Restricts to RS256 only
// - Validates issuer and audience
let auth = new;
Path and URI Security
TurboMCP WASM runs in edge environments (Cloudflare Workers, Deno Deploy) where there is typically no filesystem access. Resources are URIs pointing to:
- KV stores (
kv://namespace/key) - Durable Objects (
do://object/path) - External APIs (
https://api.example.com/resource) - D1 databases (
d1://database/table)
Path Traversal Protection: When implementing resource handlers that map URIs to storage backends, you are responsible for validating and sanitizing URIs to prevent unauthorized access:
use *;
async
Best Practices:
- Validate URI schemes (reject unexpected prefixes)
- Sanitize path segments (reject
.., absolute paths, special characters) - Use allowlists for valid characters when possible
- Log suspicious URI patterns for security monitoring
WASM Architecture Considerations
TurboMCP uses MaybeSend/MaybeSync marker traits to support both native and WASM environments:
- Native: Requires
Send + Syncfor multi-threaded executors (tokio, etc.) - WASM: No thread-safety requirements (single-threaded execution model)
This architecture allows you to use Rc<RefCell<T>> for shared state in WASM handlers:
use Rc;
use RefCell;
// This works in WASM but would NOT compile for native targets
For portable code that works on both native and WASM, use Arc<RwLock<T>> instead.
Security Checklist
- Use
JwtConfig::new()instead of manual construction - Always configure
issuerandaudiencein JwtConfig - Use HTTPS for all JWKS endpoints
- Store secrets using
env.secret(), never hardcode - Use Cloudflare Access for production deployments
- Configure rate limiting at the Cloudflare level
- CORS: Origin is echoed by default for security;
*is only used for non-browser clients - Validate and sanitize URIs in resource handlers
- Use allowlists for URI path components where possible
With Resources and Prompts
use *;
use *;
async
API Reference
Client Methods
| Method | Description |
|---|---|
withAuth(token: string) |
Add Bearer token authentication |
withHeader(key: string, value: string) |
Add custom header |
withTimeout(ms: number) |
Set request timeout |
initialize() |
Initialize MCP session |
isInitialized() |
Check if session is initialized |
getServerInfo() |
Get server implementation info |
getServerCapabilities() |
Get server capabilities |
listTools() |
List available tools |
callTool(name: string, args?: object) |
Call a tool |
listResources() |
List available resources |
readResource(uri: string) |
Read a resource |
listResourceTemplates() |
List resource templates |
listPrompts() |
List available prompts |
getPrompt(name: string, args?: object) |
Get a prompt |
ping() |
Ping the server |
Server Builder Methods
| Method | Description |
|---|---|
builder(name, version) |
Create new server builder |
description(text) |
Set server description |
instructions(text) |
Set server instructions |
tool(name, desc, handler) |
Register tool (ergonomic API) |
tool_no_args(name, desc, handler) |
Register tool without arguments |
tool_raw(name, desc, handler) |
Register tool with raw JSON args |
resource(uri, name, desc, handler) |
Register static resource |
resource_template(uri, name, desc, handler) |
Register resource template |
prompt(name, desc, handler) |
Register prompt with typed args |
prompt_no_args(name, desc, handler) |
Register prompt without args |
build() |
Build the server |
Return Types (IntoToolResponse)
| Type | Behavior |
|---|---|
String, &str |
Returns as text content |
Json<T> |
Serializes to JSON text |
ToolResult |
Full control over response |
Result<T, E> |
Ok becomes success, Err becomes error |
() |
Empty success response |
Option<T> |
None returns "No result" |
(A, B) |
Combines multiple contents |
Error Types
| From Type | Conversion |
|---|---|
std::io::Error |
Auto-converts to ToolError |
serde_json::Error |
Auto-converts to ToolError |
String, &str |
Direct message |
Box<dyn Error> |
Auto-converts to ToolError |
worker::Error |
Via WorkerError wrapper or WorkerResultExt trait |
Worker Error Integration
Due to Rust's orphan rules, worker::Error cannot directly convert to ToolError. TurboMCP provides two ergonomic solutions:
Option 1: WorkerError wrapper
use ;
async
Option 2: WorkerResultExt trait (more ergonomic)
use ;
async
Both approaches enable full ? operator support when working with Cloudflare Workers APIs (KV, Durable Objects, R2, D1, etc.).
Binary Size
| Configuration | Size |
|---|---|
| Core types only | ~50KB |
| + JSON serialization | ~100KB |
| + HTTP client | ~200KB |
| wasm-server feature | ~536KB |
Browser Compatibility
- Chrome 89+
- Firefox 89+
- Safari 15+
- Edge 89+
Requires support for:
- WebAssembly
- Fetch API
- ES2020 (async/await)
WASI Support
WASI Preview 2 support for running in server-side WASM runtimes:
- Wasmtime 29+
- WasmEdge
- Wasmer
License
MIT