diff --git a/apps/web/public/assets/mcp-clients/anthropic-icon-light.svg b/apps/web/public/assets/mcp-clients/anthropic-icon-light.svg new file mode 100644 index 00000000..a02957f9 --- /dev/null +++ b/apps/web/public/assets/mcp-clients/anthropic-icon-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/public/assets/mcp-clients/cline-icon-dark.png b/apps/web/public/assets/mcp-clients/cline-icon-dark.png new file mode 100644 index 00000000..36c37766 Binary files /dev/null and b/apps/web/public/assets/mcp-clients/cline-icon-dark.png differ diff --git a/apps/web/public/assets/mcp-clients/cline-icon-light.png b/apps/web/public/assets/mcp-clients/cline-icon-light.png new file mode 100644 index 00000000..2f028e0a Binary files /dev/null and b/apps/web/public/assets/mcp-clients/cline-icon-light.png differ diff --git a/apps/web/public/assets/mcp-clients/cursor-icon-dark.webp b/apps/web/public/assets/mcp-clients/cursor-icon-dark.webp new file mode 100644 index 00000000..489e77b7 Binary files /dev/null and b/apps/web/public/assets/mcp-clients/cursor-icon-dark.webp differ diff --git a/apps/web/public/assets/mcp-clients/cursor-icon-light.svg b/apps/web/public/assets/mcp-clients/cursor-icon-light.svg new file mode 100644 index 00000000..abadee50 --- /dev/null +++ b/apps/web/public/assets/mcp-clients/cursor-icon-light.svg @@ -0,0 +1 @@ +Cursor \ No newline at end of file diff --git a/apps/web/public/assets/mcp-clients/raycast-icon-dark.svg b/apps/web/public/assets/mcp-clients/raycast-icon-dark.svg new file mode 100644 index 00000000..af54eee3 --- /dev/null +++ b/apps/web/public/assets/mcp-clients/raycast-icon-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/public/assets/mcp-clients/raycast-icon-light.svg b/apps/web/public/assets/mcp-clients/raycast-icon-light.svg new file mode 100644 index 00000000..e46a0827 --- /dev/null +++ b/apps/web/public/assets/mcp-clients/raycast-icon-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/public/assets/mcp-clients/raycast-screenshot.jpg b/apps/web/public/assets/mcp-clients/raycast-screenshot.jpg new file mode 100644 index 00000000..e55072d0 Binary files /dev/null and b/apps/web/public/assets/mcp-clients/raycast-screenshot.jpg differ diff --git a/apps/web/public/assets/mcp-clients/trae-icon-light.svg b/apps/web/public/assets/mcp-clients/trae-icon-light.svg new file mode 100644 index 00000000..74867ee7 --- /dev/null +++ b/apps/web/public/assets/mcp-clients/trae-icon-light.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/assets/mcp-clients/vscode-icon-light.svg b/apps/web/public/assets/mcp-clients/vscode-icon-light.svg new file mode 100644 index 00000000..c453e633 --- /dev/null +++ b/apps/web/public/assets/mcp-clients/vscode-icon-light.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/assets/mcp-clients/warp-icon-light.png b/apps/web/public/assets/mcp-clients/warp-icon-light.png new file mode 100644 index 00000000..bb26f7d5 Binary files /dev/null and b/apps/web/public/assets/mcp-clients/warp-icon-light.png differ diff --git a/apps/web/public/assets/mcp-clients/windsurf-icon-dark.svg b/apps/web/public/assets/mcp-clients/windsurf-icon-dark.svg new file mode 100644 index 00000000..b1f22035 --- /dev/null +++ b/apps/web/public/assets/mcp-clients/windsurf-icon-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/web/public/assets/mcp-clients/windsurf-icon-light.svg b/apps/web/public/assets/mcp-clients/windsurf-icon-light.svg new file mode 100644 index 00000000..21763298 --- /dev/null +++ b/apps/web/public/assets/mcp-clients/windsurf-icon-light.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/web/src/components/example-usage.tsx b/apps/web/src/components/example-usage.tsx index 1abc176b..c518bc1f 100644 --- a/apps/web/src/components/example-usage.tsx +++ b/apps/web/src/components/example-usage.tsx @@ -1,6 +1,7 @@ 'use client' import type { Project } from '@agentic/platform-types' +import Link from 'next/link' import { type JSX, useEffect, useMemo, useState } from 'react' import { useLocalStorage } from 'react-use' @@ -8,6 +9,7 @@ import { useAgentic } from '@/components/agentic-provider' import { CodeBlock } from '@/components/code-block' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { + type CodeSnippet, defaultConfig, type DeveloperConfig, getCodeForDeveloperConfig, @@ -30,6 +32,7 @@ import { import { useQuery } from '@/lib/query-client' import { LoadingIndicator } from './loading-indicator' +import { Button } from './ui/button' export function ExampleUsage({ projectIdentifier, @@ -218,12 +221,11 @@ function ExampleUsageContent({ - ))} @@ -255,11 +257,14 @@ function ExampleUsageContent({ {tsFrameworkTargets.map((framework) => ( - - + ))} @@ -291,11 +296,14 @@ function ExampleUsageContent({ {pyFrameworkTargets.map((framework) => ( - - + ))} @@ -327,11 +335,14 @@ function ExampleUsageContent({ {httpTargets.map((httpTarget) => ( - - + ))} @@ -340,3 +351,60 @@ function ExampleUsageContent({ ) } + +function CodeSnippetBlock({ + codeSnippet, + initialCodeBlock +}: { + codeSnippet: CodeSnippet + initialCodeBlock?: JSX.Element +}) { + return ( + <> + + + {codeSnippet.action && ( + + )} + + ) +} diff --git a/apps/web/src/lib/developer-config.ts b/apps/web/src/lib/developer-config.ts index 1f93132a..c816de08 100644 --- a/apps/web/src/lib/developer-config.ts +++ b/apps/web/src/lib/developer-config.ts @@ -1,7 +1,7 @@ import type { Deployment, Project } from '@agentic/platform-types' import type { BundledLanguage } from 'shiki/bundle/web' import type { Simplify } from 'type-fest' -import { assert } from '@agentic/platform-core' +import { assert, pruneUndefined } from '@agentic/platform-core' import { gatewayBaseUrl } from './config' @@ -27,13 +27,17 @@ export type HTTPTarget = (typeof httpTargets)[number] export const mcpClientTargetLabels = { url: 'MCP Server URL', - 'claude-desktop': 'Claude Desktop', - chatgpt: 'ChatGPT', - raycast: 'Raycast', + 'mcp-json': 'mcp.json', cursor: 'Cursor', + 'claude-code': 'Claude Code', + 'claude-desktop': 'Claude Desktop', + // chatgpt: 'ChatGPT', // TODO + raycast: 'Raycast', + trae: 'Trae', windsurf: 'Windsurf', + vscode: 'VSCode', cline: 'Cline', - goose: 'Goose' + warp: 'Warp' } as const export const mcpClientTargets = Object.keys( mcpClientTargetLabels @@ -88,7 +92,13 @@ export const defaultConfig: DeveloperConfig = { export type CodeSnippet = { code: string lang: BundledLanguage - // install?: string // TODO + action?: { + href: string + label: string + logoImageUrl?: string + logoImageUrlDark?: string + logoImageUrlLight?: string + } } export type GetCodeForDeveloperConfigOpts = { @@ -157,12 +167,123 @@ export function getCodeForDeveloperConfig( } export function getCodeForMCPClientConfig({ - identifier + config, + identifier, + project, + apiKey }: GetCodeForDeveloperConfigInnerOpts): CodeSnippet { - const mcpUrl = `${gatewayBaseUrl}/${identifier}/mcp` - return { - code: mcpUrl, - lang: 'bash' + const mcpUrl = `${gatewayBaseUrl}/${identifier}/mcp${ + apiKey ? `?apiKey=${apiKey}` : '' + }` + + const mcpConfig = { + mcpServers: { + [identifier]: { + url: mcpUrl + } + } + } + const mcpConfigCode = JSON.stringify(mcpConfig, null, 2) + + const mcpRemoteConfig = { + mcpServers: { + [identifier]: { + command: 'npx', + args: ['mcp-remote', '-y', mcpUrl] + } + } + } + const mcpRemoteConfigCode = JSON.stringify(mcpRemoteConfig, null, 2) + + switch (config.mcpClientTarget) { + case 'url': + return { + code: mcpUrl, + lang: 'bash' + } + + case 'mcp-json': + return { + code: mcpConfigCode, + lang: 'json' + } + + case 'claude-code': + return { + code: `claude mcp add --transport http "${identifier}" "${mcpUrl}"`, + lang: 'bash' + } + + case 'cursor': { + const config = Buffer.from(JSON.stringify(mcpConfig)).toString('base64') + const href = `cursor://anysphere.cursor-deeplink/mcp/install?name=${identifier}&config=${config}` + + return { + code: mcpConfigCode, + lang: 'json', + action: { + href, + label: `Add the ${identifier} MCP server to Cursor`, + logoImageUrlLight: '/assets/mcp-clients/cursor-icon-light.svg', + logoImageUrlDark: '/assets/mcp-clients/cursor-icon-dark.webp' + } + } + } + + case 'windsurf': + return { + code: mcpRemoteConfigCode, + lang: 'json' + } + + case 'claude-desktop': + return { + code: mcpRemoteConfigCode, + lang: 'json' + } + + case 'raycast': { + // https://manual.raycast.com/model-context-protocol + const customMcpConfig = pruneUndefined({ + name: identifier, + type: 'http', + url: mcpUrl, + description: project.lastPublishedDeployment?.description + }) + const customMcpConfigCode = JSON.stringify(customMcpConfig) + const href = `raycast://mcp/install?${encodeURIComponent(customMcpConfigCode)}` + + return { + code: mcpConfigCode, + lang: 'json', + action: { + href, + label: `Add the ${identifier} MCP server to Raycast`, + logoImageUrlLight: '/assets/mcp-clients/raycast-icon-light.svg', + logoImageUrlDark: '/assets/mcp-clients/raycast-icon-dark.svg' + } + } + } + + case 'vscode': + return { + code: ` +"mcp": { + "servers": { + "${identifier}": { + "type": "http", + "url": "${mcpUrl}" + } + } +}`.trim(), + lang: 'json' + } + + default: + return { + code: mcpConfigCode, + lang: 'json' + } } }