abstract_toolserver
The abstract_* ecosystem exposed as an API-callable AI toolset — every tool
is a plain Python function turned into a self-describing HTTP endpoint by
abstract_flask. A portable
tool layer any model (Claude, hugpy, …) can drive over HTTP instead of being
bound to one runtime's tool harness.
Run it
pip install abstract_toolserver # + the extras you want to expose
python -m abstract_toolserver # HOST/PORT/DEBUG from TOOLSERVER_* env
from abstract_toolserver import get_toolserver_app
app = get_toolserver_app() # a normal Flask/WSGI app
Self-describing surface
The app auto-mounts introspection endpoints (from abstract_flask):
| Endpoint | What it gives an LLM |
|---|---|
GET /prefixes |
the tool categories (/fs, /db, /ui, …) |
GET /endpoints |
every tool as {endpoint, url, methods} |
GET /<cat>/<tool>?help=true |
that tool's signature/help |
Call a tool with JSON; unknown keys are pruned to the function signature, and the
reply is {"result": ...} (or {"error": ...}). Discover-and-dispatch from a
client is already provided by abstract_apis.make_endpoint_call.
curl -s localhost:5000/fs/count_tokens -d '{"text":"hello world"}'
# {"result": 2}
curl -s localhost:5000/db/schema # {"result": {table: [cols...]}}
curl -s 'localhost:5000/db/query?help=true'
Tool categories
| Prefix | Tools | Backend |
|---|---|---|
/fs |
search, read_span, extract, read_file, write_file, read_json, find_keys, find_paths, glob, imports, find_content | abstract_search, abstract_utilities, abstract_paths |
/text |
count_tokens, chunk, detect_language | abstract_utilities |
/web |
text, links, attributes | abstract_webtools |
/media |
ocr_image, pdf_text, summarize, keywords, transcribe | abstract_ocr, abstract_pandas, media_intelligence |
/ai |
query | abstract_ai |
/db |
tables, schema, columns, fetch, query | abstract_database |
/sys |
run_cmd | stdlib (gated) |
/ui |
capture, monitors, ocr, windows, click_verify | abstract_clicks, abstract_windows |
/browser |
shot, go, locate, click, type, key, scroll, read, console_save — Firefox in a libvirt guest driven by screen only (vm=, default ubuntu-desktop) |
abstract_clicks (backends.qemu, vision, macros.firefox) + /vl fleet |
/loci |
list, pointers (the hugpy-station distribution feed), register, archive | central locus registry (Postgres) |
/handoff |
request, list, claim, station (seat-API probe) | jump-in seats via hugpy-station |
/session |
identity, pull, spin, list, release | pull a live Claude Code session into a hugpy-station seat |
/instructions |
tree, read, add | composition guides plus create-only caller contributions |
Operating instructions
Tool schemas describe individual calls; the built-in instruction tree documents how calls compose into repeatable workflows:
GET /tools/toolserver/— MCP configuration, discovery, runtime-neutral channel comms, and optional runtime-specific wake-up adapters.GET /instructions/ae/solcatcher/— Solcatcher-specific entry point.GET /instructions/a-brain/alpha/— Alpha's capability-channel instructions.- MCP:
instructions_tree, theninstructions_read, throughts_call.
Authenticated MCP callers may create a new document with instructions_add at
an instructions/... path. Creation is durable and immediately readable, but
MCP intentionally exposes no update or delete tool. Those operations remain
local to server administration, and built-in documents are immutable.
Safety gates
Backends load lazily, so the server boots on a headless box and a missing backend errors only when its tool is called. Beyond that:
/db/query— read-only gate: rejects anything that isn't a singleSELECT/WITH, blocks stacked statements and data-modifying keywords. Use/db/fetch(identifier-composed, params-not-SQL) as the default read path./sys/run_cmd— disabled unlessTOOLSERVER_CMD_ALLOWLIST=ls,grep,…is set; only allowlisted binaries run./fs/read_file·/fs/write_file— local-only; the underlying SSH/remote kwargs are never exposed at the boundary./session/pull— the agent's own MCP bridge (abstract-claude mcp) fills the calling session's identity (session id, user@host, cwd); the toolserver registers machine + session loci, records apullhandoff, and asks the station (HANDOFF_SPAWN_URL) to seatclaude --resume <id> --fork-sessionthere. Without a resume-capable station it stays pending (/session/spinretries) — never a silent fresh seat. A pulled session carries its OWN name in the station (name=→ tmux seat and session locus; defaultsess-<id8>).- default loci — the station service user (
vm_mgr) and the host are always in the/loci/pointersdistribution: seeded once into the registry, re-merged into the feed even before the table exists (TOOLSERVER_DEFAULT_LOCI,TOOLSERVER_STATION_USER). /ui/click_verify— the click→observe→verify loop: locate (text or image template) → click → re-capture → report whether the screen (or aregion) changed. The half most tool APIs lack.
Authentication — the operator token is the ONLY gate
Every route requires TOOLSERVER_OPERATOR_TOKEN, sent as X-Operator-Token: <token>
or Authorization: Bearer <token> (the MCP bridge sends both). There is no IP
allow-list and no loopback bypass: a LAN, WireGuard or 127.0.0.1 caller without the
header gets 401 {"error":"unauthorized"} exactly like the public internet (operator
ruling 2026-09-29 — the nginx allow 192.168.x/deny all block that used to front
toolserver.hugpy.ai was a second, redundant gate and is gone). With the env var unset
the server fails closed (every gated route 401s; startup logs an error).
Open by design (they carry their own credential or expose nothing):
| Path | Why |
|---|---|
GET /healthz |
liveness probe, {"ok": true} only |
GET /endpoints?access=<TOOLSERVER_ENDPOINTS_TOKEN> |
read-only catalog capability for a browser link |
/ch/<id>?t=<token> |
shareable comms link — per-channel token (channels.py) |
/clients/heartbeat · /clients/work · /clients/result |
per-client token (clients.py) |
GET / /console /ui |
the static console page where the operator types the token (wsgi.py) |
TOOLSERVER_REQUIRE_TOKEN (the old opt-in blueprint gate) is deprecated: parsed,
logged as ignored, never enforced.
Configuration
| Env var | Purpose |
|---|---|
TOOLSERVER_OPERATOR_TOKEN |
required — the only access gate (see Authentication) |
TOOLSERVER_ENDPOINTS_TOKEN |
optional read-only capability for GET /endpoints?access= |
TOOLSERVER_HOST / TOOLSERVER_PORT / TOOLSERVER_DEBUG |
bind + debug |
TOOLSERVER_CMD_ALLOWLIST |
comma-separated binaries /sys/run_cmd may run |
SOLCATCHER_POSTGRESQL_* |
DB connection (via abstract_database) |
HANDOFF_SPAWN_URL / HANDOFF_SPAWN_TOKEN |
hugpy-station seat API (/api/handoff/spawn) + its X-Console-Token |
TOOLSERVER_DEFAULT_LOCI |
name=user@host[:port][|goal],… — loci every station inherits (default: vm_mgr + this login on this host) |
TOOLSERVER_STATION_USER |
station service user for the fallback default locus (default vm_mgr) |
canvas.* — per-locus ◳ design / flow documents (2026-09-03)
The station's ◳ canvas tab (⬚ design = wireframe.v1, ⋔ flow = flow.v1) and
every seat share ONE copy per (locus, kind) in the canvas table:
POST /canvas/get {locus, kind}→{state|null, rev, by, note, updated}POST /canvas/put {locus, kind, state, by?, note?, notify?}— whole document, validated fail-closed, stored verbatim; a flow'srevbumps on every changed put;notify=truealso posts a[canvas]high-priority request on the locus board (a deliberate hand-off — never for autosave).POST /canvas/list {locus?}→ which loci hold which kinds (no bodies).
Writes fire on the locus_change bus (table canvas, id = kind) so open
drawers reload live. Through abstract-claude mcp these are the Claude Code
tools canvas_get / canvas_put / canvas_list.
Metadata
Release files for abstract-toolserver 0.0.40
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| abstract_toolserver-0.0.40.tar.gz | 345.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| abstract_toolserver-0.0.40-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 628.0 kB
Release files / abstract_toolserver-0.0.40.tar.gz
| Download URL | abstract_toolserver-0.0.40.tar.gz |
|---|---|
| Size | 345.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7ca05f8c2a98fd4d746075cb090a56a1d44802e25321832bc094dfbb6e7431bf
|
|
BLAKE2b-256 checksum How to use checksums |
299e787786a4ad756301cae55e795d4895efb55d9b218fb848a9d8314b04c6e6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / abstract_toolserver-0.0.40-py3-none-any.whl
| Download URL | abstract_toolserver-0.0.40-py3-none-any.whl |
|---|---|
| Size | 282.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d8bb9f568acc82c4e0b064c2ab31584d56892c75e27019b4da101ab6e5fd5fdf
|
|
BLAKE2b-256 checksum How to use checksums |
0e33230c162793d94867ec42c89edc4ed64f1b5b830bf80b6d38c9350af3be4f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|