Skip to main content
Glama
TECHNICAL_DIAGRAMS.md40.5 kB
# Technical Diagrams - Langfuse MCP Server Visual representations of key architectural concepts and data flows. --- ## 1. Request Processing Flow Shows how an MCP client request flows through the system: ``` ┌─────────────────────────────────────────────────────────────────────┐ │ MCP Client (e.g., Claude) │ └────────────────────────────┬────────────────────────────────────────┘ │ │ JSON-RPC 2.0 │ (over stdio) ▼ ┌────────────────────────────────────────┐ │ index.ts: LangfuseAnalyticsServer │ │ │ │ CallToolRequestSchema Handler │ │ { │ │ name: "get_traces", │ │ arguments: { ... } │ │ } │ └────────────────┬───────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Zod Input Validation │ │ getTracesSchema.parse(args) │ │ Throws ZodError if invalid │ └────────────────┬───────────────────────┘ │ ┌──────────────┴──────────────┐ │ Valid inputs Valid inputs ▼ ▼ ┌──────────────────┐ ┌──────────────────────┐ │ Tool Handler │ │ Error Handler │ │ getTraces() │ │ Return MCP error │ │ │ │ isError: true │ └────────┬─────────┘ └──────────────────────┘ │ ▼ ┌──────────────────────────────────┐ │ LangfuseAnalyticsClient │ │ .listTraces({...}) │ │ (HTTP wrapper + Basic Auth) │ └────────────────┬─────────────────┘ │ ▼ ┌──────────────────────────────────────┐ │ HTTP Fetch to Langfuse API │ │ GET /api/public/traces?params │ │ Headers: { Authorization: Basic } │ └────────────────┬─────────────────────┘ │ ┌────────────────┴────────────────┐ │ │ Success Error │ │ ▼ ▼ JSON Response throw new Error(...) │ │ └──────────────┬───────────────────┘ │ ▼ ┌──────────────────────────────────┐ │ Tool: Data Processing │ │ - Map API response │ │ - Apply filters/sorting │ │ - Build typed response │ └────────────────┬─────────────────┘ │ ▼ ┌──────────────────────────────────┐ │ MCP Response Format │ │ { │ │ content: [{ │ │ type: 'text', │ │ text: JSON string │ │ }], │ │ isError?: boolean │ │ } │ └────────────────┬─────────────────┘ │ ▼ (JSON-RPC response) ┌────────────────────────────────────┐ │ MCP Client Receives Result │ │ Parses JSON response │ │ Updates UI/state │ └────────────────────────────────────┘ ``` --- ## 2. Tool Categories & Dependencies Tool organization and what each depends on: ``` ┌──────────────────────────────────────────────────────────────┐ │ MCP Server (index.ts) │ │ Registers 18 tools & dispatches requests │ └──────────────────────────────────────────────────────────────┘ │ ┌────────────────┼────────────────┐ │ │ │ ▼ ▼ ▼ Config LangfuseClient Types (config.ts) (langfuse-client.ts) (types.ts) │ │ │ │ ┌─────────┴────────┐ │ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────────────────────────────────────┐ │ 18 Tool Handlers │ │ (src/tools/*.ts) │ │ │ │ ┌─────────────────────────────────┐ │ │ │ Category A: Core Analytics (6) │ │ │ │ ├─ list_projects │ │ │ │ ├─ project_overview │ │ │ │ ├─ usage_by_model │ │ │ │ ├─ usage_by_service │ │ │ │ ├─ top_expensive_traces │ │ │ │ └─ get_trace_detail │ │ │ │ │ │ │ │ Uses: getDailyMetrics(), │ │ │ │ listTraces(), │ │ │ │ getTrace() │ │ │ └─────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ Category B: Advanced (6) │ │ │ │ ├─ get_metrics │ │ │ │ ├─ get_traces │ │ │ │ ├─ get_observations │ │ │ │ ├─ get_cost_analysis │ │ │ │ ├─ get_daily_metrics │ │ │ │ └─ get_projects │ │ │ │ │ │ │ │ Uses: getMetrics(), │ │ │ │ listObservations(), │ │ │ │ getDailyMetrics() │ │ │ └─────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ Category C: Additional APIs (6) │ │ │ │ ├─ get_observation_detail │ │ │ │ ├─ get_health_status │ │ │ │ ├─ list_models │ │ │ │ ├─ get_model_detail │ │ │ │ ├─ list_prompts │ │ │ │ └─ get_prompt_detail │ │ │ │ │ │ │ │ Uses: getObservation(), │ │ │ │ getHealthStatus(), │ │ │ │ listModels(), etc. │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────┘ │ ┌────────────┴────────────┐ │ │ ▼ ▼ ┌──────────────────┐ ┌────────────────┐ │ Zod Schemas │ │ Type Inference│ │ (Validation) │ │ z.infer<> │ └──────────────────┘ └────────────────┘ ``` --- ## 3. API Endpoint Map Which tools use which Langfuse API endpoints: ``` LANGFUSE APIs Tools Using Them ───────────────────────────────────────────────────────────── /api/public/metrics get_metrics │ get_cost_analysis (fallback) │ usage_by_model (fallback) │ ├─ view: "traces" ├─ metrics: [...] ├─ dimensions: [...] └─ filters: [...] /api/public/metrics/daily project_overview │ usage_by_model │ get_cost_analysis │ get_daily_metrics │ ├─ Aggregated per day ├─ Includes usage breakdown └─ Field names: totalCost, countTraces, usage[] /api/public/traces get_traces │ top_expensive_traces │ count_active_users (example) │ ├─ Filtering by: name, userId, tags, timestamp ├─ Ordering by: timestamp, cost, name └─ Pagination: page, limit /api/public/traces/{id} get_trace_detail │ └─ Returns full trace with observations array /api/public/observations get_observations │ usage_by_service (future) │ ├─ Filtering by: type, model, name, level, traceId ├─ Time filtering └─ Pagination: page, limit /api/public/observations/{id} get_observation_detail │ └─ Returns single observation details /api/public/health get_health_status │ └─ Returns system health status /api/public/models list_models │ ├─ Pagination: page, limit └─ Returns available AI models /api/public/models/{id} get_model_detail │ └─ Returns single model details /api/public/prompts list_prompts │ ├─ Pagination: page, limit └─ Optional name filter /api/public/prompts/{name} get_prompt_detail │ ├─ Optional: version number ├─ Optional: label └─ Returns prompt template ``` --- ## 4. Data Flow: From API Response to MCP Response Example: `get_cost_analysis` tool multi-step aggregation ``` ┌─────────────────────────────────────────────────────────┐ │ Input: { from: "2024-01-01", to: "2024-01-31" } │ └────────────────────┬────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Step 1: Get Daily Data │ │ client.getDailyMetrics({...}) │ │ Returns: Array of daily summaries │ └────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Response: │ │ [ │ │ { │ │ date: "2024-01-01", │ │ totalCost: 10.50, │ │ countTraces: 150, │ │ usage: [ │ │ { │ │ model: "gpt-4", │ │ totalCost: 8.00, │ │ totalUsage: 5000, │ │ countObservations: 100 │ │ }, │ │ ... │ │ ] │ │ }, │ │ ... │ │ ] │ └────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Step 2: Filter by Date Range │ │ Remove dates outside [from, to] │ │ Keeps: 31 days of data │ └────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Step 3: Calculate Total Cost │ │ Sum all daily costs │ │ Total: $321.45 │ └────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Step 4: Aggregate by Model │ │ For each day's usage data: │ │ - Accumulate cost per model │ │ - Accumulate tokens per model │ │ - Count observations per model │ │ Result: │ │ { │ │ "gpt-4": { │ │ cost: 250.00, │ │ tokens: 500000, │ │ observations: 3100, │ │ percentage: 77.8% │ │ }, │ │ "gpt-3.5-turbo": { │ │ cost: 71.45, │ │ tokens: 200000, │ │ observations: 2200, │ │ percentage: 22.2% │ │ } │ │ } │ └────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Step 5: Get User Breakdown (opt) │ │ Call getMetrics() separately │ │ Dimension by userId │ │ Result: Array of user costs │ └────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Step 6: Build Response Object │ │ { │ │ projectId: "abc12345", │ │ from: "2024-01-01", │ │ to: "2024-01-31", │ │ totalCost: 321.45, │ │ breakdown: { │ │ byModel: [{model, cost, ...}], │ │ byUser: [{userId, cost, ...}], │ │ byDay: [{date, cost, ...}] │ │ } │ │ } │ └────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Step 7: MCP Response Wrapper │ │ { │ │ content: [{ │ │ type: 'text', │ │ text: JSON.stringify(response) │ │ }] │ │ } │ └────────────┬───────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Return to MCP Client │ │ (JSON-RPC response) │ └────────────────────────────────────────┘ ``` --- ## 5. Authentication Flow How API keys are converted to HTTP headers: ``` ┌──────────────────────────────────────┐ │ Environment Variables │ │ │ │ LANGFUSE_PUBLIC_KEY=pk-lf-xxx │ │ LANGFUSE_SECRET_KEY=sk-lf-xxx │ └────────────────┬─────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ config.ts: getProjectConfig() │ │ - Read from process.env │ │ - Validate required fields │ │ - Return typed config object │ └────────────────┬───────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ index.ts: Initialize LangfuseClient │ │ new LangfuseAnalyticsClient(config) │ └────────────────┬───────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ langfuse-client.ts: Each API call │ │ │ │ const credentials = │ │ `${config.publicKey}:${config.secretKey}` │ │ │ const encoded = Buffer.from( │ │ credentials │ │ ).toString('base64') │ │ │ │ Result: "cGstbGYtMDEyMzpzay1sZi1hYmMx" │ │ │ └────────────────┬───────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ HTTP Header Construction │ │ │ │ Authorization: Basic + encoded │ │ Authorization: Basic cGstbGYtMDEyMzpz... │ └────────────────┬───────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ fetch(url, { │ │ headers: { Authorization: authHeader}│ │ }) │ └────────────────┬───────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Langfuse API │ │ - Decodes Authorization header │ │ - Verifies credentials │ │ - Returns data for authenticated user │ └────────────────────────────────────────┘ ``` --- ## 6. Error Handling Architecture How errors flow through the system: ``` Error Source │ ┌──────────────┼──────────────┐ │ │ │ ▼ ▼ ▼ Validation API Call Tool Logic Error Error Error │ │ │ │ │ Network, │ │ Zod.parse │ Auth, │ Parsing, │ failure │ 404, 500 │ Type error │ │ │ └──────────────┼──────────────┘ │ ▼ ┌──────────────────────────────┐ │ Try-Catch Block │ │ In Tool Handler │ └────────────┬─────────────────┘ │ ┌────────────────┴────────────────┐ │ │ ▼ ▼ Synchronous Asynchronous Errors (Zod) Errors (API) │ │ │ ▼ │ Await fetch + response │ Catch network errors │ │ │ ├─ Response not ok? │ │ Throw: "API error: 404" │ │ │ └─ Throw other error │ from client method │ └──────────────────────────┬────────────────┐ │ │ ▼ ▼ Error object error instanceof? extraction Error → message │ │ │ → "Unknown" │ │ └────────┬───────┘ │ ▼ ┌─────────────────────┐ │ Build Error Response │ │ { │ │ content: [{ │ │ type: 'text', │ │ text: JSON │ │ }], │ │ isError: true │ │ } │ └────────┬────────────┘ │ ▼ ┌──────────────────────┐ │ Return to MCP Client │ │ Client logs error │ │ User informed │ └──────────────────────┘ ``` --- ## 7. Tool Registration & Dispatch Mechanism How tools are registered and called: ``` ┌──────────────────────────────────────────────────────────┐ │ Server Initialization: Constructor │ │ │ │ 1. Create MCP Server instance │ │ 2. Initialize Langfuse client │ │ 3. Call setupHandlers() │ └────────────────┬─────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ setupHandlers(): Register 2 endpoints │ │ │ │ - ListToolsRequestSchema handler │ │ - CallToolRequestSchema handler │ └────────────┬───────────────────────────────┘ │ │ ListToolsRequestSchema ▼ ┌────────────────────────────────────────────┐ │ Return Tool Registry │ │ (18 tools) │ │ [ │ │ { │ │ name: "list_projects", │ │ description: "...", │ │ inputSchema: { type: 'object', ... } │ │ }, │ │ { │ │ name: "project_overview", │ │ description: "...", │ │ inputSchema: { ... } │ │ }, │ │ ... 16 more tools │ │ ] │ └────────────┬───────────────────────────────┘ │ │ Client discovers tools │ │ CallToolRequestSchema ▼ ┌────────────────────────────────────────────┐ │ Tool Dispatch Switch Statement │ │ │ │ switch(request.params.name) { │ │ case 'list_projects': │ │ args = schema.parse(arguments) │ │ return listProjects(client) │ │ │ │ case 'project_overview': │ │ args = schema.parse(arguments) │ │ return projectOverview(client, args) │ │ │ │ ... (16 more cases) │ │ │ │ default: │ │ throw Error("Unknown tool") │ │ } │ └────────────┬───────────────────────────────┘ │ ┌────────┴────────┐ ▼ ▼ Zod Parse Tool Handler Input Execution │ │ │ Valid ▼ │ 1. Prepare params │ 2. Call client API │ 3. Process response │ 4. Build MCP response │ └────────┬────────┘ │ ▼ ┌────────────────────────────────────────┐ │ MCP Response │ │ { │ │ content: [{ type, text }], │ │ isError?: boolean │ │ } │ └────────────────────────────────────────┘ ``` --- ## 8. Dependency Injection Pattern How the Langfuse client is passed through the system: ``` ┌──────────────────────────────────────────────┐ │ index.ts: Constructor │ │ │ │ const config = getProjectConfig() │ │ this.client = new LangfuseAnalyticsClient( │ │ config │ │ ) │ └────────────┬─────────────────────────────────┘ │ │ Stored as instance property │ this.client │ ▼ ┌────────────────────────────────────┐ │ setupHandlers() │ │ │ │ For each tool: │ │ return toolHandler( │ │ this.client, ← injected │ │ parsedArgs │ │ ) │ │ │ └────────────┬───────────────────────┘ │ ┌────────────┴────────────────────────────┐ │ Tool Handler (tools/*.ts) │ │ │ │ export async function toolHandler( │ │ client: LangfuseAnalyticsClient, │ │ args: ArgsType │ │ ) { │ │ const response = │ │ await client.method(...) ← use │ │ } │ └────────────┬────────────────────────────┘ │ │ Client has all credentials │ and API methods │ ▼ ┌────────────────────────────────────────┐ │ LangfuseAnalyticsClient │ │ - this.config (publicKey, secretKey) │ │ - getMetrics(), listTraces(), etc. │ └────────────────────────────────────────┘ Benefits: - Tools don't create own client - Single client instance reused - Credentials centralized - Easy to mock for testing - Follows dependency injection pattern ``` --- ## 9. Type System Validation Flow How Zod validation ensures type safety: ``` ┌────────────────────────────────────┐ │ Tool File (e.g., get-traces.ts) │ │ │ │ export const getTracesSchema = │ │ z.object({ │ │ from: z.string().datetime(), │ │ to: z.string().datetime(), │ │ limit: z.number() │ │ .min(1).max(100) │ │ .default(25), │ │ orderBy: z.enum([ │ │ 'timestamp', │ │ 'totalCost' │ │ ]) │ │ .default('timestamp'), │ │ }) │ │ │ │ export async function getTraces( │ │ client, │ │ args: z.infer< │ │ typeof getTracesSchema │ │ > │ │ ) { ... } │ └────────────┬───────────────────────┘ │ │ Schema definition │ ▼ ┌────────────────────────────────┐ │ MCP Client sends: │ │ { │ │ from: "2024-01-01T...", │ │ to: "2024-01-31T...", │ │ limit: 10, │ │ orderBy: "totalCost" │ │ } │ └────────────┬────────────────────┘ │ ▼ ┌────────────────────────────────┐ │ index.ts: CallToolRequest │ │ │ │ const args = │ │ getTracesSchema.parse( │ │ request.params.arguments │ │ ) │ └────────────┬────────────────────┘ │ ┌────────┴────────┐ ▼ ▼ Valid Invalid args args │ │ ▼ ▼ Continue throw ZodError Execution │ │ Error details: │ - field name │ - expected type │ - actual value │ ▼ ┌─────────────────────┐ │ Catch ZodError │ │ Return error │ │ response to client │ └─────────────────────┘ Type Safety Guarantees: - args.from: definitely a datetime string - args.limit: definitely a number, 1-100 - args.orderBy: definitely one of enum values - TypeScript knows exact types (z.infer) - Runtime validation ensures contract ``` --- ## 10. Multi-Project Architecture (Future) How the server could support multiple projects: ``` Current (Single Project): ┌──────────────────────────────────────┐ │ MCP Server Instance │ │ ├─ Config: LANGFUSE_PUBLIC_KEY │ │ ├─ Config: LANGFUSE_SECRET_KEY │ │ └─ LangfuseAnalyticsClient │ │ └─ One project only │ └──────────────────────────────────────┘ Future (Multi-Project): ┌────────────────────────────────────────┐ │ MCP Server Instance │ │ ├─ Config: {projects: [...]} │ │ ├─ ProjectRegistry │ │ │ ├─ Project A: config + client │ │ │ ├─ Project B: config + client │ │ │ └─ Project C: config + client │ │ │ │ │ └─ All Tools: │ │ Tools take projectId parameter │ │ Lookup correct client in registry │ │ Execute with project-specific auth │ └────────────────────────────────────────┘ Tool Signature Change: Before: getTraces(client, args) After: getTraces(client, args, projectId) → client = registry.getClient(projectId) → proceed as before Benefits: - Single server for multiple projects - Same credentials system - No code duplication - Cleaner Claude Desktop config ```

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/therealsachin/langfuse-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server