npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@odel/module-sdk

v2.0.0

Published

SDK for building Odel modules — a thin, additive wrapper over the Model Context Protocol TypeScript SDK for Cloudflare Workers

Readme

@odel/module-sdk

A thin, additive wrapper over the Model Context Protocol TypeScript SDK for building Odel modules on Cloudflare Workers.

The official MCP SDK owns the protocol. This package adds only Odel's conventions on top: the per-request context/secrets envelope, typed errors, input validators, response schemas, and code-declared config. No fork, no hand-rolled protocol — just @modelcontextprotocol/sdk plus a handful of helpers.

Installation

npm install @odel/module-sdk zod
npm install -D wrangler @cloudflare/workers-types

@modelcontextprotocol/sdk comes along as a dependency; zod is a peer dependency (you import it directly in your module).

Quick start

import { createOdelServer, WebStandardStreamableHTTPServerTransport } from '@odel/module-sdk/server';
import { getModuleContext, getRequiredSecret, validators } from '@odel/module-sdk';
import { z } from 'zod';

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); // { userId, displayName, conversationId?, requestId, timestamp }
			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);
	}
};

Use createOdelServer rather than McpServer directly: it's a thin wrapper that always registers the odel://config marker resource (see below), so your module is identifiable as an Odel module and its declared config is discoverable — even if it declares none. (McpServer is still re-exported if you need it.)

A fresh server + transport per request is the canonical stateless pattern for Workers. With sessionIdGenerator: undefined the transport accepts a single tools/call with no initialize handshake — which is exactly how Odel's mcp-proxy invokes modules.

See examples/calculator-basic for a complete, runnable module.

The context envelope

Odel's mcp-proxy authenticates the user, resolves their per-module secrets, and injects them into each request's params._meta under namespaced keys:

  • app.odel/context — identity (userId, displayName, conversationId?, requestId, timestamp)
  • app.odel/secrets — the per-user secret map for this module

The official SDK surfaces params._meta to tool handlers as extra._meta, so you read all of this from the handler's extra:

import { getModuleContext, getRequiredSecret, getOptionalSecret } from '@odel/module-sdk';

const ctx = getModuleContext(extra);              // identity, with anonymous fallbacks
const apiKey = getRequiredSecret(extra, 'KEY');   // throws ModuleError if missing
const maybe = getOptionalSecret(extra, 'WEBHOOK'); // string | undefined

Declaring config in code

Declare the secrets/config your module needs as a Zod schema. parseConfig validates the envelope and returns a typed, validated object; configRequiredSecretNames is what the dev-portal extracts into the module's required-secrets list.

import { parseConfig } from '@odel/module-sdk';
import { z } from 'zod';

export const configSchema = z.object({
	RESEND_API_KEY: z.string().min(1).describe('Resend API key'),
	FROM_ADDRESS: z.string().email().optional().describe('Override sender'),
});

// inside a handler:
const cfg = parseConfig(configSchema, extra); // { RESEND_API_KEY: string; FROM_ADDRESS?: string }

The odel://config marker

Every server made with createOdelServer exposes an MCP resource at odel://config — shape { secrets: [{ name, description, required }] }, derived from your configSchema (or { secrets: [] } when you declare none). It does double duty: it's the marker that identifies an Odel module to Odel tooling (the inspector tells Odel servers from plain MCP servers by its presence), and it lets clients discover what config a module needs to drive secret-entry UI. If you construct McpServer yourself, call registerOdelConfig(server, configSchema?) to expose it.

What's exported

| Import | Provides | | --- | --- | | @odel/module-sdk/server | createOdelServer, McpServer, WebStandardStreamableHTTPServerTransport, CallToolResult, RequestHandlerExtra | | @odel/module-sdk | getModuleContext, getRequiredSecret, getOptionalSecret, createToolContext, parseConfig, configRequiredSecretNames, buildConfigManifest, registerOdelConfig, ODEL_CONFIG_URI, validators, ModuleError, ErrorCode, SuccessResponseSchema, SimpleSuccessSchema, types | | @odel/module-sdk/odel | same helper surface as the root (explicit subpath) |

License

MIT