Langfuse AgentOps MCP Server with OpenTelemetry Instrumentation & Session Replay for Claude Desktop
Run Langfuse as an AgentOps observability plane reachable from Claude Desktop: OpenTelemetry GenAI semantic conventions, gen_ai.* trace ingestion, dual-device session replay, and per-trace cost tracking over a native MCP server at /api/public/mcp.
Deepak Bagada
CEO, SaaSNext
- Langfuse is a warehouse-first (ClickHouse) observability layer with a native MCP server and an OpenTelemetry OTLP ingestion endpoint.
- The OpenTelemetry GenAI semantic conventions are the unified wire format; Langfuse is an OTLP destination, not a proprietary SDK lock.
- Session replay of traces grouped by session ID is what turns vague feedback into pinpointed actions at a specific step.
Langfuse AgentOps MCP Server with OpenTelemetry Instrumentation & Session Replay for Claude Desktop
Ship an agent in 2026 and you inherit a distributed tracing problem dressed up as a feature. Every LLM call, retrieval step, tool invocation, and provider round-trip emits telemetry, and unless you standardize how that telemetry is encoded, you cannot replay a session, explain a failure, or answer the only question your CFO cares about: which conversations are burning the budget.
This guide builds a production-grade Langfuse AgentOps MCP server — observability functions exposed as Model Context Protocol tools that a Claude Desktop client can invoke directly — with three non-negotiable pillars: OpenTelemetry GenAI semantic conventions for the wire format, session replay so your debugger can step through what an agent actually did, and cost tracking normalized at ingest time.
By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.
Key insight: Langfuse is now the deep storage layer, not the dashboard. ClickHouse completed its acquisition in January 2026, and the platform is warehouse-first. The agent-facing surface is a native MCP server at
/api/public/mcpplus an OpenTelemetry OTLP ingestion endpoint at/api/public/otel. Your job is to make traces speak the same vocabulary on both sides.
Why AgentOps MCP, and why Langfuse
Model Context Protocol is the interface between models and tools. A2A is the interface between agents. Observability sits underneath both, and the fastest way to give your Claude Desktop, Cursor, or Claude Code session a lever on your production agent estate is to expose observability as tools. Instead of alt-tabbing to a dashboard when a run fails, an assistant asks which session failed, why was it slow, which user journey regressed, how much did our refund agent cost last week?
Langfuse is the reference destination for this because of four concrete properties:
- OpenTelemetry-native ingestion. Traces arrive over OTLP at
/api/public/otel, mapped to the Langfuse data model. You instrument once with the standard SDK and repoint exporters without rewriting agent code. - Warehouse-first storage on ClickHouse. Cost per trace is joined with token usage at ingest, so budget queries become column filters, not table scans.
- Replayable sessions. Every run is grouped by a session identifier and can be stepped through as a trace, with input, output, and reasoning at each observation.
- A first-class MCP server. The native Langfuse endpoint at
/api/public/mcp(streamable HTTP) exposes trace, generation, session, prompt, dataset, and scoring tools to any MCP client.
The observability market consolidated sharply: ClickHouse took Langfuse in January 2026 and Cisco closed Galileo (folded into Splunk) in May 2026. Mature backends now ingest OpenTelemetry-shaped traces as their primary wire format, so choosing a non-OTel format in 2026 means opting out of the two largest acquisitions in the category.
Add to that the momentum of the AgentOps tooling ecosystem: the decorator-style AgentOps Python SDK ships with more than 400 integrations and a single decorator that covers most agent frameworks, making it a natural companion for whichever tracing backend you pick.
The two-layer model: transport and meaning
OpenTelemetry solves transport. It handles span creation, batching, context propagation, OTLP export, and sampling — the same plumbing your REST microservices already use. Semantic conventions solve meaning: the attribute vocabulary that lets a backend render a conversation view, compute per-call cost, and group agent runs without custom parsing.
There are two serious vocabularies, and your pick changes how much a rendered trace can tell you:
| Dimension | OpenTelemetry GenAI conventions | OpenInference |
|---|---|---|
| Namespace | gen_ai.* |
openinference-oriented span kinds |
| Span vocabulary | Model + agent spans, gen_ai.operation.name |
LLM, TOOL, AGENT, CHAIN, RETRIEVER spans |
| Auto-instrumentation | Growing community support | Broad (LangChain, LlamaIndex, OpenAI, CrewAI, Mastra, ADK) |
| Maturity in 2026 | Pre-stable, Development status | Established, active releases |
| Wire format | OpenTelemetry OTLP | OpenTelemetry OTLP |
Key insight: The choice is vocabulary, not wire format. Both ride the same OpenTelemetry transport, so either vocabulary keeps you portable across backends. As of core semconv v1.42.0 (June 2026), the entire
gen_ai.*namespace was moved into a dedicated repository and remains Development status with no 1.0 — emit the five agent spans now to insulate your code from churn, but do not treat the convention as a stability promise yet.
Architecture: instruments, collector, Langfuse
Here is the shape you are building:
+-----------------------------+ OTLP (gRPC 4317 / HTTP 4318)
| Claude Desktop / Cursor | -------------------------------+
| (MCP client, stdio / HTTP) | |
+-------------+---------------+ v
| MCP streamable HTTP +-----------------------+--------------------------+
+-------------v---------------+ | OpenTelemetry Collector |
| Langfuse AgentOps MCP | ------->| receivers: otlp |
| Server (Node FastMCP) | OTLP | processors: batch + attribute upsert |
| - telemetry / tools / rest | expos. | exporters: otlphttp -> langfuse |
+-----------------------------+ +-----------------------------------------+-----+
|
Agent runtime --- LLM providers ---- v
(LangGraph, +---------------------------+
CrewAI, Mastra) | Langfuse (ClickHouse) |
| traces / sessions / obs |
| cost + usage at ingest |
+---------------------------+
The split is deliberately redundant at the client edge. The collector receives both MCP-specific analytics (user intentions, session replay) and framework-generated spans, and it forwards a normalized stream to Langfuse (and optionally a second backend for alerts). Nothing slow runs in the MCP path; Langfuse remains a destination, not a hop.
Step 1 — Environment and export
Start from an env file referenced by both the collector and the MCP server so secrets live in one place per environment:
# Langfuse cloud (EU region shown)
LANGFUSE_HOST=https://cloud.langfuse.com
LANGFUSE_OTLP_ENDPOINT=https://cloud.langfuse.com/api/public/otel
LANGFUSE_PUBLIC_KEY=pk-lf-xxxxxxxx
LANGFUSE_SECRET_KEY=sk-lf-xxxxxxxx
# OpenTelemetry
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_SERVICE_NAME=langfuse-agentops-mcp
# AgentOps companion
AGENTOPS_API_KEY=xxxxxxxx
AGENTOPS_AUTO_START_SESSION=true
Key insight: Langfuse authenticates via an Authorization basic header composed from public key and secret key. Keep only environment tokens server-side; never let a Claude Desktop config carry a secret past a pointer to a remote connector that performs OAuth.
Step 2 — OpenTelemetry Collector config
The collector adds production fidelity: batch buffers and a clean route to Langfuse.
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
grpc:
endpoint: 0.0.0.0:4317
processors:
batch:
timeout: 5s
send_batch_size: 1024
attributes:
actions:
- key: environment
value: production
action: upsert
exporters:
otlphttp/langfuse:
endpoint: "https://cloud.langfuse.com/api/public/otel"
headers:
Authorization: "Basic ${LANGFUSE_BASIC_AUTH}"
x-langfuse-ingestion-version: "4"
connectors:
spanmetrics:
dimensions:
- name: gen_ai.operation.name
- name: gen_ai.request.model
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/langfuse]
Langfuse aims to be compliant with the OpenAI telemetry conventions and maps attributes in the langfuse.* namespace onto its data model. The usual production trap is turning off the batch processor under load, which floods the endpoint; keep it on.
Step 3 — A TypeScript FastMCP observability server
Rather than scrape the platform API, implement a FastMCP server that exposes observability as tools a Claude Desktop session can call. Each tool declares its inputSchema as plain JSON so the model can build well-shaped invocations.
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { trace } from '@opentelemetry/api'
const tracer = trace.getTracer('langfuse-agentops-mcp')
const server = new McpServer({
name: 'langfuse-agentops-mcp',
version: '1.0.0',
})
server.tool(
'get_trace',
'Fetch a single Langfuse trace with its full span tree and cost',
{
inputSchema: {
type: 'object',
properties: {
trace_id: { type: 'string', description: 'Langfuse trace identifier' },
include_events: {
type: 'boolean',
default: false,
description: 'Expand events under the trace',
},
},
required: ['trace_id'],
} as any,
},
async ({ trace_id, include_events }) => {
const span = tracer.startSpan('mcp.get_trace')
span.setAttribute('mcp.tool.name', 'get_trace')
try {
const data = await langfuseClient.fetchTrace(trace_id)
span.setAttribute('gen_ai.usage.input_tokens', data.inputTokens)
span.setAttribute('gen_ai.usage.output_tokens', data.outputTokens)
return {
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
}
} finally {
span.end()
}
},
)
server.tool(
'list_session_traces',
'Return all traces grouped under a session ID (session replay entry point)',
{
inputSchema: {
type: 'object',
properties: {
session_id: { type: 'string' },
limit: { type: 'number', default: 50 },
},
required: ['session_id'],
} as any,
},
async ({ session_id, limit }) => {
const traces = await langfuseClient.listSession(session_id, limit)
return {
content: [{ type: 'text', text: JSON.stringify(traces, null, 2) }],
}
},
)
server.tool(
'agent_cost_by_agent',
'Aggregate cost and token usage grouped by agent for a time window',
{
inputSchema: {
type: 'object',
properties: {
from: { type: 'string', format: 'date-time' },
to: { type: 'string', format: 'date-time' },
group_by: { type: 'string', enum: ['agent_id', 'model'] },
},
required: ['from', 'to'],
} as any,
},
async ({ from, to, group_by }) => {
const agg = await langfuseClient.aggregateUsage({ from, to, groupBy: group_by })
return {
content: [{ type: 'text', text: JSON.stringify(agg, null, 2) }],
}
},
)
const transport = new StdioServerTransport()
await server.connect(transport)
This is deliberately minimal but model-usable: each tool carries a reference inputSchema, the model sees well-named tools, and cost columns flow because Langfuse stores the per-call usage joined at ingest.
The Python FastMCP equivalent
If your observability glue lives in Python, the same tools are a few lines with the official SDK and a native tracing story:
from langfuse import get_client
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("langfuse-agentops")
langfuse = get_client()
@mcp.tool()
def get_trace(trace_id: str, include_events: bool = False) -> dict:
'''Return a full trace including cost and any events.'''
return langfuse.fetch_trace(trace_id, include_events=include_events)
@mcp.tool()
def list_session_traces(session_id: str, limit: int = 50) -> dict:
'''Replay a session by listing all traces in order.'''
return langfuse.list_traces({"session_id": session_id, "limit": limit})
@mcp.tool()
def agent_cost(from_ts: str, to_ts: str, group_by: str = "agent") -> dict:
'''Cost and token aggregate per agent for a window.'''
return langfuse.aggregate_usage(from_ts=from_ts, to_ts=to_ts, group_by=group_by)
mcp.run(transport="streamable-http")
Key insight: Because Langfuse stores the joined cost on every observation, the "which conversations are burning the budget" query is a ClickHouse
select ... group by agent_id order by cost_usdwith no price-table join at query time.
Step 4 — Connect Claude Desktop / Cursor
For a server the client launches in-process, add it to the config. For a remote streamable-HTTP server behind OAuth, prefer the Connectors UI.
{
"mcpServers": {
"langfuse": {
"command": "node",
"args": [],
"env": {
"LANGFUSE_HOST": "https://cloud.langfuse.com",
"LANGFUSE_PUBLIC_KEY": "pk-xxxx",
"LANGFUSE_SECRET_KEY": "sk-xxxx",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://otel-collector:4318"
}
}
}
}
Cursor uses the same top-level mcpServers key in .cursor/mcp.json. Local servers use command and args and env; remote (OAuth) servers use a url and are wired through the streamable-HTTP transport, which replaced the deprecated SSE transport in clients like Desktop, Claude Code, and VS Code.
OAuth 2.0 / Security guide
Never run the Langfuse secret in an MCP stdio child shipped to a shared desktop. The secure pattern is remote MCP plus OAuth, and Claude Desktop auto-discovers the standard metadata endpoint:
- Expose
.well-known/oauth-authorization-serverand a metadata document describing your issuer and token endpoint. - The client fetches the issuer, then runs the authorization-code flow with PKCE; the client never sees the secret.
- Your MCP server validates the bearer JWT (check
iss,aud, andexp) per request and returns RFC 6750-compliant401s when the token is missing or invalid. - Issue scopes such as
langfuse:read:tracesandlangfuse:read:costand assert them before each tool answers.
{
"mcpServers": {
"langfuse": {
"url": "https://mcp.example.com/mcp"
}
}
}
Claude Desktop fetches the well-known metadata, walks the user through OAuth, and stores the token itself — the client never holds your key. Keep real secrets out of tracked config files and inject them at runtime.
Session replay in practice
Replay is the difference between "the site is broken" and "the agent called the refund tool twice at 03:14 because the tool response was empty". Because each run carries a session identifier and ordered observations, you can walk input-to-output per step, diff two runs to surface behavioral regressions, and pinpoint the exact divergence step — which is what production MCP replay suites now benchmark.
FAQ
Does Langfuse require the Langfuse SDK to benefit from MCP plus OTel? No. The OTLP endpoint at /api/public/otel ingests any OTel-compliant exporter, and Langfuse maps gen_ai.* and langfuse.* attributes onto its model. SDKs are an easier path, not a hard requirement.
Is the OpenTelemetry GenAI semantic convention stable? As of mid-2026 it is Development status only; the whole namespace moved to a dedicated semantic-conventions-genai repository and no 1.0 is published. Emit the five agent spans with gen_ai.operation.name set and track the migration notes.
Can the MCP server write back, for example submit scores or edit prompts? Yes. The native Langfuse MCP server exposes dataset, prompt, annotation, and scoring tools; your own FastMCP server can wrap writes too, but gate them behind read-only versus write OAuth scopes.
Do session replay and cost land for every framework? Langfuse surfaces pre-aggregated cost per trace and reads traces from LangChain, LlamaIndex, CrewAI, Mastra, and any OpenTelemetry-compatible runtime, so replay and cost work for all of them.
For more server recipes, browse the MCP directory, apply these patterns in real workflow examples, and follow the protocol roadmap on our latest AI news.
By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.
Enjoyed this breakdown? Get our morning dispatch in your inbox.
Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.
Deepak Bagada
CEO, SaaSNext
Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.
Cursor 2026 Agent Mode & Google Workspace Plugins: Multi-File Automated Code Execution Architecture
Next Story →Google ADK in 2026: Enterprise Multi-Agent Systems with Native A2A Protocol & Multimodal Agents
Related Intelligence Analysis
Vercel AI SDK Tool Calling React: 5 Steps (2026)
Vercel AI SDK tool calling React integration is a programming pattern that executes server-side functions based on large language model decisions and streams the results to a React frontend. By combining streamText with...
Fact-Density vs. Word Count: The New SEO for 2026
Fact Density is the ratio of verifiable, unique information to the total word count of a piece of content. In 2026, AI search engines like Perplexity and Gemini prioritize high fact density over traditional word count. A...
NVIDIA Audex vs Qwen3.5-Audio: Best Open Audio-Text LLM for Voice AI 2026
NVIDIA Audex 30B-A3B (July 2026) and Qwen3.5-35B-A3B are the two leading open audio-text LLMs. Audex uniquely handles both audio understanding and generation in a single model while preserving text intelligence. Qwen3.5-...