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

opentel-mcp-ui

v0.3.0

Published

Zero-infrastructure local dashboard for opentel-mcp -- the silent failures, cost, and thrash loops standard OTel tracing misses

Readme

opentel-mcp-ui

A zero-infrastructure local dashboard for opentel-mcp — see which of your MCP server's tool calls failed silently, without standing up a backend.

opentel-mcp can tell you that a tool call returned isError: true inside an otherwise-successful JSON-RPC response — the exact failure a standard OpenTelemetry setup renders as a clean, green span. This package is the one-command way to actually look at that signal, with no Prometheus, no Grafana, no OTel Collector to configure first.

30-second demo

npx opentel-mcp-ui --demo

Opens a dashboard at http://127.0.0.1:4319 seeded with realistic fixture data: 86 tool calls (68 clean successes, 7 ordinary thrown/protocol failures a standard OTel setup would also catch, and 11 silent failures it wouldn't), plus a few resource and prompt calls. Two of the tools are market-data tools whose successful responses are sometimes stale. Nothing to configure, no MCP server required. The panels:

  • Observation matrix — SUCCESS/FAILURE rows × visible-to-OTel/missed-by-OTel columns. The "missed" + "failure" cell is the whole point: calls a plain OTel setup would show as green.
  • Tool health — an A–F grade per tool from silent-failure rate, error rate, thrash episodes, p95 latency and, for tools that report freshness (opentel-mcp 0.17.0+), stale-data rate. The grade is the worst of them (latency alone never goes below C), and hovering a grade tells you which signal set it. Tools with fewer than 10 calls show "Not enough data". With freshness data, the panel also counts stale responses. Formula and thresholds: docs/health-grades.md.
  • Silent-failure feed — defaults to that exact cell, newest-first, with the side-by-side comparison of what standard tracing would have shown versus what actually happened. With opentel-mcp 0.16.0+, a filter narrows it to errors your agent can't act on: isError: true with no content, or under 10 characters of text and nothing else. With opentel-mcp 0.17.0+, rows show the data's age when the tool reported it, and a Stale data filter lists successful responses whose data was older than the tool's own limit ("data 4m old · limit 5s"). See opentel-mcp's "Staleness detection" section for how a tool reports it.
  • Resources & prompts — with opentel-mcp 0.16.0+'s opt-in coverage: { resources: true, prompts: true }, calls to resources/* and prompts/* by method (and prompt name), with their failures. Hidden for tools-only servers.

Only tools/call spans count toward the matrix, the hero number, the feed and the grades. A tools/list or resources/read span isn't a tool call.

  • Detector status banner — which of opentel-mcp's four in-memory trackers (thrash detection, cost/budget, schema drift, tool-outcome counting) are confidently live for the server you've pointed this at — stated honestly, not implied by a confident-looking zero. (In --demo mode, since there's no real server, it says so plainly instead of guessing.)

Add --open to launch your browser automatically. Run npx opentel-mcp-ui --help for the full flag list.

Pointing a real MCP server at it

Drop --demo and start the dashboard against nothing. Until the first span arrives it shows a Connect your server screen: this instance's OTLP endpoint and the exact snippet below, already filled in with it. Then it switches to the live dashboard by itself.

npx opentel-mcp-ui
opentel-mcp-ui: dashboard listening at http://localhost:4319
opentel-mcp-ui: OTLP/HTTP JSON trace receiver at http://localhost:4319/v1/traces
opentel-mcp-ui: point your instrumented server's exporterUrl at the URL above (instrumentMcpServer(server, { serviceName: 'my-mcp-server', setupNodeSdk: true, exporterUrl: 'http://localhost:4319/v1/traces' })).

Then, in the MCP server you're instrumenting with opentel-mcp core, point its exporter at that URL — no new dependency, no new mechanism; this is the same OTLP/HTTP JSON export path setupNodeSdk: true already ships:

import { instrumentMcpServer } from 'opentel-mcp';

instrumentMcpServer(server, {
  serviceName: 'my-mcp-server',
  setupNodeSdk: true,
  exporterUrl: 'http://127.0.0.1:4319/v1/traces',
});

Make real tool calls against your server and the dashboard replaces the connect screen live, over Server-Sent Events, with no page reload needed.

Environment variable, as an alternative to hardcoding the URL: opentel-mcp core doesn't read an env var for exporterUrl itself (it's a plain constructor option), so set one yourself and reference it:

OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4319 node your-server.js
instrumentMcpServer(server, {
  serviceName: 'my-mcp-server',
  setupNodeSdk: true,
  exporterUrl: `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? 'http://127.0.0.1:4319'}/v1/traces`,
});

In-process mode: withUI()

If your server runs in the same process you want the dashboard in (dev tooling, a single-process demo), skip the separate CLI process:

import { instrumentMcpServer } from 'opentel-mcp';
import { withUI } from 'opentel-mcp-ui';

const instrumented = instrumentMcpServer(server, { setupNodeSdk: true });
const { url } = await withUI(instrumented, { open: true });
// Point setupNodeSdk's own OTLP exporter back at this URL -- see the
// note below for why that extra step is still required.

withUI()'s most reliable ingestion path is the exact same OTLP/HTTP receiver the standalone CLI exposes (<url>/v1/traces) — it also attempts to attach directly to your TracerProvider as a bonus, best effort only, since the installed @opentelemetry/sdk-trace-node (2.x) has no public API to add a SpanProcessor after construction. Don't rely on the direct-attach path; wire exporterUrl to withUI()'s own url the same way the two-process setup above does.

CLI reference

opentel-mcp-ui [options]

  --demo              Seed a realistic fixture with no live MCP server.
  --port=<n>          Port to listen on (default: 4319). Binds to
                       127.0.0.1 only -- never reachable off this machine.
  --open               Open the dashboard in your default browser.
  --stateless          Assert the transport is stateless HTTP.
  --stateful            Assert the transport is a long-lived instance.
  --help, -h            Show this help and exit.

--stateless/--stateful matter only when you have real traffic and want accurate detector-status reporting: whether opentel-mcp core's in-memory trackers (thrash detection, cost/budget, schema drift, tool-outcome counting) can meaningfully accumulate state depends on whether your server is re-instrumented fresh per request (common under stateless HTTP deployments) or long-lived (stdio, or a persistent HTTP process). Auto-detection (the default) makes a best-effort structural guess from the transport shape and says so honestly when it can't be sure — pass one of these two flags if you know your own deployment topology and want a confident answer instead.

What this never does

  • Never phones home. No telemetry, no update check, no outbound request beyond what your own instrumented server sends it.
  • Never persists anything outside the running process. Spans live in an in-memory, bounded ring buffer; nothing touches disk. Stop the process and the data is gone.
  • Never binds to anything but 127.0.0.1. This dashboard has no authentication, so it is never reachable from another machine, even on your own local network, regardless of flags.
  • Never answers other websites (0.3.0+). Requests must be addressed to localhost, 127.0.0.1 or [::1] on the dashboard's own port, and browser requests to /api/* or /v1/traces must come from the dashboard itself; anything else gets 403. That stops a page open in your browser from reading your spans or posting fake ones, including via DNS rebinding. Reaching it through a port forward? Forward to the same port number it listens on.
  • Never accepts anything but OTLP/HTTP JSON on /v1/traces (415 otherwise), up to 8 MiB per request (413 above that).
  • Never runs anything the page didn't ship with. The dashboard is served with a strict Content-Security-Policy that allows only its own inline script and stylesheet, by hash.

Version compatibility

opentel-mcp-ui has zero compile-time coupling to opentel-mcp core's exports — it reads well-known span attribute keys as plain strings (error.type, mcp.tool.argument_count, …), not imported constants, so an attribute this UI version doesn't recognize from a newer core simply doesn't render anything new; it never breaks a build or crashes the dashboard. Requires opentel-mcp >=0.8.0 <1.0.0 (the version range where getObservationState() — used for the detector banner when a real in-process server is attached — has existed). Full reasoning: docs/adr/022-publish-ui.md in the main repo.

License

MIT © Thirumalaiboobathi B