> ## Documentation Index
> Fetch the complete documentation index at: https://base-a060aa97-fix-dead-service-links.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Started with Base MCP

> Connect Base MCP to your agent in under 5 minutes

export const TruncatedPrompt = ({description, children}) => {
  const extractText = node => {
    if (node == null || typeof node === "boolean") return "";
    if (typeof node === "string" || typeof node === "number") return String(node);
    if (Array.isArray(node)) return node.map(extractText).join("");
    if (typeof node === "object" && node.props) {
      const inner = extractText(node.props.children);
      if (node.type === "code") return "`" + inner + "`";
      return inner;
    }
    return "";
  };
  const text = extractText(children).replace(/\s+/g, " ").trim();
  const [expanded, setExpanded] = useState(false);
  const [copied, setCopied] = useState(false);
  const timeoutRef = useRef(null);
  useEffect(() => () => {
    if (timeoutRef.current) clearTimeout(timeoutRef.current);
  }, []);
  const handleCopy = () => {
    navigator.clipboard.writeText(text).then(() => {
      setCopied(true);
      if (timeoutRef.current) clearTimeout(timeoutRef.current);
      timeoutRef.current = setTimeout(() => setCopied(false), 1500);
    }).catch(() => {});
  };
  const mono = "ui-monospace,'SF Mono','Cascadia Code',Menlo,Monaco,Consolas,monospace";
  const sans = "ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,sans-serif";
  return <div style={{
    margin: "16px 0",
    borderRadius: 10,
    border: "1px solid rgba(125,125,125,0.25)",
    background: "rgba(125,125,125,0.06)",
    overflow: "hidden"
  }}>
      {description && <div style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    padding: "8px 12px",
    borderBottom: "1px solid rgba(125,125,125,0.18)",
    fontFamily: sans,
    fontSize: 12,
    fontWeight: 600,
    color: "#6b7280",
    letterSpacing: "0.02em"
  }}>
          <span>{description}</span>
        </div>}

      <div style={{
    display: "flex",
    alignItems: "flex-start",
    gap: 8,
    padding: "10px 12px"
  }}>
        <button onClick={() => setExpanded(e => !e)} aria-label={expanded ? "Collapse prompt" : "Expand prompt"} aria-expanded={expanded} style={{
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    width: 18,
    height: 18,
    marginTop: 2,
    flexShrink: 0,
    background: "transparent",
    border: "none",
    cursor: "pointer",
    padding: 0,
    color: "#6b7280"
  }}>
          <svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor" style={{
    transform: expanded ? "rotate(90deg)" : "rotate(0deg)",
    transition: "transform 0.15s ease"
  }}>
            <path d="M8 5l8 7-8 7V5z" />
          </svg>
        </button>

        <code onClick={() => setExpanded(e => !e)} style={{
    flex: 1,
    minWidth: 0,
    fontFamily: mono,
    fontSize: 13,
    lineHeight: 1.55,
    cursor: "pointer",
    whiteSpace: expanded ? "pre-wrap" : "nowrap",
    overflow: expanded ? "visible" : "hidden",
    textOverflow: expanded ? "clip" : "ellipsis",
    wordBreak: expanded ? "break-word" : "normal"
  }}>
          {text}
        </code>

        <button onClick={handleCopy} aria-label={copied ? "Copied" : "Copy prompt"} style={{
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    width: 24,
    height: 24,
    flexShrink: 0,
    background: "transparent",
    border: "1px solid rgba(125,125,125,0.25)",
    borderRadius: 6,
    cursor: "pointer",
    color: copied ? "#22c55e" : "#6b7280",
    transition: "color 0.15s ease, border-color 0.15s ease"
  }}>
          {copied ? <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M20 6 9 17l-5-5" />
            </svg> : <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <rect x="9" y="9" width="11" height="11" rx="2" />
              <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
            </svg>}
        </button>
      </div>
    </div>;
};

export const AuthApprovalDemo = ({client = "Claude"}) => {
  const sans = "ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,sans-serif";
  const c = {
    bg: "#0a0a0a",
    cardBorder: "#1c1c1c",
    rowBorder: "#1f1f1f",
    text: "#ffffff",
    muted: "#9a9a9a",
    dim: "#6b6b6b",
    accent: "#a796f7",
    accentRing: "#c8bcff",
    btnDark: "#1c1c1c",
    iconBlue: "#2a64ff",
    iconBlue2: "#5b8eff",
    permIconBg: "#1c1c1c",
    permIconFg: "#d2d2d2"
  };
  const permissions = [{
    label: "View address, balances & activity."
  }, {
    label: "Prepare transactions for you to review."
  }];
  const EyeIcon = () => <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke={c.permIconFg} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z" />
      <circle cx="12" cy="12" r="3" />
    </svg>;
  const SendIcon = () => <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke={c.permIconFg} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M22 2 11 13" /><path d="M22 2l-7 20-4-9-9-4 20-7z" />
    </svg>;
  const SignIcon = () => <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke={c.permIconFg} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" />
    </svg>;
  const ChainIcon = () => <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke={c.permIconFg} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M10 13a5 5 0 0 0 7.5.5l3-3a5 5 0 0 0-7-7l-1.5 1.5" />
      <path d="M14 11a5 5 0 0 0-7-.5l-3 3a5 5 0 0 0 7 7l1.5-1.5" />
    </svg>;
  const icons = [<EyeIcon />, <SendIcon />, <SignIcon />, <ChainIcon />];
  return <div style={{
    margin: "28px 0",
    fontFamily: sans
  }}>
      <style>{`
        .aad-card  { max-width: 420px; margin: 0 auto; }
        .aad-title { font-size: 23px; line-height: 1.22; letter-spacing: -0.01em; }
        .aad-body  { padding: 28px 24px 36px; }
        .aad-perm-list { display: flex; flex-direction: column; gap: 16px; margin-bottom: 96px; }
        .aad-foot  { padding: 0 20px 22px; }
        .aad-btn   { font-size: 15px; font-weight: 700; padding: 14px 0; border-radius: 12px; }

        @media (max-width: 540px) {
          .aad-card  { max-width: 100%; }
          .aad-title { font-size: 19px; }
          .aad-body  { padding: 22px 18px 28px; }
          .aad-perm-list { gap: 14px; margin-bottom: 72px; }
          .aad-foot  { padding: 0 16px 18px; }
          .aad-btn   { font-size: 14px; padding: 12px 0; }
          .aad-perm-text { font-size: 14px !important; }
          .aad-signed { padding: 14px 18px !important; font-size: 13px !important; }
        }
      `}</style>

      <div className="aad-card" style={{
    background: c.bg,
    border: `1px solid ${c.cardBorder}`,
    borderRadius: 18,
    color: c.text,
    boxShadow: "0 16px 48px rgba(0,0,0,0.45)",
    overflow: "hidden"
  }}>
        {}
        <div className="aad-signed" style={{
    display: "flex",
    alignItems: "center",
    gap: 12,
    padding: "16px 22px",
    borderBottom: `1px solid ${c.rowBorder}`,
    fontSize: 14
  }}>
          <span style={{
    display: "inline-block",
    width: 14,
    height: 14,
    background: "#fff",
    borderRadius: 3,
    flexShrink: 0
  }} />
          <span style={{
    color: c.muted,
    overflow: "hidden",
    textOverflow: "ellipsis",
    whiteSpace: "nowrap"
  }}>
            Signed in as <span style={{
    color: c.text,
    fontWeight: 700
  }}>0x71Dc…7244</span>
          </span>
        </div>

        {}
        <div className="aad-body">
          {}
          <div style={{
    display: "flex",
    marginBottom: 22
  }}>
            <div style={{
    width: 56,
    height: 56,
    borderRadius: "50%",
    background: `radial-gradient(circle at 30% 30%, ${c.iconBlue2}, ${c.iconBlue})`,
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    boxShadow: `0 0 0 3px ${c.bg}`,
    position: "relative",
    zIndex: 2
  }}>
              <svg viewBox="0 0 32 32" width="34" height="34" fill="#fff">
                <path d="M16 5c-2.4 0-4.4 1-5.7 2.7-.7-.5-1.7-.7-2.6-.4-1.4.4-2.2 1.9-1.8 3.3.1.3.2.6.4.8C5 12.1 4 13.5 4 15.4c0 1.9 1 3.4 2.3 4.1-.2.2-.3.5-.4.8-.4 1.4.4 2.9 1.8 3.3.9.3 1.9.1 2.6-.4C11.6 24.9 13.6 26 16 26s4.4-1.1 5.7-2.8c.7.5 1.7.7 2.6.4 1.4-.4 2.2-1.9 1.8-3.3-.1-.3-.2-.6-.4-.8 1.3-.7 2.3-2.2 2.3-4.1 0-1.9-1-3.3-2.3-4 .2-.3.3-.5.4-.8.4-1.4-.4-2.9-1.8-3.3-.9-.3-1.9-.1-2.6.4C20.4 6 18.4 5 16 5z" />
                <circle cx="12.5" cy="16" r="2" fill={c.iconBlue} />
                <circle cx="19.5" cy="16" r="2" fill={c.iconBlue} />
              </svg>
            </div>
            <div style={{
    width: 56,
    height: 56,
    borderRadius: "50%",
    background: c.iconBlue,
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    marginLeft: -14,
    boxShadow: `0 0 0 3px ${c.bg}`
  }}>
              <svg viewBox="0 0 24 24" width="30" height="30" fill="none">
                <rect x="3" y="6" width="18" height="13" rx="2.5" fill="#fff" opacity="0.95" />
                <rect x="3" y="9" width="18" height="2.5" fill={c.iconBlue} />
              </svg>
            </div>
          </div>

          <h3 className="aad-title" style={{
    fontWeight: 700,
    margin: "0 0 10px 0",
    color: c.text
  }}>
            Allow {client} to access your account.
          </h3>

          <p style={{
    fontSize: 14,
    color: c.muted,
    margin: "0 0 26px 0",
    lineHeight: 1.5
  }}>
            By continuing, you allow {client} to:
          </p>

          <div className="aad-perm-list">
            {permissions.map((p, i) => <div key={i} style={{
    display: "flex",
    alignItems: "center",
    gap: 14
  }}>
                <div style={{
    width: 36,
    height: 36,
    borderRadius: "50%",
    background: c.permIconBg,
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    flexShrink: 0
  }}>
                  {icons[i]}
                </div>
                <span className="aad-perm-text" style={{
    fontSize: 15,
    color: c.text,
    lineHeight: 1.4
  }}>
                  {p.label}
                </span>
              </div>)}
          </div>
        </div>

        <div className="aad-foot" style={{
    display: "flex",
    gap: 10,
    position: "relative"
  }}>
          <button className="aad-btn" style={{
    flex: 1,
    background: c.btnDark,
    border: "none",
    color: c.text,
    cursor: "default",
    fontFamily: sans
  }}>Learn More</button>
          <button className="aad-btn" style={{
    flex: 1,
    background: c.accent,
    border: `2px solid ${c.accentRing}`,
    color: "#0a0a0a",
    cursor: "default",
    fontFamily: sans,
    boxShadow: "0 0 0 1px rgba(167,150,247,0.3)"
  }}>Allow</button>
          <span style={{
    position: "absolute",
    right: 12,
    bottom: -2,
    color: c.accent,
    opacity: 0.8,
    display: "inline-flex",
    padding: 4
  }}>
            <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <circle cx="12" cy="12" r="3" />
              <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
            </svg>
          </span>
        </div>
      </div>

      <div style={{
    textAlign: "center",
    marginTop: 12,
    fontFamily: sans,
    fontSize: 12,
    color: c.dim
  }}>
        Preview · Shown in <span style={{
    color: c.muted
  }}>Base Account</span> on first wallet-tool use
      </div>
    </div>;
};

export const WalletSetupDemo = () => {
  const sans = "ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,sans-serif";
  const serif = "'Tiempos Headline','Iowan Old Style','Source Serif Pro',ui-serif,Georgia,serif";
  const mono = "ui-monospace,'SF Mono','Cascadia Code',Menlo,Monaco,Consolas,monospace";
  const c = {
    bg: "#1f1e1d",
    header: "#262624",
    border: "#34322f",
    inputBg: "#2a2926",
    text: "#f5f4ed",
    body: "#e8e4dc",
    muted: "#a8a39d",
    dim: "#6b6663",
    accent: "#D97757",
    bubble: "#2c2b28",
    bubbleText: "#f5f4ed",
    code: "#e89972",
    codeBg: "rgba(217,119,87,0.12)",
    toolBg: "#272622",
    toolBorder: "#3a3835",
    success: "#a3c585"
  };
  const ACCENT = "#D97757";
  const tokenBg = ticker => {
    if (!ticker) return ACCENT;
    const t = ticker.toUpperCase();
    if (t === "USDC") return "#2775CA";
    if (t === "ETH" || t === "WETH") return "#627EEA";
    if (t === "CBBTC" || t === "BTC") return "#F7931A";
    if (t === "DEGEN") return "#A06CFF";
    if (t === "POL") return "#8247E5";
    return ACCENT;
  };
  const tokenGlow = ticker => {
    if (!ticker) return "rgba(217,119,87,0.14)";
    const t = ticker.toUpperCase();
    if (t === "USDC") return "rgba(39,117,202,0.14)";
    if (t === "ETH" || t === "WETH") return "rgba(98,126,234,0.14)";
    if (t === "CBBTC" || t === "BTC") return "rgba(247,147,26,0.14)";
    if (t === "DEGEN") return "rgba(160,108,255,0.14)";
    return "rgba(217,119,87,0.14)";
  };
  const BigTokenAvatar = ({ticker}) => <div style={{
    width: 46,
    height: 46,
    borderRadius: "50%",
    background: tokenBg(ticker),
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    border: "1.5px solid rgba(255,255,255,0.10)",
    boxShadow: `0 0 0 5px ${tokenGlow(ticker)}`,
    flexShrink: 0
  }}>
      <span style={{
    fontFamily: sans,
    fontSize: 12,
    fontWeight: 800,
    color: "#fff",
    letterSpacing: "-0.4px"
  }}>
        {(ticker || "??").slice(0, 2).toUpperCase()}
      </span>
    </div>;
  const SmallTokenAvatar = ({ticker}) => <div style={{
    width: 30,
    height: 30,
    borderRadius: "50%",
    background: tokenBg(ticker),
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    border: "1.5px solid rgba(255,255,255,0.08)",
    flexShrink: 0
  }}>
      <span style={{
    fontFamily: sans,
    fontSize: 9,
    fontWeight: 800,
    color: "#fff",
    letterSpacing: "-0.2px"
  }}>
        {(ticker || "??").slice(0, 2).toUpperCase()}
      </span>
    </div>;
  const CBAvatar = () => <div style={{
    width: 20,
    height: 20,
    borderRadius: "50%",
    flexShrink: 0,
    background: "radial-gradient(circle at 35% 30%, #5d8cff 0%, #2949d8 80%)",
    position: "relative",
    overflow: "hidden"
  }}>
      <span style={{
    position: "absolute",
    top: 6,
    left: 5,
    width: 3,
    height: 3.5,
    borderRadius: "50%",
    background: "#fff"
  }} />
      <span style={{
    position: "absolute",
    top: 6,
    right: 5,
    width: 3,
    height: 3.5,
    borderRadius: "50%",
    background: "#fff"
  }} />
      <span style={{
    position: "absolute",
    bottom: 3.5,
    left: "50%",
    transform: "translateX(-50%)",
    width: 3.5,
    height: 4,
    borderRadius: "50%",
    background: "#1a1208"
  }} />
    </div>;
  const SignAvatar = () => <div style={{
    width: 46,
    height: 46,
    borderRadius: "50%",
    background: "linear-gradient(135deg, #a796f7 0%, #7c5ae8 100%)",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    border: "1.5px solid rgba(255,255,255,0.10)",
    boxShadow: "0 0 0 5px rgba(167,150,247,0.14)",
    flexShrink: 0
  }}>
      <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        <path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" />
      </svg>
    </div>;
  const ApprovalButton = ({preview, onApprove, label}) => {
    const [hover, setHover] = useState(false);
    return <div style={{
      marginBottom: 10,
      marginTop: 4
    }}>
        <button onClick={() => onApprove(preview)} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} style={{
      display: "inline-flex",
      alignItems: "center",
      gap: 8,
      background: hover ? "rgba(217,119,87,0.18)" : "rgba(217,119,87,0.10)",
      border: `1px solid ${ACCENT}`,
      borderRadius: 8,
      padding: "9px 14px",
      cursor: "pointer",
      color: ACCENT,
      fontFamily: sans,
      fontSize: 13.5,
      fontWeight: 600,
      boxShadow: hover ? `0 0 0 3px rgba(217,119,87,0.18)` : `0 0 0 3px rgba(217,119,87,0.08)`,
      transition: "all 0.15s ease"
    }}>
          <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke={ACCENT} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
      flexShrink: 0
    }}>
            <rect x="3" y="11" width="18" height="11" rx="2" />
            <path d="M7 11V7a5 5 0 0 1 10 0v4" />
          </svg>
          {label || (preview && preview.type && preview.type.startsWith("sign") ? "Approve Signature" : "Approve Transaction")}
        </button>
      </div>;
  };
  const TxModal = ({preview, onConfirm, onCancel}) => {
    const mbg = "#0a0a0a";
    const mcard = "#1a1816";
    const mhair = "#1f1d1b";
    const mwhite = "#ffffff";
    const mvalue = "#a09b95";
    const msub = "#7a7470";
    const isSign = preview.type && preview.type.startsWith("sign");
    const renderPreview = () => {
      if (preview.type === "send") return <div style={{
        padding: "16px 16px 14px",
        textAlign: "center"
      }}>
          <div style={{
        display: "flex",
        justifyContent: "center",
        marginBottom: 10
      }}>
            <BigTokenAvatar ticker={preview.asset} />
          </div>
          <div style={{
        fontFamily: sans,
        fontSize: 20,
        fontWeight: 700,
        color: mwhite,
        lineHeight: 1.1,
        letterSpacing: "-0.4px"
      }}>
            {preview.amount} {preview.asset}
          </div>
          {preview.usdValue && <div style={{
        fontFamily: sans,
        fontSize: 12,
        color: msub,
        marginTop: 3
      }}>
              {preview.usdValue}
            </div>}
          <div style={{
        height: 1,
        background: mhair,
        margin: "12px 0 10px"
      }} />
          <div style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between"
      }}>
            <span style={{
        fontFamily: sans,
        fontSize: 13,
        fontWeight: 500,
        color: mwhite
      }}>To</span>
            <span style={{
        fontFamily: sans,
        fontSize: 13,
        color: mvalue
      }}>{preview.to}</span>
          </div>
        </div>;
      if (preview.type === "swap") return <div>
          <div style={{
        display: "flex",
        alignItems: "center",
        gap: 10,
        padding: "12px 16px"
      }}>
            <SmallTokenAvatar ticker={preview.fromAsset} />
            <div style={{
        flex: 1
      }}>
              <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub,
        marginBottom: 1
      }}>You send</div>
              <div style={{
        fontFamily: sans,
        fontSize: 15,
        fontWeight: 700,
        color: mwhite,
        letterSpacing: "-0.2px"
      }}>
                {preview.fromAmount} {preview.fromAsset}
              </div>
            </div>
            {preview.fromUsd && <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub
      }}>{preview.fromUsd}</div>}
          </div>
          <div style={{
        display: "flex",
        justifyContent: "center",
        height: 0
      }}>
            <div style={{
        width: 22,
        height: 22,
        borderRadius: "50%",
        background: mbg,
        border: `1px solid ${mhair}`,
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        marginTop: -11,
        position: "relative",
        zIndex: 2
      }}>
              <svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke={mvalue} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M12 5v14M5 12l7 7 7-7" />
              </svg>
            </div>
          </div>
          <div style={{
        display: "flex",
        alignItems: "center",
        gap: 10,
        padding: "12px 16px",
        borderTop: `1px solid ${mhair}`
      }}>
            <SmallTokenAvatar ticker={preview.toAsset} />
            <div style={{
        flex: 1
      }}>
              <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub,
        marginBottom: 1
      }}>You receive</div>
              <div style={{
        fontFamily: sans,
        fontSize: 15,
        fontWeight: 700,
        color: "#a3c585",
        letterSpacing: "-0.2px"
      }}>
                {preview.toAmount} {preview.toAsset}
              </div>
            </div>
            {preview.toUsd && <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub
      }}>{preview.toUsd}</div>}
          </div>
        </div>;
      if (preview.type === "deposit") return <div>
          <div style={{
        display: "flex",
        alignItems: "center",
        gap: 10,
        padding: "12px 16px"
      }}>
            <SmallTokenAvatar ticker={preview.asset} />
            <div style={{
        flex: 1
      }}>
              <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub,
        marginBottom: 1
      }}>You deposit</div>
              <div style={{
        fontFamily: sans,
        fontSize: 15,
        fontWeight: 700,
        color: mwhite,
        letterSpacing: "-0.2px"
      }}>
                {preview.amount} {preview.asset}
              </div>
            </div>
            {preview.usdValue && <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub
      }}>{preview.usdValue}</div>}
          </div>
          <div style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between",
        padding: "11px 16px",
        borderTop: `1px solid ${mhair}`
      }}>
            <span style={{
        fontFamily: sans,
        fontSize: 13,
        fontWeight: 500,
        color: mwhite
      }}>Into</span>
            <div style={{
        textAlign: "right"
      }}>
              <div style={{
        fontFamily: sans,
        fontSize: 13,
        color: mvalue
      }}>{preview.vault}</div>
              {preview.apy && <div style={{
        fontFamily: sans,
        fontSize: 11,
        color: "#a3c585",
        marginTop: 1,
        fontWeight: 600
      }}>{preview.apy} APY</div>}
            </div>
          </div>
        </div>;
      if (preview.type === "borrow") return <div>
          <div style={{
        display: "flex",
        alignItems: "center",
        gap: 10,
        padding: "12px 16px"
      }}>
            <SmallTokenAvatar ticker={preview.collateralAsset} />
            <div style={{
        flex: 1
      }}>
              <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub,
        marginBottom: 1
      }}>Supply collateral</div>
              <div style={{
        fontFamily: sans,
        fontSize: 15,
        fontWeight: 700,
        color: mwhite,
        letterSpacing: "-0.2px"
      }}>
                {preview.collateralAmount} {preview.collateralAsset}
              </div>
            </div>
          </div>
          <div style={{
        display: "flex",
        alignItems: "center",
        gap: 10,
        padding: "12px 16px",
        borderTop: `1px solid ${mhair}`
      }}>
            <SmallTokenAvatar ticker={preview.loanAsset} />
            <div style={{
        flex: 1
      }}>
              <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub,
        marginBottom: 1
      }}>You borrow</div>
              <div style={{
        fontFamily: sans,
        fontSize: 15,
        fontWeight: 700,
        color: "#a3c585",
        letterSpacing: "-0.2px"
      }}>
                {preview.loanAmount} {preview.loanAsset}
              </div>
            </div>
          </div>
          {preview.healthFactor && <div style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between",
        padding: "10px 16px",
        borderTop: `1px solid ${mhair}`
      }}>
              <span style={{
        fontFamily: sans,
        fontSize: 12.5,
        color: msub
      }}>Health factor</span>
              <span style={{
        fontFamily: sans,
        fontSize: 13,
        color: "#a3c585",
        fontWeight: 600
      }}>{preview.healthFactor}</span>
            </div>}
        </div>;
      if (preview.type === "repay") return <div>
          <div style={{
        display: "flex",
        alignItems: "center",
        gap: 10,
        padding: "12px 16px"
      }}>
            <SmallTokenAvatar ticker={preview.asset} />
            <div style={{
        flex: 1
      }}>
              <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub,
        marginBottom: 1
      }}>You repay</div>
              <div style={{
        fontFamily: sans,
        fontSize: 15,
        fontWeight: 700,
        color: mwhite,
        letterSpacing: "-0.2px"
      }}>
                {preview.amount} {preview.asset}
              </div>
            </div>
            {preview.usdValue && <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub
      }}>{preview.usdValue}</div>}
          </div>
          <div style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between",
        padding: "11px 16px",
        borderTop: `1px solid ${mhair}`
      }}>
            <span style={{
        fontFamily: sans,
        fontSize: 13,
        fontWeight: 500,
        color: mwhite
      }}>To market</span>
            <span style={{
        fontFamily: sans,
        fontSize: 13,
        color: mvalue
      }}>{preview.market}</span>
          </div>
        </div>;
      if (preview.type === "sign-message") return <div style={{
        padding: "16px 16px 14px",
        textAlign: "center"
      }}>
          <div style={{
        display: "flex",
        justifyContent: "center",
        marginBottom: 10
      }}>
            <SignAvatar />
          </div>
          <div style={{
        fontFamily: sans,
        fontSize: 16,
        fontWeight: 700,
        color: mwhite,
        letterSpacing: "-0.3px"
      }}>
            Sign message
          </div>
          <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub,
        marginTop: 3
      }}>
            personal_sign
          </div>
          <div style={{
        marginTop: 12,
        padding: "10px 12px",
        background: "rgba(255,255,255,0.04)",
        border: `1px solid ${mhair}`,
        borderRadius: 8,
        textAlign: "left",
        fontFamily: mono,
        fontSize: 12,
        color: mvalue,
        lineHeight: 1.45,
        wordBreak: "break-word"
      }}>
            "{preview.message}"
          </div>
        </div>;
      if (preview.type === "sign-siwe") return <div style={{
        padding: "16px 16px 14px",
        textAlign: "center"
      }}>
          <div style={{
        display: "flex",
        justifyContent: "center",
        marginBottom: 10
      }}>
            <SignAvatar />
          </div>
          <div style={{
        fontFamily: sans,
        fontSize: 16,
        fontWeight: 700,
        color: mwhite,
        letterSpacing: "-0.3px"
      }}>
            Sign in with Ethereum
          </div>
          <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub,
        marginTop: 3
      }}>
            EIP-4361 · session login
          </div>
          <div style={{
        height: 1,
        background: mhair,
        margin: "12px 0 10px"
      }} />
          <div style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between"
      }}>
            <span style={{
        fontFamily: sans,
        fontSize: 13,
        fontWeight: 500,
        color: mwhite
      }}>Domain</span>
            <span style={{
        fontFamily: sans,
        fontSize: 13,
        color: mvalue
      }}>{preview.domain}</span>
          </div>
        </div>;
      if (preview.type === "sign-permit") return <div style={{
        padding: "16px 16px 14px",
        textAlign: "center"
      }}>
          <div style={{
        display: "flex",
        justifyContent: "center",
        marginBottom: 10
      }}>
            <SignAvatar />
          </div>
          <div style={{
        fontFamily: sans,
        fontSize: 16,
        fontWeight: 700,
        color: mwhite,
        letterSpacing: "-0.3px"
      }}>
            Approve token spending
          </div>
          <div style={{
        fontFamily: sans,
        fontSize: 11.5,
        color: msub,
        marginTop: 3
      }}>
            EIP-712 · Permit2
          </div>
          <div style={{
        height: 1,
        background: mhair,
        margin: "12px 0 8px"
      }} />
          <div style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between",
        padding: "4px 0"
      }}>
            <span style={{
        fontFamily: sans,
        fontSize: 12.5,
        color: mwhite
      }}>Token</span>
            <div style={{
        display: "flex",
        alignItems: "center",
        gap: 6
      }}>
              <SmallTokenAvatar ticker={preview.token} />
              <span style={{
        fontFamily: sans,
        fontSize: 13,
        color: mvalue
      }}>{preview.token}</span>
            </div>
          </div>
          <div style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between",
        padding: "4px 0"
      }}>
            <span style={{
        fontFamily: sans,
        fontSize: 12.5,
        color: mwhite
      }}>Spender</span>
            <span style={{
        fontFamily: sans,
        fontSize: 13,
        color: mvalue
      }}>{preview.spender}</span>
          </div>
          {preview.amount && <div style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between",
        padding: "4px 0"
      }}>
              <span style={{
        fontFamily: sans,
        fontSize: 12.5,
        color: mwhite
      }}>Allowance</span>
              <span style={{
        fontFamily: sans,
        fontSize: 13,
        color: mvalue
      }}>{preview.amount}</span>
            </div>}
        </div>;
      return null;
    };
    const FieldRow = ({label, right}) => <div style={{
      display: "flex",
      alignItems: "center",
      justifyContent: "space-between",
      padding: "10px 16px"
    }}>
        <span style={{
      fontFamily: sans,
      fontSize: 13.5,
      fontWeight: 500,
      color: mwhite
    }}>{label}</span>
        <div style={{
      display: "flex",
      alignItems: "center",
      gap: 6
    }}>{right}</div>
      </div>;
    return <div onClick={onCancel} style={{
      position: "absolute",
      top: 0,
      left: 0,
      right: 0,
      bottom: 0,
      zIndex: 50,
      background: "rgba(0,0,0,0.78)",
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      backdropFilter: "blur(3px)",
      padding: 14
    }}>
        <div onClick={e => e.stopPropagation()} style={{
      background: mbg,
      borderRadius: 16,
      border: `1px solid #1f1d1b`,
      width: 320,
      maxWidth: "100%",
      maxHeight: "calc(100% - 8px)",
      overflowY: "auto",
      boxShadow: "0 24px 80px rgba(0,0,0,0.85)"
    }}>
          {}
          <div style={{
      display: "flex",
      alignItems: "center",
      justifyContent: "space-between",
      padding: "14px 16px 12px",
      borderBottom: `1px solid ${mhair}`
    }}>
            <span style={{
      fontFamily: sans,
      fontSize: 17,
      fontWeight: 700,
      color: mwhite,
      letterSpacing: "-0.3px"
    }}>
              {isSign ? "Sign" : "Review"}
            </span>
            <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="#d4d0ca" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" />
              <circle cx="12" cy="12" r="3" />
            </svg>
          </div>

          {}
          <div style={{
      display: "flex",
      alignItems: "center",
      gap: 6,
      padding: "6px 16px",
      background: "rgba(217,119,87,0.10)",
      borderBottom: `1px solid rgba(217,119,87,0.18)`
    }}>
            <svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke={ACCENT} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" style={{
      flexShrink: 0
    }}>
              <circle cx="12" cy="12" r="10" /><path d="M12 8v4" /><path d="M12 16h.01" />
            </svg>
            <span style={{
      fontFamily: sans,
      fontSize: 10.5,
      color: ACCENT,
      fontWeight: 700,
      letterSpacing: "0.3px",
      whiteSpace: "nowrap"
    }}>
              DEMO · Not a real {isSign ? "signature" : "transaction"}
            </span>
          </div>

          {}
          <div style={{
      background: mcard,
      borderBottom: `1px solid ${mhair}`
    }}>
            {renderPreview()}
          </div>

          {}
          <div style={{
      padding: "4px 0"
    }}>
            <FieldRow label="Signing with" right={<>
                  <CBAvatar />
                  <span style={{
      fontFamily: sans,
      fontSize: 13,
      color: mvalue
    }}>0x71Dc…7244</span>
                </>} />
            {!isSign && <FieldRow label="Payment methods" right={<>
                    <CBAvatar />
                    <span style={{
      fontFamily: sans,
      fontSize: 13,
      color: mvalue
    }}>0x71Dc…7244</span>
                    <svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke={msub} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{
      marginLeft: 1
    }}><path d="m9 18 6-6-6-6" /></svg>
                  </>} />}
            <FieldRow label="Network" right={<>
                  <div style={{
      width: 16,
      height: 16,
      borderRadius: 4,
      background: "#0052FF",
      flexShrink: 0
    }} />
                  <span style={{
      fontFamily: sans,
      fontSize: 13,
      color: mvalue
    }}>Base</span>
                </>} />
            {!isSign && <FieldRow label="Network fee (est.)" right={<span style={{
      fontFamily: sans,
      fontSize: 13,
      color: mvalue
    }}>{"< $0.01"}</span>} />}
          </div>

          {}
          <div style={{
      display: "flex",
      gap: 8,
      padding: "12px 16px 16px"
    }}>
            <button onClick={onCancel} onMouseEnter={e => {
      e.currentTarget.style.background = "#3a3835";
    }} onMouseLeave={e => {
      e.currentTarget.style.background = "#2a2826";
    }} style={{
      flex: 1,
      padding: "12px 0",
      background: "#2a2826",
      border: "none",
      borderRadius: 12,
      cursor: "pointer",
      fontFamily: sans,
      fontSize: 14,
      fontWeight: 700,
      color: "#ffffff",
      transition: "background 0.15s ease"
    }}>Cancel</button>
            <button onClick={onConfirm} onMouseEnter={e => {
      e.currentTarget.style.background = "#1a4fd6";
    }} onMouseLeave={e => {
      e.currentTarget.style.background = "#0052FF";
    }} style={{
      flex: 1,
      padding: "12px 0",
      background: "#0052FF",
      border: "none",
      borderRadius: 12,
      cursor: "pointer",
      fontFamily: sans,
      fontSize: 14,
      fontWeight: 700,
      color: "#fff",
      transition: "background 0.15s ease"
    }}>Confirm</button>
          </div>
        </div>
      </div>;
  };
  const examples = [{
    prompt: "Send 5 USDC to jesse.base.eth",
    events: [{
      delay: 380,
      type: "thinking"
    }, {
      delay: 600,
      type: "tool",
      tool: {
        server: "base-mcp",
        action: "send",
        args: {
          recipient: "jesse.base.eth",
          asset: "USDC",
          amount: "5",
          chain: "base"
        }
      }
    }, {
      delay: 550,
      type: "text",
      text: "Resolved jesse.base.eth → 0xd8dA…6045. Approve to send:"
    }, {
      delay: 250,
      type: "approval",
      preview: {
        type: "send",
        asset: "USDC",
        amount: "5",
        usdValue: "$5.00",
        to: "jesse.base.eth"
      }
    }, {
      delay: 1100,
      type: "confirm",
      text: "Sent 5 USDC to jesse.base.eth"
    }]
  }, {
    prompt: "Swap 0.05 ETH to USDC on Base",
    events: [{
      delay: 380,
      type: "thinking"
    }, {
      delay: 600,
      type: "tool",
      tool: {
        server: "base-mcp",
        action: "swap",
        args: {
          fromAsset: "ETH",
          toAsset: "USDC",
          amount: "0.05",
          chain: "base"
        }
      }
    }, {
      delay: 600,
      type: "text",
      text: "Swap request ready: estimated 179.41 USDC. Approve to swap:"
    }, {
      delay: 450,
      type: "approval",
      preview: {
        type: "swap",
        fromAsset: "ETH",
        fromAmount: "0.05",
        fromUsd: "~$179.50",
        toAsset: "USDC",
        toAmount: "179.41",
        toUsd: "~$179.41"
      }
    }, {
      delay: 1100,
      type: "confirm",
      text: "Swapped 0.05 ETH → 179.41 USDC"
    }]
  }, {
    prompt: "Find the best USDC yield on Base and deposit 100",
    events: [{
      delay: 380,
      type: "thinking"
    }, {
      delay: 600,
      type: "tool",
      tool: {
        server: "morpho",
        action: "query_vaults",
        args: {
          chain: "base",
          asset: "USDC",
          sort: "apy_desc"
        }
      }
    }, {
      delay: 700,
      type: "tool",
      tool: {
        server: "moonwell",
        action: "list_markets",
        args: {
          asset: "USDC",
          chain: "base"
        }
      }
    }, {
      delay: 600,
      type: "text",
      text: "Compared Morpho vaults and Moonwell markets — top USDC yields on Base:"
    }, {
      delay: 250,
      type: "rows",
      rows: [{
        token: "Morpho · Steakhouse USDC",
        amount: "8.42% APY",
        value: "$24.1M TVL · winner"
      }, {
        token: "Morpho · Re7 USDC",
        amount: "7.91% APY",
        value: "$18.7M TVL"
      }, {
        token: "Moonwell · USDC market",
        amount: "5.13% APY",
        value: "$41.2M supplied"
      }]
    }, {
      delay: 700,
      type: "tool",
      tool: {
        server: "morpho",
        action: "prepare_deposit",
        args: {
          vault: "Steakhouse USDC",
          amount: "100 USDC"
        }
      }
    }, {
      delay: 500,
      type: "tool",
      tool: {
        server: "base-mcp",
        action: "send_calls",
        args: {
          chain: "base",
          calls: "[approve, deposit]"
        }
      }
    }, {
      delay: 450,
      type: "approval",
      preview: {
        type: "deposit",
        asset: "USDC",
        amount: "100",
        usdValue: "~$100.00",
        vault: "Steakhouse USDC",
        apy: "8.42%"
      }
    }, {
      delay: 1100,
      type: "confirm",
      text: "Deposited 100 USDC into Steakhouse USDC · earning 8.42% APY"
    }]
  }, {
    prompt: "What chains are supported by Base MCP?",
    events: [{
      delay: 380,
      type: "thinking"
    }, {
      delay: 500,
      type: "tool",
      tool: {
        server: "base-mcp",
        action: "get_wallets",
        args: {}
      }
    }, {
      delay: 500,
      type: "text",
      text: "Base MCP currently supports 7 mainnets and 1 testnet:"
    }, {
      delay: 200,
      type: "rows",
      rows: [{
        token: "Base",
        amount: "Mainnet",
        value: ""
      }, {
        token: "Arbitrum",
        amount: "Mainnet",
        value: ""
      }, {
        token: "Optimism",
        amount: "Mainnet",
        value: ""
      }, {
        token: "Polygon",
        amount: "Mainnet",
        value: ""
      }, {
        token: "BNB Chain",
        amount: "Mainnet",
        value: ""
      }, {
        token: "Avalanche",
        amount: "Mainnet",
        value: ""
      }, {
        token: "Ethereum",
        amount: "Mainnet",
        value: ""
      }, {
        token: "Base Sepolia",
        amount: "Testnet",
        value: ""
      }]
    }]
  }];
  const [activeIdx, setActiveIdx] = useState(null);
  const [eventIdx, setEventIdx] = useState(0);
  const [modalPreview, setModalPreview] = useState(null);
  const scrollRef = useRef(null);
  const timersRef = useRef([]);
  const clearTimers = () => {
    timersRef.current.forEach(clearTimeout);
    timersRef.current = [];
  };
  useEffect(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [eventIdx, activeIdx]);
  useEffect(() => () => clearTimers(), []);
  const pick = idx => {
    if (activeIdx !== null) return;
    setActiveIdx(idx);
    setEventIdx(0);
    clearTimers();
    let cumulative = 0;
    const events = examples[idx].events;
    for (let i = 0; i < events.length; i++) {
      cumulative += events[i].delay;
      timersRef.current.push(setTimeout(() => setEventIdx(i + 1), cumulative));
      if (events[i].type === "approval") break;
    }
  };
  const reset = () => {
    clearTimers();
    setActiveIdx(null);
    setEventIdx(0);
    setModalPreview(null);
  };
  const handleConfirm = () => {
    setModalPreview(null);
    clearTimers();
    if (activeIdx !== null) setEventIdx(examples[activeIdx].events.length);
  };
  const ex = activeIdx !== null ? examples[activeIdx] : null;
  const TrafficLights = () => <div style={{
    display: "flex",
    gap: 6,
    marginRight: 14
  }}>
      <span style={{
    width: 11,
    height: 11,
    borderRadius: "50%",
    background: "#ed6a5e",
    display: "inline-block"
  }} />
      <span style={{
    width: 11,
    height: 11,
    borderRadius: "50%",
    background: "#f5bf4f",
    display: "inline-block"
  }} />
      <span style={{
    width: 11,
    height: 11,
    borderRadius: "50%",
    background: "#61c554",
    display: "inline-block"
  }} />
    </div>;
  const UserBubble = ({children}) => <div style={{
    display: "flex",
    justifyContent: "flex-end",
    marginBottom: 20
  }}>
      <div className="wsd-bubble" style={{
    background: c.bubble,
    color: c.bubbleText,
    padding: "12px 16px",
    borderRadius: 14,
    fontFamily: sans,
    lineHeight: 1.45,
    border: `1px solid ${c.toolBorder}`
  }}>{children}</div>
    </div>;
  const ToolCall = ({tool, completed}) => <div style={{
    marginBottom: 10
  }}>
      <div className="wsd-tool-chip" style={{
    display: "inline-flex",
    alignItems: "flex-start",
    gap: 8,
    background: c.toolBg,
    border: `1px solid ${c.toolBorder}`,
    borderRadius: 8,
    padding: "6px 11px",
    opacity: completed ? 0.85 : 1
  }}>
        <span style={{
    width: 14,
    height: 14,
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    flexShrink: 0,
    marginTop: 1
  }}>
          {completed ? <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke={c.success} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6 9 17l-5-5" /></svg> : <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke={c.accent} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M14.7 6.3a4 4 0 0 0-5.4 0l-7 7a3.5 3.5 0 0 0 5 5l5.5-5.5" />
              <path d="m11 8 5 5" />
            </svg>}
        </span>
        <span className="wsd-tool-text" style={{
    fontFamily: mono,
    color: c.muted
  }}>
          <span style={{
    color: c.accent
  }}>{tool.server}</span>
          <span style={{
    color: c.dim
  }}> · </span>
          <span style={{
    color: c.body
  }}>{tool.action}</span>
          <span style={{
    color: c.dim
  }}>(</span>
          {Object.entries(tool.args).map(([k, v], i, arr) => <span key={k}>
              <span style={{
    color: c.muted
  }}>{k}: </span>
              <span style={{
    color: c.code
  }}>"{v}"</span>
              {i < arr.length - 1 && <span style={{
    color: c.dim
  }}>, </span>}
            </span>)}
          <span style={{
    color: c.dim
  }}>)</span>
        </span>
      </div>
    </div>;
  const Thinking = () => <div style={{
    display: "flex",
    alignItems: "center",
    gap: 8,
    marginBottom: 14,
    fontFamily: sans,
    fontSize: 13,
    color: c.muted
  }}>
      <span style={{
    display: "inline-flex",
    gap: 3
  }}>
        {[0, 1, 2].map(i => <span key={i} style={{
    width: 5,
    height: 5,
    borderRadius: "50%",
    background: c.muted,
    opacity: 0.4,
    animation: `wsd-pulse 1.2s infinite ${i * 0.18}s`
  }} />)}
      </span>
      <span style={{
    fontStyle: "italic"
  }}>Thinking</span>
    </div>;
  const ResponseText = ({children, top}) => <div style={{
    fontFamily: serif,
    fontSize: 15,
    lineHeight: 1.55,
    color: c.body,
    marginBottom: 12,
    marginTop: top ? 8 : 0
  }}>{children}</div>;
  const ResponseRows = ({rows}) => <div style={{
    marginBottom: 14
  }}>
      {rows.map((r, i) => <div key={i} className="wsd-row" style={{
    display: "flex",
    alignItems: "baseline",
    padding: "5px 0",
    fontFamily: serif,
    fontSize: 14,
    color: c.body
  }}>
          <span style={{
    minWidth: 12,
    color: c.dim,
    flexShrink: 0
  }}>•</span>
          <span className="wsd-row-token" style={{
    fontWeight: 500
  }}>{r.token}</span>
          <span style={{
    fontFamily: mono,
    fontSize: 12.5,
    color: c.code,
    background: c.codeBg,
    padding: "1px 6px",
    borderRadius: 4,
    whiteSpace: "nowrap"
  }}>{r.amount}</span>
          <span style={{
    color: c.muted,
    fontSize: 13
  }}>{r.value}</span>
        </div>)}
    </div>;
  const Confirm = ({text}) => <div style={{
    fontFamily: serif,
    fontSize: 14,
    color: c.success,
    display: "flex",
    alignItems: "center",
    gap: 8,
    marginTop: 4
  }}>
      <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke={c.success} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
        <path d="M20 6 9 17l-5-5" />
      </svg>
      {text}
    </div>;
  const ChipBtn = ({onClick, children}) => {
    const [hover, setHover] = useState(false);
    return <button onClick={onClick} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} className="wsd-chip" style={{
      fontFamily: serif,
      lineHeight: 1.4,
      color: hover ? c.text : c.body,
      background: hover ? c.toolBg : c.header,
      border: `1px solid ${hover ? c.accent : c.toolBorder}`,
      borderRadius: 14,
      textAlign: "left",
      cursor: "pointer",
      transition: "all 0.15s ease",
      display: "flex",
      alignItems: "center",
      justifyContent: "space-between",
      gap: 14,
      width: "100%"
    }}>
        <span style={{
      flex: 1
    }}>{children}</span>
        <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke={hover ? c.accent : c.dim} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
      flexShrink: 0,
      transition: "stroke 0.15s ease, transform 0.15s ease",
      transform: hover ? "translateX(2px)" : "translateX(0)"
    }}>
          <path d="M5 12h14M13 6l6 6-6 6" />
        </svg>
      </button>;
  };
  const renderEvents = () => {
    if (!ex) return null;
    const shown = ex.events.slice(0, eventIdx);
    return shown.map((event, i) => {
      if (event.type === "thinking") {
        if (i < shown.length - 1) return null;
        return <Thinking key={i} />;
      }
      if (event.type === "tool") {
        const hasLater = shown.slice(i + 1).some(e => e.type !== "thinking");
        return <ToolCall key={i} tool={event.tool} completed={hasLater} />;
      }
      if (event.type === "text") return <ResponseText key={i} top>{event.text}</ResponseText>;
      if (event.type === "rows") return <ResponseRows key={i} rows={event.rows} />;
      if (event.type === "approval") return <ApprovalButton key={i} preview={event.preview} onApprove={setModalPreview} />;
      if (event.type === "confirm") return <Confirm key={i} text={event.text} />;
      return null;
    });
  };
  return <div style={{
    position: "relative",
    margin: "28px 0",
    borderRadius: 14,
    overflow: "hidden",
    border: `1px solid ${c.border}`,
    background: c.bg,
    boxShadow: "0 8px 24px rgba(0,0,0,0.25)"
  }}>
      {}
      <style>{`
        @keyframes wsd-pulse{0%,100%{opacity:0.3;transform:scale(1)}50%{opacity:1;transform:scale(1.3)}}

        .wsd-chat       { height: 400px; padding: 24px 28px 16px; }
        .wsd-input-row  { padding: 10px 16px 14px; }
        .wsd-tool-text  { white-space: nowrap; font-size: 12px; line-height: 1.4; }
        .wsd-tool-chip  { max-width: 100%; }
        .wsd-row        { gap: 12px; flex-wrap: nowrap; }
        .wsd-row-token  { min-width: 200px; }
        .wsd-bubble     { max-width: 78%; font-size: 14px; }
        .wsd-chip       { padding: 16px 18px; font-size: 15px; }
        .wsd-empty-text { font-size: 16px; }
        .wsd-footnote   { font-size: 11px; }

        @media (max-width: 640px) {
          .wsd-chat       { height: 460px; padding: 16px 14px 12px; }
          .wsd-input-row  { padding: 8px 10px 10px; }
          .wsd-tool-chip  { display: block; }
          .wsd-tool-text  { white-space: normal; word-break: break-word; font-size: 11px; }
          .wsd-row        { flex-wrap: wrap; gap: 4px 10px; }
          .wsd-row-token  { min-width: 100%; flex: 1 1 100%; }
          .wsd-bubble     { max-width: 88%; font-size: 13.5px; }
          .wsd-chip       { padding: 14px 14px; font-size: 14px; }
          .wsd-empty-text { font-size: 14.5px; }
          .wsd-footnote   { font-size: 10.5px; }
          .wsd-input-placeholder { font-size: 13px !important; }
          .wsd-model-label { font-size: 12px !important; margin-right: 8px !important; }
        }
      `}</style>

      {}
      {modalPreview && <TxModal preview={modalPreview} onConfirm={handleConfirm} onCancel={() => setModalPreview(null)} />}

      {}
      <div style={{
    display: "flex",
    alignItems: "center",
    padding: "11px 14px",
    background: c.header,
    borderBottom: `1px solid ${c.border}`
  }}>
        <TrafficLights />
        <span style={{
    fontFamily: sans,
    fontSize: 13,
    color: c.muted,
    fontWeight: 500
  }}>
          Base MCP
        </span>
        <span style={{
    fontFamily: sans,
    fontSize: 12,
    color: c.dim,
    marginLeft: 8
  }}>▾</span>
        <div style={{
    flex: 1
  }} />
        {activeIdx !== null && <button onClick={reset} title="Reset" style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    width: 28,
    height: 24,
    borderRadius: 6,
    background: "transparent",
    border: "1px solid transparent",
    cursor: "pointer",
    color: c.dim
  }} onMouseEnter={e => {
    e.currentTarget.style.color = c.text;
    e.currentTarget.style.borderColor = c.toolBorder;
  }} onMouseLeave={e => {
    e.currentTarget.style.color = c.dim;
    e.currentTarget.style.borderColor = "transparent";
  }}>
            <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7L21 8" /><path d="M21 3v5h-5" /></svg>
          </button>}
      </div>

      {}
      <div ref={scrollRef} className="wsd-chat" style={{
    overflowY: "auto"
  }}>
        {!ex && <div>
            <div className="wsd-empty-text" style={{
    fontFamily: serif,
    color: c.muted,
    marginBottom: 20,
    lineHeight: 1.5
  }}>
              Try asking your assistant once <span style={{
    fontFamily: mono,
    fontSize: "0.85em",
    color: c.code,
    background: c.codeBg,
    padding: "1px 6px",
    borderRadius: 4
  }}>mcp.base.org</span> is connected:
            </div>
            <div style={{
    display: "grid",
    gap: 10
  }}>
              {examples.map((e, i) => <ChipBtn key={i} onClick={() => pick(i)}>{e.prompt}</ChipBtn>)}
            </div>
          </div>}

        {ex && <>
            <UserBubble>{ex.prompt}</UserBubble>
            {renderEvents()}
          </>}
      </div>

      {}
      <div className="wsd-input-row">
        <div style={{
    display: "flex",
    alignItems: "center",
    background: c.inputBg,
    border: `1px solid ${c.toolBorder}`,
    borderRadius: 14,
    padding: "10px 14px"
  }}>
          <button style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    width: 26,
    height: 26,
    borderRadius: 8,
    border: "none",
    background: "transparent",
    color: c.muted,
    cursor: "default",
    padding: 0,
    flexShrink: 0
  }}>
            <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 5v14M5 12h14" /></svg>
          </button>
          <span className="wsd-input-placeholder" style={{
    flex: 1,
    marginLeft: 8,
    fontFamily: sans,
    fontSize: 14,
    color: c.dim,
    overflow: "hidden",
    textOverflow: "ellipsis",
    whiteSpace: "nowrap"
  }}>
            Write a message...
          </span>
          <span className="wsd-model-label" style={{
    fontFamily: sans,
    fontSize: 13,
    color: c.muted,
    marginRight: 12,
    flexShrink: 0
  }}>
            Base MCP <span style={{
    color: c.dim
  }}>▾</span>
          </span>
          <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke={c.muted} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    flexShrink: 0
  }}>
            <path d="M12 2a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3z" />
            <path d="M19 11a7 7 0 0 1-14 0" />
            <line x1="12" y1="18" x2="12" y2="22" />
          </svg>
        </div>
        <div className="wsd-footnote" style={{
    textAlign: "center",
    marginTop: 8,
    fontFamily: sans,
    color: c.dim
  }}>
          Demo · Every write action requires your approval in <span style={{
    color: c.muted
  }}>Base Account</span>
        </div>
      </div>
    </div>;
};

## Demo

<WalletSetupDemo />

## Steps

<Steps>
  <Step title="Connect the MCP">
    <Tabs>
      <Tab title="Claude">
        <a href="https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=Base%20MCP&connectorUrl=https%3A%2F%2Fmcp.base.org" target="_blank" rel="noopener noreferrer">
          <img src="https://img.shields.io/badge/Add%20to%20Claude-1F1F1F?style=for-the-badge" alt="Add to Claude" noZoom />
        </a>

        Works in Claude.ai and Claude Apps (Desktop, iOS, Android). Click the button above, or:

        1. Open **Customize → Connectors → Add custom connector**
        2. The **Add custom connector** modal opens
        3. Fill in:
           * **Name**: `Base MCP`
           * **Remote MCP server URL**: `https://mcp.base.org`
        4. Click **Add**
        5. Next hit **Connect**, then approve the connection in Base Account. Click **Allow** once to authorize:

        <AuthApprovalDemo />
      </Tab>

      <Tab title="ChatGPT">
        <a href="https://chatgpt.com/#settings/Connectors" target="_blank" rel="noopener noreferrer">
          <img src="https://img.shields.io/badge/Add%20to%20ChatGPT-1F1F1F?style=for-the-badge" alt="Add to ChatGPT" noZoom />
        </a>

        Click the button above, or open **Settings → Connectors** manually. Then:

        1. Enable **Developer Mode** if prompted (under Advanced)
        2. Click **Create** to open the **New App** modal
        3. Fill in:
           * **Name**: `Base MCP`
           * **Description** (optional): `Wallet and onchain tools for Base`
           * **MCP Server URL**: `https://mcp.base.org`
           * **Authentication**: `OAuth`
        4. Check **I understand and want to continue** on the risk warning
        5. Click **Create**
        6. You will be automatically redirected to Base Account. Click **Allow** once to authorize.
      </Tab>

      <Tab title="Claude Code">
        Run this in your terminal to add the server to the current project:

        ```bash Terminal theme={null}
        claude mcp add --transport http base-mcp https://mcp.base.org
        ```

        To install globally (available across all your projects):

        ```bash Terminal theme={null}
        claude mcp add --transport http --scope user base-mcp https://mcp.base.org
        ```

        Verify it connected:

        ```bash Terminal theme={null}
        claude mcp list
        ```

        The `base-mcp` server will show with a tool count once active. You can also run `/mcp` inside a Claude Code session to see server status.
      </Tab>

      <Tab title="Codex">
        ```bash Terminal theme={null}
        codex mcp add base-mcp --url https://mcp.base.org/
        ```

        Or add to your `codex.toml`:

        ```toml codex.toml theme={null}
        [mcp_servers.base-mcp]
        url = "https://mcp.base.org/"
        ```
      </Tab>

      <Tab title="Cursor">
        <a href="cursor://anysphere.cursor-deeplink/mcp/install?name=base-mcp&config=eyJ1cmwiOiJodHRwczovL21jcC5iYXNlLm9yZyJ9" target="_blank" rel="noopener noreferrer">
          <img src="https://img.shields.io/badge/Add%20to%20Cursor-1F1F1F?style=for-the-badge" alt="Add to Cursor" noZoom />
        </a>

        Or add manually to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project):

        ```json mcp.json theme={null}
        {
          "mcpServers": {
            "base-mcp": {
              "url": "https://mcp.base.org"
            }
          }
        }
        ```

        Restart Cursor, then open **Settings → MCP** to confirm `base-mcp` shows as active.
      </Tab>

      <Tab title="Hermes">
        Hand the agent this quickstart and let it install itself:

        ```text Prompt theme={null}
        Install the Base MCP server from https://docs.base.org/ai-agents/quickstart
        ```

        Hermes will fetch the page, write the entry to `~/.hermes/config.yaml`, and reload — no manual editing needed.

        **Manual install** — if you'd rather edit the config yourself:

        ```yaml ~/.hermes/config.yaml theme={null}
        mcp_servers:
          base-mcp:
            url: "https://mcp.base.org"
        ```

        Then start a Hermes chat (or run `/reload-mcp` inside an existing session) and Hermes will discover the tools automatically.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Install the skill">
    The `base-mcp` skill extends your assistant with pre-built prompts and workflows for wallet operations, token transfers, and DeFi interactions on Base.

    <Tabs>
      <Tab title="Claude">
        <Warning>
          Pick **one** of the options below — don't do both. Running the prompt while a persistent skill is also installed can confuse the assistant about which onboarding to follow.
        </Warning>

        **Option 1: Paste this prompt into a new conversation**

        <TruncatedPrompt description="Onboard via the SKILL.md">
          I'd like to use Base MCP. For setup notes, please open `https://docs.base.org/ai-agents/skills/SKILL.md` as your reference. If your built-in browser can't reach the page, the Base MCP also exposes a `web_request` tool that can fetch it. If a section points to a related file under `references/` or `plugins/`, open that one too when it's relevant to what I'm asking.
        </TruncatedPrompt>

        Nothing to install — Claude reads the skill on the fly and fetches each reference or plugin file only when it needs one.

        **Option 2: Install as a persistent skill**

        <a href="https://github.com/base/skills/releases/download/base-mcp-v0.1.2/base-mcp.zip" target="_blank" rel="noopener noreferrer">
          <img src="https://img.shields.io/badge/Download%20skill-1F1F1F?style=for-the-badge&logo=download&logoColor=white" alt="Download for Claude" noZoom />
        </a>

        Click the button above to download `base-mcp.zip`, then:

        1. In Claude Desktop or Claude.ai, open [**Customize → Skills**](https://claude.ai/customize/skills)
        2. Click **Upload skill** and select the downloaded `base-mcp.zip`
        3. Toggle the skill on

        Claude activates the skill automatically when relevant to your prompt. See [Use skills in Claude](https://support.claude.com/en/articles/12512180-use-skills-in-claude) for details.
      </Tab>

      <Tab title="ChatGPT">
        <Warning>
          Pick **one** of the options below — don't do both. Running the prompt while a persistent skill is also installed can confuse the assistant about which onboarding to follow.
        </Warning>

        **Option 1: Paste this prompt into a new conversation**

        <TruncatedPrompt description="Onboard via the SKILL.md">
          I'd like to use Base MCP. For setup notes, please open `https://docs.base.org/ai-agents/skills/SKILL.md` as your reference. If your built-in browser can't reach the page, the Base MCP also exposes a `web_request` tool that can fetch it. If a section points to a related file under `references/` or `plugins/`, open that one too when it's relevant to what I'm asking.
        </TruncatedPrompt>

        Nothing to install — ChatGPT reads the skill on the fly and fetches each reference or plugin file only when it needs one. Works on any ChatGPT plan.

        **Option 2: Install as a persistent skill (Business, Enterprise, Edu, Teachers, Healthcare plans)**

        <a href="https://github.com/base/skills/releases/download/base-mcp-v0.1.2/base-mcp.zip" target="_blank" rel="noopener noreferrer">
          <img src="https://img.shields.io/badge/Download%20skill-1F1F1F?style=for-the-badge&logo=download&logoColor=white" alt="Download for ChatGPT" noZoom />
        </a>

        Click the button above to download `base-mcp.zip`, then:

        1. In ChatGPT, open [**Settings → Skills**](https://chatgpt.com/skills)
        2. Click **Add skill** and upload the downloaded `base-mcp.zip`
        3. Enable the skill for the conversations where you want it active

        See [Skills in ChatGPT](https://help.openai.com/en/articles/20001066-skills-in-chatgpt) for details.
      </Tab>

      <Tab title="Claude Code">
        ```bash Terminal theme={null}
        npx skills add base/skills --skill base-mcp -a claude-code
        ```

        Installs to `~/.claude/skills/base-mcp/`. The skill loads on your next session — Claude Code will use it automatically when wallet questions come up.
      </Tab>

      <Tab title="Codex">
        ```bash Terminal theme={null}
        npx skills add base/skills --skill base-mcp -a codex
        ```

        Installs to `~/.codex/skills/base-mcp/`. Codex picks it up automatically on the next run.
      </Tab>

      <Tab title="Cursor">
        ```bash Terminal theme={null}
        npx skills add base/skills --skill base-mcp -a cursor
        ```

        Installs to `~/.cursor/skills/base-mcp/`. Cursor picks it up automatically — invoke it in agent chat for any wallet workflow.
      </Tab>

      <Tab title="Hermes">
        ```bash Terminal theme={null}
        hermes skills install github:base/skills/base-mcp
        ```

        Installs to `~/.hermes/skills/base-mcp/`. Run `/reload-skills` inside Hermes (or restart the session) and it's available immediately.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Try it">
    Ask your assistant:

    ```text theme={null}
    Show me my wallets
    ```

    ```text theme={null}
    What's my USDC balance on Base?
    ```

    ```text theme={null}
    Send 1 USDC to jesse.base.eth
    ```

    ```text theme={null}
    Find the best USDC vault on Base by APY and deposit 100 USDC
    ```

    Every send, swap, or sign operation will give you an approval link. Open it, review the action in Base Account, and confirm.
  </Step>
</Steps>
