Skip to Content
SDKFrameworks

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-ai

Direct 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] langchain

Automatic 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] langgraph

Per-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] openai

Every 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-agents

Runner 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=1

Set 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] anthropic

Wrap 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-sdk

claude_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/sdk

Direct 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/agents

Runner 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/langgraph

One 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/core

Chains, 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/sdk

Every 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 automatically

Claude Agent SDK

npm install @morsehq-dev/sdk @anthropic-ai/claude-agent-sdk

Agent, 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 ai

One 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();
  • 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.
Last updated on