Skip to content

Commit 61a67f9

Browse files
Icons and badges in MCP servers (#479)
* checkpoint * big icons * mobile * Update app/_components/tool-info.tsx Co-authored-by: Sergio Serrano <[email protected]> * padding fix --------- Co-authored-by: Sergio Serrano <[email protected]>
1 parent 606faf1 commit 61a67f9

File tree

2 files changed

+140
-45
lines changed

2 files changed

+140
-45
lines changed

app/_components/tool-info.tsx

Lines changed: 110 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1+
"use client";
2+
import {
3+
Badge,
4+
ByocBadge,
5+
getToolkitIconByName,
6+
ProBadge,
7+
} from "@arcadeai/design-system";
8+
import { usePathname } from "next/navigation";
19
import type React from "react";
10+
import { findToolkitFromPath } from "../en/mcp-servers/components/toolkit-utils";
11+
import { TYPE_CONFIG } from "../en/mcp-servers/components/type-config";
212

313
type ToolInfoProps = {
414
description: string;
@@ -11,6 +21,31 @@ type ToolInfoProps = {
1121
note?: string;
1222
};
1323

24+
// Render toolkit badges
25+
const ToolkitBadges: React.FC<{
26+
typeInfo: { label: string; icon: React.ElementType; color: string } | null;
27+
isByoc: boolean;
28+
isPro: boolean;
29+
}> = ({ typeInfo, isByoc, isPro }) => {
30+
const TypeIcon = typeInfo?.icon;
31+
32+
return (
33+
<div className="mt-3 flex flex-wrap items-center gap-1.5">
34+
{typeInfo && TypeIcon && (
35+
<Badge
36+
className="gap-1 border-gray-300 bg-gray-100 text-gray-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300"
37+
variant="outline"
38+
>
39+
<TypeIcon className={`h-3.5 w-3.5 ${typeInfo.color}`} />
40+
{typeInfo.label}
41+
</Badge>
42+
)}
43+
{isByoc && <ByocBadge className="gap-1.5 rounded-md" />}
44+
{isPro && <ProBadge className="gap-1.5 rounded-md" />}
45+
</div>
46+
);
47+
};
48+
1449
const ToolInfo: React.FC<ToolInfoProps> = ({
1550
description,
1651
author,
@@ -20,59 +55,89 @@ const ToolInfo: React.FC<ToolInfoProps> = ({
2055
authProviderDocsUrl,
2156
note,
2257
}) => {
23-
authProviderDocsUrl =
58+
const pathname = usePathname();
59+
60+
const resolvedAuthDocsUrl =
2461
authProviderName && !authProviderDocsUrl
2562
? `/home/auth-providers/${authProviderName.toLowerCase()}`
2663
: authProviderDocsUrl;
64+
65+
const toolkit = findToolkitFromPath(pathname);
66+
const toolkitName = toolkit?.label;
67+
const IconComponent = toolkitName ? getToolkitIconByName(toolkitName) : null;
68+
69+
const isPro = toolkit?.isPro ?? false;
70+
const isByoc = toolkit?.isBYOC ?? false;
71+
const toolkitType = toolkit?.type;
72+
73+
const typeInfo =
74+
toolkitType && toolkitType !== "auth" ? TYPE_CONFIG[toolkitType] : null;
75+
const showBadges = isPro || isByoc || typeInfo;
76+
2777
return (
2878
<div className="mt-5 mb-6 rounded-lg border-4 border-neutral-dark-medium p-6 text-text-color shadow-lg sm:p-3">
29-
<p className="my-1.5 text-base text-text-color sm:text-sm">
30-
<strong className="text-text-color">Description: </strong> {description}
31-
</p>
32-
<p className="my-1.5 text-base text-text-color sm:text-sm">
33-
<strong className="text-text-color">Author: </strong> {author}
34-
</p>
35-
{codeLink && (
36-
<p className="my-1.5 text-base text-text-color sm:text-sm">
37-
<strong className="text-text-color">Code: </strong>
38-
<a
39-
className="text-brand-accent no-underline underline-offset-0.5 hover:underline"
40-
href={codeLink}
41-
rel="noopener noreferrer"
42-
target="_blank"
43-
>
44-
GitHub
45-
</a>
46-
</p>
47-
)}
48-
<p className="my-1.5 text-base text-text-color sm:text-sm">
49-
<strong className="text-text-color">Auth: </strong>
50-
{authType.toLowerCase() === "oauth2" ? (
51-
<>
52-
User authorization
53-
{authProviderName && authProviderDocsUrl && (
79+
<div className="flex flex-col gap-4 sm:flex-row sm:gap-6">
80+
{IconComponent && (
81+
<div className="flex items-center justify-center sm:justify-start">
82+
<div className="rounded-lg bg-neutral-light p-3 dark:bg-neutral-dark-medium">
83+
<IconComponent className="h-16 w-16 shrink-0" />
84+
</div>
85+
</div>
86+
)}
87+
<div className="min-w-0 flex-1">
88+
{showBadges && (
89+
<ToolkitBadges isByoc={isByoc} isPro={isPro} typeInfo={typeInfo} />
90+
)}
91+
<p className="my-1.5 text-base text-text-color sm:text-sm">
92+
<strong className="text-text-color">Description: </strong>{" "}
93+
{description}
94+
</p>
95+
<p className="my-1.5 text-base text-text-color sm:text-sm">
96+
<strong className="text-text-color">Author: </strong> {author}
97+
</p>
98+
{codeLink && (
99+
<p className="my-1.5 text-base text-text-color sm:text-sm">
100+
<strong className="text-text-color">Code: </strong>
101+
<a
102+
className="text-brand-accent no-underline underline-offset-0.5 hover:underline"
103+
href={codeLink}
104+
rel="noopener noreferrer"
105+
target="_blank"
106+
>
107+
GitHub
108+
</a>
109+
</p>
110+
)}
111+
<p className="my-1.5 text-base text-text-color sm:text-sm">
112+
<strong className="text-text-color">Auth: </strong>
113+
{authType.toLowerCase() === "oauth2" ? (
54114
<>
55-
{" "}
56-
via the{" "}
57-
<a
58-
className="text-brand-accent no-underline underline-offset-0.5 hover:underline"
59-
href={authProviderDocsUrl}
60-
>
61-
{authProviderName} auth provider
62-
</a>
115+
User authorization
116+
{authProviderName && resolvedAuthDocsUrl && (
117+
<>
118+
{" "}
119+
via the{" "}
120+
<a
121+
className="text-brand-accent no-underline underline-offset-0.5 hover:underline"
122+
href={resolvedAuthDocsUrl}
123+
>
124+
{authProviderName} auth provider
125+
</a>
126+
</>
127+
)}
63128
</>
129+
) : (
130+
authType
64131
)}
65-
</>
66-
) : (
67-
authType
68-
)}
69-
</p>
70-
{note && (
71-
<p className="my-1.5 text-base text-text-color sm:text-sm">
72-
<strong className="text-text-color">Note: </strong>
73-
{note}
74-
</p>
75-
)}
132+
</p>
133+
{note && (
134+
<p className="my-1.5 text-base text-text-color sm:text-sm">
135+
<strong className="text-text-color">Note: </strong>
136+
{note}
137+
</p>
138+
)}
139+
</div>
140+
</div>
76141
</div>
77142
);
78143
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { TOOLKIT_CATALOGUE } from "@arcadeai/design-system";
2+
3+
/**
4+
* Normalize string for comparison (remove hyphens and underscores)
5+
*/
6+
export const normalizeString = (str: string) =>
7+
str.toLowerCase().replace(/[-_]/g, "");
8+
9+
/**
10+
* Find toolkit from pathname
11+
* e.g., /en/mcp-servers/productivity/gmail -> Gmail toolkit
12+
*/
13+
export const findToolkitFromPath = (pathname: string | null) => {
14+
if (!pathname) {
15+
return null;
16+
}
17+
18+
const pathSegments = pathname.split("/").filter(Boolean);
19+
const toolkitSlug = pathSegments.at(-1);
20+
21+
if (!toolkitSlug) {
22+
return null;
23+
}
24+
25+
return (
26+
Object.values(TOOLKIT_CATALOGUE).find(
27+
(t) => normalizeString(t.id) === normalizeString(toolkitSlug)
28+
) || null
29+
);
30+
};

0 commit comments

Comments
 (0)