/**
 * Local MCP (Model Context Protocol) Client Manager for stdio transport
 *
 * This module manages local MCP servers that run as child processes using stdio transport.
 * Local MCP servers are defined in mcp.json configuration file in the working directory.
 *
 * Tool naming follows the same convention as remote MCPs:
 * - Tools are prefixed with server name: `mcp__servername__toolname`
 * - This prevents conflicts with built-in tools and other MCP tools
 */
import type { ContentMessageItemImage, ContentMessageItemText, MCPClientStatus, MCPServerConfig } from "#ai-utils";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import type { DevToolsSys } from "../core";
import type { AuthToolCandidate } from "#vcp-common/mcp";
/**
 * Default connect timeout in milliseconds for remote MCP servers.
 * Override per-server via `connectTimeoutMs` in mcp.json.
 */
export declare const DEFAULT_MCP_CONNECT_TIMEOUT_MS = 10000;
export interface MCPServerStdioDefinition {
    name: string;
    command: string;
    args?: string[];
    env?: Record<string, string>;
    envFile?: string;
    retries?: number;
    /** When true, the server is registered but not connected to. */
    disabled?: boolean;
    /**
     * Where this server was discovered. Drives precedence on name collision:
     * built-in/fusion (unset) > `project` > `user` > `plugin`. Set by the
     * loader, not by the config file.
     */
    scope?: "project" | "user" | "plugin";
    /**
     * Name of the plugin that contributed this server, if any. Set by the
     * plugin loader (Phase 2); always `undefined` for project-level and
     * user-level standalone configs (Phase 1).
     */
    pluginName?: string;
    /**
     * Absolute path of the `mcp.json` / plugin manifest this entry was
     * loaded from. `undefined` for built-in/fusion configs. Surfaced to the
     * `/mcp` slash command so the UI can render "Config location: ...".
     */
    configFilePath?: string;
}
/**
 * Optional OAuth configuration for an `mcp.json` remote-MCP entry.
 *
 * The actual OAuth flow lives in {@link LocalOAuthProvider}
 * (`mcp-oauth-local.ts`); this type is the JSON surface — just the bits
 * the user can set in `mcp.json`. All fields are optional: a remote MCP
 * with no `oauth` block at all uses anonymous / static-bearer auth as it
 * does today; an empty `{}` enables the OAuth flow with all-defaults
 * (DCR, ephemeral loopback port).
 */
export interface MCPServerOAuthConfig {
    /**
     * Pre-registered DCR client ID. Use only for IdPs that don't support
     * dynamic client registration — most DCR-capable IdPs (Linear, GitHub,
     * Notion) work with the empty default.
     */
    clientId?: string;
    /**
     * Pre-registered DCR client secret. Optional even when `clientId` is set
     * (public clients have no secret).
     */
    clientSecret?: string;
    /**
     * Fixed loopback redirect port, for IdPs that require a pre-registered
     * `redirect_uri`. Default: kernel-assigned ephemeral port.
     */
    redirectPort?: number;
    /**
     * Phase 4 setting — type-only declaration here; the connect logic
     * doesn't act on it yet. When true, locally-obtained credentials get
     * pushed to the server registry on next sync (enables server-driven
     * codegen to use the same connection).
     */
    syncToServer?: boolean;
}
export interface MCPServerRemoteDefinition {
    name: string;
    type: "http" | "sse";
    url: string;
    headers?: Record<string, string>;
    sessionId?: string;
    envFile?: string;
    /**
     * Maximum total time (ms) to wait for the initial connect handshake.
     * Defaults to {@link DEFAULT_MCP_CONNECT_TIMEOUT_MS}.
     */
    connectTimeoutMs?: number;
    retries?: number;
    /**
     * Optional OAuth configuration. See {@link MCPServerOAuthConfig}. The
     * Phase 1c provider in `mcp-oauth-local.ts` consumes these fields;
     * Phase 2b will plumb the provider into `connectRemoteMCP` so a 401
     * automatically triggers the OAuth flow.
     */
    oauth?: MCPServerOAuthConfig;
    /** When true, the server is registered but not connected to. */
    disabled?: boolean;
    /**
     * Where this server was discovered. Drives precedence on name collision:
     * built-in/fusion (unset) > `project` > `user` > `plugin`. Set by the
     * loader, not by the config file.
     */
    scope?: "project" | "user" | "plugin";
    /**
     * Name of the plugin that contributed this server, if any. Set by the
     * plugin loader (Phase 2); always `undefined` for project-level and
     * user-level standalone configs (Phase 1).
     */
    pluginName?: string;
    /** See {@link MCPServerStdioDefinition.configFilePath}. */
    configFilePath?: string;
}
/**
 * Build privacy-safe metadata about a sessionId for logging.
 * Never logs the sessionId itself or any substring of it.
 */
export declare function describeSessionIdForLog(sessionId: string): {
    length: number;
    looksLikeJWT: boolean;
    hasEnvPlaceholder: boolean;
};
/**
 * Fallback heuristic for detecting 401/Unauthorized errors when the SDK's
 * `UnauthorizedError` instance has been wrapped or has lost its prototype
 * across module boundaries (e.g. dual ESM/CJS, JSON-serialized re-throws).
 * Prefer `instanceof UnauthorizedError`; only use this as a last resort.
 */
export declare function isLikelyUnauthorizedError(error: unknown): boolean;
export type MCPServerDefinition = MCPServerStdioDefinition | MCPServerRemoteDefinition;
export interface MCPConfig {
    mcpServers: Record<string, Omit<MCPServerStdioDefinition, "name"> | Omit<MCPServerRemoteDefinition, "name">>;
}
export interface LocalMCPClient {
    client: Client | undefined;
    transport: StdioClientTransport | SSEClientTransport | StreamableHTTPClientTransport | undefined;
    status: MCPClientStatus;
    serverName: string;
    normalizedServerName: string;
    serverType: "stdio" | "http" | "sse";
    command?: string;
    url?: string;
    resources?: {
        uri: string;
        name?: string;
        description?: string;
        mimeType?: string;
    }[];
    /**
     * True when the connection failed with a 401/Unauthorized — the LLM-driven
     * `__authenticate` flow (Phase 2b) routes through `getAuthRequiredServers()`
     * to surface a synthetic auth tool. Set in the connect catch path; not
     * persisted across manager rebuilds.
     */
    authRequired?: boolean;
    /**
     * OAuth config from `mcp.json` (Phase 1c type). Carried on the client
     * record so the LLM-driven auth flow (Phase 2b) can construct a
     * `LocalOAuthProvider` with pinned client credentials when running.
     */
    oauth?: MCPServerOAuthConfig;
    /**
     * Original remote server definition. Stored on auth-required clients so
     * `reconnectServer` can rebuild the transport after OAuth completes
     * without re-loading `mcp.json`. Only populated for http/sse servers
     * that failed the initial connect — stdio servers can't reconnect via
     * this path.
     */
    serverDef?: MCPServerRemoteDefinition;
}
export interface LocalMCPClientManager {
    clients: LocalMCPClient[];
    listTools: () => {
        name: string;
        description?: string;
        inputSchema?: any;
        serverName: string;
    }[];
    callTool: (name: string, args?: any, signal?: AbortSignal) => Promise<{
        content: (ContentMessageItemText | ContentMessageItemImage)[];
        isError?: boolean;
    }>;
    getResources: (serverName?: string) => Array<{
        uri: string;
        name?: string;
        description?: string;
        mimeType?: string;
        serverName: string;
        text?: string;
    }>;
    getStatus: () => Record<string, MCPClientStatus>;
    /**
     * Returns the list of CLI-managed MCPs that need OAuth before they can
     * be used. Used by `code-tools.ts` to look up the matching server config
     * when the LLM calls a `mcp__<n>__authenticate` synthetic tool, and by
     * `mcp list` for inventory display. The synthetic auth tools themselves
     * are surfaced through `listTools()` (alongside real MCP tools) — there
     * is no separate wire field for auth-required servers; they round-trip
     * back through the existing `localMCPTools` execution path.
     *
     * Today this is just the set of remote MCPs whose connection failed
     * with a 401. Discovery is deferred until the LLM actually invokes the
     * auth tool (spec §B "Mechanics").
     */
    getAuthRequiredServers: () => AuthToolCandidate[];
    /**
     * Retry connecting a single previously-auth-required server after the
     * LLM-driven OAuth flow completes. Re-runs `connectRemoteMCP` (which
     * picks up the freshly-stored bearer token) and, on success, fetches
     * tools/resources for the server and updates the manager's caches so
     * the next `listTools()` call returns real tools instead of the
     * synthetic `__authenticate` tool.
     *
     * Returns a summary of the attempt for the caller (typically
     * `code-tools.ts`) to surface as a completion event.
     */
    reconnectServer: (serverName: string, signal?: AbortSignal) => Promise<{
        success: boolean;
        toolsAdded: number;
        message?: string;
    }>;
    cleanup: () => Promise<void>;
}
/**
 * Map a server definition to the `source` discriminator surfaced on
 * `MCPClientStatus`. Built-in / fusion-config servers have no `scope`
 * (the loader doesn't set one), plugin-contributed servers always have
 * `scope: "plugin"`, and everything else is a CLI-managed local server.
 */
export declare function getLocalMCPSource(server: MCPServerDefinition): "local" | "plugin" | "builtin";
/**
 * Create a local MCP client manager from server definitions
 *
 * @param servers - MCP server definitions parsed from `mcp.json`.
 * @param sys - DevTools system bindings.
 * @param workingDirectory - The working directory used to resolve
 *   relative paths (env files, stdio cwd).
 * @param signal - Optional abort signal for the connection batch.
 */
export declare function createLocalMCPClientManager(servers: MCPServerDefinition[], sys: DevToolsSys, workingDirectory: string, signal?: AbortSignal): Promise<LocalMCPClientManager>;
/**
 * Apply environment variable substitution to MCP server configuration
 * This is separated from loadMCPConfig to allow easy unit testing
 */
export declare function applyEnvSubstitution(serverConfig: Omit<MCPServerStdioDefinition, "name">, name: string, baseEnv: Record<string, string | undefined>, envFileVars: Record<string, string>): MCPServerStdioDefinition;
/**
 * Apply environment variable substitution to remote MCP server configuration
 * This is separated from loadMCPConfig to allow easy unit testing
 */
export declare function applyEnvSubstitutionRemote(serverConfig: Omit<MCPServerRemoteDefinition, "name">, name: string, baseEnv: Record<string, string | undefined>, envFileVars: Record<string, string>): MCPServerRemoteDefinition;
/**
 * Discover and load MCP configuration from working directory and fusionConfig
 * Servers from fusionConfig will be merged with servers from mcp.json
 * If a server with the same name exists in both, fusionConfig takes precedence
 * Supports both stdio (command-based) and remote (http/sse) server definitions
 *
 * Precedence (lowest → highest):
 *   1. ~/.builder/mcp.json (user-level)
 *   2. <workingDirectory>/mcp.json (project-level)
 *   3. serverConfigs from fusionConfig
 *
 * @param homeDir Override for the user's home directory (for testing).
 */
export declare function loadMCPConfig(sys: DevToolsSys, workingDirectory: string, serverConfigs: MCPServerConfig, autoImportLocalMCPs: boolean, signal?: AbortSignal, homeDir?: string): Promise<MCPServerDefinition[]>;
