Documentation
SinkAgents and MCP
Updated
Sink runs an MCP server over HTTP so an agent can check captured mail while it runs tests. Every tool is body-blind: no tool ever returns the rendered or raw text of a message body. An agent sees metadata, recipients, headers, extracted links and attachment details, and can ask whether a body contains a string, getting back only yes or no and a count. People read full bodies in the inbox.
Ways in
Sink mounts the same tools at three paths:
| Path | Who connects | Tools |
|---|---|---|
/mcp |
A direct client holding a Sink MCP credential | All ten, including purge |
/mcp/read |
Delegated callers, such as Scalpels relaying for a signed-in team member | The nine read tools; purge is excluded |
/mcp/write |
Delegated callers allowed destructive actions | All ten |
/mcp/read is the path Sink advertises for delegated access.
Through Scalpels
If your agent already uses the Scalpels MCP server (see Use Scalpels from your agent) with the ability to call installed apps, it can list your team's MCP-enabled products, see Sink's tools and call them through Scalpels. Sink applies your role to each relayed call.
Directly
- Sign in to Sink as an administrator and open the installation credentials page at
/bfc/ui/credentials/installation. - Create a Bearer credential with the purpose
sink.mcp. Use one credential per MCP client. - Copy the secret straight into your agent's configuration. It is shown once.
Then point an HTTP MCP client at /mcp with the credential as a bearer token:
{
"mcpServers": {
"sink": {
"url": "https://your-sink-host/mcp",
"headers": {
"Authorization": "Bearer YOUR-SINK-MCP-CREDENTIAL"
}
}
}
}
An ingest credential (sink.ingest) is rejected here, and an MCP credential is rejected by /ingest. Keep them separate.
Filters
Most tools accept the same optional filters. Leave a filter out to ignore it.
| Filter | Matches |
|---|---|
app |
This exact app label |
subject |
This exact subject |
subject_contains |
Subjects containing this text |
recipient |
Messages with this exact recipient address |
since |
Received at or after this date or time |
until |
Received at or before this date or time |
stream |
This exact stream label, set by a sending app's SINK_STREAM |
The tools
| Tool | Arguments | Returns |
|---|---|---|
list_apps |
none | Each app label with its message count and most recent receipt time |
list_recent |
filters, limit (default 20, maximum 200) |
Newest messages first: id, app, subject, sender, To addresses, sent and received times, size, attachment names and count, link count, truncation |
count_messages |
filters | {"count": n} |
assert_count |
filters, expected (required) |
{"expected": n, "actual": n, "pass": true or false} |
recipients |
filters | Distinct recipient addresses with their kind: to, cc or bcc |
stats |
filters, group_by (required: app, subject or recipient_domain) |
Counts per group |
message_detail |
id |
Sender, all recipients, Message-ID, times, size, counts, truncation, every header, and each attachment's filename, MIME type and size |
links |
id |
The URLs extracted from the message |
body_matches |
id, pattern |
{"matches": true or false, "count": n} for a case-insensitive substring search across the text and HTML parts |
purge |
filters | Deletes the matching messages and returns how many |
purge refuses to run with no filters at all, so it cannot empty the whole installation. On the delegated /mcp/write path it is a two-step tool: a preview reports the scope and how many messages it would delete before anything is removed.
Writing reliable assertions
Sending is synchronous but parsing is not: a message is accepted first and its subject and recipients are filled in by a queued job a moment later. Poll rather than asserting once. A typical end-to-end check sends a message with a unique subject, then calls assert_count until it passes:
{
"subject": "Sink smoke 01K4...",
"recipient": "[email protected]",
"expected": 1
}
The check passes when the tool returns {"expected": 1, "actual": 1, "pass": true}.
A few more habits help:
- Scope every query with
appso mail from another application or test run does not change your counts. - Check
truncationinlist_recentormessage_detailbefore asserting on a body or attachments. Aheaders_onlymessage has no body to match. - Use
linksto pull a verification or password-reset URL out of a message without reading its body.