diff --git a/app/src/components/channels/mcp/McpStatusBadge.test.tsx b/app/src/components/channels/mcp/McpStatusBadge.test.tsx
new file mode 100644
index 0000000000..2c3a840a2a
--- /dev/null
+++ b/app/src/components/channels/mcp/McpStatusBadge.test.tsx
@@ -0,0 +1,44 @@
+/**
+ * Tests for McpStatusBadge — renders the i18n'd label and a11y role
+ * for each ServerStatus, and forwards custom className.
+ */
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+
+import McpStatusBadge from './McpStatusBadge';
+import type { ServerStatus } from './types';
+
+describe('McpStatusBadge', () => {
+ it.each<[ServerStatus, string]>([
+ ['connected', 'Connected'],
+ ['connecting', 'Connecting'],
+ ['disconnected', 'Disconnected'],
+ ['error', 'Error'],
+ ])('renders i18n label for status=%s', (status, expectedLabel) => {
+ render();
+ expect(screen.getByRole('status')).toHaveTextContent(expectedLabel);
+ });
+
+ it('exposes role="status" and aria-live="polite" for assistive tech', () => {
+ render();
+ const badge = screen.getByRole('status');
+ expect(badge).toHaveAttribute('aria-live', 'polite');
+ });
+
+ it('falls back to the disconnected style for an unknown status value', () => {
+ // ServerStatus is a closed union, but the runtime fallback exists for
+ // forward-compat with possible future Rust-side variants — exercise it.
+ render();
+ expect(screen.getByRole('status')).toHaveTextContent('Disconnected');
+ });
+
+ it('appends the optional className without dropping the built-in classes', () => {
+ render();
+ const badge = screen.getByRole('status');
+ expect(badge.className).toContain('my-custom-class');
+ expect(badge.className).toContain('ml-2');
+ // Built-in look-and-feel preserved.
+ expect(badge.className).toContain('rounded-full');
+ expect(badge.className).toContain('bg-sage-500/10');
+ });
+});
diff --git a/app/src/components/channels/mcp/McpStatusBadge.tsx b/app/src/components/channels/mcp/McpStatusBadge.tsx
index 586fb8fc1e..009c54c9ba 100644
--- a/app/src/components/channels/mcp/McpStatusBadge.tsx
+++ b/app/src/components/channels/mcp/McpStatusBadge.tsx
@@ -1,25 +1,28 @@
/**
* Status badge for MCP server connection states.
- * Mirrors ChannelStatusBadge but uses ServerStatus values.
+ * Mirrors ChannelStatusBadge but uses ServerStatus values; reuses the
+ * shared `channels.status.*` i18n keys since the label vocabulary is
+ * identical (Connected / Connecting / Disconnected / Error).
*/
+import { useT } from '../../../lib/i18n/I18nContext';
import type { ServerStatus } from './types';
-const STATUS_STYLES: Record = {
+const STATUS_META: Record = {
connected: {
- label: 'Connected',
+ i18nKey: 'channels.status.connected',
className: 'bg-sage-500/10 text-sage-700 border-sage-500/30 dark:text-sage-300',
},
connecting: {
- label: 'Connecting',
+ i18nKey: 'channels.status.connecting',
className: 'bg-amber-500/10 text-amber-700 border-amber-500/30 dark:text-amber-300',
},
disconnected: {
- label: 'Disconnected',
+ i18nKey: 'channels.status.disconnected',
className:
'bg-stone-100 dark:bg-neutral-800 text-stone-500 dark:text-neutral-400 border-stone-200 dark:border-neutral-700',
},
error: {
- label: 'Error',
+ i18nKey: 'channels.status.error',
className: 'bg-coral-500/10 text-coral-700 border-coral-500/30 dark:text-coral-300',
},
};
@@ -30,11 +33,14 @@ interface McpStatusBadgeProps {
}
const McpStatusBadge = ({ status, className = '' }: McpStatusBadgeProps) => {
- const style = STATUS_STYLES[status] ?? STATUS_STYLES.disconnected;
+ const { t } = useT();
+ const meta = STATUS_META[status] ?? STATUS_META.disconnected;
return (
- {style.label}
+ role="status"
+ aria-live="polite"
+ className={`shrink-0 px-2 py-1 text-[11px] border rounded-full ${meta.className} ${className}`}>
+ {t(meta.i18nKey)}
);
};