Overview
xmcp is a framework that compiles your TypeScript/React code into MCP (Model Context Protocol) servers. It consists of three main components:
- Compiler - Discovers and bundles your tools/prompts/resources
- Runtime - Handles MCP protocol communication and request routing
- Adapters - Integrates with existing frameworks (Next.js, Express, NestJS)
How xmcp Works
Compilation Process
When you run xmcp dev or xmcp build, the compiler:
- Discovers files - Scans
src/tools/, src/prompts/, and src/resources/ directories
- Generates import maps - Creates
.xmcp/import-map.js with all discovered modules
- Builds client bundles - For React components (
.tsx files), transpiles them into browser-ready bundles
- Bundles server code - Uses Rspack to bundle everything into
dist/ directory
- Generates types - Creates
xmcp-env.d.ts for global type definitions
// Generated .xmcp/import-map.js (simplified)
export const tools = {
"greet": () => import("../src/tools/greet.ts"),
"users_[userId]_profile": () => import("../src/tools/users/[userId]/profile.ts")
};
export const prompts = {
"review-code": () => import("../src/prompts/review-code.ts")
};
File Watching (Dev Mode)
In development mode, the compiler watches for changes:
- Tools/Prompts/Resources - Automatically regenerates import maps and recompiles
- Middleware - Detects
src/middleware.ts and recompiles
- Config changes - Reloads
xmcp.config.ts and rebuilds
From packages/xmcp/src/compiler/index.ts:61-95:
const watcher = new Watcher({
persistent: mode === "development",
ignored: /(^|[\/\\])\../,
ignoreInitial: false,
});
watcher.watch(`${toolsPath}/**/*.{ts,tsx}`, {
onAdd: async (path) => {
toolPaths.add(path);
if (compilerStarted) {
await generateCode();
}
},
onUnlink: async (path) => {
toolPaths.delete(path);
if (compilerStarted) {
await generateCode();
}
},
});
MCP Protocol Integration
xmcp implements the Model Context Protocol, which defines how AI applications communicate with context providers (your tools, prompts, and resources).
Protocol Handlers
The runtime automatically handles MCP protocol methods:
tools/list - Lists all available tools
tools/call - Executes a tool with given arguments
prompts/list - Lists all available prompts
prompts/get - Retrieves a prompt with arguments
resources/list - Lists all available resources
resources/read - Reads resource content
Request Flow
Transport Layers
xmcp supports two transport mechanisms:
HTTP Transport
Stateless HTTP/SSE (Server-Sent Events) for web-based integrations.
// packages/xmcp/src/runtime/transports/http/stateless-streamable-http.ts
export class StatelessHttpServerTransport {
async handleRequest(
req: IncomingMessage,
res: ServerResponse,
bodyContent: any
) {
// Parse JSON-RPC request
// Route to appropriate handler
// Send JSON-RPC response
}
}
Configuration:
// xmcp.config.ts
export default {
http: {
port: 3001,
host: "127.0.0.1",
endpoint: "/mcp",
bodySizeLimit: 1024 * 1024 * 10, // 10MB
cors: {
origin: "*",
methods: ["GET", "POST"],
credentials: false
}
}
}
stdio Transport
CLI-based communication using stdin/stdout (used by Claude Desktop, Cursor, etc.).
// packages/xmcp/src/runtime/transports/stdio/index.ts
export class StdioTransport {
constructor(mcpServer: McpServer, debug: boolean = false) {
this.transport = new StdioServerTransport();
}
public start(): void {
this.mcpServer.connect(this.transport);
// Reads from stdin, writes to stdout
}
}
Configuration:
// xmcp.config.ts
export default {
stdio: {
debug: false
}
}
Build vs Dev Modes
Development Mode (xmcp dev)
- File watching enabled - Hot reload on changes
- Source maps - Full debugging support with
eval-cheap-module-source-map
- HTTP server auto-restart - Restarts server after successful compilation
- No optimization - Faster builds
// From compiler/index.ts
const bundlerConfig: RspackOptions = {
mode: "development",
watch: true,
devtool: "eval-cheap-module-source-map",
optimization: {
minimize: false
}
};
Production Mode (xmcp build)
- No file watching - One-time build
- No source maps - Smaller bundle size
- Minification enabled - Optimized code
- Telemetry - Build metrics and error reporting
const bundlerConfig: RspackOptions = {
mode: "production",
watch: false,
devtool: false,
optimization: {
minimize: true,
mergeDuplicateChunks: true
}
};
Framework Adapters
Adapters allow xmcp to integrate with existing frameworks without running a separate HTTP server.
Next.js Adapter
Integrates as an API route handler:
// packages/xmcp/src/runtime/adapters/nextjs/index.ts
export async function xmcpHandler(request: Request): Promise<Response> {
// Only handle POST requests
if (request.method !== "POST") {
return createMethodNotAllowedResponse();
}
// Convert Web Request to Node.js IncomingMessage
const bodyContent = await request.json();
const incomingRequest = createIncomingMessage({
method: request.method,
url: request.url,
headers: Object.fromEntries(request.headers.entries())
});
// Handle request through transport
await lifecycle.transport.handleRequest(
incomingRequest,
res,
bodyContent
);
}
Usage:
// xmcp.config.ts
export default {
http: true,
experimental: {
adapter: "nextjs"
}
}
// app/api/mcp/route.ts
import { xmcpHandler } from "xmcp/adapters/nextjs";
export const POST = xmcpHandler;
Express Adapter
Provides an Express middleware:
// packages/xmcp/src/runtime/adapters/express/index.ts
export async function xmcpHandler(req: Request, res: Response) {
const server = await createServer();
const transport = new StatelessHttpServerTransport(
debug,
bodySizeLimit || "10mb"
);
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
}
Usage:
// xmcp.config.ts
export default {
experimental: {
adapter: "express"
}
}
// server.ts
import express from "express";
import { xmcpHandler } from "xmcp/adapters/express";
const app = express();
app.use(express.json());
app.post("/mcp", xmcpHandler);
NestJS Adapter
Provides a NestJS controller and service:
// xmcp.config.ts
export default {
experimental: {
adapter: "nestjs"
}
}
// app.module.ts
import { XmcpModule } from "xmcp/adapters/nestjs";
@Module({
imports: [XmcpModule],
})
export class AppModule {}
Bundler Configuration
xmcp uses Rspack as its bundler, configured in packages/xmcp/src/compiler/get-bundler-config/index.ts.
Key Configuration
- Target:
node (or webworker for Cloudflare)
- Module system: CommonJS (or ES modules for Cloudflare)
- Externals: Node.js built-ins and
node_modules dependencies
- Loaders: SWC for TypeScript/TSX transformation
Custom Bundler Config
You can extend the bundler configuration:
// xmcp.config.ts
import { XmcpConfig } from "xmcp";
export default {
bundler: (config) => {
// Add custom loader for images
config.module?.rules?.push({
test: /\.(png|jpe?g|gif)$/i,
type: "asset/inline"
});
// Add custom alias
config.resolve = {
...config.resolve,
alias: {
...config.resolve?.alias,
"@utils": "./src/utils"
}
};
return config;
}
} satisfies XmcpConfig;
Type Generation
xmcp generates TypeScript definitions for global access to your tools:
// Generated xmcp-env.d.ts
declare global {
namespace XMCP {
interface Tools {
greet: ToolRegistryItem;
"users_[userId]_profile": ToolRegistryItem;
}
}
}
This enables type-safe access in adapters:
import { tools } from ".xmcp/tools";
// TypeScript knows all available tools
const greetTool = tools.greet;
await greetTool.execute({ name: "Alice" });
xmcp can be deployed to multiple platforms:
- Node.js - Standalone server or framework adapter
- Vercel - Serverless functions (stdio disabled automatically)
- Cloudflare Workers - Edge runtime with ES modules output
- Docker - Standard containerization
Platform detection happens automatically. For example, Vercel and Cloudflare builds disable stdio transport and ensure HTTP is enabled.