import type { DeclaredToolAnnotations } from '#src/core/approvals/annotations.js'; /** * A built-in's authored hint set. **All four hints are stated**, never a subset: an omitted hint * silently takes the fail-closed default, so a partial row would read as a considered judgment * while actually being a gap. Requiring the whole set makes a forgotten hint a compile error. */ export type AuthoredToolAnnotations = Required; /** * §4.7 — what each of our own tools does, in the MCP `ToolAnnotations` vocabulary. Read **verbatim** * through the trusted provenance (§4.7.1: we author these), so every value here has to be *true* * rather than convenient. * * Keyed by the registered tool NAME, not by owner — gsloth's own `GthFileSystemToolkit` and * `gth_*` tools, plus the conventional `ls`/`glob`/`grep` a graph builder or MCP server may * register — because the conventions overlap on `read_file`/`write_file`/`edit_file` and one flat * table by name serves them all, exactly as `BUILT_IN_TOOL_ACCESS` does. * * The judgments that carry security weight: * * - **A local read is `readOnlyHint: true`, `openWorldHint: false`.** It mutates nothing and reaches * nothing off this machine. * - **`gth_web_fetch` is `readOnlyHint: true` AND `openWorldHint: true`** — §4.7.3. It mutates * nothing locally *and* reaches the network, and the two facts are independent. Reading it as a * local read is precisely what §4.7.3 forbids: §4.6 floors a shell fetch before any model call, * and "the same fetch reached through a tool instead of through `curl` must not be ungated", or * the preflight is a rule about spelling rather than about fetching. * - **Anything that writes, moves or deletes is not `readOnlyHint: true`**, and is * `destructiveHint: true` wherever the call can destroy something that was already there — * which `create_directory` cannot and `write_file`, `edit_file`, `move_file` and the two deletes * can. * - `idempotentHint` says whether repeating the identical call changes anything further. It has no * built-in consumer (§4.7.2) and is recorded so the vocabulary round-trips and a user's own * `hint` entry can reference it. MCP defines it, like `destructiveHint`, as meaningful only when * `readOnlyHint` is false, so on a read row it is **moot and stated `true`**: a tool that changes * nothing cannot change anything further on a second call, and a read row claiming otherwise * would contradict itself. */ export declare const BUILT_IN_TOOL_ANNOTATIONS: Readonly>; /** * §4.7.1 — the `builtIn` half of a `DeclaredToolAnnotationLookup`: what WE say one of our own tools * does. * * **Keyed off {@link BUILT_IN_TOOL_ANNOTATIONS}, our own authored table, and never off the bound * tool list.** The bound list is the merged set the graph is given, and it contains every MCP * server's tools; a `builtIn` lookup implemented over it would read a third-party declaration * through the trusted-verbatim path — the same "a gate any server can opt itself out of" hole as * routing an MCP call to a `tool` subject, arriving by a different door. §4.7.1 puts it more * strongly still: what decides trust is *who authored the annotation*, so even a tool running * in-process whose annotations we did not write does not take this path. * * Returns a fresh object, so the table cannot be mutated through the lookup; `undefined` for * anything not in it, which is fail-closed. */ export declare function builtInToolAnnotations(toolName: string): DeclaredToolAnnotations | undefined; /** The slice of a registered tool this module reads. Keeps it free of any langchain type. */ export interface AnnotationDeclaringTool { name?: string; /** `unknown` on purpose: it is whatever the adapter attached, and it is network-derived. */ metadata?: unknown; } /** * Read the four MCP hints a tool DECLARES, off the `metadata.annotations` the MCP adapter carries * over verbatim from the server's `tools/list` response. * * Own-property reads of the four known hint names only, so nothing else in a server's payload can * arrive as a hint. Values are passed through **unchanged**, including non-booleans: the one place * that decides a string `"true"` is not a `true` is the trust computation * (`createEffectiveToolAnnotationSource`), and duplicating that judgment here would give it two * homes that can disagree. * * `undefined` when the payload declares none of the four — a server that says nothing about itself * is the ordinary case and must land on fail-closed, not on an empty "it declared nothing" object. */ export declare function declaredToolAnnotationsFrom(metadata: unknown): DeclaredToolAnnotations | undefined; /** * §4.7.1 — collect what the connected MCP servers declared, keyed by the **registered tool name** * (`mcp____`). * * Keying by the registered name rather than by a parsed `(server, tool)` pair is what makes the * lookup immune to the `__`-in-a-server-key hazard: the reader rebuilds the same string with * `mcpToolRegisteredName` from a server key it has already resolved against the configured set, so * nothing is ever split apart and re-joined differently. * * Only MCP-namespaced names are collected. Our own tools' annotations come from * {@link BUILT_IN_TOOL_ANNOTATIONS} and must not be reachable through the per-hint-trusted MCP * path, and a custom tool's own metadata is not ours to believe either (§4.7.1). */ export declare function collectDeclaredMcpToolAnnotations(tools: readonly AnnotationDeclaringTool[]): ReadonlyMap; /** * §4.7.1/§4.7.5 — the `mcp` half of a `DeclaredToolAnnotationLookup`, over what * {@link collectDeclaredMcpToolAnnotations} recorded. * * **{@link UNRESOLVED_MCP_SERVER} is refused outright**, and that guard is structural rather than * belt-and-braces. A call whose server could not be resolved has no identity to look a declaration * up by; without the guard the lookup would fall back to *string arithmetic* on the sentinel and * find a declaration in whatever entry happened to reconstruct to the same registered name. * "Happened not to collide" is not a security property. Refusing the sentinel makes an * unidentifiable call fail-closed for a reason that does not depend on the shape of anyone's tool * names — and, because trust for an unnamed server would otherwise fall through to * `approvals.mcp.defaults`, this is also what stops a permissive `defaults` from reaching it. * * There is one lookup rather than one per call site so the guard cannot be present in the runner * and absent in the next consumer. */ export declare function mcpDeclaredAnnotationLookup(declared: ReadonlyMap | undefined): (server: string, toolName: string) => DeclaredToolAnnotations | undefined;