get_observations
Retrieve and filter LLM generations, spans, and events with timestamps, trace IDs, models, and log levels for analysis.
Instructions
Get LLM generations/spans with details and filtering.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| traceId | No | Filter by specific trace ID | |
| from | No | Start timestamp (ISO 8601) | |
| to | No | End timestamp (ISO 8601) | |
| limit | No | Maximum number of observations to return (default: 25) | |
| type | No | Filter by observation type | |
| model | No | Filter by model name (substring match) | |
| name | No | Filter by observation name (substring match) | |
| level | No | Filter by log level |
Input Schema (JSON Schema)
{
"properties": {
"from": {
"description": "Start timestamp (ISO 8601)",
"format": "date-time",
"type": "string"
},
"level": {
"description": "Filter by log level",
"enum": [
"DEBUG",
"DEFAULT",
"WARNING",
"ERROR"
],
"type": "string"
},
"limit": {
"description": "Maximum number of observations to return (default: 25)",
"maximum": 100,
"minimum": 1,
"type": "number"
},
"model": {
"description": "Filter by model name (substring match)",
"type": "string"
},
"name": {
"description": "Filter by observation name (substring match)",
"type": "string"
},
"to": {
"description": "End timestamp (ISO 8601)",
"format": "date-time",
"type": "string"
},
"traceId": {
"description": "Filter by specific trace ID",
"type": "string"
},
"type": {
"description": "Filter by observation type",
"enum": [
"GENERATION",
"SPAN",
"EVENT"
],
"type": "string"
}
},
"type": "object"
}
Implementation Reference
- src/tools/get-observations.ts:22-136 (handler)Core implementation of the get_observations tool handler. Fetches observations from Langfuse, processes them (including truncation of input/output), applies client-side filters, pagination, and returns JSON-formatted response.export async function getObservations( client: LangfuseAnalyticsClient, args: z.infer<typeof getObservationsSchema> ) { const response = await client.listObservations({ fromStartTime: args.from, toStartTime: args.to, limit: args.limit, page: args.page, name: args.name, userId: args.userId, type: args.type, traceId: args.traceId, level: args.level, }); let observations = response.data || []; // Helper function to truncate content const truncateContent = (content: any, maxLength: number): any => { if (!content) return content; if (typeof content === 'string') { return content.length > maxLength ? content.substring(0, maxLength) + '...[truncated]' : content; } if (typeof content === 'object') { const jsonStr = JSON.stringify(content); return jsonStr.length > maxLength ? jsonStr.substring(0, maxLength) + '...[truncated]' : content; } return content; }; // Process and filter observations with content size control let processedObservations = observations.map((obs: any) => { const baseObs: any = { id: obs.id, traceId: obs.traceId, type: obs.type || 'SPAN', name: obs.name || 'Unnamed observation', startTime: obs.startTime, endTime: obs.endTime, model: obs.model, usage: { input: obs.usage?.input || obs.inputTokens, output: obs.usage?.output || obs.outputTokens, total: obs.usage?.total || obs.totalTokens, }, cost: obs.calculatedTotalCost || obs.cost, level: obs.level || 'DEFAULT', }; // Only include input/output if requested, and truncate if necessary if (args.includeInputOutput) { baseObs.input = truncateContent(obs.input, args.truncateContent); baseObs.output = truncateContent(obs.output, args.truncateContent); baseObs.modelParameters = obs.modelParameters; } return baseObs; }); // Apply filters if (args.type) { processedObservations = processedObservations.filter((obs: any) => obs.type === args.type); } if (args.model) { processedObservations = processedObservations.filter((obs: any) => obs.model && obs.model.toLowerCase().includes(args.model!.toLowerCase()) ); } if (args.name) { processedObservations = processedObservations.filter((obs: any) => obs.name.toLowerCase().includes(args.name!.toLowerCase()) ); } if (args.level) { processedObservations = processedObservations.filter((obs: any) => obs.level === args.level); } if (args.minCost !== undefined) { processedObservations = processedObservations.filter((obs: any) => (obs.cost || 0) >= args.minCost! ); } if (args.maxCost !== undefined) { processedObservations = processedObservations.filter((obs: any) => (obs.cost || 0) <= args.maxCost! ); } // Apply pagination const startIndex = (args.page - 1) * args.limit; const paginatedObservations = processedObservations.slice(startIndex, startIndex + args.limit); const result: ObservationsResponse = { projectId: client.getProjectId(), observations: paginatedObservations, pagination: { page: args.page, limit: args.limit, total: processedObservations.length, }, }; return { content: [ { type: 'text' as const, text: JSON.stringify(result, null, 2), }, ], }; }
- src/tools/get-observations.ts:5-20 (schema)Zod schema defining input parameters for the get_observations tool, including filters, pagination, and content options.export const getObservationsSchema = z.object({ traceId: z.string().optional(), from: z.string().datetime().optional(), to: z.string().datetime().optional(), limit: z.number().min(1).max(50).default(10), // Reduced default limit page: z.number().min(1).default(1), type: z.enum(['GENERATION', 'SPAN', 'EVENT']).optional(), model: z.string().optional(), name: z.string().optional(), userId: z.string().optional(), level: z.enum(['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']).optional(), minCost: z.number().optional(), maxCost: z.number().optional(), includeInputOutput: z.boolean().default(false), // New option to include full content truncateContent: z.number().min(100).max(2000).default(500), // Max chars for input/output });
- src/index.ts:408-454 (registration)MCP tool registration in the server's listTools handler, providing the tool name, description, and JSON input schema.{ name: 'get_observations', description: 'Get LLM generations/spans with details and filtering.', inputSchema: { type: 'object', properties: { traceId: { type: 'string', description: 'Filter by specific trace ID', }, from: { type: 'string', format: 'date-time', description: 'Start timestamp (ISO 8601)', }, to: { type: 'string', format: 'date-time', description: 'End timestamp (ISO 8601)', }, limit: { type: 'number', minimum: 1, maximum: 100, description: 'Maximum number of observations to return (default: 25)', }, type: { type: 'string', enum: ['GENERATION', 'SPAN', 'EVENT'], description: 'Filter by observation type', }, model: { type: 'string', description: 'Filter by model name (substring match)', }, name: { type: 'string', description: 'Filter by observation name (substring match)', }, level: { type: 'string', enum: ['DEBUG', 'DEFAULT', 'WARNING', 'ERROR'], description: 'Filter by log level', }, }, }, },
- src/index.ts:1046-1049 (registration)Dispatch handler in the MCP server's callTool request handler that parses arguments using the schema and invokes the getObservations handler function.case 'get_observations': { const args = getObservationsSchema.parse(request.params.arguments); return await getObservations(this.client, args); }
- src/index.ts:61-61 (registration)Import statement bringing in the handler function and schema from the tool module.import { getObservations, getObservationsSchema } from './tools/get-observations.js';