-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(core): MCP Server - Capture prompt results from prompt function calls #17284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
@sentry review |
packages/core/src/integrations/mcp-server/attributeExtraction.ts
Outdated
Show resolved
Hide resolved
packages/core/src/integrations/mcp-server/attributeExtraction.ts
Outdated
Show resolved
Hide resolved
if (typeof source.name === 'string') partyInfo.name = source.name; | ||
if (typeof source.title === 'string') partyInfo.title = source.title; | ||
if (typeof source.version === 'string') partyInfo.version = source.version; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l: I prefer if avoid inline conditionals as it makes future diffs easier to read. With the version below, intent when git blaming when we change the condition vs. changing the setter is more clear
Ditto everywhere else in the code that this happens.
if (typeof source.name === 'string') partyInfo.name = source.name; | |
if (typeof source.title === 'string') partyInfo.title = source.title; | |
if (typeof source.version === 'string') partyInfo.version = source.version; | |
if (typeof source.name === 'string') { | |
partyInfo.name = source.name; | |
} | |
if (typeof source.title === 'string') { | |
partyInfo.title = source.title; | |
} | |
if (typeof source.version === 'string') { | |
partyInfo.version = source.version; | |
} |
if (typeof item !== 'object' || item === null) continue; | ||
|
||
const contentItem = item as Record<string, unknown>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l: generally I prefer if we use functions to type narrow instead of casts, but feel free not to change.
function isValidContentItem(item: unknown): item is Record<string, unknown> {
return item != null && typeof item === 'object';
}
if (!isValidContentItem(item)) {
return;
}
// continue with rest of code.
|
||
if (typeof contentItem.text === 'string') { | ||
const text = contentItem.text; | ||
const maxLength = 500; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l: Let's extract maxLength
out of the function body and give it a bit more of a descriptive name.
safeSet('name', contentItem.name); | ||
|
||
if (typeof contentItem.text === 'string') { | ||
const text = contentItem.text; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
l: if we're going to do this, I would prefer if we just destructure it above, and use it in here.
const { text } = contentItem;
if (typeof contentItem.text === 'string') { | ||
const text = contentItem.text; | ||
const maxLength = 500; | ||
attributes[`${prefix}.content`] = text.length > maxLength ? `${text.slice(0, maxLength - 3)}...` : text; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m: We have a truncate
helper in the sdk that does this (though it doesn't do maxLength - 3
😬 - we need to fix that).
let attributes: Record<string, string | number | boolean> = {}; | ||
if (typeof result !== 'object' || result === null) return attributes; | ||
|
||
const resultObj = result as Record<string, unknown>; | ||
if (typeof resultObj.isError === 'boolean') { | ||
attributes[MCP_TOOL_RESULT_IS_ERROR_ATTRIBUTE] = resultObj.isError; | ||
} | ||
if (Array.isArray(resultObj.content)) { | ||
attributes = { ...attributes, ...buildAllContentItemAttributes(resultObj.content) }; | ||
} | ||
return attributes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let attributes: Record<string, string | number | boolean> = {}; | |
if (typeof result !== 'object' || result === null) return attributes; | |
const resultObj = result as Record<string, unknown>; | |
if (typeof resultObj.isError === 'boolean') { | |
attributes[MCP_TOOL_RESULT_IS_ERROR_ATTRIBUTE] = resultObj.isError; | |
} | |
if (Array.isArray(resultObj.content)) { | |
attributes = { ...attributes, ...buildAllContentItemAttributes(resultObj.content) }; | |
} | |
return attributes; | |
if (!result || typeof result !== 'object') { | |
return {}; | |
}; | |
const attributes = Array.isArray(resultObj.content) ? buildAllContentItemAttributes(resultObj.content) : {} | |
if (typeof result.isError === 'boolean') { | |
attributes[MCP_TOOL_RESULT_IS_ERROR_ATTRIBUTE] = result.isError; | |
} | |
return attributes; |
m: Generally I'd like us to follow code paths that look like the same, where we avoid spread operators (or similar obj/array mutations) until they are absolutely necessary. In this case, there's no need to spread buildAllContentItemAttributes
into a new object.
closes #17283
includes these attributes for
mcp.server
spans:mcp.prompt.result.description
mcp.prompt.result.message_content
mcp.prompt.result.message_role
mcp.prompt.result.message_count
Example:

Needed to make
attributeExtraction.ts
<300 lines of code (requirement) so it's now split betweensessionExtraction.ts
,sessionExtraction.ts
andresultExtraction.ts
.So changes explained so it's easier to review:
extractPromptResultAttributes
insideresultExtraction.ts
.piiFiltering.ts
. Just add them to theset
.else if (method === 'prompts/get')
to execute theextractPromptResultAttributes
function.