Sign In

@supericons/mcp

Package Overview
Dependencies
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@supericons/mcp - npm Package Compare versions

Comparing version
0.4.30
to
0.4.31
+113
hosted-si-catalog-status.js
import { createHash } from 'node:crypto';
const DEFAULT_PAGE_SIZE = 1000;
export function exactSiSetSummary(refs) {
const values = [...new Set((refs || []).map((value) => String(value || '').trim()).filter(Boolean))].sort();
return {
count: values.length,
sha256: createHash('sha256').update(values.join('\n')).digest('hex'),
refs: values,
};
}
export function compareSiSets(actualRefs, expectedRefs) {
const actual = exactSiSetSummary(actualRefs);
const expected = exactSiSetSummary(expectedRefs);
const actualSet = new Set(actual.refs);
const expectedSet = new Set(expected.refs);
return {
count: actual.count,
sha256: actual.sha256,
matches_expected: actual.sha256 === expected.sha256 && actual.count === expected.count,
missing: expected.refs.filter((ref) => !actualSet.has(ref)),
extra: actual.refs.filter((ref) => !expectedSet.has(ref)),
};
}
async function fetchRows({
supabaseUrl,
serviceRoleKey,
table,
filterName,
filterValue,
fetchImpl,
pageSize,
timeoutMs,
}) {
const rows = [];
for (let offset = 0; ; offset += pageSize) {
const url = new URL(`${String(supabaseUrl).replace(/\/+$/, '')}/rest/v1/${table}`);
url.searchParams.set('select', 'icon_id');
url.searchParams.set(filterName, filterValue);
url.searchParams.set('order', 'icon_id.asc');
url.searchParams.set('limit', String(pageSize));
url.searchParams.set('offset', String(offset));
const response = await fetchImpl(url, {
headers: {
apikey: serviceRoleKey,
Authorization: `Bearer ${serviceRoleKey}`,
},
signal: AbortSignal.timeout(timeoutMs),
});
if (!response.ok) {
const detail = await response.text().catch(() => '');
throw new Error(`Supabase read failed for ${table} (${response.status}): ${detail}`);
}
const page = await response.json();
if (!Array.isArray(page)) throw new Error(`Supabase returned a non-array payload for ${table}.`);
rows.push(...page);
if (page.length < pageSize) return rows;
}
}
export async function readHostedSiCatalogStatus({
supabaseUrl,
serviceRoleKey,
expectedRefs,
fetchImpl = fetch,
pageSize = DEFAULT_PAGE_SIZE,
timeoutMs = 5000,
} = {}) {
const expected = exactSiSetSummary(expectedRefs);
if (!supabaseUrl || !serviceRoleKey) {
return {
available: false,
reason: 'service_role_unavailable',
expected: { count: expected.count, sha256: expected.sha256 },
};
}
const [catalogRows, registryRows] = await Promise.all([
fetchRows({
supabaseUrl,
serviceRoleKey,
table: 'icon_catalog',
filterName: 'source_library',
filterValue: 'eq.si',
fetchImpl,
pageSize,
timeoutMs,
}),
fetchRows({
supabaseUrl,
serviceRoleKey,
table: 'icon_search_public_registry_metadata',
filterName: 'icon_id',
filterValue: 'like.si:*',
fetchImpl,
pageSize,
timeoutMs,
}),
]);
const catalog = compareSiSets(catalogRows.map((row) => row.icon_id), expected.refs);
const registry = compareSiSets(registryRows.map((row) => row.icon_id), expected.refs);
return {
available: true,
matches_bundle: catalog.matches_expected && registry.matches_expected,
expected: { count: expected.count, sha256: expected.sha256 },
icon_catalog: catalog,
public_registry: registry,
};
}
import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
export const SEARCH_RUNTIME_FILES = Object.freeze([
'hosted-si-catalog-status.js',
'railway-local-search.js',
'remote-server.js',
'search-release-fingerprint.js',
'search.js',
'semantic-registry.js',
'public/icon-index.json',
'public/synonyms.json',
'runtime/search-pipeline.js',
]);
export function buildSearchReleaseFingerprint({
packageRoot,
version,
siIconSetSha256,
releaseCommit = '',
} = {}) {
const runtimeHash = createHash('sha256');
for (const relativePath of SEARCH_RUNTIME_FILES) {
runtimeHash.update(relativePath);
runtimeHash.update('\0');
runtimeHash.update(readFileSync(join(packageRoot, relativePath)));
runtimeHash.update('\0');
}
const runtimeSha256 = runtimeHash.digest('hex');
const configuredCommit = String(releaseCommit || '').trim();
const releaseCommitSha = /^[0-9a-f]{7,64}$/i.test(configuredCommit)
? configuredCommit.toLowerCase()
: null;
const releaseSha256 = createHash('sha256')
.update([String(version || ''), String(siIconSetSha256 || ''), runtimeSha256].join('\n'))
.digest('hex');
return {
release_commit_sha: releaseCommitSha,
runtime_sha256: runtimeSha256,
release_sha256: releaseSha256,
};
}
+14
-0
# Changelog
## 0.4.31 - 2026-08-10
### Fixed
- made exact SI DNA synonyms and semantic tags searchable across local and hosted routes
- kept exact SI identity after hosted result fusion, including XAI and SpaceXAI aliases
- linked website, hosted catalog, Railway runtime, and npm release checks
### Verified
- added full-library identity and alternate DNA search coverage
- added exact hosted SI catalog and content-bearing runtime fingerprints
- added a bounded release runner with rollback for Supabase, Railway, Netlify, and npm latest
## 0.4.30 - 2026-08-10

@@ -4,0 +18,0 @@

+4
-2
{
"name": "@supericons/mcp",
"version": "0.4.30",
"version": "0.4.31",
"mcpName": "io.github.curlymolelabs/supericons",

@@ -37,2 +37,3 @@ "description": "MCP server for Supericons: multilingual semantic SVG icon search and recommendations for AI coding agents.",

"hosted-search-resilience.js",
"hosted-si-catalog-status.js",
"index.js",

@@ -62,2 +63,3 @@ "library-capabilities.js",

"search-query-normalization.js",
"search-release-fingerprint.js",
"search-tool-shell.js",

@@ -96,3 +98,3 @@ "server.json",

"verify:search-v2-shell": "node ../scripts/verify-search-v2-one-call-contract.mjs --package-root . && node ../scripts/verify-mcp-agent-friendly-errors.mjs && node ../scripts/verify-search-v2-packaged-query-frame.mjs --package-root . && node ../scripts/verify-search-v2-429-propagation.mjs && node ../scripts/verify-recommend-icons-clarification.mjs",
"prepublishOnly": "npm run verify:si-search-surface-parity && npm run verify:public-safety && node ../scripts/verify-mcp-preview-icons-image.mjs && npm run verify:preview-exact-ref && npm run verify:synchronized-surfaces && npm run verify:route-package && npm run verify:search-v2-shell && npm run verify:package",
"prepublishOnly": "npm run verify:si-search-surface-parity && node ../scripts/verify-si-dna-search-coverage.mjs && node ../scripts/verify-si-search-release-guards.mjs && npm run verify:public-safety && node ../scripts/verify-mcp-preview-icons-image.mjs && npm run verify:preview-exact-ref && npm run verify:synchronized-surfaces && npm run verify:route-package && npm run verify:search-v2-shell && npm run verify:package",
"verify:package": "node ../scripts/verify-motion-lab-mcp-package.mjs",

@@ -99,0 +101,0 @@ "verify:si-search-surface-parity": "node ../scripts/verify-si-search-surface-parity.mjs"

{
"generatedAt": "2026-08-09T15:12:04.283Z",
"freeIconCount": 21541,
"generatedAt": "2026-08-09T20:16:05.277Z",
"freeIconCount": 21542,
"freeLibraryCount": 11,

@@ -9,3 +9,3 @@ "premiumCollectionCount": 9,

"mcpFreeToolCount": 5,
"mcpPackageVersion": "0.4.30",
"mcpPackageVersion": "0.4.31",
"display": {

@@ -12,0 +12,0 @@ "freeIconsRounded": "20,000+",

@@ -449,3 +449,3 @@ /**

const { name, id, fullId, tokens, compactPrimaryValues } = getIconSearchMetadata(icon);
const { name, id, fullId, tokens, primaryTokens, compactPrimaryValues } = getIconSearchMetadata(icon);
const meaningfulQueryWords = getMeaningfulQueryWords(queryWords);

@@ -463,2 +463,10 @@

const hasBrandIdentityInflection =
isLikelyBrandIdentityIcon(icon) &&
meaningfulQueryWords.length > 0 &&
meaningfulQueryWords.every((word) =>
[...primaryTokens].some((token) => token === word || isSafeInflectionalTokenMatch(word, token)),
);
if (hasBrandIdentityInflection) return 280;
const singleQueryWord = queryWords.length === 1 ? queryWords[0] : null;

@@ -950,2 +958,36 @@ const hasLongTokenPrefix =

function getExactMetadataValueAnchors(query, icons, synonyms, options = {}) {
const normalizedQuery = normalizeSemanticText(query);
if (!normalizedQuery) return [];
const library = String(options.library || '').toLowerCase();
const libraryMode = normalizeSearchLibraryMode(options.libraryMode);
const normalizedStyle = normalizeRequestedStyle(options.style || 'any');
const withoutLogoIntent = tokenizeSemanticText(query)
.filter((word) => !LOGO_INTENT_TOKENS.has(word))
.join(' ');
const exactQueries = new Set([normalizedQuery, withoutLogoIntent].filter(Boolean));
return getIndexedCandidatePool(icons, query, synonyms)
.filter((icon) => {
if (icon.lib !== 'si') return false;
if (library && libraryMode === 'strict' && icon.lib !== library) return false;
if (!iconMatchesRequestedStyle(icon, normalizedStyle)) return false;
const values = [
...(icon.semanticTags || []),
...(icon.synonyms || []),
...(icon.aliases || []),
...(icon.searchTerms || []),
];
return values.some((value) => exactQueries.has(normalizeSemanticText(value)));
})
.sort((a, b) => {
const aBrand = getBrandRankAdjustment(query, a);
const bBrand = getBrandRankAdjustment(query, b);
if (bBrand.boost !== aBrand.boost) return bBrand.boost - aBrand.boost;
if (aBrand.penalty !== bBrand.penalty) return aBrand.penalty - bBrand.penalty;
const rankDiff = getIconJobRank(a) - getIconJobRank(b);
return rankDiff || a.name.localeCompare(b.name);
});
}
export function prioritizeExactIconMatches(query, icons, synonyms, results = [], options = {}) {

@@ -962,2 +1004,3 @@ const requestedLibrary = String(options.library || '').toLowerCase();

const anchors = getExactIconIdentityAnchors(query, icons, synonyms, options);
const metadataAnchors = getExactMetadataValueAnchors(query, icons, synonyms, options);
const exact = [];

@@ -980,3 +1023,3 @@ const remaining = [];

const seen = new Set();
const merged = [...anchors, ...exact, ...remaining].filter((icon) => {
const merged = [...anchors, ...exact, ...metadataAnchors, ...remaining].filter((icon) => {
const key = iconKey(icon);

@@ -983,0 +1026,0 @@ if (seen.has(key)) return false;

@@ -62,2 +62,18 @@ import { GENERATED_SEARCH_RANKING_POLICY } from './generated-search-ranking-policy.js';

function isSafeBrandIdentityInflection(query, identity) {
const queryTokens = tokenize(query);
const identityTokens = tokenize(identity);
if (queryTokens.length === 0 || queryTokens.length !== identityTokens.length) return false;
let changed = false;
const matches = queryTokens.every((queryToken, index) => {
const identityToken = identityTokens[index];
if (queryToken === identityToken) return true;
if (identityToken.length < 3) return false;
const inflected = queryToken === `${identityToken}s` || queryToken === `${identityToken}es`;
if (inflected) changed = true;
return inflected;
});
return matches && changed;
}
function unique(values = []) {

@@ -304,3 +320,10 @@ return [...new Set(values.filter(Boolean))];

}
if (identity && (identity === meaningfulQuery || compactIdentity === compactQuery)) {
if (
identity
&& (
identity === meaningfulQuery
|| compactIdentity === compactQuery
|| isSafeBrandIdentityInflection(meaningfulQuery, identity)
)
) {
return { boost: 160, penalty: 0, match_class: 'distinctive_exact' };

@@ -307,0 +330,0 @@ }

@@ -10,3 +10,3 @@ {

},
"version": "0.4.30",
"version": "0.4.31",
"remotes": [

@@ -22,3 +22,3 @@ {

"identifier": "@supericons/mcp",
"version": "0.4.30",
"version": "0.4.31",
"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