Direct Tools
Run deterministic host tools without spending an LLM turn
Direct Tools
session.tool(name, args) (and the typed helpers like glob, grep, readFile)
run a host tool directly, with no model call in the loop. Use them for tests,
migrations, and host-driven workflows where you want deterministic results
instead of an agent turn.
Direct calls are host control-plane calls. Apply your own product authorization
before invoking them; permissionPolicy gates model-selected tool calls inside
an agent turn, not whether your application code is allowed to call an SDK
helper.
import { Agent } from '@a3s-lab/code';
const agent = await Agent.create('agent.acl');
const session = agent.session('.');
const allowedDirectTools = new Set(['glob', 'grep', 'read', 'generate_object']);
function assertDirectToolAllowed(name: string) {
if (!allowedDirectTools.has(name)) {
throw new Error(`direct tool not allowed here: ${name}`);
}
}
// Glob: list files by pattern
assertDirectToolAllowed('glob');
const files = await session.glob('**/*.ts');
console.log(`glob found ${files.length} TypeScript files`);
// Grep: search file contents
assertDirectToolAllowed('grep');
const matches = await session.grep('Agent.create');
const matchCount = matches.split('\n').filter(Boolean).length;
console.log(`grep found ${matchCount} matching lines`);
// Read a file
assertDirectToolAllowed('read');
const readme = await session.readFile('README.md');
console.log(`README is ${readme.length} bytes`);
// Direct tool call by name
assertDirectToolAllowed('read');
const raw = await session.tool('read', { file_path: 'package.json' });
console.log(`package.json via tool(): ${raw.output.length} bytes`);
// Inspect available tool schemas
const schemas = session.toolDefinitions();
console.log(`session exposes ${schemas.length} tools`);
// Structured output: generate a schema-validated JSON object
assertDirectToolAllowed('generate_object');
const structured = await session.tool('generate_object', {
schema: {
type: 'object',
required: ['count', 'language'],
properties: {
count: { type: 'integer' },
language: { type: 'string' },
},
},
prompt: 'How many TypeScript files are in this project?',
schema_name: 'file_stats',
});
if (structured.exitCode !== 0) {
throw new Error(structured.output);
}
console.log('structured output:', JSON.parse(structured.output).object);
session.close();import json
from a3s_code import Agent
agent = Agent.create("agent.acl")
session = agent.session('.')
ALLOWED_DIRECT_TOOLS = {'glob', 'grep', 'read', 'generate_object'}
def assert_direct_tool_allowed(name: str) -> None:
if name not in ALLOWED_DIRECT_TOOLS:
raise RuntimeError(f'direct tool not allowed here: {name}')
# Glob: list files by pattern
assert_direct_tool_allowed('glob')
files = session.glob('**/*.py')
print(f'glob found {len(files)} Python files')
# Grep: search file contents
assert_direct_tool_allowed('grep')
matches = session.grep('Agent.create')
match_count = len([line for line in matches.splitlines() if line])
print(f'grep found {match_count} matching lines')
# Read a file
assert_direct_tool_allowed('read')
readme = session.read_file('README.md')
print(f'README is {len(readme)} bytes')
# Direct tool call by name
assert_direct_tool_allowed('read')
raw = session.tool('read', {'file_path': 'pyproject.toml'})
print(f'pyproject.toml via tool(): {len(raw.output)} bytes')
# Inspect available tool schemas
schemas = session.tool_definitions()
print(f'session exposes {len(schemas)} tools')
# Structured output: generate a schema-validated JSON object
assert_direct_tool_allowed('generate_object')
structured = session.tool('generate_object', {
'schema': {
'type': 'object',
'required': ['count', 'language'],
'properties': {
'count': {'type': 'integer'},
'language': {'type': 'string'},
},
},
'prompt': 'How many Python files are in this project?',
'schema_name': 'file_stats',
})
if structured.exit_code != 0:
raise RuntimeError(structured.output)
print('structured output:', json.loads(structured.output)['object'])
session.close()Direct tools execute under the session workspace and should be treated as
privileged host operations. Most calls (read, glob, grep) are purely
deterministic; generate_object is the exception — it still calls the model
to fill a schema-validated JSON object, but you drive it explicitly rather than
through a free-form agent turn.
A runnable version ships at crates/code/sdk/node/examples/basic/test_generate_object.ts
(Python: crates/code/sdk/python/examples/test_generate_object.py).