/**
* Copyright (c) 2026-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useCallback, useEffect, useRef } from 'react';
import { observer } from 'mobx-react-lite';
import { flowResult } from 'mobx';
import { useAuth } from 'react-oidc-context';
import { useParams } from '@finos/legend-application/browser';
import { guaranteeNonNullable } from '@finos/legend-shared';
import {
ArrowLeftIcon,
ChevronDownIcon,
clsx,
CopyIcon,
MarkdownTextViewer,
} from '@finos/legend-art';
import {
Accordion,
AccordionDetails,
AccordionSummary,
Chip,
CircularProgress,
Container,
IconButton,
Tooltip,
Typography,
} from '@mui/material';
import type {
McpServer,
McpServerTool,
} from '@finos/legend-server-marketplace';
import { LegendMarketplacePage } from '../LegendMarketplacePage.js';
import { useIntelligenceCatalogStore } from '../../application/providers/IntelligenceCatalogStoreProvider.js';
import { useLegendMarketplaceBaseStore } from '../../application/providers/LegendMarketplaceFrameworkProvider.js';
import {
type McpServerPathParams,
LEGEND_MARKETPLACE_ROUTE_PATTERN,
LEGEND_MARKETPLACE_ROUTE_PATTERN_TOKEN,
} from '../../__lib__/LegendMarketplaceNavigation.js';
import {
formatMcpTimestamp,
hasMeaningfulMcpDescription,
NO_DESCRIPTION_PLACEHOLDER,
NO_VALUE_PLACEHOLDER,
} from '../../stores/intelligence/IntelligenceCatalogUtils.js';
import { parseMcpToolDocumentation } from '../../stores/intelligence/McpToolDocumentation.js';
import { LegendMarketplaceTelemetryHelper } from '../../__lib__/LegendMarketplaceTelemetryHelper.js';
enum MCP_SUPPORT_LINK_TYPE {
DOCUMENTATION = 'documentation',
DISTRIBUTION_LIST = 'distribution-list',
}
const displayFieldValue = (value: string | undefined): string =>
value ?? NO_VALUE_PLACEHOLDER;
type McpServerViewerAccent =
| 'connection'
| 'security'
| 'ownership'
| 'registry';
const McpServerViewerPanel = (props: {
title: string;
accent: McpServerViewerAccent;
children: React.ReactNode;
}): React.ReactNode => {
const { title, accent, children } = props;
return (
);
};
const McpServerViewerField = (props: {
label: string;
children: React.ReactNode;
}): React.ReactNode => {
const { label, children } = props;
return (
<>
{label}
{children}
>
);
};
const McpServerViewerSection = (props: {
title: string;
count?: number | undefined;
scrollable: boolean;
children: React.ReactNode;
}): React.ReactNode => {
const { title, count, scrollable, children } = props;
return (
{title}
{count !== undefined && (
{count}
)}
{children}
);
};
const McpServerViewerChipList = (props: {
values: string[] | undefined;
}): React.ReactNode => {
const { values } = props;
if (values === undefined || values.length === 0) {
return NO_VALUE_PLACEHOLDER;
}
return (
{values.map((value) => (
))}
);
};
const McpServerCopyButton = observer(
(props: {
mcpServerName: string;
label: string;
value: string;
}): React.ReactNode => {
const { mcpServerName, label, value } = props;
const applicationStore = useLegendMarketplaceBaseStore().applicationStore;
const copyValue = (event: React.MouseEvent): void => {
event.stopPropagation();
LegendMarketplaceTelemetryHelper.logEvent_ClickMcpServerCopy(
applicationStore.telemetryService,
mcpServerName,
label,
);
applicationStore.clipboardService
.copyTextToClipboard(value)
.then(() =>
applicationStore.notificationService.notifySuccess(
`Copied ${label} to clipboard`,
),
)
.catch(applicationStore.alertUnhandledError);
};
return (
);
},
);
const McpServerDistributionList = observer(
(props: {
mcpServerName: string;
address: string | undefined;
}): React.ReactNode => {
const { mcpServerName, address } = props;
const applicationStore = useLegendMarketplaceBaseStore().applicationStore;
if (address === undefined) {
return NO_VALUE_PLACEHOLDER;
}
return (
LegendMarketplaceTelemetryHelper.logEvent_ClickMcpServerSupportLink(
applicationStore.telemetryService,
mcpServerName,
MCP_SUPPORT_LINK_TYPE.DISTRIBUTION_LIST,
)
}
>
{address}
);
},
);
const McpServerViewerHeader = observer(
(props: { mcpServer: McpServer }): React.ReactNode => {
const { mcpServer } = props;
const intelligenceCatalogStore = useIntelligenceCatalogStore();
return (
{intelligenceCatalogStore.getVendorForServer(mcpServer)}
{mcpServer.displayName}
{mcpServer.active && (
)}
{mcpServer.requireApproval && (
)}
{mcpServer.category?.map((category) => (
))}
{hasMeaningfulMcpDescription(mcpServer)
? mcpServer.description
: NO_DESCRIPTION_PLACEHOLDER}
);
},
);
const McpServerConnectionPanel = (props: {
mcpServer: McpServer;
}): React.ReactNode => {
const { mcpServer } = props;
return (
{mcpServer.url}
{mcpServer.type}
{displayFieldValue(mcpServer.tokenType)}
);
};
const McpServerClassificationPanel = (props: {
mcpServer: McpServer;
}): React.ReactNode => {
const { mcpServer } = props;
return (
{displayFieldValue(mcpServer.securityDetail?.dataPrivacyClassification)}
{displayFieldValue(
mcpServer.securityDetail?.dataSensitivityClassification,
)}
);
};
const McpServerOwnershipPanel = (props: {
mcpServer: McpServer;
}): React.ReactNode => {
const { mcpServer } = props;
return (
{displayFieldValue(mcpServer.ownershipInfo?.ownerDid)}
);
};
const McpServerRegistrationPanel = (props: {
mcpServer: McpServer;
}): React.ReactNode => {
const { mcpServer } = props;
return (
{mcpServer.name}
{displayFieldValue(formatMcpTimestamp(mcpServer.createdAt))}
{displayFieldValue(formatMcpTimestamp(mcpServer.updatedAt))}
);
};
const McpServerSampleQuestionList = (props: {
mcpServerName: string;
questions: string[];
}): React.ReactNode => {
const { mcpServerName, questions } = props;
return (
{questions.map((question) => (
{question}
))}
);
};
const McpServerToolAccordion = (props: {
mcpServerName: string;
tool: McpServerTool;
}): React.ReactNode => {
const { mcpServerName, tool } = props;
const applicationStore = useLegendMarketplaceBaseStore().applicationStore;
const { servicePattern, documentation, groundingRules } =
parseMcpToolDocumentation(tool.description ?? '');
return (
{
if (expanded) {
LegendMarketplaceTelemetryHelper.logEvent_ExpandMcpServerTool(
applicationStore.telemetryService,
mcpServerName,
tool.name,
);
}
}}
>
}
className="marketplace-mcp-server-viewer__tool-summary"
>
{tool.title ?? tool.name}
{servicePattern !== undefined && (
)}
{groundingRules !== undefined && (
{
if (expanded) {
LegendMarketplaceTelemetryHelper.logEvent_ExpandMcpServerGroundingRules(
applicationStore.telemetryService,
mcpServerName,
tool.name,
);
}
}}
>
}
className="marketplace-mcp-server-viewer__tool-grounding-summary"
>
LLM grounding rules
)}
);
};
const McpServerSupportSection = observer(
(props: { mcpServer: McpServer }): React.ReactNode => {
const { mcpServer } = props;
const applicationStore = useLegendMarketplaceBaseStore().applicationStore;
const supportInfo = mcpServer.supportInfo;
if (supportInfo === undefined || supportInfo.length === 0) {
return null;
}
return (
{supportInfo.map((support) => (
{support.message ?? NO_VALUE_PLACEHOLDER}
{support.documentationLink !== undefined && (
LegendMarketplaceTelemetryHelper.logEvent_ClickMcpServerSupportLink(
applicationStore.telemetryService,
mcpServer.name,
MCP_SUPPORT_LINK_TYPE.DOCUMENTATION,
)
}
>
Documentation
)}
))}
);
},
);
const McpServerToolsSection = observer(
(props: { mcpServer: McpServer }): React.ReactNode => {
const { mcpServer } = props;
const intelligenceCatalogStore = useIntelligenceCatalogStore();
const tools = intelligenceCatalogStore.toolsByServerName.get(
mcpServer.name,
);
if (intelligenceCatalogStore.isFetchingToolsFor(mcpServer.name)) {
return (
);
}
return (
{tools === undefined || tools.tools.length === 0 ? (
This server exposes no tools
) : (
tools.tools.map((tool) => (
))
)}
);
},
);
const McpServerViewerContent = observer(
(props: { mcpServer: McpServer }): React.ReactNode => {
const { mcpServer } = props;
const intelligenceCatalogStore = useIntelligenceCatalogStore();
const applicationStore = useLegendMarketplaceBaseStore().applicationStore;
const auth = useAuth();
const token = auth.user?.access_token;
const tokenRef = useRef(token);
tokenRef.current = token;
// the registry does not guarantee distinct questions, and a repeated one would
// both render twice and collide as a React key
const sampleQuestions = mcpServer.sampleQuestions
? Array.from(new Set(mcpServer.sampleQuestions))
: undefined;
useEffect(() => {
const currentToken = tokenRef.current;
if (currentToken) {
flowResult(
intelligenceCatalogStore.fetchMcpServerTools(
mcpServer.name,
currentToken,
),
).catch(applicationStore.alertUnhandledError);
}
}, [intelligenceCatalogStore, mcpServer.name, token, applicationStore]);
return (
{sampleQuestions !== undefined && sampleQuestions.length > 0 && (
)}
);
},
);
export const McpServerViewer = observer(() => {
const intelligenceCatalogStore = useIntelligenceCatalogStore();
const applicationStore = useLegendMarketplaceBaseStore().applicationStore;
const auth = useAuth();
const token = auth.user?.access_token;
const tokenRef = useRef(token);
tokenRef.current = token;
const params = useParams();
const mcpServerName = decodeURIComponent(
guaranteeNonNullable(
params[LEGEND_MARKETPLACE_ROUTE_PATTERN_TOKEN.MCP_SERVER_NAME],
),
);
const mcpServer = intelligenceCatalogStore.findLegendMcpServer(mcpServerName);
const fetchMcpServers = useCallback((): void => {
const currentToken = tokenRef.current;
if (currentToken) {
flowResult(intelligenceCatalogStore.fetchMcpServers(currentToken)).catch(
applicationStore.alertUnhandledError,
);
}
}, [intelligenceCatalogStore, applicationStore]);
useEffect(() => {
fetchMcpServers();
}, [fetchMcpServers, token]);
const renderContent = (): React.ReactNode => {
if (mcpServer) {
return ;
}
if (intelligenceCatalogStore.fetchingServersState.hasFailed) {
return (
Could not load MCP servers from the registry
{
LegendMarketplaceTelemetryHelper.logEvent_ClickMcpServerRetry(
applicationStore.telemetryService,
mcpServerName,
);
fetchMcpServers();
}}
>
Try again
);
}
if (intelligenceCatalogStore.fetchingServersState.hasSucceeded) {
return (
MCP server '{mcpServerName}' not found
);
}
return ;
};
return (
{
LegendMarketplaceTelemetryHelper.logEvent_ClickMcpServerBack(
applicationStore.telemetryService,
mcpServerName,
);
applicationStore.navigationService.navigator.goToLocation(
LEGEND_MARKETPLACE_ROUTE_PATTERN.AGENTS,
);
}}
>
Back to MCPs
{renderContent()}
);
});