@supericons/mcp 0.4.8 → 0.4.9

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "generated_at": "2026-05-21T16:35:51.493Z",
3
+ "generated_at": "2026-05-22T20:23:11.858Z",
4
4
  "presets": [
5
5
  {
6
6
  "preset": "bounce",
@@ -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
- const response = await fetch(url, {
77
- method: 'POST',
78
- headers,
79
- body: JSON.stringify(body),
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
+ });
95
+
96
+ if (response.ok) {
97
+ return response.json();
98
+ }
81
99
 
82
- if (!response.ok) {
83
- throw new Error(`hosted MCP search failed (${response.status})`);
100
+ lastStatus = response.status;
101
+ if (response.status !== 429 && response.status < 500) break;
102
+ await sleep(getRetryDelayMs(response, attempt));
84
103
  }
85
104
 
86
- return response.json();
105
+ throw new Error(`hosted MCP search failed (${lastStatus})`);
87
106
  }
88
107
 
89
108
  async function postPublicSearch(url, headers, body) {
90
- const response = await fetch(url, {
91
- method: 'POST',
92
- headers,
93
- body: JSON.stringify(body),
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
- if (!response.ok) {
97
- throw new Error(`public MCP search failed (${response.status})`);
121
+ lastStatus = response.status;
122
+ if (response.status !== 429 && response.status < 500) break;
123
+ await sleep(getRetryDelayMs(response, attempt));
98
124
  }
99
125
 
100
- return response.json();
126
+ throw new Error(`public MCP search failed (${lastStatus})`);
101
127
  }
102
128
 
103
129
  async function retryLocalizedHostedSearch({ postSearch, url, headers, body, locale }) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supericons/mcp",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
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",
@@ -26,6 +26,8 @@ const multilingualSearchAliases = existsSync(multilingualAliasesPath)
26
26
  ? JSON.parse(readFileSync(multilingualAliasesPath, 'utf8')).aliases || []
27
27
  : [];
28
28
  const multilingualExpansionTerms = [...cjkSearchTerms, ...multilingualSearchAliases];
29
+ const SLOT_SEARCH_CONCURRENCY = 2;
30
+ const SLOT_QUERY_CONCURRENCY = 1;
29
31
 
30
32
  const GENERIC_SLOT_WORDS = new Set([
31
33
  'action',
@@ -478,6 +480,17 @@ const COMMON_SLOT_PREFERENCE_RULES = Object.freeze([
478
480
  { pattern: /fork|knife|git|branch|merge|forklift|grill|cancel|\bx\b|off|discount|heart|exclamation|minus|plus|search|share|star|question|bolt|code|copy|dollar|down|up|pin|pause/i, bonus: -140 },
479
481
  ],
480
482
  },
483
+ {
484
+ priority: 120,
485
+ slotPatterns: [/^cart$/i, /shopping cart/i, /\bbasket\b/i],
486
+ queryVariants: ['shopping cart', 'cart', 'basket'],
487
+ iconPreferences: [
488
+ { pattern: /^shopping[_-]?cart$|^basket$/i, bonus: 220 },
489
+ { pattern: /shopping-cart-(cog|x|off|discount|dollar|exclamation|heart|minus|pause|plus|question|share|star|bolt|copy|down|up|search|pin)/i, bonus: -260 },
490
+ { pattern: /basket-(cog|x|off|discount|dollar|exclamation|heart|minus|pause|plus|question|share|star|bolt|copy|down|up|search|pin)/i, bonus: -180 },
491
+ { pattern: /garden-cart/i, bonus: -180 },
492
+ ],
493
+ },
481
494
  {
482
495
  priority: 90,
483
496
  slotPatterns: [/customers?/i, /shoppers?/i, /buyers?/i],
@@ -514,6 +527,26 @@ const COMMON_SLOT_PREFERENCE_RULES = Object.freeze([
514
527
  { pattern: /border|sort|align|cancel|\bx\b|off|discount|heart|exclamation|minus|plus|search|share|star|question|bolt|code|copy|dollar|down|up|pin|pause/i, bonus: -130 },
515
528
  ],
516
529
  },
530
+ {
531
+ priority: 120,
532
+ slotPatterns: [/shipping/i, /delivery/i, /fulfillment/i, /dispatch/i],
533
+ queryVariants: ['truck delivery', 'delivery', 'shipping', 'package delivery'],
534
+ iconPreferences: [
535
+ { pattern: /^truck[_-]?delivery$/i, bonus: 260 },
536
+ { pattern: /^truck$|package[_-]?export|package[_-]?import/i, bonus: 180 },
537
+ { pattern: /^truck[_-]?return$/i, bonus: -120 },
538
+ { pattern: /cloud-upload|upload|ship-off|cloud|arrow-up/i, bonus: -220 },
539
+ ],
540
+ },
541
+ {
542
+ priority: 120,
543
+ slotPatterns: [/returns?/i, /refunds?/i, /reverse logistics/i],
544
+ queryVariants: ['return', 'refund', 'truck return', 'receipt refund'],
545
+ iconPreferences: [
546
+ { pattern: /^truck[_-]?return$|receipt[_-]?refund|credit-card[_-]?refund|arrow-back|cash.*move.*back/i, bonus: 220 },
547
+ { pattern: /player|chevron|stack|upload|cloud|ship-off/i, bonus: -160 },
548
+ ],
549
+ },
517
550
  {
518
551
  priority: 90,
519
552
  slotPatterns: [/products?/i, /catalog/i, /inventory/i],
@@ -523,14 +556,23 @@ const COMMON_SLOT_PREFERENCE_RULES = Object.freeze([
523
556
  { pattern: /brand-producthunt|brand-stocktwits|border|sort|cancel|\bx\b|off|discount|heart|exclamation|minus|plus|search|share|star|question|bolt|code|copy|dollar|down|up|pin|pause/i, bonus: -130 },
524
557
  ],
525
558
  },
526
- {
527
- slotPatterns: [/reports?/i, /analytics/i, /insights?/i],
528
- queryVariants: ['bar chart', 'file chart', 'analytics chart', 'report document'],
559
+ {
560
+ slotPatterns: [/reports?/i, /analytics/i, /insights?/i],
561
+ queryVariants: ['bar chart', 'file chart', 'analytics chart', 'report document'],
529
562
  iconPreferences: [
530
563
  { pattern: /file-.*chart|chart-bar|bar-chart-3|bar-chart|chart-line|line-chart/i, bonus: 40 },
531
- { pattern: /chart|report|analytics/i, bonus: 16 },
532
- ],
533
- },
564
+ { pattern: /chart|report|analytics/i, bonus: 16 },
565
+ ],
566
+ },
567
+ {
568
+ priority: 100,
569
+ slotPatterns: [/^quotes?$/i, /blockquote/i, /quotation/i, /citation/i],
570
+ queryVariants: ['quotes', 'quotation', 'quote', 'blockquote'],
571
+ iconPreferences: [
572
+ { pattern: /^quotes?$|quotation|blockquote/i, bonus: 180 },
573
+ { pattern: /indent|text-align|receipt|ticket/i, bonus: -120 },
574
+ ],
575
+ },
534
576
  {
535
577
  slotPatterns: [/settings?/i, /preferences?/i, /configure/i],
536
578
  queryVariants: ['settings', 'cog', 'sliders'],
@@ -1278,7 +1320,7 @@ export async function recommendIconsForTask({
1278
1320
  semanticMap,
1279
1321
  }) {
1280
1322
  const normalizedResponseMode = normalizeResponseMode(responseMode);
1281
- const scoredSlotResults = await mapWithConcurrency(slots, 6, async (slotLabel) => {
1323
+ const scoredSlotResults = await mapWithConcurrency(slots, SLOT_SEARCH_CONCURRENCY, async (slotLabel) => {
1282
1324
  const intentTerms = buildSlotIntentTerms(task, slotLabel, locale);
1283
1325
  const requestedVariantTerms = dedupe([
1284
1326
  ...tokenizeText(slotLabel),
@@ -1289,7 +1331,7 @@ export async function recommendIconsForTask({
1289
1331
  const pooledIcons = [];
1290
1332
  const seen = new Set();
1291
1333
 
1292
- const resultGroups = await mapWithConcurrency(queryVariants, 2, async (queryVariant) => {
1334
+ const resultGroups = await mapWithConcurrency(queryVariants, SLOT_QUERY_CONCURRENCY, async (queryVariant) => {
1293
1335
  try {
1294
1336
  return await searchIconsForQuery({
1295
1337
  query: queryVariant,
package/server.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "url": "https://github.com/curlymolelabs/supericons",
8
8
  "source": "github"
9
9
  },
10
- "version": "0.4.7",
10
+ "version": "0.4.9",
11
11
  "remotes": [
12
12
  {
13
13
  "type": "streamable-http",
@@ -18,7 +18,7 @@
18
18
  {
19
19
  "registryType": "npm",
20
20
  "identifier": "@supericons/mcp",
21
- "version": "0.4.7",
21
+ "version": "0.4.9",
22
22
  "transport": {
23
23
  "type": "stdio"
24
24
  }