[mcp] Refine passes returned (#32930)
Adds some new options to request the HIR, ReactiveFunction passes --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32930). * #32932 * #32931 * __->__ #32930 * #32929 * #32928
lauren committed
Apr 16, 2025 at 17:49 UTC
3e04b2a214cdc962dd5acde412c7107321ec7a56
3 files changed
+99
-15
compiler/packages/react-mcp-server/src/compiler/index.ts
+10
@@ -14,6 +14,16 @@ import * as prettier from 'prettier';
14
15
export let lastResult: BabelCore.BabelFileResult | null = null;
16
17
+export type PrintedCompilerPipelineValue =
18
+ | {
19
+ kind: 'hir';
20
+ name: string;
21
+ fnName: string | null;
22
+ value: string;
23
+ }
24
+ | {kind: 'reactive'; name: string; fnName: string | null; value: string}
25
+ | {kind: 'debug'; name: string; fnName: string | null; value: string};
26
+
27
type CompileOptions = {
28
text: string;
29
file: string;
compiler/packages/react-mcp-server/src/index.ts
+76
-15
@@ -11,7 +11,7 @@ import {
11
} from '@modelcontextprotocol/sdk/server/mcp.js';
12
import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js';
13
import {z} from 'zod';
14
-import {compile} from './compiler';
14
+import {compile, type PrintedCompilerPipelineValue} from './compiler';
15
import {
16
CompilerPipelineValue,
17
printReactiveFunctionWithOutlined,
@@ -22,19 +22,9 @@ import {
22
import * as cheerio from 'cheerio';
23
import TurndownService from 'turndown';
24
import {queryAlgolia} from './utils/algolia';
25
+import assertExhaustive from './utils/assertExhaustive';
26
27
const turndownService = new TurndownService();
27
-
28
-export type PrintedCompilerPipelineValue =
29
- | {
30
- kind: 'hir';
31
- name: string;
32
- fnName: string | null;
33
- value: string;
34
- }
35
- | {kind: 'reactive'; name: string; fnName: string | null; value: string}
36
- | {kind: 'debug'; name: string; fnName: string | null; value: string};
37
-
28
const server = new McpServer({
29
name: 'React',
30
version: '0.0.0',
@@ -114,7 +104,7 @@ server.tool(
104
'Compile code with React Compiler. Optionally, for debugging provide a pass name like "HIR" to see more information.',
105
{
106
text: z.string(),
117
- passName: z.string().optional(),
107
+ passName: z.enum(['HIR', 'ReactiveFunction', 'All', '@DEBUG']).optional(),
108
},
109
async ({text, passName}) => {
110
const pipelinePasses = new Map<
@@ -164,8 +154,7 @@ server.tool(
154
break;
155
}
156
default: {
167
- const _: never = result;
168
- throw new Error(`Unhandled result ${result}`);
157
+ assertExhaustive(result, `Unhandled result ${result}`);
158
}
159
}
160
};
@@ -205,6 +194,78 @@ server.tool(
194
}
195
const requestedPasses: Array<{type: 'text'; text: string}> = [];
196
if (passName != null) {
197
+ switch (passName) {
198
+ case 'All': {
199
+ const hir = pipelinePasses.get('PropagateScopeDependenciesHIR');
200
+ if (hir !== undefined) {
201
+ for (const pipelineValue of hir) {
202
+ requestedPasses.push({
203
+ type: 'text' as const,
204
+ text: pipelineValue.value,
205
+ });
206
+ }
207
+ }
208
+ const reactiveFunc = pipelinePasses.get('PruneHoistedContexts');
209
+ if (reactiveFunc !== undefined) {
210
+ for (const pipelineValue of reactiveFunc) {
211
+ requestedPasses.push({
212
+ type: 'text' as const,
213
+ text: pipelineValue.value,
214
+ });
215
+ }
216
+ }
217
+ break;
218
+ }
219
+ case 'HIR': {
220
+ // Last pass before HIR -> ReactiveFunction
221
+ const requestedPass = pipelinePasses.get(
222
+ 'PropagateScopeDependenciesHIR',
223
+ );
224
+ if (requestedPass !== undefined) {
225
+ for (const pipelineValue of requestedPass) {
226
+ requestedPasses.push({
227
+ type: 'text' as const,
228
+ text: pipelineValue.value,
229
+ });
230
+ }
231
+ } else {
232
+ console.error(`Could not find requested pass ${passName}`);
233
+ }
234
+ break;
235
+ }
236
+ case 'ReactiveFunction': {
237
+ // Last pass
238
+ const requestedPass = pipelinePasses.get('PruneHoistedContexts');
239
+ if (requestedPass !== undefined) {
240
+ for (const pipelineValue of requestedPass) {
241
+ requestedPasses.push({
242
+ type: 'text' as const,
243
+ text: pipelineValue.value,
244
+ });
245
+ }
246
+ } else {
247
+ console.error(`Could not find requested pass ${passName}`);
248
+ }
249
+ break;
250
+ }
251
+ case '@DEBUG': {
252
+ for (const [, pipelinePass] of pipelinePasses) {
253
+ for (const pass of pipelinePass) {
254
+ requestedPasses.push({
255
+ type: 'text' as const,
256
+ text: `${pass.name}\n\n${pass.value}`,
257
+ });
258
+ }
259
+ }
260
+ break;
261
+ }
262
+ default: {
263
+ assertExhaustive(
264
+ passName,
265
+ `Unhandled passName option: ${passName}`,
266
+ );
267
+ }
268
+ }
269
const requestedPass = pipelinePasses.get(passName);
270
if (requestedPass !== undefined) {
271
for (const pipelineValue of requestedPass) {
compiler/packages/react-mcp-server/src/utils/assertExhaustive.ts
new
+13
@@ -0,0 +1,13 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+/**
9
+ * Trigger an exhaustiveness check in TypeScript and throw at runtime.
10
+ */
11
+export default function assertExhaustive(_: never, errorMsg: string): never {
12
+ throw new Error(errorMsg);
13
+}