Skip to content

Extensions

The desks can be given more than their own tools: tools from MCP servers, your documents, hooks that react to events, and packages that bring any of these, including web apps.

MCP servers

mcp_servers.json uses the "mcpServers" format of Claude Code and Claude Desktop, over stdio or streamable HTTP. Each server gets only the environment variables listed for it. A server's tools start out deferred: only their names are in a desk's instructions, and the desk loads a tool's full description with tool_search and runs it with call_mcp_tool. This keeps the local models' smaller context free as servers are added.

MCP servers and tool search A desk finds an MCP tool with tool_search and calls it with call_mcp_tool; the client manager keeps one connection per server. The gateway A desk turn Claude, Qwen or Gemma: every backend reaches MCP the same way The prompt lists tool names only, so many servers fit Gemma's window: everything: echo, get-sum, … deepwiki: ask_wiki_question, … 1 tool_search returns the matching tools' descriptions and schemas 2 call_mcp_tool through the tool executor: journal, loop guard, redaction; output marked as outside data MCP client manager one connection per server, shared by every desk a failed server is retried after 30 s, 1 min, 5 min; the others carry on mcp_servers.json Claude Code's format, ${NAME} from .env everything (stdio) a program the gateway starts; it sees only HOME, PATH, USER … plus the env you list for it, never your tokens deepwiki (http) mcp.deepwiki.com/mcp a server on the internet a search, then a call going out the schemas, then the tool's output
A desk turn finds a tool with tool_search, then calls it with call_mcp_tool; the client manager keeps one connection per server, here a stdio server and an HTTP one.

bin/trading-agent mcp list shows each server's state and tools.

Untrusted text

Text from the web, the knowledge base, MCP servers and imported skills could contain instructions planted for an AI to follow. Such text is wrapped and labelled as data before a model reads it, and the files the desks read are scanned for phrases that look like instructions; the doctor and the importers warn about any they find.

Untrusted text, hooks and packages Text from the web, MCP servers and the knowledge base is labelled as data; hooks run as separate programs on events; a package brings skills, servers and hooks. an order is refused labelled hooks/ skills The web pages, searches, browser MCP servers output of call_mcp_tool Knowledge base shared/knowledge/ · FTS5 Untrusted-text label "data, not instructions", invisible characters removed; the injection scanner adds a warning when text tries to steer the model A desk turn reads the label and the warning; the risk engine checks orders regardless Event bus risk_decision Hook runner masked JSON, clean environment, 4 at a time, 30 s each Your hook scripts hooks/risk_rejected/log.sh A package used in place from shared/packages/ skills/ into every desk's index mcp_servers.json started with the gateway hooks/<event>/ run like your own agents/ · app.toml subagents and web apps packages install folder, zip or git URL outside text on its way to the model an event reaching your hooks
Text from the web, MCP servers and the knowledge base passes through the untrusted-text label before a desk reads it. Hooks run as separate programs on events from the event bus, and a package brings skills, servers and hooks together.

On 17 October 2026, Qwen ignored an order instruction planted in a web page and warned about it.

The knowledge base

Documents you put in workspace/shared/knowledge/, such as strategy write-ups, broker rules or exchange circulars, are indexed with SQLite's full-text search, and the desks search them with search_knowledge.

Hooks

An executable file in workspace/shared/hooks/<event>/, or in a package's hooks/<event>/, runs as a separate program each time that event happens. It gets the event as JSON on its standard input, the event's name in TRADING_AGENT_EVENT and the desk in TRADING_AGENT_DESK. The folder can be named after any event kind, such as order, risk_decision, kill_switch, session_started, desk_stopped or error, or one of two narrower names: order_placed and risk_rejected. A failing hook is logged and cannot stop a desk.

Packages

A package is one folder or git repository that holds any of skills/, agents/, mcp_servers.json, hooks/ and an app.toml. bin/trading-agent packages install <folder or git URL> copies it into workspace/shared/packages/ and shows what it brings, and its parts are used from there. No package code runs inside the gateway: its MCP servers, hooks and apps are separate programs.

Apps

An app.toml adds an existing web app to the web page's sidebar, without changing the app. It names the app's page and the systemd user unit that runs it.

[app]
title = "Sridhara"
description = "A Kite-style trading terminal on UBI: watchlists, charts, option chains and orders."
url = "http://127.0.0.1:8095/"
open_in = "frame"

[service]
unit = "sridhara.service"
unit_file = "%h/Projects/sridhara/services/sridhara.service"
Packages as apps A package app.toml adds an existing web app to the sidebar; the gateway checks its page and controls its systemd unit. trading-agent packages install shown in a frame page answers? start, stop,restart packages/sridhara app.toml page :8095, sridhara.service sridhara itself is unchanged Installed workspace/shared/ packages/sridhara App catalog in the gateway; reads each package's app.toml afresh no app code runs in here Sidebar: Apps Sridhara green when it answers sridhara port 8095, its own login systemd --user sridhara.service install, check or act what comes back to the page
The orange dots are the package being installed, the gateway checking that the app's page answers, and a restart sent to its systemd unit; the blue dots are the answers reaching the sidebar.

The gateway shows the app in a frame or a new tab, says whether its page answers, and can start, stop or restart its unit, but only units that installed packages name. bin/trading-agent apps enable <package> links and starts a unit that is not set up yet. The packages/sridhara package in this repository is the working example.