Audience: Platform engineers, compliance leads, AI governance teams. No prior SWT3 experience required.
CRITICAL ASSESSOR NOTICE: SWT3 witness anchors are evidence artifacts, not compliance determinations. Each anchor records that a governance-relevant event occurred and preserves its cryptographic fingerprint. The assessor determines whether the evidence satisfies a given control requirement. The protocol does not make pass/fail compliance decisions on behalf of any regulatory body. Substance verification remains the assessor's responsibility.

1. The Problem

Your MCP server runs tools. Those tools make decisions, access data, and take actions on behalf of users. When an auditor asks "what did the AI do?", the answer is usually application logs.

Application logs were never designed to be compliance evidence. They can be incomplete, modified, or lost. They lack cryptographic integrity. They do not map to regulatory controls. They are not independently verifiable.

The Witness Middleware creates a cryptographic witness record for every tool call your server processes (or a deterministic fraction, if you configure sampling). Each record proves which tool ran, how long it took, and whether it succeeded. The record is anchored by a SHA-256 fingerprint that anyone can independently recompute. If a single bit changes, the hash breaks. That is the difference between a log entry and evidence.

2. One Line of Code

Key point: You do not modify your tools. You do not modify your server logic. You wrap the transport layer, and the audit trail appears automatically.
import { withSWT3 } from "@tenova/swt3-mcp/middleware";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const transport = withSWT3(new StdioServerTransport(), {
  apiKey: process.env.SWT3_API_KEY,
});
await server.connect(transport);

// Drain buffered evidence before exit
process.on("SIGTERM", async () => {
  await transport.flush();
  process.exit(0);
});

This wraps the communication layer between your server and the MCP client. Your tool handlers run exactly as before. The middleware buffers witness payloads and flushes them in batches. The flush() call on shutdown ensures no evidence is lost when the process exits. See Batching and Graceful Shutdown for details.

No API key? Omit it entirely. The middleware runs in demo mode and logs witness records to stderr. No network calls, no account required. Upgrade to a free account when you want persistent evidence.

3. What Happens When a Tool Runs

Client sends tool request ──> Your tool handler runs ──> Response sent to client │ Sampling gate (deterministic) ● Witnessed? ──> Payload buffered ● Skipped? ──> Skip counter incremented │ Buffer flushed in batch (on count threshold, timer, or shutdown) ● Includes AI-SAMPLE.1 summaries ● Failed batches retried automatically
Three guarantees:
  1. Your tool response is already sent before the witness fires. The middleware cannot slow down, block, or fail your tool calls.
  2. If the witness network call fails, your tool call is completely unaffected. Failed batches are automatically retried with exponential backoff. If all retries are exhausted, error receipts are fired and the batch is dropped. Evidence delivery is best-effort; tool execution is never compromised.
  3. The middleware sees tool names and execution time. It does not see prompt content, response content, or anything inside your tool handlers.

Each witness record is anchored to the AI-TOOL.1 procedure in the Universal Control Taxonomy. This maps directly to EU AI Act Art. 12 (record-keeping), NIST AI RMF MEASURE 2.5, and OWASP Agentic Top 10 controls for tool abuse prevention.

4. When to Use This

SWT3 offers three integration levels. Choose based on how much compliance depth your system requires.

Integration LevelComponentsChoose This IfRegulatory Coverage
Level 1: Passive Observation withSWT3(transport) middleware only Existing MCP server, need an audit trail fast, zero code changes to tools EU AI Act Art. 12, CMMC AU-2/AU-3, NIST MEASURE 2.5
Level 2: Active Governance SDK Witness class with strict: true Need pre-inference gates, gatekeeper blocking on failure, full procedure coverage (drift, HITL, model integrity) Full AI RMF, EU AI Act Art. 9/14, SR 11-7
Level 3: Full Compliance Stack Middleware + SDK + MCP server (33 tools) Regulated industry, multi-framework requirements, auditor-facing evidence packages, sovereign deployment All 36 frameworks, full UCT taxonomy (114 procedures)
Levels are additive. The middleware and SDK run together without conflict. Level 1 adds transport-layer evidence. Level 2 adds governance control (gatekeeper blocking, drift detection, human oversight witnessing). Level 3 adds compliance tools (forensic timelines, crosswalk resolution, Verifiable Credentials). Each level includes the capabilities of the levels below it.

This guide covers Level 1 (the middleware). For framework-specific configuration recipes that combine all three levels into copy-paste regulatory compliance configs, see the Regulatory Configuration Recipes guide.

5. Configuration

All settings are optional. The middleware runs with zero configuration in demo mode.

SettingDefaultWhen You Need It
Core
endpointsovereign.tenova.ioOnly change if you are self-hosting the Axiom platform
apiKeydemo modeOmit to evaluate locally. Set an axm_ key to persist anchors to the ledger
tenantIdauto-resolvedSet explicitly if your API key serves multiple tenants
clearingLevel1 (standard)Raise to 2 or 3 to reduce how much metadata leaves the wire
agentIdnoneSet to identify which agent produced the anchor (required for multi-agent chains)
signingKeynoneSet for HMAC non-repudiation. If someone disputes the evidence, the signature proves who minted it
maxBuffer100Raise if your server handles more than 100 concurrent in-flight tool calls
onWitnessnoneSet a callback to pipe witness receipts to your logging or metrics system
Batching
batchSize10Number of witness payloads buffered before a batch flush. Set to 1 for per-call behavior
flushIntervalMs5000Maximum time (ms) before the buffer is flushed, even if batchSize has not been reached
Sampling
samplingRate1.0Global sampling rate (0.0 to 1.0). Set below 1.0 to witness a fraction of tool calls
samplingRatesnonePer-tool overrides: { "critical_tool": 1.0, "health_check": 0.01 }
Multi-Tenant
resolveTenantnoneCallback to resolve tenant per tool call. For multi-tenant MCP servers
Retry
maxRetries3Number of retry attempts for failed batch submissions (5xx or network errors)
maxRetryBuffer500Maximum payloads held in the retry queue. Oldest batches evicted first

6. Batching and Graceful Shutdown

By default, the middleware buffers witness payloads and flushes them in batches of 10 (or every 5 seconds, whichever comes first). This reduces network overhead from one HTTP call per tool invocation to one call per batch.

Flush triggers: The buffer flushes when any of these conditions is met:
  1. The buffer reaches batchSize payloads (default: 10)
  2. The flush timer fires (default: every 5000ms)
  3. You call transport.flush() manually
  4. You call transport.close() (best-effort flush before teardown)

Graceful Shutdown

withSWT3() returns the transport with a flush() method attached. Call it before your process exits to drain any remaining payloads.

const transport = withSWT3(new StdioServerTransport(), {
  apiKey: process.env.SWT3_API_KEY,
});
await server.connect(transport);

// On shutdown:
process.on("SIGTERM", async () => {
  await transport.flush();
  process.exit(0);
});

If you call transport.close() instead (the standard MCP transport lifecycle), the middleware flushes automatically before closing. If the flush fails, the close still completes. Evidence delivery is best-effort; transport teardown is never blocked.

The flush timer uses .unref() so it will not keep a Node.js process alive after all other work is done. You do not need to explicitly clean up the timer.

Tuning for Real-Time vs. Throughput

// Real-time evidence (every tool call flushed immediately)
withSWT3(transport, { batchSize: 1 });

// High-throughput server (batch up to 50, flush every 10s)
withSWT3(transport, { batchSize: 50, flushIntervalMs: 10_000 });
Upgrading from v0.6.4: Previous versions fired one HTTP POST per tool call. The default is now batched (10 payloads per flush). To preserve the old per-call behavior, set batchSize: 1. All other settings are backward-compatible.

7. Sampling

High-volume servers may not need to witness every single tool call. The middleware supports deterministic sampling: given the same fingerprint input, the same witness/skip decision is made every time. This is not random. It is reproducible.

// Witness 10% of tool calls globally
withSWT3(transport, {
  apiKey: process.env.SWT3_API_KEY,
  samplingRate: 0.1,
});

Per-Tool Overrides

Critical tools can be witnessed at a higher rate than routine ones. Per-tool rates override the global rate.

withSWT3(transport, {
  apiKey: process.env.SWT3_API_KEY,
  samplingRate: 0.01,   // 1% for everything else
  samplingRates: {
    "execute_trade": 1.0,    // 100% for trades
    "modify_account": 1.0,   // 100% for account changes
    "health_check": 0.0,     // never witness health checks
  },
});

Why Deterministic Sampling Matters to Assessors

Deterministic sampling is a security property, not a performance optimization.
  1. Random sampling can be gamed. If sampling were random, an adversary could retry or resend a tool call until the unfavorable evidence lands outside the sample. Selective exclusion of damaging evidence would be undetectable.
  2. SWT3 sampling is deterministic. The witness/skip decision is derived from the SHA-256 hash of the fingerprint input itself. The first two bytes of the hash are interpreted as a uint16 threshold and compared against the configured rate. The same payload always produces the same decision.
  3. Adversaries cannot selectively exclude evidence. Changing the payload to alter the sampling decision changes the fingerprint, which breaks the cryptographic chain. The sampling decision is bound to the evidence it governs.
  4. Assessors can independently verify. Given any payload, an assessor can recompute the SHA-256 hash, extract the first two bytes, and confirm the sampling decision was correct. No trust in the system operator is required.
AI-SAMPLE.1 summaries: When tools are sampled out, the middleware tracks how many calls were skipped per tool. On each batch flush that contains witnessed payloads, it prepends an AI-SAMPLE.1 summary anchor that records the skip count and sampling rate. The audit trail always shows what was witnessed and what was intentionally excluded.
This is not data loss. It is documented statistical coverage with full accountability for exclusions. The AI-SAMPLE.1 summaries create a complete audit record of what was witnessed, what was excluded, and at what rate. The assessor has full visibility into the sampling regime and can independently verify that every exclusion decision was deterministic and reproducible.

The sampling algorithm is identical across all SWT3 implementations (Python, TypeScript, Go, Rust, C#, Ruby, Swift), producing the same sampling decisions for the same input across all languages.

Demo mode note: In demo mode (no API key), every tool call logs to stderr regardless of sampling settings. Sampling only applies to live API submissions. If you set samplingRate: 0.1 in demo mode, all calls appear in stderr. When you switch to a live API key, only 10% will produce anchors. This is intentional: demo mode is for validation, not volume simulation.

8. Multi-Tenant

If your MCP server handles requests from multiple tenants, the middleware can resolve the correct tenant for each tool call at runtime.

withSWT3(transport, {
  apiKey: process.env.SWT3_API_KEY,
  tenantId: "DEFAULT_TENANT",
  resolveTenant: (toolName, params) => {
    // Extract tenant from the tool call parameters
    return params.tenant_id;
  },
});

The callback receives the tool name and the full JSON-RPC params object. Return a tenant ID string, or undefined to fall back to the configured tenantId.

Each payload in the batch carries its own tenant_id field. A single batch can contain payloads for multiple tenants. The server routes each payload to the correct tenant ledger.

The resolved tenant is also used in the fingerprint formula, so fingerprints are always scoped to the correct tenant. Cross-tenant fingerprint collisions are not possible.

9. Retry

Network failures and server errors do not lose evidence. The middleware maintains an in-memory retry queue for failed batches.

ResponseBehavior
2xx (success)Receipts fired, payloads cleared
4xx (client error)Dropped immediately. Error receipts fired. No retry (the request itself is invalid)
5xx (server error)Queued for retry with exponential backoff (1s, 2s, 4s)
Network errorQueued for retry with exponential backoff

Retries piggyback on the flush interval timer. There are no extra timers or background threads. Each flush cycle checks the retry queue first, sends any eligible batches (past their backoff delay), then flushes new payloads.

The retry queue is bounded at maxRetryBuffer payloads (default: 500). If the queue overflows, the oldest batches are evicted first. After maxRetries attempts (default: 3), the batch is dropped and error receipts are fired via onWitness.

Not a WAL: The retry queue is in-memory only. If the process crashes, queued payloads are lost. This is intentional. The middleware is a best-effort evidence layer, not a message broker. For crash-resilient witnessing, use the full SDK (@tenova/swt3-ai for TypeScript or swt3-ai for Python) which includes write-ahead log support.

10. Real-World Patterns

Production MCP Server

A typical production configuration combines batching, sampling, signing, and retry. This is the recommended starting point for regulated environments.

const transport = withSWT3(new StdioServerTransport(), {
  apiKey: process.env.SWT3_API_KEY,
  agentId: "claims-processor-v3",
  signingKey: process.env.SWT3_SIGNING_KEY,
  clearingLevel: 2,

  // Batching: flush every 20 payloads or 10 seconds
  batchSize: 20,
  flushIntervalMs: 10_000,

  // Sampling: witness all trades, 10% of reads, skip health checks
  samplingRate: 0.1,
  samplingRates: {
    "execute_trade": 1.0,
    "modify_account": 1.0,
    "health_check": 0.0,
  },

  // Retry: 3 attempts with exponential backoff
  maxRetries: 3,

  // Observability: pipe receipts to your logging stack
  onWitness: (receipt) => {
    logger.info("swt3.witness", {
      tool: receipt.toolName,
      fingerprint: receipt.fingerprint,
      error: receipt.error ?? null,
    });
  },
});
await server.connect(transport);

process.on("SIGTERM", async () => {
  await transport.flush();
  process.exit(0);
});

Observability Pipeline

Pipe witness receipts into your existing observability stack. Every tool call becomes a structured event in Datadog, Splunk, CloudWatch, or any system that accepts structured logs.

withSWT3(transport, {
  apiKey: process.env.SWT3_API_KEY,
  onWitness: (receipt) => {
    logger.info("swt3.tool.witnessed", {
      tool: receipt.toolName,
      fingerprint: receipt.fingerprint,
      latency_ms: receipt.latencyMs,
      error: receipt.error ?? null,
    });
  },
});

The callback fires for every witnessed tool call when the batch is flushed, regardless of whether the POST succeeded or failed. If the POST failed, receipt.error tells you why. Sampled-out calls do not fire onWitness (they appear in AI-SAMPLE.1 summaries instead). The tool call itself is never affected.

Signed Anchors for Regulated Environments

When evidence may be disputed, HMAC-SHA256 signatures bind each anchor to the holder of the signing key. The signature proves the anchor was minted by an authorized system, not reconstructed after the fact.

withSWT3(transport, {
  apiKey: process.env.SWT3_API_KEY,
  signingKey: process.env.SWT3_SIGNING_KEY,
  agentId: "claims-processor-v3",
});

Register the signing key server-side to enable verification. Every anchor minted by this agent carries a tamper-evident signature.

Minimal Metadata for Classified Environments

Clearing level 3 strips all descriptive metadata. Tool names are hashed. Only numeric factors (call count, latency, success/failure) and the cryptographic fingerprint leave the wire. The evidence proves something happened without revealing what.

withSWT3(transport, {
  apiKey: process.env.SWT3_API_KEY,
  clearingLevel: 3,
});

11. Common Questions

"Can the middleware break my server?"

No. The tool response is committed to the wire before the witness fires. If the witness fails, your server never knows. This behavior is verified by 358 automated tests covering error injection, network failure, concurrent load, batching, sampling, multi-tenant routing, and retry scenarios.

"Can I use this with any MCP transport?"

Yes. Stdio, SSE, HTTP, WebSocket, or any custom transport that implements the standard MCP Transport interface. The middleware does not import the MCP SDK. It works with the interface shape, not a specific implementation.

"What if I already use the full SWT3 MCP server?"

They coexist without conflict. The middleware witnesses at the transport layer (tool names and latency). The server's 33 tools witness at the handler layer (detailed compliance metadata). Two independent evidence streams that complement each other.

"What about high-volume servers?"

The middleware batches witness payloads (default: 10 per batch, flush every 5 seconds) to reduce network overhead. For servers processing thousands of tool calls per minute, use sampling to witness a configurable fraction of calls while emitting AI-SAMPLE.1 summaries that document exactly what was excluded. Failed batches are automatically retried with exponential backoff. The pending call tracker holds up to 100 concurrent in-flight calls (configurable via maxBuffer).

"Does this work without an account?"

Yes. Omit the API key and the middleware runs in demo mode. Witness records log to stderr with a (demo) tag. No network calls. When you are ready for persistent evidence, create a free account and set the API key.

"What regulatory frameworks does this cover?"

Every AI-TOOL.1 anchor maps to EU AI Act Art. 12 (record-keeping), NIST AI RMF MEASURE 2.5 (AI system monitoring), OWASP Agentic Top 10 (tool abuse detection), and CMMC AU-2/AU-3 (audit event generation). The UCT Registry documents the full mapping.

12. What This Does Not Do

Clear boundaries:

Getting Started

Install the package:

npm install @tenova/swt3-mcp

Import the middleware, wrap your transport, and connect. Every tool call your server processes now produces a cryptographic witness record (or a deterministic fraction, if you configure sampling). Start in demo mode, verify the output, then connect to the ledger when you are ready.