Skip to content

Commit 2bde655

Browse files
authored
feat(mcp): Adds dataconnect_compile tool to MCP server. (#8979)
1 parent bcc5a08 commit 2bde655

File tree

16 files changed

+821
-11
lines changed

16 files changed

+821
-11
lines changed

src/experiments.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ export const ALL_EXPERIMENTS = experiments({
147147
default: true,
148148
public: false,
149149
},
150+
mcpalpha: {
151+
shortDescription: "Opt-in to early MCP features before they're widely released.",
152+
default: false,
153+
public: true,
154+
},
150155
apptesting: {
151156
shortDescription: "Adds experimental App Testing feature",
152157
public: true,

src/mcp/prompts/dataconnect/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { isEnabled } from "../../../experiments";
2+
import { schema } from "./schema";
3+
import type { ServerPrompt } from "../../prompt";
4+
5+
export const dataconnectPrompts: ServerPrompt[] = [];
6+
7+
if (isEnabled("mcpalpha")) {
8+
dataconnectPrompts.push(schema);
9+
}

src/mcp/prompts/dataconnect/schema.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { prompt } from "../../prompt";
2+
import { loadAll } from "../../../dataconnect/load";
3+
import type { ServiceInfo } from "../../../dataconnect/types";
4+
import { BUILTIN_SDL, MAIN_INSTRUCTIONS } from "../../util/dataconnect/content";
5+
import { compileErrors } from "../../util/dataconnect/compile";
6+
7+
function renderServices(fdcServices: ServiceInfo[]) {
8+
if (!fdcServices.length) return "Data Connect Status: <UNCONFIGURED>";
9+
10+
return `\n\n## Data Connect Schema
11+
12+
The following is the up-to-date content of existing schema files (their paths are relative to the Data Connect source directory).
13+
14+
${fdcServices[0].schema.source.files?.map((f) => `\`\`\`graphql ${f.path}\n${f.content}\n\`\`\``).join("\n\n")}`;
15+
}
16+
17+
function renderErrors(errors?: string) {
18+
return `\n\n## Current Schema Build Errors\n\n${errors || "<NO ERRORS>"}`;
19+
}
20+
21+
export const schema = prompt(
22+
{
23+
name: "schema",
24+
description: "Generate or update your Firebase Data Connect schema.",
25+
arguments: [
26+
{
27+
name: "prompt",
28+
description:
29+
"describe the schema you want generated or the edits you want to make to your existing schema",
30+
required: true,
31+
},
32+
],
33+
annotations: {
34+
title: "Generate Data Connect Schema",
35+
},
36+
},
37+
async ({ prompt }, { config, projectId, accountEmail }) => {
38+
const fdcServices = await loadAll(projectId, config);
39+
const buildErrors = fdcServices.length
40+
? await compileErrors(fdcServices[0].sourceDirectory)
41+
: "";
42+
43+
return [
44+
{
45+
role: "user" as const,
46+
content: {
47+
type: "text",
48+
text: `
49+
${MAIN_INSTRUCTIONS}\n\n${BUILTIN_SDL}
50+
51+
==== CURRENT ENVIRONMENT INFO ====
52+
53+
User Email: ${accountEmail || "<NONE>"}
54+
Project ID: ${projectId || "<NONE>"}
55+
${renderServices(fdcServices)}${renderErrors(buildErrors)}
56+
57+
==== USER PROMPT ====
58+
59+
${prompt}
60+
61+
==== TASK INSTRUCTIONS ====
62+
63+
1. If Data Connect is marked as \`<UNCONFIGURED>\`, first run the \`firebase_init\` tool with \`{dataconnect: {}}\` arguments to initialize it.
64+
2. If there is not an existing schema to work with (or the existing schema is the commented-out default schema about a movie app), follow the user's prompt to generate a robust schema meeting the specified requirements.
65+
3. If there is already a schema, perform edits to the existing schema file(s) based on the user's instructions. If schema build errors are present and seem relevant to your changes, attempt to fix them.
66+
4. After you have performed edits on the schema, run the \`dataconnect_compile\` tool to build the schema and see if there are any errors. Fix errors that are related to the user's prompt or your changes.
67+
5. If there are errors, attempt to fix them. If you have attempted to fix them 3 times without success, ask the user for help.
68+
6. If there are no errors, write a brief paragraph summarizing your changes.`,
69+
},
70+
},
71+
];
72+
},
73+
);

src/mcp/prompts/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { ServerFeature } from "../types";
22
import { ServerPrompt } from "../prompt";
33
import { corePrompts } from "./core";
4+
import { dataconnectPrompts } from "./dataconnect";
45
import { crashlyticsPrompts } from "./crashlytics";
56

67
const prompts: Record<ServerFeature, ServerPrompt[]> = {
78
core: corePrompts,
89
firestore: [],
910
storage: [],
10-
dataconnect: [],
11+
dataconnect: dataconnectPrompts,
1112
auth: [],
1213
messaging: [],
1314
remoteconfig: [],

src/mcp/tools/dataconnect/compile.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { z } from "zod";
2+
import { tool } from "../../tool";
3+
import { pickService } from "../../../dataconnect/load";
4+
import { compileErrors } from "../../util/dataconnect/compile";
5+
6+
export const compile = tool(
7+
{
8+
name: "build",
9+
description:
10+
"Use this to compile Firebase Data Connect schema, operations, and/or connectors and check for build errors.",
11+
inputSchema: z.object({
12+
error_filter: z
13+
.enum(["all", "schema", "operations"])
14+
.describe("filter errors to a specific type only. defaults to `all` if omitted.")
15+
.optional(),
16+
service_id: z
17+
.string()
18+
.optional()
19+
.describe(
20+
"The Firebase Data Connect service ID to look for. If omitted, builds all services defined in `firebase.json`.",
21+
),
22+
}),
23+
annotations: {
24+
title: "Compile Data Connect",
25+
readOnlyHint: true,
26+
},
27+
_meta: {
28+
requiresProject: false,
29+
requiresAuth: false,
30+
},
31+
},
32+
async ({ service_id, error_filter }, { projectId, config }) => {
33+
const serviceInfo = await pickService(projectId, config, service_id || undefined);
34+
const errors = await compileErrors(serviceInfo.sourceDirectory, error_filter);
35+
if (errors)
36+
return {
37+
content: [
38+
{
39+
type: "text",
40+
text: `The following errors were encountered while compiling Data Connect from directory \`${serviceInfo.sourceDirectory}\`:\n\n${errors}`,
41+
},
42+
],
43+
isError: true,
44+
};
45+
return { content: [{ type: "text", text: "Compiled successfully." }] };
46+
},
47+
);

src/mcp/tools/dataconnect/execute_graphql.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { z } from "zod";
33
import { tool } from "../../tool";
44
import * as dataplane from "../../../dataconnect/dataplaneClient";
55
import { pickService } from "../../../dataconnect/load";
6-
import { graphqlResponseToToolResponse, parseVariables } from "./converter";
6+
import { graphqlResponseToToolResponse, parseVariables } from "../../util/dataconnect/converter";
77
import { Client } from "../../../apiv2";
8-
import { getDataConnectEmulatorClient } from "./emulator";
8+
import { getDataConnectEmulatorClient } from "../../util/dataconnect/emulator";
99

1010
export const execute_graphql = tool(
1111
{

src/mcp/tools/dataconnect/execute_graphql_read.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { z } from "zod";
33
import { tool } from "../../tool";
44
import * as dataplane from "../../../dataconnect/dataplaneClient";
55
import { pickService } from "../../../dataconnect/load";
6-
import { graphqlResponseToToolResponse, parseVariables } from "./converter";
6+
import { graphqlResponseToToolResponse, parseVariables } from "../../util/dataconnect/converter";
77
import { Client } from "../../../apiv2";
8-
import { getDataConnectEmulatorClient } from "./emulator";
8+
import { getDataConnectEmulatorClient } from "../../util/dataconnect/emulator";
99

1010
export const execute_graphql_read = tool(
1111
{

src/mcp/tools/dataconnect/execute_mutation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { tool } from "../../tool";
44
import { mcpError } from "../../util";
55
import * as dataplane from "../../../dataconnect/dataplaneClient";
66
import { pickService } from "../../../dataconnect/load";
7-
import { graphqlResponseToToolResponse, parseVariables } from "./converter";
7+
import { graphqlResponseToToolResponse, parseVariables } from "../../util/dataconnect/converter";
88
import { Client } from "../../../apiv2";
9-
import { getDataConnectEmulatorClient } from "./emulator";
9+
import { getDataConnectEmulatorClient } from "../../util/dataconnect/emulator";
1010

1111
export const execute_mutation = tool(
1212
{

src/mcp/tools/dataconnect/execute_query.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { tool } from "../../tool";
44
import { mcpError } from "../../util";
55
import * as dataplane from "../../../dataconnect/dataplaneClient";
66
import { pickService } from "../../../dataconnect/load";
7-
import { graphqlResponseToToolResponse, parseVariables } from "./converter";
7+
import { graphqlResponseToToolResponse, parseVariables } from "../../util/dataconnect/converter";
88
import { Client } from "../../../apiv2";
9-
import { getDataConnectEmulatorClient } from "./emulator";
9+
import { getDataConnectEmulatorClient } from "../../util/dataconnect/emulator";
1010

1111
export const execute_query = tool(
1212
{

src/mcp/tools/dataconnect/get_connector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { tool } from "../../tool";
33
import { toContent } from "../../util";
44
import * as client from "../../../dataconnect/client";
55
import { pickService } from "../../../dataconnect/load";
6-
import { connectorToText } from "./converter";
6+
import { connectorToText } from "../../util/dataconnect/converter";
77

88
export const get_connectors = tool(
99
{

0 commit comments

Comments
 (0)