Skip to main content
Glama
therealsachin

Langfuse MCP Server

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

TableJSON Schema
NameRequiredDescriptionDefault
traceIdNoFilter by specific trace ID
fromNoStart timestamp (ISO 8601)
toNoEnd timestamp (ISO 8601)
limitNoMaximum number of observations to return (default: 25)
typeNoFilter by observation type
modelNoFilter by model name (substring match)
nameNoFilter by observation name (substring match)
levelNoFilter by log level

Implementation Reference

  • 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),
          },
        ],
      };
    }
  • 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';

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