Two issues prevented the script from running cleanly when piped into
sudo (the most reliable invocation over SSH):
1. The header recommended `bash <(curl …)`, but with sudo the process
substitution opens /dev/fd/63 in the unprivileged parent shell and
the elevated bash cannot read it — the script aborted with
`bash: /dev/fd/63: No such file or directory`. Replace with the
`curl -fsSL … | sudo bash` pattern (and keep an explicit
download-then-run fallback). Also call out the broken sudo + process
substitution form so users do not retry it.
2. With stdin piped to bash, `gpg --dearmor` tried to write status to
/dev/tty and failed with `gpg: cannot open '/dev/tty'`, breaking the
keyring import (and silently leaving an empty keyring on a fresh
host). Pass `--no-tty --batch --yes` to both gpg calls.
Tested end-to-end on Ubuntu 26.04 ("resolute") via
`curl -fsSL …/install-chrome.sh | sudo bash` from a clean state
(no preexisting keyring or apt list): keyring valid OpenPGP file, deb
fetched and installed, `google-chrome --version` returns 147.0.7727.116.
- Flag <name>.ServeHTTP(<writer>, c.Request) calls without a nearby
LimitRequestBodyBytes so SDK-delegated handlers (MCP today, future
SDKs tomorrow) are held to the same body-cap contract as BindJSON.
- Also flag direct reads via io.ReadAll, io.LimitReader,
json.NewDecoder, xml.NewDecoder, and yaml.NewDecoder on c.Request.Body
Signed-off-by: Michael Mayer <michael@photoprism.app>
Set BUILDKIT_STEP_LOG_MAX_SIZE=5 MiB and BUILDKIT_STEP_LOG_MAX_SPEED=10 MiB/s
on both the local and remote ssh://arm multibuilder nodes so long apt-get
transcripts on unstable Ubuntu bases aren't truncated during preview builds.
- Rewrite package doc, struct comments, log line, and route-registration
comment so they describe the MCP server as a first-class feature
rather than a pre-release prototype. The "Usage" strings on
MCPCommands / MCPServeCommand were updated separately and stay
unchanged.
- Drop "in this prototype" from the edition-advisory warning emitted by
list_config_keys; the message now reads "edition filtering is
advisory; results come from the current <edition> build metadata".
- Rewrite internal/mcp/README.md: new "Scope" section with
in-scope/out-of-scope invariants, corrected authorization summary
(admin + API client roles + manager in Pro/Portal, anonymous in
public mode for the registered read-only tools), and a clearer
"Current Capabilities" block.
Signed-off-by: Michael Mayer <michael@photoprism.app>
- Drop the public-mode 403 short-circuit in internal/api/mcp.go. In
public mode api.Session() returns the default public session, so
Auth(c, acl.ResourceMCP, acl.ActionView) + s.Abort(c) authorize
anonymous callers naturally. This enables the prototype to run on
demo.photoprism.app and similar showcase installations without
loosening the auth path or adding another toggle.
- The exemption is only safe because every currently registered tool
returns static reference metadata derived from config.Flags and
form.Report(&form.SearchPhotos{}) — no database access, no per-user
state, no secrets, no mutations. A handler-level comment captures
that contract so a future tool that does not fit cannot slip in
unnoticed.
- Flip the public-mode test case (ForbiddenPublicMode ->
AllowedPublicMode) in internal/api/mcp_test.go to a full anonymous
initialize + notifications/initialized + tools/call round-trip
against both registered tools, so any future tightening of the
policy regresses loudly.
- Update internal/mcp/README.md: the Authorization bullets now
describe the public-mode path, the Rate Limiting note reflects that
CE/Plus have no generic throttle in public mode, and a new
"Extending the Tool Surface" section documents the contract every
new tool must uphold plus the three supported extension paths
(two-server factory, per-tool context checks, SDK middleware).
Signed-off-by: Michael Mayer <michael@photoprism.app>