Skip to main content
The Runlayer Hooks SDK wires custom agent runtimes into Runlayer’s hook enforcement and telemetry pipeline. It streams an agent’s full conversation — prompts, reasoning, tool calls, and responses — into Runlayer Sessions, and lets Runlayer observe, rewrite, or block the tool calls your agent executes directly. The same SDK ships for TypeScript and Python — use the language switch on any code sample to flip the whole page.
This SDK does not replace MCP client setup. To connect an agent framework (Claude Agent SDK, Vercel AI SDK, OpenAI Agents SDK, Google ADK) to Runlayer-hosted MCP servers, see Agent Frameworks. Use the adapters on this page for tools your own process executes locally.

Copy prompt

Paste this into your coding agent to wire Runlayer into an existing agent project:
Read the Runlayer Hooks SDK docs at https://docs.runlayer.com/runlayer-hooks-sdk and update my agent code to integrate with Runlayer's hook enforcement and telemetry pipeline.

Capabilities

  • lifecycle telemetry for session, prompt, and stop events
  • pre-tool enforcement with optional argument rewriting
  • post-tool output scanning and blocking
  • failed tool telemetry
  • direct MCP source enforcement
  • preflight session emission for ingestion checks
  • transcript-bearing Stop events for assistant reasoning extraction
  • framework tool wrappers for Claude Agent SDK, Vercel AI SDK, OpenAI Agents SDK, and Google ADK

Getting started

1

Install the package

pnpm add @runlayer/hooks-sdk@0.1.2
Install the agent framework package you use separately, such as @anthropic-ai/claude-agent-sdk, ai, @openai/agents, or @google/adk.Latest verified TypeScript framework versions as of 2026-06-17:
SurfacePackageVersion
Claude Agent SDK hooks@anthropic-ai/claude-agent-sdk0.3.172
OpenAI Agents SDK tools@openai/agents0.11.6
Vercel AI SDK toolsai6.0.200
Google ADK tools@google/adk1.2.0
2

Create credentials

Create a Runlayer user API key in Settings → Personal API keys, then set:
export RUNLAYER_BASE_URL="https://your-runlayer-instance.com"
export RUNLAYER_API_KEY="rl_..."
To authenticate as an agent account instead, set:
export RUNLAYER_BASE_URL="https://your-runlayer-instance.com"
export RUNLAYER_AGENT_CLIENT_ID="client_..."
export RUNLAYER_AGENT_CLIENT_SECRET="..."
The SDK exchanges these credentials for a Runlayer bearer token with client_credentials, then caches and refreshes that token internally. Do not configure or pass a pre-minted bearer token.For agent tokens, also set:
export RUNLAYER_AGENT_SUBJECT_TOKEN="user@example.com"
export RUNLAYER_AGENT_SUBJECT_TOKEN_TYPE="urn:runlayer:token-type:user-email"
3

Enable the SDK session monitoring client

The SDK attributes events to the first-party Hooks SDK client (typescript-sdk or python-sdk). In Runlayer, open Settings → General and enable the matching SDK session monitoring client before testing. If the client option is missing, ask a Runlayer admin to enable SDK session monitoring for the workspace.
4

Run preflight

Verify that Runlayer receives hook events before invoking a real model.
import { RunlayerClient, sendRunlayerPreflight } from "@runlayer/hooks-sdk";

const runlayer = RunlayerClient.fromEnv({ clientVersion: "my-agent/1.0.0" });

const result = await sendRunlayerPreflight(runlayer, {
  prompt: "Runlayer ingestion preflight",
});

console.log(`Runlayer session: ${result.sessionUrl}`);
Preflight sends a strict SessionStart, UserPromptSubmit, and Stop sequence for a synthetic session. If any request fails, or if session monitoring is not enabled for this workspace, the helper throws. Open the printed session to confirm it appears in Runlayer.

Configuration

Optional environment variables (identical across languages):
VariablePurpose
RUNLAYER_AGENT_CLIENT_IDAgent account Client ID for client_credentials token exchange.
RUNLAYER_AGENT_CLIENT_SECRETAgent account Client Secret for client_credentials token exchange.
RUNLAYER_AGENT_SUBJECT_TOKENOptional user identity for OBO agent token exchange.
RUNLAYER_AGENT_SUBJECT_TOKEN_TYPEToken type for RUNLAYER_AGENT_SUBJECT_TOKEN; use urn:runlayer:token-type:user-id, urn:runlayer:token-type:user-email, or urn:ietf:params:oauth:token-type:access_token.
RUNLAYER_HOOK_CLIENTOverride the client name sent to Runlayer. Only use this when your deployment supports a dedicated SDK client name.
RUNLAYER_HOOK_DEBUG=1Log non-strict hook event failures to stderr.
RUNLAYER_HOOK_TIMEOUT_MSRequest timeout for Runlayer hook calls.
RUNLAYER_HOOK_MAX_TOOL_OUTPUT_BYTESMaximum serialized tool output sent to Runlayer. Defaults to 65536; larger values are truncated with a marker.
RUNLAYER_HOOK_ENFORCEMENT_FAILURE_MODEEnforcement outage behavior. Defaults to closed; set to open only if your agent should continue when Runlayer cannot be reached.
RUNLAYER_ALLOW_INSECURE_TRANSPORT=1Allow a non-HTTPS RUNLAYER_BASE_URL for local development only.
RUNLAYER_DIRECT_MCP_SOURCE_ENFORCEMENT_PATHEndpoint path for direct MCP source validation, when enabled by your deployment.
Default request timeouts differ by SDK. TypeScript uses a single 10000 ms default. Python uses 30000 ms for enforcement and tool lifecycle hooks, 5000 ms for lifecycle event hooks, and 10000 ms for direct endpoint calls. Override either with RUNLAYER_HOOK_TIMEOUT_MS.

Runtime safety

Runlayer enforcement is by default. If pre-tool, post-tool, or direct MCP source enforcement cannot reach Runlayer, the SDK treats the call as blocked. Lifecycle telemetry is best-effort unless a helper documents strict behavior, such as preflight and transcript Stop emission. Every hook request uses a timeout, and tool output is capped before upload. Failed tool outputs include bounded stdout, stderr, and output fields with truncation metadata when available, so Runlayer can scan failure context without the SDK uploading unbounded logs. The SDK skips pre-tool and post-tool enforcement for Runlayer’s own MCP server names (runlayer, runlayer-plugin, and onelayer) and for MCP calls whose tool URL points at a Runlayer proxy URL. Third-party MCP tools remain enforced by default. For custom runtimes, use the exported shouldEnforceTool / should_enforce_tool helper or the client’s tool-enforcement option so new adapters make the same decision consistently. The base URL must use https:// unless insecure transport is explicitly enabled for a trusted local endpoint.

Framework adapters

Wrap the tools your runtime executes so Runlayer can enforce them. Pick your harness — the language toggle inside each example still controls the whole page.
Pass Runlayer hooks into Claude Agent SDK’s query options.
import { query } from "@anthropic-ai/claude-agent-sdk";
import {
  claudeAgentSdkAssistantMessageToTranscriptLine,
  createClaudeAgentSdkHooks,
  emitClaudeAgentSdkTranscriptStop,
  RunlayerClient,
} from "@runlayer/hooks-sdk/claude-agent-sdk";

const runlayer = RunlayerClient.fromEnv({ clientVersion: "my-agent/1.0.0" });

const transcriptLines: string[] = [];
let sessionId: string | undefined;

for await (const message of query({
  prompt: "Inspect this workspace and summarize the active project.",
  options: {
    allowedTools: ["Read", "Glob", "Grep", "Bash"],
    hooks: createClaudeAgentSdkHooks(runlayer, { includeStop: false }),
    maxTurns: 4,
    model: "claude-opus-4-6",
  },
})) {
  if (message.type === "system" && message.subtype === "init") {
    sessionId = message.session_id;
  }
  if (message.type === "assistant") {
    transcriptLines.push(
      claudeAgentSdkAssistantMessageToTranscriptLine(message.message),
    );
  }
}

if (sessionId) {
  await emitClaudeAgentSdkTranscriptStop(runlayer, {
    model: "claude-opus-4-6",
    sessionId,
    transcriptLines,
  });
}
Use includeStop: false / include_stop=False when you emit a transcript-bearing Stop manually. That lets Runlayer extract assistant thinking or reasoning blocks from the completed transcript instead of receiving a bare lifecycle stop.

Direct MCP source enforcement

Use direct MCP source enforcement when a custom agent can call MCP servers without going through the Runlayer proxy.
const runlayer = RunlayerClient.fromEnv({
  directMcpSourceEnforcementPath: "/api/v1/hooks/direct-mcp-source",
});

await runlayer.enforceMcpSource({
  generationId: "generation-id",
  sessionId: "session-id",
  toolName: "mcp__github__list_repos",
  url: "https://tenant.runlayer.com/api/v1/proxy/server-id/mcp",
});
If Runlayer denies the source, the call throws RunlayerBlockedError.
The Python direct MCP source route posts to the Cursor hook route and currently requires user API-key auth. Configure RUNLAYER_API_KEY for this client; agent-account bearer auth is supported for lifecycle and tool hooks, but not this direct route.

API reference

RunlayerClient.fromEnv(options?)

Creates a client from RUNLAYER_BASE_URL plus one auth source: RUNLAYER_API_KEY or RUNLAYER_AGENT_CLIENT_ID / RUNLAYER_AGENT_CLIENT_SECRET.
client
string
Client name sent to Runlayer. Defaults to RUNLAYER_HOOK_CLIENT or typescript-sdk.
clientVersion
string
Version string for your agent runtime; used for audit-log attribution.
enforcementFailureMode
string
default:"closed"
closed blocks on enforcement outages; open allows on outages.
timeoutMs
number
default:"10000"
Per-request timeout in milliseconds.
toolEnforcement
object
Tool filtering shared by generic wrappers and framework adapters.
  • createClaudeAgentSdkHooks(client, options?) — Claude Agent SDK hook map.
  • sendRunlayerPreflight(client, options?) — strict synthetic lifecycle sequence.
  • emitClaudeAgentSdkTranscriptStop(client, options) — strict transcript Stop.
  • runTool(options) — run a custom tool through pre/post enforcement.
  • withRunlayerVercelAiTool / withRunlayerVercelAiTools / withRunlayerOpenAIAgentsTool / withRunlayerGoogleAdkTool — adapter helpers.
  • enforceMcpSource(options) — validate a direct MCP source.

Troubleshooting

Enable the matching SDK session monitoring client (TypeScript SDK or Python SDK) in Settings → General, then rerun preflight. If you cannot see the client option, ask a Runlayer admin to enable SDK session monitoring for the workspace.
Use the session URL first. If you only have an ID, use the Runlayer session ID in the Sessions URL or search field. The synthetic external session ID emitted by your agent is useful for logs but may not be the best lookup key.
Confirm you constructed the client with the same credentials used for preflight, and wrap local tool execution with a framework adapter or runTool / run_tool. Preflight only proves lifecycle ingestion; it does not wrap your tools by itself.