Documentation Index
Fetch the complete documentation index at: https://docs.rkat.ai/llms.txt
Use this file to discover all available pages before exploring further.
For concepts, see Tools.
If you are new to the tools system, start with Enable built-in tools, then Register MCP servers, then come back to Custom tools (Rust SDK) when you need to extend the runtime directly.
Start here
This page is structured from common product usage to advanced extension work:
- enable built-in tools
- register MCP servers
- manage live MCP controls and tool scoping
- implement custom Rust tools when you need to extend the runtime directly
Built-in tool categories: builtins, shell, semantic memory, and mob orchestration. On the CLI, these are selected with tool presets. Other surfaces expose per-category booleans such as enable_builtins, enable_shell, enable_memory, and enable_mob.
CLI
JSON-RPC
REST
MCP
Python
TypeScript
Rust
# Enable builtins + shell
rkat run --tools workspace "List files in /tmp"
# Short flag form
rkat run -t workspace "List files in /tmp"
# Enable mob orchestration too
rkat run --tools full "Coordinate the team"
rkat run --yolo "Coordinate the team"
{
"jsonrpc": "2.0", "id": 1,
"method": "session/create",
"params": {
"prompt": "List files in /tmp",
"enable_builtins": true,
"enable_shell": true,
"enable_memory": true,
"enable_mob": true
}
}
curl -X POST http://localhost:8080/sessions \
-H "Content-Type: application/json" \
-d '{
"prompt": "List files in /tmp",
"enable_builtins": true,
"enable_shell": true,
"enable_memory": true,
"enable_mob": true
}'
{
"prompt": "List files in /tmp",
"enable_builtins": true,
"builtin_config": {
"enable_shell": true,
"shell_timeout_secs": 30
},
"enable_memory": true,
"enable_mob": true
}
Called via the meerkat_run tool.session = await client.create_session(
"List files in /tmp",
enable_builtins=True,
enable_shell=True,
enable_memory=True,
enable_mob=True,
)
const session = await client.createSession("List files in /tmp", {
enableBuiltins: true,
enableShell: true,
enableMemory: true,
enableMob: true,
});
let factory = AgentFactory::new(root)
.builtins(true)
.shell(true)
.memory(true)
.mob(true);
let service = build_ephemeral_service(factory, config, 64); // Queue-only embedded/testing substrate
let result = service.create_session(CreateSessionRequest {
model: "claude-sonnet-4-6".into(),
prompt: "List files in /tmp".into(),
render_metadata: None,
system_prompt: None,
max_tokens: None,
event_tx: None,
skill_references: None,
initial_turn: InitialTurnPolicy::RunImmediately,
deferred_prompt_policy: DeferredPromptPolicy::Discard,
build: None,
labels: None,
}).await?;
Register MCP servers
# Add a stdio server
rkat mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /tmp
# Add a streamable HTTP server
rkat mcp add remote-api --transport http --url https://mcp.example.com/api
# List all registered servers
rkat mcp list
rkat mcp list --scope project --json
# Get details for a specific server
rkat mcp get filesystem
# Remove a server
rkat mcp remove filesystem
Config is stored in .rkat/mcp.toml (project) or ~/.rkat/mcp.toml (user). Project scope wins on name collisions.
Each server supports an optional connect_timeout_secs field (default: 10 seconds) covering the entire connect + handshake + tool enumeration budget:
# .rkat/mcp.toml
[[servers]]
name = "slow-server"
command = "npx"
args = ["-y", "@my/slow-mcp-server"]
connect_timeout_secs = 30
Live MCP controls
Add, remove, or reload MCP servers on a running session without restarting the agent. Changes are staged and applied at the next turn boundary.
CLI rkat mcp add/remove/list/get manages server config (persisted to .rkat/mcp.toml or ~/.rkat/mcp.toml). The methods below manage servers on a running session — changes are staged and applied at the next turn boundary.
JSON-RPC
REST
MCP
Python
TypeScript
{
"jsonrpc": "2.0", "id": 2,
"method": "mcp/add",
"params": {
"session_id": "01936f8a-...",
"server_config": {
"name": "my-tools",
"command": "npx",
"args": ["-y", "@my/mcp-server"]
},
"persisted": false
}
}
{
"jsonrpc": "2.0", "id": 3,
"method": "mcp/remove",
"params": {
"session_id": "01936f8a-...",
"server_name": "my-tools",
"persisted": false
}
}
{
"jsonrpc": "2.0", "id": 4,
"method": "mcp/reload",
"params": {
"session_id": "01936f8a-...",
"server_name": "my-tools"
}
}
# Add
curl -X POST http://localhost:8080/sessions/01936f8a-.../mcp/add \
-H "Content-Type: application/json" \
-d '{
"server_config": {"name": "my-tools", "command": "npx", "args": ["-y", "@my/mcp-server"]},
"persisted": false
}'
# Remove
curl -X POST http://localhost:8080/sessions/01936f8a-.../mcp/remove \
-H "Content-Type: application/json" \
-d '{"server_name": "my-tools", "persisted": false}'
# Reload
curl -X POST http://localhost:8080/sessions/01936f8a-.../mcp/reload \
-H "Content-Type: application/json" \
-d '{"server_name": "my-tools"}'
// Reload a specific server
{"name": "meerkat_mcp_reload", "arguments": {
"session_id": "01936f8a-...",
"server_name": "my-tools",
"persisted": false
}}
// Reload all servers
{"name": "meerkat_mcp_reload", "arguments": {
"session_id": "01936f8a-...",
"persisted": false
}}
await client.mcp_add(
session_id="01936f8a-...",
server_config={"name": "my-tools", "command": "npx", "args": ["-y", "@my/mcp-server"]},
persisted=False,
)
await client.mcp_remove(session_id="01936f8a-...", server_name="my-tools", persisted=False)
await client.mcp_reload(session_id="01936f8a-...", server_name="my-tools")
await client.mcpAdd({
session_id: "01936f8a-...",
server_config: { name: "my-tools", command: "npx", args: ["-y", "@my/mcp-server"] },
persisted: false,
});
await client.mcpRemove({ session_id: "01936f8a-...", server_name: "my-tools", persisted: false });
await client.mcpReload({ session_id: "01936f8a-...", server_name: "my-tools" });
Set persisted: true to write the change to disk config so it survives restart.
Tool visibility can change during a session. Changes are staged and atomically applied at the turn boundary — the LLM never sees a tool list change mid-stream.
External filters. Allow-list or deny-list filters are persisted in session metadata and survive resume.
Per-turn overlays. Mob flow steps can restrict tools for a single turn via TurnToolOverlay. The overlay is ephemeral and cleared after the turn completes.
CLI
JSON-RPC
REST
MCP
Python
TypeScript
# Allow-list specific tools for this turn
rkat run --resume 01936f8a-... \
--allow-tool shell --allow-tool read_file \
--block-tool task_create \
"Execute the next step"
{
"jsonrpc": "2.0", "id": 5,
"method": "turn/start",
"params": {
"session_id": "01936f8a-...",
"prompt": "Execute the next step",
"flow_tool_overlay": {
"allowed_tools": ["shell", "read_file"],
"blocked_tools": ["task_create"]
}
}
}
curl -X POST http://localhost:8080/sessions/01936f8a-.../messages \
-H "Content-Type: application/json" \
-d '{
"session_id": "01936f8a-...",
"prompt": "Execute the next step",
"flow_tool_overlay": {
"allowed_tools": ["shell", "read_file"],
"blocked_tools": ["task_create"]
}
}'
{"name": "meerkat_resume", "arguments": {
"session_id": "01936f8a-...",
"prompt": "Continue with restricted tools",
"flow_tool_overlay": {
"allowed_tools": ["shell", "task_create"],
"blocked_tools": []
}
}}
result = await session.turn(
"Execute the next step",
flow_tool_overlay={
"allowed_tools": ["shell", "read_file"],
"blocked_tools": ["task_create"],
},
)
const result = await session.turn("Execute the next step", {
flowToolOverlay: {
allowedTools: ["shell", "read_file"],
blockedTools: ["task_create"],
},
});
Composition rule: most-restrictive wins. Multiple allow-lists intersect, multiple deny-lists union, and deny always beats allow. The conversation records a typed tool_config system notice when the tool set changes, and a tool_config_changed event is emitted to the event stream.
Implement AgentToolDispatcher to define your own tools. The agent sees them alongside built-in and MCP tools.
struct MathTools;
#[async_trait]
impl AgentToolDispatcher for MathTools {
fn tools(&self) -> Arc<[Arc<ToolDef>]> {
vec![Arc::new(ToolDef {
name: "add".to_string(),
description: "Add two numbers".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"}
},
"required": ["a", "b"]
}),
})].into()
}
async fn dispatch(&self, call: ToolCallView<'_>) -> Result<ToolDispatchOutcome, ToolError> {
match call.name {
"add" => {
let args: AddArgs = call.parse_args()
.map_err(|e| ToolError::invalid_arguments("add", e.to_string()))?;
Ok(ToolResult::new(call.id.to_string(), format!("{}", args.a + args.b), false).into())
}
_ => Err(ToolError::not_found(call.name)),
}
}
}
Next step