Skip to content

Commit 79ae926

Browse files
authored
[mcp] expose logging (#84226)
1 parent 06207d8 commit 79ae926

File tree

13 files changed

+236
-0
lines changed

13 files changed

+236
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,7 @@ export async function createHotReloaderTurbopack(
751751
? [
752752
getMcpMiddleware(
753753
projectPath,
754+
distDir,
754755
(message) => hotReloader.send(message),
755756
() => clients.size
756757
),

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,6 +1647,7 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
16471647
? [
16481648
getMcpMiddleware(
16491649
this.dir,
1650+
this.distDir,
16501651
(message) => this.send(message),
16511652
() => this.webpackHotMiddleware?.getClientCount() ?? 0
16521653
),

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { HmrMessageSentToBrowser } from '../dev/hot-reloader-types'
66

77
export function getMcpMiddleware(
88
projectPath: string,
9+
distDir: string,
910
sendHmrMessage: (message: HmrMessageSentToBrowser) => void,
1011
getActiveConnectionCount: () => number
1112
) {
@@ -20,6 +21,7 @@ export function getMcpMiddleware(
2021
}
2122
const mcpServer = getOrCreateMcpServer(
2223
projectPath,
24+
distDir,
2325
sendHmrMessage,
2426
getActiveConnectionCount
2527
)

packages/next/src/server/mcp/get-or-create-mcp-server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import { McpServer } from 'next/dist/compiled/@modelcontextprotocol/sdk/server/m
22
import { registerGetProjectPathTool } from './tools/get-project-path'
33
import { registerGetErrorsTool } from './tools/get-errors'
44
import { registerGetPageMetadataTool } from './tools/get-page-metadata'
5+
import { registerGetLogsTool } from './tools/get-logs'
56
import type { HmrMessageSentToBrowser } from '../dev/hot-reloader-types'
67

78
let mcpServer: McpServer | undefined
89

910
export const getOrCreateMcpServer = (
1011
projectPath: string,
12+
distDir: string,
1113
sendHmrMessage: (message: HmrMessageSentToBrowser) => void,
1214
getActiveConnectionCount: () => number
1315
) => {
@@ -27,6 +29,7 @@ export const getOrCreateMcpServer = (
2729
sendHmrMessage,
2830
getActiveConnectionCount
2931
)
32+
registerGetLogsTool(mcpServer, distDir)
3033

3134
return mcpServer
3235
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* MCP tool for getting the path to the Next.js development log file.
3+
*
4+
* This tool returns the path to the {nextConfig.distDir}/logs/next-development.log file
5+
* that contains browser console logs and other development information.
6+
*/
7+
import type { McpServer } from 'next/dist/compiled/@modelcontextprotocol/sdk/server/mcp'
8+
import { stat } from 'fs/promises'
9+
import { join } from 'path'
10+
11+
export function registerGetLogsTool(server: McpServer, distDir: string) {
12+
server.registerTool(
13+
'get_logs',
14+
{
15+
description:
16+
'Get the path to the Next.js development log file. Returns the file path so the agent can read the logs directly.',
17+
},
18+
async () => {
19+
try {
20+
const logFilePath = join(distDir, 'logs', 'next-development.log')
21+
22+
// Check if the log file exists
23+
try {
24+
await stat(logFilePath)
25+
} catch (error) {
26+
return {
27+
content: [
28+
{
29+
type: 'text',
30+
text: `Log file not found at ${logFilePath}.`,
31+
},
32+
],
33+
}
34+
}
35+
36+
return {
37+
content: [
38+
{
39+
type: 'text',
40+
text: `Next.js log file path: ${logFilePath}`,
41+
},
42+
],
43+
}
44+
} catch (error) {
45+
return {
46+
content: [
47+
{
48+
type: 'text',
49+
text: `Error getting log file path: ${error instanceof Error ? error.message : String(error)}`,
50+
},
51+
],
52+
}
53+
}
54+
}
55+
)
56+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!tsconfig.json
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use client'
2+
3+
import { useEffect } from 'react'
4+
5+
export default function ClientPage() {
6+
useEffect(() => {
7+
// Logging in client component useEffect
8+
// Test complex partial circular object
9+
const circularObj: any = {
10+
name: 'test',
11+
data: {
12+
nested: {
13+
value: 42,
14+
items: [1, 2, 3],
15+
},
16+
},
17+
metadata: {
18+
name: 'safe stringify',
19+
version: '1.0.0',
20+
},
21+
}
22+
// Create partial circular reference
23+
circularObj.data.parent = circularObj
24+
console.log('Client: Complex circular object:', circularObj)
25+
console.error('Client: This is an error message from client component')
26+
console.warn('Client: This is a warning message from client component')
27+
}, [])
28+
29+
return <p>client page with logging</p>
30+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ReactNode } from 'react'
2+
export default function Root({ children }: { children: ReactNode }) {
3+
return (
4+
<html>
5+
<body>{children}</body>
6+
</html>
7+
)
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default async function Page() {
2+
// Logging in RSC render
3+
console.log('RSC: This is a log message from server component')
4+
console.error('RSC: This is an error message from server component')
5+
console.warn('RSC: This is a warning message from server component')
6+
7+
return <p>hello world</p>
8+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* @type {import('next').NextConfig}
3+
*/
4+
const nextConfig = {
5+
experimental: {
6+
mcpServer: true,
7+
},
8+
}
9+
10+
module.exports = nextConfig

0 commit comments

Comments
 (0)