/**
 * Transport-agnostic tool registry for the Extenshi MCP server.
 *
 * The SAME tool set must run over two transports:
 *   - stdio  (`index.ts`)  — local, single `ek_…` key from the environment.
 *   - remote (`http.ts`, in the sibling `@extenshi/mcp-server`) — Streamable
 *     HTTP behind OAuth; identity is per-request (an access token → userId).
 *
 * To keep ONE source of truth for what the tools do, every tool body here is
 * identity-agnostic: it resolves its catalog client / scan auth through the
 * injected `ToolDeps` instead of reaching for a module-global key. stdio passes
 * deps that ignore the call context and use the env key; the remote server
 * passes deps that read `context.session` (the validated OAuth identity).
 *
 * Capability gating: the remote connector deliberately exposes a SUBSET — the
 * catalog/project/policy/guidance tools. `scan_extension` and `publish_extension` need
 * the caller's LOCAL filesystem and LOCAL store credentials, which a hosted
 * server has no access to (and must never store) — so they are registered only
 * when the corresponding capability is present (stdio enables all four).
 * See internal-docs/plans/2026-06-25-claude-connector-directory.md §13 #1.
 *
 *   capability → tools
 *   ─────────────────────────────────────────────────────────────
 *   'read'    → search_extensions, get_extension, get_reviews,
 *               get_security, get_risk_by_store_ids, market_overview,
 *               list_my_projects, get_project_state, get_project_scaffold,
 *               list/get/publish/update privacy policy (hosted; Pro)
 *   'docs'    → get_development_guide, search_docs, list_extension_templates, generate_icon_workflow,
 *               generate_welcome_page_workflow, generate_landing_page, localize_workflow (free; no key)
 *   'scan'    → scan_extension             (local artifact; stdio only)
 *   'publish' → publish_extension          (local creds; stdio only)
 *
 * stdout is the MCP protocol channel for stdio — nothing here may write to it.
 */
import { type FastMCP, type FastMCPSessionAuth } from 'fastmcp';
import type { Bff } from './bff.js';
export declare const KEY_PAGE = "https://dojo.extenshi.io/api-keys";
export declare const SIGNUP_PAGE = "https://auth.extenshi.io/signup";
export declare const BILLING_PAGE = "https://dojo.extenshi.io/billing";
export declare const HOSTED_PAGES_DOCS = "https://docs.extenshi.io/developers/project-sync#hosted-pages-from-the-api";
export declare const MISSING_KEY_MESSAGE: string;
/**
 * The remote connector's counterpart of MISSING_KEY_MESSAGE. It signs in over
 * OAuth, so a rejected request means the sign-in lapsed — there is no key to find.
 */
export declare const RECONNECT_MESSAGE: string;
/**
 * Max extensions `get_risk_by_store_ids` accepts per call.
 *
 * CROSS-PACKAGE SYNC POINT — nothing can import across it: this is a published
 * npm package, the enforcing code is `METERED_BATCH_MAX_EXTENSIONS` in
 * `catalog/catalog-bff/src/lib/metered-batch-cap.ts`. The BFF is the authority
 * (it rejects an over-cap metered batch); this constant only keeps the tool
 * schema and description honest so an agent is told the limit up front instead
 * of discovering it as a round-tripped error. Pinned by `tools.test.ts`.
 */
export declare const MAX_BATCH_EXTENSIONS = 40;
export declare const SERVER_NAME = "extenshi";
/**
 * How the caller signs in. The stdio package reads an `ek_…` API key; the remote
 * connector signs in over OAuth and has no key at all, so any text that sends the
 * user to find one is wrong there.
 */
export type McpAuth = 'api-key' | 'oauth';
/**
 * Shown to the model at `initialize`. It states what the tools return and what
 * the account can do — not how to work — and is kept short: directory review
 * reads this block in full, and a cut-off copy hides the end of it. The length
 * is pinned by a test.
 */
export declare const SERVER_INSTRUCTIONS: string;
/** Match initialize instructions to the same capabilities used for registration. */
export declare function getServerInstructions(capabilities: ReadonlySet<Capability>, auth?: McpAuth): string;
/** Which tool groups to register on this transport. */
export type Capability = 'read' | 'docs' | 'scan' | 'publish';
/**
 * Minimal structural view of a FastMCP tool-execute context. The real context
 * (passed by FastMCP to `execute`) carries more (`reportProgress`, `log`, …);
 * we only need `session` for per-request identity resolution. Extra props are
 * fine — this is a structural supertype.
 */
export interface ToolCallContext {
    session?: FastMCPSessionAuth;
}
export interface ToolDeps {
    /** Base URLs for the BFF (reads), scan backend, and docs site. */
    cfg: {
        bffUrl: string;
        scanUrl: string;
        docsUrl: string;
    };
    /** Tool groups to register. */
    capabilities: ReadonlySet<Capability>;
    /**
     * Build a catalog BFF client for THIS call's identity.
     * stdio: ignores `ctx`, uses the env key (throws MISSING_KEY if absent).
     * remote: reads `ctx.session` (already OAuth-authenticated) and binds the
     * caller's userId.
     */
    getBff: (ctx: ToolCallContext) => Bff;
    /**
     * Resolve the scan/publish API key for THIS call (paid local ops). Required
     * only when the 'scan' or 'publish' capability is enabled. stdio supplies
     * the env key (or throws MISSING_KEY); the remote connector never enables
     * these capabilities, so it may omit this.
     */
    requireApiKey?: (ctx: ToolCallContext) => string;
    /**
     * Non-throwing key accessor for the publish-access preflight, which fails
     * OPEN when no key is present (it must not hard-require a key). Returns the
     * key or undefined. stdio supplies the (nullable) env key.
     */
    getApiKey?: (ctx: ToolCallContext) => string | undefined;
    /**
     * How this transport signs the caller in (default `api-key`, the stdio package).
     * The remote connector passes `oauth`, which swaps every "get an API key" hint
     * for the reconnect path.
     */
    auth?: McpAuth;
    /** Optional override of the missing-key message (defaults to the one for `auth`). */
    missingKeyMessage?: string;
}
/**
 * True when a thrown error is an expected, user-facing condition rather than a
 * bug worth an exception report.
 *
 * `UserError` alone can't answer this. It has two jobs in this file: messages we
 * AUTHORED for the caller ("No extension found …", the missing-key help), and a
 * last-resort wrapper the catch-alls put around anything that escaped (readError,
 * the docs/scan handlers). Treating every `UserError` as expected would let that
 * second group launder a BFF 500 or a plain TypeError into "expected" and drop it
 * from error tracking — so the two are told apart by `cause`:
 *
 *   - authored (no `cause`)  → expected by construction: we wrote the message.
 *   - wrapping (has `cause`) → only as expected as what it wraps; classifyError
 *     follows the chain, so a 429 gate stays expected and a 500 does not.
 *
 * Corollary for anything that wraps a caught error: build it with
 * userErrorFrom(), or it reads as authored and its origin never gets captured.
 *
 * `kind` defaults to classifying `err`; pass one to reuse a classification the
 * caller already made, so the reported `error_kind` and this decision can't
 * disagree.
 */
export declare function isExpectedError(err: unknown, kind?: string): boolean;
/**
 * Register the Extenshi tools once on a FastMCP server.
 * Only the tools whose capability is present in `deps.capabilities` are added.
 */
export declare function registerTools(server: FastMCP, deps: ToolDeps): void;
//# sourceMappingURL=tools.d.ts.map