client.tools.<name>.handle and the harness end calls it with session.tools.<name>.call. A handler receives that tool’s input (a call missing a required member fails before the handler runs) and returns text, content blocks, a tool result or, for bash, a bash result. Registering a handler advertises the shell or files capability; throwing ToolError reports is_error: true to the model, any other error is outcome: "failed".
Generated from bma-executor-sdk 1.3.0 and its contract (
contracts/bma/executor, protocol bma.executor.v1.1). Install, credentials and quick starts are on the bma-executor-sdk overview; the protocol itself is described in Executor Protocol.Tool slot (executor end)
The generic slot behind everyclient.tools.<name>; the tool sections show it with each tool’s input type.
export interface ToolSlot<N extends ToolName> {
/**
* Serves tool.call for this tool; replaces an earlier handler. After
* start() the changed capabilities are sent with executor.capabilities.update.
*/
handle(handler: ToolHandler<N>): void
}
def handler(self, fn: ToolHandler[I, O]) -> ToolHandler[I, O]
func (s ToolSlot[In]) Handle(handler func(ctx context.Context, input In, tool ToolContext) (ToolOutput, error))
Tool slot (harness end)
The generic entry behind everysession.tools.<name>; it sends tool.call and waits for tool.result, like callTool.
export type HarnessTools = {
readonly [N in ToolName]: { call(input: ToolCallInputMap[N], options?: CallToolOptions): Promise<ToolResultParams> }
}
async def call(
self, input: _I, *, tool_call_id: str = "", deadline_ms: int | None = None, permission: ToolPermission | None = None
) -> ToolResultParams
func (s HarnessToolSlot[In]) Call(ctx context.Context, input In, options CallToolOptions) (ToolResultParams, error)
ToolContext
What a handler learns about the call it serves, andprogress for tool.progress updates.
export interface ToolContext {
/** Aborted by tool.cancel, deadline_ms, or stop(). */
signal: AbortSignal
/** Sends tool.progress for this call (best-effort). */
progress(message: string): void
sessionId: string
toolCallId: string
permission?: ToolPermission
}
@dataclass(frozen=True)
class ToolContext:
session_id: str
tool_call_id: str
progress: Callable[[str], None]
permission: Optional[ToolPermission] = None
type ToolContext struct {
SessionID string
ToolCallID string
Permission *ToolPermission
// Progress sends tool.progress for this call (best-effort).
Progress func(message string)
params ToolCallParams
}
BashResult
A finished command, rendered as stdout, stderr andexit_code: N; output beyond 48 KiB (or marked truncated) is cut to a preview.
export interface BashResult {
stdout: string
stderr: string
exitCode: number
/** The handler already cut the output short. */
truncated?: boolean
}
@dataclass(frozen=True)
class BashResult:
"""bash output, rendered as Go Core renders it: stdout, stderr, ``exit_code: N``."""
stdout: str
stderr: str
exit_code: int
truncated: bool = False
type BashResult struct {
Stdout string
Stderr string
ExitCode int
// Truncated reports that the handler already cut the output short.
Truncated bool
}
func (r BashResult) Output() ToolOutput
ToolError
A tool-level error reported to the model (outcome succeeded,is_error: true), the same role as the Anthropic SDK ToolError.
export class ToolError extends Error {
readonly content: ContentBlock[]
constructor(content: string | ContentBlock[])
}
class ToolError(Exception):
"""Raised from a tool handler: a tool-level error reported to the model"""
def __init__(self, content: Union[str, list[ContentBlock]]) -> None: ...
type ToolError struct {
Content []ContentBlock
}
func NewToolError(text string) *ToolError
Built-in tools
bash
bash input: run command with a login shell. InputBashInput
| Name | Type | Required | Description |
|---|---|---|---|
command | string | yes | Shell command line. |
cwd | string | no | Working directory, relative to the session workspace. |
timeout_ms | integer | no | Per-call timeout; the executor default applies when absent. |
env | map<string, string> | no | Extra environment variables. |
client.tools.bash.handle(handler: ToolHandler<"bash">): void
// ToolHandler<"bash"> = (input: ToolCallInputMap["bash"], context: ToolContext) => Promise<ToolOutputOf<"bash">>
client.tools.bash: ToolSlot[BashInput, Union[ToolOutput, BashResult]]
@client.tools.bash.handler
async def handler(input: BashInput, ctx: ToolContext) -> ...
client.Tools.Bash.Handle(handler func(ctx context.Context, input BashInput, tool ToolContext) (ToolOutput, error))
session.tools.bash.call(input: ToolCallInputMap["bash"], options?: CallToolOptions): Promise<ToolResultParams>
session.tools.bash: HarnessToolSlot[BashInput]
await session.tools.bash.call(input, *, tool_call_id="", deadline_ms=None, permission=None) -> ToolResultParams
session.Tools.Bash.Call(ctx context.Context, input BashInput, options CallToolOptions) (ToolResultParams, error)
client.tools.bash.handle(async ({ command }, { progress }) => {
progress("running");
return `bash ${command}`;
});
// harness end
const result = await session.tools.bash.call({ command: "ls -la" });
@client.tools.bash.handler
async def bash(input: BashInput, ctx: ToolContext) -> str:
ctx.progress("running")
return f"bash {input["command"]}"
# harness end
result = await session.tools.bash.call({"command": "ls -la"})
client.Tools.Bash.Handle(func(ctx context.Context, in bmaexecutor.BashInput, tool bmaexecutor.ToolContext) (bmaexecutor.ToolOutput, error) {
tool.Progress("running")
return bmaexecutor.TextOutput("bash " + in.Command), nil
})
// harness end
result, err := session.Tools.Bash.Call(ctx, bmaexecutor.BashInput{Command: "ls -la"}, bmaexecutor.CallToolOptions{})
read
read input: return lines of a text file. InputReadInput
| Name | Type | Required | Description |
|---|---|---|---|
path | string | yes | File path, relative to the session workspace. |
offset | integer | no | First line to return, 1-based. |
limit | integer | no | Maximum number of lines. |
client.tools.read.handle(handler: ToolHandler<"read">): void
// ToolHandler<"read"> = (input: ToolCallInputMap["read"], context: ToolContext) => Promise<ToolOutputOf<"read">>
client.tools.read: ToolSlot[ReadInput, ToolOutput]
@client.tools.read.handler
async def handler(input: ReadInput, ctx: ToolContext) -> ...
client.Tools.Read.Handle(handler func(ctx context.Context, input ReadInput, tool ToolContext) (ToolOutput, error))
session.tools.read.call(input: ToolCallInputMap["read"], options?: CallToolOptions): Promise<ToolResultParams>
session.tools.read: HarnessToolSlot[ReadInput]
await session.tools.read.call(input, *, tool_call_id="", deadline_ms=None, permission=None) -> ToolResultParams
session.Tools.Read.Call(ctx context.Context, input ReadInput, options CallToolOptions) (ToolResultParams, error)
client.tools.read.handle(async ({ path }, { progress }) => {
progress("running");
return `read ${path}`;
});
// harness end
const result = await session.tools.read.call({ path: "README.md" });
@client.tools.read.handler
async def read(input: ReadInput, ctx: ToolContext) -> str:
ctx.progress("running")
return f"read {input["path"]}"
# harness end
result = await session.tools.read.call({"path": "README.md"})
client.Tools.Read.Handle(func(ctx context.Context, in bmaexecutor.ReadInput, tool bmaexecutor.ToolContext) (bmaexecutor.ToolOutput, error) {
tool.Progress("running")
return bmaexecutor.TextOutput("read " + in.Path), nil
})
// harness end
result, err := session.Tools.Read.Call(ctx, bmaexecutor.ReadInput{Path: "README.md"}, bmaexecutor.CallToolOptions{})
write
write input: create or replace a file. InputWriteInput
| Name | Type | Required | Description |
|---|---|---|---|
path | string | yes | File path, relative to the session workspace. |
content | string | yes | Full file content. |
client.tools.write.handle(handler: ToolHandler<"write">): void
// ToolHandler<"write"> = (input: ToolCallInputMap["write"], context: ToolContext) => Promise<ToolOutputOf<"write">>
client.tools.write: ToolSlot[WriteInput, ToolOutput]
@client.tools.write.handler
async def handler(input: WriteInput, ctx: ToolContext) -> ...
client.Tools.Write.Handle(handler func(ctx context.Context, input WriteInput, tool ToolContext) (ToolOutput, error))
session.tools.write.call(input: ToolCallInputMap["write"], options?: CallToolOptions): Promise<ToolResultParams>
session.tools.write: HarnessToolSlot[WriteInput]
await session.tools.write.call(input, *, tool_call_id="", deadline_ms=None, permission=None) -> ToolResultParams
session.Tools.Write.Call(ctx context.Context, input WriteInput, options CallToolOptions) (ToolResultParams, error)
client.tools.write.handle(async ({ path, content }, { progress }) => {
progress("running");
return `write ${path}`;
});
// harness end
const result = await session.tools.write.call({ path: "README.md", content: "hello" });
@client.tools.write.handler
async def write(input: WriteInput, ctx: ToolContext) -> str:
ctx.progress("running")
return f"write {input["path"]}"
# harness end
result = await session.tools.write.call({"path": "README.md", "content": "hello"})
client.Tools.Write.Handle(func(ctx context.Context, in bmaexecutor.WriteInput, tool bmaexecutor.ToolContext) (bmaexecutor.ToolOutput, error) {
tool.Progress("running")
return bmaexecutor.TextOutput("write " + in.Path), nil
})
// harness end
result, err := session.Tools.Write.Call(ctx, bmaexecutor.WriteInput{Path: "README.md", Content: "hello"}, bmaexecutor.CallToolOptions{})
edit
edit input: replace the single occurrence of old_string. InputEditInput
| Name | Type | Required | Description |
|---|---|---|---|
path | string | yes | File path, relative to the session workspace. |
old_string | string | yes | Text that must occur exactly once. |
new_string | string | yes | Replacement text. |
client.tools.edit.handle(handler: ToolHandler<"edit">): void
// ToolHandler<"edit"> = (input: ToolCallInputMap["edit"], context: ToolContext) => Promise<ToolOutputOf<"edit">>
client.tools.edit: ToolSlot[EditInput, ToolOutput]
@client.tools.edit.handler
async def handler(input: EditInput, ctx: ToolContext) -> ...
client.Tools.Edit.Handle(handler func(ctx context.Context, input EditInput, tool ToolContext) (ToolOutput, error))
session.tools.edit.call(input: ToolCallInputMap["edit"], options?: CallToolOptions): Promise<ToolResultParams>
session.tools.edit: HarnessToolSlot[EditInput]
await session.tools.edit.call(input, *, tool_call_id="", deadline_ms=None, permission=None) -> ToolResultParams
session.Tools.Edit.Call(ctx context.Context, input EditInput, options CallToolOptions) (ToolResultParams, error)
client.tools.edit.handle(async ({ path, old_string, new_string }, { progress }) => {
progress("running");
return `edit ${path}`;
});
// harness end
const result = await session.tools.edit.call({ path: "README.md", old_string: "hello", new_string: "hi" });
@client.tools.edit.handler
async def edit(input: EditInput, ctx: ToolContext) -> str:
ctx.progress("running")
return f"edit {input["path"]}"
# harness end
result = await session.tools.edit.call({"path": "README.md", "old_string": "hello", "new_string": "hi"})
client.Tools.Edit.Handle(func(ctx context.Context, in bmaexecutor.EditInput, tool bmaexecutor.ToolContext) (bmaexecutor.ToolOutput, error) {
tool.Progress("running")
return bmaexecutor.TextOutput("edit " + in.Path), nil
})
// harness end
result, err := session.Tools.Edit.Call(ctx, bmaexecutor.EditInput{Path: "README.md", OldString: "hello", NewString: "hi"}, bmaexecutor.CallToolOptions{})
glob
glob input: list paths matching pattern. InputGlobInput
| Name | Type | Required | Description |
|---|---|---|---|
pattern | string | yes | Glob pattern. |
path | string | no | Base directory, relative to the session workspace. |
client.tools.glob.handle(handler: ToolHandler<"glob">): void
// ToolHandler<"glob"> = (input: ToolCallInputMap["glob"], context: ToolContext) => Promise<ToolOutputOf<"glob">>
client.tools.glob: ToolSlot[GlobInput, ToolOutput]
@client.tools.glob.handler
async def handler(input: GlobInput, ctx: ToolContext) -> ...
client.Tools.Glob.Handle(handler func(ctx context.Context, input GlobInput, tool ToolContext) (ToolOutput, error))
session.tools.glob.call(input: ToolCallInputMap["glob"], options?: CallToolOptions): Promise<ToolResultParams>
session.tools.glob: HarnessToolSlot[GlobInput]
await session.tools.glob.call(input, *, tool_call_id="", deadline_ms=None, permission=None) -> ToolResultParams
session.Tools.Glob.Call(ctx context.Context, input GlobInput, options CallToolOptions) (ToolResultParams, error)
client.tools.glob.handle(async ({ pattern }, { progress }) => {
progress("running");
return `glob ${pattern}`;
});
// harness end
const result = await session.tools.glob.call({ pattern: "**/*.go" });
@client.tools.glob.handler
async def glob(input: GlobInput, ctx: ToolContext) -> str:
ctx.progress("running")
return f"glob {input["pattern"]}"
# harness end
result = await session.tools.glob.call({"pattern": "**/*.go"})
client.Tools.Glob.Handle(func(ctx context.Context, in bmaexecutor.GlobInput, tool bmaexecutor.ToolContext) (bmaexecutor.ToolOutput, error) {
tool.Progress("running")
return bmaexecutor.TextOutput("glob " + in.Pattern), nil
})
// harness end
result, err := session.Tools.Glob.Call(ctx, bmaexecutor.GlobInput{Pattern: "**/*.go"}, bmaexecutor.CallToolOptions{})
grep
grep input: search file lines with a regular expression. InputGrepInput
| Name | Type | Required | Description |
|---|---|---|---|
pattern | string | yes | Regular expression. |
path | string | no | Directory to search, relative to the session workspace. |
glob | string | no | File name filter. |
client.tools.grep.handle(handler: ToolHandler<"grep">): void
// ToolHandler<"grep"> = (input: ToolCallInputMap["grep"], context: ToolContext) => Promise<ToolOutputOf<"grep">>
client.tools.grep: ToolSlot[GrepInput, ToolOutput]
@client.tools.grep.handler
async def handler(input: GrepInput, ctx: ToolContext) -> ...
client.Tools.Grep.Handle(handler func(ctx context.Context, input GrepInput, tool ToolContext) (ToolOutput, error))
session.tools.grep.call(input: ToolCallInputMap["grep"], options?: CallToolOptions): Promise<ToolResultParams>
session.tools.grep: HarnessToolSlot[GrepInput]
await session.tools.grep.call(input, *, tool_call_id="", deadline_ms=None, permission=None) -> ToolResultParams
session.Tools.Grep.Call(ctx context.Context, input GrepInput, options CallToolOptions) (ToolResultParams, error)
client.tools.grep.handle(async ({ pattern }, { progress }) => {
progress("running");
return `grep ${pattern}`;
});
// harness end
const result = await session.tools.grep.call({ pattern: "**/*.go" });
@client.tools.grep.handler
async def grep(input: GrepInput, ctx: ToolContext) -> str:
ctx.progress("running")
return f"grep {input["pattern"]}"
# harness end
result = await session.tools.grep.call({"pattern": "**/*.go"})
client.Tools.Grep.Handle(func(ctx context.Context, in bmaexecutor.GrepInput, tool bmaexecutor.ToolContext) (bmaexecutor.ToolOutput, error) {
tool.Progress("running")
return bmaexecutor.TextOutput("grep " + in.Pattern), nil
})
// harness end
result, err := session.Tools.Grep.Call(ctx, bmaexecutor.GrepInput{Pattern: "**/*.go"}, bmaexecutor.CallToolOptions{})
list_files
list_files input. InputListFilesInput
| Name | Type | Required | Description |
|---|---|---|---|
path | string | no | Directory, relative to the session workspace. |
client.tools.list_files.handle(handler: ToolHandler<"list_files">): void
// ToolHandler<"list_files"> = (input: ToolCallInputMap["list_files"], context: ToolContext) => Promise<ToolOutputOf<"list_files">>
client.tools.list_files: ToolSlot[ListFilesInput, ToolOutput]
@client.tools.list_files.handler
async def handler(input: ListFilesInput, ctx: ToolContext) -> ...
client.Tools.ListFiles.Handle(handler func(ctx context.Context, input ListFilesInput, tool ToolContext) (ToolOutput, error))
session.tools.list_files.call(input: ToolCallInputMap["list_files"], options?: CallToolOptions): Promise<ToolResultParams>
session.tools.list_files: HarnessToolSlot[ListFilesInput]
await session.tools.list_files.call(input, *, tool_call_id="", deadline_ms=None, permission=None) -> ToolResultParams
session.Tools.ListFiles.Call(ctx context.Context, input ListFilesInput, options CallToolOptions) (ToolResultParams, error)
client.tools.list_files.handle(async (_input, { progress }) => {
progress("running");
return "list_files done";
});
// harness end
const result = await session.tools.list_files.call({});
@client.tools.list_files.handler
async def list_files(input: ListFilesInput, ctx: ToolContext) -> str:
ctx.progress("running")
return "list_files done"
# harness end
result = await session.tools.list_files.call({})
client.Tools.ListFiles.Handle(func(ctx context.Context, in bmaexecutor.ListFilesInput, tool bmaexecutor.ToolContext) (bmaexecutor.ToolOutput, error) {
tool.Progress("running")
return bmaexecutor.TextOutput("list_files done"), nil
})
// harness end
result, err := session.Tools.ListFiles.Call(ctx, bmaexecutor.ListFilesInput{}, bmaexecutor.CallToolOptions{})
computer_observe
computer_observe input. InputComputerObserveInput
| Name | Type | Required | Description |
|---|---|---|---|
display | string | no | Display to observe. |
// Not in the TypeScript SDK 1.3.0. (no typed slot)
# Not in the Python SDK 1.3.0. (no typed slot)
// Not in the Go SDK 1.3.0. (no typed slot)
session.tools.computer_observe.call(input: ToolCallInputMap["computer_observe"], options?: CallToolOptions): Promise<ToolResultParams>
# Not in the Python SDK 1.3.0. (no typed slot)
// Not in the Go SDK 1.3.0. (no typed slot)
computer_action
computer_action input. InputComputerActionInput
| Name | Type | Required | Description |
|---|---|---|---|
action | "click" | "type" | "key" | "scroll" | "drag" | yes | Input actions accepted by the computer_action tool. |
display | string | no | Display to act on. |
// Not in the TypeScript SDK 1.3.0. (no typed slot)
# Not in the Python SDK 1.3.0. (no typed slot)
// Not in the Go SDK 1.3.0. (no typed slot)
session.tools.computer_action.call(input: ToolCallInputMap["computer_action"], options?: CallToolOptions): Promise<ToolResultParams>
# Not in the Python SDK 1.3.0. (no typed slot)
// Not in the Go SDK 1.3.0. (no typed slot)