fix(mcp): make the LinkedIn redirect URL impossible to miss
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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() {
|
||||
<ShieldCheck size={18} />
|
||||
LinkedIn OAuth
|
||||
</h3>
|
||||
<p style={{ margin: "4px 0 0", color: "#475569", fontSize: 13 }}>
|
||||
|
||||
{/* 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. */}
|
||||
<div
|
||||
style={{
|
||||
marginTop: 10,
|
||||
padding: "10px 12px",
|
||||
borderRadius: 6,
|
||||
border: "1px solid #fde68a",
|
||||
background: "#fffbeb",
|
||||
}}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
margin: 0,
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
color: "#92400e",
|
||||
}}
|
||||
>
|
||||
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.
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
marginTop: 6,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<code
|
||||
style={{
|
||||
fontSize: 12,
|
||||
wordBreak: "break-all",
|
||||
background: "#fff",
|
||||
border: "1px solid #fde68a",
|
||||
borderRadius: 4,
|
||||
padding: "3px 6px",
|
||||
}}
|
||||
>
|
||||
{linkedinStatus.redirect_uri}
|
||||
</code>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<Copy size={12} />}
|
||||
onClick={() =>
|
||||
handleCopyLinkedInRedirectUri(linkedinStatus.redirect_uri)
|
||||
}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p style={{ margin: "10px 0 0", color: "#475569", fontSize: 13 }}>
|
||||
{linkedinStatusLine(linkedinStatus)}
|
||||
</p>
|
||||
{linkedinStatus.missing_step && (
|
||||
@@ -487,17 +562,6 @@ function MCPPage() {
|
||||
linkedinStatus.linkedin_image_post_enabled,
|
||||
)}
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
margin: "10px 0 0",
|
||||
color: "#64748b",
|
||||
fontSize: 12,
|
||||
wordBreak: "break-all",
|
||||
}}
|
||||
>
|
||||
Add this callback URL in LinkedIn Developer Portal:{" "}
|
||||
<code>{linkedinStatus.redirect_uri}</code>
|
||||
</p>
|
||||
{linkedinStatus.scope.length > 0 && (
|
||||
<p
|
||||
style={{
|
||||
|
||||
Reference in New Issue
Block a user