Skip to main content

SerenWorkbench

The tool surface an LLM reaches through.

One process on port 7425 serving an MCP endpoint, an operator dashboard and a small HTTP API. It is the one place a model connects: the standard Seren components — Memory, Loci, the Corpus Callosum, the Hippocampus and Lodestar — are passed through, so the Workbench offers each one's own tools exactly as that service defines them, with a switch per component (components: in the config, GET /components to see what each is offering). Builtins cover web search, time and the cluster. On top of those, you can add your own tools as YAML files — no Python, no restart.

Part of the Seren stack, but it doesn't require the rest of it. Point it at whichever services you actually run.


Install

pip install seren-workbench
python -m seren_workbench

That's a working server with the builtin tools and an empty tool directory. Configuration is optional — copy seren-workbench.yaml.sample to seren-workbench.yaml when you want to change ports, service URLs or which tools start enabled.

python -m seren_workbench --config /etc/seren/workbench.yaml --port 7425

Connect a client

The MCP endpoint is at /mcp/ (trailing slash — a bare /mcp gets a 307).

{
  "mcpServers": {
    "seren-workbench": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://127.0.0.1:7425/mcp/",
               "--transport", "http-only"]
    }
  }
}

Set a bearer token in the config (or SEREN_WORKBENCH_BEARER_TOKEN) before this leaves localhost - a bind beyond loopback with no token refuses to start and prints the ways out.

If Memory or Lodestar were installed with a token, tell the builtins what to present:

services:
  bearer_token_env: SEREN_CLUSTER_TOKEN        # one token for memory / lodestar / scheduler
  # memory_bearer_token_env: SEREN_MEMORY_TOKEN  # or per service

Adding your own tools

Drop a YAML file in tools_dir (default ~/seren-workbench/tools):

schema_version: 1
tools:
  - name: disk_free
    description: >
      Report free space on the Seren data volume. Use when someone asks
      whether there's room for another model.
    invoke:
      kind: process
      argv: ["df", "-h", "--", "{mount_point}"]
    parameters:
      - name: mount_point
        type: string
        required: true
        pattern: "/[A-Za-z0-9._/-]*"

Then reload — the tool is live immediately, no restart:

curl -X POST localhost:7425/tools/manifests/reload

Tools can run a process or make a web call. Parameters are typed, defaulted, range-checked and — for strings — matched against a pattern or an enum, with the rules published into the JSON schema so the model reads them rather than discovering them by being corrected.

→ docs/TOOL-MANIFESTS.md is the full reference. examples/tools/example-tool.yaml is an annotated file you can copy; the test suite loads it, so it can't quietly stop being true.

Tools the model asks for

A model can propose a tool for itself with propose_tool. The manifest lands in a staging directory that nothing loads — it's inert text until you read it and approve, at which point it moves into tools/ and goes live.

Open /viewer → Proposals; the tab shows a count when something's waiting. Or from the shell:

curl -s localhost:7425/proposals                     # what's waiting
curl -s localhost:7425/proposals/prop_a1b2c3d4e5     # the manifest + what it would run
curl -X POST localhost:7425/proposals/prop_a1b2c3d4e5/approve
curl -X POST localhost:7425/proposals/prop_a1b2c3d4e5/reject \
     -d '{"critique":"argv shells out to a script I cannot read"}'

Approving installs it switched off. The tool appears in your list, disabled; you turn it on separately from the Tool State tab. Two decisions — "this isn't malicious" and "I want it live now" — kept apart on purpose.

The model can propose and read its proposals. It cannot approve — there's no MCP tool behind any approval route, and a test asserts there never is. Reject requires a critique, which the proposer reads and can revise against; same loop as the memory consolidator's draft gate.

Turn it off with dashboard.proposals_enabled: false, which removes the tools entirely rather than leaving them to fail.

A model can ask for a whole MCP server the same way, with propose_plugin: an address and a reason. Nothing is dialled until you approve; then the server is plugged in and every tool it offers arrives switched off, for you to turn on one at a time. A proposal never carries a token — it can only name where one is kept on this box, and the review says in so many words that approving sends it.

→ docs/TOOL-PROPOSALS.md — what to look at when reviewing one.

What reload won't do

It reads files that are already on disk. It cannot author a tool, it refuses a manifest that would shadow a builtin, it won't re-enable something you switched off, and it treats a missing tools directory as "something's wrong" rather than "delete everything." A tool exists because a person put it somewhere.

Neither does a restart re-enable anything: toggles are remembered in <tools_dir>/.tool-state.json. A remote from: import may only hand you kind: web tools, and no tool may point back at the Workbench itself.


What a connected model can count on

  • The list it has is the list there is. The Workbench tells connected clients when its tools change (notifications/tools/list_changed): a component that came up late, a switch you flipped, a proposal you approved. A tool that is switched off is not offered at all, and appears the moment it is switched on.
  • A failure is an error. A builtin that could not do what was asked comes back as an MCP error, not as a successful result whose text is bad news.
  • It can read its own record. list_my_tool_calls is the audit log from the caller's side: which tools, when, whether they worked. Names and outcomes only — never an argument or a result.
  • A service it asks for stays up while it is using it. ensure_service_running takes a lease with Lodestar and release_service gives it back; the service is stopped when its last holder lets go. A hold a session forgot is let go by the Workbench (tools.ensure_service_running. hold_minutes, and at shutdown). GET /components shows what is held.

Snapshots

The tools folder — manifests, approved plugins, proposals, and the switches — is what a Workbench keeps, and it is snapshotted like the rest of the Seren family: daily by default, into backups/ beside the tools folder.

curl -s localhost:7425/stores                                  # what is kept, and the snapshots
curl -X POST localhost:7425/stores/snapshot -d '{"reason":"before I tidy up"}'
curl -X POST localhost:7425/stores/snapshots/<id>/rehearse     # a restore's dry run

Putting one back is two config keys and a restart, into an empty tools folder only — there is no route and no tool that restores:

backup:
  restore_from: /path/to/backups/seren-workbench/20261006-151256
  restore_reason: "moving to the new box"

Endpoints

GET / service info, tool counts and update status
GET /health liveness
GET /tools every tool with its schema
GET/POST /tools/state enable and disable, per tool or per action
GET /tools/manifests what loaded, what was skipped, and why
POST /tools/manifests/reload re-read the directory, apply it live
GET /components each component and plugin: on or off, reachable or why not; what is held
POST /components/state switch a component or plugin on or off
GET /proposals tools and plugins the model has asked for
GET /proposals/{id} one, with the full manifest and what it would run
POST /proposals/{id}/approve install it, switched off
POST /proposals/{id}/reject refuse it, with a critique
GET /config resolved config, secrets masked
GET /logs recent server logs
GET /stores what the Workbench keeps, and its snapshots
POST /stores/snapshot take a snapshot now
POST /stores/snapshots/{id}/rehearse prove one can be put back, without putting it back
/mcp/ the MCP streamable-HTTP transport
/viewer the operator dashboard

Update checking

GET / reports whether a newer seren-workbench has been published, alongside the tool counts:

{
  "service": "SerenWorkbench",
  "version": "1.2.0",
  "tools_count": 26,
  "updates": {
    "status": "ok",
    "installed": "1.2.0",
    "latest": "1.3.0",
    "update_available": true
  }
}

On by default: update checking is part of the core install (it lives in seren-meninges). A box that never leaves the LAN has no business calling PyPI; set enabled: false there. The knobs:

updates:
  enabled: true
  check_interval_hours: 6      # cached; never checked per-request
  allow_prerelease: false

The result is cached and the check never happens in the request path, so / stays fast. updates.status is always one of ok, disabled, unavailable (a broken install: the checker's dependencies are missing) or error — never absent, and never a silent "you're fine" when it couldn't actually check. SEREN_WORKBENCH_UPDATES_ENABLED=false turns it off without editing config.

The Workbench never upgrades itself. Applying an update is pip install -U seren-workbench and a restart — your service supervisor's job, not this process's. A running process can't reliably swap its own code out from under itself, so it doesn't pretend to.

Development

pip install -e ".[dev]"
pytest -q

License

AGPL-3.0-or-later.

Metadata

Release files for seren-workbench 5.0.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for seren-workbench 5.0.5
File Size Uploaded
seren_workbench-5.0.5.tar.gz 193.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for seren-workbench 5.0.5
File Interpreter ABI Platform
seren_workbench-5.0.5-py3-none-any.whl Python 3 none any Details

Total release size: 345.9 kB

Release files / seren_workbench-5.0.5.tar.gz

Download URL seren_workbench-5.0.5.tar.gz
Size 193.4 kB
Tags Source
SHA-256 checksum
How to use checksums
8629a5da56062433c5cfe488584787afb692295c1103d866d57534307921acc4
BLAKE2b-256 checksum
How to use checksums
e0e5b7fa52d6806e6af061ad39a77b062ee0cb8a12351e0f726b9534770a7c89
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / seren_workbench-5.0.5-py3-none-any.whl

Download URL seren_workbench-5.0.5-py3-none-any.whl
Size 152.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d801958f092ca08d85231e99cb087cf1b36ac88d969e3fed43dc2c5efd47b426
BLAKE2b-256 checksum
How to use checksums
527f4a15752f736d0a238cddb4feac03435c7777ee860f73223c4863198ed69c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release history Release notifications | RSS feed

5.0.6

2 release files

This release

5.0.5 This release

2 release files

5.0.1

2 release files

5.0.0

2 release files

2.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page