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

mcp-lambda-nodejs

v1.1.1

Published

SDK for creating Model Context Protocol (MCP) servers with AWS Lambda using decorators

Readme

mcp-lambda-nodejs

npm version License: MIT Node.js

TypeScript SDK for building Model Context Protocol (MCP) servers that run as AWS Lambda functions.

Define your MCP tools with decorators, wire up a single handler, and deploy — the SDK handles JSON-RPC routing, Zod validation, CORS, and session management automatically.

Requirements

  • Node.js 18+
  • TypeScript 5.0+ with experimentalDecorators and emitDecoratorMetadata enabled

Installation

npm install mcp-lambda-nodejs
# peer dependency
npm install --save-dev @types/aws-lambda

Quick Start

1. Enable decorator metadata in tsconfig.json

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

2. Create your MCP server class

// calculator-server.ts
import { MCPServer, MCPTool, z } from 'mcp-lambda-nodejs';

@MCPServer({
  name: 'my-calculator',
  version: '1.0.0'
})
export class CalculatorServer {
  @MCPTool({
    title: 'Add Numbers',
    description: 'Adds two numbers together',
    inputSchema: {
      a: z.number().describe('First number'),
      b: z.number().describe('Second number')
    },
    outputSchema: {
      result: z.number().describe('The sum'),
      operation: z.string().describe('Human-readable equation')
    }
  })
  async add(params: { a: number; b: number }) {
    return {
      result: params.a + params.b,
      operation: `${params.a} + ${params.b} = ${params.a + params.b}`
    };
  }
}

3. Create the Lambda handler

// handler.ts
import { APIGatewayProxyEventV2, APIGatewayProxyResultV2, MCPHandlerFactory } from 'mcp-lambda-nodejs';
import { CalculatorServer } from './calculator-server';

const handler = MCPHandlerFactory.createHandler(CalculatorServer, 'calculator');

export async function main(event: APIGatewayProxyEventV2): Promise<APIGatewayProxyResultV2> {
  return handler(event);
}

4. Deploy (Serverless Framework example)

# serverless.yml
service: my-mcp-server

provider:
  name: aws
  runtime: nodejs20.x
  build:
    esbuild:
      bundle: true

functions:
  calculator:
    handler: handler.main
    events:
      - httpApi:
          path: /calculator
          method: post
      - httpApi:
          path: /calculator
          method: options
serverless deploy

5. Call your MCP server

# List tools
curl -X POST https://<your-api>.execute-api.<region>.amazonaws.com/calculator \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

# Call a tool
curl -X POST https://<your-api>.execute-api.<region>.amazonaws.com/calculator \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": { "name": "add", "arguments": { "a": 5, "b": 3 } }
  }'

Response:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [{ "type": "text", "text": "{\"result\":8,\"operation\":\"5 + 3 = 8\"}" }],
    "structuredContent": { "result": 8, "operation": "5 + 3 = 8" }
  }
}

API Reference

@MCPServer(config)

Class decorator that registers an MCP server.

| Field | Type | Description | | --------- | -------- | ----------------- | | name | string | Server identifier | | version | string | Server version |

@MCPTool(config)

Method decorator that exposes a method as an MCP tool.

| Field | Type | Description | | -------------- | ------------------------- | --------------------------- | | title | string | Human-readable tool name | | description | string | What the tool does | | inputSchema | Record<string, ZodType> | Zod schema per input field | | outputSchema | Record<string, ZodType> | Zod schema per output field |

The method name becomes the tool's name in the MCP protocol.

MCPHandlerFactory.createHandler(ServerClass, serverName?)

Returns an AWS Lambda handler (APIGatewayProxyEventV2 → APIGatewayProxyResultV2).

| Parameter | Type | Description | | ------------- | --------- | ---------------------------------------- | | ServerClass | Class | Decorated server class | | serverName | string? | Name used for instance keying (optional) |

Automatically handles:

  • CORS preflight (OPTIONS)
  • JSON-RPC 2.0 parsing and routing
  • initialize, tools/list, tools/call, resources/list, prompts/list
  • Input/output validation via Zod

MCPSessionManager

Optional session state manager for tools that need to maintain state across calls.

import { MCPSessionManager } from 'mcp-lambda-nodejs';

const sessionManager = new MCPSessionManager();

// Create session
const session = await sessionManager.createSession({ ttlHours: 2 });

// Update state
await sessionManager.updateSessionState(session.sessionId, { lastValue: 42 });

// Read session
const current = await sessionManager.getSession(session.sessionId);

Default storage is in-memory. For multi-container deployments inject a custom SessionStorage:

import { MCPSessionManager, SessionStorage, MCPSession } from 'mcp-lambda-nodejs';

class DynamoDBSessionStorage implements SessionStorage {
  async get(sessionId: string): Promise<MCPSession | null> {
    /* ... */
  }
  async set(session: MCPSession): Promise<void> {
    /* ... */
  }
  async delete(sessionId: string): Promise<void> {
    /* ... */
  }
  async cleanup(): Promise<void> {
    /* ... */
  }
}

const sessionManager = new MCPSessionManager(new DynamoDBSessionStorage());

Session ID in requests

Pass mcp-session-id as an HTTP header. The SDK extracts it automatically and injects it into tool arguments when not already present:

curl -X POST https://... \
  -H "mcp-session-id: user-abc-123" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{...}}'

Advanced Usage

Stateful tools

import { MCPServer, MCPTool, MCPSessionManager, z } from 'mcp-lambda-nodejs';

@MCPServer({ name: 'stateful-server', version: '1.0.0' })
export class StatefulServer {
  private sessionManager = new MCPSessionManager();

  @MCPTool({
    title: 'Increment Counter',
    description: 'Increments a per-session counter',
    inputSchema: { sessionId: z.string().optional() },
    outputSchema: { count: z.number() }
  })
  async increment(params: { sessionId?: string }) {
    const session = params.sessionId ? await this.sessionManager.getSession(params.sessionId) : null;

    const count = ((session?.state.count as number) ?? 0) + 1;

    if (params.sessionId) {
      await this.sessionManager.updateSessionState(params.sessionId, { count });
    }

    return { count };
  }
}

Error handling

Throw inside a tool method — the SDK catches it and returns a proper MCP error response:

@MCPTool({
  title: 'Safe Divide',
  description: 'Divides two numbers',
  inputSchema: { dividend: z.number(), divisor: z.number() },
  outputSchema: { result: z.number() }
})
async divide(params: { dividend: number; divisor: number }) {
  if (params.divisor === 0) {
    throw new Error('Division by zero');
  }
  return { result: params.dividend / params.divisor };
}

Supported JSON-RPC Methods

| Method | Description | | ---------------- | ----------------------------------------------------- | | initialize | Handshake — returns protocol version and capabilities | | tools/list | Lists all @MCPTool-decorated methods | | tools/call | Calls a tool by name | | resources/list | Returns empty list (stub) | | resources/read | Not implemented | | prompts/list | Returns empty list (stub) | | prompts/get | Not implemented |

Dependencies

| Package | Purpose | | --------------------------- | --------------------------- | | @modelcontextprotocol/sdk | MCP protocol implementation | | zod | Schema validation | | reflect-metadata | Decorator metadata | | uuid | Session ID generation |

License

MIT © Tyago Veras

Contributing

  1. Fork the repo
  2. Create a feature branch
  3. Make changes with tests
  4. Open a pull request

Issues: https://github.com/TyagoVeras/mcp-lambda-nodejs/issues