@supericons/mcp
Advanced tools
+14
-0
| # Changelog | ||
| ## 0.4.25 - 2026-07-30 | ||
| ### Fixed | ||
| - added reviewed Spanish aliases for storage, hardware, towels, and tools, with or without an explicit locale | ||
| - repaired plural and compound searches such as `categories` and `airflow` | ||
| - preserved exact Apache Airflow and Alibaba Cloud brand identity before generic concept aliases | ||
| - kept default and solid icon catalogs consistent across the packaged helper and MCP tool route | ||
| ### Improved | ||
| - added focused cross-surface checks for multilingual, compound, plural, mixed-script, and brand requests | ||
| - preserved honest no-results, strict-library behavior, hosted error visibility, and established search fingerprints | ||
| ## 0.4.24 - 2026-07-28 | ||
@@ -4,0 +18,0 @@ |
+1
-1
| { | ||
| "name": "@supericons/mcp", | ||
| "version": "0.4.24", | ||
| "version": "0.4.25", | ||
| "mcpName": "io.github.curlymolelabs/supericons", | ||
@@ -5,0 +5,0 @@ "description": "MCP server for Supericons: multilingual semantic SVG icon search and recommendations for AI coding agents.", |
| { | ||
| "generatedAt": "2026-07-27T14:21:54.815Z", | ||
| "freeIconCount": 21427, | ||
| "generatedAt": "2026-07-29T18:07:22.627Z", | ||
| "freeIconCount": 21467, | ||
| "freeLibraryCount": 11, | ||
@@ -9,3 +9,3 @@ "premiumCollectionCount": 9, | ||
| "mcpFreeToolCount": 5, | ||
| "mcpPackageVersion": "0.4.24", | ||
| "mcpPackageVersion": "0.4.25", | ||
| "display": { | ||
@@ -12,0 +12,0 @@ "freeIconsRounded": "20,000+", |
@@ -103,2 +103,3 @@ export const CJK_SEARCH_LOCALES = Object.freeze(['zh-Hans', 'zh-Hant', 'ja', 'ko']); | ||
| if (wordForms.some((form) => form.normalized === normalizedQuery)) return true; | ||
| if (wordForms.some((form) => ` ${normalizedQuery} `.includes(` ${form.normalized} `))) return true; | ||
| if (wordForms.some((form) => containsSegmentSequence(querySegments, form.segments))) return true; | ||
@@ -122,4 +123,4 @@ | ||
| const canUseExplicitLocaleTerms = EXPLICIT_MULTILINGUAL_LOCALES.has(locale); | ||
| const canUseMultilingualTerms = isLikelyCjkQuery(query) || canUseExplicitLocaleTerms; | ||
| if (!normalizedQuery || !canUseMultilingualTerms) { | ||
| const isCjkQuery = isLikelyCjkQuery(query); | ||
| if (!normalizedQuery) { | ||
| return { query: normalizedQuery, variants: [String(query || '').trim()].filter(Boolean), matched: [] }; | ||
@@ -130,4 +131,9 @@ } | ||
| const eligibleTerms = locale | ||
| ? terms.filter((record) => record.locale === locale) | ||
| : terms; | ||
| ? terms.filter((record) => record.locale === locale || record.scope === 'global') | ||
| : isCjkQuery | ||
| ? terms.filter((record) => CJK_SEARCH_LOCALES.includes(record.locale) || record.scope === 'global') | ||
| : terms.filter((record) => record.scope === 'global'); | ||
| if (!isCjkQuery && !canUseExplicitLocaleTerms && eligibleTerms.length === 0) { | ||
| return { query: normalizedQuery, variants: [String(query || '').trim()].filter(Boolean), matched: [] }; | ||
| } | ||
| const querySegments = segmentNormalizedSearchText(normalizedQuery, locale); | ||
@@ -134,0 +140,0 @@ const matched = eligibleTerms.filter((record) => ( |
@@ -93,2 +93,6 @@ /** | ||
| function compactSemanticText(value) { | ||
| return normalizeSemanticText(value).replace(/\s+/g, ''); | ||
| } | ||
| function tokenizeSemanticText(value) { | ||
@@ -297,2 +301,14 @@ const normalized = normalizeSemanticText(value); | ||
| function isLikelyBrandIdentityIcon(icon) { | ||
| const key = iconKey(icon).toLowerCase(); | ||
| return ( | ||
| key.startsWith('simpleicons:') || | ||
| key.includes(':brand-') || | ||
| isSupericonsBrandLogo(icon) || | ||
| icon.assetType === 'brand-logo' || | ||
| icon.filterTags?.includes('brand-logo') || | ||
| icon.aiFilterTags?.includes('brand-logo') | ||
| ); | ||
| } | ||
| function getIconSearchMetadata(icon) { | ||
@@ -325,2 +341,7 @@ const cached = iconSearchMetadataCache.get(icon); | ||
| .filter(Boolean); | ||
| const compactPrimaryValues = new Set( | ||
| [name, id] | ||
| .map(compactSemanticText) | ||
| .filter((value) => value.length >= 5), | ||
| ); | ||
| const metadata = { | ||
@@ -336,2 +357,3 @@ name, | ||
| aliases, | ||
| compactPrimaryValues, | ||
| }; | ||
@@ -343,2 +365,29 @@ | ||
| function getExactBrandIdentityResults(query, icons, synonyms, options = {}) { | ||
| const identityWords = getMeaningfulQueryWords(tokenizeSemanticText(query)); | ||
| const identityQuery = identityWords.join(' '); | ||
| if (!identityQuery) return []; | ||
| const identityResults = searchIconsForSingleQuery(identityQuery, icons, synonyms, { | ||
| ...options, | ||
| limit: Math.max(Number(options.limit || 20) * 2, 20), | ||
| applyExpressiveFallback: false, | ||
| candidatePool: getIndexedCandidatePool(icons, identityQuery, synonyms), | ||
| }); | ||
| return identityResults.filter((icon) => { | ||
| const directScore = getDirectSearchScore( | ||
| icon, | ||
| normalizeSemanticText(identityQuery), | ||
| tokenizeSemanticText(identityQuery), | ||
| ); | ||
| const brandAdjustment = getBrandRankAdjustment(query, icon); | ||
| return ( | ||
| directScore >= 300 && | ||
| isLikelyBrandIdentityIcon(icon) && | ||
| brandAdjustment.penalty === 0 | ||
| ); | ||
| }); | ||
| } | ||
| function getIconCandidateIndex(icons) { | ||
@@ -349,4 +398,5 @@ const cached = iconCandidateIndexCache.get(icons); | ||
| const byToken = new Map(); | ||
| const byCompactPrimaryValue = new Map(); | ||
| for (const icon of icons) { | ||
| const { tokens } = getIconSearchMetadata(icon); | ||
| const { tokens, compactPrimaryValues } = getIconSearchMetadata(icon); | ||
| for (const token of tokens) { | ||
@@ -358,5 +408,10 @@ if (!token) continue; | ||
| } | ||
| for (const compactValue of compactPrimaryValues) { | ||
| const matches = byCompactPrimaryValue.get(compactValue) || []; | ||
| matches.push(icon); | ||
| byCompactPrimaryValue.set(compactValue, matches); | ||
| } | ||
| } | ||
| const index = { byToken }; | ||
| const index = { byToken, byCompactPrimaryValue }; | ||
| iconCandidateIndexCache.set(icons, index); | ||
@@ -374,3 +429,3 @@ return index; | ||
| ]); | ||
| const { byToken } = getIconCandidateIndex(icons); | ||
| const { byToken, byCompactPrimaryValue } = getIconCandidateIndex(icons); | ||
| const candidates = []; | ||
@@ -391,2 +446,5 @@ const seen = new Set(); | ||
| } | ||
| if (normalizedQuery.includes(' ')) { | ||
| addMatches(byCompactPrimaryValue.get(compactSemanticText(normalizedQuery))); | ||
| } | ||
@@ -399,3 +457,3 @@ return candidates; | ||
| const { name, id, fullId, tokens } = getIconSearchMetadata(icon); | ||
| const { name, id, fullId, tokens, compactPrimaryValues } = getIconSearchMetadata(icon); | ||
| const meaningfulQueryWords = getMeaningfulQueryWords(queryWords); | ||
@@ -406,2 +464,8 @@ | ||
| } | ||
| if ( | ||
| normalizedQuery.includes(' ') | ||
| && compactPrimaryValues.has(compactSemanticText(normalizedQuery)) | ||
| ) { | ||
| return 300; | ||
| } | ||
@@ -584,3 +648,3 @@ const singleQueryWord = queryWords.length === 1 ? queryWords[0] : null; | ||
| */ | ||
| export function searchIcons(query, icons, synonyms, options = {}) { | ||
| function searchIconsCore(query, icons, synonyms, options = {}) { | ||
| const { library, limit = 20, style = 'any' } = options; | ||
@@ -595,4 +659,34 @@ const libraryMode = normalizeSearchLibraryMode(options.libraryMode); | ||
| const hasExpandedCjk = cjkExpansion.matched.length > 0 && queryVariants.length > 1; | ||
| const existingBrandResults = semanticQueryFrame.is_brand_logo_query | ||
| ? searchIconsForSingleQuery(query, icons, synonyms, { | ||
| library, | ||
| libraryMode, | ||
| limit, | ||
| style, | ||
| candidatePool: getIndexedCandidatePool(icons, query, synonyms), | ||
| }) | ||
| : []; | ||
| const shouldResolveExactBrandIdentity = | ||
| (hasExpandedCjk && !semanticQueryFrame.is_brand_logo_query) || | ||
| (semanticQueryFrame.is_brand_logo_query && existingBrandResults.length === 0); | ||
| if (hasExpandedCjk && !semanticQueryFrame.matched) { | ||
| if (shouldResolveExactBrandIdentity) { | ||
| const exactBrandResults = getExactBrandIdentityResults(query, icons, synonyms, { | ||
| library, | ||
| libraryMode, | ||
| limit, | ||
| style, | ||
| }); | ||
| if (exactBrandResults.length > 0) { | ||
| return rerankSearchCandidatesAtFusion(query, exactBrandResults, { | ||
| libraryMode, | ||
| requestedLibrary: library, | ||
| }).slice(0, Math.max(1, limit)); | ||
| } | ||
| } | ||
| if ( | ||
| hasExpandedCjk | ||
| && (!semanticQueryFrame.matched || semanticQueryFrame.is_brand_logo_query) | ||
| ) { | ||
| const merged = []; | ||
@@ -697,3 +791,3 @@ const seen = new Set(); | ||
| }) | ||
| .filter((icon) => iconMatchesOriginalQueryConcept(icon, query)) | ||
| .filter((icon) => iconMatchesOriginalQueryConcept(icon, inflectionVariant)) | ||
| .map((icon) => ({ | ||
@@ -805,2 +899,50 @@ ...icon, | ||
| function normalizeExactIconIdentity(value) { | ||
| return String(value || '') | ||
| .normalize('NFKC') | ||
| .toLowerCase() | ||
| .replace(/[_:]+/g, ' ') | ||
| .replace(/[^\p{L}\p{N}\s-]/gu, ' ') | ||
| .replace(/-/g, ' ') | ||
| .replace(/\s+/g, ' ') | ||
| .trim(); | ||
| } | ||
| function prioritizeExactIconMatches(query, results = [], options = {}) { | ||
| if ( | ||
| String(options.library || '').toLowerCase() !== 'si' | ||
| || normalizeSearchLibraryMode(options.libraryMode) !== 'strict' | ||
| ) { | ||
| return results; | ||
| } | ||
| const normalizedQuery = normalizeExactIconIdentity(query); | ||
| if (!normalizedQuery || results.length < 2) return results; | ||
| const exact = []; | ||
| const remaining = []; | ||
| for (const icon of results) { | ||
| const identities = [ | ||
| icon.id, | ||
| icon.name, | ||
| icon.label, | ||
| icon.icon_id, | ||
| icon.lib && icon.id ? `${icon.lib}:${icon.id}` : null, | ||
| ]; | ||
| const isExact = identities.some( | ||
| (identity) => normalizeExactIconIdentity(identity) === normalizedQuery, | ||
| ); | ||
| (isExact ? exact : remaining).push(icon); | ||
| } | ||
| return exact.length > 0 ? [...exact, ...remaining] : results; | ||
| } | ||
| export function searchIcons(query, icons, synonyms, options = {}) { | ||
| return prioritizeExactIconMatches( | ||
| query, | ||
| searchIconsCore(query, icons, synonyms, options), | ||
| options, | ||
| ); | ||
| } | ||
| function searchIconsWithInterpretationPlan(query, icons, synonyms, plan, options = {}) { | ||
@@ -807,0 +949,0 @@ const limit = Math.max(1, Number(options.limit || 20)); |
@@ -127,2 +127,50 @@ const BASE_FILTER_TAGS = Object.freeze(['agentic-ai-tools-pack', 'brand-logo']); | ||
| }, | ||
| { | ||
| id: 'agent-identity', | ||
| label: 'Agent Identity', | ||
| description: 'Faces and avatars for named agents in a fleet: scouts, companions, builders, and assistants.', | ||
| sidebarGlyph: 'face', | ||
| }, | ||
| { | ||
| id: 'game-assets', | ||
| label: 'Game Assets', | ||
| description: 'Controllers, characters, enemies, and props for builders shipping their own games.', | ||
| sidebarGlyph: 'sports_esports', | ||
| }, | ||
| { | ||
| id: 'everyday-objects', | ||
| label: 'Everyday Objects', | ||
| description: 'Ordinary things from daily life: household items, hardware, tools, and personal effects.', | ||
| sidebarGlyph: 'category', | ||
| }, | ||
| { | ||
| id: 'health-body', | ||
| label: 'Health and Body', | ||
| description: 'Anatomy, organs, microbiology, and human or veterinary health concepts.', | ||
| sidebarGlyph: 'health_and_safety', | ||
| }, | ||
| { | ||
| id: 'personal-care', | ||
| label: 'Personal Care', | ||
| description: 'Grooming, hair care, oral care, cosmetics, and everyday bathroom routines.', | ||
| sidebarGlyph: 'self_care', | ||
| }, | ||
| { | ||
| id: 'food-dining', | ||
| label: 'Food and Dining', | ||
| description: 'Meals, dishes, restaurant service, and food delivery.', | ||
| sidebarGlyph: 'restaurant', | ||
| }, | ||
| { | ||
| id: 'nature-animals', | ||
| label: 'Nature and Animals', | ||
| description: 'Animals, prehistoric life, fossils, and the natural world.', | ||
| sidebarGlyph: 'pets', | ||
| }, | ||
| { | ||
| id: 'kitchen', | ||
| label: 'Kitchen', | ||
| description: 'Cookware, tableware, prep tools, and kitchen appliances.', | ||
| sidebarGlyph: 'skillet', | ||
| }, | ||
| ]); | ||
@@ -679,2 +727,202 @@ | ||
| ), | ||
| 'si:agent-scout': taxonomy( | ||
| 'agent-identity', | ||
| ['agent-lifecycle-states', 'people-accounts'], | ||
| ['bot', 'explorer', 'chatbot', 'avatar'] | ||
| ), | ||
| 'si:agent-wink': taxonomy( | ||
| 'agent-identity', | ||
| ['agent-lifecycle-states', 'people-accounts'], | ||
| ['bot', 'friendly', 'companion', 'avatar'] | ||
| ), | ||
| 'si:game-pad': taxonomy( | ||
| 'game-assets', | ||
| ['devices-hardware', 'media-playback'], | ||
| ['controller', 'play', 'gaming', 'console'] | ||
| ), | ||
| 'si:game-ghost': taxonomy( | ||
| 'game-assets', | ||
| ['trending-culture'], | ||
| ['enemy', 'sprite', 'arcade', 'npc'] | ||
| ), | ||
| 'si:toothpaste': taxonomy( | ||
| 'everyday-objects', | ||
| ['health-body', 'trending-culture'], | ||
| ['dental', 'hygiene', 'teeth', 'routine'] | ||
| ), | ||
| 'si:bacteria': taxonomy( | ||
| 'health-body', | ||
| ['frontier-compute'], | ||
| ['germ', 'microbe', 'pathogen', 'microbiology'] | ||
| ), | ||
| 'si:stomach': taxonomy( | ||
| 'health-body', | ||
| [], | ||
| ['gut', 'digestion', 'organ', 'anatomy'] | ||
| ), | ||
| 'si:lawn-mower': taxonomy( | ||
| 'physical-automation', | ||
| ['everyday-objects'], | ||
| ['mower', 'grass', 'gardening', 'yard'] | ||
| ), | ||
| 'si:house-key': taxonomy( | ||
| 'everyday-objects', | ||
| ['security-access', 'maps-places-travel'], | ||
| ['key', 'home', 'real-estate', 'property'] | ||
| ), | ||
| 'si:screw': taxonomy( | ||
| 'everyday-objects', | ||
| [], | ||
| ['fastener', 'hardware', 'diy', 'tornillo'] | ||
| ), | ||
| 'si:agent-pod': taxonomy( | ||
| 'agent-identity', | ||
| ['agent-lifecycle-states', 'people-accounts'], | ||
| ['bot', 'avatar', 'ai-face', 'companion'] | ||
| ), | ||
| 'si:cashback': taxonomy( | ||
| 'agentic-payments', | ||
| ['commerce-finance', 'everyday-objects'], | ||
| ['refund', 'rebate', 'rewards', 'money-back'] | ||
| ), | ||
| 'si:lottery-ticket': taxonomy( | ||
| 'agentic-payments', | ||
| ['trending-culture', 'commerce-finance'], | ||
| ['raffle', 'lotto', 'prize-draw', 'sweepstakes'] | ||
| ), | ||
| 'si:noodle-bowl': taxonomy( | ||
| 'food-dining', | ||
| ['kitchen', 'everyday-objects'], | ||
| ['ramen', 'noodles', 'pasta', 'meal'] | ||
| ), | ||
| 'si:dinosaur': taxonomy( | ||
| 'nature-animals', | ||
| ['trending-culture'], | ||
| ['sauropod', 'long-neck', 'dino', 'prehistoric', 'paleontology'] | ||
| ), | ||
| 'si:fossil': taxonomy( | ||
| 'nature-animals', | ||
| [], | ||
| ['ammonite', 'paleontology', 'archaeology', 'geology'] | ||
| ), | ||
| 'si:comb': taxonomy( | ||
| 'personal-care', | ||
| ['everyday-objects'], | ||
| ['hair-care', 'grooming', 'barber', 'styling'] | ||
| ), | ||
| 'si:hairbrush': taxonomy( | ||
| 'personal-care', | ||
| ['everyday-objects'], | ||
| ['hair-care', 'grooming', 'paddle-brush', 'styling'] | ||
| ), | ||
| 'si:hair-clipper': taxonomy( | ||
| 'personal-care', | ||
| ['everyday-objects'], | ||
| ['hair-care', 'grooming', 'barber', 'trimmer'] | ||
| ), | ||
| 'si:mascara': taxonomy( | ||
| 'personal-care', | ||
| ['trending-culture'], | ||
| ['cosmetics', 'beauty', 'makeup', 'eyelashes'] | ||
| ), | ||
| 'si:nail-polish': taxonomy( | ||
| 'personal-care', | ||
| ['trending-culture'], | ||
| ['cosmetics', 'beauty', 'manicure', 'nails'] | ||
| ), | ||
| 'si:toothbrush': taxonomy( | ||
| 'personal-care', | ||
| ['everyday-objects', 'health-body'], | ||
| ['oral-care', 'hygiene', 'dental', 'brushing'] | ||
| ), | ||
| 'si:dental-floss': taxonomy( | ||
| 'personal-care', | ||
| ['health-body'], | ||
| ['oral-care', 'hygiene', 'flossing', 'dental'] | ||
| ), | ||
| 'si:shampoo': taxonomy( | ||
| 'personal-care', | ||
| ['everyday-objects'], | ||
| ['hair-care', 'shower', 'toiletries', 'grooming'] | ||
| ), | ||
| 'si:lotion': taxonomy( | ||
| 'personal-care', | ||
| ['everyday-objects', 'health-body'], | ||
| ['skincare', 'moisturizer', 'toiletries', 'grooming'] | ||
| ), | ||
| 'si:sunscreen': taxonomy( | ||
| 'personal-care', | ||
| ['everyday-objects', 'health-body'], | ||
| ['skincare', 'spf', 'sun-protection', 'summer'] | ||
| ), | ||
| 'si:cotton-swab': taxonomy( | ||
| 'personal-care', | ||
| ['everyday-objects', 'health-body'], | ||
| ['hygiene', 'first-aid', 'q-tip', 'cotton-bud'] | ||
| ), | ||
| 'si:tweezers': taxonomy( | ||
| 'personal-care', | ||
| ['everyday-objects', 'health-body'], | ||
| ['grooming', 'beauty', 'eyebrows', 'precision'] | ||
| ), | ||
| 'si:plate': taxonomy( | ||
| 'kitchen', | ||
| ['food-dining'], | ||
| ['tableware', 'dining', 'restaurant', 'place-setting'] | ||
| ), | ||
| 'si:cutting-board': taxonomy( | ||
| 'kitchen', | ||
| ['everyday-objects'], | ||
| ['prep-tools', 'chopping', 'food-prep', 'cookware'] | ||
| ), | ||
| 'si:wok': taxonomy( | ||
| 'kitchen', | ||
| ['food-dining'], | ||
| ['cookware', 'stir-fry', 'asian-cooking', 'pan'] | ||
| ), | ||
| 'si:toaster': taxonomy( | ||
| 'kitchen', | ||
| ['food-dining', 'everyday-objects'], | ||
| ['appliance', 'breakfast', 'toast', 'bread'] | ||
| ), | ||
| 'si:mixer': taxonomy( | ||
| 'kitchen', | ||
| ['food-dining'], | ||
| ['appliance', 'baking', 'stand-mixer', 'whisk'] | ||
| ), | ||
| 'si:grater': taxonomy( | ||
| 'kitchen', | ||
| [], | ||
| ['prep-tools', 'shredding', 'cheese', 'zester'] | ||
| ), | ||
| 'si:peeler': taxonomy( | ||
| 'kitchen', | ||
| [], | ||
| ['prep-tools', 'peeling', 'vegetables', 'y-peeler'] | ||
| ), | ||
| 'si:rolling-pin': taxonomy( | ||
| 'kitchen', | ||
| ['food-dining'], | ||
| ['baking', 'dough', 'pastry', 'prep-tools'] | ||
| ), | ||
| 'si:tongs': taxonomy( | ||
| 'kitchen', | ||
| ['food-dining'], | ||
| ['prep-tools', 'serving', 'grilling', 'bbq'] | ||
| ), | ||
| 'si:colander': taxonomy( | ||
| 'kitchen', | ||
| [], | ||
| ['prep-tools', 'draining', 'strainer', 'pasta'] | ||
| ), | ||
| 'si:corkscrew': taxonomy( | ||
| 'kitchen', | ||
| ['food-dining'], | ||
| ['bar-tools', 'wine', 'opener', 'bottle'] | ||
| ), | ||
| 'si:can-opener': taxonomy( | ||
| 'kitchen', | ||
| ['everyday-objects'], | ||
| ['prep-tools', 'tin', 'opening', 'pantry'] | ||
| ), | ||
| }); | ||
@@ -681,0 +929,0 @@ |
+2
-2
@@ -10,3 +10,3 @@ { | ||
| }, | ||
| "version": "0.4.24", | ||
| "version": "0.4.25", | ||
| "remotes": [ | ||
@@ -22,3 +22,3 @@ { | ||
| "identifier": "@supericons/mcp", | ||
| "version": "0.4.24", | ||
| "version": "0.4.25", | ||
| "transport": { | ||
@@ -25,0 +25,0 @@ "type": "stdio" |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
25968912
0.41%120992
0.43%