Build MCP (Model Context Protocol) servers with TypeScript and Deno
A high-performance, type-safe bridge for building Model Context Protocol servers using TypeScript and Deno. Seamlessly integrates with the Rust pforge runtime via FFI while providing an ergonomic TypeScript API.
import { PforgeBridge } from "https://raw.githubusercontent.com/paiml/pforge/main/bridges/deno/bridge.ts";
// Create bridge
const bridge = new PforgeBridge();
// Register a tool
bridge.register({
name: "greet",
description: "Greet a user by name",
handler: (input: { name: string }) => ({
success: true,
data: { message: `Hello, ${input.name}! 👋` },
}),
});
// Execute the tool
const result = await bridge.execute("greet", { name: "Alice" });
console.log(result);
// => { success: true, data: { message: "Hello, Alice! 👋" } }
// Cleanup
bridge.close();- Deno (v2.0+): Install Deno
- Rust pforge library: Build the FFI bridge
cd ../../ # Navigate to pforge root cargo build -p pforge-bridge --release
import { PforgeBridge } from "https://raw.githubusercontent.com/paiml/pforge/main/bridges/deno/bridge.ts";┌─────────────────────────────────────┐
│ Your TypeScript MCP Server │
│ ┌───────────────────────────────┐ │
│ │ PforgeBridge │ │
│ │ • register() - Add tools │ │
│ │ • execute() - Run tools │ │
│ │ • list() - List tools │ │
│ └────────┬──────────┬───────────┘ │
│ │ │ │
│ ┌────────▼──────┐ │ │
│ │ TypeScript │ │ │
│ │ Handlers │ │ │
│ └───────────────┘ │ │
│ │ │
│ ┌─────────▼──────────┐ │
│ │ FFI Bridge (Deno) │ │
│ └─────────┬──────────┘ │
└─────────────────────┼───────────────┘
│
┌───────▼────────┐
│ Rust pforge │
│ (C ABI / FFI) │
└────────────────┘
Key Components:
- PforgeBridge: High-level API for tool registration and execution
- HandlerRegistry: O(1) lookup, type-safe handler management
- FfiBridge: Low-level FFI bindings to Rust pforge runtime
- Generic Handlers: Full TypeScript type inference
- Strict Type Checking: Compile-time safety
- Runtime Schema Validation: Built-in JSON schema validation (docs)
- Result Types:
{ success: true, data: T } | { success: false, error: string }
- O(1) Handler Lookup: HashMap-based registry
- Zero-Copy JSON: Minimal serialization overhead
- 9.5µs Execution: Per-call overhead (reused bridge)
- Memory Safe: Validated with 1000+ property-based tests
- Simple API: Just
register()andexecute() - Async/Sync Support: Both handler types work
- Clear Errors: Descriptive error messages
- Working Examples: Learn by example
Main class for building MCP servers.
const bridge = new PforgeBridge();Register a new tool handler.
bridge.register({
name: "calculate",
description: "Perform calculations",
handler: (input: { a: number; b: number; op: string }) => {
const operations = {
add: input.a + input.b,
subtract: input.a - input.b,
multiply: input.a * input.b,
};
return {
success: true,
data: { result: operations[input.op] },
};
},
timeoutMs: 5000, // Optional timeout (default: 30000)
});Parameters:
name: Unique tool namedescription: Tool descriptionhandler: Function that processes input and returns resulttimeoutMs: Optional timeout in milliseconds
Execute a tool by name.
const result = await bridge.execute("calculate", {
a: 10,
b: 5,
op: "add",
});
if (result.success) {
console.log(result.data); // => { result: 15 }
} else {
console.error(result.error);
}Returns: Promise<HandlerResult<TOutput>>
Get list of registered tool names.
const tools = bridge.list();
console.log(tools); // => ["calculate", "greet", "fetch_data"]Check if a tool is registered.
if (bridge.has("calculate")) {
// Tool exists
}Get number of registered tools.
console.log(bridge.count()); // => 3Get pforge version.
console.log(bridge.version()); // => "0.1.2"Clean up FFI resources. Always call when done.
bridge.close();Tool handler definition.
interface HandlerDef<TInput = unknown, TOutput = unknown> {
name: string;
description: string;
handler: HandlerFn<TInput, TOutput>;
timeoutMs?: number;
}Handler function signature. Can be sync or async.
type HandlerFn<TInput, TOutput> = (
input: TInput,
context: HandlerContext,
) => Promise<HandlerResult<TOutput>> | HandlerResult<TOutput>;Handler execution result.
type HandlerResult<T> =
| { success: true; data: T }
| { success: false; error: string };Execution context passed to handlers.
interface HandlerContext {
handlerName: string;
timestamp: Date;
log: Logger;
}bridge.register({
name: "uppercase",
description: "Convert text to uppercase",
handler: (input: { text: string }) => ({
success: true,
data: { result: input.text.toUpperCase() },
}),
});bridge.register({
name: "fetch_user",
description: "Fetch user data",
handler: async (input: { userId: number }) => {
// Validate input
if (input.userId < 1) {
return {
success: false,
error: "Invalid user ID",
};
}
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 100));
return {
success: true,
data: {
id: input.userId,
name: "Alice",
email: "alice@example.com",
},
};
},
timeoutMs: 5000,
});bridge.register({
name: "divide",
description: "Divide two numbers",
handler: (input: { a: number; b: number }) => {
if (input.b === 0) {
return {
success: false,
error: "Cannot divide by zero",
};
}
return {
success: true,
data: { result: input.a / input.b },
};
},
});
// Execute with error
const result = await bridge.execute("divide", { a: 10, b: 0 });
if (!result.success) {
console.error(result.error); // => "Cannot divide by zero"
}bridge.register({
name: "log_operation",
description: "Operation with logging",
handler: (input: { value: number }, context) => {
context.log.info(`Processing value: ${input.value}`);
context.log.debug(`Handler: ${context.handlerName}`);
context.log.debug(`Time: ${context.timestamp.toISOString()}`);
return {
success: true,
data: { processed: input.value * 2 },
};
},
});Automatically validate input with runtime type checking. Full documentation
import { SchemaBuilder } from "./schema.ts";
bridge.register({
name: "create_user",
description: "Create a new user",
handler: (input: { name: string; age: number; email: string }) => ({
success: true,
data: { id: 123, ...input }
}),
inputSchema: SchemaBuilder.object({
name: SchemaBuilder.string({ minLength: 1, maxLength: 100 }),
age: SchemaBuilder.number({ min: 0, max: 120 }),
email: SchemaBuilder.string({ minLength: 5 }),
}, ["name", "age", "email"]), // Required fields
});
// Valid input - passes validation
const result1 = await bridge.execute("create_user", {
name: "Alice",
age: 30,
email: "alice@example.com"
});
// => { success: true, data: { ... } }
// Invalid input - fails validation
const result2 = await bridge.execute("create_user", {
name: "Alice",
age: -5 // Invalid!
});
// => { success: false, error: "Validation failed: Field 'age' must be at least 0" }Supported types: string (with minLength/maxLength), number (with min/max), boolean, array, object
See also: Complete Schema Validation Guide
deno test --unstable-ffi --allow-ffi --allow-env --allow-read tests/- Unit Tests (42 tests): Core functionality
- Integration Tests (22 tests): End-to-end workflows
- Property Tests (10 tests): Memory safety, 1000+ iterations each
deno bench --unstable-ffi --allow-ffi --allow-env --allow-read benchmarks/| Metric | Value |
|---|---|
| FFI Overhead (reused bridge) | ~9.5µs per call |
| Create/Close Overhead | ~300µs |
| Throughput (sequential) | >100K req/s |
| Memory per Handler | <256 bytes |
| Cold Start | <100ms |
- Zero SATD: No technical debt comments
- Complexity ≤ 20: Per-function cyclomatic complexity
- 100% Tests Passing: 57/57 tests green
- Memory Safe: Validated with property-based tests
- Type Safe: Strict TypeScript compilation
bridges/deno/
├── bridge.ts # Main PforgeBridge class
├── handler.ts # Handler registry and types
├── ffi.ts # Low-level FFI bindings
├── examples/
│ └── hello_server.ts # Complete working example
├── tests/
│ ├── unit/ # Unit tests
│ ├── integration/ # Integration tests
│ └── property/ # Property-based tests
├── benchmarks/ # Performance benchmarks
└── README.md # This file
deno run --unstable-ffi --allow-ffi --allow-env --allow-read examples/hello_server.ts# Format code
deno fmt
# Lint code
deno lint
# Run tests
deno test --unstable-ffi --allow-ffi --allow-env --allow-read tests/
# All quality gates
deno fmt --check && deno lint && deno test --unstable-ffi --allow-ffi --allow-env --allow-read tests/- Follow EXTREME TDD methodology (5-minute cycles)
- Maintain zero SATD comments
- Keep complexity ≤ 20 per function
- Ensure all tests pass
- Run quality gates before committing
See LICENSE in the pforge repository.
Built with:
- Deno - Secure TypeScript runtime
- Rust pforge - High-performance MCP runtime
- EXTREME TDD - Toyota Way + Test-Driven Development
Status: Production-ready for basic MCP server development
Version: 0.1.0
Maintained by: pforge team