Frameworks
Every way to instrument an agent, in Python and TypeScript — from a bare context manager to a fully patched framework runner. Pick the one that matches your stack; the install command pulls in the right adapter and the snippet is the minimal working integration.
Python
Python SDK (direct)
pip install morse-aiDirect instrumentation of any Python function via a context manager — no framework required.
import morse_ai
morse_ai.init() # reads MORSE_API_KEY from your environment
with morse_ai.run("my-agent") as r:
# your agent logic here
result = "Hello, world!"
r.record(success=True, outcome="completed", cost=0.04)LangChain
pip install morse-ai[langgraph] langchainAutomatic chain/agent tracing via a callback handler — no code inside the chain changes. If
langchain_core isn’t installed, MorseCallbackHandler is still importable but produces no
output (silent no-op).
import morse_ai
from morse_ai.adapters.langchain import MorseCallbackHandler
morse_ai.init() # reads MORSE_API_KEY from your environment
handler = MorseCallbackHandler(agent_name="my-agent")
# Add the callback to any LangChain chain or agent
chain = your_chain.with_config(callbacks=[handler])
chain.invoke({"input": "Hello, world!"})LangGraph
pip install morse-ai[langgraph] langgraphPer-node span capture plus automatic sub-agent topology from graph structure — uses the same
MorseCallbackHandler as LangChain. When both LangGraph and LangChain are importable, auto-detect
installs only the LangGraph adapter so a graph built on LangChain primitives doesn’t get duplicate
spans.
import morse_ai
from morse_ai.adapters.langchain import MorseCallbackHandler
morse_ai.init() # reads MORSE_API_KEY from your environment
handler = MorseCallbackHandler(agent_name="my-graph")
# Pass the callback when invoking your StateGraph
config = {"callbacks": [handler]}
result = your_graph.invoke({"messages": [...]}, config=config)OpenAI
pip install morse-ai[openai] openaiEvery chat.completions.create call on the wrapped client is traced.
import morse_ai
from morse_ai.adapters.openai import wrap
import openai
morse_ai.init() # reads MORSE_API_KEY from your environment
# Wrap your OpenAI client — every chat.completions.create call is traced
client = wrap(openai.OpenAI())
with morse_ai.run("my-agent"):
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hello, world!"}],
)OpenAI Agents
pip install morse-ai[openai_agents] openai-agentsRunner is patched automatically once you call install() — every agent run and handoff is
traced.
import morse_ai
from morse_ai.adapters.openai_agents import install
from agents import Agent, Runner
morse_ai.init() # reads MORSE_API_KEY from your environment
install() # patches Runner automatically — no further changes needed
agent = Agent(name="my-agent", instructions="You are a helpful assistant.")
result = Runner.run_sync(agent, "Hello, world!")OpenAI’s own tracer keeps running. The Agents SDK uploads its own trace of every run to OpenAI, independently of Morse. We deliberately leave that alone — it is your data path, your key, and switching off another vendor’s telemetry inside your process is not something an observability SDK should do behind your back.
If you don’t want the double capture, turn it off yourself:
export OPENAI_AGENTS_DISABLE_TRACING=1Set it before the first agent run — the Agents SDK reads it once, lazily. Note the name: a
plausible-looking OPENAI_TRACING_DISABLED is read by nothing and will leave tracing on.
Anthropic
pip install morse-ai[anthropic] anthropicWrap your client with wrap() — every messages.create call on the returned instance is traced,
including cache/token usage. No monkey-patching of the Anthropic class; only the wrapped instance
is instrumented, so an unwrapped client alongside it is unaffected.
import morse_ai
from morse_ai.adapters.anthropic import wrap
import anthropic
morse_ai.init() # reads MORSE_API_KEY from your environment
# Wrap your Anthropic client — every messages.create call is traced
client = wrap(anthropic.Anthropic())
with morse_ai.run("my-agent"):
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, world!"}],
)wrap() is the explicit, recommended path. A module-level install() also exists (used by
zero-config auto-detect when you only set MORSE_API_KEY), but wrap() mirrors the TypeScript
SDK’s wrapAnthropic() and has no global state.
Claude Agent SDK
pip install morse-ai claude-agent-sdkclaude_agent_sdk.query is patched automatically once you call install() — existing code works
unchanged.
import morse_ai
from morse_ai.adapters.claude_agent_sdk import install
import claude_agent_sdk
morse_ai.init() # reads MORSE_API_KEY from your environment
install() # patches claude_agent_sdk.query automatically
# Your existing claude_agent_sdk code works unchanged
result = claude_agent_sdk.query("Hello, world!")TypeScript
The TypeScript SDK covers the same ground as Python, plus the Vercel AI SDK. Install commands
use the published npm name @morsehq-dev/sdk.
TypeScript SDK (direct)
npm install @morsehq-dev/sdkDirect instrumentation of any async function via run()/spanAsync().
import * as morse from "@morsehq-dev/sdk";
morse.init({ apiKey: process.env.MORSE_API_KEY });
await morse.run({ agentName: "my-agent" }, async (handle) => {
// your agent logic here
handle.setOutcome(true, "completed");
});OpenAI Agents
npm install @morsehq-dev/sdk @openai/agentsRunner is wrapped — agent, llm, tool, handoff and guardrail spans.
import * as morse from "@morsehq-dev/sdk";
import { Runner, Agent } from "@openai/agents";
import { wrapRunnerWithFullInstrumentation } from "@morsehq-dev/sdk/openai-agents";
morse.init({ apiKey: process.env.MORSE_API_KEY });
const { runner, dispose } = wrapRunnerWithFullInstrumentation(new Runner());
const agent = new Agent({ name: "my-agent", model: "gpt-4o" });
await runner.run(agent, "Hello, world!");
dispose();LangGraph
npm install @morsehq-dev/sdk @langchain/langgraphOne callback handler — agent spans per chain, plus llm and tool spans.
import * as morse from "@morsehq-dev/sdk";
import { MorseCallbackHandler } from "@morsehq-dev/sdk/langgraph";
morse.init({ apiKey: process.env.MORSE_API_KEY });
const handler = new MorseCallbackHandler();
const result = await graph.invoke(input, { callbacks: [handler] });LangChain
npm install @morsehq-dev/sdk @langchain/coreChains, LLMs, tools and retrievers outside a LangGraph graph.
import * as morse from "@morsehq-dev/sdk";
import { MorseCallbackHandler } from "@morsehq-dev/sdk/langchain";
morse.init({ apiKey: process.env.MORSE_API_KEY });
// Unlike the langgraph adapter this one opens no trace of its own —
// wrap the invocation so the spans have a parent.
const handler = new MorseCallbackHandler({ agentName: "my-agent" });
await morse.runAsync({ agentName: "my-agent" }, async () => {
await chain.invoke(input, { callbacks: [handler] });
});Anthropic
npm install @morsehq-dev/sdk @anthropic-ai/sdkEvery messages.create call on the wrapped client is traced.
import * as morse from "@morsehq-dev/sdk";
import Anthropic from "@anthropic-ai/sdk";
import { wrapAnthropic } from "@morsehq-dev/sdk/anthropic";
morse.init({ apiKey: process.env.MORSE_API_KEY });
const client = wrapAnthropic(new Anthropic());
// every messages.create call now emits an llm span automaticallyClaude Agent SDK
npm install @morsehq-dev/sdk @anthropic-ai/claude-agent-sdkAgent, llm, tool, subagent-spawn and hook spans. Wrap the package’s top-level
query and call the returned function in its place — the result is still a
Query, with interrupt() and the other control methods forwarded through.
import * as morse from "@morsehq-dev/sdk";
import { query } from "@anthropic-ai/claude-agent-sdk";
import { wrapClaudeAgentQuery } from "@morsehq-dev/sdk/anthropic-agent-sdk";
morse.init({ apiKey: process.env.MORSE_API_KEY });
const tracedQuery = wrapClaudeAgentQuery(query);
for await (const message of tracedQuery({ prompt: "Hello, world!" })) {
// your code unchanged — spans flow to Morse as a side-effect
}The TypeScript and Python packages have genuinely different APIs. TypeScript exposes a top-level
query({ prompt, options }); Python exposes a ClaudeSDKClient class. Use each language’s own
snippet — the two are not interchangeable.
Vercel AI SDK
npm install @morsehq-dev/sdk aiOne agent span per streamText / generateText / generateObject call.
import * as morse from "@morsehq-dev/sdk";
import { createTracedVercelAI } from "@morsehq-dev/sdk/vercel-ai";
morse.init({ apiKey: process.env.MORSE_API_KEY });
// "ai" ships pure ESM with non-configurable exports, so there is nothing to
// monkey-patch — call these traced replacements instead of ai's own.
const { streamText, generateText, generateObject } = await createTracedVercelAI();Related
- Overview — auto-detect, adapter priority, and the silent-failure guarantee.
- Installation — create an API key and the minimal
init()call. - set_context() — attach cost-attribution dimensions to a trace.