From 7bd48c14a58f4de91147175dffa2794e9353def5 Mon Sep 17 00:00:00 2001 From: naaa760 Date: Sun, 5 Oct 2025 13:57:02 +0530 Subject: [PATCH 01/10] new file with media detection and filtering logic --- .../integrations/mcp-server/mediaFiltering.ts | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 packages/core/src/integrations/mcp-server/mediaFiltering.ts diff --git a/packages/core/src/integrations/mcp-server/mediaFiltering.ts b/packages/core/src/integrations/mcp-server/mediaFiltering.ts new file mode 100644 index 000000000000..14768b879d7a --- /dev/null +++ b/packages/core/src/integrations/mcp-server/mediaFiltering.ts @@ -0,0 +1,123 @@ +import { getClient } from '../../currentScopes'; +import { isValidContentItem } from './validation'; + +const MEDIA_MIME_TYPES = new Set([ + 'image/jpeg', + 'image/jpg', + 'image/png', + 'image/gif', + 'image/webp', + 'image/svg+xml', + 'image/bmp', + 'image/tiff', + 'video/mp4', + 'video/avi', + 'video/mov', + 'video/wmv', + 'video/flv', + 'video/webm', + 'video/mkv', + 'audio/mp3', + 'audio/wav', + 'audio/ogg', + 'audio/mpeg', + 'audio/aac', + 'audio/flac', + 'application/pdf', + 'application/zip', + 'application/x-zip-compressed', +]); + +function isMediaContent(item: unknown): boolean { + if (!isValidContentItem(item)) { + return false; + } + + if (typeof item.type === 'string' && item.type === 'image') { + return true; + } + + if (typeof item.mimeType === 'string' && MEDIA_MIME_TYPES.has(item.mimeType.toLowerCase())) { + return true; + } + + if (typeof item.data === 'string' && item.data.length > 1000) { + const dataStart = item.data.substring(0, 50).toLowerCase(); + if (dataStart.includes('data:image/') || dataStart.includes('/9j/') || dataStart.includes('iVBORw0KGgo')) { + return true; + } + } + + return false; +} + +function recordDroppedMedia(reason: string, count: number = 1): void { + const client = getClient(); + if (client) { + client.recordDroppedEvent(reason as any, 'attachment', count); + } +} + +export function filterMediaFromContentItem(item: unknown): unknown | null { + if (!isValidContentItem(item)) { + return item; + } + + if (isMediaContent(item)) { + recordDroppedMedia('media_content_dropped'); + return null; + } + + if (Array.isArray(item.content)) { + const filteredContent = item.content + .map(contentItem => { + if (isMediaContent(contentItem)) { + recordDroppedMedia('media_content_dropped'); + return null; + } + return contentItem; + }) + .filter(contentItem => contentItem !== null); + + if (filteredContent.length === 0) { + return null; + } + + return { + ...item, + content: filteredContent, + }; + } + + if (isValidContentItem(item.content) && isMediaContent(item.content)) { + recordDroppedMedia('media_content_dropped'); + return null; + } + + return item; +} + +export function filterMediaFromContentArray(content: unknown[]): unknown[] { + return content + .map(item => filterMediaFromContentItem(item)) + .filter(item => item !== null); +} + +export function filterMediaFromAttributes(attributes: Record): Record { + const filteredAttributes = { ...attributes }; + + for (const [key, value] of Object.entries(filteredAttributes)) { + if (Array.isArray(value)) { + if (value.length > 0 && value.some(item => isValidContentItem(item))) { + const filtered = filterMediaFromContentArray(value); + if (filtered.length === 0) { + delete filteredAttributes[key]; + } else { + filteredAttributes[key] = filtered; + } + } + } + } + + return filteredAttributes; +} From 526687c4e63edc2a1573693f4a00a9d0526de240 Mon Sep 17 00:00:00 2001 From: naaa760 Date: Sun, 5 Oct 2025 13:57:39 +0530 Subject: [PATCH 02/10] updated to filter media from tool and prompt results --- .../mcp-server/resultExtraction.ts | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/core/src/integrations/mcp-server/resultExtraction.ts b/packages/core/src/integrations/mcp-server/resultExtraction.ts index 34dc2be9d09c..dddfd320b8e0 100644 --- a/packages/core/src/integrations/mcp-server/resultExtraction.ts +++ b/packages/core/src/integrations/mcp-server/resultExtraction.ts @@ -10,24 +10,22 @@ import { MCP_TOOL_RESULT_CONTENT_COUNT_ATTRIBUTE, MCP_TOOL_RESULT_IS_ERROR_ATTRIBUTE, } from './attributes'; +import { filterMediaFromContentArray, filterMediaFromContentItem } from './mediaFiltering'; import { isValidContentItem } from './validation'; -/** - * Build attributes for tool result content items - * @param content - Array of content items from tool result - * @returns Attributes extracted from each content item including type, text, mime type, URI, and resource info - */ function buildAllContentItemAttributes(content: unknown[]): Record { + const filteredContent = filterMediaFromContentArray(content); + const attributes: Record = { - [MCP_TOOL_RESULT_CONTENT_COUNT_ATTRIBUTE]: content.length, + [MCP_TOOL_RESULT_CONTENT_COUNT_ATTRIBUTE]: filteredContent.length, }; - for (const [i, item] of content.entries()) { + for (const [i, item] of filteredContent.entries()) { if (!isValidContentItem(item)) { continue; } - const prefix = content.length === 1 ? 'mcp.tool.result' : `mcp.tool.result.${i}`; + const prefix = filteredContent.length === 1 ? 'mcp.tool.result' : `mcp.tool.result.${i}`; const safeSet = (key: string, value: unknown): void => { if (typeof value === 'string') { @@ -93,19 +91,22 @@ export function extractPromptResultAttributes(result: unknown): Record filterMediaFromContentItem(message)) + .filter(message => message !== null); + + attributes[MCP_PROMPT_RESULT_MESSAGE_COUNT_ATTRIBUTE] = filteredMessages.length; - const messages = result.messages; - for (const [i, message] of messages.entries()) { + for (const [i, message] of filteredMessages.entries()) { if (!isValidContentItem(message)) { continue; } - const prefix = messages.length === 1 ? 'mcp.prompt.result' : `mcp.prompt.result.${i}`; + const prefix = filteredMessages.length === 1 ? 'mcp.prompt.result' : `mcp.prompt.result.${i}`; const safeSet = (key: string, value: unknown): void => { if (typeof value === 'string') { - const attrName = messages.length === 1 ? `${prefix}.message_${key}` : `${prefix}.${key}`; + const attrName = filteredMessages.length === 1 ? `${prefix}.message_${key}` : `${prefix}.${key}`; attributes[attrName] = value; } }; @@ -115,7 +116,7 @@ export function extractPromptResultAttributes(result: unknown): Record Date: Sun, 5 Oct 2025 13:58:53 +0530 Subject: [PATCH 03/10] aded documentation about media dropping behavior --- packages/core/src/integrations/mcp-server/index.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/core/src/integrations/mcp-server/index.ts b/packages/core/src/integrations/mcp-server/index.ts index a1eb8815805a..e3c10a96e595 100644 --- a/packages/core/src/integrations/mcp-server/index.ts +++ b/packages/core/src/integrations/mcp-server/index.ts @@ -16,6 +16,19 @@ const wrappedMcpServerInstances = new WeakSet(); * Compatible with versions `^1.9.0` of the `@modelcontextprotocol/sdk` package. * Automatically instruments transport methods and handler functions for comprehensive monitoring. * + * ## Media Content Handling + * + * The MCP server integration automatically filters out media content (images, videos, audio, etc.) + * from message attributes to prevent large media from breaking instrumentation. When media content + * is detected and dropped, it is tracked via client reports for monitoring and user awareness. + * + * The following content types are automatically filtered: + * - Images: JPEG, PNG, GIF, WebP, SVG, BMP, TIFF + * - Videos: MP4, AVI, MOV, WMV, FLV, WebM, MKV + * - Audio: MP3, WAV, OGG, MPEG, AAC, FLAC + * - Documents: PDF, ZIP files + * - Large base64 encoded data that appears to be media + * * @example * ```typescript * import * as Sentry from '@sentry/core'; From cccf345dac8d0680481f332da0ec0877a2ec6043 Mon Sep 17 00:00:00 2001 From: naaa760 Date: Tue, 7 Oct 2025 15:31:23 +0530 Subject: [PATCH 04/10] update --- packages/core/src/utils/ai/mediaFiltering.ts | 117 +++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 packages/core/src/utils/ai/mediaFiltering.ts diff --git a/packages/core/src/utils/ai/mediaFiltering.ts b/packages/core/src/utils/ai/mediaFiltering.ts new file mode 100644 index 000000000000..8d915a28e369 --- /dev/null +++ b/packages/core/src/utils/ai/mediaFiltering.ts @@ -0,0 +1,117 @@ +import { getClient } from '../../currentScopes'; + +const MEDIA_MIME_TYPES = new Set([ + 'image/jpeg', + 'image/jpg', + 'image/png', + 'image/gif', + 'image/webp', + 'image/svg+xml', + 'image/bmp', + 'image/tiff', + 'video/mp4', + 'video/avi', + 'video/mov', + 'video/wmv', + 'video/flv', + 'video/webm', + 'video/mkv', + 'audio/mp3', + 'audio/wav', + 'audio/ogg', + 'audio/mpeg', + 'audio/aac', + 'audio/flac', + 'application/pdf', + 'application/zip', + 'application/x-zip-compressed', +]); + +function isMediaContent(item: unknown): boolean { + if (typeof item !== 'object' || item === null) { + return false; + } + + const obj = item as Record; + + if (typeof obj.type === 'string' && (obj.type === 'image' || obj.type === 'image_url')) { + return true; + } + + if (typeof obj.mime_type === 'string' && MEDIA_MIME_TYPES.has(obj.mime_type.toLowerCase())) { + return true; + } + + if (typeof obj.mimeType === 'string' && MEDIA_MIME_TYPES.has(obj.mimeType.toLowerCase())) { + return true; + } + + if (typeof obj.data === 'string' && obj.data.length > 1000) { + const dataStart = obj.data.substring(0, 50).toLowerCase(); + if (dataStart.includes('data:image/') || dataStart.includes('/9j/') || dataStart.includes('ivborw0kggo')) { + return true; + } + } + + if (typeof obj.source === 'object' && obj.source !== null) { + const source = obj.source as Record; + if (typeof source.type === 'string' && source.type === 'base64' && typeof source.data === 'string') { + return true; + } + } + + return false; +} + +function recordDroppedMedia(count: number = 1): void { + const client = getClient(); + if (client) { + client.recordDroppedEvent('media_content_dropped' as any, 'attachment', count); + } +} + +export function filterMediaFromMessages(messages: unknown): unknown { + if (!Array.isArray(messages)) { + return messages; + } + + let droppedCount = 0; + + const filtered = messages.map(message => { + if (typeof message !== 'object' || message === null) { + return message; + } + + const msg = message as Record; + + if (Array.isArray(msg.content)) { + const filteredContent = msg.content.filter(item => { + if (isMediaContent(item)) { + droppedCount++; + return false; + } + return true; + }); + + if (filteredContent.length === 0) { + return { ...msg, content: '' }; + } + + return { ...msg, content: filteredContent }; + } + + if (isMediaContent(msg.content)) { + droppedCount++; + return { ...msg, content: '' }; + } + + return message; + }); + + if (droppedCount > 0) { + recordDroppedMedia(droppedCount); + } + + return filtered; +} + From dcbc9242077072b0b8395d284791a5504588caf9 Mon Sep 17 00:00:00 2001 From: naaa760 Date: Tue, 7 Oct 2025 15:31:44 +0530 Subject: [PATCH 05/10] update --- packages/core/src/utils/openai/index.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/core/src/utils/openai/index.ts b/packages/core/src/utils/openai/index.ts index 4ecfad625062..291ea2018a11 100644 --- a/packages/core/src/utils/openai/index.ts +++ b/packages/core/src/utils/openai/index.ts @@ -19,6 +19,7 @@ import { GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE, GEN_AI_SYSTEM_ATTRIBUTE, } from '../ai/gen-ai-attributes'; +import { filterMediaFromMessages } from '../ai/mediaFiltering'; import { OPENAI_INTEGRATION_NAME } from './constants'; import { instrumentStream } from './streaming'; import type { @@ -188,13 +189,14 @@ function addResponseAttributes(span: Span, result: unknown, recordOutputs?: bool } } -// Extract and record AI request inputs, if present. This is intentionally separate from response attributes. function addRequestAttributes(span: Span, params: Record): void { if ('messages' in params) { - span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(params.messages) }); + const filtered = filterMediaFromMessages(params.messages); + span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(filtered) }); } if ('input' in params) { - span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(params.input) }); + const filtered = filterMediaFromMessages(params.input); + span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(filtered) }); } } From 7932dba84eeda846feb8ca2a7f21d87a4c7aed5d Mon Sep 17 00:00:00 2001 From: naaa760 Date: Tue, 7 Oct 2025 15:32:40 +0530 Subject: [PATCH 06/10] update --- packages/core/src/utils/anthropic-ai/index.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/core/src/utils/anthropic-ai/index.ts b/packages/core/src/utils/anthropic-ai/index.ts index 8e77dd76b34e..0907205bdba5 100644 --- a/packages/core/src/utils/anthropic-ai/index.ts +++ b/packages/core/src/utils/anthropic-ai/index.ts @@ -23,6 +23,7 @@ import { GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE, GEN_AI_SYSTEM_ATTRIBUTE, } from '../ai/gen-ai-attributes'; +import { filterMediaFromMessages } from '../ai/mediaFiltering'; import { buildMethodPath, getFinalOperationName, getSpanOperation, setTokenUsageAttributes } from '../ai/utils'; import { handleCallbackErrors } from '../handleCallbackErrors'; import { instrumentAsyncIterableStream, instrumentMessageStream } from './streaming'; @@ -71,16 +72,14 @@ function extractRequestAttributes(args: unknown[], methodPath: string): Record): void { if ('messages' in params) { - span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(params.messages) }); + const filtered = filterMediaFromMessages(params.messages); + span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(filtered) }); } if ('input' in params) { - span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(params.input) }); + const filtered = filterMediaFromMessages(params.input); + span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(filtered) }); } if ('prompt' in params) { span.setAttributes({ [GEN_AI_PROMPT_ATTRIBUTE]: JSON.stringify(params.prompt) }); From f6f5dacccd465a593fb270946574071caca406ef Mon Sep 17 00:00:00 2001 From: naaa760 Date: Tue, 7 Oct 2025 15:32:58 +0530 Subject: [PATCH 07/10] update --- packages/core/src/utils/google-genai/index.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/core/src/utils/google-genai/index.ts b/packages/core/src/utils/google-genai/index.ts index 20e6e2a53606..038f688fdf13 100644 --- a/packages/core/src/utils/google-genai/index.ts +++ b/packages/core/src/utils/google-genai/index.ts @@ -22,6 +22,7 @@ import { GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE, } from '../ai/gen-ai-attributes'; +import { filterMediaFromMessages } from '../ai/mediaFiltering'; import { buildMethodPath, getFinalOperationName, getSpanOperation } from '../ai/utils'; import { handleCallbackErrors } from '../handleCallbackErrors'; import { CHAT_PATH, CHATS_CREATE_METHOD, GOOGLE_GENAI_SYSTEM_NAME } from './constants'; @@ -128,25 +129,20 @@ function extractRequestAttributes( return attributes; } -/** - * Add private request attributes to spans. - * This is only recorded if recordInputs is true. - * Handles different parameter formats for different Google GenAI methods. - */ function addPrivateRequestAttributes(span: Span, params: Record): void { - // For models.generateContent: ContentListUnion: Content | Content[] | PartUnion | PartUnion[] if ('contents' in params) { - span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(params.contents) }); + const filtered = filterMediaFromMessages(params.contents); + span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(filtered) }); } - // For chat.sendMessage: message can be string or Part[] if ('message' in params) { - span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(params.message) }); + const filtered = filterMediaFromMessages(params.message); + span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(filtered) }); } - // For chats.create: history contains the conversation history if ('history' in params) { - span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(params.history) }); + const filtered = filterMediaFromMessages(params.history); + span.setAttributes({ [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(filtered) }); } } From f965f45063a9a2275d6599f09b6b86d7a5ac6981 Mon Sep 17 00:00:00 2001 From: naaa760 Date: Tue, 7 Oct 2025 15:33:18 +0530 Subject: [PATCH 08/10] update --- packages/core/src/utils/vercel-ai/index.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/core/src/utils/vercel-ai/index.ts b/packages/core/src/utils/vercel-ai/index.ts index 912dcaee3bc4..54108aeef15c 100644 --- a/packages/core/src/utils/vercel-ai/index.ts +++ b/packages/core/src/utils/vercel-ai/index.ts @@ -2,6 +2,7 @@ import type { Client } from '../../client'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes'; import type { Event } from '../../types-hoist/event'; import type { Span, SpanAttributes, SpanAttributeValue, SpanJSON, SpanOrigin } from '../../types-hoist/span'; +import { filterMediaFromMessages } from '../ai/mediaFiltering'; import { spanToJSON } from '../spanUtils'; import { toolCallSpanMap } from './constants'; import type { TokenSummary } from './types'; @@ -115,7 +116,16 @@ function processEndedVercelAiSpan(span: SpanJSON): void { attributes[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE] + attributes[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]; } - // Rename AI SDK attributes to standardized gen_ai attributes + if (attributes[AI_PROMPT_MESSAGES_ATTRIBUTE]) { + try { + const messages = JSON.parse(String(attributes[AI_PROMPT_MESSAGES_ATTRIBUTE])); + const filtered = filterMediaFromMessages(messages); + attributes[AI_PROMPT_MESSAGES_ATTRIBUTE] = JSON.stringify(filtered); + } catch { + // noop + } + } + renameAttributeKey(attributes, AI_PROMPT_MESSAGES_ATTRIBUTE, 'gen_ai.request.messages'); renameAttributeKey(attributes, AI_RESPONSE_TEXT_ATTRIBUTE, 'gen_ai.response.text'); renameAttributeKey(attributes, AI_RESPONSE_TOOL_CALLS_ATTRIBUTE, 'gen_ai.response.tool_calls'); From 5bddc4c0fd1365a3e79f44449b6e871f43eacb00 Mon Sep 17 00:00:00 2001 From: naaa760 Date: Tue, 7 Oct 2025 15:33:41 +0530 Subject: [PATCH 09/10] reverted --- packages/core/src/integrations/mcp-server/index.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/core/src/integrations/mcp-server/index.ts b/packages/core/src/integrations/mcp-server/index.ts index e3c10a96e595..a1eb8815805a 100644 --- a/packages/core/src/integrations/mcp-server/index.ts +++ b/packages/core/src/integrations/mcp-server/index.ts @@ -16,19 +16,6 @@ const wrappedMcpServerInstances = new WeakSet(); * Compatible with versions `^1.9.0` of the `@modelcontextprotocol/sdk` package. * Automatically instruments transport methods and handler functions for comprehensive monitoring. * - * ## Media Content Handling - * - * The MCP server integration automatically filters out media content (images, videos, audio, etc.) - * from message attributes to prevent large media from breaking instrumentation. When media content - * is detected and dropped, it is tracked via client reports for monitoring and user awareness. - * - * The following content types are automatically filtered: - * - Images: JPEG, PNG, GIF, WebP, SVG, BMP, TIFF - * - Videos: MP4, AVI, MOV, WMV, FLV, WebM, MKV - * - Audio: MP3, WAV, OGG, MPEG, AAC, FLAC - * - Documents: PDF, ZIP files - * - Large base64 encoded data that appears to be media - * * @example * ```typescript * import * as Sentry from '@sentry/core'; From 28bfcba7a7f9082e80e6e87d5599e984cbf388ee Mon Sep 17 00:00:00 2001 From: naaa760 Date: Tue, 7 Oct 2025 15:34:04 +0530 Subject: [PATCH 10/10] update --- .../mcp-server/resultExtraction.ts | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/packages/core/src/integrations/mcp-server/resultExtraction.ts b/packages/core/src/integrations/mcp-server/resultExtraction.ts index dddfd320b8e0..edf4f9722ff9 100644 --- a/packages/core/src/integrations/mcp-server/resultExtraction.ts +++ b/packages/core/src/integrations/mcp-server/resultExtraction.ts @@ -10,22 +10,19 @@ import { MCP_TOOL_RESULT_CONTENT_COUNT_ATTRIBUTE, MCP_TOOL_RESULT_IS_ERROR_ATTRIBUTE, } from './attributes'; -import { filterMediaFromContentArray, filterMediaFromContentItem } from './mediaFiltering'; import { isValidContentItem } from './validation'; function buildAllContentItemAttributes(content: unknown[]): Record { - const filteredContent = filterMediaFromContentArray(content); - const attributes: Record = { - [MCP_TOOL_RESULT_CONTENT_COUNT_ATTRIBUTE]: filteredContent.length, + [MCP_TOOL_RESULT_CONTENT_COUNT_ATTRIBUTE]: content.length, }; - for (const [i, item] of filteredContent.entries()) { + for (const [i, item] of content.entries()) { if (!isValidContentItem(item)) { continue; } - const prefix = filteredContent.length === 1 ? 'mcp.tool.result' : `mcp.tool.result.${i}`; + const prefix = content.length === 1 ? 'mcp.tool.result' : `mcp.tool.result.${i}`; const safeSet = (key: string, value: unknown): void => { if (typeof value === 'string') { @@ -91,22 +88,19 @@ export function extractPromptResultAttributes(result: unknown): Record filterMediaFromContentItem(message)) - .filter(message => message !== null); - - attributes[MCP_PROMPT_RESULT_MESSAGE_COUNT_ATTRIBUTE] = filteredMessages.length; + attributes[MCP_PROMPT_RESULT_MESSAGE_COUNT_ATTRIBUTE] = result.messages.length; - for (const [i, message] of filteredMessages.entries()) { + const messages = result.messages; + for (const [i, message] of messages.entries()) { if (!isValidContentItem(message)) { continue; } - const prefix = filteredMessages.length === 1 ? 'mcp.prompt.result' : `mcp.prompt.result.${i}`; + const prefix = messages.length === 1 ? 'mcp.prompt.result' : `mcp.prompt.result.${i}`; const safeSet = (key: string, value: unknown): void => { if (typeof value === 'string') { - const attrName = filteredMessages.length === 1 ? `${prefix}.message_${key}` : `${prefix}.${key}`; + const attrName = messages.length === 1 ? `${prefix}.message_${key}` : `${prefix}.${key}`; attributes[attrName] = value; } };