UNPKG

@extenshi/mcp

Version:

Extenshi MCP server — bring the extension catalog (search, security, market research) and pre-publish scanning into your AI tools

106 lines • 5.51 kB
/** * Output shaping for MCP tool results. * * MCP tool results land directly in the AI client's context window, so we keep * them compact and curated. Field names below match the live BFF payloads * (catalog router mounted as `catalog`, security as `security`): * - search items wrap a cross-store cluster: `{ id, slug, snapshots[], availableStores, security }` * where the human-facing fields (name, rating, users, store) live on each snapshot. * - `getExtensionById` returns the cluster with aggregated `latest*` fields. * - `getSecurityData` returns `{ riskAssessment, scanExecution, findings:{ total, groupTotal, bySeverity } }`. * - `getRiskSummary` is already a compact summary (or null if never scanned). * * `compact()` is a defensive fallback that bounds array length / string length / * depth so a tool can never blow up the context even if a shape drifts. */ interface CompactOpts { maxArray?: number; maxString?: number; maxDepth?: number; } /** Recursively bound a value's size: cap arrays, truncate strings, limit depth. */ export declare function compact(value: unknown, opts?: CompactOpts, depth?: number): unknown; type Obj = Record<string, unknown>; /** Compact a search result down to a count + curated items. */ export declare function shapeSearch(result: unknown, limit: number): Obj; /** Curate a single extension detail (`getExtensionById`). */ export declare function shapeExtension(result: unknown): unknown; /** * Curate a page of store user reviews (`get_reviews`). Passes through the * keyset `nextCursor` so the model can page, a `count` for quick sizing, and the * store-level `aggregate` (rating / count / snapshot date + reviews link) — the * bare facts that stay public even for Chrome. */ export declare function shapeReviews(result: unknown, limit: number): Obj; /** * Curate the install-dialog preview — the consolidated permission prompt the * browser shows at install (computed server-side by catalog-api from the * manifest's required permissions; source of truth: shared-types/permission-warnings.ts). * Reduces each browser's warnings to their human-readable lines for terseness. */ export declare function shapeInstallDialog(preview: unknown): Obj | undefined; /** Capability phrase for a scanner key; unknown keys get a generic phrase, never the raw name. */ export declare function scannerPhrase(scanner: unknown): string | undefined; /** * Some rule ids carry the scanner key as a namespace (`jstap:document-cookie-access`); * the same string is often the finding title. Drop a known scanner prefix so the * tool name never leaks through the rule or title either. */ export declare function stripScannerPrefix(value: unknown): unknown; /** Curate the security view: risk summary + grouped findings (top N per severity). */ export declare function shapeSecurity(security: unknown, riskSummary: unknown, installDialogPreview?: unknown): Obj; /** * Shape one row of `security.getSecuritySummaryBatch` for the risk-by-store-id * tool. * * Two things this deliberately does NOT hide: * - `scoredStore` / `scoredVariantId` — the batch resolves a store id through * its cross-store cluster to the record the extension page renders, and * cluster members are scanned independently (the same extension scored 78.55 * HIGH on its Chrome record and 80.47 CRITICAL on its Edge one). An agent * reporting a number to a developer must be able to say which listing it * describes. * - a `null` score — "not scanned" is an expected state, not an error, and * must never be flattened into a reassuring 100. * * `catalogUrl` uses the CANONICAL id so the link lands on the page the score * came from without a redirect hop. */ export declare function shapeStoreRiskRow(row: unknown): Obj; /** * Shape a whole risk-by-store-id batch, and say which requested ids the catalog * had no listing for — an agent that cannot tell "safe" from "unknown" will * report the wrong thing. */ export declare function shapeStoreRiskBatch(rows: unknown, requested: Array<{ storeId: string; store: string; }>): Obj; /** Facet groups `market_overview` can return, selectable via its `facets` argument. */ export declare const MARKET_FACETS: readonly ["stats", "stores", "categories", "extended", "questionnaire", "downloads"]; export type MarketFacet = (typeof MARKET_FACETS)[number]; /** Default number of entries kept per open-ended facet list (categories, permissions, sites). */ export declare const DEFAULT_MARKET_TOP_N = 10; export declare const MAX_MARKET_TOP_N = 100; export interface MarketOverviewInput { scope: 'search' | 'catalog-wide'; stats?: unknown; /** Raw facets as the BFF returns them (search facets, or the catalog-wide assembly). */ facets?: unknown; note?: string; } export interface MarketShapeOpts { facets?: readonly string[]; topN?: number; } /** * Trim a market_overview payload for the model's context: pick the requested * facet groups (default: all), cut open-ended lists to the `topN` largest * entries, and reduce category nodes to name/slug/count. Counts are passed * through unchanged — this shapes the response, it does not recompute data. * A `truncated` map says how many entries each trimmed list dropped, so the * caller knows when a larger `top_n` would reveal more. */ export declare function shapeMarketOverview(input: MarketOverviewInput, opts?: MarketShapeOpts): Obj; export {}; //# sourceMappingURL=shape.d.ts.map