DoclightDocs

MCP instrumentation

Record tool execution inside your own MCP server with @doclight/mcp 0.1.0, automatically or manually, and shut down cleanly.

@doclight/mcp records tool execution inside an MCP server you run. Version covered: npm 0.1.0. Source: sdk-mcp · npm.

Not the Doclight MCP server

This package instruments a customer's MCP server. The Doclight MCP server is a separate, local program that exposes your Doclight visibility data to AI clients. Installing @doclight/mcp does not install it.

Install

pnpm add @doclight/[email protected] @modelcontextprotocol/[email protected]

@modelcontextprotocol/sdk is a peer dependency (>=1.0.0). 1.29.0 is the version the examples were written against. Configure credentials as described on the Express page.

Automatic instrumentation

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { withDoclight } from "@doclight/mcp";

const server = new McpServer({ name: "example", version: "1.0.0" });
withDoclight(server, {
  apiKey: process.env.DOCLIGHT_API_KEY,
  projectId: process.env.DOCLIGHT_PROJECT_ID,
  endpoint: process.env.DOCLIGHT_ENDPOINT,
});

server.registerTool("health", { description: "Return a safe test result" }, async () => ({
  content: [{ type: "text", text: "ok" }],
}));
await server.connect(new StdioServerTransport());
  • Use McpServer, not the low-level Server. The wrapper patches tool() and registerTool() and wraps handlers registered before or after the call. It relies on MCP SDK internals, so test it with your pinned MCP version.
  • Each invocation creates its own session, records tool_called with duration and status, then completes the session.
  • Tool arguments and return payloads are not recorded. Internal reasoning and other MCP protocol operations are not observed.

Limits of 0.1.0

A thrown exception counts as failure. A resolved result with isError: true is not treated as a failure. Call withDoclight once per server; repeated wrapping is not guarded and duplicates events. getDoclightMcp() is not exported, and automatic wrapping gives no handle for a final flush.

Manual instrumentation and shutdown

Use the manual context when you need to own the lifecycle:

import { createDoclightMcp } from "@doclight/mcp";

const mcp = createDoclightMcp({
  apiKey: process.env.DOCLIGHT_API_KEY,
  projectId: process.env.DOCLIGHT_PROJECT_ID,
  endpoint: process.env.DOCLIGHT_ENDPOINT,
});

const sessionId = mcp.startSession("safe-tool-test");
try {
  const result = await mcp.trackTool("health", sessionId, async () => ({ ok: true }));
  mcp.endSession(sessionId, "success");
  console.error(result);
} catch (error) {
  mcp.endSession(sessionId, "failed");
  throw error;
} finally {
  await mcp.shutdown();
}

createDoclightMcp returns client, startSession, endSession, trackTool, flush and shutdown. Lifecycle hooks are off by default so your server owns shutdown.

  • Stdio servers: stdout carries MCP protocol messages only. Log to stderr (console.error), never console.log.
  • In a long-running server, shut down the shared context once when the server exits, not after each tool call.
  • Do not wrap the same call both manually and automatically.

Evidence strength

Tool timing and thrown errors measured inside your server are stronger execution evidence than website request logs. Even so, a successful tool call does not show the agent completed its whole task.