@supericons/mcp 0.4.8 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/generated/motion-lab-baseline.json +1 -1
- package/hosted-search-client.js +65 -24
- package/index.js +18 -17
- package/package.json +4 -3
- package/public/product-facts.json +8 -8
- package/recommend-icons.js +1329 -1287
- package/remote-server.js +23 -7
- package/runtime/icon-taxonomy-seed.js +30 -2
- package/runtime/supericons-ai-taxonomy.js +403 -0
- package/search.js +98 -32
- package/semantic-registry.js +177 -56
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.10 - 2026-06-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- added Supericons (`si`) library discovery for hosted and stdio MCP clients
|
|
8
|
+
- added hosted search support for Supericons AI and developer tool logo queries
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- updated packaged product facts to reflect 21,367 free icons across 11 libraries
|
|
13
|
+
- sends the public Supabase key header when calling the public MCP search gateway
|
|
14
|
+
|
|
3
15
|
## 0.4.5 - 2026-05-14
|
|
4
16
|
|
|
5
17
|
### Fixed
|
package/hosted-search-client.js
CHANGED
|
@@ -51,6 +51,18 @@ function hasSearchResults(payload) {
|
|
|
51
51
|
return Array.isArray(payload?.results) && payload.results.length > 0;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function sleep(ms) {
|
|
55
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getRetryDelayMs(response, attempt) {
|
|
59
|
+
const retryAfter = Number(response.headers.get('retry-after'));
|
|
60
|
+
if (Number.isFinite(retryAfter) && retryAfter > 0) {
|
|
61
|
+
return Math.min(retryAfter * 1000, 5000);
|
|
62
|
+
}
|
|
63
|
+
return Math.min(500 * 2 ** attempt, 2500);
|
|
64
|
+
}
|
|
65
|
+
|
|
54
66
|
function buildLocalizedRetryQueries(query, locale) {
|
|
55
67
|
if (!locale || multilingualExpansionTerms.length === 0) return [];
|
|
56
68
|
|
|
@@ -73,31 +85,45 @@ function buildLocalizedRetryQueries(query, locale) {
|
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
async function postHostedSearch(url, headers, body) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
let lastStatus = 0;
|
|
89
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
90
|
+
const response = await fetch(url, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers,
|
|
93
|
+
body: JSON.stringify(body),
|
|
94
|
+
});
|
|
81
95
|
|
|
82
|
-
|
|
83
|
-
|
|
96
|
+
if (response.ok) {
|
|
97
|
+
return response.json();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
lastStatus = response.status;
|
|
101
|
+
if (response.status !== 429 && response.status < 500) break;
|
|
102
|
+
await sleep(getRetryDelayMs(response, attempt));
|
|
84
103
|
}
|
|
85
104
|
|
|
86
|
-
|
|
105
|
+
throw new Error(`hosted MCP search failed (${lastStatus})`);
|
|
87
106
|
}
|
|
88
107
|
|
|
89
108
|
async function postPublicSearch(url, headers, body) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
109
|
+
let lastStatus = 0;
|
|
110
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
111
|
+
const response = await fetch(url, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers,
|
|
114
|
+
body: JSON.stringify(body),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (response.ok) {
|
|
118
|
+
return response.json();
|
|
119
|
+
}
|
|
95
120
|
|
|
96
|
-
|
|
97
|
-
|
|
121
|
+
lastStatus = response.status;
|
|
122
|
+
if (response.status !== 429 && response.status < 500) break;
|
|
123
|
+
await sleep(getRetryDelayMs(response, attempt));
|
|
98
124
|
}
|
|
99
125
|
|
|
100
|
-
|
|
126
|
+
throw new Error(`public MCP search failed (${lastStatus})`);
|
|
101
127
|
}
|
|
102
128
|
|
|
103
129
|
async function retryLocalizedHostedSearch({ postSearch, url, headers, body, locale }) {
|
|
@@ -117,6 +143,16 @@ async function retryLocalizedHostedSearch({ postSearch, url, headers, body, loca
|
|
|
117
143
|
return null;
|
|
118
144
|
}
|
|
119
145
|
|
|
146
|
+
function getPublicGatewayAnonKey() {
|
|
147
|
+
return (
|
|
148
|
+
process.env.SUPERICONS_MCP_SEARCH_ANON_KEY
|
|
149
|
+
|| process.env.SUPERICONS_SEARCH_ENGINE_ANON_KEY
|
|
150
|
+
|| process.env.SUPABASE_ANON_KEY
|
|
151
|
+
|| process.env.SUPERICONS_SUPABASE_ANON
|
|
152
|
+
|| SUPABASE_ANON
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
120
156
|
export async function searchIconsHostedMcp({
|
|
121
157
|
query,
|
|
122
158
|
library = null,
|
|
@@ -168,14 +204,19 @@ export async function searchIconsHostedMcp({
|
|
|
168
204
|
}) || payload;
|
|
169
205
|
}
|
|
170
206
|
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
207
|
+
const publicAnonKey = getPublicGatewayAnonKey();
|
|
208
|
+
const headers = {
|
|
209
|
+
'Content-Type': 'application/json',
|
|
210
|
+
apikey: publicAnonKey,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
if (apiKey) {
|
|
214
|
+
headers['x-supericons-api-key'] = apiKey;
|
|
215
|
+
}
|
|
216
|
+
if (looksLikeJwt(publicAnonKey)) {
|
|
217
|
+
headers.Authorization = `Bearer ${publicAnonKey}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
179
220
|
const url = getPublicGatewayUrl();
|
|
180
221
|
const body = {
|
|
181
222
|
query,
|
package/index.js
CHANGED
|
@@ -699,18 +699,19 @@ async function searchAccessibleIcons({ query, library, limit, style = VARIANT_ST
|
|
|
699
699
|
// ============================================================
|
|
700
700
|
// Library Metadata
|
|
701
701
|
// ============================================================
|
|
702
|
-
const libraryMeta = {
|
|
703
|
-
material: { name: 'Material Symbols', description: 'Google Material Symbols with 4-axis variable font support', hasStroke: false, hasFilled: true, count: 4205, outlineCount: 4205, solidCount: 4205 },
|
|
704
|
-
lucide: { name: 'Lucide', description: 'Beautiful, consistent open-source icons', hasStroke: true, hasFilled: false, count: 1951, outlineCount: 1951, solidCount: 0 },
|
|
705
|
-
tabler: { name: 'Tabler', description: 'Over 5,000 free MIT-licensed SVG icons', hasStroke: true, hasFilled: true, count: 5021, outlineCount: 5021, solidCount: 1053 },
|
|
702
|
+
const libraryMeta = {
|
|
703
|
+
material: { name: 'Material Symbols', description: 'Google Material Symbols with 4-axis variable font support', hasStroke: false, hasFilled: true, count: 4205, outlineCount: 4205, solidCount: 4205 },
|
|
704
|
+
lucide: { name: 'Lucide', description: 'Beautiful, consistent open-source icons', hasStroke: true, hasFilled: false, count: 1951, outlineCount: 1951, solidCount: 0 },
|
|
705
|
+
tabler: { name: 'Tabler', description: 'Over 5,000 free MIT-licensed SVG icons', hasStroke: true, hasFilled: true, count: 5021, outlineCount: 5021, solidCount: 1053 },
|
|
706
706
|
phosphor: { name: 'Phosphor', description: 'Flexible icon family for interfaces and beyond', hasStroke: false, hasFilled: true, count: 1512, outlineCount: 1512, solidCount: 1512 },
|
|
707
707
|
heroicons: { name: 'Heroicons', description: 'Beautiful hand-crafted SVG icons by Tailwind CSS', hasStroke: true, hasFilled: true, count: 324, outlineCount: 324, solidCount: 324 },
|
|
708
708
|
bootstrap: { name: 'Bootstrap', description: 'Official open-source SVG icon library for Bootstrap', hasStroke: false, hasFilled: true, count: 1373, outlineCount: 1373, solidCount: 705 },
|
|
709
709
|
iconoir: { name: 'Iconoir', description: 'High-quality open-source icon library', hasStroke: true, hasFilled: true, count: 1383, outlineCount: 1383, solidCount: 288 },
|
|
710
|
-
ionicons: { name: 'Ionicons', description: 'Premium open-source icons for Ionic Framework', hasStroke: true, hasFilled: true, count: 421, outlineCount: 421, solidCount: 515 },
|
|
711
|
-
simpleicons: { name: 'Simple Icons', description: '3,400+ SVG icons for popular brands', hasStroke: false, hasFilled: false, count: 3412, outlineCount: 3412, solidCount: 0 },
|
|
712
|
-
mingcute: { name: 'MingCute', description: 'Modern open-source icon set with broad interface coverage', hasStroke: false, hasFilled: true, count: 1662, outlineCount: 1662, solidCount: 1662 },
|
|
713
|
-
}
|
|
710
|
+
ionicons: { name: 'Ionicons', description: 'Premium open-source icons for Ionic Framework', hasStroke: true, hasFilled: true, count: 421, outlineCount: 421, solidCount: 515 },
|
|
711
|
+
simpleicons: { name: 'Simple Icons', description: '3,400+ SVG icons for popular brands', hasStroke: false, hasFilled: false, count: 3412, outlineCount: 3412, solidCount: 0 },
|
|
712
|
+
mingcute: { name: 'MingCute', description: 'Modern open-source icon set with broad interface coverage', hasStroke: false, hasFilled: true, count: 1662, outlineCount: 1662, solidCount: 1662 },
|
|
713
|
+
si: { name: 'Supericons', description: 'First-party AI and developer tool logos curated for agentic app builders', hasStroke: false, hasFilled: false, count: 50, outlineCount: 50, solidCount: 0 },
|
|
714
|
+
};
|
|
714
715
|
|
|
715
716
|
// Add premium pack libraries
|
|
716
717
|
const packDirNames = existsSync(packsDir)
|
|
@@ -759,10 +760,10 @@ const server = new McpServer({
|
|
|
759
760
|
// --- Tool: search_icons ---
|
|
760
761
|
server.tool(
|
|
761
762
|
'search_icons',
|
|
762
|
-
`Search ${freeIconCountLabel} using AI-powered synonym expansion. Returns matching free icons with SVG code and SI semantic guidance when available. Pro API keys unlock workflow tools; premium pack icon search is not exposed through MCP yet.`,
|
|
763
|
+
`Search ${freeIconCountLabel} using AI-powered synonym expansion. Returns matching free icons with SVG code and SI semantic guidance when available, including Supericons AI and developer tool logos. Pro API keys unlock workflow tools; premium pack icon search is not exposed through MCP yet.`,
|
|
763
764
|
{
|
|
764
765
|
query: z.string().describe('Search term (e.g. "heart", "login", "download arrow")'),
|
|
765
|
-
library: z.string().optional().describe('Filter by free library: lucide, tabler, phosphor, heroicons, bootstrap, iconoir, ionicons, material, simpleicons, or mingcute'),
|
|
766
|
+
library: z.string().optional().describe('Filter by free library: si, lucide, tabler, phosphor, heroicons, bootstrap, iconoir, ionicons, material, simpleicons, or mingcute'),
|
|
766
767
|
style: z.enum(['any', 'outline', 'solid']).optional().default('any').describe('Optional style preference. Use `solid` only for libraries that ship fill or solid variants.'),
|
|
767
768
|
locale: z.enum(['zh-Hans', 'zh-Hant', 'ja', 'ko', 'es', 'de', 'pt', 'ar', 'hi', 'vi', 'th']).optional().describe('Optional locale for multilingual search terms. Supported values: zh-Hans, zh-Hant, ja, ko, es, de, pt, ar, hi, vi, th.'),
|
|
768
769
|
limit: z.number().min(1).max(50).optional().default(10).describe('Max results (1-50, default 10)'),
|
|
@@ -834,12 +835,12 @@ server.tool(
|
|
|
834
835
|
);
|
|
835
836
|
|
|
836
837
|
// --- Tool: recommend_icons ---
|
|
837
|
-
server.tool(
|
|
838
|
-
'recommend_icons',
|
|
839
|
-
'Recommend the most suitable icons for one or more UI slots. Returns shortlist choices with preview-ready SVGs, short reasons, and SI semantic guidance when available.',
|
|
840
|
-
{
|
|
841
|
-
task: z.string().describe('Overall UI task, for example "replace the 4 bottom navigation icons" or "choose icons for a settings panel".'),
|
|
842
|
-
library: z.string().optional().describe('Optional library filter such as mingcute, lucide, tabler, material, or simpleicons.'),
|
|
838
|
+
server.tool(
|
|
839
|
+
'recommend_icons',
|
|
840
|
+
'Recommend the most suitable icons for one or more UI slots. Returns shortlist choices with preview-ready SVGs, short reasons, and SI semantic guidance when available.',
|
|
841
|
+
{
|
|
842
|
+
task: z.string().describe('Overall UI task, for example "replace the 4 bottom navigation icons" or "choose icons for a settings panel".'),
|
|
843
|
+
library: z.string().optional().describe('Optional library filter such as si, mingcute, lucide, tabler, material, or simpleicons.'),
|
|
843
844
|
style: z.enum(['any', 'outline', 'solid']).optional().default('any').describe('Optional style preference. Use `solid` to prefer filled variants where they exist.'),
|
|
844
845
|
locale: z.enum(['zh-Hans', 'zh-Hant', 'ja', 'ko', 'es', 'de', 'pt', 'ar', 'hi', 'vi', 'th']).optional().describe('Optional locale for multilingual slot labels. Supported values: zh-Hans, zh-Hant, ja, ko, es, de, pt, ar, hi, vi, th.'),
|
|
845
846
|
slots: z.array(z.string().min(1)).min(1).max(12).describe('List of UI slots to fill, for example ["Home tab", "Create action", "Alerts tab", "Profile tab"].'),
|
|
@@ -882,7 +883,7 @@ server.tool(
|
|
|
882
883
|
'Retrieve a specific free icon by its ID and library. Returns the full SVG code, metadata, and SI semantic guidance when available. Premium pack icon retrieval is not exposed through MCP yet.',
|
|
883
884
|
{
|
|
884
885
|
id: z.string().describe('Icon ID (e.g. "heart", "arrow-right", "settings")'),
|
|
885
|
-
library: z.string().describe('Free library name (e.g. "lucide", "tabler", "phosphor", "iconoir", or "mingcute")'),
|
|
886
|
+
library: z.string().describe('Free library name (e.g. "si", "lucide", "tabler", "phosphor", "iconoir", or "mingcute")'),
|
|
886
887
|
style: z.enum(['any', 'outline', 'solid']).optional().default('any').describe('Optional style preference. Use `solid` to request a filled variant when the library supports it.'),
|
|
887
888
|
},
|
|
888
889
|
async ({ id, library, style }) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supericons/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.10",
|
|
4
4
|
"mcpName": "io.github.curlymolelabs/supericons",
|
|
5
5
|
"description": "MCP server for Supericons: multilingual semantic SVG icon search and recommendations for AI coding agents.",
|
|
6
6
|
"type": "module",
|
|
@@ -36,8 +36,9 @@
|
|
|
36
36
|
"runtime/converter-workflow.js",
|
|
37
37
|
"runtime/cjk-search-core.js",
|
|
38
38
|
"runtime/generated-search-intent-rules.js",
|
|
39
|
-
"runtime/icon-semantic-aliases.js",
|
|
40
|
-
"runtime/
|
|
39
|
+
"runtime/icon-semantic-aliases.js",
|
|
40
|
+
"runtime/supericons-ai-taxonomy.js",
|
|
41
|
+
"runtime/icon-taxonomy-seed.js",
|
|
41
42
|
"runtime/public-metadata-sanitizer.js",
|
|
42
43
|
"runtime/search-intent-core.js",
|
|
43
44
|
"search.js",
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-
|
|
3
|
-
"freeIconCount":
|
|
4
|
-
"freeLibraryCount":
|
|
2
|
+
"generatedAt": "2026-06-27T16:34:16.739Z",
|
|
3
|
+
"freeIconCount": 21367,
|
|
4
|
+
"freeLibraryCount": 11,
|
|
5
5
|
"premiumCollectionCount": 8,
|
|
6
6
|
"premiumIconCount": 400,
|
|
7
7
|
"mcpToolCount": 13,
|
|
8
8
|
"mcpFreeToolCount": 3,
|
|
9
|
-
"mcpPackageVersion": "0.4.
|
|
9
|
+
"mcpPackageVersion": "0.4.10",
|
|
10
10
|
"display": {
|
|
11
11
|
"freeIconsRounded": "20,000+",
|
|
12
12
|
"freeIconsLabel": "20,000+ curated SVG icons",
|
|
13
|
-
"freeIconsAcrossLibrariesLabel": "20,000+ icons across
|
|
14
|
-
"freeIconsAcrossLibrariesFreeLabel": "20,000+ curated SVG icons across
|
|
15
|
-
"freeSvgIconsAcrossLibrariesLabel": "20,000+ free SVG icons from
|
|
16
|
-
"openSourceSvgIconsAcrossLibrariesLabel": "20,000+ open-source SVG icons from
|
|
13
|
+
"freeIconsAcrossLibrariesLabel": "20,000+ icons across 11 libraries",
|
|
14
|
+
"freeIconsAcrossLibrariesFreeLabel": "20,000+ curated SVG icons across 11 libraries",
|
|
15
|
+
"freeSvgIconsAcrossLibrariesLabel": "20,000+ free SVG icons from 11 libraries",
|
|
16
|
+
"openSourceSvgIconsAcrossLibrariesLabel": "20,000+ open-source SVG icons from 11 libraries",
|
|
17
17
|
"freeIconLibraryLabel": "20,000+ icon library",
|
|
18
18
|
"freeResultsLabel": "20,000+ results",
|
|
19
19
|
"searchPlaceholderLabel": "Search 20,000+ icons...",
|