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

mcp-audit-otel

v0.1.0

Published

Vendor-neutral MCP audit-event schema + OpenTelemetry emitter + middleware that maps each Model Context Protocol call to SOC 2, HIPAA, CMMC/NIST 800-171, and DORA controls.

Readme

mcp-audit-otel

CI npm license: MIT

A vendor-neutral audit-event schema for Model Context Protocol servers, an OpenTelemetry emitter for it, drop-in middleware for the MCP TypeScript SDK, and a machine-readable catalog mapping each audit event to specific SOC 2 / HIPAA / CMMC (NIST 800-171) / DORA controls.

MCP servers expose tools that read data and execute actions on a user's behalf, but the protocol says nothing about audit logging — there's no standard for what to record or how to structure it. Generic LLM-observability tools (OpenLLMetry, Langfuse, OpenInference) emit OpenTelemetry but carry zero compliance mapping. mcp-audit-otel fills that gap: one typed event shape, emitted as standard OTel, annotated with the real controls each event helps satisfy.

This is not a compliance attestation

The library records an audit stream and maps it to controls. It does not make you SOC 2 / HIPAA / CMMC / DORA compliant. Satisfying a control in full also requires log retention, tamper-proof storage, synchronized clocks, and human review — your responsibility, not the library's. Every control in the catalog carries a basis flag (records / evidences / feeds) that states honestly how far an audit log actually gets you. See The control catalog.

Install

npm install mcp-audit-otel

OpenTelemetry is an optional peer dependency — required only if you use the emitter or middleware (the default mcp-audit-otel entry). Install it alongside an SDK + exporter to actually ship records:

npm install @opentelemetry/api @opentelemetry/api-logs
# plus, in your app, an SDK + exporter to actually ship the records:
npm install @opentelemetry/sdk-logs @opentelemetry/exporter-logs-otlp-http

The OTel-free subpaths need none of the above: mcp-audit-otel/schema (event types + validators), mcp-audit-otel/catalog (the control catalog), and the mcp-audit-otel CLI all work without OpenTelemetry. The MCP SDK (@modelcontextprotocol/sdk) is likewise an optional peer — needed only for the withAudit middleware.

Quick start

1. Wrap your MCP server

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { withAudit } from "mcp-audit-otel";

const server = new Server({ name: "my-server", version: "1.0.0" }, { capabilities: { tools: {} } });

withAudit(server, {
  transport: "tcp", // "pipe" for stdio
  getActor: (extra) => (extra as { authInfo?: { clientId?: string } })?.authInfo?.clientId,
  // tool/resource params are SHA-256 hashed by default (raw args never emitted).
  // The target (tool name / resource URI) is recorded as-is — see Redaction.
});

// tools/call, resources/read, resources/subscribe, prompts/get,
// sampling/createMessage, and elicitation/create are audited automatically.
// NOTE: the SDK registers its own initialize handler in the constructor, so
// session setup is not audited unless you register an initialize handler
// yourself after withAudit (or call recordAuth — see Manual events).
server.setRequestHandler(/* ... */);

2. Wire an OTel logs pipeline (your app's bootstrap)

import { LoggerProvider, BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { logs } from "@opentelemetry/api-logs";

const provider = new LoggerProvider({
  processors: [new BatchLogRecordProcessor(new OTLPLogExporter())],
});
logs.setGlobalLoggerProvider(provider);

Do not route audit records through a head-based trace sampler. Audit events are point-in-time evidence; a dropped record is lost evidence. Send them through a dedicated LogRecordProcessor with sampling effectively always-on. From there your OTLP Collector fans out to any backend (Datadog, Grafana, Splunk, stdout) with no code change.

3. Emit events the middleware doesn't cover

withAudit auto-instruments the request methods listed above (and initialize, only if you register your own initialize handler after withAudit, since the SDK pre-registers one). For the rest of the taxonomy — auth, config_change, enumerate, roots_change, logging_failure — emit by hand:

import { recordAuthFailure, recordAuth, recordLoggingFailure, recordAuditEvent } from "mcp-audit-otel";

// Transport-level auth rejection (happens before any handler runs):
recordAuthFailure({ actor_id: "client-9", error_type: "invalid_token", mcp_method: "tools/call" });

// Successful session auth:
recordAuth({ actor_id: "client-9", outcome: "success" });

// Your audit pipeline failed — the CMMC 3.3.4 / DORA RTS Art.12 signal:
recordLoggingFailure({ actor_id: "system", error_type: "exporter_write_failed" });

// Anything else: the generic helper fills schema_version + timestamp, validates, and emits.
recordAuditEvent({
  event_name: "mcp.audit.config_change",
  outcome: "success",
  mcp_method: "tools/call",
  actor_id: "admin",
  target: "feature_flag.enable",
});

Redaction options

Params are SHA-256 hashed by default. Swap the redactor or (carefully) capture raw values:

import { keyDigest } from "mcp-audit-otel";

withAudit(server, {
  redactParams: (method, params) => keyDigest(params),       // record field NAMES only, no values
  // redactTarget: (method, target) => target.split("?")[0], // strip query strings from URIs
  // captureRawParams: true,                                  // DANGER — see below
});
  • sha256Digest (default) — one-way hash; identical params hash alike, values never exposed.
  • keyDigest — records only top-level field NAMES (e.g. keys:amount,ssn). The names are cleartext and not one-way; use only when the names themselves aren't sensitive.
  • redactTarget — the target (tool name / resource URI) is recorded in cleartext by default and is not covered by redactParams. A resource URI can embed a presigned token or a PII path; use this hook to hash or strip it.
  • captureRawParams: trueDANGER. Disables redaction and writes raw arguments into telemetry, which may persist ePHI/CUI/secrets. Enable only with a documented data-handling justification.

Programmatic validation & reporting

import { validateAuditEvent, safeValidateAuditEvent } from "mcp-audit-otel/schema"; // OTel-free
import { computeCoverage, renderReportMarkdown } from "mcp-audit-otel";

const result = safeValidateAuditEvent(input);  // { success, data } | { success: false, error }
const report = computeCoverage(loadedEvents);  // control coverage over a captured log
console.log(renderReportMarkdown(report));

Why Log Records, not Spans

Audit events are emitted as OTel Log Records (Events) via @opentelemetry/api-logs, not as spans. Spans can be dropped by a head-based sampler — unacceptable for audit evidence — and OTel is deprecating Span Events. Each record instead carries the active traceId/spanId (via the attached context) so it correlates to a trace without living inside one. event_name is the static OTel EventName (low cardinality); per-event identifiers live in attributes.

Fields map onto existing semantic conventions wherever they exist (user.id, user.roles, rpc.*, gen_ai.system/gen_ai.tool.name, network.transport, server.address, error.type, code.function.name); anything without a standard home is namespaced under mcp.audit.*.

The event schema

The audit event (the moat) — six required fields establish accountability, the rest add context. Validated with zod; unknown keys are rejected.

| Field | Req | Purpose | | --- | --- | --- | | schema_version | ✓ | Version of this event schema. | | event_name | ✓ | One of 13 mcp.audit.* types (the taxonomy). | | timestamp | ✓ | UTC epoch milliseconds. | | outcome | ✓ | success | failure. | | mcp_method | ✓ | JSON-RPC method, e.g. tools/call. | | actor_id | ✓ | Authenticated principal (the "who"). | | actor_roles | | Roles at event time (least-privilege review). | | target | | Tool name / resource URI / prompt name. | | session_id | | Correlates events within a session. | | params_digest | | Hash/redaction of params — never raw args by default. | | error_type | | Error class. Required iff outcome = failure. | | protocol_version | | MCP version negotiated (on initialize). | | transport | | pipe (stdio) | tcp (HTTP/SSE). | | server_address | | Server host/IP. | | trace_id / span_id | | For serialized events; correlation to a trace. | | code_function | | Handler FQN for forensic correlation (manual / serialized events only — not set by the middleware). |

The control catalog

35 controls across four frameworks, every identifier verified against its authoritative source:

| Framework | Controls | Identifiers | | --- | --- | --- | | SOC 2 (AICPA TSC) | 8 | CC6.1–6.3, CC6.8, CC7.1–7.4 | | HIPAA Security Rule | 6 | 45 CFR 164.312(b), 164.312(c)(2), 164.312(a)(2)(i), 164.308(a)… | | CMMC L2 / NIST 800-171 Rev. 2 | 14 | AU 3.3.1–3.3.9, AC 3.1.7/3.1.12, CM 3.4.3, SI 3.14.6/3.14.7 | | DORA (Reg. EU 2022/2554 + RTS 2024/1774) | 7 | Arts. 9, 10, 17 + RTS Art. 12 |

Per-control authoritative source URLs live in catalog.json and the sourceUrl field.

The machine-readable catalog ships at catalog.json and is importable:

import { getControlsForEvent, getControl } from "mcp-audit-otel/catalog";

getControlsForEvent("mcp.audit.tool_call");   // controls a tool call contributes to
getControl("3.3.1", "CMMC");                   // a single control mapping

Each control's basis is the integrity feature:

  • records — the audit log is the primary artifact the control requires (e.g. CMMC 3.3.1, HIPAA 164.312(b)).
  • evidences — the control's real subject is elsewhere (ID assignment, clock-sync, integrity crypto); the log corroborates it.
  • feeds — a process you run (review, retention, correlation, alerting) must consume the log to satisfy it.

CLI

# Validate a captured audit log (JSONL, or a single JSON array)
npx mcp-audit-otel validate audit.jsonl

# Generate a control-coverage report (markdown or json)
npx mcp-audit-otel report audit.jsonl --format md

# Inspect the control catalog
npx mcp-audit-otel catalog --format json

validate exits non-zero on any invalid event — drop it into CI over a sampled audit log to catch schema drift.

Compatibility notes

  • The OTel mcp.* and gen_ai.* semantic conventions are still at Development stability and may rename upstream. This package pins the attribute strings and tracks renames through its own changelog rather than importing the volatile constants.
  • The OTel JS logs SDK is younger than its traces SDK; this package only touches the stable @opentelemetry/api-logs surface.
  • For a formal SSP/audit deliverable, re-confirm verbatim control text against the primary source PDFs (linked per control via sourceUrl).

License

MIT © Kwashawn Warren