diff --git a/apps/studio/components/interfaces/MCP/InterstitialShell.tsx b/apps/studio/components/interfaces/MCP/InterstitialShell.tsx index 2eec2a32b54..556bea99dad 100644 --- a/apps/studio/components/interfaces/MCP/InterstitialShell.tsx +++ b/apps/studio/components/interfaces/MCP/InterstitialShell.tsx @@ -1,5 +1,5 @@ import type { ReactNode } from 'react' -import { cn, Skeleton } from 'ui' +import { Skeleton } from 'ui' import { InterstitialLayout, SupabaseLogo } from '@/components/layouts/InterstitialLayout' @@ -36,19 +36,6 @@ export const InterstitialShellSkeleton = ({ children }: { children: ReactNode }) ) -export const InterstitialFooter = ({ - children, - align = 'center', -}: { - children: ReactNode - align?: 'center' | 'start' -}) => ( -

- {children} -

+export const InterstitialFooter = ({ children }: { children: ReactNode }) => ( +

{children}

) diff --git a/apps/studio/components/interfaces/MCP/InterstitialTerminalScreen.test.tsx b/apps/studio/components/interfaces/MCP/InterstitialTerminalScreen.test.tsx new file mode 100644 index 00000000000..4494b011528 --- /dev/null +++ b/apps/studio/components/interfaces/MCP/InterstitialTerminalScreen.test.tsx @@ -0,0 +1,32 @@ +import { screen } from '@testing-library/react' +import { describe, expect, it } from 'vitest' + +import { InterstitialTerminalScreen } from './InterstitialTerminalScreen' +import { customRender } from '@/tests/lib/custom-render' + +const baseCopy = { + title: 'Key stored', + subtitle: 'RESEND_API_KEY is saved for billing-staging.', + calloutTitle: 'Next step', + calloutBody: 'Head back to your agent and let it know you finished.', + footer: 'You can close this tab.', +} + +describe('InterstitialTerminalScreen', () => { + it('links to the project Edge Functions secrets page when a project ref is given', () => { + customRender() + + expect(screen.getByRole('link', { name: 'Go to Edge Functions secrets' })).toHaveAttribute( + 'href', + '/project/abcdefghijklmnopqrst/functions/secrets' + ) + }) + + it('omits the Edge Functions secrets link when there is no project to point at', () => { + customRender() + + expect( + screen.queryByRole('link', { name: 'Go to Edge Functions secrets' }) + ).not.toBeInTheDocument() + }) +}) diff --git a/apps/studio/components/interfaces/MCP/InterstitialTerminalScreen.tsx b/apps/studio/components/interfaces/MCP/InterstitialTerminalScreen.tsx index 93f1d1255a8..801f3cdfbdc 100644 --- a/apps/studio/components/interfaces/MCP/InterstitialTerminalScreen.tsx +++ b/apps/studio/components/interfaces/MCP/InterstitialTerminalScreen.tsx @@ -1,3 +1,4 @@ +import { Button, Separator } from 'ui' import { Admonition } from 'ui-patterns/Admonition' import { InterstitialFooter, InterstitialShell } from './InterstitialShell' @@ -8,6 +9,7 @@ export type InterstitialTerminalCopy = { calloutTitle: string calloutBody: string footer: string + projectRef?: string } export const InterstitialTerminalScreen = ({ @@ -16,9 +18,19 @@ export const InterstitialTerminalScreen = ({ calloutTitle, calloutBody, footer, + projectRef, }: InterstitialTerminalCopy) => ( + + {projectRef && ( + + )} + + + {footer} ) diff --git a/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.constants.ts b/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.constants.ts index bea5ba8befb..7f7ff933493 100644 --- a/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.constants.ts +++ b/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.constants.ts @@ -2,8 +2,6 @@ export const MCP_SECRETS_ROUTE = '/mcp/secrets' export const MCP_SECRETS_FLAG = 'McpElicitURLMode' -export const UNKNOWN_CLIENT_LABEL = 'your AI client' - export const SECRETS_TOOL_NAME = 'create_edge_function_secret' export const MAX_SECRET_NAME_LENGTH = 256 diff --git a/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.utils.test.ts b/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.utils.test.ts index 7ba13370581..4753302dfbb 100644 --- a/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.utils.test.ts +++ b/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.utils.test.ts @@ -35,9 +35,7 @@ describe('getSecretsCopy', () => { it('names the generic client, because v1 never learns which one sent the user', () => { const copy = getSecretsCopy({ status: 'stored', request, timedOut: false }) - expect(copy.calloutBody).toBe( - `Go back to your AI client and choose "I've completed it" to finish the tool call.` - ) + expect(copy.calloutBody).toBe(`Head back to your agent and let it know you're finished.`) }) it('sends the user back to the agent instead of the client on timeout', () => { diff --git a/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.utils.ts b/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.utils.ts index 796fa0434b2..008c5229472 100644 --- a/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.utils.ts +++ b/apps/studio/components/interfaces/MCP/Secrets/McpSecrets.utils.ts @@ -3,7 +3,6 @@ import relativeTime from 'dayjs/plugin/relativeTime' import utc from 'dayjs/plugin/utc' import type { InterstitialTerminalCopy } from '../InterstitialTerminalScreen' -import { UNKNOWN_CLIENT_LABEL } from './McpSecrets.constants' import type { SecretProviderHint, SecretRequest, @@ -14,7 +13,7 @@ import type { dayjs.extend(relativeTime) dayjs.extend(utc) -const RETURN_TO_CLIENT_STEP = `Go back to ${UNKNOWN_CLIENT_LABEL} and choose "I've completed it" to finish the tool call.` +const RETURN_TO_CLIENT_STEP = `Head back to your agent and let it know you're finished.` const CLOSE_TAB_FOOTER = 'You can close this tab.' @@ -35,6 +34,7 @@ export function getSecretsCopy(state: SecretsOutcomeState): InterstitialTerminal calloutBody: 'Ask your agent to store the key again. It will find the saved key and finish without sending you back here.', footer: UNVERIFIED_KEY_FOOTER, + projectRef: state.request.ref, } } @@ -44,6 +44,7 @@ export function getSecretsCopy(state: SecretsOutcomeState): InterstitialTerminal calloutTitle: 'Next step', calloutBody: RETURN_TO_CLIENT_STEP, footer: UNVERIFIED_KEY_FOOTER, + projectRef: state.request.ref, } } @@ -54,6 +55,7 @@ export function getSecretsCopy(state: SecretsOutcomeState): InterstitialTerminal calloutTitle: 'Next step', calloutBody: RETURN_TO_CLIENT_STEP, footer: CLOSE_TAB_FOOTER, + projectRef: state.request.ref, } case 'expired': diff --git a/apps/studio/components/interfaces/MCP/Secrets/McpSecretsForm.tsx b/apps/studio/components/interfaces/MCP/Secrets/McpSecretsForm.tsx index 834a506c478..18fc2b19471 100644 --- a/apps/studio/components/interfaces/MCP/Secrets/McpSecretsForm.tsx +++ b/apps/studio/components/interfaces/MCP/Secrets/McpSecretsForm.tsx @@ -18,7 +18,7 @@ import { Admonition } from 'ui-patterns/Admonition' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import z from 'zod' -import { InterstitialShell } from '../InterstitialShell' +import { InterstitialFooter, InterstitialShell } from '../InterstitialShell' import type { SecretRequest } from './McpSecrets.types' import { getOverwriteWarning, @@ -163,7 +163,7 @@ export const McpSecretsForm = ({ {providerHint && ( <> -

+ {`Don't have your key? Create one in the `} {providerHint.dashboardUrl ? ( + )} diff --git a/apps/studio/components/interfaces/MCP/Secrets/McpSecretsWrongAccount.tsx b/apps/studio/components/interfaces/MCP/Secrets/McpSecretsWrongAccount.tsx index 3fec66f87bd..215251db82a 100644 --- a/apps/studio/components/interfaces/MCP/Secrets/McpSecretsWrongAccount.tsx +++ b/apps/studio/components/interfaces/MCP/Secrets/McpSecretsWrongAccount.tsx @@ -27,7 +27,7 @@ export const McpSecretsWrongAccount = ({ - + After switching, ask your agent to run the tool again. Nothing has been stored.