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

@longrun/turtle

v0.2.7

Published

Lightweight HTTP MCP Server framework

Readme

Turtle

A lightweight Model Context Protocol HTTP server framework

Overview

Turtle makes it easy to create internet-accessible Model Context Protocol servers. It supports:

  • Multi-tenant operation with saved configuration per user
  • Horizontal scaling with shared database support
  • Multiple MCP servers on different URLs
  • Built-in security and safety features

There's also experimental support for long-running asynchronous MCP tools that's backwards-compatible with existing MCP clients.

Quick Start

import { defineTool, startHttpServer } from '@longrun/turtle';
import { z } from 'zod';

const hello = defineTool(
  'hello',
  'Say hello to the world',
  z.object({
    name: z.string(),
  }),
  async ({ name }) => `Hello ${name}!`
);

// Basic server
startHttpServer(3001, [
  {
    tools: [hello],
  },
]);

// With middleware (optional)
import cors from 'cors';
startHttpServer(
  3001,
  [
    {
      tools: [hello],
    },
  ],
  cors()
);

Then start the server and connect to http://localhost:3001 using any MCP client.

Tool Definition

Tools are defined using defineTool with:

  • name
  • description
  • Zod schema for parameters
  • handler function returning a string

The name and description are crucial as they expose your tool to models. The schema validates parameters, and TypeScript ensures handler parameters match the schema.

The schema defines the parameters for your tool, and the handler function is the implementation. If you're using Typescript, defineTool will enforce that the schema and handler function parameters match.

Handler functions can take a second parameter to access userId and per-user configuration values. See "Authentication" and "User configuration" sections for details.

async ({ name }, ctx) => {
  // Get an optional value (could be undefined)
  const customGreeting = await ctx.getConfig('customGreeting');
  // Get a required value (will throw an error if not found)
  const company = await ctx.requireConfig('company');
  return `Welcome to ${company}, ${name}! Your username is ${ctx.userId}.${customGreeting}`;
};

Tools return strings - Turtle automatically converts them to MCP response objects.

Hosting

Turtle uses Express as an HTTP server.

startHttpServer takes a list of servers as input - each can have a separate list of tools and sub-URL under the root:

startHttpServer(3001, [
  {
    mountpath: '/en',
    name: 'English',
    version: '1.0.0',
    tools: [hello, goodbye],
  },
  {
    mountpath: '/fr',
    name: 'French',
    version: '1.0.0',
    tools: [bonjour, auRevoir],
  },
]);

A single server without a path mounts at root. Servers can define name and version. Defaults are "1.0.0" for version and first tool name for name.

Adding Express Middlewares

You can add Express middlewares to your Turtle server by passing them as an optional third parameter to startHttpServer. This allows you to add security, logging, CORS, and other middleware before the MCP servers are mounted:

import cors from 'cors';
import helmet from 'helmet';

// Single middleware
startHttpServer(3001, [mcpConfig], cors());

// Multiple middlewares
startHttpServer(
  3001,
  [mcpConfig],
  [
    cors(),
    helmet(),
    (req, res, next) => {
      console.log(`${req.method} ${req.path}`);
      next();
    },
  ]
);

The middlewares are applied in order before the MCP servers are mounted, ensuring they run for all requests.

Using Your Own Express Server

You can use your own Express server or mount alongside other routes. Each Turtle MCP server is an Express Router, so you can use it just like any other Router:

import express from 'express';
import { createHttpServer } from '@longrun/turtle';

const app = express();
app.get('/', (req, res) => {
  res.send('Hello World');
});

// Add your own middlewares first
app.use(cors());
app.use(helmet());

// Create and mount the MCP server
const mcpServer = createHttpServer(
  [
    {
      tools: [hello, goodbye],
      mountpath: '/mcp',
    },
  ],
  [
    // Additional middlewares specific to MCP routes
    (req, res, next) => {
      console.log('MCP request:', req.path);
      next();
    },
  ]
);
app.use(mcpServer); // Clients connect at /mcp

Any Express middleware can be applied to your server or the one returned by startHttpServer, enabling standard security and safety solutions from the Express ecosystem.

Long-running asynchronous tools

Turtle supports long-running tools (experimental) for operations that run longer than the typical MCP tool timeout. The developer is responsible for creating the actual system that runs and persists these operations - Turtle provides the MCP interface.

Define these with two functions:

const writeReport = defineLongRunningTool(
  'writeQuartelyReport',
  'Generate a quarterly report - this could take up to an hour',
  z.object({
    year: z.number(),
    quarter: z.enum(['Q1', 'Q2', 'Q3', 'Q4']),
  }),
  // The start function
  async (args) => {
    const id = `quarterly-report-${args.year}-${args.quarter}`;
    const existingReport = _getReport(id);
    if (existingReport) {
      throw new Error(
        `There's already a report for ${args.year} ${args.quarter}. Please call writeQuarterlyReport-results with executionId ${id} to get it.`
      );
    } else {
      _enqueue_report(id, args.year, args.quarter);
      return {
        executionId: id,
        etaSeconds: 60 * 60, // optional
      };
    }
  },
  // The results function
  async (executionId) => {
    const report = _getReport(executionId);
    if (report.completed) {
      return report.url;
    } else {
      throw new Error('Report is not complete. Please check back later');
    }
  }
);

Turtle creates two tools: one to start the process and one to get results. The start function returns an executionId and optional etaSeconds. The results function checks completion status. Turtle automatically generates instructions in the tool descriptions for the calling model to understand how to use these.

FAQ

  • Does Turtle support non-text return types from tools? Not today.