Overview
A thin wrapper over the official MCP TypeScript SDK, plus Odel's conventions.
The official Model Context Protocol TypeScript SDK
owns the protocol. @odel/module-sdk adds only what Odel needs on top of it: the per-request
context and secrets envelope, typed errors, input validators, response schemas, and config
declared in code.
It is not a fork. Everything you know about registerTool, resources, and prompts works
unchanged, because it is the official implementation — re-exported so you install one
dependency instead of two.
Install
pnpm add @odel/module-sdk zod
pnpm add -D wrangler @cloudflare/workers-types@modelcontextprotocol/sdk comes along as a dependency. zod is a peer dependency because you
import it directly in your own schemas.
A module, end to end
import { createOdelServer, WebStandardStreamableHTTPServerTransport } from '@odel/module-sdk/server';
import { getModuleContext, validators } from '@odel/module-sdk';
function buildServer() {
const server = createOdelServer({ name: 'my-module', version: '1.0.0' });
server.registerTool(
'greet',
{ description: 'Greet the current user', inputSchema: { name: validators.nonEmptyString() } },
async ({ name }, extra) => {
const ctx = getModuleContext(extra);
const result = { success: true as const, greeting: `Hello ${name}, from ${ctx.displayName}` };
return { content: [{ type: 'text', text: JSON.stringify(result) }], structuredContent: result };
}
);
return server;
}
export default {
async fetch(request: Request): Promise<Response> {
const server = buildServer();
const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
await server.connect(transport);
return transport.handleRequest(request);
}
};Two things in there are deliberate rather than incidental.
A fresh server and transport per request. Isolates don't persist between calls in any way you
should rely on, so building both inside fetch is the pattern that behaves the same on the first
request and the ten-thousandth. With sessionIdGenerator: undefined the transport is stateless: it
accepts a single tools/call with no initialize handshake first, which is exactly how Odel's
proxy invokes a module.
createOdelServer rather than new McpServer. It is a three-line wrapper that registers one
extra resource — see below. McpServer is re-exported if you need to construct it yourself.
The odel://config marker
Every server built with createOdelServer exposes an MCP resource at odel://config, shaped
{ secrets: [{ name, description, required }] } and derived from the config schema you declare
(or { secrets: [] } if you declare none).
It does two jobs. It is how Odel tooling recognises an Odel module at all — the inspector tells an Odel server from a plain MCP server by whether this resource is there. And it is how a client discovers what a module needs, so a secret-entry form can be built from the module itself rather than from a list someone maintained by hand.
If you construct McpServer directly, call registerOdelConfig(server, configSchema) to expose
it. Declaring config covers the schema
side.
What Odel adds
Context and secrets
Who is calling, and the credentials resolved for them.
Errors
Typed codes and factories that serialize consistently.
Validators
Zod schemas for emails, URLs, keys, and the rest.
Response schemas
Output schemas so callers know the shape they'll get.
What each import gives you
| Import | Provides |
|---|---|
@odel/module-sdk/server | createOdelServer, McpServer, WebStandardStreamableHTTPServerTransport, and the CallToolResult / RequestHandlerExtra types |
@odel/module-sdk | getModuleContext, getRequiredSecret, getOptionalSecret, createToolContext, parseConfig, configRequiredSecretNames, buildConfigManifest, registerOdelConfig, ODEL_CONFIG_URI, validators, ModuleError, ErrorCode, SuccessResponseSchema, SimpleSuccessSchema, and the types |
@odel/module-sdk/odel | The same helpers as the root, as an explicit subpath |
The root and /odel are interchangeable; the root is shorter and is what these pages use.
Where the code lives
- npm package — published from CI with signed provenance
- GitHub source, including a minimal
calculator-basicexample and an all-featuresfoobarone - MCP TypeScript SDK, upstream