Skip to main content
Glama

Elysia MCP Plugin

A comprehensive ElysiaJS plugin for implementing Model Context Protocol (MCP) servers with HTTP transport support.

Features

  • HTTP Transport: Full HTTP-based MCP transport with Streamable HTTP

  • Session Management: Stateful session handling via headers

  • Type-Safe: Built with TypeScript and Zod validation

  • Easy Integration: Simple plugin architecture for Elysia apps

  • Comprehensive Support: Tools, Resources, Prompts, and Logging

  • Custom Logger Support: Use any logger (pino, winston, bunyan, etc.)

  • Error Handling: Proper JSON-RPC 2.0 error responses

  • Testing: Full unit test coverage with Bun test runner

Related MCP server: MCP Framework

Installation

bun add elysia-mcp
# or
npm install elysia-mcp

Starter Template

To quickly get started with a pre-configured Elysia MCP project, you can use our starter template:

# Create a new project from the starter template
bun create https://github.com/kerlos/elysia-mcp-starter my-mcp-project

# Navigate to the project
cd my-mcp-project

# Install dependencies
bun install

# Start development server
bun run dev

The elysia-mcp-starter template includes:

  • Pre-configured Elysia setup with MCP plugin

  • TypeScript configuration

  • Development scripts

  • Basic project structure

  • Example MCP server implementation

Quick Start

import { Elysia } from 'elysia';
import { mcp } from 'elysia-mcp';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

const app = new Elysia()
  .use(
    mcp({
      serverInfo: {
        name: 'my-mcp-server',
        version: '1.0.0',
      },
      capabilities: {
        tools: {},
        resources: {},
        prompts: {},
        logging: {},
      },
      setupServer: async (server: McpServer) => {
        // Register your MCP tools, resources, and prompts here
        server.registerTool(
          'echo',
          {
            description: 'Echoes back the provided text',
            inputSchema: {
              text: z.string().describe('Text to echo back'),
            },
          },
          async (args) => {
            return {
              content: [{ type: 'text', text: `Echo: ${args.text}` }],
            };
          }
        );
      },
    })
  )
  .listen(3000);

Usage

Running the Examples

Basic Example:

# Run the basic example server (port 3000)
bun run example

# Or with development mode (auto-restart)
bun run dev

Multiple Servers Example:

# Run the multiple MCP servers example (port 3000)
bun example:multi

This example demonstrates how to create multiple MCP plugins in a single Elysia application:

  • Math Operations Plugin (/math) - Basic arithmetic tools:

    • add - Add two numbers

    • multiply - Multiply two numbers

    • power - Calculate base to the power of exponent

  • Text Utilities Plugin (/text) - Text processing tools:

    • uppercase - Convert text to uppercase

    • word_count - Count words in text

    • reverse - Reverse text characters

    • replace - Replace text with global matching

Testing with MCP Inspector

  1. Install MCP Inspector:

    npx @modelcontextprotocol/inspector
  2. Connect to your server:

    • Basic Example: http://localhost:3000/mcp

    • Multiple Servers Example:

      • Math Plugin: http://localhost:3000/math

      • Text Plugin: http://localhost:3000/text

Configuration Options

  • serverInfo: Server information

  • capabilities: MCP capabilities to advertise

  • logger: Custom logger instance (pino, winston, etc.) - see Custom Logger section

  • setupServer: Callback to register tools, resources, and prompts

  • basePath: Base path for MCP endpoints (default: '/mcp')

  • enableJsonResponse: Enable JSON response mode instead of SSE streaming

  • stateless: Enable stateless mode (no session management)

  • authentication: Authentication handler for protected routes

  • eventStore: Event store for resumability support

Session Management

The plugin automatically handles session management via the Mcp-Session-Id header. Each session maintains its own state and can be terminated cleanly.

Modular Handler Architecture

The plugin supports a modular handler architecture that allows you to create specialized endpoints for different MCP capabilities:

import {
  mcp,
  ToolsHandler,
  ResourcesHandler,
  PromptsHandler,
} from 'elysia-mcp';

const app = new Elysia().use(
  mcp({
    /* config */
  })
);

Custom Logger

The plugin supports custom logger instances, allowing you to use any logging library that conforms to the ILogger interface:

interface ILogger {
  info(message: string, ...args: unknown[]): void;
  error(message: string, ...args: unknown[]): void;
  warn(message: string, ...args: unknown[]): void;
  debug(message: string, ...args: unknown[]): void;
}

Using Pino Logger

import { Elysia } from 'elysia';
import { mcp } from 'elysia-mcp';
import pino from 'pino';

const logger = pino({ level: 'debug' });

const app = new Elysia()
  .use(
    mcp({
      logger, // Pass your custom logger
      serverInfo: {
        name: 'my-mcp-server',
        version: '1.0.0',
      },
      // ... other options
    })
  )
  .listen(3000);

Using Winston Logger

import winston from 'winston';

const logger = winston.createLogger({
  level: 'debug',
  format: winston.format.json(),
  transports: [new winston.transports.Console()],
});

const app = new Elysia()
  .use(
    mcp({
      logger, // Pass your winston logger
      // ... other options
    })
  )
  .listen(3000);

Default Console Logger

If you don't provide a custom logger, the plugin will use a default console logger when enableLogging is set to true:

const app = new Elysia()
  .use(
    mcp({
      enableLogging: true, // Uses default console logger with colors
      // ... other options
    })
  )
  .listen(3000);

Custom Logger Implementation

You can also implement your own logger:

import type { ILogger } from 'elysia-mcp';

class MyCustomLogger implements ILogger {
  info(message: string, ...args: unknown[]): void {
    // Your custom implementation
  }
  
  error(message: string, ...args: unknown[]): void {
    // Your custom implementation
  }
  
  warn(message: string, ...args: unknown[]): void {
    // Your custom implementation
  }
  
  debug(message: string, ...args: unknown[]): void {
    // Your custom implementation
  }
}

const logger = new MyCustomLogger();

const app = new Elysia()
  .use(
    mcp({
      logger,
      // ... other options
    })
  )
  .listen(3000);

API Reference

Tools

Register tools using the MCP Server instance:

server.registerTool(
  'tool-name',
  {
    description: 'Tool description',
    inputSchema: {
      param: z.string().describe('Parameter description'),
    },
  },
  async (args) => {
    // Tool implementation
    return {
      content: [{ type: 'text', text: 'Tool result' }],
    };
  }
);

Resources

Register resources for file or data access:

server.registerResource(
  'resource-name',
  'resource://uri',
  {
    title: 'Resource Name',
    description: 'Resource description',
  },
  async () => {
    return {
      contents: [
        {
          uri: 'resource://uri',
          mimeType: 'text/plain',
          text: 'Resource content',
        },
      ],
    };
  }
);

Prompts

Register reusable prompt templates following MCP best practices:

server.registerPrompt(
  'prompt-name',
  {
    title: 'Prompt Title',
    description: 'Prompt description',
    argsSchema: {
      param: z.string().describe('Parameter description'),
    },
  },
  async (args) => {
    return {
      description: 'Generated prompt',
      messages: [
        {
          role: 'user',
          content: {
            type: 'text',
            text: `Generated prompt with ${args.param}`,
          },
        },
      ],
    };
  }
);

Testing

Run the comprehensive test suite:

bun test

License

MIT - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Plugin Configuration

Plugin Options

interface MCPPluginOptions {
  /**
   * Base path for MCP endpoints (default: '/mcp')
   */
  basePath?: string;

  /**
   * Server information
   */
  serverInfo?: {
    name: string;
    version: string;
  };

  /**
   * MCP server capabilities
   */
  capabilities?: ServerCapabilities;

  /**
   * @deprecated Use logger option instead
   * Enable or disable logging
   */
  enableLogging?: boolean;

  /**
   * Custom logger instance (pino, winston, etc.)
   * If not provided and enableLogging is true, will use default console logger
   */
  logger?: ILogger;

  /**
   * Enable JSON response mode instead of SSE streaming
   */
  enableJsonResponse?: boolean;

  /**
   * Authentication handler
   */
  authentication?: (
    context: McpContext
  ) => Promise<{ authInfo?: AuthInfo; response?: unknown }>;

  /**
   * Setup function to configure the MCP server with tools, resources, and prompts
   */
  setupServer?: (server: McpServer) => void | Promise<void>;

  /**
   * Enable stateless mode (no session management)
   */
  stateless?: boolean;

  /**
   * Provide a custom MCP server instance
   */
  mcpServer?: McpServer;

  /**
   * Event store for resumability support
   */
  eventStore?: EventStore;
}

Architecture

flowchart LR
    A["HTTP Client"] --> B["Elysia HTTP Handler"]
    B --> C["MCP Plugin"]
    C --> D["McpServer<br/>(Singleton)"]
A
license - permissive license
-
quality - not tested
D
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Issues opened vs closed

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kerlos/elysia-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server