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

@koduhai/mcp-kit

v0.3.0

Published

Auth and versioning for MCP servers, solved. Upstream API auth (API key / bearer / OAuth client-credentials), API versioning with a get_version tool, and a one-call OAuth 2.1 Resource Server (JWKS + introspection verifiers, RFC 9728 metadata) on top of th

Readme

@koduhai/mcp-kit

CI codecov npm version OpenSSF Scorecard License: MIT Node

The two things people get wrong building MCP servers, solved: auth and versioning.

It does three things, on three import paths so the lightweight parts pull no heavy deps:

  • Upstream auth (/upstream) — how your server authenticates to the API it wraps: API key, bearer, or OAuth client-credentials with automatic token caching/refresh.
  • Versioning (/versioning) — pin the upstream API version, send it on every call, expose a get_version tool, and detect drift.
  • Server OAuth (/auth) — turn a remote (Streamable HTTP) MCP server into a spec-compliant OAuth 2.1 Resource Server in one call. Ships the JWKS + introspection token verifiers the MCP SDK needs but does not include.

Built on top of @modelcontextprotocol/sdk. Aligned to the 2025-06-18 authorization spec (MCP servers are Resource Servers; they verify tokens and serve RFC 9728 metadata, they do not act as an Authorization Server).

npm install @koduhai/mcp-kit

📚 API reference · 🔌 IdP recipes (Auth0, Keycloak, Okta, Clerk, …)


1. Upstream auth — @koduhai/mcp-kit/upstream

Most MCP servers wrap an API and need to authenticate to it. Stop hand-rolling this.

import {
  apiKeyAuth,
  bearerAuth,
  clientCredentialsAuth,
  createUpstreamFetch,
} from '@koduhai/mcp-kit/upstream';

// Static API key (defaults to `Authorization: Bearer <key>`; pass a header for raw keys)
const auth = apiKeyAuth({ key: process.env.API_KEY! });
const auth2 = apiKeyAuth({ key: process.env.API_KEY!, header: 'X-Api-Key' });

// OAuth 2.0 machine-to-machine — fetches, caches, and refreshes the token for you
const m2m = clientCredentialsAuth({
  tokenUrl: 'https://issuer/oauth/token',
  clientId: process.env.CLIENT_ID!,
  clientSecret: process.env.CLIENT_SECRET!,
  audience: 'https://api.example.com', // if your IdP needs it (e.g. Auth0)
});

// A fetch that carries your auth (+ any standing headers) on every request
const api = createUpstreamFetch({ baseUrl: 'https://api.example.com', auth });
const res = await api('/things/123'); // -> GET https://api.example.com/things/123, authorized

clientCredentialsAuth caches the access token and refreshes it shortly before expiry; concurrent callers during a refresh share a single in-flight request.

2. Versioning — @koduhai/mcp-kit/versioning

APIs evolve; agents get confused when response shapes shift under them. Pin a version, send it everywhere, and surface it.

import { apiVersioning, versionTool } from '@koduhai/mcp-kit/versioning';

const versioning = apiVersioning({
  header: 'Api-Version',
  version: '2026-01-01',
  current: '2026-03-01', // optional: flags drift
  supported: ['2026-01-01', '2026-03-01'], // optional: refuses an unknown pin at startup
});

// Feed it into createUpstreamFetch so every request carries the header:
const api = createUpstreamFetch({ baseUrl, auth, headers: () => versioning.headers() });

// Register this descriptor as a tool so the agent can ask which version it's talking to:
const tool = versionTool(versioning); // { name: 'get_version', inputSchema, handler }

3. Server-side OAuth — @koduhai/mcp-kit/auth

This is the part everyone gets stuck on. Per the current spec, a remote MCP server is an OAuth 2.1 Resource Server: it must verify access tokens and serve Protected Resource Metadata (RFC 9728) so clients can discover where to log in. The MCP SDK gives you requireBearerAuth and the metadata router, but not a token verifier — you have to write JWT/JWKS or introspection validation yourself. mcp-kit ships both, plus a one-call assembly.

import express from 'express';
import { jwtVerifier, protectMcpServer } from '@koduhai/mcp-kit/auth';

const app = express();
const issuer = 'https://your-tenant.auth0.com';
const resourceServerUrl = 'https://mcp.example.com';

const { requireAuth } = await protectMcpServer({
  app,
  resourceServerUrl,
  issuer, // AS metadata + JWKS auto-discovered
  verifier: jwtVerifier({ issuer, audience: resourceServerUrl }),
  scopesSupported: ['mcp:tools'],
  requiredScopes: ['mcp:tools'],
});

app.post('/mcp', requireAuth, mcpHttpHandler); // req.auth is now populated

That gives you, for free:

  • GET /.well-known/oauth-protected-resource → RFC 9728 metadata pointing at your IdP.
  • 401 on missing/invalid tokens with a WWW-Authenticate: Bearer ... resource_metadata="..." header, so compliant MCP clients can discover the auth server and start the flow.
  • JWT validation of signature (via the issuer's JWKS), iss, aud (this is what stops token-passthrough/confused-deputy attacks), exp/nbf, and scope enforcement.

Opaque tokens instead of JWTs? Swap the verifier:

import { introspectionVerifier } from '@koduhai/mcp-kit/auth';

const verifier = introspectionVerifier({
  introspectionUrl: 'https://your-tenant.auth0.com/oauth/introspect',
  clientId: process.env.RS_CLIENT_ID!,
  clientSecret: process.env.RS_CLIENT_SECRET!,
  cacheTtlSeconds: 60, // cache active results (default 60); set 0 to introspect every request
});

Introspection results are cached for a short TTL (capped by the token's own exp) and deduplicated while a call is in flight, so a busy server doesn't introspect the same token on every request. Caching delays revocation visibility by at most the TTL; set cacheTtlSeconds: 0 if every request must hit the AS.

Works with any standards-compliant IdP: Auth0, Logto, Clerk, Keycloak, Okta, Cognito, WorkOS, and friends — see RECIPES.md for per-provider configs. mcp-kit verifies tokens; it does not try to be your Authorization Server (the spec says don't, and you shouldn't).

See examples/ for a full stdio server and a full remote OAuth server.

4. Mounting tools — @koduhai/mcp-kit/server

versionTool (and your own tools) are plain, transport-agnostic ToolDescriptors. serveTools wires a list of them onto a low-level MCP Server in one call: it registers the tools/list and tools/call handlers, JSON-encodes each result as MCP text content, and rejects unknown tool names. This is the only entry point that needs the MCP SDK at runtime.

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { serveTools } from '@koduhai/mcp-kit/server';
import { apiVersioning, versionTool } from '@koduhai/mcp-kit/versioning';

const versioning = apiVersioning({ header: 'Api-Version', version: '2026-01-01' });
const server = new Server({ name: 'my-mcp', version: '1.0.0' }, { capabilities: { tools: {} } });

serveTools(server, [
  versionTool(versioning),
  {
    name: 'get_widget',
    description: 'Fetch a widget.',
    inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
    handler: (a) => api(`/widgets/${a.id}`).then((r) => r.json()),
  },
]);
// await server.connect(transport)

Design

  • Layered, optional peers. /upstream and /versioning have zero dependencies. /auth and /server declare @modelcontextprotocol/sdk (and /auth also express and jose) as optional peers, so you only install them if you build a remote or SDK-backed server.
  • Injectable everything. Every network call and clock is injectable, so the whole thing is tested offline (57 tests, including a real Express + token-verification integration and an in-memory MCP client/server round trip).
  • Resilient outbound calls. Every control-plane request the library makes (token, introspection, JWKS, discovery) carries a timeout (default 10s, configurable via timeoutMs) and optional bounded retries (retries) so a slow or flaky IdP can't hang your server.
  • ESM, Node ≥ 20, TypeScript-first.

Compatibility

Targets @modelcontextprotocol/sdk ≥ 1.20 and the 2025-06-18 MCP authorization spec. The SDK's auth helpers are Express-based, so /auth integrates with Express; /upstream and /versioning are transport-agnostic.

License

MIT © Koduhai. Built alongside KoduhMail, generalizing the auth + versioning patterns from its MCP server.