import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import type { McpResponseFormat, ToolOutcome } from './tools.js';
import type { EntityBounds } from '../code/entity/types.js';
import type { CreateEntitySessionOptions, OccEntityIntelligenceSession } from '../code/entity/session.js';
import type { TypeScriptCheckerMode } from '../code/types.js';
type OnProgress = NonNullable<CreateEntitySessionOptions['onProgress']>;
/**
 * `occ mcp` — a non-resident MCP stdio server over OCC's entity intelligence.
 *
 * Non-resident is the whole point: the process exists only while a client holds
 * the pipe. It starts no daemon, opens no socket, makes no network call, sends
 * no telemetry, and writes nothing into the analyzed repository — the lineage
 * cache defaults to the user cache directory, outside the working tree.
 *
 * stdout belongs to the JSON-RPC transport alone. Every diagnostic in this
 * module goes to stderr; a stray `console.log` anywhere on this path would
 * corrupt the protocol stream, so there are none.
 */
export interface McpServerOptions {
    /** The one repository this server will answer about. Paths outside it are refused. */
    repoRoot: string;
    cacheDir?: string;
    excludeDir?: string[];
    testsDir?: string[];
    formats?: boolean;
    tokenizer?: string;
    /** TypeScript semantic-resolution policy. Defaults to `preferred`. */
    typescriptChecker?: TypeScriptCheckerMode;
    /**
     * Index bounds, overriding `DEFAULT_ENTITY_BOUNDS`.
     *
     * A server against a repository larger than the defaults were sized for will
     * otherwise exhaust them and answer from a starved index, so this has to be
     * settable at start — it cannot be a per-request argument, because every
     * request shares one index.
     */
    bounds?: EntityBounds;
    /**
     * Re-check freshness before each tool call and reindex when the repository
     * moved. Default true.
     *
     * Off is for a server pointed at a revision that will not change under it,
     * where the check is pure cost.
     */
    refresh?: boolean;
    /** Reported in the MCP handshake; resolved from package.json when omitted. */
    version?: string;
    /** Closed by the caller in tests; production closes on transport shutdown. */
    onClose?: () => void;
}
export interface SessionHolder {
    /** The shared session, building it if this is the first call. */
    get(signal: AbortSignal, onProgress?: OnProgress): Promise<OccEntityIntelligenceSession>;
    dispose(): Promise<void>;
}
export interface SessionHolderOptions {
    /**
     * Produce the session. A parameter rather than something the holder reaches
     * for, because none of the holder's behaviour depends on what a session *is*
     * — and because the interesting parts of that behaviour only appear while a
     * build is still running, which a test can only arrange by controlling it.
     */
    build(signal: AbortSignal, onProgress: OnProgress): Promise<OccEntityIntelligenceSession>;
    /** Re-check freshness before handing the session out. Default true. */
    refresh?: boolean;
}
/**
 * The shared session, built once, kept current, and abandoned when nobody wants it.
 *
 * Building it walks history and indexes the repository, which can take seconds
 * on a large repo — far longer than a client will wait for `initialize`. So it
 * is built on the *first tool call* instead, and memoized as a promise so that
 * concurrent first calls share one build rather than racing two.
 *
 * A failed build is not cached: the usual cause is a transient one (a git
 * operation mid-flight, a repository being cloned), and a client that retries
 * should get a real second attempt rather than a replayed error.
 *
 * Two properties beyond memoization:
 *
 * **Cancellation is reference-counted.** A request's signal cannot simply be
 * passed to the build, because the build is shared: one client cancelling would
 * abandon work another client is still waiting on. Instead every waiter is
 * counted, and the build is aborted only when the last of them leaves — at
 * which point finishing it would be work for nobody.
 *
 * **Freshness is re-checked, not assumed.** A long-lived server outlives the
 * commits and edits made under it. Before each call the session is asked
 * whether it still describes the repository — one `rev-parse` plus a manifest
 * stat-diff — and reindexed when it does not. Concurrent callers share one
 * reconcile; a failed check is logged and the existing session is used, because
 * a possibly-stale answer beats no answer.
 */
export declare function createSessionHolder(options: SessionHolderOptions, lifetime: AbortSignal): SessionHolder;
/** Count cadence for deterministic cold-build progress; never time based. */
export declare const MCP_PROGRESS_COMMIT_INTERVAL = 10;
/** Fields deliberately omitted only by the concise MCP projection. */
export declare const MCP_CONCISE_OMITTED_FIELDS: readonly ["blobSha", "oldBlobSha", "newBlobSha", "blobOid", "oldBlobOid", "newBlobOid", "spanDigest", "span_digest", "exactSpanHash", "exact_span_hash", "spanHash", "spanSha256", "sourceSpanHash", "sourceSha256", "sha256", "provenance", "matchReason", "engine", "evidence", "hashes", "bodyHash", "exactBodyHash", "normalizedHash", "structuralHash", "signatureHash", "primaryHash", "commentsElidedHash"];
/**
 * Apply the one wire-level response projection after a tool has finished.
 * Detailed is the compatibility path and deliberately returns the same object.
 */
export declare function projectMcpResponse(data: Record<string, unknown>, responseFormat?: McpResponseFormat): Record<string, unknown>;
export declare function toCallResult(outcome: ToolOutcome, responseFormat?: McpResponseFormat): {
    content: {
        type: "text";
        text: string;
    }[];
    structuredContent: Record<string, unknown>;
    isError: boolean;
};
export declare function createOccMcpServer(options: McpServerOptions): Promise<{
    server: McpServer;
    dispose: () => Promise<void>;
}>;
/**
 * Serve on stdio until the client disconnects, then exit.
 *
 * There is no idle timeout, no reconnect, and no daemon: the transport closing
 * is the end of the process's reason to exist. But "closing" has to mean
 * *finished*, not merely *no more input*. `StdioServerTransport` never closes
 * itself, so a piped invocation — `echo '…' | occ mcp` — would otherwise hang
 * forever; and tearing down the moment stdin ends would drop the answer to a
 * request already in flight.
 *
 * So shutdown waits for stdin to end *and* every outstanding request to be
 * accounted for. A request leaves the outstanding set when its response has
 * been flushed to stdout, or when the client cancels it — the SDK deliberately
 * sends no response for a cancelled request, so cancellation has to retire the
 * id too or the drain would never complete.
 */
export declare function runMcpServer(options: McpServerOptions): Promise<void>;
export {};
