Skip to content

Commit 5e59617

Browse files
authored
[mcp] change get-project-path to get-project-metadata (#84619)
1 parent 9962fa5 commit 5e59617

File tree

6 files changed

+74
-51
lines changed

6 files changed

+74
-51
lines changed

packages/next/src/server/dev/hot-reloader-turbopack.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -758,12 +758,13 @@ export async function createHotReloaderTurbopack(
758758
}),
759759
...(nextConfig.experimental.mcpServer
760760
? [
761-
getMcpMiddleware(
761+
getMcpMiddleware({
762762
projectPath,
763763
distDir,
764-
(message) => hotReloader.send(message),
765-
() => clients.size
766-
),
764+
sendHmrMessage: (message) => hotReloader.send(message),
765+
getActiveConnectionCount: () => clients.size,
766+
getDevServerUrl: () => process.env.__NEXT_PRIVATE_ORIGIN,
767+
}),
767768
]
768769
: []),
769770
]

packages/next/src/server/dev/hot-reloader-webpack.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,12 +1632,14 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
16321632
}),
16331633
...(this.config.experimental.mcpServer
16341634
? [
1635-
getMcpMiddleware(
1636-
this.dir,
1637-
this.distDir,
1638-
(message) => this.send(message),
1639-
() => this.webpackHotMiddleware?.getClientCount() ?? 0
1640-
),
1635+
getMcpMiddleware({
1636+
projectPath: this.dir,
1637+
distDir: this.distDir,
1638+
sendHmrMessage: (message) => this.send(message),
1639+
getActiveConnectionCount: () =>
1640+
this.webpackHotMiddleware?.getClientCount() ?? 0,
1641+
getDevServerUrl: () => process.env.__NEXT_PRIVATE_ORIGIN,
1642+
}),
16411643
]
16421644
: []),
16431645
]

packages/next/src/server/mcp/get-mcp-middleware.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import type { ServerResponse, IncomingMessage } from 'http'
2-
import { getOrCreateMcpServer } from './get-or-create-mcp-server'
2+
import {
3+
getOrCreateMcpServer,
4+
type McpServerOptions,
5+
} from './get-or-create-mcp-server'
36
import { parseBody } from '../api-utils/node/parse-body'
47
import { StreamableHTTPServerTransport } from 'next/dist/compiled/@modelcontextprotocol/sdk/server/streamableHttp'
5-
import type { HmrMessageSentToBrowser } from '../dev/hot-reloader-types'
68

7-
export function getMcpMiddleware(
8-
projectPath: string,
9-
distDir: string,
10-
sendHmrMessage: (message: HmrMessageSentToBrowser) => void,
11-
getActiveConnectionCount: () => number
12-
) {
9+
export function getMcpMiddleware(options: McpServerOptions) {
1310
return async function (
1411
req: IncomingMessage,
1512
res: ServerResponse,
@@ -19,12 +16,7 @@ export function getMcpMiddleware(
1916
if (!pathname.startsWith('/_next/mcp')) {
2017
return next()
2118
}
22-
const mcpServer = getOrCreateMcpServer(
23-
projectPath,
24-
distDir,
25-
sendHmrMessage,
26-
getActiveConnectionCount
27-
)
19+
const mcpServer = getOrCreateMcpServer(options)
2820
const transport = new StreamableHTTPServerTransport({
2921
sessionIdGenerator: undefined,
3022
})
Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import { McpServer } from 'next/dist/compiled/@modelcontextprotocol/sdk/server/mcp'
2-
import { registerGetProjectPathTool } from './tools/get-project-path'
2+
import { registerGetProjectMetadataTool } from './tools/get-project-metadata'
33
import { registerGetErrorsTool } from './tools/get-errors'
44
import { registerGetPageMetadataTool } from './tools/get-page-metadata'
55
import { registerGetLogsTool } from './tools/get-logs'
66
import { registerGetActionByIdTool } from './tools/get-server-action-by-id'
77
import type { HmrMessageSentToBrowser } from '../dev/hot-reloader-types'
88

9+
export interface McpServerOptions {
10+
projectPath: string
11+
distDir: string
12+
sendHmrMessage: (message: HmrMessageSentToBrowser) => void
13+
getActiveConnectionCount: () => number
14+
getDevServerUrl: () => string | undefined
15+
}
16+
917
let mcpServer: McpServer | undefined
1018

11-
export const getOrCreateMcpServer = (
12-
projectPath: string,
13-
distDir: string,
14-
sendHmrMessage: (message: HmrMessageSentToBrowser) => void,
15-
getActiveConnectionCount: () => number
16-
) => {
19+
export const getOrCreateMcpServer = (options: McpServerOptions) => {
1720
if (mcpServer) {
1821
return mcpServer
1922
}
@@ -23,15 +26,23 @@ export const getOrCreateMcpServer = (
2326
version: '0.1.0',
2427
})
2528

26-
registerGetProjectPathTool(mcpServer, projectPath)
27-
registerGetErrorsTool(mcpServer, sendHmrMessage, getActiveConnectionCount)
29+
registerGetProjectMetadataTool(
30+
mcpServer,
31+
options.projectPath,
32+
options.getDevServerUrl
33+
)
34+
registerGetErrorsTool(
35+
mcpServer,
36+
options.sendHmrMessage,
37+
options.getActiveConnectionCount
38+
)
2839
registerGetPageMetadataTool(
2940
mcpServer,
30-
sendHmrMessage,
31-
getActiveConnectionCount
41+
options.sendHmrMessage,
42+
options.getActiveConnectionCount
3243
)
33-
registerGetLogsTool(mcpServer, distDir)
34-
registerGetActionByIdTool(mcpServer, distDir)
44+
registerGetLogsTool(mcpServer, options.distDir)
45+
registerGetActionByIdTool(mcpServer, options.distDir)
3546

3647
return mcpServer
3748
}

packages/next/src/server/mcp/tools/get-project-path.ts renamed to packages/next/src/server/mcp/tools/get-project-metadata.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import type { McpServer } from 'next/dist/compiled/@modelcontextprotocol/sdk/server/mcp'
22

3-
export function registerGetProjectPathTool(
3+
export function registerGetProjectMetadataTool(
44
server: McpServer,
5-
projectPath: string
5+
projectPath: string,
6+
getDevServerUrl: () => string | undefined
67
) {
78
server.registerTool(
8-
'get_project_path',
9+
'get_project_metadata',
910
{
1011
description:
11-
'Returns the absolute path of the root directory for this Next.js project.',
12+
'Returns the the metadata of this Next.js project, including project path, dev server URL, etc.',
1213
inputSchema: {},
1314
},
1415
async (_request) => {
@@ -24,11 +25,16 @@ export function registerGetProjectPathTool(
2425
}
2526
}
2627

28+
const devServerUrl = getDevServerUrl()
29+
2730
return {
2831
content: [
2932
{
3033
type: 'text',
31-
text: projectPath,
34+
text: JSON.stringify({
35+
projectPath,
36+
devServerUrl,
37+
}),
3238
},
3339
],
3440
}

test/development/mcp-server/mcp-server-get-project-path.test.ts renamed to test/development/mcp-server/mcp-server-get-project-metadata.test.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { FileRef, nextTestSetup } from 'e2e-utils'
22
import path from 'path'
33

4-
describe('mcp-server get_project_path tool', () => {
4+
describe('mcp-server get_project_metadata tool', () => {
55
const { next } = nextTestSetup({
66
files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')),
77
})
8-
it('should return correct project path via get_project_path tool', async () => {
8+
it('should return correct project metadata via get_project_metadata tool', async () => {
99
const mcpEndpoint = `${next.url}/_next/mcp`
1010

11-
// Call get_project_path tool
11+
// Call get_project_metadata tool
1212
const callToolResponse = await fetch(mcpEndpoint, {
1313
method: 'POST',
1414
headers: {
@@ -20,7 +20,7 @@ describe('mcp-server get_project_path tool', () => {
2020
id: 'call-tool-1',
2121
method: 'tools/call',
2222
params: {
23-
name: 'get_project_path',
23+
name: 'get_project_metadata',
2424
arguments: {},
2525
},
2626
}),
@@ -38,12 +38,23 @@ describe('mcp-server get_project_path tool', () => {
3838
expect(content).toBeInstanceOf(Array)
3939
expect(content?.[0]?.type).toBe('text')
4040

41-
const actualProjectPath = content?.[0]?.text
41+
const metadataText = content?.[0]?.text
42+
expect(metadataText).toBeTruthy()
4243

43-
// Verify it's an absolute path
44-
expect(path.isAbsolute(actualProjectPath)).toBe(true)
44+
const metadata = JSON.parse(metadataText)
4545

46-
// Should match the test directory
47-
expect(actualProjectPath).toBe(next.testDir)
46+
// Verify projectPath
47+
expect(metadata.projectPath).toBeTruthy()
48+
expect(path.isAbsolute(metadata.projectPath)).toBe(true)
49+
expect(metadata.projectPath).toBe(next.testDir)
50+
51+
// Verify devServerUrl
52+
expect(metadata.devServerUrl).toBeTruthy()
53+
expect(metadata.devServerUrl).toMatch(/^https?:\/\//)
54+
expect(metadata.devServerUrl).toContain('localhost')
55+
56+
// The devServerUrl should match the next.url (base URL without path)
57+
const expectedBaseUrl = next.url
58+
expect(metadata.devServerUrl).toBe(expectedBaseUrl)
4859
})
4960
})

0 commit comments

Comments
 (0)