diff --git a/mlflow/server/js/src/common/components/MlflowSidebar.tsx b/mlflow/server/js/src/common/components/MlflowSidebar.tsx index a6c8be27871bb..c665332d291fc 100644 --- a/mlflow/server/js/src/common/components/MlflowSidebar.tsx +++ b/mlflow/server/js/src/common/components/MlflowSidebar.tsx @@ -20,7 +20,7 @@ import { InfoBookIcon, Tooltip, NewWindowIcon, - WrenchIcon, + McpIcon, } from '@databricks/design-system'; import { useQueryClient } from '@mlflow/mlflow/src/common/utils/reactQueryHooks'; import type { Location } from '../utils/RoutingUtils'; @@ -243,7 +243,7 @@ export function MlflowSidebar({ ? [ { key: 'mcp-registry', - icon: , + icon: , linkProps: { to: MCPRegistryRoutes.mcpRegistryPageRoute, isActive: isMCPRegistryActive, diff --git a/mlflow/server/js/src/mcp-registry/components/MCPServerCard.tsx b/mlflow/server/js/src/mcp-registry/components/MCPServerCard.tsx index b524bf8563524..633ad4eb5d54d 100644 --- a/mlflow/server/js/src/mcp-registry/components/MCPServerCard.tsx +++ b/mlflow/server/js/src/mcp-registry/components/MCPServerCard.tsx @@ -3,9 +3,10 @@ import { FormattedMessage, useIntl } from 'react-intl'; import type { MCPServer } from '../types'; import MCPRegistryRoutes from '../routes'; -import { resolveDisplayName } from '../utils'; +import { resolveDisplayName, resolveIconSrc } from '../utils'; import { useLatestMCPServerVersionQuery } from '../hooks/useMCPServerDetailQuery'; import { CardIconWrapper } from './CardIconWrapper'; +import { MCPServerIcon } from './MCPServerIcon'; import Utils from '../../common/utils/Utils'; export const MCPServerCard = ({ server }: { server: MCPServer }) => { @@ -28,7 +29,7 @@ export const MCPServerCard = ({ server }: { server: MCPServer }) => { >
- +
diff --git a/mlflow/server/js/src/mcp-registry/components/MCPServerIcon.tsx b/mlflow/server/js/src/mcp-registry/components/MCPServerIcon.tsx new file mode 100644 index 0000000000000..c115bd1036e77 --- /dev/null +++ b/mlflow/server/js/src/mcp-registry/components/MCPServerIcon.tsx @@ -0,0 +1,27 @@ +import { useState } from 'react'; +import { McpIcon, useDesignSystemTheme } from '@databricks/design-system'; + +export const MCPServerIcon = ({ iconSrc, className }: { iconSrc?: string; className?: string }) => { + const { theme } = useDesignSystemTheme(); + const [iconError, setIconError] = useState(false); + + if (iconSrc && !iconError) { + return ( + setIconError(true)} + className={className} + css={{ + flexShrink: 0, + width: theme.general.iconFontSize, + height: theme.general.iconFontSize, + objectFit: 'contain', + }} + /> + ); + } + + return ; +}; diff --git a/mlflow/server/js/src/mcp-registry/components/MCPServerListTable.tsx b/mlflow/server/js/src/mcp-registry/components/MCPServerListTable.tsx index 6303d3e359ff3..1531e0f1d8c74 100644 --- a/mlflow/server/js/src/mcp-registry/components/MCPServerListTable.tsx +++ b/mlflow/server/js/src/mcp-registry/components/MCPServerListTable.tsx @@ -4,7 +4,6 @@ import type { CursorPaginationProps } from '@databricks/design-system'; import { CursorPagination, Empty, - McpIcon, NoIcon, Overflow, PencilIcon, @@ -24,10 +23,11 @@ import { FormattedMessage, useIntl } from 'react-intl'; import type { MCPServer } from '../types'; import MCPRegistryRoutes from '../routes'; -import { emptyCenterStyles, resolveDisplayName, tagsRecordToArray } from '../utils'; +import { emptyCenterStyles, resolveDisplayName, tagsRecordToArray, resolveIconSrc } from '../utils'; import { useLatestMCPServerVersionQuery } from '../hooks/useMCPServerDetailQuery'; import { Link } from '../../common/utils/RoutingUtils'; import { KeyValueTag } from '../../common/components/KeyValueTag'; +import { MCPServerIcon } from './MCPServerIcon'; import Utils from '../../common/utils/Utils'; interface MCPServerTableMeta { @@ -36,10 +36,13 @@ interface MCPServerTableMeta { const MCPServerNameCell = ({ getValue, row }: CellContext) => { const { theme } = useDesignSystemTheme(); + const { data: latestVersion } = useLatestMCPServerVersionQuery(row.original.name); const value = getValue() as string; return ( - +
- +
{displayName} diff --git a/mlflow/server/js/src/mcp-registry/pages/MCPRegistryPage.tsx b/mlflow/server/js/src/mcp-registry/pages/MCPRegistryPage.tsx index d5fd476f98ced..d68307e0d56e1 100644 --- a/mlflow/server/js/src/mcp-registry/pages/MCPRegistryPage.tsx +++ b/mlflow/server/js/src/mcp-registry/pages/MCPRegistryPage.tsx @@ -9,7 +9,7 @@ import { PlusIcon, SegmentedControlButton, SegmentedControlGroup, - WrenchIcon, + McpIcon, Spacer, TableFilterInput, TableFilterLayout, @@ -36,7 +36,6 @@ import type { MCPAccessBinding } from '../types'; import { MCPAccessBindingCardGrid } from '../components/MCPAccessBindingCardGrid'; import { MCPAccessBindingListTable } from '../components/MCPAccessBindingListTable'; import { AccessBindingModal } from '../components/AccessBindingModal'; -import type { MCPServer } from '../types'; import { useDebounce } from 'use-debounce'; type ViewMode = 'list' | 'grid'; @@ -168,7 +167,7 @@ const MCPRegistryPage = () => { padding: theme.spacing.sm, }} > - + diff --git a/mlflow/server/js/src/mcp-registry/pages/MCPServerDetailPage.test.tsx b/mlflow/server/js/src/mcp-registry/pages/MCPServerDetailPage.test.tsx index e18b63902dd5b..7797d9b471ac4 100644 --- a/mlflow/server/js/src/mcp-registry/pages/MCPServerDetailPage.test.tsx +++ b/mlflow/server/js/src/mcp-registry/pages/MCPServerDetailPage.test.tsx @@ -528,5 +528,5 @@ describe('MCPServerDetailPage', () => { }, { timeout: 10000 }, ); - }); + }, 15000); }); diff --git a/mlflow/server/js/src/mcp-registry/types.ts b/mlflow/server/js/src/mcp-registry/types.ts index fde0779cff51c..20e697aefe7f8 100644 --- a/mlflow/server/js/src/mcp-registry/types.ts +++ b/mlflow/server/js/src/mcp-registry/types.ts @@ -8,10 +8,10 @@ export interface MCPTool { name: string; title?: string; description?: string; + icons?: MCPIcon[]; inputSchema?: Record; outputSchema?: Record; annotations?: Record; - icons?: MCPIcon[]; execution?: Record; } @@ -124,6 +124,7 @@ export interface ServerJSONPayload { version: string; title?: string; description?: string; + icons?: MCPIcon[]; packages?: ServerJSONPackage[]; remotes?: ServerJSONTransport[]; repository?: ServerJSONRepository; diff --git a/mlflow/server/js/src/mcp-registry/utils.ts b/mlflow/server/js/src/mcp-registry/utils.ts index b86c2243434ad..9c44430405bb3 100644 --- a/mlflow/server/js/src/mcp-registry/utils.ts +++ b/mlflow/server/js/src/mcp-registry/utils.ts @@ -1,5 +1,5 @@ import type { TagProps } from '@databricks/design-system'; -import type { MCPRemoteTransportType, MCPStatus, MCPTool, ServerJSONPayload } from './types'; +import type { MCPIcon, MCPRemoteTransportType, MCPStatus, MCPTool, ServerJSONPayload } from './types'; export const STATUS_TAG_COLOR: Record = { draft: 'charcoal', @@ -85,6 +85,10 @@ export const buildSearchFilterClause = (searchFilter: string | undefined, field: return `${field} LIKE '%${searchFilter.replace(/'/g, "''")}%'`; }; +export const resolveIconSrc = (icons: MCPIcon[] | undefined): string | undefined => { + return icons?.[0]?.src; +}; + export const isValidEndpointUrl = (url: string): boolean => { const trimmed = url.trim(); if (!/^https?:\/\//.test(trimmed)) return false;