Hosted tool hooks
Transform one indexed tool’s public contract, arguments, result, approval requirement, and logs with hosted TypeScript.
Author one explicit adapter
A hook belongs to one indexed tool. Rayrun stores a mutable draft, immutable attributed draft versions, and separate immutable deployment revisions under rayrun.hooks/v1.
One versioned tool adapter
export default defineToolAdapter<{ query: string }, { items: unknown[] }>({
apiVersion: "rayrun.hooks/v1",
publicContract: {
description: "Search public release notes",
inputSchema: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
additionalProperties: false,
},
},
before({ arguments: args, context }) {
context.log.info("Preparing search", { query: args.query });
const prefix = typeof context.config.prefix === "string" ? context.config.prefix : "";
return {
action: "continue",
arguments: { q: prefix + args.query },
};
},
after({ result }) {
return { action: "return", result: { items: result.items } };
},
});The editor generates argument and result types from the current upstream schemas. The compiler rejects imports, unsupported exports, and invalid public contracts.
publicContract can replace the description, input schema, and output schema shown in Direct and Code discovery. Rayrun extracts it at compile time, so a hook cannot change the definition per request. Hook-controlled pattern and patternProperties are rejected because regular-expression execution is not time-bounded.
What hooks can do
- before can continue with transformed upstream arguments, reject with a stable reason and message, or require one-call approval with transformed arguments and a readable reason.
- after can return a transformed result or fail with a stable reason and message.
- Both stages can read non-secret JSON config and immutable call metadata, and emit debug, info, warning, and error log entries.
- Rayrun evaluates access before before runs. A hook may rewrite arguments within the selected tool, reject the call, or add approval, but it cannot weaken Rayrun’s access decision or bypass a block, upstream authorization requirement, or changed-definition review.
Context and helpers
context.calledAtis the fixed ISO timestamp for the call;context.requestIdis its correlation ID.context.workspaceUid,context.connectionId,context.toolUid, andcontext.toolNameidentify the immutable target.- context.config is the immutable, non-secret JSON object deployed with the revision.
- context.encoding.base64Encode(value) and base64Decode(value) convert UTF-8 strings without adding an external capability.
- context.log.debug, info, warn, and error accept a message plus optional JSON data. Log count, message length, payload capture, and retention are bounded.
What hooks cannot do
Rayrun runs compiled JavaScript in a capability-free QuickJS isolate with bounded time, memory, output size, and log count.
- No imports, npm packages, network, filesystem, environment variables, process APIs, timers, raw credentials, or arbitrary tool calls.
- No durable mutable state, cache control, retries, background work, or access to a next middleware function.
- No weakening a workspace, access-profile, client, risk, definition, or approval decision.
- No live upstream call from the test console; after-stage tests use the mock result you provide.
- Source: 100 KiB. Compiled JavaScript: 150 KiB. Config: 16 KiB. Active and shadow code: 16 MiB per workspace. Draft history: 64 MiB per workspace.
- Draft saves: 120 per workspace each hour. Compilation: 120 attempts per browser user or API key each hour.
Timeouts, invalid return values, oversized transformed calls, schema mismatches, and runtime exceptions fail closed.
Execution order
The same order applies in Direct Mode and Code Mode. Hook rejections and pre-dispatch failures never contact the upstream. Upstream MCP errors remain upstream errors and do not run after.
- Rayrun evaluates ordinary workspace and client access policy against the public tool.
- The public arguments are validated, then before transforms or rejects them.
- The transformed arguments are validated against the upstream schema. Any hook-requested approval binds the exact active revision and transformed call.
- Rayrun rechecks the active revision immediately before dispatch, authorizes the upstream, and makes one upstream call.
- A successful result passes through after, public output validation, and configured response projection before returning to the client.
Develop, test, and deploy
Use the service Hooks tab to take one adapter from draft to active deployment.
- Choose a tool and edit against its generated TypeScript declarations.
- Save the draft. Parseable source is formatted; incomplete source remains unchanged.
- Test
beforeand, when needed,afterwith a mock result. - Deploy to shadow first and compare its best-effort records without changing live calls.
- Deploy active, or roll a deployment pointer back to an earlier compatible revision.
Compare and restore draft history
Use Hooks or hooks history to inspect immutable draft history. To reclaim the 64 MiB workspace allowance, export required versions and reset the whole hook.
- History records source, non-secret config, actor, channel, time, and an optional note. Identical saves are no-ops.
- Diff compares a version with its predecessor. Restore copies a version into a new draft under optimistic concurrency; it never rewrites history.
- Incomplete drafts remain restorable, but only compiled and tested deployment revisions can execute.
- Reset permanently removes one tool’s draft and deployment revisions and preserves a separate actor-attributed reset event.
- A service with hook history cannot be deleted until its hooks are reset. Legacy deleted services expose a history-only cleanup view.
Debug deployed code
The Hooks tab and hooks logs command show active and shadow stages, outcomes, duration, revision, request ID, and whether shadow differed from active. Use hooks logs --json for structured data. Pre-dispatch rejections and failures are retained even when no ordinary tool call exists.
Outcome, timing, revision, correlation, and the shadow comparison are metadata. Messages, log data, reasons, and errors require service payload capture plus the workspace detail policy.
The public API returns captured details only to hooks:write keys; the browser requires an administrator. Retention clears those details. Never put secrets in config, source, log messages, or log data.
