File size: 4,372 Bytes
b91e262 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | /**
* MCP tool for retrieving error state from Next.js dev server.
*
* This tool provides comprehensive error reporting including:
* - Next.js global errors (e.g., next.config validation errors)
* - Browser runtime errors with source-mapped stack traces
* - Build errors from webpack/turbopack compilation
*
* For browser errors, it leverages the HMR infrastructure for server-to-browser communication.
*
* Flow:
* MCP client → server generates request ID → HMR message to browser →
* browser queries error overlay state → HMR response back → server performs source mapping →
* combined with global errors → formatted output.
*/
import type { McpServer } from 'next/dist/compiled/@modelcontextprotocol/sdk/server/mcp'
import type { OverlayState } from '../../../next-devtools/dev-overlay/shared'
import {
HMR_MESSAGE_SENT_TO_BROWSER,
type HmrMessageSentToBrowser,
} from '../../dev/hot-reloader-types'
import { formatErrors } from './utils/format-errors'
import {
createBrowserRequest,
handleBrowserPageResponse,
DEFAULT_BROWSER_REQUEST_TIMEOUT_MS,
} from './utils/browser-communication'
import { NextInstanceErrorState } from './next-instance-error-state'
import { mcpTelemetryTracker } from '../mcp-telemetry-tracker'
export function registerGetErrorsTool(
server: McpServer,
sendHmrMessage: (message: HmrMessageSentToBrowser) => void,
getActiveConnectionCount: () => number
) {
server.registerTool(
'get_errors',
{
description:
'Get the current error state from the Next.js dev server, including Next.js global errors (e.g., next.config validation), browser runtime errors, and build errors with source-mapped stack traces',
inputSchema: {},
},
async (_request) => {
// Track telemetry
mcpTelemetryTracker.recordToolCall('mcp/get_errors')
try {
const connectionCount = getActiveConnectionCount()
if (connectionCount === 0) {
return {
content: [
{
type: 'text',
text: 'No browser sessions connected. Please open your application in a browser to retrieve error state.',
},
],
}
}
const responses = await createBrowserRequest<OverlayState>(
HMR_MESSAGE_SENT_TO_BROWSER.REQUEST_CURRENT_ERROR_STATE,
sendHmrMessage,
getActiveConnectionCount,
DEFAULT_BROWSER_REQUEST_TIMEOUT_MS
)
// The error state for each route
// key is the route path, value is the error state
const routesErrorState = new Map<string, OverlayState>()
for (const response of responses) {
if (response.data) {
routesErrorState.set(response.url, response.data)
}
}
const hasRouteErrors = Array.from(routesErrorState.values()).some(
(state) => state.errors.length > 0 || !!state.buildError
)
const hasInstanceErrors = NextInstanceErrorState.nextConfig.length > 0
if (!hasRouteErrors && !hasInstanceErrors) {
return {
content: [
{
type: 'text',
text:
responses.length === 0
? 'No browser sessions responded.'
: `No errors detected in ${responses.length} browser session(s).`,
},
],
}
}
const output = await formatErrors(
routesErrorState,
NextInstanceErrorState
)
return {
content: [
{
type: 'text',
text: output,
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
}
}
}
)
}
// Browser will first receive an HMR message from server to send back its error state.
// The actual state is sent back in a subsequent HMR message, which is handled by this function
// on the server.
export function handleErrorStateResponse(
requestId: string,
errorState: OverlayState | null,
url: string | undefined
) {
handleBrowserPageResponse<OverlayState | null>(
requestId,
errorState,
url || ''
)
}
|