From 00761b919df5e2e6961dd77190c48c8d886fb293 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 18 Jul 2026 00:45:47 +0000 Subject: [PATCH] Add MCP CTA to dashboard AI chat empty state Co-Authored-By: Konstantin Wohlwend --- .../src/components/assistant-ui/thread.tsx | 49 +++++++++++++++++-- .../hexclave-companion/ai-chat-widget.tsx | 3 +- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/apps/dashboard/src/components/assistant-ui/thread.tsx b/apps/dashboard/src/components/assistant-ui/thread.tsx index 5c25a1b4b..7bdc3ebcf 100644 --- a/apps/dashboard/src/components/assistant-ui/thread.tsx +++ b/apps/dashboard/src/components/assistant-ui/thread.tsx @@ -557,6 +557,50 @@ const Composer: FC<{ placeholder?: ComposerPlaceholder, autoFocus?: boolean }> = ); }; +/** + * Canonical link to the "set up the Hexclave MCP server" docs. Uses the docs + * base URL when configured (so it points at localhost:8104 in dev) and falls + * back to the public docs site otherwise. + */ +function getMcpDocsUrl(): string { + const base = (getPublicEnvVar("NEXT_PUBLIC_STACK_DOCS_BASE_URL") ?? "https://docs.hexclave.com").replace(/\/$/, ""); + return `${base}/guides/getting-started/ai-integration#option-3-mcp`; +} + +/** + * Empty-state variant of the agent-eject CTA: an "or" divider followed by a + * card inviting the user to continue the chat in their own coding agent via + * the Hexclave MCP server. Rendered inside the welcome node of general-purpose + * dashboard AI chats, where there is no transcript to copy yet. + */ +export const AgentEjectWelcomeCta: FC = () => { + const mcpDocsUrl = useMemo(() => getMcpDocsUrl(), []); + + return ( +
+
+
+ or +
+
+ + + + Chat with Hexclave in your own agent with our{" "} + + MCP server + + + +
+ ); +}; + function serializeThreadToMarkdown(messages: readonly ThreadMessage[]): string { const blocks: string[] = []; for (const message of messages) { @@ -589,10 +633,7 @@ const AgentEjectFooter: FC = () => { return threadRuntime.subscribe(sync); }, [threadRuntime]); - const mcpDocsUrl = useMemo(() => { - const base = (getPublicEnvVar("NEXT_PUBLIC_STACK_DOCS_BASE_URL") ?? "https://docs.hexclave.com").replace(/\/$/, ""); - return `${base}/guides/getting-started/ai-integration#option-3-mcp`; - }, []); + const mcpDocsUrl = useMemo(() => getMcpDocsUrl(), []); const hasMessages = messageCount > 0; diff --git a/apps/dashboard/src/components/hexclave-companion/ai-chat-widget.tsx b/apps/dashboard/src/components/hexclave-companion/ai-chat-widget.tsx index 3143c42be..ecd6aa12b 100644 --- a/apps/dashboard/src/components/hexclave-companion/ai-chat-widget.tsx +++ b/apps/dashboard/src/components/hexclave-companion/ai-chat-widget.tsx @@ -4,7 +4,7 @@ import { cn } from "@/components/ui"; import { createUnifiedAiChatAdapter, getFriendlyAiErrorMessage } from "@/components/assistant-ui/chat-stream"; import { ImageAttachmentAdapter } from "@/components/assistant-ui/image-attachment-adapter"; import { MarkdownText } from "@/components/assistant-ui/markdown-text"; -import { Thread } from "@/components/assistant-ui/thread"; +import { AgentEjectWelcomeCta, Thread } from "@/components/assistant-ui/thread"; import { ToolFallback } from "@/components/assistant-ui/tool-fallback"; import { createConversation, @@ -547,6 +547,7 @@ function AskAiWelcome() {

Get AI-powered answers about Hexclave, your project, and analytics.

+
);