import { type ActionExecutionOutcome, type ActionRunContext } from "../action.js"; import type { ActionEntry } from "../agent/production-agent.js"; import { type McpActionResult } from "../mcp-client/app-result.js"; import { type McpApprovalCall } from "./approval-store.js"; import type { MCPCallerIdentity } from "./build-server.js"; /** * The authorization, approval, execution and result-sanitizing steps of an * MCP action call, with no MCP request or response object. The direct * `tools/call` adapter in `build-server.ts` turns an outcome into a wire * result; anything else that runs actions for an MCP caller goes through * `executeMcpActionCall` so it gets the same scope, approval and redaction. */ export declare const MCP_ACTION_APPROVAL_TTL_SECONDS: number; export interface McpActionApprovalState { version: 1; nonce: string; actionName: string; argumentsHash: string; expiresAt: number; /** The prompt asked the person to open the review page; absent for a form. */ mode?: "url"; } /** * An earlier run of this exact call that a person should know about before * approving it again: it `completed`, or it started and whether it finished * is `unknown`. */ export interface McpApprovalPriorRun { at: number; status: "completed" | "unknown"; } /** A grant waiting on its review page, as the direct adapter links to it. */ export interface McpApprovalReview { nonce: string; actionName: string; expiresAt: number; priorRun: McpApprovalPriorRun | undefined; /** The arguments as the grant stored them, for the prompt's summary. */ argumentsJson: string | null; } export declare function canonicalJson(value: unknown): string; export declare function sha256Base64Url(value: string): Promise; export declare function isActionVisibleForOAuthScope(entry: ActionEntry, scopes: string[] | undefined, readOnlyOverride?: boolean): boolean; /** What one call produced, read for embed credentials. */ export interface EmbedCredentialGuard { /** Something the call produced names the start route or an embed field. */ embedContext: boolean; /** Credential values to hide wherever a kept string echoes them. */ credentials: readonly string[]; /** Every string the call produced, keys included, read to its last layer. */ readable: boolean; } /** * `redactEmbedCredentials` could not read everything a call produced, so * none of its result may be shown. The caller withholds the result whole, as * it does one that is not JSON data. */ export declare class UnreadableResultError extends Error { constructor(); } /** * Reads everything one call produced, `_meta` included, as JSON. The call is * in embed context when any of it names the start route or an embed field, * and its credentials are the ticket of every start URL in it and, in embed * context, the value of every `ticket` field. `redactEmbedCredentials` hides * them in a field that holds no start URL itself, such as a proxy's text * beside its `_meta`. Both are read from every escaped layer of a string, a * URL's ticket in the base64url alphabet a minted ticket uses, so the * credentials are the ones the call minted or was handed, in any spelling * `StringReading` decodes, not a transformed copy of one. */ export declare function embedCredentialGuard(...sources: unknown[]): EmbedCredentialGuard; /** * `embedCredentialGuard` for a call still producing values: each call adds * them, read once, and returns the guard over everything added so far. A * value that is not JSON data throws and adds nothing. */ export declare function embedCredentialSources(): (...sources: unknown[]) => EmbedCredentialGuard; /** * Removes embed session credentials from a value before any of it reaches a * model, for the direct and shared MCP call paths alike. An embed start URL * carries a single-use ticket that signs its holder in as the user, so it may * travel only in `_meta`, where the embed runtime reads it. The value is read * as JSON first, so a `toJSON` method or getter cannot add anything after the * walk. A string that mentions the start route (`StringReading`) becomes * `[hidden embed URL]`, wherever it is, so a reader sees that a value was * hidden rather than finding its key missing. An object drops a key that * itself mentions the route; with `dropHiddenFields`, for a link whose hidden * URL would not open, it drops a key holding such a string too. In embed * context, which the value or the call's `guard` sets, every `ticket` field * is dropped as well. A string that echoes a credential in any of its escaped * layers becomes `[hidden embed ticket]`, and a key that does is dropped. * Every other key is kept, `__proto__` included. * * It hides accidental echoes: a credential the call holds, repeated raw or * under any escaping `StringReading` decodes. A result it cannot fully read, * because it is not JSON data or a string in it or in the call's `guard` * exceeds the decoding or reference-inspection work limit, is withheld whole: * this throws. The * direct adapter then reports a write, told by its effect and not by whether * it notifies, as a success, as it does any failure after the action ran, * unless its upstream reported failure or its status could not be read; a * read fails. An action that transforms a credential on purpose, by encoding * it as base64, splitting or reversing it, is out of scope: action code is * trusted, and could send the credential anywhere anyway. */ export declare function redactEmbedCredentials(value: string, guard?: EmbedCredentialGuard): string; export declare function redactEmbedCredentials(value: unknown, guard?: EmbedCredentialGuard, options?: { dropHiddenFields?: boolean; }): unknown; /** The contract error code a caller should see; generic failures have none. */ export declare function mcpActionErrorCode(error: unknown): string | undefined; /** * Whether an already classified MCP proxy result says the upstream tool * failed. A proxy's fields can be getters, so this reads only `raw` and never * classifies the value again. */ export declare function isReportedProxyError(mcpResult: McpActionResult | null): boolean; /** What a proxied tool's upstream reported, or that it could not be read. */ export type UpstreamStatus = "succeeded" | "failed" | "unknown"; /** * What a call returned, classified once, before anything can fail, with * `isMcpActionResult`'s reads. A proxied tool's result gives its `text`, its * `raw` as `result`, what its upstream reported and its other own enumerable * `fields`, which only the guard reads, in that order, each read once here; * any other value is `result` itself. The response normalizes those parts, * and the change marker's resource hint reads `result`. Copying `raw` as JSON * reads an enumerable `raw.isError` again, but only this read classifies. A * status read that throws leaves the status unknown; any other read that * throws leaves the value `unread`, kept for the hint alone. */ export declare function returnedResult(value: unknown): { upstream: UpstreamStatus; result: unknown; text?: string; fields?: Record; unread?: true; }; /** What one MCP caller may do, fixed for the lifetime of its server. */ export interface McpActionCallContext { appId: string | undefined; identity: MCPCallerIdentity | undefined; /** The app this server belongs to, which its approval grants are bound to. */ approvalAppId: string; /** Binds approval grants to this caller; unset when no action needs approval. */ approvalCallerKey: string | undefined; /** Whether this server can mint signed approval state for an elicitation. */ canRequestApproval: boolean; } /** How a resumed call presents the approval it is answering. */ export interface McpActionApprovalResponse { /** The verified state a resumed call carries; undefined on a first call. */ state(): McpActionApprovalState | undefined; /** The caller's answer to the approval elicitation. */ decision(): unknown; /** The client accepted the elicitation, whatever it sent with it. */ accepted(): boolean; } export interface McpActionCall { name: string; args: unknown; /** The actions this caller can reach in its catalog. */ callable: Record; /** Adapter-specific facts to attach to the action's audit context. */ actionRunContext?: Partial; approval?: McpActionApprovalResponse; } type McpActionCallRefusal = { status: "unknown-tool"; message: string; } | { status: "forbidden-scope"; message: string; } | { status: "approval-denied"; message: string; } /** A new grant: ask for approval, or link to its review page. */ | { status: "approval-required"; approval: Approval; review: Review; } /** * The client answered without approving, so the review page decides: * `declined` (or cancelled) ended its grant and `review` is a new one; * `opened` is a URL prompt the person accepted, still waiting there. */ | { status: "approval-pending"; review: Review; reason: "declined" | "opened"; }; /** * A pending approval as an untrusted caller may see it. The signed state and * its grant nonce stay with the direct adapter, which mints the elicitation. */ export interface McpActionApprovalRequest { actionName: string; expiresAt: number; } /** * Where a call failed. `authorization` means the caller's service-principal * grant refused the call before the action was invoked, and `approval` failed * before the action ran; in both, nothing ran. `action` means the action was * invoked and threw, so it may have done part of its work. `result` means the * action finished but its value could not be made into JSON or checked for * embed credentials, so its work is done even though the caller gets no * value. No stage says whether partial work was committed. */ export type McpActionCallFailureStage = "authorization" | "approval" | "action" | "result"; /** * Plain, frozen JSON data in which every string has had embed session * credentials removed. An adapter that hands results to sandboxed or * model-authored code still serializes its own allowlisted DTO from this. */ export type McpActionCallOutcome = Exclude, { status: "approval-required" | "approval-pending"; }> | { status: "approval-required" | "approval-pending"; approval: McpActionApprovalRequest; } | { status: "completed"; /** The action's value as JSON; an MCP proxy result contributes `raw`. */ value: unknown; /** The action reported failure inside its own value. */ reportedError: boolean; } | { status: "failed"; stage: McpActionCallFailureStage; message: string; errorCode: string | undefined; }; /** * Runs inside the caller's request context (`runWithRequestContext`), which * supplies the user and org that approval predicates and the action see. * Never throws: the result is normalized and redacted before this returns. */ export declare function executeMcpActionCall(context: McpActionCallContext, call: McpActionCall): Promise; /** * The same steps after the service-principal check, which the direct adapter * makes itself before its own built-in tools. Returns the action's own value, * its thrown error and the signed approval state with its grant nonce. These * can hold credentials, so * only the direct `tools/call` adapter calls this (`.oxlintrc.json` and * `guard:mcp-unredacted-call-boundary` reject other importers, dynamic * imports and requires): it moves embed tickets into `_meta`, mints the approval * elicitation, and redacts every model-visible field itself. */ export declare function runMcpActionCallUnredacted(context: McpActionCallContext, call: McpActionCall): Promise, Readonly> | { status: "ran"; entry: ActionEntry; result: unknown; /** What the run did, from its call record. */ execution: ActionExecutionOutcome; } | { status: "threw"; stage: "approval"; error: unknown; } | { status: "threw"; stage: "action"; error: unknown; entry: ActionEntry; execution: ActionExecutionOutcome; }>; /** * `value` as JSON data. With `markCycles`, as the direct path has always * allowed, an object nested inside itself becomes a marker; without it, the * call fails. Anything else JSON cannot hold, such as a BigInt, fails either * way. */ export declare function toJsonData(value: unknown, markCycles?: boolean): unknown; /** * The same call, approved and run within the approval window, which a person * must know about before approving it again: a client that lost the response * retries, and a second approval would run it twice. A call its action * declares `retrySafe` (an action that answers a repeated idempotency key * with its first result declares it for those inputs) can't be duplicated, * so there is nothing to warn about. The prompt, the link and the review * page all read this, so they say the same thing. */ export declare function findPriorApprovedRun(call: McpApprovalCall, retrySafe: boolean): Promise; export {};