Skip to content

Commit 3e4c6f7

Browse files
maxl0rdschnecle
authored andcommitted
Improving error handling in crashlytics tools and using more guide content
1 parent adab0c0 commit 3e4c6f7

File tree

13 files changed

+292
-321
lines changed

13 files changed

+292
-321
lines changed

src/crashlytics/filters.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import { FirebaseError } from "../error";
44
export const ApplicationIdSchema = z
55
.string()
66
.describe(
7-
"Firebase app id. For an Android application, read the " +
8-
"mobilesdk_app_id value specified in the google-services.json file for " +
9-
"the current package name. For an iOS Application, read the GOOGLE_APP_ID " +
10-
"from GoogleService-Info.plist. If neither is available, ask the user to " +
11-
"provide the app id.",
7+
"Firebase App Id. Leave blank if unknown to receive instructions for finding it.",
128
);
139

1410
export const IssueIdSchema = z.string().describe("Crashlytics issue id, as hexidecimal uuid");
@@ -109,12 +105,15 @@ export function filterToUrlSearchParams(filter: EventFilter): URLSearchParams {
109105
const displayNamePattern = /^[^()]+\s+\([^()]+\)$/; // Regular expression like "xxxx (yyy)"
110106

111107
/**
112-
* Perform some simplistic validation on filters.
108+
* Perform some simplistic validation on filters and fill .
113109
* @param filter filters to validate
114110
* @throws FirebaseError if any of the filters are invalid.
115111
*/
116-
export function validateEventFilters(filter: EventFilter): void {
117-
if (!filter) return;
112+
export function validateEventFilters(filter: EventFilter = {}): EventFilter {
113+
if (!!filter.intervalStartTime && !filter.intervalEndTime) {
114+
// interval.end_time is required if interval.start_time is set but the agent likes to forget it
115+
filter.intervalEndTime = new Date().toISOString();
116+
}
118117
const ninetyDaysAgo = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);
119118
if (filter.intervalStartTime && new Date(filter.intervalStartTime) < ninetyDaysAgo) {
120119
throw new FirebaseError("intervalStartTime must be less than 90 days in the past");
@@ -140,4 +139,5 @@ export function validateEventFilters(filter: EventFilter): void {
140139
}
141140
});
142141
}
142+
return filter;
143143
}

src/crashlytics/reports.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ describe("getReport", () => {
140140
.reply(200, mockResponse);
141141

142142
const result = await getReport(
143-
CrashlyticsReport.TopIssues,
143+
CrashlyticsReport.TOP_ISSUES,
144144
appId,
145145
{ issueErrorTypes: [issueType] },
146146
pageSize,
@@ -153,7 +153,7 @@ describe("getReport", () => {
153153
it("should throw a FirebaseError if the appId is invalid", async () => {
154154
const invalidAppId = "invalid-app-id";
155155

156-
await expect(getReport(CrashlyticsReport.TopIssues, invalidAppId, {})).to.be.rejectedWith(
156+
await expect(getReport(CrashlyticsReport.TOP_ISSUES, invalidAppId, {})).to.be.rejectedWith(
157157
FirebaseError,
158158
"Unable to get the projectId from the AppId.",
159159
);

src/crashlytics/reports.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,39 @@ import {
99
EventFilterSchema,
1010
filterToUrlSearchParams,
1111
} from "./filters";
12+
import { FirebaseError } from "../error";
1213

1314
const DEFAULT_PAGE_SIZE = 10;
1415

16+
export enum CrashlyticsReport {
17+
TOP_ISSUES = "topIssues",
18+
TOP_VARIANTS = "topVariants",
19+
TOP_VERSIONS = "topVersions",
20+
TOP_OPERATING_SYSTEMS = "topOperatingSystems",
21+
TOP_APPLE_DEVICES = "topAppleDevices",
22+
TOP_ANDROID_DEVICES = "topAndroidDevices",
23+
}
24+
25+
export const CrashlyticsReportSchema = z.nativeEnum(CrashlyticsReport);
26+
27+
export function toReportEnum(reportName: string): CrashlyticsReport | undefined {
28+
const enumValues = Object.values(CrashlyticsReport);
29+
if (enumValues.includes(reportName as CrashlyticsReport)) {
30+
return reportName as CrashlyticsReport;
31+
} else {
32+
return undefined;
33+
}
34+
}
35+
1536
export const ReportInputSchema = z.object({
1637
appId: ApplicationIdSchema,
38+
report: CrashlyticsReportSchema,
1739
filter: EventFilterSchema,
1840
pageSize: z.number().optional().describe("Number of rows to return").default(DEFAULT_PAGE_SIZE),
1941
});
2042

2143
export type ReportInput = z.infer<typeof ReportInputSchema>;
2244

23-
export enum CrashlyticsReport {
24-
TopIssues = "topIssues",
25-
TopVariants = "topVariants",
26-
TopVersions = "topVersions",
27-
TopOperatingSystems = "topOperatingSystems",
28-
TopAppleDevices = "topAppleDevices",
29-
TopAndroidDevices = "topAndroidDevices",
30-
}
31-
3245
/**
3346
* Returns a report for Crashlytics events.
3447
* @param report One of the supported reports in the CrashlyticsReport enum
@@ -62,23 +75,27 @@ export function simplifyReport(report: Report): Report {
6275
}
6376

6477
export async function getReport(
65-
report: CrashlyticsReport,
78+
reportName: CrashlyticsReport,
6679
appId: string,
6780
filter: EventFilter,
6881
pageSize = DEFAULT_PAGE_SIZE,
6982
): Promise<Report> {
83+
84+
if (!reportName) {
85+
throw new FirebaseError("Invalid Crashlytics report " + reportName);
86+
}
7087
const requestProjectNumber = parseProjectNumber(appId);
7188
const queryParams = filterToUrlSearchParams(filter);
7289
queryParams.set("page_size", `${pageSize}`);
7390
logger.debug(
74-
`[crashlytics] report ${report} called with appId: ${appId} filter: ${queryParams.toString()}, page_size: ${pageSize}`,
91+
`[crashlytics] report ${reportName} called with appId: ${appId} filter: ${queryParams.toString()}, page_size: ${pageSize}`,
7592
);
7693
const response = await CRASHLYTICS_API_CLIENT.request<void, Report>({
7794
method: "GET",
7895
headers: {
7996
"Content-Type": "application/json",
8097
},
81-
path: `/projects/${requestProjectNumber}/apps/${appId}/reports/${report}`,
98+
path: `/projects/${requestProjectNumber}/apps/${appId}/reports/${reportName}`,
8299
queryParams: queryParams,
83100
timeout: TIMEOUT,
84101
});

src/mcp/prompts/crashlytics/connect.ts

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { prompt } from "../../prompt";
2+
import { RESOURCE_CONTENT as connectResourceContent } from "../../resources/guides/crashlytics_connect";
23

34
export const connect = prompt(
45
"crashlytics",
@@ -34,25 +35,8 @@ You will assist developers in investigating and resolving mobile application iss
3435
3536
**Obtain the Firebase App ID.**
3637
If an App ID is not readily available, consult this guide for selection: [Firebase App Id Guide](firebase://guides/app_id).
37-
### Next Steps
3838
39-
After confirming the user is logged into Firebase and the correct App ID is identified, inquire about the desired actions. Your capabilities include:
40-
- Reading Crashlytics reports.
41-
- Investigating bug reports using Crashlytics event data.
42-
- Proposing code changes to resolve identified bugs.
43-
44-
Only ask the user one question at a time. Do not proceed without user instructions.
45-
Upon receiving user instructions, refer to the relevant resources for guidance.
46-
Use the Firebase \`read_resources\` tool to access the following guides.
47-
48-
1. [Firebase Crashlytics Reports Guide](firebase://guides/crashlytics/reports)
49-
This guide details how to request and use aggregated numerical data from Crashlytics. The agent should read this guide before requesting any report.
50-
51-
2. [Firebase Crashlytics Issues Guide](firebase://guides/crashlytics/issues)
52-
This guide details how to work with issues within Crashlytics. The agent should read this guide before prioritizing issues or presenting issue data to the user.
53-
54-
3. [Investigating Crashlytics Issues](firebase://guides/crashlytics/investigations)
55-
This guide provides instructions on investigating the root causes of crashes and exceptions reported in Crashlytics issues.
39+
${ connectResourceContent }
5640
`.trim(),
5741
},
5842
},

src/mcp/resources/guides/app_id.ts

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,6 @@
11
import { resource } from "../../resource";
22

3-
export const app_id = resource(
4-
{
5-
uri: "firebase://guides/app_id",
6-
name: "app_id_guide",
7-
title: "Firebase App Id Guide",
8-
description:
9-
"guides the coding agent through choosing a Firebase App ID in the current project",
10-
},
11-
async (uri) => {
12-
return {
13-
contents: [
14-
{
15-
uri,
16-
type: "text",
17-
text: `
3+
export const RESOURCE_CONTENT = `
184
### Firebase App ID
195
The Firebase App ID is used to identify a mobile or web client application to Firebase back end services such as Crashlytics or Remote Config. Use the information below to find the developer's App ID.
206
@@ -38,9 +24,19 @@ export const app_id = resource(
3824
number (typically "1"), a project number, a platform type ("android", "ios", or "web"),
3925
and a sequence of hexadecimal characters.
4026
ii. Store the valid app id value, the android package name or ios bundle identifier, and the project directory.
41-
`.trim(),
42-
},
43-
],
27+
`.trim();
28+
29+
export const app_id = resource(
30+
{
31+
uri: "firebase://guides/app_id",
32+
name: "app_id_guide",
33+
title: "Firebase App Id Guide",
34+
description:
35+
"guides the coding agent through choosing a Firebase App ID in the current project",
36+
},
37+
async (uri) => {
38+
return {
39+
contents: [{ uri, type: "text", text: RESOURCE_CONTENT }],
4440
};
4541
},
4642
);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { resource } from "../../resource";
2+
3+
export const RESOURCE_CONTENT = `
4+
### Instructions for Working with Firebase Crashlytics Tools
5+
6+
Only ask the user one question at a time. Do not proceed without user instructions. Upon receiving user instructions, refer to the relevant resources for guidance.
7+
8+
Use the \`firebase_read_resources\` tool to access the following guides.
9+
10+
1. [Firebase App Id Guide](firebase://guides/app_id)
11+
This guide provides crucial instructions for obtaining the application's App Id which is required for all API calls.
12+
13+
1. [Firebase Crashlytics Reports Guide](firebase://guides/crashlytics/reports)
14+
This guide details how to request and use aggregated numerical data from Crashlytics. The agent should read this guide before requesting any report.
15+
16+
2. [Firebase Crashlytics Issues Guide](firebase://guides/crashlytics/issues)
17+
This guide details how to work with issues within Crashlytics. The agent should read this guide before prioritizing issues or presenting issue data to the user.
18+
19+
3. [Investigating Crashlytics Issues](firebase://guides/crashlytics/investigations)
20+
This guide provides instructions on investigating the root causes of crashes and exceptions reported in Crashlytics issues.
21+
22+
### Check That You Are Connected
23+
24+
Verify that you can read the app's Crashlytics data by getting the topVersions report. This report will tell you which app versions have the most events.
25+
a. Call the \`firebase_get_environment\` tool if you need to find the app_id.
26+
b. Call the \`crashlytics_get_report\` tool to read the \`topVersions\` report.
27+
c. If you haven't read the reports guide, then the tool will include it in the response. This is OK. Simply call the tool again.
28+
d. Help the user resolve any issues that arise when trying to connect.
29+
30+
After confirming you can access Crashlytics, inquire about the desired actions. Your capabilities include:
31+
32+
- Reading Crashlytics reports.
33+
- Investigating bug reports using Crashlytics event data.
34+
- Proposing code changes to resolve identified bugs.
35+
`.trim();
36+
37+
export const crashlytics_connect = resource(
38+
{
39+
uri: "firebase://guides/crashlytics/connect",
40+
name: "crashlytics_connect_guide",
41+
title: "Firebase Crashlytics Connect Guide",
42+
description: "Guides the coding agent to connect to Firebase Crashlytics.",
43+
},
44+
async (uri) => {
45+
return {
46+
contents: [{ uri, type: "text", text: RESOURCE_CONTENT }],
47+
};
48+
},
49+
);

src/mcp/resources/guides/crashlytics_investigations.ts

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,6 @@
11
import { resource } from "../../resource";
22

3-
export const crashlytics_investigations = resource(
4-
{
5-
uri: "firebase://guides/crashlytics/investigations",
6-
name: "crashlytics_investigations_guide",
7-
title: "Firebase Crashlytics Investigations Guide",
8-
description:
9-
"Guides the coding agent when investigating bugs reported in Crashlytics issues, including procedures for diagnosing and fixing crashes.",
10-
},
11-
async (uri) => {
12-
return {
13-
contents: [
14-
{
15-
uri,
16-
type: "text",
17-
text: `
3+
const RESOURCE_CONTENT = `
184
### How to Diagnose and Fix Crashlytics Issues
195
206
Follow these steps to diagnose bugs and and propose fixes for issues.
@@ -61,9 +47,19 @@ export const crashlytics_investigations = resource(
6147
10. Only if they approve the plan, create a fix for the issue.
6248
10a. Be mindful of API contracts and do not add fields to resources without a clear way to populate those fields
6349
10b. If there is not enough information in the crash report to find a root cause, describe why you cannot fix the issue instead of making a guess.
64-
`.trim(),
65-
},
66-
],
50+
`.trim();
51+
52+
export const crashlytics_investigations = resource(
53+
{
54+
uri: "firebase://guides/crashlytics/investigations",
55+
name: "crashlytics_investigations_guide",
56+
title: "Firebase Crashlytics Investigations Guide",
57+
description:
58+
"Guides the coding agent when investigating bugs reported in Crashlytics issues, including procedures for diagnosing and fixing crashes.",
59+
},
60+
async (uri) => {
61+
return {
62+
contents: [{ uri, type: "text", text: RESOURCE_CONTENT }],
6763
};
6864
},
6965
);
Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,6 @@
11
import { resource } from "../../resource";
22

3-
export const crashlytics_issues = resource(
4-
{
5-
uri: "firebase://guides/crashlytics/issues",
6-
name: "crashlytics_issues_guide",
7-
title: "Firebase Crashlytics Issues Guide",
8-
description:
9-
"Guides the coding agent when working with Crashlytics issues, including prioritization rules and procedures for diagnosing and fixing crashes. ",
10-
},
11-
async (uri) => {
12-
return {
13-
contents: [
14-
{
15-
uri,
16-
type: "text",
17-
text: `
3+
const RESOURCE_CONTENT = `
184
### How to Display Issues
195
206
When displaying a list of issues, favor the following format:
@@ -28,11 +14,11 @@ When displaying a list of issues, favor the following format:
2814
2915
Follow these steps to fetch issues and prioritize them.
3016
31-
1. Use the 'crashlytics_get_top_issues' tool to fetch up to 20 issues.
17+
1. Use the 'crashlytics_get_report' tool to fetch the 'topIssues' report.
3218
1a. Analyze the user's query and apply the appropriate filters. Use the information in the [Firebase Crashlytics Reports Guide](firebase://guides/crashlytics/reports) to further construct appropriate report requests.
3319
1b. If the user asks for crashes, then set the *issueErrorType* filter to *FATAL*.
3420
1c. If the user asks about a particular time range, then set both the *intervalStartTime* and *intervalEndTime*.
35-
2. Use the 'crashlytics_get_top_versions' tool to fetch the top versions for this app.
21+
2. Use the 'crashlytics_get_report' tool to fetch the 'topVersions' for this app.
3622
3. If the user instructions include statements about prioritization, use those instructions.
3723
4. If the user instructions do not include statements about prioritization, then prioritize the returned issues using the following criteria:
3824
4a. The app versions for the issue include the most recent version of the app.
@@ -41,9 +27,19 @@ Follow these steps to fetch issues and prioritize them.
4127
5. Return the top 5 issues, with a brief description each in a numerical list with the recommended format.
4228
5a. Describe the rationale for the prioritization order.
4329
6. Ask the user if they would like to diagnose and fix any of the issues presented before taking any action.
44-
`.trim(),
45-
},
46-
],
30+
`.trim();
31+
32+
export const crashlytics_issues = resource(
33+
{
34+
uri: "firebase://guides/crashlytics/issues",
35+
name: "crashlytics_issues_guide",
36+
title: "Firebase Crashlytics Issues Guide",
37+
description:
38+
"Guides the coding agent when working with Crashlytics issues, including prioritization rules and procedures for diagnosing and fixing crashes. ",
39+
},
40+
async (uri) => {
41+
return {
42+
contents: [{ uri, type: "text", text: RESOURCE_CONTENT }],
4743
};
4844
},
4945
);

0 commit comments

Comments
 (0)