Skip to content
Scalpels home

Scalpels is officially launching on October 31, 2026. Special early adopter pricing is available now.

← All documentation

Documentation

Sink

Agents 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

  1. Sign in to Sink as an administrator and open the installation credentials page at /bfc/ui/credentials/installation.
  2. Create a Bearer credential with the purpose sink.mcp. Use one credential per MCP client.
  3. 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 app so mail from another application or test run does not change your counts.
  • Check truncation in list_recent or message_detail before asserting on a body or attachments. A headers_only message has no body to match.
  • Use links to pull a verification or password-reset URL out of a message without reading its body.