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

somamcp

v1.0.13

Published

Framework for building MCP servers with telemetry, introspection, and backend abstraction

Readme

SomaMCP

Framework for building MCP (Model Context Protocol) servers with built-in telemetry, introspection, agent feedback, and a backend abstraction layer.

somamcp wraps an underlying MCP framework (currently FastMCP) behind a BackendAdapter interface — giving you a stable API surface, automatic telemetry, gateway-based server composition, identity/health endpoints with sensible security defaults, and HTTP artifacts out of the box.

Features

  • Backend abstraction — framework-agnostic BackendAdapter interface; swap the underlying MCP framework without changing consumer code
  • Structured telemetry — pluggable TelemetryCollector with console, file (NDJSON), composite, and functype-log adapters
  • Error classification & enrichment — errors are auto-classified (validation / timeout / gateway / auth / not_found / internal) with actionable suggestions for LLMs
  • Per-tool capture config — configure input/output capture levels, field redaction, and output size limits per tool
  • Gateway system — connect to remote MCP servers and proxy their tools as local tools
  • Identity & build infoinfo MCP tool returns name, version, build commit, runtime, and capability counts; auto-populated from SOMAMCP_BUILD_* env vars
  • Two-tier health — public /health (minimal, for Docker/k8s probes) + protected /health/detail (full operational state)
  • Protected artifactsprotected: true on any artifact reuses FastMCP's authenticate callback; dashboard and /info protected by default
  • Agent feedback toolcreateFeedbackTool posts agent-reported issues to GitHub or any webhook, with automatic credential/PII redaction
  • HTTP dashboard — auto-generated overview at /dashboard (protected)
  • Functional style — powered by functype (Ref, Try, Either)

Installation

pnpm add somamcp
# or
npm install somamcp

Quick Start

import { createServer } from "somamcp"
import { z } from "zod"

const server = createServer({
  name: "my-server",
  version: "1.0.0",
})

server.addTool({
  name: "greet",
  description: "Greet someone by name",
  parameters: z.object({ name: z.string() }),
  execute: async ({ name }) => `Hello, ${name}!`,
})

await server.start({ transportType: "stdio" })

Identity & Health

SomaMCP auto-registers one MCP introspection tool and three HTTP endpoints:

| Surface | Default path / name | Protected | Returns | | -------- | ------------------- | --------- | -------------------------------------------------------------------------------- | | MCP tool | info | n/a | { name, version, build, runtime, capabilities: { tools, resources, prompts } } | | HTTP | /health | No | { name, status } — minimal, status code is the real signal (200 / 503) | | HTTP | /health/detail | Yes | Full ServerHealth (sessions, gateway topology) | | HTTP | /info | Yes | Same shape as the info MCP tool | | HTTP | /dashboard | Yes | Rendered HTML overview |

Why two health tiers? The public /health exists for infrastructure probes (Docker HEALTHCHECK, k8s liveness/readiness, load balancers) — they only need a status code. Operational details (session counts, gateway URLs) are reconnaissance signals and live behind protected: true.

Build info

Set these env vars at deploy time (e.g. in your Dockerfile or CI) so info reflects the actual build:

SOMAMCP_BUILD_COMMIT=abc123
SOMAMCP_BUILD_DATE=2026-05-22T00:00:00Z
SOMAMCP_BUILD_BRANCH=main
SOMAMCP_ENVIRONMENT=production

Or pass them programmatically (override wins over env):

createServer({
  name: "my-server",
  version: "1.0.0",
  build: {
    commit: process.env.GIT_SHA,
    date: process.env.BUILD_DATE,
    environment: "production",
  },
})

Disabling / customizing

createServer({
  name: "my-server",
  version: "1.0.0",
  enableIntrospection: false, // remove the `info` MCP tool
  enableHealthEndpoint: false, // remove /health and /health/detail
  enableInfoEndpoint: false, // remove /info
  enableDashboard: false, // remove /dashboard
  healthPath: "/healthz", // custom paths
  healthDetailPath: "/health/full",
  infoPath: "/about",
  introspectionPrefix: "soma_", // → `soma_info` tool name
})

Protected Artifacts

Any artifact can opt into auth with protected: true. The route runs FastMCP's authenticate callback via Hono middleware — same auth model as MCP protocol calls. If protected: true is set but no authenticate is configured, the route returns 401 unconditionally.

createServer({
  name: "my-server",
  version: "1.0.0",
  authenticate: async (req) => {
    const token = req.headers.authorization?.replace("Bearer ", "")
    if (token !== process.env.OPS_TOKEN) throw new Error("denied")
    return { user: "ops" }
  },
  artifacts: [
    {
      type: "dynamic",
      path: "/admin/stats",
      protected: true,
      handler: (c) => c.json({ secret: "stuff" }),
    },
  ],
})

Telemetry

Telemetry is opt-in via the telemetry option. Every tool/resource/prompt call is automatically wrapped with timing, error classification, and optional input/output capture.

import { createServer, createCompositeTelemetry, createConsoleTelemetry, createJsonFileTelemetry } from "somamcp"

const telemetry = createCompositeTelemetry([
  createConsoleTelemetry(),
  createJsonFileTelemetry({ filePath: "./logs/events.ndjson" }),
])

const server = createServer({
  name: "my-server",
  version: "1.0.0",
  telemetry,
})

Per-tool capture configuration

server.addTool({
  name: "processPayment",
  description: "Process a payment",
  parameters: z.object({ amount: z.number(), cardToken: z.string() }),
  execute: async (args) => {
    /* ... */
  },
  captureConfig: {
    captureLevel: "full",
    redactInputFields: ["cardToken"],
    maxOutputSize: 2000,
  },
})

Capture levels:

  • "full" — input + output + metadata (default)
  • "metadata" — timing + name + ids only
  • "none" — no telemetry

Error enrichment

Errors thrown from tools are automatically classified and returned as structured ContentResult responses with isError: true, including suggestions to help calling LLMs self-correct.

// Thrown: new Error("Request timed out after 30s")
// Returned to LLM:
{
  isError: true,
  content: [{
    type: "text",
    text: JSON.stringify({
      errorCategory: "timeout",
      message: "Request timed out after 30s",
      suggestions: [
        "The operation took too long. Try reducing the scope of the request.",
        "Check if the upstream service is responsive.",
      ],
    }),
  }],
}

Gateways

Connect to remote MCP servers and proxy their tools as local tools.

const server = createServer({
  name: "gateway-server",
  version: "1.0.0",
  gateways: [
    {
      id: "upstream",
      url: "https://remote-mcp.example.com",
      proxyTools: true, // register remote tools as local (prefixed: "upstream_toolname")
      reconnect: true,
    },
  ],
})

Agent Feedback

Let agents file issues against your repo when they hit bugs or confusing behavior. The tool auto-redacts common credential patterns and PII, and includes a tool description that explicitly warns the LLM not to include proprietary data.

import { createServer, createFeedbackTool, createGithubFeedback } from "somamcp"

const server = createServer({ name: "my-mcp", version: "1.0.0" })

server.addTool(
  createFeedbackTool({
    provider: createGithubFeedback({
      repo: "myorg/my-mcp",
      getToken: () => process.env.GITHUB_FEEDBACK_TOKEN,
      defaultLabels: ["agent-feedback"],
    }),
    // Auto-attached to each issue (visible in the issue body)
    enrichment: async () => ({
      server: server.getInfo(),
    }),
    extraLabels: ["from-agent"],
  }),
)

What gets redacted automatically (best-effort, not a substitute for caution):

  • GitHub PATs (ghp_*, github_pat_*)
  • AWS access keys (AKIA*)
  • Stripe keys (sk_live_*, pk_test_*, etc.)
  • Slack tokens (xoxb-*, xoxp-*, etc.)
  • JWTs
  • OpenAI/Anthropic API keys
  • Bearer tokens (Bearer <secret>)
  • Email addresses
  • Private IPv4 addresses (RFC1918)
  • Internal hostnames (*.internal, *.local, etc.)

The tool response includes a redactionDetails summary so the agent knows what was scrubbed. Add custom patterns via redactionPatterns.

Providers:

  • createGithubFeedback({ repo, getToken, defaultLabels?, baseUrl? }) — posts to GitHub Issues
  • createWebhookFeedback({ url, headers?, transform? }) — generic JSON POST to any endpoint (Linear, Jira, Sentry, custom relay)

Token security: the token only needs issues: write scope on the target repo. Use a GitHub App for fleet-scale (auditable, scoped, rotatable).

Artifacts & Dashboard

Mount HTTP routes on the embedded Hono app. A protected health dashboard is auto-mounted at /dashboard unless disabled.

const server = createServer({
  name: "my-server",
  version: "1.0.0",
  enableDashboard: true, // default; requires `authenticate` to view
  artifacts: [
    {
      type: "dynamic",
      path: "/status",
      handler: (c) => c.json({ ok: true }),
    },
  ],
})

await server.start({
  transportType: "httpStream",
  httpStream: { port: 8080 },
})

Backend Abstraction

The underlying MCP framework is accessed exclusively through the BackendAdapter interface. To use a non-FastMCP backend, implement BackendFactory:

import { createFastMCPBackend } from "somamcp/backend"
import type { BackendFactory } from "somamcp/backend"

const myBackend: BackendFactory = (config, backendOptions) => {
  // ...
}

Framework-specific options (e.g. FastMCP's OAuth, ping) flow through backendOptions:

createServer({
  name: "my-server",
  version: "1.0.0",
  backendOptions: {
    ping: { enabled: true, intervalMs: 30000 },
    // ...any FastMCP ServerOptions fields
  },
})

API Surface

Core

  • createServer(options)SomaServerInstance
  • Types: SomaServerOptions, SomaServerInstance, ServerHealth, ServerInfo, ServerCapabilities, CapabilitiesSummary, ToolOptions

Primitives (somamcp-owned, no FastMCP leakage)

  • Tool, Resource, Prompt, Context, Content, ContentResult, SessionAuth, UserError

Build info

  • readBuildInfoFromEnv, resolveBuildInfo, getRuntimeInfo
  • Types: BuildInfo, RuntimeInfo

Telemetry

  • createConsoleTelemetry, createJsonFileTelemetry, createCompositeTelemetry, createLogLayerTelemetry, NoopTelemetry
  • classifyError, createEnrichedError
  • Types: TelemetryCollector, TelemetryEvent, CaptureLevel, ToolCaptureConfig, ErrorCategory

Gateway

  • createGateway, createGatewayManager, createProxiedTools
  • Types: GatewayConfig, GatewayInstance, GatewayManagerInstance, GatewayStatus

Artifacts

  • registerArtifacts, createDashboardArtifact, createHealthArtifact, createHealthDetailArtifact, createInfoArtifact
  • Types: StaticArtifact, DynamicArtifact, DirectoryArtifact, ArtifactConfig, ArtifactAuthenticate

Introspection

  • createInfoTool (auto-registered as info)
  • createHealthTool, createCapabilitiesTool, createConnectionsTool (legacy, exported for manual use)

Feedback

  • createFeedbackTool, createGithubFeedback, createWebhookFeedback
  • redact, DEFAULT_REDACTION_PATTERNS
  • Types: FeedbackProvider, FeedbackToolOptions, GithubFeedbackOptions, WebhookFeedbackOptions, RedactionPattern, RedactionResult, NormalizedFeedback, FeedbackSubmitResult

Content helpers

  • imageContent, audioContent

Backend (via somamcp/backend)

  • createFastMCPBackend
  • Types: BackendAdapter, BackendFactory, BackendSession

Scripts

pnpm validate       # Pre-commit: format + lint + typecheck + test + build
pnpm test           # Run tests
pnpm build          # Build via tsdown
pnpm dev            # Watch mode
pnpm typecheck      # Type check only

License

MIT © Jordan Burke