The API token read every GET endpoint. It now reads only the scopes its user allows
(search, models, dashboard, activity, collections, tags, categories, authors, images,
model files, imports, everything else), set beside the MCP tools in a sectioned Token
permissions dialog. Existing and new tokens get every scope, so integrations such as
Homarr and holm keep working; a refused request names the scope it needs.
Follow-up to the review of the MCP endpoint, after merging develop.
Backend
- Instance-wide MCP access an admin sets (Off / Read only / Read and
change, default Read and change), at GET/POST /settings/mcp and logged
as mcp_access_changed. Off turns every token away at /api/mcp; Read
only offers and answers only the tools that read, refuses turning a
write tool on, and keeps a token's earlier write choices so they come
back with Read and change. GET /profile/api-token carries mcp_access
and only the tools it offers.
- Model links come from the public URL an admin saved, not the
PUBLIC_URL env var it's only seeded from.
- An import refuses a category that isn't the user's own, for POST
/import and import_model alike.
- add_to_collection inserts and logs only the models it actually adds.
- get_import_status refuses a task the web app started instead of
failing on its result.
- The migration is now 20261030120000_api_token_mcp_tools, after every
migration on develop and on open branches.
- Tests for each access level, the category check, a web app task id
and the logged count.
Frontend
- The token's MCP tools move off the profile into a Token permissions
dialog behind a cog beside the token, with MCP as its first section
so the REST API's permissions can join it later.
- Each tool is a row: a small CSS tile picturing what it does (moving
only while its row is hovered or focused, still for reduced motion,
greyed when denied), its name and description, and a Deny / Allow
Segmented control. Tools are grouped by whether they read or change
the library.
- Admin Auth page: an AI assistants (MCP) section to set the access.
- The connect command gets a copy button and is hidden while MCP is off.
- Regenerating says the token's tools go back to the read-only ones.
- Save errors go through toasts.
- Spanish and French strings for every MCP key, and the new keys in all
four languages.
Docs
- mcp.md and api-token.md: the Token permissions dialog, the admin
setting, the regenerate reset, and what an assistant reading scraped
descriptions could be talked into.
An AI assistant connects over Streamable HTTP (stateless) with the user's
API token and gets read-only tools: search_models, list_models,
get_model, list_collections, get_collection, list_tags,
list_categories, get_author and library_stats. Each runs as the token's
user through the same services as the REST API.
Every MCP message is a POST, which requireAuth refuses to an API token,
so /api/mcp checks the token itself and is mounted before the routers
that apply requireAuth to every path. The token stays read-only over
REST; over MCP it only reaches the tools its user ticked, stored per
token in User.apiTokenMcpTools. Existing tokens get the read-only tools;
a tool that writes would be off for every token until its user turns it
on.
get_model reads through printOutById rather than GET /print/:id, so an
assistant looking a model up doesn't count as a view or land in
Browsing History.