Ah, good catch, it was the same browser, but different chrome profiles. I'm assuming I have some chrome extension installed that is messing things up. I will report back here if it seems like there is anything on your end, but I'm very strongly guessing it's my fault.
Wait, actually, it appears that the bug has flipped. Now the links work (open a confirmation modal) when the dive is viewed in claude.ai, but when viewed natively on app.motherduck.com they silently no-op (eg the opposite of what I originally reported). cc Yana C.
Hi! Small improvement request: In the data profiler, when viewing an enum, the histogram it shows is labeled with the numeric ID of the enum member, not it's text label. Eg for this enum of political party affiliation, it shows numbers like 73 and 18 instead of the text "D" and "R"
Here is a full dive content that tests 10 different "open a link" implementations to see how they behave on app.motherduck.com and on claude.ai. Ask claude to write this to a dive and it probably will be able to.
/**
* Title: Link Navigation Test
* Description: Tests 10 different link/navigation techniques to find what works in Claude.ai embedded context vs app.motherduck.com
* Dive ID: 0255c64e-d420-4b5d-adfa-3ab2a36fa650
*/
import { useState } from "react";
import { useSQLQuery } from "@motherduck/react-sql-query";
export const REQUIRED_DATABASES = [
{ type: 'share', path: 'md:_share/meta_ads/c10f2f13-41a3-4fe6-9240-7cdb068d0ecd', alias: 'meta_ads' }
];
const TEST_URL = "https://www.google.com";
export default function LinkNavigationTest() {
const [log, setLog] = useState([]);
// Trivial query to satisfy validation
useSQLQuery(`SELECT 1 as x`);
const addLog = (msg) => setLog(prev => [`[${new Date().toLocaleTimeString()}] ${msg}`, ...prev.slice(0, 19)]);
const handleWindowOpen = () => {
addLog("Trying window.open...");
const w = window.open(TEST_URL, "_blank");
addLog(w ? "window.open returned a handle ✓" : "window.open returned null (blocked or no-op)");
};
const handleWindowOpenNoopener = () => {
addLog("Trying window.open with noopener...");
const w = window.open(TEST_URL, "_blank", "noopener,noreferrer");
addLog(w ? "window.open (noopener) returned handle ✓" : "window.open (noopener) returned null");
};
const handlePostMessage = () => {
addLog("Trying window.parent.postMessage navigation-request...");
try {
window.parent.postMessage({ type: "navigation-request", url: TEST_URL }, "*");
addLog("postMessage sent ✓");
} catch (e) {
addLog(`postMessage failed: ${e.message}`);
}
};
const handleLocationHref = () => {
addLog("Trying window.location.href...");
try {
window.location.href = TEST_URL;
addLog("location.href set ✓");
} catch (e) {
addLog(`location.href failed: ${e.message}`);
}
};
const handleHiddenAnchor = () => {
addLog("Trying programmatic anchor .click()...");
try {
const a = document.createElement("a");
a.href = TEST_URL;
a.target = "_blank";
a.rel = "noopener noreferrer";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
addLog("Anchor .click() dispatched ✓");
} catch (e) {
addLog(`Anchor click failed: ${e.message}`);
}
};
const handleTopLocation = () => {
addLog("Trying window.top.location.href...");
try {
window.top.location.href = TEST_URL;
addLog("window.top.location.href set ✓");
} catch (e) {
addLog(`window.top failed: ${e.message}`);
}
};
const handleOpener = () => {
addLog(`window.opener is: ${window.opener}`);
try {
if (window.opener) {
window.opener.postMessage({ type: "navigation-request", url: TEST_URL }, "*");
addLog("opener.postMessage sent ✓");
} else {
addLog("window.opener is null — cannot use");
}
} catch (e) {
addLog(`opener failed: ${e.message}`);
}
};
const handleCustomEvent = () => {
addLog("Trying CustomEvent 'navigation-request'...");
try {
window.dispatchEvent(new CustomEvent("navigation-request", { detail: { url: TEST_URL }, bubbles: true }));
window.parent.dispatchEvent && window.parent.dispatchEvent(new CustomEvent("navigation-request", { detail: { url: TEST_URL }, bubbles: true }));
addLog("CustomEvent dispatched ✓");
} catch (e) {
addLog(`CustomEvent failed: ${e.message}`);
}
};
const handleInspect = () => {
addLog(`--- Environment Info ---`);
addLog(`window === window.top: ${window === window.top}`);
addLog(`window.opener: ${window.opener}`);
addLog(`document.referrer: "${document.referrer}"`);
addLog(`window.location.origin: ${window.location.origin}`);
addLog(`window.location.href: ${window.location.href.slice(0, 80)}...`);
try {
const parentOrigin = window.parent.location.origin;
addLog(`parent.location.origin: ${parentOrigin}`);
} catch (e) {
addLog(`parent.location.origin: (cross-origin, blocked)`);
}
};
const btn = (color, label, handler) => (
<button
onClick={handler}
style={{ padding: "7px 12px", background: color, color: "#fff", border: "none", borderRadius: 5, cursor: "pointer", fontSize: 12, fontWeight: 500, width: "100%" }}
>
{label}
</button>
);
const approaches = [
{ label: "1. <a> target=_blank (standard)", note: "Plain anchor element, standard approach", el: (
<a href={TEST_URL} target="_blank" rel="noopener noreferrer"
onClick={() => addLog("<a href target=_blank> clicked")}
style={{ color: "#0777b3", fontSize: 13, fontWeight: 600, textDecoration: "underline" }}>
Open FB Ads Library ↗
</a>
)},
{ label: "2. <a> onClick → e.preventDefault + window.open", note: "Intercept anchor click, do window.open instead", el: (
<a href={TEST_URL}
onClick={(e) => { e.preventDefault(); addLog("<a> intercepted → window.open..."); window.open(TEST_URL, "_blank"); }}
style={{ color: "#bd4e35", fontSize: 13, fontWeight: 600, textDecoration: "underline", cursor: "pointer" }}>
Open FB Ads Library ↗
</a>
)},
{ label: "3. button → window.open(url, '_blank')", note: "No anchor, pure window.open", el: btn("#0777b3", "window.open", handleWindowOpen) },
{ label: "4. button → window.open + 'noopener,noreferrer'", note: "window.open with feature string", el: btn("#2d7a00", "window.open (noopener)", handleWindowOpenNoopener) },
{ label: "5. window.parent.postMessage navigation-request", note: "What the dive guide recommends", el: btn("#e18727", "postMessage to parent", handlePostMessage) },
{ label: "6. window.location.href", note: "Navigate the iframe itself away", el: btn("#638CAD", "location.href = url", handleLocationHref) },
{ label: "7. Programmatic anchor .click()", note: "createElement('a') → click() → remove", el: btn("#8B5CF6", "hidden anchor .click()", handleHiddenAnchor) },
{ label: "8. window.top.location.href", note: "Navigate top frame (cross-origin may throw)", el: btn("#bc1200", "top.location.href", handleTopLocation) },
{ label: "9. window.opener.postMessage", note: "Only works if dive was opened via window.open", el: btn("#14B8A6", "opener.postMessage", handleOpener) },
{ label: "10. CustomEvent on window + parent", note: "Custom event bubbled to parent frame", el: btn("#6366F1", "CustomEvent dispatch", handleCustomEvent) },
];
return (
<div style={{ background: "#f8f8f8", minHeight: "100vh", fontFamily: "system-ui, sans-serif", padding: 20 }}>
<h1 style={{ color: "#231f20", fontSize: 17, fontWeight: 700, margin: "0 0 2px 0" }}>Link Navigation Test</h1>
<p style={{ color: "#6a6a6a", fontSize: 11, margin: "0 0 4px 0" }}>
Target URL: <code style={{ background: "#e0e0e0", padding: "1px 4px", borderRadius: 3 }}>google.com</code>
</p>
<button onClick={handleInspect} style={{ marginBottom: 14, padding: "5px 10px", fontSize: 11, background: "#231f20", color: "#fff", border: "none", borderRadius: 4, cursor: "pointer" }}>
🔍 Inspect iframe environment first
</button>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
{approaches.map((a, i) => (
<div key={i} style={{ background: "#fff", border: "1px solid #e0e0e0", borderRadius: 6, padding: "10px 12px" }}>
<div style={{ fontSize: 11, fontWeight: 700, color: "#231f20", marginBottom: 1 }}>{a.label}</div>
<div style={{ fontSize: 10, color: "#6a6a6a", marginBottom: 7 }}>{a.note}</div>
{a.el}
</div>
))}
</div>
<div style={{ marginTop: 14 }}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 5 }}>
<span style={{ fontSize: 11, fontWeight: 700, color: "#231f20" }}>Event Log</span>
<button onClick={() => setLog([])} style={{ fontSize: 10, color: "#6a6a6a", border: "1px solid #ddd", background: "#fff", borderRadius: 3, padding: "1px 7px", cursor: "pointer" }}>Clear</button>
</div>
<div style={{ background: "#1a1a2e", borderRadius: 6, padding: 10, minHeight: 70, maxHeight: 200, overflowY: "auto" }}>
{log.length === 0
? <span style={{ color: "#555", fontSize: 11 }}>Click anything above — events appear here.</span>
: log.map((l, i) => <div key={i} style={{ color: "#a8e6cf", fontSize: 11, fontFamily: "monospace", lineHeight: 1.7 }}>{l}</div>)
}
</div>
</div>
</div>
);
}
Bug: External link navigation broken when Dive is embedded in Claude.ai MCP widget 1. Create a Dive with external links (e.g. `<a href="https://www.facebook.com/ads/library/..." target="_blank">`). See my first comment in this thread for a full dive code snippet. 2. View the Dive as an MCP app widget inside Claude.ai (ask claude to "view my motherduck dive abc-def-123") 3. Click any external link Expected behavior: Link opens in a new tab, or at minimum the user gets a prompt/confirmation. Actual behavior: - Claude.ai embed: Click does nothing. No navigation, no error, no feedback. - app.motherduck.com: Click triggers a "Open external link?" confirmation modal. I assume this is expected behavior, but want to double check. I would personally prefer this to just open directly and for this modal not to appear. Diagnosis: The Dive sandbox intercepts `<a>` clicks and `window.open()` calls and forwards them as `navigation-request` postMessages to the parent frame. On app.motherduck.com, the parent frame listens for this message and shows the confirmation modal. Inside the Claude.ai MCP widget, no handler is registered for the message, so navigation silently no-ops. All standard navigation approaches are affected: - `<a href target="_blank">` - `window.open(url, '_blank')` - `window.open(url, '_blank', 'noopener,noreferrer')` - Programmatic anchor `.click()` - `window.parent.postMessage({ type: 'navigation-request', url })` Workaround: None currently available within the Dive sandbox. Displaying URLs as copyable text is the only reliable fallback. Requested fix: 1. Make links work when clicked in MCP widgets on claude.ai. I'm not sure if you actually have control over this or if this is a problem on the anthropic side. 2. Ideally, suppress the confirmation modal on app.motherduck.com for links that open in `_blank`. Full repro code snippet in the comments.
excellent, thanks!
wait, hold the phone, this might be solved. It looks like in the settings on claude.ai I can select "all domains" in the allowlist. I know I looked here in the last week, and I SWEAR that wasn't an option then. Not sure if anthropic is shipping like mad, or if my account didn't have the right permissions, etc. But I can now download the duckdb cli, python package, motherduck extension, and then use those to query motherduck. I think my advice around adjusting the MCP descriptions is still good advice though. Unless I suggest that to the LLM in a custom skill, it still LOVES to try to download huge CSVs using the MCP. Thanks for the time and effort here everyone, I really appreciate it!
Yes, per this thread, it turns out doing anything via a local duckdb process won't work (because the md extension will make network requests to a motherduck domain, not on anthropic's allowlist). It currently seems to me that to get around this network firewall, I HAVE to use the MCP in the claude.ai cloud env.
Thank you! I'd be happy to chat more about my usecase if you/they want more clarification.
.png)