Skip to main content
Session stores provide persistent session storage for stateful MCP servers, enabling session restoration across Lambda container recycling or server restarts.

ISessionStore Interface

All session stores implement the ISessionStore interface:
interface ISessionStore {
  sessionExists(sessionId: string): Promise<boolean>;
  createSession(sessionId: string, data?: Record<string, any>): Promise<void>;
  getSession(sessionId: string): Promise<SessionData | null>;
  updateSession(sessionId: string, data: Partial<SessionData>): Promise<void>;
  deleteSession(sessionId: string): Promise<void>;
}

SessionData

interface SessionData {
  sessionId: string;
  createdAt: Date;
  updatedAt: Date;
  ttl?: number;
  data?: Record<string, any>;
}

DynamoDBSessionStore

DynamoDB-backed session store for production Lambda deployments.

Constructor

new DynamoDBSessionStore(options?: DynamoDBSessionStoreOptions)
options
DynamoDBSessionStoreOptions

Example

import { createHTTPServer, DynamoDBSessionStore } from '@leanmcp/core';

const sessionStore = new DynamoDBSessionStore({
  tableName: 'my-sessions',
  region: 'us-west-2',
  ttlSeconds: 3600, // 1 hour
  logging: true,
});

await createHTTPServer({
  name: 'my-server',
  version: '1.0.0',
  stateless: false,
  sessionStore,
});

DynamoDB Table Schema

The DynamoDB table must have the following schema: Primary Key:
  • sessionId (String) - Partition key
Attributes:
  • createdAt (String) - ISO timestamp
  • updatedAt (String) - ISO timestamp
  • ttl (Number) - Unix timestamp for TTL
  • data (Map) - Custom session data
TTL Configuration:
  • Enable TTL on the ttl attribute

Creating the Table

Using AWS CLI

aws dynamodb create-table \
  --table-name leanmcp-sessions \
  --attribute-definitions AttributeName=sessionId,AttributeType=S \
  --key-schema AttributeName=sessionId,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST \
  --region us-east-1

# Enable TTL
aws dynamodb update-time-to-live \
  --table-name leanmcp-sessions \
  --time-to-live-specification "Enabled=true,AttributeName=ttl" \
  --region us-east-1

Using CloudFormation

Resources:
  SessionTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: leanmcp-sessions
      AttributeDefinitions:
        - AttributeName: sessionId
          AttributeType: S
      KeySchema:
        - AttributeName: sessionId
          KeyType: HASH
      BillingMode: PAY_PER_REQUEST
      TimeToLiveSpecification:
        AttributeName: ttl
        Enabled: true

Constants

export const DEFAULT_TABLE_NAME = 'leanmcp-sessions';
export const DEFAULT_TTL_SECONDS = 86400; // 24 hours

Methods

sessionExists()

Check if a session exists in DynamoDB.
await store.sessionExists(sessionId: string): Promise<boolean>

createSession()

Create a new session in DynamoDB.
await store.createSession(
  sessionId: string,
  data?: Record<string, any>
): Promise<void>

getSession()

Get session data from DynamoDB.
await store.getSession(sessionId: string): Promise<SessionData | null>

updateSession()

Update session data and refresh TTL.
await store.updateSession(
  sessionId: string,
  updates: Partial<SessionData>
): Promise<void>

deleteSession()

Delete a session from DynamoDB.
await store.deleteSession(sessionId: string): Promise<void>

InMemorySessionStore

In-memory session store for local development and testing.

Constructor

new InMemorySessionStore()
No configuration required - sessions are stored in a JavaScript Map.

Example

import { createHTTPServer, InMemorySessionStore } from '@leanmcp/core';

const sessionStore = new InMemorySessionStore();

await createHTTPServer({
  name: 'my-server',
  version: '1.0.0',
  stateless: false,
  sessionStore,
});

Methods

sessionExists()

await store.sessionExists(sessionId: string): Promise<boolean>

createSession()

await store.createSession(
  sessionId: string,
  data?: Record<string, any>
): Promise<void>

getSession()

await store.getSession(sessionId: string): Promise<SessionData | null>

updateSession()

await store.updateSession(
  sessionId: string,
  updates: Partial<SessionData>
): Promise<void>

deleteSession()

await store.deleteSession(sessionId: string): Promise<void>

clear()

Clear all sessions (useful for testing).
store.clear(): void

size

Get the number of sessions.
const count = store.size; // number

Limitations

  • Sessions are lost on server restart
  • Not suitable for production or multi-instance deployments
  • No TTL support (sessions persist until manually deleted or cleared)

Auto-Configuration

When running on LeanMCP Lambda (LEANMCP_LAMBDA=true env var), the server automatically configures a DynamoDB session store in stateful mode:
// Automatic configuration on LeanMCP Lambda
await createHTTPServer({
  name: 'my-server',
  version: '1.0.0',
  stateless: false, // DynamoDBSessionStore automatically configured
});

Usage Patterns

Local Development

Use InMemorySessionStore for fast local development:
import { InMemorySessionStore } from '@leanmcp/core';

const sessionStore = new InMemorySessionStore();

Production (Lambda)

Use DynamoDBSessionStore with TTL for production:
import { DynamoDBSessionStore } from '@leanmcp/core';

const sessionStore = new DynamoDBSessionStore({
  tableName: process.env.DYNAMODB_TABLE_NAME || 'leanmcp-sessions',
  region: process.env.AWS_REGION || 'us-east-1',
  ttlSeconds: 3600, // 1 hour
});

Conditional Store Selection

import { DynamoDBSessionStore, InMemorySessionStore } from '@leanmcp/core';

const sessionStore =
  process.env.NODE_ENV === 'production'
    ? new DynamoDBSessionStore({ ttlSeconds: 3600 })
    : new InMemorySessionStore();

await createHTTPServer({
  name: 'my-server',
  version: '1.0.0',
  stateless: false,
  sessionStore,
});

Custom Session Store

Implement the ISessionStore interface for custom backends (Redis, PostgreSQL, etc.):
import { ISessionStore, SessionData } from '@leanmcp/core';

class RedisSessionStore implements ISessionStore {
  private client: RedisClient;

  constructor(redisUrl: string) {
    this.client = createClient({ url: redisUrl });
  }

  async sessionExists(sessionId: string): Promise<boolean> {
    const exists = await this.client.exists(sessionId);
    return exists === 1;
  }

  async createSession(sessionId: string, data?: Record<string, any>): Promise<void> {
    const session: SessionData = {
      sessionId,
      createdAt: new Date(),
      updatedAt: new Date(),
      data: data || {},
    };
    await this.client.set(sessionId, JSON.stringify(session), { EX: 3600 });
  }

  async getSession(sessionId: string): Promise<SessionData | null> {
    const data = await this.client.get(sessionId);
    return data ? JSON.parse(data) : null;
  }

  async updateSession(sessionId: string, updates: Partial<SessionData>): Promise<void> {
    const existing = await this.getSession(sessionId);
    if (!existing) return;
    const updated = { ...existing, ...updates, updatedAt: new Date() };
    await this.client.set(sessionId, JSON.stringify(updated), { EX: 3600 });
  }

  async deleteSession(sessionId: string): Promise<void> {
    await this.client.del(sessionId);
  }
}

Build docs developers (and LLMs) love