From fa86fc53f9455e99fc512e4ecb1e2ecb52c7ce03 Mon Sep 17 00:00:00 2001 From: AFFAANh Date: Thu, 3 Sep 2026 19:07:02 +0530 Subject: [PATCH] fix(mcp): make the LinkedIn redirect URL impossible to miss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Confirmed the architecture is correct as-is: each company already has its own isolated envs.json, providers.json and LinkedIn OAuth tokens, swapped in on switch (company_profiles.py) — LinkedIn's own client_id and secret are meant to be per-company, by design, and nothing about that changes here. What was genuinely broken: the exact URL a new company's owner has to add to their LinkedIn app before Authenticate can work was rendered as small gray footnote text below the status badges, easy to open LinkedIn Developer Portal without ever noticing. That's the entire reason a working setup on one machine is completely unreproducible from memory a week later on another one. Moved it to a highlighted box, first thing shown in the card, with the warning stated in plain words and a one-click Copy button — removing the one place a manual retype silently drops a character. No backend change: the URL itself was already computed correctly and consistently; only where and how it's shown was the problem. Co-Authored-By: Claude Opus 5 --- src/pages/Agent/MCP/index.tsx | 90 ++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/src/pages/Agent/MCP/index.tsx b/src/pages/Agent/MCP/index.tsx index f7ebfdd..aa86aa9 100644 --- a/src/pages/Agent/MCP/index.tsx +++ b/src/pages/Agent/MCP/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useState, useEffect } from "react"; import { Button, Empty, Modal, Card, message } from "@agentscope-ai/design"; -import { ExternalLink, RefreshCw, ShieldCheck } from "lucide-react"; +import { Copy, ExternalLink, RefreshCw, ShieldCheck } from "lucide-react"; import api, { request } from "../../../api"; import type { LinkedInOAuthStatus, @@ -262,6 +262,20 @@ function MCPPage() { } }; + // The single most common way LinkedIn login breaks for a new client: this + // exact URL was never added to their LinkedIn app's Authorized redirect + // URLs. Copy-to-clipboard removes the one place a manual retype goes + // wrong — a trailing space or a dropped character here fails silently + // until someone clicks Authenticate and gets LinkedIn's own error page. + const handleCopyLinkedInRedirectUri = async (redirectUri: string) => { + try { + await navigator.clipboard.writeText(redirectUri); + message.success("Copied. Paste it into LinkedIn Developer Portal."); + } catch { + message.error("Could not copy automatically — select and copy it by hand."); + } + }; + const handleCreateClient = async () => { try { const parsed: unknown = JSON.parse(newClientJson); @@ -454,7 +468,68 @@ function MCPPage() { LinkedIn OAuth -

+ + {/* Shown first, before anything else in this card: this is + the one step a new client's owner has to get exactly + right before Authenticate can possibly work, and the old + small-print placement below the status badges made it + easy to open LinkedIn Developer Portal without ever + noticing it. */} +

+

+ Before clicking Authenticate: add this exact URL to this + LinkedIn app's Authorized redirect URLs (Developer Portal + → your app → Auth tab), or LinkedIn will refuse the login. +

+
+ + {linkedinStatus.redirect_uri} + + +
+
+ +

{linkedinStatusLine(linkedinStatus)}

{linkedinStatus.missing_step && ( @@ -487,17 +562,6 @@ function MCPPage() { linkedinStatus.linkedin_image_post_enabled, )} -

- Add this callback URL in LinkedIn Developer Portal:{" "} - {linkedinStatus.redirect_uri} -

{linkedinStatus.scope.length > 0 && (