Skip to content

Adding dart MCP for Flutter projects#10100

Merged
joehan merged 2 commits intomainfrom
jh-dart-mcp
Mar 13, 2026
Merged

Adding dart MCP for Flutter projects#10100
joehan merged 2 commits intomainfrom
jh-dart-mcp

Conversation

@joehan
Copy link
Member

@joehan joehan commented Mar 13, 2026

Description

b/492452409

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, 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 introduces support for automatically configuring a Dart Multi-Client Protocol (MCP) server for Flutter projects during the migration process. This enhancement aims to simplify the setup for Flutter developers by ensuring that the appropriate Dart MCP server is added to the Antigravity configuration when a Flutter application is detected and Dart is available on the system. It also provides helpful feedback if Dart is not found, guiding users to install Flutter.

Highlights

  • Dart MCP Server Configuration: Implemented automatic configuration of a Dart Multi-Client Protocol (MCP) server for Flutter projects during the migration process.
  • Flutter Project Detection: Added logic to detect Flutter projects by checking for the presence of a pubspec.yaml file.
  • Dart Executable Check: Included a check for the dart executable's availability on the system PATH before configuring the Dart MCP server.
  • User Feedback for Missing Dart: Introduced a warning message that is logged if Dart is not found when a Flutter project is detected, guiding users to install Flutter.
  • Refactored MCP Setup Function: Refactored the setupAntigravityMcpServer function to accept an appType parameter, enabling conditional server configuration based on the project type.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/firebase_studio/migrate.spec.ts
    • Added tests for configuring the Dart MCP server when Dart is available for Flutter apps.
    • Added tests for not configuring the Dart MCP server and logging a warning when Dart is unavailable for Flutter apps.
    • Added tests for not configuring the Dart MCP server when the app is not a Flutter app.
  • src/firebase_studio/migrate.ts
    • Modified setupAntigravityMcpServer to accept an optional appType parameter.
    • Updated setupAntigravityMcpServer to conditionally configure the Dart MCP server if appType is "FLUTTER" and the dart command exists.
    • Added a warning log if Dart is not found when a Flutter app is detected.
    • Refactored the Firebase MCP server configuration to only write if not already present and to use an updated flag to control file writing.
    • Passed the appType parameter to setupAntigravityMcpServer in the main migrate function.
Activity
  • No specific activity was provided in the pull request context.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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 adds support for configuring the Dart MCP server for Flutter projects. The implementation looks good, but I have a few suggestions for improvement.

In migrate.ts, I've suggested a refactoring to reduce nesting, in line with the repository's style guide.

In migrate.spec.ts, I've found a test that will fail due to an incorrect assertion on a warning message and provided a fix. I've also pointed out significant code duplication in the new tests and suggested a refactoring to improve maintainability.

Comment on lines +461 to +462
expect(logWarningStub.calledWith("Dart is not available. You should install Flutter.")).to.be
.true;
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The warning message asserted in this test does not match the one in the implementation. This will cause the test to fail.

The implementation in migrate.ts logs:
"Couldn't find Dart/Flutter on PATH. Install Flutter by following the instruction at https://docs.flutter.dev/install."

Please update the expectation to match the actual warning message.

      expect(logWarningStub.calledWith("Couldn't find Dart/Flutter on PATH. Install Flutter by following the instruction at https://docs.flutter.dev/install.")).to.be.true;

Comment on lines +66 to +83
if (appType === "FLUTTER") {
if (utils.commandExistsSync("dart")) {
if (!mcpConfig.mcpServers["dart"]) {
mcpConfig.mcpServers["dart"] = {
command: "dart",
args: ["mcp-server"],
};
updated = true;
logger.info(`✅ Configured Dart MCP server in ${mcpConfigPath}`);
} else {
logger.info("ℹ️ Dart MCP server already configured in Antigravity, skipping.");
}
} else {
utils.logWarning(
"Couldn't find Dart/Flutter on PATH. Install Flutter by following the instruction at https://docs.flutter.dev/install.",
);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The nesting for configuring the Dart MCP server is a bit deep. According to the repository's style guide, we should aim to reduce nesting.

You could refactor this logic into a helper function to improve readability and reduce nesting. For example:

function configureDartMcp(mcpConfig: McpConfig, mcpConfigPath: string): boolean {
  if (!utils.commandExistsSync("dart")) {
    utils.logWarning(
      "Couldn't find Dart/Flutter on PATH. Install Flutter by following the instruction at https://docs.flutter.dev/install.",
    );
    return false;
  }

  if (mcpConfig.mcpServers["dart"]) {
    logger.info("ℹ️ Dart MCP server already configured in Antigravity, skipping.");
    return false;
  }

  mcpConfig.mcpServers["dart"] = {
    command: "dart",
    args: ["mcp-server"],
  };
  logger.info(`✅ Configured Dart MCP server in ${mcpConfigPath}`);
  return true;
}

Then you can call it like this:

    if (appType === "FLUTTER") {
      if (configureDartMcp(mcpConfig, mcpConfigPath)) {
        updated = true;
      }
    }

This encapsulates the branching logic as suggested in the style guide.

References
  1. The style guide recommends reducing code nesting and considering helper functions to encapsulate branching logic for better maintainability. (link)

Comment on lines +403 to +492
it("should configure Dart MCP server for Flutter apps if dart is available", async () => {
accessStub.withArgs(sinon.match("pubspec.yaml")).resolves();
commandStub.withArgs("dart").returns(true);

readFileStub.callsFake(async (p: any) => {
const pStr = p.toString();
if (pStr.endsWith("metadata.json"))
return JSON.stringify({ projectId: "test-project", appName: "Test App" });
if (pStr.endsWith("readme_template.md"))
return "# ${appName}\nRun ${startCommand} at ${localUrl}";
if (pStr.endsWith("system_instructions_template.md")) return "Project: ${appName}";
if (pStr.endsWith("startup_workflow.md")) return "Step 1: Build";
if (pStr.endsWith(".firebaserc"))
return JSON.stringify({ projects: { default: "test-project" } });
if (pStr.endsWith("blueprint.md")) return "# **App Name**: Test App";
if (pStr.endsWith("package.json")) return "{}";
if (pStr.endsWith("mcp_config.json"))
throw Object.assign(new Error("File not found"), { code: "ENOENT" });
throw new Error(`Unexpected readFile: ${pStr}`);
});

await migrate(testRoot);

const mcpConfigDir = path.join(require("os").homedir(), ".gemini", "antigravity");
const mcpConfigPath = path.join(mcpConfigDir, "mcp_config.json");

expect(writeFileStub.calledWith(mcpConfigPath, sinon.match(/"dart":/))).to.be.true;
});

it("should NOT configure Dart MCP server for Flutter apps if dart is NOT available, and log warning", async () => {
accessStub.withArgs(sinon.match("pubspec.yaml")).resolves();
commandStub.withArgs("dart").returns(false);

const logWarningStub = sandbox.stub(utils, "logWarning");

readFileStub.callsFake(async (p: any) => {
const pStr = p.toString();
if (pStr.endsWith("metadata.json"))
return JSON.stringify({ projectId: "test-project", appName: "Test App" });
if (pStr.endsWith("readme_template.md"))
return "# ${appName}\nRun ${startCommand} at ${localUrl}";
if (pStr.endsWith("system_instructions_template.md")) return "Project: ${appName}";
if (pStr.endsWith("startup_workflow.md")) return "Step 1: Build";
if (pStr.endsWith(".firebaserc"))
return JSON.stringify({ projects: { default: "test-project" } });
if (pStr.endsWith("blueprint.md")) return "# **App Name**: Test App";
if (pStr.endsWith("package.json")) return "{}";
if (pStr.endsWith("mcp_config.json"))
throw Object.assign(new Error("File not found"), { code: "ENOENT" });
throw new Error(`Unexpected readFile: ${pStr}`);
});

await migrate(testRoot);

const mcpConfigDir = path.join(require("os").homedir(), ".gemini", "antigravity");
const mcpConfigPath = path.join(mcpConfigDir, "mcp_config.json");

expect(writeFileStub.calledWith(mcpConfigPath, sinon.match(/"dart"/))).to.be.false;
expect(logWarningStub.calledWith("Dart is not available. You should install Flutter.")).to.be
.true;
});

it("should NOT configure Dart MCP server if app is not Flutter", async () => {
accessStub.withArgs(sinon.match("pubspec.yaml")).rejects({ code: "ENOENT" });
commandStub.withArgs("dart").returns(true);

readFileStub.callsFake(async (p: any) => {
const pStr = p.toString();
if (pStr.endsWith("metadata.json"))
return JSON.stringify({ projectId: "test-project", appName: "Test App" });
if (pStr.endsWith("readme_template.md"))
return "# ${appName}\nRun ${startCommand} at ${localUrl}";
if (pStr.endsWith("system_instructions_template.md")) return "Project: ${appName}";
if (pStr.endsWith("startup_workflow.md")) return "Step 1: Build";
if (pStr.endsWith(".firebaserc"))
return JSON.stringify({ projects: { default: "test-project" } });
if (pStr.endsWith("blueprint.md")) return "# **App Name**: Test App";
if (pStr.endsWith("package.json")) return "{}";
if (pStr.endsWith("mcp_config.json"))
throw Object.assign(new Error("File not found"), { code: "ENOENT" });
throw new Error(`Unexpected readFile: ${pStr}`);
});

await migrate(testRoot);

const mcpConfigDir = path.join(require("os").homedir(), ".gemini", "antigravity");
const mcpConfigPath = path.join(mcpConfigDir, "mcp_config.json");

expect(writeFileStub.calledWith(mcpConfigPath, sinon.match(/"dart"/))).to.be.false;
});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

There's a significant amount of duplicated code across the three new tests for Dart MCP server configuration. Specifically, the readFileStub.callsFake implementation is nearly identical in all three tests.

To improve maintainability and reduce redundancy, consider extracting this common setup into a beforeEach block within a new nested describe block for these related tests. This will make the tests cleaner and easier to manage.

For example:

describe("Dart MCP server configuration", () => {
  beforeEach(() => {
    readFileStub.callsFake(async (p: any) => {
      const pStr = p.toString();
      if (pStr.endsWith("metadata.json"))
        return JSON.stringify({ projectId: "test-project", appName: "Test App" });
      if (pStr.endsWith("readme_template.md"))
        return "# ${appName}\nRun ${startCommand} at ${localUrl}";
      if (pStr.endsWith("system_instructions_template.md")) return "Project: ${appName}";
      if (pStr.endsWith("startup_workflow.md")) return "Step 1: Build";
      if (pStr.endsWith(".firebaserc"))
        return JSON.stringify({ projects: { default: "test-project" } });
      if (pStr.endsWith("blueprint.md")) return "# **App Name**: Test App";
      if (pStr.endsWith("package.json")) return "{}";
      if (pStr.endsWith("mcp_config.json"))
        throw Object.assign(new Error("File not found"), { code: "ENOENT" });
      throw new Error(`Unexpected readFile: ${pStr}`);
    });
  });

  it("should configure Dart MCP server for Flutter apps if dart is available", ...);
  it("should NOT configure Dart MCP server for Flutter apps if dart is NOT available, and log warning", ...);
  it("should NOT configure Dart MCP server if app is not Flutter", ...);
});

@joehan joehan enabled auto-merge (squash) March 13, 2026 23:12
@joehan joehan merged commit ad7c1e3 into main Mar 13, 2026
46 of 47 checks passed
@joehan joehan deleted the jh-dart-mcp branch March 13, 2026 23:23
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.

3 participants