diff --git a/packages/mcp-core/src/skillDefinitions.json b/packages/mcp-core/src/skillDefinitions.json index 96eb42533..961b3439e 100644 --- a/packages/mcp-core/src/skillDefinitions.json +++ b/packages/mcp-core/src/skillDefinitions.json @@ -49,7 +49,7 @@ }, { "name": "search_issues", - "description": "Search for grouped issues/problems in Sentry - returns a LIST of issues, NOT counts or aggregations.\n\nUses AI to translate natural language queries into Sentry issue search syntax.\nReturns grouped issues with metadata like title, status, and user count.\n\nUSE THIS TOOL WHEN USERS WANT:\n- A LIST of issues: 'show me issues', 'what problems do we have'\n- Filtered issue lists: 'unresolved issues', 'critical bugs'\n- Issues by impact: 'errors affecting more than 100 users'\n- Issues by assignment: 'issues assigned to me'\n\nDO NOT USE FOR COUNTS/AGGREGATIONS:\n- 'how many errors' → use search_events\n- 'count of issues' → use search_events\n- 'total number of errors today' → use search_events\n- 'sum/average/statistics' → use search_events\n\nALSO DO NOT USE FOR:\n- Individual error events with timestamps → use search_events\n- Details about a specific issue ID → use get_issue_details\n\nREMEMBER: This tool returns a LIST of issues, not counts or statistics!\n\n\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='critical bugs from last week')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='unhandled errors affecting 100+ users')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='issues assigned to me')\n\n\n\n- If the user passes a parameter in the form of name/otherName, it's likely in the format of /.\n- Parse org/project notation directly without calling find_organizations or find_projects.\n- The projectSlugOrId parameter accepts both project slugs (e.g., 'my-project') and numeric IDs (e.g., '123456').\n", + "description": "Search for grouped issues/problems in Sentry - returns a LIST of issues, NOT counts or aggregations.\n\nUses AI to translate natural language queries into Sentry issue search syntax.\nReturns grouped issues with metadata like title, status, and user count.\n\nUSE THIS TOOL WHEN USERS WANT:\n- A LIST of issues: 'show me issues', 'what problems do we have'\n- Filtered issue lists: 'unresolved issues', 'critical bugs'\n- Issues by impact: 'errors affecting more than 100 users'\n- Issues by assignment: 'issues assigned to me'\n- User feedback: 'show me user feedback', 'feedback from last week'\n\nDO NOT USE FOR COUNTS/AGGREGATIONS:\n- 'how many errors' → use search_events\n- 'count of issues' → use search_events\n- 'total number of errors today' → use search_events\n- 'sum/average/statistics' → use search_events\n\nALSO DO NOT USE FOR:\n- Individual error events with timestamps → use search_events\n- Details about a specific issue ID → use get_issue_details\n\nREMEMBER: This tool returns a LIST of issues, not counts or statistics!\n\n\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='critical bugs from last week')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='unhandled errors affecting 100+ users')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='issues assigned to me')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='user feedback from production')\n\n\n\n- If the user passes a parameter in the form of name/otherName, it's likely in the format of /.\n- Parse org/project notation directly without calling find_organizations or find_projects.\n- The projectSlugOrId parameter accepts both project slugs (e.g., 'my-project') and numeric IDs (e.g., '123456').\n", "requiredScopes": ["event:read"] }, { @@ -94,7 +94,7 @@ }, { "name": "search_issues", - "description": "Search for grouped issues/problems in Sentry - returns a LIST of issues, NOT counts or aggregations.\n\nUses AI to translate natural language queries into Sentry issue search syntax.\nReturns grouped issues with metadata like title, status, and user count.\n\nUSE THIS TOOL WHEN USERS WANT:\n- A LIST of issues: 'show me issues', 'what problems do we have'\n- Filtered issue lists: 'unresolved issues', 'critical bugs'\n- Issues by impact: 'errors affecting more than 100 users'\n- Issues by assignment: 'issues assigned to me'\n\nDO NOT USE FOR COUNTS/AGGREGATIONS:\n- 'how many errors' → use search_events\n- 'count of issues' → use search_events\n- 'total number of errors today' → use search_events\n- 'sum/average/statistics' → use search_events\n\nALSO DO NOT USE FOR:\n- Individual error events with timestamps → use search_events\n- Details about a specific issue ID → use get_issue_details\n\nREMEMBER: This tool returns a LIST of issues, not counts or statistics!\n\n\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='critical bugs from last week')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='unhandled errors affecting 100+ users')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='issues assigned to me')\n\n\n\n- If the user passes a parameter in the form of name/otherName, it's likely in the format of /.\n- Parse org/project notation directly without calling find_organizations or find_projects.\n- The projectSlugOrId parameter accepts both project slugs (e.g., 'my-project') and numeric IDs (e.g., '123456').\n", + "description": "Search for grouped issues/problems in Sentry - returns a LIST of issues, NOT counts or aggregations.\n\nUses AI to translate natural language queries into Sentry issue search syntax.\nReturns grouped issues with metadata like title, status, and user count.\n\nUSE THIS TOOL WHEN USERS WANT:\n- A LIST of issues: 'show me issues', 'what problems do we have'\n- Filtered issue lists: 'unresolved issues', 'critical bugs'\n- Issues by impact: 'errors affecting more than 100 users'\n- Issues by assignment: 'issues assigned to me'\n- User feedback: 'show me user feedback', 'feedback from last week'\n\nDO NOT USE FOR COUNTS/AGGREGATIONS:\n- 'how many errors' → use search_events\n- 'count of issues' → use search_events\n- 'total number of errors today' → use search_events\n- 'sum/average/statistics' → use search_events\n\nALSO DO NOT USE FOR:\n- Individual error events with timestamps → use search_events\n- Details about a specific issue ID → use get_issue_details\n\nREMEMBER: This tool returns a LIST of issues, not counts or statistics!\n\n\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='critical bugs from last week')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='unhandled errors affecting 100+ users')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='issues assigned to me')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='user feedback from production')\n\n\n\n- If the user passes a parameter in the form of name/otherName, it's likely in the format of /.\n- Parse org/project notation directly without calling find_organizations or find_projects.\n- The projectSlugOrId parameter accepts both project slugs (e.g., 'my-project') and numeric IDs (e.g., '123456').\n", "requiredScopes": ["event:read"] }, { @@ -174,7 +174,7 @@ }, { "name": "search_issues", - "description": "Search for grouped issues/problems in Sentry - returns a LIST of issues, NOT counts or aggregations.\n\nUses AI to translate natural language queries into Sentry issue search syntax.\nReturns grouped issues with metadata like title, status, and user count.\n\nUSE THIS TOOL WHEN USERS WANT:\n- A LIST of issues: 'show me issues', 'what problems do we have'\n- Filtered issue lists: 'unresolved issues', 'critical bugs'\n- Issues by impact: 'errors affecting more than 100 users'\n- Issues by assignment: 'issues assigned to me'\n\nDO NOT USE FOR COUNTS/AGGREGATIONS:\n- 'how many errors' → use search_events\n- 'count of issues' → use search_events\n- 'total number of errors today' → use search_events\n- 'sum/average/statistics' → use search_events\n\nALSO DO NOT USE FOR:\n- Individual error events with timestamps → use search_events\n- Details about a specific issue ID → use get_issue_details\n\nREMEMBER: This tool returns a LIST of issues, not counts or statistics!\n\n\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='critical bugs from last week')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='unhandled errors affecting 100+ users')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='issues assigned to me')\n\n\n\n- If the user passes a parameter in the form of name/otherName, it's likely in the format of /.\n- Parse org/project notation directly without calling find_organizations or find_projects.\n- The projectSlugOrId parameter accepts both project slugs (e.g., 'my-project') and numeric IDs (e.g., '123456').\n", + "description": "Search for grouped issues/problems in Sentry - returns a LIST of issues, NOT counts or aggregations.\n\nUses AI to translate natural language queries into Sentry issue search syntax.\nReturns grouped issues with metadata like title, status, and user count.\n\nUSE THIS TOOL WHEN USERS WANT:\n- A LIST of issues: 'show me issues', 'what problems do we have'\n- Filtered issue lists: 'unresolved issues', 'critical bugs'\n- Issues by impact: 'errors affecting more than 100 users'\n- Issues by assignment: 'issues assigned to me'\n- User feedback: 'show me user feedback', 'feedback from last week'\n\nDO NOT USE FOR COUNTS/AGGREGATIONS:\n- 'how many errors' → use search_events\n- 'count of issues' → use search_events\n- 'total number of errors today' → use search_events\n- 'sum/average/statistics' → use search_events\n\nALSO DO NOT USE FOR:\n- Individual error events with timestamps → use search_events\n- Details about a specific issue ID → use get_issue_details\n\nREMEMBER: This tool returns a LIST of issues, not counts or statistics!\n\n\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='critical bugs from last week')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='unhandled errors affecting 100+ users')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='issues assigned to me')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='user feedback from production')\n\n\n\n- If the user passes a parameter in the form of name/otherName, it's likely in the format of /.\n- Parse org/project notation directly without calling find_organizations or find_projects.\n- The projectSlugOrId parameter accepts both project slugs (e.g., 'my-project') and numeric IDs (e.g., '123456').\n", "requiredScopes": ["event:read"] }, { diff --git a/packages/mcp-core/src/toolDefinitions.json b/packages/mcp-core/src/toolDefinitions.json index 487b48ca5..055751b5a 100644 --- a/packages/mcp-core/src/toolDefinitions.json +++ b/packages/mcp-core/src/toolDefinitions.json @@ -739,7 +739,7 @@ }, { "name": "search_issues", - "description": "Search for grouped issues/problems in Sentry - returns a LIST of issues, NOT counts or aggregations.\n\nUses AI to translate natural language queries into Sentry issue search syntax.\nReturns grouped issues with metadata like title, status, and user count.\n\nUSE THIS TOOL WHEN USERS WANT:\n- A LIST of issues: 'show me issues', 'what problems do we have'\n- Filtered issue lists: 'unresolved issues', 'critical bugs'\n- Issues by impact: 'errors affecting more than 100 users'\n- Issues by assignment: 'issues assigned to me'\n\nDO NOT USE FOR COUNTS/AGGREGATIONS:\n- 'how many errors' → use search_events\n- 'count of issues' → use search_events\n- 'total number of errors today' → use search_events\n- 'sum/average/statistics' → use search_events\n\nALSO DO NOT USE FOR:\n- Individual error events with timestamps → use search_events\n- Details about a specific issue ID → use get_issue_details\n\nREMEMBER: This tool returns a LIST of issues, not counts or statistics!\n\n\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='critical bugs from last week')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='unhandled errors affecting 100+ users')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='issues assigned to me')\n\n\n\n- If the user passes a parameter in the form of name/otherName, it's likely in the format of /.\n- Parse org/project notation directly without calling find_organizations or find_projects.\n- The projectSlugOrId parameter accepts both project slugs (e.g., 'my-project') and numeric IDs (e.g., '123456').\n", + "description": "Search for grouped issues/problems in Sentry - returns a LIST of issues, NOT counts or aggregations.\n\nUses AI to translate natural language queries into Sentry issue search syntax.\nReturns grouped issues with metadata like title, status, and user count.\n\nUSE THIS TOOL WHEN USERS WANT:\n- A LIST of issues: 'show me issues', 'what problems do we have'\n- Filtered issue lists: 'unresolved issues', 'critical bugs'\n- Issues by impact: 'errors affecting more than 100 users'\n- Issues by assignment: 'issues assigned to me'\n- User feedback: 'show me user feedback', 'feedback from last week'\n\nDO NOT USE FOR COUNTS/AGGREGATIONS:\n- 'how many errors' → use search_events\n- 'count of issues' → use search_events\n- 'total number of errors today' → use search_events\n- 'sum/average/statistics' → use search_events\n\nALSO DO NOT USE FOR:\n- Individual error events with timestamps → use search_events\n- Details about a specific issue ID → use get_issue_details\n\nREMEMBER: This tool returns a LIST of issues, not counts or statistics!\n\n\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='critical bugs from last week')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='unhandled errors affecting 100+ users')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='issues assigned to me')\nsearch_issues(organizationSlug='my-org', naturalLanguageQuery='user feedback from production')\n\n\n\n- If the user passes a parameter in the form of name/otherName, it's likely in the format of /.\n- Parse org/project notation directly without calling find_organizations or find_projects.\n- The projectSlugOrId parameter accepts both project slugs (e.g., 'my-project') and numeric IDs (e.g., '123456').\n", "inputSchema": { "type": "object", "properties": { diff --git a/packages/mcp-core/src/tools/search-issues/config.ts b/packages/mcp-core/src/tools/search-issues/config.ts index 01c94e42d..d8e8202f0 100644 --- a/packages/mcp-core/src/tools/search-issues/config.ts +++ b/packages/mcp-core/src/tools/search-issues/config.ts @@ -16,13 +16,16 @@ BUILT-IN FIELDS: - level: Severity level (error, warning, info, debug, fatal) IMPORTANT: Almost NEVER use this field. Terms like "critical", "important", "severe" refer to IMPACT not level. Only use if user explicitly says "error level", "warning level", etc. +- issueCategory: Issue classification (error, performance, metric, feedback) + IMPORTANT: User feedback from the User Feedback Widget is stored as issues with issueCategory:feedback + Use this field when users ask about "user feedback", "feedback submissions", "user reports", etc. - environment: Deployment environment (production, staging, development) - release: Version/release identifier - firstSeen: When the issue was FIRST encountered (use for "new issues", "started", "began") WARNING: Excludes ongoing issues that started before the time window - lastSeen: When the issue was LAST encountered (use for "from the last", "recent", "active") This includes ALL issues seen during the time window, regardless of when they started -- assigned: Issues explicitly assigned to a user (email or "me") +- assigned: Issues explicitly assigned to a user (email or "me") - assignedOrSuggested: Issues assigned to OR suggested for a user (broader match) - userCount: Number of unique users affected - eventCount: Total number of events @@ -34,6 +37,9 @@ COMMON QUERY PATTERNS: - New issues: firstSeen:-7d - High impact: userCount:>100 - My work: assignedOrSuggested:me +- User feedback: issueCategory:feedback +- Recent feedback: issueCategory:feedback lastSeen:-7d +- Feedback from production: issueCategory:feedback environment:production SORTING RULES: 1. CRITICAL: Sort MUST go in the separate "sort" field, NEVER in the "query" field @@ -57,6 +63,8 @@ EXAMPLES: "critical bugs" → query: "level:error is:unresolved", sort: "date" "worst issues affecting the most users" → query: "is:unresolved", sort: "user" "assigned to john@example.com" → query: "assignedOrSuggested:john@example.com", sort: "date" +"show me user feedback" → query: "issueCategory:feedback", sort: "date" +"feedback from the last week" → query: "issueCategory:feedback lastSeen:-7d", sort: "date" NEVER: query: "is:unresolved sort:user" ← Sort goes in separate field! diff --git a/packages/mcp-core/src/tools/search-issues/formatters.test.ts b/packages/mcp-core/src/tools/search-issues/formatters.test.ts new file mode 100644 index 000000000..1de89db2c --- /dev/null +++ b/packages/mcp-core/src/tools/search-issues/formatters.test.ts @@ -0,0 +1,216 @@ +import { describe, it, expect } from "vitest"; +import { formatIssueResults } from "./formatters.js"; +import { + createPerformanceIssue, + createFeedbackIssue, +} from "@sentry/mcp-server-mocks"; + +describe("formatIssueResults", () => { + const baseParams = { + organizationSlug: "test-org", + regionUrl: "https://sentry.io", + }; + + describe("issueCategory display", () => { + it("displays category for feedback issues", () => { + const feedbackIssue = createFeedbackIssue({ + shortId: "TEST-FEEDBACK-1", + title: "User Feedback: Login broken", + status: "unresolved", + userCount: 1, + count: "1", + firstSeen: "2025-01-01T00:00:00Z", + lastSeen: "2025-01-01T00:00:00Z", + issueCategory: "feedback", + }); + + const result = formatIssueResults({ + ...baseParams, + issues: [feedbackIssue], + }); + + expect(result).toContain("**Category**: feedback"); + }); + + it("displays category for performance issues", () => { + const perfIssue = createPerformanceIssue({ + shortId: "TEST-PERF-1", + title: "N+1 Query", + status: "unresolved", + userCount: 10, + count: "100", + firstSeen: "2025-01-01T00:00:00Z", + lastSeen: "2025-01-01T00:00:00Z", + issueCategory: "performance", + }); + + const result = formatIssueResults({ + ...baseParams, + issues: [perfIssue], + }); + + expect(result).toContain("**Category**: performance"); + }); + + it("does not display category for error issues", () => { + const errorIssue = createFeedbackIssue({ + shortId: "TEST-ERROR-1", + title: "TypeError: Cannot read property", + status: "unresolved", + userCount: 5, + count: "50", + firstSeen: "2025-01-01T00:00:00Z", + lastSeen: "2025-01-01T00:00:00Z", + issueCategory: "error", + }); + + const result = formatIssueResults({ + ...baseParams, + issues: [errorIssue], + }); + + expect(result).not.toContain("**Category**:"); + }); + + it("does not display category when issueCategory is undefined", () => { + const issue = createFeedbackIssue({ + shortId: "TEST-1", + title: "Some Issue", + status: "unresolved", + userCount: 1, + count: "1", + firstSeen: "2025-01-01T00:00:00Z", + lastSeen: "2025-01-01T00:00:00Z", + issueCategory: undefined, + }); + + const result = formatIssueResults({ + ...baseParams, + issues: [issue], + }); + + expect(result).not.toContain("**Category**:"); + }); + }); + + describe("feedback-specific guidance", () => { + it("includes feedback guidance when results contain feedback issues", () => { + const feedbackIssue = createFeedbackIssue({ + shortId: "TEST-FEEDBACK-2", + title: "User Feedback: Page not loading", + issueCategory: "feedback", + }); + + const result = formatIssueResults({ + ...baseParams, + issues: [feedbackIssue], + }); + + expect(result).toContain( + "View feedback details: Use get_issue_details to see full feedback content and linked error events", + ); + }); + + it("does not include feedback guidance for non-feedback issues", () => { + const errorIssue = createPerformanceIssue({ + shortId: "TEST-ERROR-2", + title: "N+1 Query", + issueCategory: "performance", + }); + + const result = formatIssueResults({ + ...baseParams, + issues: [errorIssue], + }); + + expect(result).not.toContain("View feedback details:"); + }); + + it("includes feedback guidance when mixed issues contain at least one feedback", () => { + const errorIssue = createPerformanceIssue({ + shortId: "TEST-ERROR-3", + title: "N+1 Query", + issueCategory: "performance", + }); + const feedbackIssue = createFeedbackIssue({ + shortId: "TEST-FEEDBACK-3", + title: "User Feedback: Bug report", + issueCategory: "feedback", + }); + + const result = formatIssueResults({ + ...baseParams, + issues: [errorIssue, feedbackIssue], + }); + + expect(result).toContain("View feedback details:"); + }); + }); + + describe("empty results", () => { + it("handles empty issue list gracefully", () => { + const result = formatIssueResults({ + ...baseParams, + issues: [], + naturalLanguageQuery: "user feedback", + }); + + expect(result).toContain("No issues found matching your search criteria"); + expect(result).not.toContain("View feedback details:"); + }); + }); + + describe("output format", () => { + it("formats feedback issue correctly", () => { + const feedbackIssue = createFeedbackIssue({ + shortId: "PROJ-FB-1", + title: "User Feedback: Checkout broken", + status: "unresolved", + userCount: 1, + count: "1", + firstSeen: "2025-01-01T00:00:00Z", + lastSeen: "2025-01-01T00:00:00Z", + issueCategory: "feedback", + culprit: "User Feedback Widget", + }); + + const result = formatIssueResults({ + ...baseParams, + issues: [feedbackIssue], + naturalLanguageQuery: "show me user feedback", + }); + + expect(result).toMatchInlineSnapshot(` + "# Search Results for "show me user feedback" + + ⚠️ **IMPORTANT**: Display these issues as highlighted cards with status indicators, assignee info, and clickable Issue IDs. + + **View these results in Sentry**: + https://test-org.sentry.io/issues/ + _Please share this link with the user to view the search results in their Sentry dashboard._ + + Found **1** issue: + + ## 1. [PROJ-FB-1](https://test-org.sentry.io/issues/PROJ-FB-1) + + **User Feedback: Checkout broken** + + - **Status**: unresolved + - **Category**: feedback + - **Users**: 1 + - **Events**: 1 + - **First seen**: 2025-01-01 + - **Last seen**: 2025-01-01 + - **Culprit**: \`User Feedback Widget\` + + ## Next Steps + + - Get more details about a specific issue: Use the Issue ID with get_issue_details + - Update issue status: Use update_issue to resolve or assign issues + - View event counts: Use search_events for aggregated statistics + - View feedback details: Use get_issue_details to see full feedback content and linked error events + " + `); + }); + }); +}); diff --git a/packages/mcp-core/src/tools/search-issues/formatters.ts b/packages/mcp-core/src/tools/search-issues/formatters.ts index b28d502c9..d10badf7f 100644 --- a/packages/mcp-core/src/tools/search-issues/formatters.ts +++ b/packages/mcp-core/src/tools/search-issues/formatters.ts @@ -93,6 +93,10 @@ export function formatIssueResults(params: FormatIssueResultsParams): string { // Issue metadata // Issues don't have a level field in the API response output += `- **Status**: ${issue.status}\n`; + // Display issue category for non-error types (feedback, performance, metric) + if (issue.issueCategory && issue.issueCategory !== "error") { + output += `- **Category**: ${issue.issueCategory}\n`; + } output += `- **Users**: ${issue.userCount || 0}\n`; output += `- **Events**: ${issue.count || 0}\n`; @@ -128,6 +132,13 @@ export function formatIssueResults(params: FormatIssueResultsParams): string { output += "- View event counts: Use search_events for aggregated statistics\n"; + // Add feedback-specific guidance if results contain feedback + const hasFeedback = issues.some((i) => i.issueCategory === "feedback"); + if (hasFeedback) { + output += + "- View feedback details: Use get_issue_details to see full feedback content and linked error events\n"; + } + return output; } @@ -153,5 +164,5 @@ function formatDate(dateString?: string | null): string { if (diffDays < 30) { return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`; } - return date.toLocaleDateString(); + return date.toISOString().split("T")[0]; } diff --git a/packages/mcp-core/src/tools/search-issues/handler.ts b/packages/mcp-core/src/tools/search-issues/handler.ts index a0e4e9401..2134feacf 100644 --- a/packages/mcp-core/src/tools/search-issues/handler.ts +++ b/packages/mcp-core/src/tools/search-issues/handler.ts @@ -23,6 +23,7 @@ export default defineTool({ "- Filtered issue lists: 'unresolved issues', 'critical bugs'", "- Issues by impact: 'errors affecting more than 100 users'", "- Issues by assignment: 'issues assigned to me'", + "- User feedback: 'show me user feedback', 'feedback from last week'", "", "DO NOT USE FOR COUNTS/AGGREGATIONS:", "- 'how many errors' → use search_events", @@ -40,6 +41,7 @@ export default defineTool({ "search_issues(organizationSlug='my-org', naturalLanguageQuery='critical bugs from last week')", "search_issues(organizationSlug='my-org', naturalLanguageQuery='unhandled errors affecting 100+ users')", "search_issues(organizationSlug='my-org', naturalLanguageQuery='issues assigned to me')", + "search_issues(organizationSlug='my-org', naturalLanguageQuery='user feedback from production')", "", "", "", diff --git a/packages/mcp-server-mocks/src/fixtures.ts b/packages/mcp-server-mocks/src/fixtures.ts index 9311c6772..2f7f7b843 100644 --- a/packages/mcp-server-mocks/src/fixtures.ts +++ b/packages/mcp-server-mocks/src/fixtures.ts @@ -48,6 +48,9 @@ import cspIssueFixtureJson from "./fixtures/csp-issue.json" with { import cspEventFixtureJson from "./fixtures/csp-event.json" with { type: "json", }; +import feedbackIssueFixtureJson from "./fixtures/feedback-issue.json" with { + type: "json", +}; // Type helper for deep partial overrides type DeepPartial = T extends object @@ -66,6 +69,7 @@ const unsupportedIssueFixture = unsupportedIssueFixtureJson as any; const performanceEventFixture = performanceEventFixtureJson as any; const cspIssueFixture = cspIssueFixtureJson as any; const cspEventFixture = cspEventFixtureJson as any; +const feedbackIssueFixture = feedbackIssueFixtureJson as any; /** * Deep merge helper that recursively merges objects. @@ -314,3 +318,27 @@ export function createCspEvent( ): typeof cspEventFixture { return deepMerge(JSON.parse(JSON.stringify(cspEventFixture)), overrides); } + +/** + * Create a FeedbackIssue with optional overrides. + * Represents a user feedback submission from the User Feedback Widget. + * + * @param overrides - Partial issue properties to override + * @returns A complete FeedbackIssue object + * + * @example + * ```typescript + * const issue = createFeedbackIssue({ + * shortId: "PROJ-FEEDBACK-001", + * title: "User Feedback: Can't login", + * metadata: { + * value: "Login button is not responding" + * } + * }); + * ``` + */ +export function createFeedbackIssue( + overrides: DeepPartial = {}, +): typeof feedbackIssueFixture { + return deepMerge(JSON.parse(JSON.stringify(feedbackIssueFixture)), overrides); +} diff --git a/packages/mcp-server-mocks/src/fixtures/feedback-issue.json b/packages/mcp-server-mocks/src/fixtures/feedback-issue.json new file mode 100644 index 000000000..bb456a958 --- /dev/null +++ b/packages/mcp-server-mocks/src/fixtures/feedback-issue.json @@ -0,0 +1,75 @@ +{ + "id": "6507376999", + "shareId": null, + "shortId": "CLOUDFLARE-MCP-99", + "title": "User Feedback: Bug report from Jane", + "culprit": "User Feedback Widget", + "permalink": "https://sentry-mcp-evals.sentry.io/issues/6507376999/", + "logger": null, + "level": "info", + "status": "unresolved", + "statusDetails": {}, + "substatus": "new", + "isPublic": false, + "platform": "javascript", + "project": { + "id": "4509062593708032", + "name": "CLOUDFLARE-MCP", + "slug": "CLOUDFLARE-MCP", + "platform": "javascript" + }, + "type": "feedback", + "metadata": { + "value": "The checkout button doesn't work on mobile", + "type": "Feedback", + "title": "User Feedback: Bug report from Jane" + }, + "numComments": 0, + "assignedTo": null, + "isBookmarked": false, + "isSubscribed": false, + "subscriptionDetails": null, + "hasSeen": false, + "annotations": [], + "issueType": "feedback", + "issueCategory": "feedback", + "priority": "medium", + "priorityLockedAt": null, + "isUnhandled": false, + "count": "1", + "userCount": 1, + "firstSeen": "2025-04-12T10:30:00.000000Z", + "lastSeen": "2025-04-12T10:30:00.000000Z", + "firstRelease": null, + "lastRelease": null, + "activity": [ + { + "id": "0", + "user": null, + "type": "first_seen", + "data": { + "priority": "medium" + }, + "dateCreated": "2025-04-12T10:30:00.000000Z" + } + ], + "openPeriods": [ + { + "start": "2025-04-12T10:30:00.000000Z", + "end": null, + "duration": null, + "isOpen": true, + "lastChecked": "2025-04-12T10:30:00.000000Z" + } + ], + "seenBy": [], + "pluginActions": [], + "pluginIssues": [], + "pluginContexts": [], + "userReportCount": 0, + "stats": { + "24h": [], + "30d": [] + }, + "participants": [] +} diff --git a/packages/mcp-server-mocks/src/index.ts b/packages/mcp-server-mocks/src/index.ts index ee468b8f5..e1520b514 100644 --- a/packages/mcp-server-mocks/src/index.ts +++ b/packages/mcp-server-mocks/src/index.ts @@ -1418,6 +1418,7 @@ export { createUnsupportedIssue, createCspIssue, createCspEvent, + createFeedbackIssue, } from "./fixtures"; // Export utilities for creating mock servers