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

@speakeasy-api/docs-mcp-server

v0.19.1

Published

MCP server runtime exposing hybrid search over documentation via HTTP and stdio transports

Readme

@speakeasy-api/docs-mcp-server

MCP server runtime exposing hybrid search over documentation via HTTP and stdio transports.

Beta. Part of the Speakeasy Docs MCP monorepo.

Installation

npm install -g @speakeasy-api/docs-mcp-server

CLI Usage

# HTTP transport
docs-mcp-server --index-dir ./dist/.lancedb --transport http --port 20310

# Stdio transport (for MCP host integration)
docs-mcp-server --index-dir ./dist/.lancedb --transport stdio

Security

Every request's Origin header is validated, as the Streamable HTTP transport requires: a request without one (any non-browser client) passes, a request whose Origin is not allowed is answered with 403 and a JSON-RPC error body. By default only localhost origins are allowed. Browser-served clients on other origins need allowedOrigins (CLI: --allowed-origins app.example,..., env: ALLOWED_ORIGINS), which replaces the localhost default.

The server binds every interface by default, which suits containers and reverse proxies. When running locally, bind loopback as the spec recommends (host: "127.0.0.1", CLI: --host 127.0.0.1, env: HOST); a loopback bind also validates the Host header, so a page that resolves its own domain to 127.0.0.1 (DNS rebinding) cannot reach the server.

Errors

Protocol errors are JSON-RPC errors: an unknown tool, an unknown prompt, a missing required prompt argument, an invalid resource URI and a missing resource all answer -32602 (Invalid params); a method outside the declared capabilities answers -32601 (Method not found). Tool execution failures, including rejected tool arguments and errors thrown by custom tool handlers, are tool results with isError: true so the model can recover.

Capabilities

tools is always declared. prompts is declared only when the corpus defines prompts and resources only when a taxonomy value is marked with mcp_resource: true. prompts/list, resources/list and resources/templates/list still answer an empty list when their capability is not declared, for clients that ask without checking; prompts/get and resources/read answer -32602 for anything not listed. subscriptions/listen on the 2026-07-28 revision is acknowledged with an empty filter and completed at once, because the server never emits change notifications.

Protocol revisions

Both transports serve the 2026-07-28 MCP revision (per-request _meta envelope, server/discover, no initialize handshake) and every 2025-era revision from 2024-11-05 to 2025-11-25 (the initialize handshake) from the same server. Over HTTP a 2026-07-28 request is always served per request; 2025-era requests use sessions unless stateless mode is on. Over stdio the opening message pins the connection to one era. List results on 2026-07-28 carry cache hints (ttlMs, cacheScope); see cacheHints in createMcpServer to change the defaults.

Programmatic Usage

Boot with defaults

import { createDocsServer, startStdioServer } from "@speakeasy-api/docs-mcp-server";

const server = await createDocsServer({ indexDir: "./my-index" });
await startStdioServer(server);

Inject a custom tool

import { createDocsServer, startStdioServer } from "@speakeasy-api/docs-mcp-server";

const server = await createDocsServer({
  indexDir: "./my-index",
  customTools: [
    {
      name: "submit_feedback",
      description: "Submit user feedback about a doc page",
      inputSchema: {
        type: "object",
        properties: {
          chunk_id: { type: "string" },
          rating: { type: "integer", minimum: 1, maximum: 5 },
        },
        required: ["chunk_id", "rating"],
      },
      handler: async (args) => {
        console.log("Feedback:", args);
        return { content: [{ type: "text", text: "Thanks!" }], isError: false };
      },
    },
  ],
});
await startStdioServer(server);

Run over HTTP

import { createDocsServer, startHttpServer } from "@speakeasy-api/docs-mcp-server";

const server = await createDocsServer({ indexDir: "./my-index" });
const { port } = await startHttpServer(server, { port: 3000 });
console.log(`Listening on http://localhost:${port}/mcp`);

createDocsServer() and startHttpServer() share the same defaults as the CLI:

  • serverName defaults to SERVER_NAME, then @speakeasy-api/docs-mcp-server, or ${toolPrefix}-docs-server when only toolPrefix is provided.
  • serverVersion defaults to SERVER_VERSION, then the package version.
  • HTTP build metadata also picks up GIT_COMMIT and BUILD_DATE when present.
  • Both work without an explicit logger. If you do pass one, a plain console-shaped logger is enough.

HTTP authentication

The authenticate hook runs before each request. Return AuthInfo to attach caller identity to the request context, or throw to reject with 401.

import { createDocsServer, startHttpServer } from "@speakeasy-api/docs-mcp-server";
import type { AuthInfo } from "@speakeasy-api/docs-mcp-server";

const server = await createDocsServer({
  indexDir: "./my-index",
  customTools: [
    {
      name: "whoami",
      description: "Return the authenticated caller's client ID",
      inputSchema: { type: "object", properties: {} },
      handler: async (_args, context) => ({
        content: [{ type: "text", text: `You are: ${context.authInfo?.clientId ?? "unknown"}` }],
        isError: false,
      }),
    },
  ],
});

await startHttpServer(server, {
  port: 3000,
  authenticate: async ({ headers }) => {
    const token = (headers.authorization as string | undefined)?.replace("Bearer ", "");
    if (!token) throw new Error("Missing bearer token");
    // Validate the token and return AuthInfo
    return { token, clientId: "my-client", scopes: ["read"] };
  },
});

Custom tool handlers receive a ToolCallContext with authInfo, headers, clientInfo (from the initialize handshake on 2025-era connections or the per-request envelope on 2026-07-28; best-effort and may be missing in stateless/degraded handling), and an abort signal.

Stateless HTTP mode

Pass stateless: true (CLI: --stateless, env: STATELESS=true) to serve every 2025-era request with a fresh server and transport. No sessions are created, the mcp-session-id request header is ignored, no Mcp-Session-Id response header is issued, and DELETE /mcp responds 405. Use this when requests may hit different replicas, e.g. behind a load balancer. GET /mcp responds 405 in both modes: the server never opens the 2025-era standalone notification stream.

Option Reference

| Field | Type | Default | Description | | ------------------------- | -------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | indexDir | string | required | Directory containing chunks.json and metadata.json from docs-mcp build. | | toolPrefix | string | — | Prefix for built-in tool names, e.g. "acme"acme_search_docs. Does not affect custom tool names. Alphanumeric, dash, or underscore. | | queryEmbeddingApiKey | string | OPENAI_API_KEY env | API key for query-time embeddings. | | queryEmbeddingBaseUrl | string | Provider default | Base URL for the embedding API. Defaults to the provider's official endpoint (e.g. https://api.openai.com/v1 for OpenAI). Override to use a proxy or compatible API. | | queryEmbeddingBatchSize | number | 128 | Number of texts per embedding API call. Reduce if hitting provider rate or payload limits. Positive integer. | | proximityWeight | number | 1.25 | RRF blend weight for lexical phrase-proximity matches. Higher values boost results where query terms appear close together. Positive. | | phraseSlop | number | 0 | Maximum word distance allowed for phrase matches (0 = exact phrase only, up to 5). | | vectorWeight | number | 1 | RRF blend weight for vector (semantic) search results. Higher values boost semantically similar results. Positive. | | customTools | CustomTool[] | [] | Additional tools registered alongside the built-in search_docs and get_doc. |

The exported CreateDocsServerOptionsSchema (Zod) is the canonical machine-readable spec for these options.

MCP Tools

| Tool | Description | | ------------- | ---------------------------------------------------------------------------------------------------------------- | | search_docs | Hybrid search with dynamically generated parameters and JSON Schema enum validation. Supports cursor pagination. | | get_doc | Retrieve a specific chunk with optional neighboring context. |

Tool names, descriptions, and parameters are dynamically generated from the metadata.json produced during indexing.

License

AGPL-3.0