n8n-io/n8n · error · Error
Failed to read MCP servers from endpoint: response is not a
Error message
Failed to read MCP servers from endpoint: response is not a server list
What it means
Shape-validation branch of MCP server discovery: response was 2xx but the JSON payload didn't match an expected server-list shape (getRawMcpServers returned falsy). The code intentionally does NOT log the raw body (untrusted, may carry sensitive values) — it logs only the payload type to console.error — then throws this message. So the payload arrived but in an unexpected schema.
Source
Thrown at packages/@n8n/nodes-langchain/nodes/vendors/Microsoft/microsoft-utils.ts:251
mcpAuthToken,
turnContext,
MICROSOFT_TOOL_OPTIONS,
),
});
if (!response.ok) {
throw new Error(`Failed to read MCP servers from endpoint: ${response.status}`);
}
const payload: unknown = await response.json();
const rawServers = getRawMcpServers(payload);
if (!rawServers) {
// Log only the payload type, never the raw body: it's an untrusted external
// response that may carry sensitive values.
console.error('Microsoft MCP server discovery returned an unsupported payload shape', {
payloadType: Array.isArray(payload) ? 'array' : typeof payload,
});
throw new Error('Failed to read MCP servers from endpoint: response is not a server list');
}
const servers = rawServers
.map((rawServer) => normalizeMcpServerConfig(rawServer))
.filter((server): server is MCPServerConfig => server !== undefined);
console.warn(`Microsoft MCP server discovery completed: ${servers.length} servers found`);
return servers;
}
async function attachMcpServerAuthorization(
server: MCPServerConfig,
turnContext: TurnContext,
authorization: Authorization,
mcpAuthToken: string,
) {
const sharedScope =View on GitHub (pinned to 5ac6606e81)
Solutions
- Check the console.error log line for payloadType (object vs array) to understand what came back.
- Capture the raw response (out-of-band, never logged by this code) to compare against the current Microsoft MCP discovery schema; update getRawMcpServers if the schema evolved.
- Verify the agentic app id resolves to an MCP-capable agent, not a different resource that returns a different envelope.
- If a captive portal / proxy is intercepting, fix the network egress path so the request reaches the real Tooling Gateway.
Defensive patterns
Strategy: try-catch
Validate before calling
function looksLikeServerList(payload: unknown): boolean {
return Array.isArray(payload)
|| (!!payload && typeof payload === 'object'
&& Array.isArray((payload as { servers?: unknown }).servers));
} Type guard
interface McpServerList { servers?: unknown[] }
function isServerListPayload(p: unknown): p is McpServerList {
return !!p && typeof p === 'object' && Array.isArray((p as McpServerList).servers);
} Try / catch
try {
return await getMcpServerConfigsWithoutAudienceTokens(turnContext, token);
} catch (e) {
if ((e as Error).message.endsWith('response is not a server list')) {
// schema drift: capture the raw payload out-of-band (never log inline) and
// update getRawMcpServers to the current Microsoft MCP discovery schema
await captureForDiagnosis(endpoint);
}
throw e;
} Prevention
- Keep getRawMcpServers/normalizeMcpServerConfig in sync with the current Microsoft MCP discovery schema.
- Ensure network egress reaches the real Tooling Gateway — not a captive portal returning a different envelope with HTTP 200.
- Confirm the agentic app id resolves to an MCP-capable agent before relying on the payload shape.
When it happens
Trigger: Microsoft's Tooling Gateway returned 200 with a payload that isn't a server list: an error object masquerading as success, a newer/undocumented schema version, an HTML login page captured as JSON, or an empty object when a servers array was expected.
Common situations: Gateway version drift (Microsoft changed the discovery schema); a transparent proxy/portal returning a captive-redirect JSON; tenant returned an empty/error envelope with HTTP 200; the agentic app id resolved to a non-MCP resource.
Related errors
- Failed to read MCP servers from endpoint: ${response.status}
- MCP server "${cfg.name}": exactly one of "url" or "command"
- MCP server "${cfg.name}": provide either "url" or "command",
- Tool "${this.name}" requires an input schema
- No prompt found in inputs - expected "prompt" string or "mes
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/4751214516354693.
Report an issue: GitHub.