Class: GroupedToolBuilder<TContext, TCommon, TName, TRouterMap>
Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:162
Fluent builder for creating consolidated MCP tools.
Groups multiple related operations behind a single discriminator field (default: "action"), producing one MCP tool definition with a union schema and auto-generated descriptions.
See
createTool for the recommended factory function
Type Parameters
| Type Parameter | Default type | Description |
|---|---|---|
TContext | void | Application context passed to every handler |
TCommon extends Record<string, unknown> | Record<string, never> | Shape of the common schema (inferred automatically) |
TName extends string | string | Tool name literal (inferred by createTool) |
TRouterMap extends Record<string, unknown> | Record<string, never> | Accumulated action entries for InferRouter (phantom type) |
Implements
ToolBuilder<TContext>
Constructors
Constructor
new GroupedToolBuilder<TContext, TCommon, TName, TRouterMap>(name): GroupedToolBuilder<TContext, TCommon, TName, TRouterMap>;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:192
Parameters
| Parameter | Type |
|---|---|
name | string |
Returns
GroupedToolBuilder<TContext, TCommon, TName, TRouterMap>
Methods
action()
Call Signature
action<TActionName, TSchema, TOmit>(config): GroupedToolBuilder<TContext, TCommon, TName, TRouterMap & { [K in `${string}.${string}`]: TSchema["_output"] & Omit<TCommon, TOmit> }>;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:713
Register a flat action.
Flat actions use simple keys (e.g. "list", "create"). Cannot be mixed with .group() on the same builder.
When a schema is provided, the handler args are fully typed as TSchema["_output"] & TCommon — no type assertions needed.
Type Parameters
| Type Parameter | Default type |
|---|---|
TActionName extends string | - |
TSchema extends ZodObject<ZodRawShape, UnknownKeysParam, ZodTypeAny, { [key: string]: any; }, { [key: string]: any; }> | - |
TOmit extends string | number | symbol | never |
Parameters
| Parameter | Type | Description |
|---|---|---|
config | { description?: string; destructive?: boolean; handler: (ctx, args) => Promise<ToolResponse>; idempotent?: boolean; name: TActionName; omitCommon?: TOmit[]; readOnly?: boolean; schema: TSchema; } | Action configuration |
config.description? | string | - |
config.destructive? | boolean | - |
config.handler | (ctx, args) => Promise<ToolResponse> | - |
config.idempotent? | boolean | - |
config.name | TActionName | - |
config.omitCommon? | TOmit[] | - |
config.readOnly? | boolean | - |
config.schema | TSchema | - |
Returns
GroupedToolBuilder<TContext, TCommon, TName, TRouterMap & { [K in `${string}.${string}`]: TSchema["_output"] & Omit<TCommon, TOmit> }>
this for chaining
Example
createTool<AppContext>('projects')
.action({
name: 'list',
description: 'List all projects',
readOnly: true,
schema: z.object({ status: z.enum(['active', 'archived']).optional() }),
handler: async (ctx, args) => {
// args: { status?: 'active' | 'archived' } — fully typed
return success(await ctx.db.projects.findMany({ where: args }));
},
})
.action({
name: 'delete',
destructive: true,
schema: z.object({ id: z.string() }),
handler: async (ctx, args) => {
await ctx.db.projects.delete({ where: { id: args.id } });
return success('Deleted');
},
});See
- ActionConfig for all configuration options
- GroupedToolBuilder.group for hierarchical grouping
Call Signature
action<TActionName>(config): GroupedToolBuilder<TContext, TCommon, TName, TRouterMap & { [K in `${string}.${string}`]: TCommon extends Record<string, never> ? Record<string, unknown> : TCommon }>;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:724
Register a flat action (untyped: no schema, args default to Record<string, unknown>)
Type Parameters
| Type Parameter |
|---|
TActionName extends string |
Parameters
| Parameter | Type |
|---|---|
config | ActionConfig<TContext> & { name: TActionName; } |
Returns
GroupedToolBuilder<TContext, TCommon, TName, TRouterMap & { [K in `${string}.${string}`]: TCommon extends Record<string, never> ? Record<string, unknown> : TCommon }>
annotations()
annotations(a): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:262
Set MCP tool annotations.
Manual override for tool-level annotations. If not set, annotations are automatically aggregated from per-action properties.
Parameters
| Parameter | Type | Description |
|---|---|---|
a | Record<string, unknown> | Annotation key-value pairs |
Returns
this
this for chaining
Example
createTool('admin')
.annotations({ openWorldHint: true, returnDirect: false })See
bindState()
bindState(states, transition?): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:587
Bind this tool to specific FSM states.
When a StateMachineGate is configured, this tool is only visible in tools/list when the FSM is in one of the specified states.
Parameters
| Parameter | Type | Description |
|---|---|---|
states | string[] | FSM state(s) where this tool is visible |
transition? | string | Event to send on successful execution |
Returns
this
this for chaining
buildToolDefinition()
buildToolDefinition(): {
_meta?: {
[key: string]: unknown;
};
annotations?: {
destructiveHint?: boolean;
idempotentHint?: boolean;
openWorldHint?: boolean;
readOnlyHint?: boolean;
title?: string;
};
description?: string;
execution?: {
taskSupport?: "optional" | "required" | "forbidden";
};
icons?: {
mimeType?: string;
sizes?: string[];
src: string;
theme?: "light" | "dark";
}[];
inputSchema: {
[key: string]: unknown;
properties?: {
[key: string]: object;
};
required?: string[];
type: "object";
};
name: string;
outputSchema?: {
[key: string]: unknown;
properties?: {
[key: string]: object;
};
required?: string[];
type: "object";
};
title?: string;
};Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:854
Generate the MCP Tool definition.
Compiles all actions into a single MCP tool with auto-generated description, union schema, and aggregated annotations. Caches the result and permanently freezes the builder.
Called automatically by execute if not called explicitly.
Returns
{
_meta?: {
[key: string]: unknown;
};
annotations?: {
destructiveHint?: boolean;
idempotentHint?: boolean;
openWorldHint?: boolean;
readOnlyHint?: boolean;
title?: string;
};
description?: string;
execution?: {
taskSupport?: "optional" | "required" | "forbidden";
};
icons?: {
mimeType?: string;
sizes?: string[];
src: string;
theme?: "light" | "dark";
}[];
inputSchema: {
[key: string]: unknown;
properties?: {
[key: string]: object;
};
required?: string[];
type: "object";
};
name: string;
outputSchema?: {
[key: string]: unknown;
properties?: {
[key: string]: object;
};
required?: string[];
type: "object";
};
title?: string;
}The compiled MCP Tool object
| Name | Type | Defined in |
|---|---|---|
_meta? | { [key: string]: unknown; } | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2397 |
annotations? | { destructiveHint?: boolean; idempotentHint?: boolean; openWorldHint?: boolean; readOnlyHint?: boolean; title?: string; } | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2383 |
annotations.destructiveHint? | boolean | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2386 |
annotations.idempotentHint? | boolean | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2387 |
annotations.openWorldHint? | boolean | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2388 |
annotations.readOnlyHint? | boolean | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2385 |
annotations.title? | string | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2384 |
description? | string | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2372 |
execution? | { taskSupport?: "optional" | "required" | "forbidden"; } | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2390 |
execution.taskSupport? | "optional" | "required" | "forbidden" | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2391 |
icons? | { mimeType?: string; sizes?: string[]; src: string; theme?: "light" | "dark"; }[] | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2398 |
inputSchema | { [key: string]: unknown; properties?: { [key: string]: object; }; required?: string[]; type: "object"; } | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2373 |
inputSchema.properties? | { [key: string]: object; } | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2375 |
inputSchema.required? | string[] | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2376 |
inputSchema.type | "object" | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2374 |
name | string | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2407 |
outputSchema? | { [key: string]: unknown; properties?: { [key: string]: object; }; required?: string[]; type: "object"; } | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2378 |
outputSchema.properties? | { [key: string]: object; } | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2380 |
outputSchema.required? | string[] | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2381 |
outputSchema.type | "object" | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2379 |
title? | string | node_modules/@modelcontextprotocol/sdk/dist/esm/types.d.ts:2408 |
Throws
If no actions are registered
Example
const tool = builder.buildToolDefinition();
console.log(tool.name); // "projects"
console.log(tool.description); // Auto-generated
console.log(tool.inputSchema); // Union of all action schemasImplementation of
ToolBuilder.buildToolDefinition
cached()
cached(): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:401
Mark this tool's data as immutable (safe to cache forever).
Use for reference data: countries, currencies, ICD-10 codes. Equivalent to cacheControl: 'immutable' in manual policies.
Returns
this
this for chaining
Example
createTool('countries')
.cached()
.action({ name: 'list', readOnly: true, handler: listCountries });commonSchema()
commonSchema<TSchema>(schema): GroupedToolBuilder<TContext, TSchema["_output"], TName, TRouterMap>;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:324
Set a common schema shared by all actions.
Fields from this schema are injected into every action's input and marked as (always required) in the auto-generated description. The return type narrows to propagate types to all handlers.
Type Parameters
| Type Parameter | Description |
|---|---|
TSchema extends ZodObject<ZodRawShape, UnknownKeysParam, ZodTypeAny, { [key: string]: any; }, { [key: string]: any; }> | Zod object schema type (inferred) |
Parameters
| Parameter | Type | Description |
|---|---|---|
schema | TSchema | A z.object() defining shared fields |
Returns
GroupedToolBuilder<TContext, TSchema["_output"], TName, TRouterMap>
A narrowed builder with TCommon set to TSchema["_output"]
Example
createTool<AppContext>('projects')
.commonSchema(z.object({
workspace_id: z.string().describe('Workspace identifier'),
}))
.action({
name: 'list',
handler: async (ctx, args) => {
// ✅ args.workspace_id is typed as string
const projects = await ctx.db.projects.findMany({
where: { workspaceId: args.workspace_id },
});
return success(projects);
},
});concurrency()
concurrency(config): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:497
Set concurrency limits for this tool (Semaphore + Queue pattern).
Prevents thundering-herd scenarios where the LLM fires N concurrent calls in the same millisecond. Implements a semaphore with backpressure queue and load shedding.
When all active slots are occupied, new calls enter the queue. When the queue is full, calls are immediately rejected with a self-healing SERVER_BUSY error.
MCP Spec Compliance: The MCP specification requires servers to rate-limit tool invocations. This method fulfills that requirement.
Zero overhead when not configured — no semaphore exists.
Parameters
| Parameter | Type | Description |
|---|---|---|
config | ConcurrencyConfig | Concurrency configuration |
Returns
this
this for chaining
Example
createTool<AppContext>('billing')
.concurrency({ maxActive: 5, maxQueue: 20 })
.action({ name: 'process_invoice', handler: processInvoice });
// 5 concurrent executions, 20 queued, rest rejectedSee
ConcurrencyConfig for configuration options
debug()
debug(observer): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:911
Enable debug observability for this tool.
When enabled, structured DebugEvent events are emitted at each step of the execution pipeline.
When disabled (the default), there is zero runtime overhead — no conditionals, no timing, no object allocations in the hot path.
Parameters
| Parameter | Type | Description |
|---|---|---|
observer | DebugObserverFn | A DebugObserverFn created by createDebugObserver() |
Returns
this
this for chaining
Example
import { createTool, createDebugObserver, success } from '@vurb/core';
const debug = createDebugObserver();
const tool = createTool<void>('users')
.debug(debug) // ← enable observability
.action({ name: 'list', handler: async () => success([]) });description()
description(desc): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:239
Set the tool description.
Appears as the first line in the auto-generated tool description that the LLM sees.
Parameters
| Parameter | Type | Description |
|---|---|---|
desc | string | Human-readable description of what this tool does |
Returns
this
this for chaining
Example
createTool('projects')
.description('Manage workspace projects')discriminator()
discriminator(field): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:218
Set the discriminator field name.
The discriminator is the field the LLM uses to select which action to execute. Defaults to "action".
Parameters
| Parameter | Type | Description |
|---|---|---|
field | string | Field name for the discriminator enum |
Returns
this
this for chaining
Example
// Custom discriminator
const builder = createTool('projects')
.discriminator('operation')
.action({ name: 'list', handler: listProjects });
// LLM sends: { operation: 'list' }Default Value
"action"
enableSelect()
enableSelect(): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:462
Enable _select reflection for context window optimization.
When enabled, actions that use a Presenter with a Zod schema expose an optional _select parameter in the input schema. The AI can send _select: ['status', 'amount'] to receive only the specified top-level fields in the data payload, reducing context window usage without developer effort.
Disabled by default — opt-in to avoid changing existing tool schemas.
Late Guillotine: UI blocks, system rules, and action suggestions are always computed with the full validated data. Only the wire-facing data block is filtered.
Shallow (top-level only): Nested objects are returned whole. If the AI selects 'user', it gets the entire user object. No recursive GraphQL-style traversal.
Returns
this
this for chaining
Example
createTool<AppContext>('invoices')
.enableSelect() // Expose _select in input schema
.action({
name: 'get',
returns: InvoicePresenter,
handler: async (ctx, args) => ctx.db.invoices.findUnique(args.id),
});
// AI sends: { action: 'get', id: '123', _select: ['status'] }
// Returns: { status: 'paid' } instead of full invoiceSee
Presenter.getSchemaKeys for introspection
execute()
execute(
ctx,
args,
progressSink?,
signal?): Promise<ToolResponse>;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1009
Route a tool call to the correct action handler.
Pipeline: parseDiscriminator → resolveAction → validateArgs → runChain
Auto-calls buildToolDefinition if not called yet. If a debug observer is active, structured events are emitted at each pipeline step with timing information.
Parameters
| Parameter | Type | Description |
|---|---|---|
ctx | TContext | Application context |
args | Record<string, unknown> | Raw arguments from the LLM (includes discriminator) |
progressSink? | ProgressSink | Optional callback for streaming progress notifications. When attached via attachToServer(), this is automatically wired to MCP notifications/progress. When omitted, progress events are silently consumed. |
signal? | AbortSignal | Optional AbortSignal from the MCP SDK protocol layer. Fired when the client sends notifications/cancelled or the connection drops. The framework checks this signal before handler execution and during generator iteration, aborting zombie operations immediately. |
Returns
Promise<ToolResponse>
The handler's ToolResponse
Example
// Direct execution (useful in tests)
const result = await builder.execute(ctx, {
action: 'list',
workspace_id: 'ws_123',
});Implementation of
getActionMetadata()
getActionMetadata(): ActionMetadata[];Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1418
Get metadata for all registered actions.
Useful for programmatic documentation, compliance audits, dashboard generation, or runtime observability.
Returns
Array of ActionMetadata objects
Example
const meta = builder.getActionMetadata();
for (const action of meta) {
console.log(`${action.key}: destructive=${action.destructive}, fields=${action.requiredFields}`);
}See
ActionMetadata for the metadata shape
Implementation of
getActionNames()
getActionNames(): string[];Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1223
Get all registered action keys (e.g. ["list", "create"] or ["users.list", "users.ban"]).
Returns
string[]
Implementation of
getActions()
getActions(): readonly InternalAction<TContext>[];Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1282
Get all registered internal actions. Used by the Exposition Compiler for atomic tool expansion.
Returns
readonly InternalAction<TContext>[]
Read-only array of internal action definitions
Implementation of
getCommonSchema()
getCommonSchema():
| ZodObject<ZodRawShape, UnknownKeysParam, ZodTypeAny, {
[key: string]: any;
}, {
[key: string]: any;
}>
| undefined;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1289
Get the common schema shared across all actions. Used by the Exposition Compiler for schema purification.
Returns
| ZodObject<ZodRawShape, UnknownKeysParam, ZodTypeAny, { [key: string]: any; }, { [key: string]: any; }> | undefined
The common Zod schema, or undefined if not set
Implementation of
getDiscriminator()
getDiscriminator(): string;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1275
Get the discriminator field name (e.g. "action"). Used by the Exposition Compiler.
Returns
string
Implementation of
getFsmBinding()
getFsmBinding():
| {
states: string[];
transition?: string;
}
| undefined;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:598
Get the FSM binding metadata (if any). Used by ToolRegistry and ServerAttachment for FSM gating.
Returns
| { states: string[]; transition?: string; } | undefined
getName()
getName(): string;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1217
Get the tool name.
Returns
string
Implementation of
getSandboxConfig()
getSandboxConfig(): SandboxConfig | undefined;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:571
Get the sandbox configuration (if any).
Important: This is metadata only — it does NOT auto-create a SandboxEngine nor inject it into the execution pipeline. The developer must create the engine manually (e.g. via f.sandbox()). This accessor exists for introspection, testing, and contract tooling.
Returns
SandboxConfig | undefined
The stored SandboxConfig, or undefined if .sandbox() was not called
getSelectEnabled()
getSelectEnabled(): boolean;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1292
Check if _select reflection is enabled. Used by the Exposition Compiler.
Returns
boolean
Implementation of
getStateSyncHints()
getStateSyncHints(): ReadonlyMap<string, StateSyncHint>;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1295
Get per-action state sync hints for auto-policy generation.
Returns
ReadonlyMap<string, StateSyncHint>
Implementation of
getTags()
getTags(): string[];Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1220
Get a copy of the capability tags.
Returns
string[]
Implementation of
getToolName()
getToolName(): string;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:635
Get the tool name. Used by framework internals for tool routing and FSM binding.
Returns
string
group()
Call Signature
group(name, configure): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:795
Register a group of actions under a namespace.
Group actions use compound keys (e.g. "users.create", "billing.refund"). Cannot be mixed with .action() on the same builder.
Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | Group name (must not contain dots) |
configure | GroupConfigurator<TContext, TCommon> | Callback that receives an ActionGroupBuilder |
Returns
this
this for chaining
Example
createTool<AppContext>('platform')
.group('users', 'User management', g => {
g.use(requireAdmin) // Group-scoped middleware
.action({ name: 'list', readOnly: true, handler: listUsers })
.action({ name: 'ban', destructive: true, schema: banSchema, handler: banUser });
})
.group('billing', g => {
g.action({ name: 'refund', destructive: true, schema: refundSchema, handler: issueRefund });
});
// Discriminator enum: "users.list" | "users.ban" | "billing.refund"See
- ActionGroupBuilder for group-level configuration
- GroupedToolBuilder.action for flat actions
Call Signature
group(
name,
description,
configure): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:796
Register a group of actions under a namespace.
Group actions use compound keys (e.g. "users.create", "billing.refund"). Cannot be mixed with .action() on the same builder.
Parameters
| Parameter | Type | Description |
|---|---|---|
name | string | Group name (must not contain dots) |
description | string | - |
configure | GroupConfigurator<TContext, TCommon> | Callback that receives an ActionGroupBuilder |
Returns
this
this for chaining
Example
createTool<AppContext>('platform')
.group('users', 'User management', g => {
g.use(requireAdmin) // Group-scoped middleware
.action({ name: 'list', readOnly: true, handler: listUsers })
.action({ name: 'ban', destructive: true, schema: banSchema, handler: banUser });
})
.group('billing', g => {
g.action({ name: 'refund', destructive: true, schema: refundSchema, handler: issueRefund });
});
// Discriminator enum: "users.list" | "users.ban" | "billing.refund"See
- ActionGroupBuilder for group-level configuration
- GroupedToolBuilder.action for flat actions
interactive()
interactive(): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:617
Mark this tool as interactive (supports MCP Elicitation).
When enabled, the standalone ask() function can be used inside the handler to pause execution and request user input.
Returns
this
this for chaining
invalidates()
invalidates(...patterns): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:375
Declare glob patterns invalidated when this tool succeeds.
Eliminates manual stateSync.policies configuration — the framework auto-collects hints from all builders.
Parameters
| Parameter | Type | Description |
|---|---|---|
...patterns | string[] | Glob patterns (e.g. 'sprints.*', 'tasks.*') |
Returns
this
this for chaining
Example
createTool('tasks')
.invalidates('tasks.*', 'sprints.*')
.action({ name: 'update', handler: updateTask });See
StateSyncConfig for centralized configuration
maxPayloadBytes()
maxPayloadBytes(bytes): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:530
Set maximum payload size for tool responses (Egress Guard).
Prevents oversized responses from crashing the Node process with OOM or overflowing the LLM context window.
When a response exceeds the limit, the text content is truncated and a system intervention message is injected, forcing the LLM to use pagination or filters.
This is a brute-force safety net. For domain-aware truncation with guidance, use Presenter .agentLimit() instead.
Zero overhead when not configured.
Parameters
| Parameter | Type | Description |
|---|---|---|
bytes | number | Maximum payload size in bytes |
Returns
this
this for chaining
Example
createTool<AppContext>('logs')
.maxPayloadBytes(2 * 1024 * 1024) // 2MB
.action({ name: 'search', handler: searchLogs });See
Presenter.agentLimit for domain-level truncation
previewPrompt()
previewPrompt(): string;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:1338
Preview the exact MCP protocol payload that the LLM will receive.
Builds the tool definition if not already built, then renders a human-readable preview of the complete tool including:
- Tool name and description
- Input schema (JSON)
- Annotations (if any)
- Approximate token count (~4 chars per token, GPT-5.2 heuristic)
Call this from your dev environment to optimize token usage and verify the LLM-facing prompt without starting an MCP server.
Returns
string
Formatted string showing the exact MCP payload + token estimate
Example
const projects = defineTool<AppContext>('projects', { ... });
console.log(projects.previewPrompt());
// Output:
// ┌─────────────────────────────────────────┐
// │ MCP Tool Preview: projects │
// ├─────────────────────────────────────────┤
// │ Name: projects │
// │ Actions: 3 (list, create, delete) │
// │ Tags: api, admin │
// ├─── Description ─────────────────────────┤
// │ Manage workspace projects. ... │
// ├─── Input Schema ────────────────────────┤
// │ { "type": "object", ... } │
// ├─── Annotations ─────────────────────────┤
// │ readOnlyHint: false │
// │ destructiveHint: true │
// ├─── Token Estimate ──────────────────────┤
// │ ~342 tokens (1,368 chars) │
// └─────────────────────────────────────────┘See
buildToolDefinition for the raw MCP Tool object
Implementation of
sandbox()
sandbox(config): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:555
Enable zero-trust sandboxed execution for this tool.
Stores the sandbox configuration so that tools built with .sandboxed() on the FluentToolBuilder can propagate it.
Parameters
| Parameter | Type | Description |
|---|---|---|
config | SandboxConfig | Sandbox configuration (timeout, memory, output size) |
Returns
this
this for chaining
Example
createTool<AppContext>('analytics')
.sandbox({ timeout: 5000, memoryLimit: 128 })
.action({ name: 'compute', handler: computeHandler });See
- SandboxConfig for configuration options
- SandboxEngine for the execution engine
stale()
stale(): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:413
Mark this tool's data as volatile (never cache).
Equivalent to cacheControl: 'no-store' in manual policies. Use for dynamic data that changes frequently.
Returns
this
this for chaining
tags()
tags(...tags): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:289
Set capability tags for selective tool exposure.
Tags control which tools the LLM sees via ToolRegistry.attachToServer's filter option. Use tags to implement per-session context gating.
Parameters
| Parameter | Type | Description |
|---|---|---|
...tags | string[] | One or more string tags |
Returns
this
this for chaining
Example
const users = createTool<AppContext>('users').tags('core');
const admin = createTool<AppContext>('admin').tags('admin', 'internal');
// Expose only 'core' tools to the LLM:
registry.attachToServer(server, { filter: { tags: ['core'] } });See
ToolRegistry.getTools for filtered tool retrieval
telemetry()
telemetry(sink): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:931
Enable out-of-band telemetry emission for Inspector TUI.
When set, validate, middleware, presenter.slice, and presenter.rules events are emitted to the TelemetrySink (Shadow Socket IPC), enabling real-time monitoring in the Inspector dashboard.
Zero overhead when not configured — no conditionals in the hot path.
Parameters
| Parameter | Type | Description |
|---|---|---|
sink | TelemetrySink | A TelemetrySink from startServer() or TelemetryBus |
Returns
this
this for chaining
toonDescription()
toonDescription(): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:349
Enable TOON-formatted descriptions for token optimization.
Uses TOON (Token-Oriented Object Notation) to encode action metadata in a compact tabular format, reducing description token count by ~30-50%.
Returns
this
this for chaining
Example
createTool('projects')
.toonDescription() // Compact descriptions
.action({ name: 'list', handler: listProjects })See
toonSuccess for TOON-encoded responses
tracing()
tracing(tracer): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:972
Enable OpenTelemetry-compatible tracing for this tool.
When enabled, each execute() call creates a single span with structured events for each pipeline step (mcp.route, mcp.validate, mcp.middleware, mcp.execute).
Zero overhead when disabled — the fast path has no conditionals.
OTel direct pass-through: The VurbTracer interface is a structural subtype of OTel's Tracer, so you can pass an OTel tracer directly without any adapter:
import { trace } from '@opentelemetry/api';
const tool = createTool<AppContext>('projects')
.tracing(trace.getTracer('vurb'))
.action({ name: 'list', handler: listProjects });Error classification:
- Validation failures →
SpanStatusCode.UNSET+mcp.error_typeattribute - Handler exceptions →
SpanStatusCode.ERROR+recordException()
Context propagation limitation: Since Vurb does not depend on @opentelemetry/api, it cannot call context.with(trace.setSpan(...)). Auto-instrumented downstream calls (Prisma, HTTP, Redis) inside handlers will appear as siblings, not children, of the MCP span.
Parameters
| Parameter | Type | Description |
|---|---|---|
tracer | VurbTracer | A VurbTracer (or OTel Tracer) instance |
Returns
this
this for chaining
See
- VurbTracer for the interface contract
- SpanStatusCode for status code semantics
use()
use(mw): this;Defined in: packages/core/src/core/builder/GroupedToolBuilder.ts:666
Add middleware to the execution chain.
Middleware runs in registration order (first registered = outermost). Chains are pre-compiled at build time — zero runtime assembly cost.
Accepts both MiddlewareDefinition from f.middleware() and raw MiddlewareFn functions.
Parameters
| Parameter | Type | Description |
|---|---|---|
mw | | MiddlewareFn<TContext> | MiddlewareDefinition<TContext, Record<string, unknown>> | Middleware function or MiddlewareDefinition |
Returns
this
this for chaining
Example
const requireAuth: MiddlewareFn<AppContext> = async (ctx, args, next) => {
if (!ctx.user) return error('Unauthorized');
return next();
};
createTool<AppContext>('projects')
.use(requireAuth) // Runs on every action
.action({ name: 'list', handler: listProjects });See
- MiddlewareFn for the middleware signature
- ActionGroupBuilder.use for group-scoped middleware