Skip to content

Commit 98d7147

Browse files
authored
fix: Enhance MCP marketplace to support custom MCPs installation (#192)
1 parent 1dc790c commit 98d7147

File tree

4 files changed

+98
-52
lines changed

4 files changed

+98
-52
lines changed

.changeset/serious-bobcats-wash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"hai-build-code-generator": patch
3+
---
4+
5+
Added support for installing custom MCPs via user-provided details.

src/core/controller/index.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -953,13 +953,36 @@ export class Controller {
953953
throw new Error("Invalid response from MCP marketplace API")
954954
}
955955

956+
// Create an array to hold all local MCPs with their star counts
957+
const localMcpItems = []
958+
959+
// Get all local MCPs from registry
960+
const localMcpIds = Object.keys(getAllLocalMcps())
961+
962+
// Fetch GitHub stars for each local MCP
963+
for (const mcpId of localMcpIds) {
964+
const mcp = getLocalMcp(mcpId)
965+
if (mcp) {
966+
// Update star count for this MCP and add isLocal flag
967+
const gitHubStars = await getStarCount(mcp.githubUrl)
968+
localMcpItems.push({
969+
...mcp,
970+
githubStars: gitHubStars || 0,
971+
isLocal: true, // Add isLocal flag to identify local MCPs
972+
})
973+
}
974+
}
956975
const catalog: McpMarketplaceCatalog = {
957-
items: (response.data || []).map((item: any) => ({
958-
...item,
959-
githubStars: item.githubStars ?? 0,
960-
downloadCount: item.downloadCount ?? 0,
961-
tags: item.tags ?? [],
962-
})),
976+
items: [
977+
...localMcpItems,
978+
...(response.data || []).map((item: any) => ({
979+
...item,
980+
githubStars: item.githubStars ?? 0,
981+
downloadCount: item.downloadCount ?? 0,
982+
tags: item.tags ?? [],
983+
isLocal: false, // Mark remote MCPs explicitly
984+
})),
985+
],
963986
}
964987

965988
// Store in global state

webview-ui/src/components/mcp/configuration/tabs/marketplace/McpMarketplaceCard.tsx

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -172,28 +172,34 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps)
172172
</span>
173173
</div>
174174
</a>
175-
<div
176-
style={{
177-
display: "flex",
178-
alignItems: "center",
179-
gap: "4px",
180-
minWidth: 0,
181-
flexShrink: 0,
182-
}}>
183-
<span className="codicon codicon-star-full" />
184-
<span style={{ wordBreak: "break-all" }}>{item.githubStars?.toLocaleString() ?? 0}</span>
185-
</div>
186-
<div
187-
style={{
188-
display: "flex",
189-
alignItems: "center",
190-
gap: "4px",
191-
minWidth: 0,
192-
flexShrink: 0,
193-
}}>
194-
<span className="codicon codicon-cloud-download" />
195-
<span style={{ wordBreak: "break-all" }}>{item.downloadCount?.toLocaleString() ?? 0}</span>
196-
</div>
175+
{!item.isLocal && (
176+
<>
177+
<div
178+
style={{
179+
display: "flex",
180+
alignItems: "center",
181+
gap: "4px",
182+
minWidth: 0,
183+
flexShrink: 0,
184+
}}>
185+
<span className="codicon codicon-star-full" />
186+
<span style={{ wordBreak: "break-all" }}>{item.githubStars?.toLocaleString() ?? 0}</span>
187+
</div>
188+
<div
189+
style={{
190+
display: "flex",
191+
alignItems: "center",
192+
gap: "4px",
193+
minWidth: 0,
194+
flexShrink: 0,
195+
}}>
196+
<span className="codicon codicon-cloud-download" />
197+
<span style={{ wordBreak: "break-all" }}>
198+
{item.downloadCount?.toLocaleString() ?? 0}
199+
</span>
200+
</div>
201+
</>
202+
)}
197203
{item.requiresApiKey && (
198204
<span className="codicon codicon-key" title="Requires API key" style={{ flexShrink: 0 }} />
199205
)}

webview-ui/src/components/mcp/configuration/tabs/marketplace/McpMarketplaceView.tsx

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,42 @@ const McpMarketplaceView = () => {
2929
}, [items])
3030

3131
const filteredItems = useMemo(() => {
32-
return items
33-
.filter((item) => {
34-
const matchesSearch =
35-
searchQuery === "" ||
36-
item.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
37-
item.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
38-
item.tags.some((tag) => tag.toLowerCase().includes(searchQuery.toLowerCase()))
39-
const matchesCategory = !selectedCategory || item.category === selectedCategory
40-
return matchesSearch && matchesCategory
41-
})
42-
.sort((a, b) => {
43-
switch (sortBy) {
44-
case "downloadCount":
45-
return b.downloadCount - a.downloadCount
46-
case "stars":
47-
return b.githubStars - a.githubStars
48-
case "name":
49-
return a.name.localeCompare(b.name)
50-
case "newest":
51-
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
52-
default:
53-
return 0
54-
}
55-
})
32+
// First, apply filtering
33+
const filtered = items.filter((item) => {
34+
const matchesSearch =
35+
searchQuery === "" ||
36+
item.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
37+
item.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
38+
item.tags.some((tag) => tag.toLowerCase().includes(searchQuery.toLowerCase()))
39+
const matchesCategory = !selectedCategory || item.category === selectedCategory
40+
return matchesSearch && matchesCategory
41+
})
42+
43+
// Split into local and remote MCPs
44+
const localMcps = filtered.filter((item) => item.isLocal)
45+
const remoteMcps = filtered.filter((item) => !item.isLocal)
46+
47+
// Sort each group separately
48+
const sortFn = (a: McpMarketplaceItem, b: McpMarketplaceItem) => {
49+
switch (sortBy) {
50+
case "downloadCount":
51+
return b.downloadCount - a.downloadCount
52+
case "stars":
53+
return b.githubStars - a.githubStars
54+
case "name":
55+
return a.name.localeCompare(b.name)
56+
case "newest":
57+
return new Date(b.createdAt || "").getTime() - new Date(a.createdAt || "").getTime()
58+
default:
59+
return 0
60+
}
61+
}
62+
63+
const sortedLocalMcps = [...localMcps].sort(sortFn)
64+
const sortedRemoteMcps = [...remoteMcps].sort(sortFn)
65+
66+
// Combine with local MCPs first
67+
return [...sortedLocalMcps, ...sortedRemoteMcps]
5668
}, [items, searchQuery, selectedCategory, sortBy])
5769

5870
useEffect(() => {

0 commit comments

Comments
 (0)