From 798143f0bbf06ed37ea7eebc808b755236bf6dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Grandin?= Date: Thu, 16 Apr 2026 15:46:45 +0200 Subject: [PATCH 1/4] feat(mcp-server): add MCP server settings route and component --- apps/console-v5/src/routeTree.gen.ts | 24 ++ .../$organizationId/settings/mcp-server.tsx | 10 + .../$organizationId/settings/route.tsx | 7 + .../organizations/feature/src/index.ts | 1 + .../settings-mcp-server.spec.tsx | 41 ++++ .../settings-mcp-server.tsx | 212 ++++++++++++++++++ 6 files changed, 295 insertions(+) create mode 100644 apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/mcp-server.tsx create mode 100644 libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.spec.tsx create mode 100644 libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx diff --git a/apps/console-v5/src/routeTree.gen.ts b/apps/console-v5/src/routeTree.gen.ts index dd5292ed91c..af24e82de80 100644 --- a/apps/console-v5/src/routeTree.gen.ts +++ b/apps/console-v5/src/routeTree.gen.ts @@ -33,6 +33,7 @@ import { Route as AuthenticatedOrganizationOrganizationIdAlertsIndexRouteImport import { Route as AuthenticatedOrganizationOrganizationIdClusterIdIndexRouteImport } from './routes/_authenticated/organization/$organizationId/$clusterId/index' import { Route as AuthenticatedOrganizationOrganizationIdSettingsWebhookRouteImport } from './routes/_authenticated/organization/$organizationId/settings/webhook' import { Route as AuthenticatedOrganizationOrganizationIdSettingsMembersRouteImport } from './routes/_authenticated/organization/$organizationId/settings/members' +import { Route as AuthenticatedOrganizationOrganizationIdSettingsMcpServerRouteImport } from './routes/_authenticated/organization/$organizationId/settings/mcp-server' import { Route as AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRouteImport } from './routes/_authenticated/organization/$organizationId/settings/labels-annotations' import { Route as AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRouteImport } from './routes/_authenticated/organization/$organizationId/settings/helm-repositories' import { Route as AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRouteImport } from './routes/_authenticated/organization/$organizationId/settings/git-repository-access' @@ -299,6 +300,13 @@ const AuthenticatedOrganizationOrganizationIdSettingsMembersRoute = getParentRoute: () => AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, } as any) +const AuthenticatedOrganizationOrganizationIdSettingsMcpServerRoute = + AuthenticatedOrganizationOrganizationIdSettingsMcpServerRouteImport.update({ + id: '/mcp-server', + path: '/mcp-server', + getParentRoute: () => + AuthenticatedOrganizationOrganizationIdSettingsRouteRoute, + } as any) const AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute = AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRouteImport.update( { @@ -1380,6 +1388,7 @@ export interface FileRoutesByFullPath { '/organization/$organizationId/settings/git-repository-access': typeof AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute '/organization/$organizationId/settings/helm-repositories': typeof AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute '/organization/$organizationId/settings/labels-annotations': typeof AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute + '/organization/$organizationId/settings/mcp-server': typeof AuthenticatedOrganizationOrganizationIdSettingsMcpServerRoute '/organization/$organizationId/settings/members': typeof AuthenticatedOrganizationOrganizationIdSettingsMembersRoute '/organization/$organizationId/settings/webhook': typeof AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute '/organization/$organizationId/$clusterId': typeof AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute @@ -1523,6 +1532,7 @@ export interface FileRoutesByTo { '/organization/$organizationId/settings/git-repository-access': typeof AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute '/organization/$organizationId/settings/helm-repositories': typeof AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute '/organization/$organizationId/settings/labels-annotations': typeof AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute + '/organization/$organizationId/settings/mcp-server': typeof AuthenticatedOrganizationOrganizationIdSettingsMcpServerRoute '/organization/$organizationId/settings/members': typeof AuthenticatedOrganizationOrganizationIdSettingsMembersRoute '/organization/$organizationId/settings/webhook': typeof AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute '/organization/$organizationId/$clusterId': typeof AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute @@ -1661,6 +1671,7 @@ export interface FileRoutesById { '/_authenticated/organization/$organizationId/settings/git-repository-access': typeof AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute '/_authenticated/organization/$organizationId/settings/helm-repositories': typeof AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute '/_authenticated/organization/$organizationId/settings/labels-annotations': typeof AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute + '/_authenticated/organization/$organizationId/settings/mcp-server': typeof AuthenticatedOrganizationOrganizationIdSettingsMcpServerRoute '/_authenticated/organization/$organizationId/settings/members': typeof AuthenticatedOrganizationOrganizationIdSettingsMembersRoute '/_authenticated/organization/$organizationId/settings/webhook': typeof AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute '/_authenticated/organization/$organizationId/$clusterId/': typeof AuthenticatedOrganizationOrganizationIdClusterIdIndexRoute @@ -1810,6 +1821,7 @@ export interface FileRouteTypes { | '/organization/$organizationId/settings/git-repository-access' | '/organization/$organizationId/settings/helm-repositories' | '/organization/$organizationId/settings/labels-annotations' + | '/organization/$organizationId/settings/mcp-server' | '/organization/$organizationId/settings/members' | '/organization/$organizationId/settings/webhook' | '/organization/$organizationId/$clusterId' @@ -1953,6 +1965,7 @@ export interface FileRouteTypes { | '/organization/$organizationId/settings/git-repository-access' | '/organization/$organizationId/settings/helm-repositories' | '/organization/$organizationId/settings/labels-annotations' + | '/organization/$organizationId/settings/mcp-server' | '/organization/$organizationId/settings/members' | '/organization/$organizationId/settings/webhook' | '/organization/$organizationId/$clusterId' @@ -2090,6 +2103,7 @@ export interface FileRouteTypes { | '/_authenticated/organization/$organizationId/settings/git-repository-access' | '/_authenticated/organization/$organizationId/settings/helm-repositories' | '/_authenticated/organization/$organizationId/settings/labels-annotations' + | '/_authenticated/organization/$organizationId/settings/mcp-server' | '/_authenticated/organization/$organizationId/settings/members' | '/_authenticated/organization/$organizationId/settings/webhook' | '/_authenticated/organization/$organizationId/$clusterId/' @@ -2381,6 +2395,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsMembersRouteImport parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute } + '/_authenticated/organization/$organizationId/settings/mcp-server': { + id: '/_authenticated/organization/$organizationId/settings/mcp-server' + path: '/mcp-server' + fullPath: '/organization/$organizationId/settings/mcp-server' + preLoaderRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsMcpServerRouteImport + parentRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsRouteRoute + } '/_authenticated/organization/$organizationId/settings/labels-annotations': { id: '/_authenticated/organization/$organizationId/settings/labels-annotations' path: '/labels-annotations' @@ -3274,6 +3295,7 @@ interface AuthenticatedOrganizationOrganizationIdSettingsRouteRouteChildren { AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsGitRepositoryAccessRoute AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute + AuthenticatedOrganizationOrganizationIdSettingsMcpServerRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsMcpServerRoute AuthenticatedOrganizationOrganizationIdSettingsMembersRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsMembersRoute AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute AuthenticatedOrganizationOrganizationIdSettingsIndexRoute: typeof AuthenticatedOrganizationOrganizationIdSettingsIndexRoute @@ -3305,6 +3327,8 @@ const AuthenticatedOrganizationOrganizationIdSettingsRouteRouteChildren: Authent AuthenticatedOrganizationOrganizationIdSettingsHelmRepositoriesRoute, AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute: AuthenticatedOrganizationOrganizationIdSettingsLabelsAnnotationsRoute, + AuthenticatedOrganizationOrganizationIdSettingsMcpServerRoute: + AuthenticatedOrganizationOrganizationIdSettingsMcpServerRoute, AuthenticatedOrganizationOrganizationIdSettingsMembersRoute: AuthenticatedOrganizationOrganizationIdSettingsMembersRoute, AuthenticatedOrganizationOrganizationIdSettingsWebhookRoute: diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/mcp-server.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/mcp-server.tsx new file mode 100644 index 00000000000..f4022d40912 --- /dev/null +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/mcp-server.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +import { SettingsMcpServer } from '@qovery/domains/organizations/feature' + +export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/mcp-server')({ + component: RouteComponent, +}) + +function RouteComponent() { + return +} diff --git a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/route.tsx b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/route.tsx index be2b95252e3..71ddba4f3a0 100644 --- a/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/route.tsx +++ b/apps/console-v5/src/routes/_authenticated/organization/$organizationId/settings/route.tsx @@ -92,6 +92,12 @@ function RouteComponent() { icon: 'sparkles' as const, } + const mcpServerLink = { + title: 'MCP server', + to: `${pathSettings}/mcp-server`, + icon: 'code' as const, + } + const dangerZoneLink = { title: 'Danger zone', to: `${pathSettings}/danger-zone`, @@ -110,6 +116,7 @@ function RouteComponent() { webhookLink, apiTokenLink, aiCopilotLink, + mcpServerLink, ...(isOrganizationAdmin ? [dangerZoneLink] : []), ] diff --git a/libs/domains/organizations/feature/src/index.ts b/libs/domains/organizations/feature/src/index.ts index 06fbe80d77b..682e9b899e0 100644 --- a/libs/domains/organizations/feature/src/index.ts +++ b/libs/domains/organizations/feature/src/index.ts @@ -99,6 +99,7 @@ export * from './lib/settings-container-registries/settings-container-registries export * from './lib/settings-billing-summary/settings-billing-summary' export * from './lib/settings-webhook/settings-webhook' export * from './lib/settings-api-token/settings-api-token' +export * from './lib/settings-mcp-server/settings-mcp-server' export * from './lib/settings-danger-zone/settings-danger-zone' export * from './lib/settings-billing-details/settings-billing-details' export * from './lib/settings-roles/settings-roles' diff --git a/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.spec.tsx b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.spec.tsx new file mode 100644 index 00000000000..df7548ebf56 --- /dev/null +++ b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.spec.tsx @@ -0,0 +1,41 @@ +import { renderWithProviders, screen } from '@qovery/shared/util-tests' +import { SettingsMcpServer } from './settings-mcp-server' + +describe('SettingsMcpServer', () => { + it('should render heading and default Claude Code tab content', () => { + renderWithProviders() + + expect(screen.getByRole('heading', { name: 'MCP server' })).toBeInTheDocument() + expect(screen.getByRole('heading', { name: 'Configure MCP server' })).toBeInTheDocument() + expect(screen.getByText('Claude Code')).toBeInTheDocument() + expect(screen.getByText('Codex')).toBeInTheDocument() + expect( + screen.getByText('claude mcp add --transport http qovery https://mcp.qovery.com/mcp --callback-port 4242') + ).toBeInTheDocument() + }) + + it('should switch to Codex instructions when clicking Codex tab', async () => { + const { userEvent } = renderWithProviders() + + await userEvent.click(screen.getByText('Codex')) + + expect(screen.getByText('1. Update your config.toml')).toBeInTheDocument() + expect(screen.getByText('mcp_oauth_callback_port = 4242')).toBeInTheDocument() + expect(screen.getByText("codex mcp add qovery --url 'https://mcp.qovery.com/mcp'")).toBeInTheDocument() + }) + + it('should display access mode badges for MCP tools', () => { + renderWithProviders() + + expect(screen.getByText('devops_copilot')).toBeInTheDocument() + expect(screen.getByText('read-write')).toBeInTheDocument() + expect(screen.getAllByText('read-only')).toHaveLength(4) + }) + + it('should render docs link for API token setup', () => { + renderWithProviders() + + const docsLink = screen.getByRole('link', { name: 'See how' }) + expect(docsLink).toHaveAttribute('href', 'https://www.qovery.com/docs/copilot/mcp-server#2-api-token') + }) +}) diff --git a/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx new file mode 100644 index 00000000000..d112b2b8fc5 --- /dev/null +++ b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx @@ -0,0 +1,212 @@ +import { type ReactNode, useState } from 'react' +import { NeedHelp } from '@qovery/shared/assistant/feature' +import { Badge, CopyButton, ExternalLink, Heading, Icon, Navbar, Section } from '@qovery/shared/ui' +import { useDocumentTitle } from '@qovery/shared/util-hooks' + +const MCP_TOOLS = [ + { + name: 'devops_copilot', + description: + 'Send a message to the DevOps Copilot for Qovery actions. Resolve organization, project, environment, and service IDs first, then reference resources by UUID in the message.', + icon: 'sparkles', + accessMode: 'read-write', + }, + { + name: 'get_service_logs', + description: 'Fetch service or application logs, optionally scoped to a deployment or a specific pod.', + icon: 'scroll', + accessMode: 'read-only', + }, + { + name: 'list_environments', + description: 'List all environments available in a Qovery project.', + icon: 'layer-group', + accessMode: 'read-only', + }, + { + name: 'list_organizations', + description: 'List all organizations the authenticated token can access.', + icon: 'building', + accessMode: 'read-only', + }, + { + name: 'list_projects', + description: 'List all projects available in a Qovery organization.', + icon: 'folder-open', + accessMode: 'read-only', + }, +] + +interface CommandBlockProps { + content: string + showPrompt?: boolean +} + +function CommandBlock({ content, showPrompt = false }: CommandBlockProps) { + return ( +
+
+ {showPrompt ? $ : null} + {content} +
+
+ +
+
+ ) +} + +interface InstructionSectionProps { + number: number + title: string + description?: string + children: ReactNode +} + +function InstructionSection({ number, title, description, children }: InstructionSectionProps) { + return ( +
+
+

+ {number}. {title} +

+ {description ?

{description}

: null} +
+ {children} +
+ ) +} + +export function SettingsMcpServer() { + useDocumentTitle('MCP server - Organization settings') + const [activeClient, setActiveClient] = useState<'claude-code' | 'codex'>('claude-code') + + return ( +
+
+
+
+
+ MCP server +

+ The Qovery MCP Server lets you interact with your Qovery infrastructure from any MCP-compatible client + (Claude, Claude Code, ChatGPT, etc.) using natural language. +

+ +
+
+
+ +
+
+
+ Configure MCP server +
+ +
+
+
+ + setActiveClient('claude-code')} + > + + Claude Code + + setActiveClient('codex')}> + + Codex + + +
+ +
+ {activeClient === 'claude-code' ? ( + + + + ) : ( +
+ + + + + + +
+ )} +
+
+ +

+ You can also configure the MCP server through an API token.{' '} + + See how + + . +

+
+
+ +
+
+ MCP tools +

+ Our Qovery MCP server provides MCP tools that let AI assistants search through projects, environments, + service logs and more. +

+
+ +
+ {MCP_TOOLS.map((tool) => ( +
+
+ + {tool.name} + + {tool.accessMode} + +
+

{tool.description}

+
+ ))} +
+
+
+
+
+ ) +} From c4cd5da80f792ddf98f5dfbe937219c1c3b7d271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Grandin?= Date: Thu, 16 Apr 2026 16:00:42 +0200 Subject: [PATCH 2/4] fix(settings-mcp-server): correct type assertion for MCP_TOOLS array --- .../feature/src/lib/settings-mcp-server/settings-mcp-server.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx index d112b2b8fc5..431861f712f 100644 --- a/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx +++ b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx @@ -35,7 +35,7 @@ const MCP_TOOLS = [ icon: 'folder-open', accessMode: 'read-only', }, -] +] as const interface CommandBlockProps { content: string From ed9aef03ecd1e87e27729a2d4f3f98e3563a3a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Grandin?= Date: Fri, 17 Apr 2026 13:41:59 +0200 Subject: [PATCH 3/4] refactor(settings-mcp-server): update component to enhance API token configuration steps and adjust headings --- .../settings-mcp-server.spec.tsx | 24 ++-- .../settings-mcp-server.tsx | 121 +++++++----------- 2 files changed, 60 insertions(+), 85 deletions(-) diff --git a/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.spec.tsx b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.spec.tsx index df7548ebf56..f7e5f956432 100644 --- a/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.spec.tsx +++ b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.spec.tsx @@ -1,12 +1,18 @@ import { renderWithProviders, screen } from '@qovery/shared/util-tests' import { SettingsMcpServer } from './settings-mcp-server' +jest.mock('@tanstack/react-router', () => ({ + ...jest.requireActual('@tanstack/react-router'), + useParams: () => ({ organizationId: 'org-1' }), +})) + describe('SettingsMcpServer', () => { it('should render heading and default Claude Code tab content', () => { renderWithProviders() expect(screen.getByRole('heading', { name: 'MCP server' })).toBeInTheDocument() - expect(screen.getByRole('heading', { name: 'Configure MCP server' })).toBeInTheDocument() + expect(screen.getByRole('heading', { name: 'Configure via OAuth (recommended)' })).toBeInTheDocument() + expect(screen.getByRole('heading', { name: 'Configure via API token' })).toBeInTheDocument() expect(screen.getByText('Claude Code')).toBeInTheDocument() expect(screen.getByText('Codex')).toBeInTheDocument() expect( @@ -24,18 +30,12 @@ describe('SettingsMcpServer', () => { expect(screen.getByText("codex mcp add qovery --url 'https://mcp.qovery.com/mcp'")).toBeInTheDocument() }) - it('should display access mode badges for MCP tools', () => { - renderWithProviders() - - expect(screen.getByText('devops_copilot')).toBeInTheDocument() - expect(screen.getByText('read-write')).toBeInTheDocument() - expect(screen.getAllByText('read-only')).toHaveLength(4) - }) - - it('should render docs link for API token setup', () => { + it('should render API token setup steps', () => { renderWithProviders() - const docsLink = screen.getByRole('link', { name: 'See how' }) - expect(docsLink).toHaveAttribute('href', 'https://www.qovery.com/docs/copilot/mcp-server#2-api-token') + expect(screen.getByText('1. Generate token and copy it')).toBeInTheDocument() + expect(screen.getByRole('button', { name: 'Generate API token' })).toBeInTheDocument() + expect(screen.getByText('2. Authenticate through your API token')).toBeInTheDocument() + expect(screen.getByText(/Authorization: Token your_qovery_token/)).toBeInTheDocument() }) }) diff --git a/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx index 431861f712f..44216174210 100644 --- a/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx +++ b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx @@ -1,41 +1,9 @@ +import { useParams } from '@tanstack/react-router' import { type ReactNode, useState } from 'react' import { NeedHelp } from '@qovery/shared/assistant/feature' -import { Badge, CopyButton, ExternalLink, Heading, Icon, Navbar, Section } from '@qovery/shared/ui' +import { Button, CopyButton, Heading, Icon, Navbar, Section, useModal } from '@qovery/shared/ui' import { useDocumentTitle } from '@qovery/shared/util-hooks' - -const MCP_TOOLS = [ - { - name: 'devops_copilot', - description: - 'Send a message to the DevOps Copilot for Qovery actions. Resolve organization, project, environment, and service IDs first, then reference resources by UUID in the message.', - icon: 'sparkles', - accessMode: 'read-write', - }, - { - name: 'get_service_logs', - description: 'Fetch service or application logs, optionally scoped to a deployment or a specific pod.', - icon: 'scroll', - accessMode: 'read-only', - }, - { - name: 'list_environments', - description: 'List all environments available in a Qovery project.', - icon: 'layer-group', - accessMode: 'read-only', - }, - { - name: 'list_organizations', - description: 'List all organizations the authenticated token can access.', - icon: 'building', - accessMode: 'read-only', - }, - { - name: 'list_projects', - description: 'List all projects available in a Qovery organization.', - icon: 'folder-open', - accessMode: 'read-only', - }, -] as const +import CrudModalFeature from '../settings-api-token/crud-modal-feature/crud-modal-feature' interface CommandBlockProps { content: string @@ -43,8 +11,14 @@ interface CommandBlockProps { } function CommandBlock({ content, showPrompt = false }: CommandBlockProps) { + const isMultiline = content.includes('\n') + return ( -
+
{showPrompt ? $ : null} {content} @@ -78,6 +52,8 @@ function InstructionSection({ number, title, description, children }: Instructio } export function SettingsMcpServer() { + const { organizationId = '' } = useParams({ strict: false }) + const { openModal, closeModal } = useModal() useDocumentTitle('MCP server - Organization settings') const [activeClient, setActiveClient] = useState<'claude-code' | 'codex'>('claude-code') @@ -100,7 +76,7 @@ export function SettingsMcpServer() {
- Configure MCP server + Configure via OAuth (recommended)
@@ -139,7 +115,7 @@ export function SettingsMcpServer() { /> ) : ( -
+
- -

- You can also configure the MCP server through an API token.{' '} - - See how - - . -

- MCP tools -

- Our Qovery MCP server provides MCP tools that let AI assistants search through projects, environments, - service logs and more. -

+ Configure via API token
-
- {MCP_TOOLS.map((tool) => ( -
+
+ -
- - {tool.name} - +
-

{tool.description}

-
- ))} + + + + + +
From 0938d7fe618b3888155dc758c7ddb6bd98790381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Grandin?= Date: Mon, 20 Apr 2026 09:57:48 +0200 Subject: [PATCH 4/4] style(settings-mcp-server): add cursor pointer to Navbar items for better UX --- .../src/lib/settings-mcp-server/settings-mcp-server.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx index 44216174210..850c77f471f 100644 --- a/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx +++ b/libs/domains/organizations/feature/src/lib/settings-mcp-server/settings-mcp-server.tsx @@ -90,12 +90,18 @@ export function SettingsMcpServer() { setActiveClient('claude-code')} > Claude Code - setActiveClient('codex')}> + setActiveClient('codex')} + > Codex