Skip to main content

majordomo

A command-line tool that reads Google Chat, reports task activity, and sends messages. The command line is the primary interface; an MCP server is a secondary interface for AI agents. Both are thin front doors over one shared core.

What it does

When someone creates a task through Google Chat's "Create a task for @Person (via Tasks)", that task cannot be retrieved through the Google Tasks API; the only durable signal is the chat message itself (see GOOGLE_CHAT_TASKS_LIMITATIONS.md). majordomo reconstructs task activity from those messages and reports who holds which tasks across spaces over a date range.

  • Tasks by assignee, space, and date; "assigned to me" and "assigned by me".
  • Spaces, people (participants with message and task counts), and raw messages by space or thread.
  • Three modes, one shape. The fast path reads an existing server-side cache of Chat (the data model). --live is up-to-dateness: it serves the cache and tops it up from the Chat API with anything newer. --nocache reads the Chat API directly and decodes tasks itself, so the tool also works without the cache. Every result is tagged with its source; an unforced read uses the cache and falls back to the direct API automatically when the cache is unreachable.
  • Send a message to a space, or a reply into a thread, as the logged-in account, with optional file attachments (majordomo send).
  • A privacy sieve in the core drops blocked spaces (and assignees) before any caller (CLI or MCP) can see them; it refuses sends into blocked spaces the same way.
  • Output as a rich console table, --json, or --csv.

Install

The simplest cross-platform install is from PyPI:

pip install majordomo                  # reads and sends through the Chat API
pip install "majordomo[bi,mcp]"        # plus the cache fast path and the MCP server

A plain install talks to Google directly and is complete on its own. The bi option adds the driver for reading an existing server-side cache of Chat, which is an accelerator for sites that already run that mirror.

With uv, uvx majordomo ... runs it without installing and uv tool install majordomo installs it permanently.

Homebrew (macOS or Linux):

brew tap overseers-desk/od
brew install majordomo

Python 3.11+.

Run directly, no virtualenv, with Ubuntu/Debian packages:

sudo apt-get install python3-typer python3-rich \
  python3-googleapi python3-google-auth python3-google-auth-oauthlib
# only if you read the server-side cache (--cache, --live) as well:
sudo apt-get install python3-pymysql

Then run from the repo without installing the package:

PYTHONPATH=src python3 -m majordomo spaces

Or install the package (puts majordomo on your PATH):

python3 -m venv .venv && . .venv/bin/activate
pip install -e ".[bi,mcp]"        # drop the options you don't need
majordomo --help

The MCP server (majordomo mcp) needs the mcp PyPI package, which is not in apt; install it through the virtualenv above (the mcp option).

Configuration

majordomo reads two files from ~/.config/majordomo/:

config.toml (hand-edited):

[me]
user_id = "users/1234567890"      # your Chat id, for --to-me / --by-me
                                   # (find it with `majordomo people`)

[sieve]
block_spaces = ["spaces/AAAA"]     # never shown through any front door
block_assignees = ["users/9999"]   # drop these assignees from every report

[api]                              # optional; defaults shown (OAuth for the API path)
token_file = "~/.config/majordomo/token.json"
client_file = "~/.config/majordomo/client_secret.json"

.env (the cache database connection, when using the cache path):

MYSQL_HOST=…
MYSQL_PORT=3306
MYSQL_USER=…
MYSQL_PASSWORD=…
MYSQL_DATABASE=…

Authenticating (per-account OAuth)

majordomo signs in as you, through Google's own browser consent screen. majordomo login opens that flow, the account you pick grants access to its own Chat, and the resulting refresh token is stored for that account. There is no service account and no domain-wide delegation. Two things follow:

  • Any account works, consumer @gmail.com or Google Workspace, as long as it can grant OAuth consent. No Workspace administrator has to enable anything first.
  • You see only what that account sees. The tool reads the signed-in user's own spaces and nothing beyond them.

The two alternatives were considered and set aside. A service account is a robot identity, not a person, so by itself it cannot read a given user's Chat. Domain-wide delegation, where a service account impersonates users, can read it, but only a Google Workspace administrator may authorize it and only for accounts inside that Workspace domain; that excludes consumer Gmail and any account you do not administer. Per-account OAuth costs one login per user and, in exchange, works for everyone.

majordomo login writes ~/.config/majordomo/token.json (the two Chat read scopes plus message create), used by --live (for the top-up), --nocache, and send. It needs a Desktop OAuth client with the Google Chat API enabled, saved as client_secret.json in the config directory. A token minted before send existed lacks its scope; send says so, and re-running majordomo login fixes it.

majordomo login

Commands

majordomo spaces
majordomo people --window year
majordomo tasks --to-me --window month
majordomo tasks --assignee-name '*Alice*' --since 2026-01-01
majordomo messages --space spaces/AAAA --window 7d
majordomo messages --thread spaces/AAAA/messages/BBBB
majordomo attachments --space spaces/AAAA --window 30d       # what files were posted
majordomo attachments --message spaces/AAAA/messages/BBBB --download ~/Downloads
majordomo send --space spaces/AAAA "On my way."
majordomo send --thread spaces/AAAA/messages/BBBB "Done, see the doc."
majordomo send --to alice@example.com "Lunch?"       # a person's existing 1:1 DM
majordomo send --space spaces/AAAA "Here it is." --attach ./report.pdf  # repeat --attach for several; text optional
majordomo mcp                       # run the MCP server (stdio)
  • Source: default cache with an automatic direct-API fallback. --cache forces the cache; --live adds a freshness top-up from the API; --nocache reads the API directly. attachments is the exception: files are read over the API always, the cache mirroring message text and not the files themselves.
  • Window: 7d | 30d | month | year | all, or --since / --until (ISO dates).
  • Output: default console, --json, or --csv.
  • attachments lists what was posted; add --download DIR to save each file into an existing directory under the name it was posted with, and the path written appears in the output. A file already at that name is left alone and named, so a download never clobbers.

Replay bounds: WORLD_AS_OF

WORLD_AS_OF is the office-wide replay bound (design: WORLD_AS_OF.design.md): an ISO-8601 timestamp with a timezone offset, exported into the environment by a replay harness so a run sees the world as it stood at that instant.

WORLD_AS_OF='2026-07-12T17:07:00+10:00' majordomo tasks --window 7d
  • Unset: normal operation, at no cost.
  • Set: nothing dated after the bound is reported, on every source (cache, --live, --nocache) and through both front doors (CLI and MCP). Relative windows anchor to the bound, not to now: 7d is the seven days before it, month the calendar month before the one containing it. A --until later than the bound is clamped down with a stderr note. Under a past bound --live degrades to the cache read (a top-up would fetch only what the bound excludes). The JSON/MCP envelope carries world_as_of, so a log proves the answer was bounded. send is refused while the bound is set: a bounded run is a replay, and a send would act in the real present.
  • Set but unparseable, or missing its timezone offset: a hard error on every command, including ones that fetch no dates, because a silently ignored bound would produce a contaminated run that looks valid.

The bound is honest about what it cannot rewind. Space and user display names are current-state (the mirror keeps no history of prior names) and the output says so. A message edited after the bound carries its post-edit text, marked edited_after_bound on the API path where the edit is observable. A bound older than the oldest cached message earns a warning that the store does not reach the as-of instant. For replaying the past the cache is the higher-fidelity source: the mirror retains messages the API has since dropped through deletion.

Not yet (deferred)

  • Task completion and stats. Google Chat does not reliably carry task completion, so every task is reported as open; completion-rate reporting waits on a later signal.
  • Directory name resolution. Names come from the chat message and the Chat API directly; a bare users/<id> with no name attached is shown as the id.

License

See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

majordomo-0.1.8.tar.gz (64.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

majordomo-0.1.8-py3-none-any.whl (51.1 kB view details)

Uploaded Python 3

File details

Details for the file majordomo-0.1.8.tar.gz.

File metadata

  • Download URL: majordomo-0.1.8.tar.gz
  • Upload date:
  • Size: 64.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.14

File hashes

Hashes for majordomo-0.1.8.tar.gz
Algorithm Hash digest
SHA256 ed327250d24f4b824d030f266f09f4ca653e51dcf0c56a97a7ad3f266ae71194
MD5 b11c11cb70da5ea327de614479c18a4a
BLAKE2b-256 dd4e0d143f1945e815dd8b6c5910c8156aff23efcd65e7bd51992ee0b7dcc679

See more details on using hashes here.

File details

Details for the file majordomo-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: majordomo-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 51.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.14

File hashes

Hashes for majordomo-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 585a0a53cb9c5822bb5b6315b6a2826fde77e3f8b48113bc5e2e992f413717d7
MD5 1db7640c2e3db486fba411c0c8552046
BLAKE2b-256 4faa5e0c65ca48aa3c596fe501eb761643f913e5041991a6e264901e198a3eef

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page