7.1 KiB
Assistant access (MCP)
Open Albums → Assistants (/dashboard/assistants) and create a connection.
The endpoint is https://ma.hadlock.tech/api/mcp, using Streamable HTTP. Configure
your MCP client to send Authorization: Bearer <token> on every request. Tokens
are displayed once, stored only as SHA-256 hashes, expire after 1–365 days, and
can be revoked in that screen. Keep them in the client's secret storage, never
in prompts or source control. This version uses bearer tokens, not OAuth discovery;
clients must support custom authorization headers. No public/anonymous access.
Permissions
Tokens default to read-only statistics/discovery permissions. Enable only the existing event, group, and platform permissions needed by that assistant. Selections are a ceiling: the same current memberships/roles used by the web UI are re-evaluated for every action. Selecting platform permissions never makes an ordinary account a platform administrator. Removing a role restricts existing tokens; revoking a token blocks new requests, not work already executing.
Private photo access is separately restricted by photos.private.read, including
when the token belongs to an owner/admin. Read-only tokens cannot call write tools
even if they include management permissions for reading associated data.
The explicit tool allowlist covers platform stats/settings/users/roles/allowances, groups and memberships, events and schedules, photos/submission/note moderation, guest lists and notification emails, invitations, exports, and banner/sign workflows. It does not expose sessions, token management, guest authentication, SQL, arbitrary HTTP requests, or shell commands. New application procedures require explicit review before being added to the MCP catalog.
Calling tools
Tool names use underscores, for example manager_stats and group_rename.
Existing application input goes under input. Dates are ISO-8601 strings.
{"name":"manager_stats","arguments":{"input":{"eventId":"EVENT_UUID"}}}
Every write requires confirm: true in addition to a write-enabled token and the
required permissions. This is an explicit API acknowledgement, not proof that a
human approved the action; configure your assistant to ask before destructive
actions or sending messages.
{"name":"group_rename","arguments":{"input":{"groupId":"GROUP_UUID","name":"Wedding team"},"confirm":true}}
Do not automatically retry writes after an ambiguous connection failure. Re-read
state first: mutations may have succeeded and are not universally idempotent.
List outputs are paginated with top-level offset and limit (default 50, max
100), returning items, total, and nextOffset. Application-specific filters
and page fields remain inside input. Some application queries impose their own
limits; total then reflects that result set rather than all database records.
Uploads still go directly to storage using signed URLs. MCP never proxies original files. Tool results may contain personal data or signed URLs according to the selected permissions—treat the connected assistant as a data recipient. Guest notes, names, and other content returned by tools are untrusted data, not instructions.
Calls have per-client and per-token rate limits, a 64 KiB request-body cap, origin validation, and no-cache responses. Tool audit entries record token/user ids, tool name and success, not raw arguments, credentials, or guest data. Existing mutation audit entries remain unchanged. No production token is created automatically.
Verification
MCP_INTEGRATION=1 bun --env-file=.env test apps/web/src/server/mcp/mcp.integration.test.ts
The integration test refuses non-local databases and does not send email.
Coolify health checks
Both Compose definitions use /api/health/ready for web readiness (bounded DB
probe) and a loopback-only worker /health endpoint on port 3001. The worker
checks DB connectivity and loop heartbeats; a loop stalled for 15 minutes is
unhealthy. Long exports can exceed this threshold and should be investigated.
/api/health remains process liveness. Responses omit internal dependency details.
Checks run every 30 seconds, with a 5-second timeout and three retries. No worker
health port is published. Docker health status by itself does not automatically
restart an unhealthy container; it supplies readiness information to Coolify.
Bulk gallery workflows
Use manager_photos with input.limit, input.offset, optional input.visibility and input.processingStatus to inspect a page. Capture explicit photo IDs; a bulk request never expands to later uploads. manager_previewBulkPhotos checks up to 100 IDs for an action (public, hidden, private, rejected, delete). Pass eligible IDs to manager_bulkPhotos with both input.confirm=true and the MCP write wrapper's confirm=true. Results are per photo, including skipped/failed items. Permissions are rechecked during execution. Deletion skips photos still uploading or processing, permanently removes originals and variants, and can partially succeed; inspect results before retrying.
For organizer uploads, manager_createGalleryPhotos accepts up to 25 file descriptors and returns presigned PUT URLs in input order. PUT each original directly to storage with its matching Content-Type, then call manager_completeGalleryPhotos with successful IDs to validate size and queue transcoding. These actions require existing settings.manage permission. Photos start pending review, even when guest publication is automatic. No file bytes pass through MCP or Next.js.
Both manager_bulkPhotos and manager_createGalleryPhotos require input.requestId (a UUID). Generate it once per intentional operation and reuse it with identical input after connection failures. Durable event/user-scoped receipts prevent duplicate creation and prevent an old bulk retry from undoing a newer action. A changed payload with the same request ID is rejected. For a new action, use a new UUID. Successful and skipped per-photo results are replayed; interrupted/failed transactions can retry. Permissions are always checked before replay. The receipts migration must run before deploying this code.
manager_retryGalleryPhoto resumes an organizer upload created by the same account. If uploadUrl is null, bytes already exist or processing has begun: do not PUT again. Call completion if its status is still uploading. Retrying a completed upload never returns a replacement PUT URL. The gallery UI keeps its batch ID and files for retries while the page remains open.
Full local verification
Start the local web server and Docker development services (including Mailpit), then run:
POLISH_INTEGRATION=1 GALLERY_STORAGE_INTEGRATION=1 MCP_INTEGRATION=1 EXPORT_INTEGRATION=1 EXPORT_PERMISSIONS_INTEGRATION=1 WEBHOOK_INTEGRATION=1 NOTIFICATIONS_INTEGRATION=1 PUBLISHING_INTEGRATION=1 INVITE_JOURNEY_INTEGRATION=1 bun --env-file=.env test
Use only local database/storage and non-production Mailpit. The MCP tests also exercise initialization and tool calls using the official SDK client over loopback HTTP. They do not configure an external assistant app or mint production tokens.