import { AnyTool } from '@tanstack/ai';
import { AnyCommand, PluginPrompt, PluginSessionApi, PluginSetupContext, RunContext, TurnInfo } from '@tanstack/ai-harness';
import { OnElicit } from './client.js';
import { McpConnectorOptions } from './connector.js';
import { MCPClientOptions } from './types.js';
import { HttpTransportConfig, StdioTransportConfig } from './transport.js';
/** One server of {@link mcp}, keyed by its name. */
export type McpServerConfig = (StdioTransportConfig | (HttpTransportConfig & {
    /**
     * Sign in with OAuth, like `mcpConnector`. This adds `/connect <name>`
     * and `/disconnect <name>`. The tools of the server come after the
     * sign-in, and the tools that are not read-only ask for approval.
     * `headers` and `authProvider` do not apply then.
     */
    oauth?: boolean;
})) & {
    /**
     * The time in milliseconds that this server can take to connect, to send
     * its tool list, or to answer a tool call. Default: the MCP SDK default,
     * 60,000.
     */
    timeoutMs?: number;
    /**
     * Give the tools of this server to code mode: the `codeMode()` plugin of
     * `@tanstack/ai-code-mode` takes them out of the tool list and lets the
     * model call them from `execute_typescript`. Without that plugin, nothing
     * changes.
     */
    codeMode?: boolean;
    /**
     * Ask the user for approval before a tool of this server runs. `true` asks
     * for every tool. A function gets the MCP tool definition and returns
     * `true` to ask. Default: no approval, and for `oauth: true` every tool
     * that the server does not mark `readOnlyHint`.
     */
    needsApproval?: boolean | McpConnectorOptions['needsApproval'];
    /**
     * The name the model sees for each tool of this server. It gets the MCP
     * tool definition. Default: `<server>_<tool>`.
     */
    toolName?: MCPClientOptions['toolName'];
};
/**
 * The status of one server. UIs read the list from
 * `snapshot().plugins['tanstack/mcp'].servers`. A server is `reconnecting`
 * after its connection closed, until a new connection is open.
 */
export type McpServerStatus = {
    name: string;
    /** How many tools the server gives the model. */
    toolCount: number;
} & ({
    status: 'connecting' | 'connected' | 'reconnecting';
} | {
    status: 'failed';
    error: string;
});
/**
 * A harness plugin that connects MCP servers and gives the model their
 * tools, named `<server>_<tool>`.
 *
 * - Each server connects on its own. A server that fails does not stop the
 *   others: it gets the status `failed` with the error.
 * - The servers connect in the background. The first turn waits for them.
 * - `/mcp` lists the servers and their status. UIs read the same list from
 *   the plugin state.
 * - A `stdio` server runs as one child process (Node only) for every
 *   session of the harness on the host that uses this `mcp()` object. It
 *   stops when the last of those sessions closes, or at `host.close()`.
 *   Create the plugin once, outside the `plugins` factory: a factory that
 *   calls `mcp()` for each session starts a process for each session. The
 *   other servers connect for each session.
 * - An `http` server with `oauth: true` signs in through `/connect <name>`.
 * - When the connection of a server closes, the server is `reconnecting`.
 *   The plugin connects again after 0.5 s, 1 s, 2 s, and so on (at most
 *   30 s), until it is connected or its session or host closes. The wait
 *   starts again at 0.5 s only after a connection stayed open for 30 s, so a
 *   server that crashes right after it connects waits longer each time. The
 *   next turn gets the tools of the new connection. A server that fails its
 *   first connect is `failed`, and connects again the same way.
 * - A spec 2025 server that asks for input (`elicitation/create`) does not
 *   name the tool call that asks. The user of the call is asked only when
 *   all the calls of that server that run now belong to one run. Else the
 *   request is declined, so a question never goes to another user.
 * - When a server sends `tools/list_changed`, the next turn lists its tools
 *   again.
 * - When two servers give a tool with the same name, the model gets the
 *   tool of the server that comes first in `servers`. The plugin warns once,
 *   to the host logger (`createHarnessHost({ logger })`). With
 *   `onDuplicateTool: 'error'`, the later server fails instead.
 *
 * @param options.servers - The servers, keyed by name. The name is the tool
 *   prefix.
 * @param options.onDuplicateTool - What happens when a server gives a tool
 *   name that a server earlier in `servers` gives too. `'warn'` (default):
 *   the model does not get that one tool, and the plugin warns once.
 *   `'error'`: the later server gets the status `failed` with an error that
 *   names both servers and the tool, and the model gets none of its tools.
 *   The other servers keep working.
 *
 * @example
 * ```ts
 * import { mcp } from '@tanstack/ai-mcp/harness'
 *
 * // Once, so the sessions share the stdio process.
 * const tools = mcp({
 *   servers: {
 *     files: { type: 'stdio', command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem', '.'] },
 *     docs: { type: 'http', url: 'https://mcp.example.com/mcp', timeoutMs: 10_000 },
 *     notion: { type: 'http', url: 'https://mcp.notion.com/mcp', oauth: true },
 *   },
 * })
 *
 * plugins: () => [tools]
 * ```
 */
export declare function mcp(options: {
    servers: Readonly<Record<string, McpServerConfig>>;
    onDuplicateTool?: 'warn' | 'error';
}): import('@tanstack/ai-harness').HarnessPlugin<NoInfer<{
    readonly name: "tanstack/mcp";
    readonly lifetime: "host";
    readonly setup: (host: import('@tanstack/ai-harness').HostPluginContext) => {
        session: (ctx: PluginSetupContext<Record<string, unknown>>) => Promise<{
            commands: Record<string, AnyCommand>;
            prompts: PluginPrompt[];
            discoverTools: (turn: TurnInfo) => Promise<AnyTool[]>;
        }>;
    };
}>, Record<string, unknown>>;
/** A tool call that runs now: its session, and the run that calls. */
export interface Caller {
    session: PluginSessionApi;
    run?: RunContext;
}
/**
 * Puts each call of `tools` in `calling` while it runs, with its session
 * and its run, so a spec 2025 elicitation can find the run that calls.
 */
export declare function trackCaller(tools: ReadonlyArray<AnyTool>, session: PluginSessionApi, calling: Array<Caller>): AnyTool[];
/**
 * The harness asks the user for form and URL elicitations, so its clients
 * declare both. `chat()` clients keep the default: form only.
 */
export declare const harnessClientOptions: {
    capabilities: {
        elicitation: {
            form: {};
            url: {};
        };
    };
};
/**
 * Ask the session user when an MCP tool asks for input (a form or a URL
 * elicitation). The tool call waits for the answer, then sends it to the
 * server. On spec 2026, a tool call can ask up to 5 times. The answer is the
 * form content, or an MCP result such as `{ action: 'decline' }` or
 * `{ action: 'cancel' }`.
 */
export declare function askForInput(tools: ReadonlyArray<AnyTool>, session: PluginSessionApi): AnyTool[];
/**
 * Answers the `elicitation/create` requests of a spec 2025 server. The
 * request does not name the tool call that it belongs to. So it asks only
 * when the calls of `calling` all belong to one run of one session, as a
 * live wait of that run. With no call, or with calls of more than one run
 * (two users or tenants can share a server), it declines: a question must
 * never go to the user of another run.
 */
export declare function elicitFor(calling: ReadonlyArray<Caller>): OnElicit;
