import type { Client } from "@connectrpc/connect"; import type { grackle } from "@grackle-ai/common"; import type { ZodType } from "zod"; import type { AuthContext } from "@grackle-ai/auth"; /** Per-service ConnectRPC clients passed to every MCP tool handler. */ export interface GrackleClients { /** Core RPCs: environments, sessions, workspaces, tokens, settings, etc. */ core: Client; /** Orchestration RPCs: tasks, personas, escalations. */ orchestration: Client; /** Scheduling RPCs: schedules. */ scheduling: Client; /** Knowledge graph RPCs. */ knowledge: Client; } /** Content item returned by an MCP tool handler. */ export interface ToolContent { type: "text"; text: string; } /** Result returned by a tool handler to the MCP protocol layer. */ export interface ToolResult { content: ToolContent[]; isError?: boolean; /** * Standard MCP result metadata. Used internally by the widget tools to hand a * render descriptor to the broker capture (see {@link WIDGET_RENDER_META_KEY}). */ _meta?: { [key: string]: unknown; }; } /** Optional hints about a tool's behavior for the MCP client. */ export interface ToolAnnotations { title?: string; readOnlyHint?: boolean; destructiveHint?: boolean; idempotentHint?: boolean; openWorldHint?: boolean; } /** * Declarative authorization target for central scope enforcement * (GHSA-f9ff-5x35-7gfw). When a tool sets `scope`, the dispatcher verifies — for * every scoped, non-root caller — that the caller is an ancestor of the targeted * task (or of the task owning the targeted session) *before* invoking the * handler, denying the call otherwise. Because the backend gRPC handlers perform * no caller-based authorization, this is the sole authorization boundary for * agent callers. * * The "fail-closed" property is per-target: a tool that *declares* a target is * denied for non-ancestors, and a malformed descriptor (see below) throws rather * than silently passing. It is NOT automatic — a tool with **no** `scope` is not * scope-checked at all, so any new task/session-targeting tool MUST add a * descriptor to be protected. * * Exactly one of the arg fields must be set; both-or-neither is a programming * error that `enforceToolScope` rejects at call time. */ export interface ToolScope { /** Name of the input arg holding the target task ID; caller must be its ancestor. */ taskIdArg?: string; /** Name of the input arg holding a session ID; resolved to its task, then ancestry-checked. */ sessionIdArg?: string; } /** Declarative definition of an MCP tool backed by a ConnectRPC call. */ export interface ToolDefinition { /** Unique tool name (snake_case by convention). */ name: string; /** Tool group for organizational purposes (e.g. "env", "session"). */ group: string; /** Human-readable description shown to the AI client (20+ chars). */ description: string; /** Zod schema for input validation (converted to JSON Schema for ListTools). */ inputSchema: ZodType; /** Name of the ConnectRPC method this tool calls (e.g. "listEnvironments"). */ rpcMethod: string; /** Whether this tool modifies state. */ mutating: boolean; /** Optional behavioral hints for the client. */ annotations?: ToolAnnotations; /** * Optional MCP Apps UI resource (`ui://…`) this tool renders. When set, the * tool carries `_meta.ui.resourceUri` in `tools/list` and is only listed to * hosts that advertise the `io.modelcontextprotocol/ui` extension. */ uiResourceUri?: string; /** * Optional declarative scope target for central fail-closed authorization of * scoped (agent) callers. See {@link ToolScope}. */ scope?: ToolScope; /** Execute the tool, forwarding to the ConnectRPC backend. */ handler: (args: Record, clients: GrackleClients, authContext?: AuthContext) => Promise; } /** Predicate function for filtering tools (used by persona-scoped filtering). */ export type ToolPredicate = (tool: ToolDefinition) => boolean; /** Registry of MCP tool definitions with lookup and filtering support. */ export declare class ToolRegistry { private readonly tools; /** * Register a tool definition. Throws if a tool with the same name already * exists, or if the name uses the reserved `render_` prefix — that namespace * belongs to the dynamic per-workspace component render tools (#1272), so no * static or plugin-contributed tool may claim it (otherwise it would shadow a * promoted component's tool). */ register(tool: ToolDefinition): void; /** Register an array of tool definitions. */ registerAll(tools: ToolDefinition[]): void; /** Return all registered tools, optionally filtered by a predicate. */ list(predicate?: ToolPredicate): ToolDefinition[]; /** Look up a tool by name. Returns undefined if not found. */ get(name: string): ToolDefinition | undefined; } //# sourceMappingURL=tool-registry.d.ts.map