bro — plan

bro is Maciek Sitkowski's personal MCP server on Cloudflare Workers. It gives his agents, and agents he lets in, one place for notes, files and speech-to-text. MCP endpoint: https://api.sitkowski.dev/mcp (Streamable HTTP, MCP revision 2026-07-28; 2025-era clients are served statelessly).

Maciek's own agent (the Cloudflare Worker bro) also reads and writes his notes and transcribes his audio through a private Cloudflare service binding. That path has no public URL and cannot be reached from the internet; it runs the same code and limits as memo_write, memo_search and transcribe, and its notes show its own client name as source.

Tools

ToolInputResultWhat it does
memo_write{ text }{ id, created_at } Saves a text note (up to 20000 characters) exactly as written.
memo_search{ query, limit? }{ notes: [{ id, text, created_at, source }] } Finds notes, newest first. Every query word must match the start of a word in the note; if none match, the whole query is tried as a substring. An empty query lists the newest notes. source names the client that saved the note.
upload{ filename, source_url?, content_type? } { url, upload_url, expires_at, size } Puts a file (up to 20971520 bytes) under https://api.sitkowski.dev/files/, public by link. With source_url, bro copies that file. Without it, the result has a one-time upload_url: the caller's code sends the bytes there with one HTTP PUT within 15 minutes, so file contents never pass through the model.
transcribe{ url, language? }{ segments: [{ start, end, text }] } Fetches audio from the URL and turns speech into timestamped text with Cloudflare Workers AI (@cf/openai/whisper-large-v3-turbo). start and end are seconds. An upload link works as input.
access_list{}{ grants: [{ id, client, client_id, created_at, current }] } Lists every client Maciek has let in, newest first.
access_revoke{ grant_id }{ revoked: { id, client, … } } Takes back one client's access: its tokens stop working at once. Notes and files it wrote stay.

Every tool declares an output schema, and every successful result returns matching structuredContent. The text content mirrors it: the same JSON, except transcribe, which gives one [start] text line per segment so long audio stays small. Expected failures (bad URL, file too large, audio that cannot be read) come back as tool results with isError: true and a message that says what to change.

Sign-in

Away agent

The MEGA.dev course POSTs messages to https://api.sitkowski.dev/away. bro accepts only POSTs signed with the course's key (x-course-timestamp, x-course-signature), answers 2xx at once, reads which acts a message asks for with Workers AI, and replies on the message's reply URL. Free acts it does alone; gated acts wait up to 60 minutes for Maciek, who gets a phone ping (ntfy) with a link to approve or decline. The acts are records in D1, not calls to real systems.

ActWhat it doesPolicy
post_statuspost a status line to the team channelfree
ack_alertacknowledge an open alertfree
archive_reportmove a finished report into the archivefree
purge_cacheclear the build cachefree
release_holdrelease a held deploy to productiongated
rotate_keyrotate a shared API keygated

Data it keeps