Skip to main content
Glama

@mgcrea/mcp-langfuse

A Model Context Protocol server for Langfuse, built for Langfuse v4 deployments in events-only mode: traces, sessions, observations, scores, metrics and prompts.

It is read-only. The only path that can mutate anything is the langfuse_request escape hatch, and it is GET-only unless LANGFUSE_ALLOW_WRITES=1.

Why another Langfuse MCP

Two failures with the existing Python langfuse-mcp prompted this one.

  1. Events-only mode. Langfuse v4 can run in an "events only" mode where GET /traces, /traces/{id}, /sessions, /observations (v1) and /scores (v1 and v2) all answer 404 — not available on deployments running in Langfuse v4 events_only mode. Every older client is built on those endpoints. This server reads only what survives: /v2/observations, /v2/metrics, /v3/scores and /v2/prompts. It rebuilds a trace from its observations and a session from its traces.

  2. The wrong project, silently. Langfuse keys are project-scoped, so the key alone decides which project answers. A key copied from the wrong project answers every query with plausible data from that project. This server checks every key against the project it is filed under before reading anything, names the project in its startup log and in langfuse_auth_status, and stamps project on every data response.

One server reads many projects: it holds one key per project, and every data tool takes a project argument. On an OSS Langfuse there is no API that mints those keys (it is the EE admin-api), so pnpm provision writes them into Langfuse's own Postgres — see Provisioning keys.

Related MCP server: Langfuse MCP Server

Features

  • Traces as a depth-first tree with total token usage, cost, ERROR steps, and the trace's input/output. That input/output comes from the first observation that has one, because the root span is often an AGENT that carries none.

  • Sessions as their traces in chronological order, each with its input and output.

  • Observation search by type, level, model, name, latency, metadata (raw filters) and indexed literal text search over input/output. level: "ERROR" is how to find failed runs.

  • Server-side aggregates through /v2/metrics: counts, cost, token sums and latency percentiles, grouped and bucketed over time.

  • Output is shaped for the context window. Empty fields and per-row pricing echoes are dropped, and so are the OpenTelemetry scope.* / resourceAttributes.* metadata keys that every span repeats (one of them is the project's public key). LLM inputs, often 20 KB each, are cut to a budget you choose, with an honest [truncated: N chars total] marker.

  • Relative time windows (since: "24h", "7d"), because a model rarely knows today's date.

  • Native fetch, no runtime dependencies beyond the MCP SDK and Zod.

Configure

Keys come from three places, merged by project name, earlier wins:

  1. LANGFUSE_PUBLIC_KEY + LANGFUSE_SECRET_KEY, filed under LANGFUSE_PROJECT.

  2. LANGFUSE_PROJECTS, a JSON map: {"sales": {"publicKey": "pk-lf-…", "secretKey": "sk-lf-…", "id": "…"}}.

  3. The keys file, ~/.config/langfuse-mcp/keys.json — what pnpm provision writes.

Variable

Description

LANGFUSE_HOST

Instance URL, e.g. https://langfuse.example.com. Else the keys file's host, else Langfuse Cloud.

LANGFUSE_KEYS_FILE

Keys file path. Defaults to $XDG_CONFIG_HOME/langfuse-mcp/keys.json.

LANGFUSE_PROJECTS

JSON map of project name → key pair, as above.

LANGFUSE_PUBLIC_KEY

pk-lf-…, a single project's key, from Project Settings → API Keys.

LANGFUSE_SECRET_KEY

sk-lf-…, the matching secret.

LANGFUSE_PROJECT

The name (or id) that single key is filed under, and checked against.

LANGFUSE_ALLOW_WRITES

1 lets langfuse_request send POST/PUT/PATCH/DELETE.

LANGFUSE_MAX_RETRIES

Retry budget for 429/5xx. Defaults to 3.

LANGFUSE_DEBUG

1 logs each request to stderr.

LANGFUSE_BASE_URL is accepted as an alias of LANGFUSE_HOST, and the names match the Langfuse SDKs and the Python langfuse-mcp, so an existing single-project .mcp.json entry switches over by changing its command.

The project check. Every key in LANGFUSE_PROJECTS or the keys file must resolve to the project it is filed under — by name ignoring case, or by its recorded id — or every call to that project is refused before anything is read. A lone LANGFUSE_PUBLIC_KEY pair without LANGFUSE_PROJECT is served unchecked as default, as before; alongside other projects it is ignored until LANGFUSE_PROJECT names it.

One project or several. With one project configured, project may be omitted. With several it is required on every data tool, with no default — a silently chosen project is the failure this server exists to prevent.

With no keys at all the server still starts and serves langfuse_auth_status, which says what to set. It does not exit, because a server that exits shows up in the client only as Connection closed. A malformed keys file is reported the same way.

Provisioning keys

Langfuse keys are project-scoped, and the API that mints them — POST /api/public/projects/{id}/apiKeys — needs an organization key and the EE admin-api entitlement (self-hosted Enterprise, or Cloud Team/Enterprise). On an OSS instance every organization endpoint answers 403 "not available on your current plan". The data plane is fully OSS, so a key written straight into Langfuse's Postgres, exactly as its own UI writes it, authenticates like any other. That is what pnpm provision does:

kubectl port-forward -n langfuse svc/langfuse-postgresql 15432:5432 &
LANGFUSE_HOST=https://langfuse.example.com \
LANGFUSE_DB_URL=postgres://langfuse:…@127.0.0.1:15432/langfuse \
LANGFUSE_SALT=… \
pnpm provision            # dry run: prints the plan, changes nothing
pnpm provision --apply    # mint, verify, save
  • LANGFUSE_SALT must be the SALT of the running langfuse-web. Read it from the deployment, not a local copy of the secrets, which can be stale. A wrong salt — or the wrong database — still inserts a row whose key never authenticates.

  • Every minted key is verified against GET /api/public/projects after its row commits, and must resolve to the right project. A key that fails is deleted again and the run stops.

  • Safe to re-run. A recorded key that still works is kept. One that returns 401 is replaced, and only then revoked. One that reads a different project is replaced but left in place, since something else may use it. New projects get keys; entries for deleted projects leave the file (no row is touched).

  • The file is written after each key, mode 0600, so a crash never leaves a key in Langfuse whose secret exists nowhere.

  • --only a,b / --exclude a,b (names or ids) limit a run.

  • Keys carry the note mcp-langfuse (read) in Langfuse's UI. Langfuse has no read-only key type: they can ingest too.

  • Coupling. This reproduces Langfuse's internal api_keys storage scheme, verified on 4.63.0. The script probes the columns it needs and stops if they moved; re-check src/provision/keys.ts on a Langfuse upgrade.

pg and bcryptjs are dev dependencies used only by the script. The published server keeps its two runtime dependencies.

Tools

Every data tool takes project: one of the configured names.

Tool

What it does

langfuse_auth_status

Every configured project and what its key resolves to; version, events-only mode

langfuse_list_traces

Traces newest first (root observations), filtered by name/user/session/tags/time

langfuse_get_trace

One trace as a tree, with usage, cost, errors and input/output

langfuse_get_session

A session's traces in order, each with its input/output

langfuse_list_observations

Search observations: type, level, model, latency, text in input/output, metadata

langfuse_get_observation

One observation in full; savePath writes it uncut to disk

langfuse_list_scores

Scores via /v3/scores, with what each is attached to

langfuse_query_metrics

Aggregates via /v2/metrics: count, cost, tokens, latency percentiles

langfuse_list_prompts

Managed prompts, versions and labels

langfuse_get_prompt

One prompt version in full

langfuse_request

Raw /api/public/* call, unshaped; GET-only unless writes are enabled

Traps worth knowing

  • Units differ. Observation rows report latency in seconds, but /v2/metrics reports latency and timeToFirstToken in milliseconds.

  • filter silently overrides plain query params on /v2/observations. This server always sends the JSON filter and never the plain params. Keep that in mind if you call the endpoint through langfuse_request.

  • A failed trace's root is usually still DEFAULT. Find failures with langfuse_list_observations and level: "ERROR", not by listing traces.

  • App roots. An SDK-marked root can carry a parentObservationId that points outside the trace. The tree treats it as a root.

  • Observation ids are scoped to a trace. Pass traceId to langfuse_get_observation whenever you have it.

  • High-cardinality fields (traceId, userId, sessionId, id) cannot be metric dimensions. Filter on them instead.

Quick start

pnpm install
pnpm build

Then copy .mcp.json.example to .mcp.json and fill it in.

Inspect the tools

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"cli","version":"0"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| node dist/cli.js 2>/dev/null | jq -r '.result.tools[]?.name'

Develop

pnpm dev            # tsdown --watch
pnpm test           # vitest, offline: fetch is mocked
pnpm typecheck
pnpm lint
pnpm format

To check against a real instance, start the built server with real keys, then call langfuse_auth_status first. It confirms the host, the project and events-only mode before you read any data.

Publish

pnpm dlx release-it       # bump, commit, tag
git push --follow-tags    # CI publishes to npm + GHCR from the tag

License

MIT

Available Tools

1 tool
langfuse_auth_statusLangfuse: Auth StatusA
Read-only

List the Langfuse projects this server can read — the names the other tools' project argument takes — and check each key against the project it is filed under. Keys are project-scoped, so this is the first thing to check when data looks wrong or missing. Also reports the Langfuse version, whether it runs in v4 events-only mode (no /traces or /sessions), whether writes are enabled, and, when something is not configured, exactly what to set. Makes one small API call per project, plus two.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=true; the description goes well beyond that. It discloses the cost profile ('one small API call per project, plus two'), the full set of things reported (version, v4 events-only mode with no /traces or /sessions, whether writes are enabled), and that remediation hints are returned when configuration is missing. That is exactly the extra context an agent needs to decide whether to call it and how to interpret the result.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core purpose and the 'check this first' trigger are front-loaded, and every clause (version, v4 mode, write flag, remediation, API call count) carries distinct information rather than restating the title. It is dense and dash-heavy, which makes it slightly harder to scan than a shorter version would be, but no sentence is waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description must describe the return shape, and it does: project names, per-key validation results, version, events-only mode, write-enabled flag, missing-config guidance, and request cost. For a zero-parameter read-only diagnostic tool, an agent has everything needed to call it and interpret the response.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is no input semantics to explain and the baseline applies. The description also usefully clarifies that the returned project names are what other tools' `project` argument accepts, which is value beyond this tool's own empty schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Uses a specific verb-plus-resource pair: it lists the Langfuse projects this server can read, validates each key against the project it is filed under, and reports configuration state. An agent immediately knows this is a discovery/diagnostic tool rather than a data-fetching one, and the description explicitly links its output ('the names the other tools' `project` argument takes') to how sibling tools are invoked.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit trigger: 'Keys are project-scoped, so this is the first thing to check when data looks wrong or missing.' That is clear situational guidance an agent can act on. It stops short of naming when not to call it or an alternative, but with no sibling tools there is little left to route between.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 1 tool updatev0.1.0
    • First observedlangfuse_auth_status

TDQS

A4.1/5.0

Scored across 1 tool

Disambiguation4/5

With only one tool exposed there is no possible confusion between tools, so selection is unambiguous. However, the description repeatedly refers to "the other tools' `project` argument," which do not exist in this set, so an agent may waste effort looking for tools that aren't there.

Naming Consistency4/5

The single name `langfuse_auth_status` is clear, prefixed, and uses consistent snake_case. There is no multi-tool set to verify a pattern against, so consistency can only be judged on readability.

Tool Count2/5

A Langfuse observability server exposing exactly one diagnostic/status tool is far too thin for the apparent scope (projects, traces, sessions, scores). The description itself implies a larger intended surface, making this a notable under-scoping rather than a deliberately minimal server.

Completeness1/5

There are no tools to actually read Langfuse data — no trace, session, observation, score, or project listing operations — despite the tool describing itself as the entry point to a broader set. An agent can check auth status and then hit a dead end for every real workflow.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    C
    quality
    A
    maintenance
    MCP server for Langfuse observability. Query traces, debug exceptions, analyze sessions, and manage prompts and datasets for your LLM applications.
    48
    3,600 PyPI
    112
    MIT
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents and developers to query Langfuse telemetry locally, fetching traces, sessions, observations, and exceptions, inspecting route decisions, and running metrics, prompt, dataset, and annotation-queue operations. Supports selective tool-group loading and a read-only mode for token-efficient debugging from Claude Code, Codex, Cursor, or any MCP client.
    -