Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/mcp-core/src/api-client/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,9 @@ export const IssueSchema = z
title: z.string().nullable().optional(),
location: z.string().nullable().optional(),
value: z.string().nullable().optional(),
// Feedback issues carry who submitted them.
name: z.string().nullable().optional(),
contact_email: z.string().nullable().optional(),
})
.optional(),
seerFixabilityScore: z.number().nullable().optional(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,16 @@
import { inlineCode } from "@sentry/toolkit-core/markdown";
import { isTopLevelToolName } from "../../tools/surfaces";
import {
formatToolCall,
isToolAvailableInSession,
} from "./tool-call-formatting";

const MAX_SUGGESTED_ACTIONS = 3;
MAX_SUGGESTED_ACTIONS,
type SuggestedToolAction,
buildSuggestedToolAction,
} from "./suggested-actions";
import { formatToolCall } from "./tool-call-formatting";

export interface AIConversationReference {
conversationId: string;
spanId?: string;
}

type SuggestedActionValue =
| string
| number
| boolean
| null
| SuggestedActionValue[]
| { [key: string]: SuggestedActionValue };

/** A concrete, session-callable MCP follow-up action. */
interface SuggestedToolAction {
type: "tool_call";
toolName: string;
arguments: Record<string, SuggestedActionValue>;
reason: string;
}

/** Builds transcript actions that match the session's direct or catalog surface. */
export function getAIConversationSuggestedActions({
organizationSlug,
Expand All @@ -42,54 +25,22 @@ export function getAIConversationSuggestedActions({
availableToolNames?: ReadonlySet<string>;
directToolNames?: ReadonlySet<string>;
}): SuggestedToolAction[] {
if (
aiConversations.length === 0 ||
!isToolAvailableInSession(
"get_agent_conversation_details",
availableToolNames,
return aiConversations
.slice(0, MAX_SUGGESTED_ACTIONS)
.map((conversation) =>
buildSuggestedToolAction({
toolName: "get_agent_conversation_details",
arguments: {
organizationSlug,
conversationId: conversation.conversationId,
},
reason: "Fetch the full transcript for this agent conversation.",
experimentalMode,
availableToolNames,
directToolNames,
}),
)
) {
return [];
}

const targetIsDirect = directToolNames
? directToolNames.has("get_agent_conversation_details")
: isTopLevelToolName("get_agent_conversation_details", experimentalMode);
const executeToolIsDirect = directToolNames
? directToolNames.has("execute_sentry_tool")
: isTopLevelToolName("execute_sentry_tool", experimentalMode);
const useCatalogGateway =
!targetIsDirect &&
executeToolIsDirect &&
isToolAvailableInSession("execute_sentry_tool", availableToolNames);

if (!targetIsDirect && !useCatalogGateway) {
return [];
}

return aiConversations.slice(0, MAX_SUGGESTED_ACTIONS).map((conversation) => {
const targetArguments = {
organizationSlug,
conversationId: conversation.conversationId,
};

return targetIsDirect
? {
type: "tool_call",
toolName: "get_agent_conversation_details",
arguments: targetArguments,
reason: "Fetch the full transcript for this agent conversation.",
}
: {
type: "tool_call",
toolName: "execute_sentry_tool",
arguments: {
name: "get_agent_conversation_details",
arguments: targetArguments,
},
reason: "Fetch the full transcript for this agent conversation.",
};
});
.filter((action): action is SuggestedToolAction => action !== null);
}

/** Returns markdown instructions that mirror the callable structured actions. */
Expand Down
77 changes: 77 additions & 0 deletions packages/mcp-core/src/internal/tool-helpers/suggested-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { z } from "zod";
import { isTopLevelToolName } from "../../tools/surfaces";
import { isToolAvailableInSession } from "./tool-call-formatting";

/** See docs/contributing/tool-responses.md#suggested-actions. */
export const MAX_SUGGESTED_ACTIONS = 3;

export type SuggestedActionValue =
| string
| number
| boolean
| null
| SuggestedActionValue[]
| { [key: string]: SuggestedActionValue };

/** A concrete, session-callable MCP follow-up action. */
export interface SuggestedToolAction {
type: "tool_call";
toolName: string;
arguments: Record<string, SuggestedActionValue>;
reason: string;
}

export const suggestedToolActionSchema = z.object({
type: z.literal("tool_call"),
toolName: z.string(),
arguments: z.record(z.string(), z.unknown()),
reason: z.string(),
});

/**
* Builds a follow-up call that matches the session's direct or catalog
* surface, or null when the target tool cannot be called in this session.
*/
export function buildSuggestedToolAction({
toolName,
arguments: targetArguments,
reason,
experimentalMode,
availableToolNames,
directToolNames,
}: {
toolName: string;
arguments: Record<string, SuggestedActionValue>;
reason: string;
experimentalMode: boolean;
availableToolNames?: ReadonlySet<string>;
directToolNames?: ReadonlySet<string>;
}): SuggestedToolAction | null {
if (!isToolAvailableInSession(toolName, availableToolNames)) {
return null;
}

const targetIsDirect = directToolNames
? directToolNames.has(toolName)
: isTopLevelToolName(toolName, experimentalMode);
if (targetIsDirect) {
return { type: "tool_call", toolName, arguments: targetArguments, reason };
}

const executeToolIsDirect = directToolNames
? directToolNames.has("execute_sentry_tool")
: isTopLevelToolName("execute_sentry_tool", experimentalMode);
if (
!executeToolIsDirect ||
!isToolAvailableInSession("execute_sentry_tool", availableToolNames)
) {
return null;
}

return {
type: "tool_call",
toolName: "execute_sentry_tool",
arguments: { name: toolName, arguments: targetArguments },
reason,
};
}
175 changes: 175 additions & 0 deletions packages/mcp-core/src/toolDefinitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -7804,6 +7804,181 @@
},
"required": ["organizationSlug"]
},
"outputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string"
},
"explanation": {
"type": ["string", "null"]
},
"executedSearch": {
"type": "object",
"properties": {
"query": {
"type": "string"
},
"sort": {
"type": "string",
"enum": ["date", "freq", "new", "user", "recommended"]
},
"period": {
"type": "string"
},
"projectSlugOrId": {
"type": ["string", "null"]
}
},
"required": ["query", "sort", "period", "projectSlugOrId"],
"additionalProperties": false
},
"url": {
"type": "string",
"description": "Opens these results in Sentry."
},
"presentationHint": {
"type": "string"
},
"notes": {
"type": "array",
"items": {
"type": "string"
}
},
"issues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"shortId": {
"type": "string"
},
"title": {
"type": "string"
},
"url": {
"type": "string"
},
"status": {
"type": "string"
},
"substatus": {
"type": ["string", "null"]
},
"issueCategory": {
"type": ["string", "null"]
},
"occurrences": {
"type": "number"
},
"usersImpacted": {
"type": "number"
},
"assignedTo": {
"type": ["string", "null"]
},
"assigneeEmail": {
"type": ["string", "null"]
},
"reporter": {
"anyOf": [
{
"type": "object",
"properties": {
"name": {
"type": ["string", "null"]
},
"email": {
"type": ["string", "null"]
}
},
"required": ["name", "email"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Who submitted a feedback issue. Null for other issues."
},
"firstSeen": {
"type": ["string", "null"]
},
"lastSeen": {
"type": ["string", "null"]
},
"culprit": {
"type": ["string", "null"]
},
"seerActionability": {
"type": ["string", "null"]
},
"projectSlug": {
"type": ["string", "null"]
}
},
"required": [
"shortId",
"title",
"url",
"status",
"substatus",
"issueCategory",
"occurrences",
"usersImpacted",
"assignedTo",
"assigneeEmail",
"reporter",
"firstSeen",
"lastSeen",
"culprit",
"seerActionability",
"projectSlug"
],
"additionalProperties": false
}
},
"suggestedActions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "tool_call"
},
"toolName": {
"type": "string"
},
"arguments": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"reason": {
"type": "string"
}
},
"required": ["type", "toolName", "arguments", "reason"],
"additionalProperties": false
}
}
},
"required": [
"query",
"explanation",
"executedSearch",
"url",
"presentationHint",
"notes",
"issues",
"suggestedActions"
],
"additionalProperties": false
},
"requiredScopes": ["event:read"],
"skills": ["inspect", "triage", "seer"],
"surface": "direct"
Expand Down
Loading
Loading