Skip to content

Releases: torfeu/owui-mcp-spawner

v0.2.2

Choose a tag to compare

@torfeu torfeu released this 12 Sep 08:04

v0.2.2

18 new features and 39 fixes. The theme of this release is state that more than
one request touches at the same time: starting, stopping, saving and installing
now say what actually happened instead of what was hoped for.

Endpoints and reachability

  • One endpoint per category — every instance in a category behind a single
    MCP URL, so a client registers one connection instead of sixteen.
  • Instance endpoints on the manager port — /mcp/<id> without a second
    listener. Off by default: it makes a localhost-bound instance reachable
    from outside, and that door is opened by a person, not by an upgrade.
  • Both can answer on the manager port, on a port of their own, on both or on
    neither — one decision per way in. The category URL segment is a setting.

Operating the installation

  • Backup and restore of the manager state — configs, tool code, settings,
    identity policy — with or without credentials.
  • Health check that asks the MCP server whether it still replies, instead of
    asking the OS whether the process exists.
  • System monitor (CPU, memory, disk, network) plus a per-instance RAM
    column, sortable; status filter and pagination on the dashboard.
  • Test call from the dashboard — real code, real credentials, refused on a
    locked instance.
  • File storage: an instance that opts in gets content/<id>/, with download
    links that work from a chat client.
  • Agent identities — one token per calling agent, so Claude Code, Codex and
    a cron job stop being the same caller. Callers can be removed, not just
    forgotten: entry, rules and token.

For the small models on the other end

  • A wrong parameter name now says which one was meant — including the
    nearest real name, any missing required one, and what the tool actually takes.
    It sits in the runner, so every tool reached through this framework gets it.
  • Control tool v0.0.13 — list_instances answers in one line per instance
    with the full records behind detail=True; categories, agent identities and
    backup status are reachable at all now.
  • manager_url fills itself in instead of defaulting to an address that is only
    right on the machine it was written on.

Fixed

Most of these were found by review or by pointing the finished UI at a running
manager, not by the suite:

  • Concurrency: a confirmed stop that left a runner running; a stop that
    deregistered the runner replacing it; a start overtaken by a later one
    publishing its own dead pid; two creates of the same id both answering ok;
    a long save writing a minutes-old config back over newer changes.
  • Secrets: a list of plain secrets that was not masked at all; a masked
    value in a list filled back in by position; a backup taken without secrets
    that carried one anyway; keywords redacted because key is a substring
    of it.
  • Permissions: an explicit ban ignored where the default was open; removing
    a caller handing their access back through the signed role; a restore dropping
    roles and the global rules.
  • Honest answers: a failed restart reported as a completed one; a repeated
    failed install reporting success; a setting that could not take effect
    reported as saved; a restart warning claiming the old runner was still
    serving.
  • The suite itself: it wrote into the live settings file, counted its own
    calls into the live usage database, and deleted real files. Each is fixed and
    each has a guard now.
  • Copying to the clipboard never worked over plain HTTP — which is how this
    manager is normally reached.

Requirements

The runner is on the MCP SDK 2.x Server API and refuses to start on an
older line rather than failing later. A runner environment created before
this release may still hold MCP 1.x
— upgrading the manager does not upgrade
it. See docs/upgrading.md before restarting instances.

Verified on a fresh installation: Python 3.11, FastAPI 0.141.1, MCP SDK 2.2.0,
787 tests green. See docs/fresh-installation.md.
Full detail in CHANGELOG.md.

v0.2.1

Choose a tag to compare

@torfeu torfeu released this 29 Aug 06:40

New

  • Settings in tabs — eleven sections in one modal had outgrown the dialog. They are now grouped into Security (password, edit mode, MCP endpoint auth, read token, agent token), Identity, System (shared port, venvs, server) and Maintenance (usage tracking, update check). The panels stay in the DOM, so loading and Save Settings still reach every field no matter which tab is open — saving remains one action for the whole dialog. The chosen tab is remembered in localStorage (in a try/catch: in a private window the access itself throws)
  • Login lockout — three rejected credentials from one address cost 60 seconds, the next three 120, then 240, doubling without a ceiling; the API answers 429 with Retry-After and the login dialog shows a countdown instead of "wrong password". There is no login route to protect — the browser sends the password as a Bearer token on every request — so the counter sits in app/lockout.py where that decision is made, keyed by the connection's address and never by X-Forwarded-For, which a caller could write to dodge their own counter or run someone else's address into a block. A request carrying no credential is not counted (the UI asks before anyone has logged in, and a reload must not cost a strike), and neither is a configured token that merely lacks the scope for a route — that is a client at the wrong door, not a guess. Counters live in memory only: a manager restart clears every block, which is also how you get back in if you lock yourself out
  • Permissions dialog at scale — a search over all tools (with a "10 of 152" counter, blocks holding a hit open themselves), a search over the people list, and instance blocks collapsed from eight instances up, each header carrying its status (no access · all tools · 7 of 86). Filtering and collapsing only show and hide the rendered blocks, so a checkbox already ticked cannot get lost on the way

Fixed

  • A new venv installed an mcp the runner cannot use — the base packages were unpinned, so any venv created after the release of mcp 2.0 got the 2.x line, which rebuilt the low-level Server API; every instance in that venv died at startup with 'Server' object has no attribute 'list_tools'. Existing venvs keep the 1.x they installed long ago and show nothing, which is why this stayed invisible on a running installation — but a fresh install of v0.2.0 was broken out of the box, as was any newly created venv. mcp is now pinned below 2 in venv_manager.BASE_PACKAGES and in pyproject.toml, with a test that fails if the ceiling is dropped without porting the runner first
  • The permissions dialog painted over its own buttons — its grid items kept their automatic minimum height, so the columns grew with their content instead of scrolling, and the overflow was drawn across the Save/Close bar (which has no background of its own). Measured at 900×500 with 86 tools: 199 px of overlap, now a 20 px gap and a scrollbar where it belongs
  • A leftover directory took the venv list down — an interpreter migration parks the old venv next to the new one under a name like default.py312-migrated-20260816-001110. The listing asked venv_dir() about every directory it found, and the dot in that name raised out of the listing and took /api/venvs with it. Unnoticed since 16 Aug because the UI has a fallback and quietly showed only default. The listing now skips names this module could never have produced; venv_dir() stays strict, so using such a name still raises
  • A three-line instance name in the permissions header — the name stacked next to the dropdown in the narrow column; the dropdown has a fixed width now and the name min-width: 0

v0.2.0

Choose a tag to compare

@torfeu torfeu released this 27 Aug 17:13

This release is about who is calling. Until now the MCP Bearer token only said that a client may connect — a tool holding credentials of its own acted with the same rights for every user of an OpenWebUI installation. The spawner can now tell users apart and give each of them their own rights.

Identity & permissions

  • Per-user identity — the runner verifies the HS256 user token OpenWebUI forwards and publishes the caller for the duration of one tool call. Per instance: off (default, behaviour unchanged), optional, or required.
  • Users & permissions (🔑) — assign rights per person: no access, all tools, or single functions. Roles act as base equipment, a personal entry adds to them, an explicit deny wins. Rules are read per call, so saving takes effect immediately.
  • Machine identity — callers without an OpenWebUI login (agent CLIs, scripts) get an assigned identity, so required no longer locks them out.
  • Identity probe (examples/identity-probe.json) — a read-only whoami tool reporting who arrived, whether the identity was signed, and what the rules grant them. Every mistake in an identity setup is otherwise silent.
  • API tokens — a read-only token and an agent token, so a tool never has to carry the admin password.

Fewer tokens, more insight

  • MCP tool router (examples/mcp-tool-router.json) — one OpenWebUI connection with three meta-tools instead of one connection per instance. On a ten-instance installation: ~690 instead of ~11,600 tokens of schemas per request.
  • Usage statistics (📊) — a report across all instances and functions, plus GET /api/usage. Every call is recorded with time, instance and function — no arguments, no results, no caller addresses.
  • Info view — description, category, version, venv, status and the full function list per instance, without starting it. Also available as GET /api/instances/{id}/specs.

Housekeeping

  • Update check — optional, off by default. Asks the GitHub releases API once a day whether a newer version exists and shows a badge. Reports only; nothing is downloaded or installed.
  • Update hints for bundled tools — the dashboard flags when an installed copy of a shipped tool has fallen behind and applies the new one through the normal save path, keeping your Valve values. Forward only, refused on locked instances.
  • Declared categories — a tool can name its default category in its docstring.

Fixed

  • Uploaded tools kept OpenWebUI's truncated specs. An OWUI export often carries only the first line of each docstring, cut off mid-sentence, and sometimes misses parameters entirely. The upload path validated the code and had the correct schemas in hand — then discarded them. Fixed for new uploads; already installed tools are repaired once, automatically, on the next start.

v0.1.2

Choose a tag to compare

@torfeu torfeu released this 26 Jul 09:24

This release adds a shared MCP port, categories with search/filter, a read-only guest view, and hardens the MCP runner. It also contains everything from v0.1.1, which was never tagged.

Highlights
One port for all MCPs — a streaming reverse proxy serves every instance as /mcp/, so only the UI port and the shared port stay open.
Categories, search and filter — group instances with a free-text label; the dashboard filters over ID, name, description and category.
Guest mode — visitors see which tools exist and whether they run, but never ports, URLs or actions.
Control tool v0.0.6 — categories over MCP, plus documentation of what a locked instance refuses.
New
Shared MCP port — all instances can be served through a single port as /mcp/ via a streaming reverse proxy; instances then bind to 127.0.0.1 only, so exactly two ports stay open.
Categories — instances carry a free-text category set on upload, in the editor or in Edit; the dashboard shows it as a column and offers a category filter plus a search box over ID, name, description and category. The label lives in the MCP config only, so setting it never rewrites the tool JSON/code or restarts the instance.
Guest mode — with auth enabled, the login screen can be dismissed with Continue as guest; GET /api/instances and GET /api/instances/{id} then return only id, name, description, category, status and version, and the UI hides ports, URLs, venv and all actions.
Control tool v0.0.6 — upload_tool and create_tool accept manager categories; update_instance_category can change or clear them without modifying tool JSON/code or restarting the instance. The write actions now document the lock: code edit, config/values/dependencies/venv changes, restart, reinstall and delete state that a locked instance answers 403, and export_tool states that it needs code-read permission. The shipped specs are regenerated from the code, so the descriptions an agent sees can no longer drift from the docstrings.
Version badge — the spawner's own version is shown next to the title in the header.
Security
Only advertised tools are callable — the MCP runner checks the requested name against the tool list built from the Tools class instead of resolving it with getattr alone, which also exposed private helpers and inherited methods.
Subresource integrity for CDN assets — the CodeMirror <script>/ tags carry SRI hashes, so a compromised CDN cannot inject code into the admin UI.
CLI edit mode is locked — a mode set via --no-edit / --no-code-edit can no longer be lifted at runtime through PUT /api/settings. Modes set via the Settings page remain changeable as before.
Lock/unlock respects edit mode — both routes now require upload or full edit mode and return 403 under --no-edit.
Fixed
Dependencies starting with http were rejected — the package-spec validator matched the prefix http, so httpx, httpcore and friends failed validation; it now only rejects http:// and https:// URLs.
Bearer scheme is matched case-insensitively — per RFC 7235 a client sending authorization: bearer is no longer turned away by the MCP endpoint.
Stop never lies about the outcome — a runner that survives SIGTERM and SIGKILL is reported as still running instead of being marked stopped while the orphan keeps its port; killed children are reaped, and restart aborts when the stop failed instead of starting a second process on the same port.
Stop works during startup — the runner PID is registered as soon as the process spawns, so stopping an instance in its startup window actually kills it.
Port race on concurrent installs fixed — auto-assigned ports are held for the duration of the install, so two parallel uploads can no longer receive the same port; the shared proxy's port is reserved during allocation as well.
ID example is reserved — creating a tool with this ID used to overwrite the shipped configs/example.json and produce an instance that never showed up in the list.
Atomic file writes — configs, pids.json and settings.json are written via temp file + rename; a crash mid-write can no longer corrupt them.
Robust request validation — malformed JSON bodies return 400/422 instead of 500.
Settings save is change-aware — an unchanged edit mode is no longer persisted to settings.json (which used to freeze a CLI-flag default).
Readable error messages — upload and config-edit dialogs render structured 422 errors (e.g. a failed pip install) as text instead of [object Object].
Reinstall visible in readonly mode — matching the documented API behaviour (it only re-runs pip for the already-pinned dependency list).
Log panel handles auth errors — an expired session shows the login dialog instead of dumping a JSON error into the log view; GET /api/instances/{id} now includes the version field like the list endpoint.
MCP token hint — changing the MCP Bearer token in Settings notes that running instances keep the previous token until restarted.
Internal
API split into route modules — admin_server.py shrank from ~1200 to ~140 lines; the endpoints now live in app/routes/ with shared guards and serialization in app/api_helpers.py.
Frontend split into ES modules — web/app.js is a bootstrap; instances, config, upload, editor, logs and settings each own a module.
Faster instance list — one directory scan feeds states and enrichment, tool versions are cached by mtime, and the disk walk runs off the event loop.
Server-classified secrets — GET /api/instances/{id}/config ships a secret_fields list so the edit dialog no longer has to guess which values are credentials.
Test suite — tests/ covers the API contract, auth and guest exposure, locking, schema/package validation, port allocation, the control tool's specs, and a full e2e lifecycle over HTTP and MCP.

Also included since v0.1.0 (v0.1.1, never tagged)
Secrets survive config round-trips — PUT /api/instances/{id} ignores values equal to the ******** mask, so a client that echoes back a fetched config no longer overwrites real API keys.
Non-blocking delete — deleting a running instance stops it in a worker thread instead of blocking the event loop for up to ~5 s.
Locked instances can be stopped again — lock means "don't modify", but a misbehaving instance must always be stoppable.
Honest upload errors — an MCP config whose dependencies fail to install returns 422 instead of reporting success.
Validation in the instance venv — POST /api/tools/validate accepts an optional instance_id, so already-installed third-party imports no longer false-fail.
pip timeout raised to 600 s per package (was 120 s).
Control tool v0.0.5 — trailing-slash tolerance in manager_url, 600 s timeouts for install-heavy calls, optional instance_id for validate_tool_code.
Upgrading
No new dependencies and no config migration. Pull, restart the spawner, and restart your instances so they pick up the new runner — with the shared port enabled, running instances keep their public binding until they are restarted.

owui-mcp-spawner V. 0.1.0

Choose a tag to compare

@torfeu torfeu released this 20 Jun 11:14
  • Renamed to owui-mcp-spawner (formerly "MCP Framework" / "MCP Manager") — the focus is spawning OpenWebUI tools as isolated MCP servers. Environment variable names (MCP_MANAGER_PASSWORD, …) are unchanged for compatibility.
  • Per-instance virtual environments — every instance runs in its own venv under runtime/venvs//; dependencies are fully isolated and never pollute the spawner. Created on demand with base packages; validation, installs and runtime all use the instance venv. New app/venv_manager.py, MCPConfig.venv field, and a one-time migration of existing instances on first start.
  • Dependencies actually install from requirements: — the docstring requirements: line is parsed and installed before validation, so tools importing numpy/pandas/yfinance/… upload in one go (previously rejected with ModuleNotFoundError).
  • One-step create_tool — POST /api/tools/create and the editor's Install as MCP create an instance from raw Python in a single step (install → validate in venv → fill Valve values → save); no more placeholder + export + upload dance.
  • Valve values sync on code save — saving edited code merges new Valve defaults into the editable config (keeps user-set values, drops removed valves), so valves stay editable in the UI after a code change.
  • Safe delete — deleting an instance no longer removes a tool file still referenced by another instance (reference-counted); MCP-config uploads copy into an instance-owned file instead of sharing.
  • Venv management UI — dashboard Venv column, venv dropdowns in the upload/editor/edit dialogs, and a Settings section to create/delete venvs (in-use protection, default protected).
  • Port choice at install — optional fixed port on upload / create_tool; a clash returns 409 instead of being silently reassigned.
  • Control tool v0.0.4+ — new tools: create_tool, update_instance_values, update_instance_dependencies, update_instance_venv (value changes report a hint to ask before restarting).

MCP Framework v0.0.5 — First public release

Choose a tag to compare

@torfeu torfeu released this 06 Jun 16:09

What's included

Upload OpenWebUI tool JSONs and run them as MCP servers via Streamable HTTP
Web UI — start/stop/restart instances, view logs, edit configs
Settings page — manage password, edit mode, MCP Bearer Token and manager restart from the browser; persisted in runtime/settings.json
MCP endpoint authentication — optional Bearer token on all MCP endpoints (--mcp-token, --no-token-edit)
Edit modes — --no-code-edit (upload-only) and --no-edit (readonly), enforced at API and UI level
Improved schema generation — Literal[...] → enum, Field(description=...) and Args: docstrings → description, defaults → default
Manager authentication — SHA-256 hashed password via env var or Settings page
Built-in tool editor with Python syntax highlighting, validation and OpenWebUI JSON export
systemd deploy examples in deploy/