-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Reorganizing Crashlytics MCP Tools #9594
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: master
Are you sure you want to change the base?
Conversation
Summary of ChangesHello @maxl0rd, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly refactors the Crashlytics MCP tools by streamlining report generation into a single, unified function and externalizing detailed instructions into comprehensive resource guides. This change aims to enhance the system's scalability, improve the user experience for various MCP clients, and provide more robust error handling and guidance for developers interacting with Crashlytics data. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
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.
Code Review
This pull request is a great refactoring of the Crashlytics MCP tools. Consolidating the report-fetching tools into a single crashlytics_get_report tool is a significant improvement, making it more extensible. Moving the large prompt content into separate guide resources is also an excellent change for modularity and maintainability. The error handling has been improved across the tools, providing more context to the agent. I've found a few minor issues, including some typos in user-facing text, a small bug in output formatting, and opportunities for code simplification. Overall, this is a high-quality change.
|
|
||
| /** | ||
| * Perform some simplistic validation on filters. | ||
| * Perform some simplistic validation on filters and fill . |
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.
The JSDoc for this function appears to be incomplete. The sentence ends abruptly with a period after "fill". It should probably clarify what is being filled.
| * Perform some simplistic validation on filters and fill . | |
| * Perform some simplistic validation on filters and fill in missing values. |
| export function toReportEnum(reportName: string): CrashlyticsReport | undefined { | ||
| const enumValues = Object.values(CrashlyticsReport); | ||
| if (enumValues.includes(reportName as CrashlyticsReport)) { | ||
| return reportName as CrashlyticsReport; | ||
| } else { | ||
| return undefined; | ||
| } | ||
| } |
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.
This function can be simplified and made more type-safe. The cast reportName as CrashlyticsReport within the includes method is a bit of a code smell. A more explicit approach would be to cast the enum values to a string array for the check.
export function toReportEnum(reportName: string): CrashlyticsReport | undefined {
if ((Object.values(CrashlyticsReport) as string[]).includes(reportName)) {
return reportName as CrashlyticsReport;
}
return undefined;
}| `.trim(); | ||
|
|
||
| const notLoggedInInstruction = ` | ||
| **Instruct the User to Log In*** |
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.
| 1. [Firebase App Id Guide](firebase://guides/app_id) | ||
| This guide provides crucial instructions for obtaining the application's App Id which is required for all API calls. | ||
| 1. [Firebase Crashlytics Reports Guide](firebase://guides/crashlytics/reports) |
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.
| function toText(response: Report, filters: EventFilter): Record<string, string> { | ||
| const result: Record<string, string> = { | ||
| name: response.name || "", | ||
| filters: dump(filters, DUMP_OPTIONS), | ||
| }; | ||
| for (const [key, value] of Object.entries(response)) { | ||
| result[key] = dump(value, DUMP_OPTIONS); | ||
| } | ||
| return result; | ||
| } |
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.
The name property is initialized in the result object and then potentially overwritten in the loop. This is redundant and can lead to inconsistent formatting. For example, if response.name is undefined, it's initialized to "", but the loop would overwrite it with the result of dump(undefined), which is "null\n". It's better to handle name once, outside the loop, or skip it inside the loop if already handled.
| function toText(response: Report, filters: EventFilter): Record<string, string> { | |
| const result: Record<string, string> = { | |
| name: response.name || "", | |
| filters: dump(filters, DUMP_OPTIONS), | |
| }; | |
| for (const [key, value] of Object.entries(response)) { | |
| result[key] = dump(value, DUMP_OPTIONS); | |
| } | |
| return result; | |
| } | |
| function toText(response: Report, filters: EventFilter): Record<string, string> { | |
| const result: Record<string, string> = { | |
| name: response.name || "", | |
| filters: dump(filters, DUMP_OPTIONS), | |
| }; | |
| for (const [key, value] of Object.entries(response)) { | |
| if (key === "name") { | |
| continue; // Already handled | |
| } | |
| result[key] = dump(value, DUMP_OPTIONS); | |
| } | |
| return result; | |
| } |
97e8d00 to
89c8cdd
Compare
| "from GoogleService-Info.plist. If neither is available, ask the user to " + | ||
| "provide the app id.", | ||
| ); | ||
| .describe("Firebase App Id. Leave blank if unknown to receive instructions for finding it."); |
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.
Should we instruct it to use read_resource here rather than telling it to leave it blank?
| Only relevant for iOS, iPadOS and MacOS applications.`, | ||
| name: "get_report", | ||
| description: | ||
| `Use this to request numerical reports from Crashlytics. Reports contain aggregated metrics describing the number of crash events and number of impacted end users. The reports are grouped by different dimensions such as issue, version or device. |
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.
Should we make mention of the guide in the tool description?
| nock.cleanAll(); | ||
| }); | ||
|
|
||
| it("should resolve with the response body on success", async () => { |
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.
Can we add some tests around the different validation cases we expect with the error responses?
| export const RESOURCE_CONTENT = ` | ||
| ### Instructions for Working with Firebase Crashlytics Tools | ||
| 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. |
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.
Is this a strong enough statement? I've seen it ignore this pretty readily without emphasis - asking multiple questions at a time being the main thing it tries to do.
| ### Check That You Are Connected | ||
| 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. |
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.
We didn't have this step before - how has this played out in your testing? My one concern here is that it increases token consumption by default without interaction from the user. That happens not infrequently anyway, but curious if it happens more with this instruction.
| if (!appId) { | ||
| result.isError = true; | ||
| result.content.push({ type: "text", text: "Must specify 'appId' parameter" }); | ||
| result.content.push({ type: "text", text: forceAppIdGuide }); |
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.
We don't return the issue guide here. At some iteration, I feel like we were doing that. If we were, why did we pull it back?
| ); | ||
| if (!report) { | ||
| result.isError = true; | ||
| result.content.push({ type: "text", text: `Error: ${REPORT_ERROR_CONTENT}` }); |
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.
Same question here - why not reference reading the guide in the error response?
Description
crashlytics_get_*tools into a single toolcrashlytics_get_report. This will enable the set of supported reports to expand without adding additional tools.crashlytics:connectprompt to guide resources. This provides better support for the many MCP clients without support for prompts.