diff --git a/vibn-frontend/app/api/chat/route.ts b/vibn-frontend/app/api/chat/route.ts index 10420e1..c455e2c 100644 --- a/vibn-frontend/app/api/chat/route.ts +++ b/vibn-frontend/app/api/chat/route.ts @@ -501,6 +501,13 @@ export async function POST(request: Request) { chatMode = "vibe", attachedFiles = [], } = body; + + // Sanitise the incoming token to handle empty strings or "undefined" hydration states cleanly + const activeMcpToken = + mcp_token && mcp_token !== "undefined" && mcp_token.trim() !== "" + ? mcp_token.trim() + : undefined; + if (!thread_id || !message?.trim()) { return NextResponse.json( { error: "thread_id and message are required" }, @@ -850,7 +857,7 @@ export async function POST(request: Request) { return conversationalPatterns.some((re) => re.test(m)); } const firstMessageIsConversational = - mcp_token !== undefined && // tools available + activeMcpToken !== undefined && // tools available isConversational(message.trim()); try { @@ -863,7 +870,7 @@ export async function POST(request: Request) { // Keep tool definitions active in the schema to avoid model confusion and // MALFORMED_FUNCTION_CALL gateway crashes, but let our system instructions // guide the model to respond in plain text for conversational inputs. - const toolDefs = mcp_token ? VIBN_TOOL_DEFINITIONS : []; + const toolDefs = activeMcpToken ? VIBN_TOOL_DEFINITIONS : []; // Every 8 silent rounds or 12 tool calls, gently nudge the model to surface a one-liner // status before continuing. This is the user's only signal of @@ -995,11 +1002,11 @@ export async function POST(request: Request) { TOOL_TIMEOUT_MS, ), ); - const toolExec = mcp_token + const toolExec = activeMcpToken ? executeMcpTool( tc.name, tc.args, - mcp_token, + activeMcpToken, baseUrl, activeProject?.id, )