UNPKG

@extenshi/mcp

Version:

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

300 lines • 12.6 kB
/** * Thin tRPC client for the public catalog BFF (`bff.extenshi.io`). * * The BFF tRPC server is configured WITHOUT a data transformer (no superjson), * so this client must match — plain JSON over the wire. We type the proxy as * `any` on purpose: catalog-api emits no `.d.ts`, so the BFF↔API type chain * can't be imported reliably across a published-package boundary. The runtime * proxy builds the procedure path from property access regardless of types. * * Every call carries `Authorization: Bearer <ek_…>`. Today the BFF read * procedures are anonymous, but they are moving to mandatory key-enforcement * (no anonymous data access) — sending the key now is forward-compatible and * needs no client change when enforcement lands. */ import { type PayBff } from './pay.js'; /** One credit pool as reported by `getApiCallerBalance`. */ export interface CreditPool { /** Total calls this pool can still fund (free grant + purchased) — the single number that gates a call. */ remaining: number; /** The slice of `remaining` still coming from the one-time signup grant (always ≤ remaining). */ freeRemaining: number; freeGranted: number; freeUsed: number; } /** Read/scan/icon/inventory balances for the `ek_` key or remote-MCP identity. */ export interface ApiCallerBalance { read: CreditPool; scan: CreditPool; icon: CreditPool; inventory: CreditPool; billingUrl: string; } /** A resolved store→catalog reference (`resolveExtensionRef`). */ export interface ExtensionRef { id: number; store: 'CHROME' | 'FIREFOX' | 'EDGE'; } export interface Bff extends PayBff { /** * Create a project owned by this identity — the same service as the Dojo * wizard. Idempotent per `idempotencyKey`. Requires project.write. */ createProject(input: import('@extenshi/contracts').AgentCreateProject): Promise<unknown>; /** Owner decisions with status, suggested defaults and what each blocks. */ getDecisions(input: { projectId: string; }): Promise<unknown>; /** Record a PROPOSAL; only the owner decides, in Dojo. Requires project.write. */ proposeDecision(input: import('@extenshi/contracts').ProposeDecision): Promise<unknown>; importManifest(input: import('@extenshi/contracts').ManifestImport): Promise<unknown>; getProjectWorkspace(input: { projectId: string; }): Promise<unknown>; diffProjectWorkspace(input: import('@extenshi/contracts').WorkspacePatch): Promise<unknown>; patchProjectWorkspace(input: import('@extenshi/contracts').WorkspacePatch): Promise<unknown>; recordProjectEvidence(input: Omit<import('@extenshi/contracts').Evidence, 'id' | 'recordedAt'>): Promise<unknown>; getReleaseReadiness(input: { projectId: string; browser?: 'chrome' | 'firefox' | 'edge'; artifactDigest?: string; }): Promise<unknown>; connectionDiagnostics(): Promise<unknown>; searchExtensions(input: Record<string, unknown>): Promise<unknown>; getExtensionById(id: number): Promise<unknown>; /** Paginated store user reviews for an extension (PII-free projection). */ getReviews(input: Record<string, unknown>): Promise<unknown>; getSecurityData(extensionId: number): Promise<unknown>; getRiskSummary(extensionId: number): Promise<unknown>; getSearchFacets(input: Record<string, unknown>): Promise<unknown>; getStats(): Promise<unknown>; /** Catalog-wide extended facets (manifest / permissions / risk / freshness …), cached server-side. */ getExtendedFilterFacets(): Promise<unknown>; /** Catalog-wide category tree with per-category counts. */ getCategoryTree(): Promise<unknown>; /** This caller's credit balances across all pools. FREE — never spends a read credit. */ getApiCallerBalance(): Promise<ApiCallerBalance>; /** * Resolve a public STORE extension id (from the store URL) to the numeric * catalog id the read tools take. FREE — the read that follows is what's * metered. Returns null when no catalog listing exists for it yet. */ resolveExtensionRef(input: { storeId: string; store?: 'CHROME' | 'FIREFOX' | 'EDGE'; }): Promise<ExtensionRef | null>; /** * Risk for MANY extensions addressed by store id, in ONE metered read. * Cluster-resolved server-side, so each row describes the same listing the * extension page renders. Capped at 40 extensions per call for metered * callers — the BFF rejects a larger batch with a message naming the limit. */ getSecuritySummaryBatch(input: { extensions: Array<{ storeId: string; store: 'CHROME' | 'FIREFOX' | 'EDGE'; }>; }): Promise<unknown>; /** * The caller's OWN extension projects. FREE — credits pay for catalog data * about other people's extensions, never for reading your own work. */ listMyProjects(): Promise<unknown>; /** * One own project as state an agent can act on: chosen extension types, the * manifest.json that project produces per browser, live hosted URLs, and an * index of saved tool state. FREE, same reasoning. `includeToolStates` inlines * named payloads; without it the response lists what exists and how big. */ getMyProjectState(input: { projectId: string; includeToolStates?: string[]; /** `summary` (BFF ≥ 2026-10): manifest bodies become digest + keys. An older BFF ignores it. */ manifests?: 'full' | 'summary'; }): Promise<unknown>; /** * The starter extension one own project produces, as files — the same set the * site commits and packs into its download. FREE, same reasoning. One browser * per call: the manifests differ between them and the rest does not. */ getMyProjectScaffold(input: { projectId: string; browser?: string; }): Promise<unknown>; listPrivacyPolicyVersions(input: { projectId: string; }): Promise<unknown>; getPrivacyPolicyVersion(input: { projectId: string; versionNumber: number; }): Promise<unknown>; publishPrivacyPolicy(input: { projectId: string; bodyMarkdown?: string; kind?: string; }): Promise<unknown>; updatePrivacyPolicyWithAi(input: { projectId: string; }): Promise<unknown>; /** * Create (or ROTATE) the per-project CI evidence ingest secret. The * plaintext is shown once and belongs in the repository's Actions secrets. */ createCiIngestSecret(input: { projectId: string; }): Promise<unknown>; revokeCiIngestSecret(input: { projectId: string; }): Promise<unknown>; /** Register a project's homepage/support URL (HTTPS, reachable, hashed). */ upsertHostedPage(input: { projectId: string; kind: string; url: string; }): Promise<unknown>; /** Re-fetch and compare: verified | changed | unreachable per kind. */ verifyHostedArtifact(input: { projectId: string; kind: string; }): Promise<unknown>; /** Forget a registration (a project moving off a URL needs a real delete). */ removeHostedPage(input: { projectId: string; kind: string; }): Promise<unknown>; listHostedPages(input: { projectId: string; }): Promise<unknown>; /** Publish (create or update) the project's landing page on page.extenshi.io. */ publishLandingPage(input: { projectId: string; form: Record<string, unknown>; registerAsHomepage?: boolean; }): Promise<unknown>; getLandingPage(input: { projectId: string; }): Promise<unknown>; unpublishLandingPage(input: { projectId: string; }): Promise<unknown>; /** Schema-v2 homepage drafted from project state: {form, sources, todos, problems, previewUrl}. Writes nothing. */ draftLandingPage(input: { projectId: string; storeUrls?: { chrome?: string; firefox?: string; edge?: string; }; accentColor?: string; }): Promise<unknown>; /** Render exactly as publish would (validation, Pay offers, canonical) without writing. */ previewLandingPage(input: { projectId: string; form: Record<string, unknown>; locale?: string; }): Promise<unknown>; listLandingVersions(input: { projectId: string; }): Promise<unknown>; /** Publish an earlier version's form as a new version. */ rollbackLandingPage(input: { projectId: string; versionNumber: number; registerAsHomepage?: boolean; }): Promise<unknown>; /** Custom domain: records to set, verification state, next step. */ getCustomDomain(input: { projectId: string; }): Promise<unknown>; setCustomDomain(input: { projectId: string; hostname: string; }): Promise<unknown>; verifyCustomDomain(input: { projectId: string; }): Promise<unknown>; removeCustomDomain(input: { projectId: string; }): Promise<unknown>; /** The hosted install instructions page (it. 10 T8): state, URL and the saved form. */ getInstructionsPage(input: { projectId: string; }): Promise<unknown>; /** Publish the install instructions (the saved form when `input` is omitted). */ publishInstructions(input: { projectId: string; input?: Record<string, unknown>; }): Promise<unknown>; setInstructionsEnabled(input: { projectId: string; enabled: boolean; }): Promise<unknown>; /** Every uninstall form and welcome page of the project: translatable fields and per-language status. */ getPageTranslations(input: { projectId: string; }): Promise<unknown>; /** Save one language of one page (text by field path; the server records the source text). */ setPageTranslations(input: { projectId: string; surface: 'uninstall' | 'welcome' | 'instructions'; publicCode: string; locale: string; translations: Record<string, string>; }): Promise<unknown>; /** * Translated copies of the hosted privacy policy / license terms (it. 13 T10 * L3): what the developer can translate (edited sections, own answers), * languages, status per language and the translation URLs. The English * version stays the binding text. */ getPrivacyPolicyTranslations(input: { projectId: string; }): Promise<unknown>; setPrivacyPolicyTranslations(input: { projectId: string; locale: string; translations: Record<string, string>; sources?: Record<string, string>; }): Promise<unknown>; getLicenseTermsTranslations(input: { projectId: string; }): Promise<unknown>; setLicenseTermsTranslations(input: { projectId: string; locale: string; translations: Record<string, string>; sources?: Record<string, string>; }): Promise<unknown>; /** * The "License terms & refunds" questions as data, and a draft generated from * answers the caller sends. project.read: neither reads saved answers or Pay * state, and neither writes (BFF payLicenseTerms.agentGetQuestionnaire / agentDraft). */ getLicenseTermsQuestionnaire(input: { projectId: string; }): Promise<unknown>; draftLicenseTerms(input: { projectId: string; answers?: Record<string, unknown>; }): Promise<unknown>; /** * Upload one PNG/JPEG/WebP to the project's PUBLIC media store (the same one * Dojo uploads use). The BFF re-checks magic bytes, strips metadata, enforces * size/dimension/quota limits and returns a stable content-addressed URL. */ uploadProjectMedia(input: { projectId: string; mime: 'image/png' | 'image/jpeg' | 'image/webp'; dataBase64: string; }): Promise<unknown>; } /** Build a BFF client from a static `ek_…` key (stdio path). */ export declare function makeBff(bffUrl: string, apiKey: string): Bff; /** * Build a BFF client whose Authorization header is produced per request by * `authHeader` (may be async). The remote OAuth server uses this to mint a * fresh, short-lived genkan JWT for each call instead of holding a static key. * The provider returns the FULL header value (e.g. `Bearer eyJ…`). */ export declare function makeBffWithAuth(bffUrl: string, authHeader: () => string | Promise<string>): Bff; //# sourceMappingURL=bff.d.ts.map