Skip to content

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

Draft
wants to merge 7 commits into
base: develop
Choose a base branch
from

Conversation

betegon
Copy link
Member

@betegon betegon commented Aug 1, 2025

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:
Screenshot 2025-08-01 at 12 40 46

Needed to make attributeExtraction.ts <300 lines of code (requirement) so it's now split between sessionExtraction.ts, sessionExtraction.ts and resultExtraction.ts.

So changes explained so it's easier to review:

  • The only function this PR adds is extractPromptResultAttributes inside resultExtraction.ts.
  • It adds the prompt results as PII in piiFiltering.ts. Just add them to the set.
  • adds a else if (method === 'prompts/get') to execute the extractPromptResultAttributes function.
  • adds a test that checks we're capturing the results and updates the PII test to check PII result attributes are being removed if sending PII is not enabled.

@betegon betegon self-assigned this Aug 1, 2025
@betegon
Copy link
Member Author

betegon commented Aug 1, 2025

@sentry review

Comment on lines +36 to +38
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;
Copy link
Member

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.

Suggested change
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;
}

Comment on lines +25 to +27
if (typeof item !== 'object' || item === null) continue;

const contentItem = item as Record<string, unknown>;
Copy link
Member

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;
Copy link
Member

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;
Copy link
Member

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;
Copy link
Member

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).

Comment on lines +66 to +76
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MCP Server - capture prompt results from prompt function calls
2 participants