From ec6a76b8e10597ebb5991182c7c4a573a3148c1f Mon Sep 17 00:00:00 2001 From: xlyoung Date: Mon, 1 Jun 2026 22:43:14 +0800 Subject: [PATCH] fix: add clipboard fallback for non-secure contexts navigator.clipboard.writeText() requires a secure context (HTTPS or localhost). When running Inspector in Docker on a local network over HTTP, the Clipboard API throws a DOMException, causing the 'Copy Servers File' and 'Copy Server Entry' buttons to fail. This adds a fallback that uses a temporary textarea element with document.execCommand('copy') when the Clipboard API is unavailable. Fixes #913 --- client/src/components/Sidebar.tsx | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index 762678f22..960ec2f86 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -135,6 +135,29 @@ const Sidebar = ({ [toast], ); + // Fallback clipboard copy for non-secure contexts (HTTP, not HTTPS/localhost) + // navigator.clipboard.writeText() requires a secure context + const copyToClipboard = useCallback(async (text: string) => { + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(text); + return; + } + // Fallback: use a temporary textarea element + const textarea = document.createElement("textarea"); + textarea.value = text; + textarea.style.position = "fixed"; + textarea.style.left = "-9999px"; + textarea.style.top = "-9999px"; + document.body.appendChild(textarea); + textarea.focus(); + textarea.select(); + try { + document.execCommand("copy"); + } finally { + document.body.removeChild(textarea); + } + }, []); + // Shared utility function to generate server config const generateServerConfig = useCallback(() => { if (transportType === "stdio") { @@ -183,8 +206,7 @@ const Sidebar = ({ const handleCopyServerEntry = useCallback(() => { try { const configJson = generateMCPServerEntry(); - navigator.clipboard - .writeText(configJson) + copyToClipboard(configJson) .then(() => { setCopiedServerEntry(true); @@ -213,8 +235,7 @@ const Sidebar = ({ const handleCopyServerFile = useCallback(() => { try { const configJson = generateMCPServerFile(); - navigator.clipboard - .writeText(configJson) + copyToClipboard(configJson) .then(() => { setCopiedServerFile(true);