This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 0.5.0 instead.
tahuti: ManageBac CLI, Python SDK & Event Engine
An unopinionated, robust toolkit for ManageBac: typed Python SDK, command-line interface, Model Context Protocol (MCP) server for AI assistants, and a near-real-time event streaming and webhook engine.
Supports both international (managebac.com) and China (managebac.cn) instances.
Core Capabilities
- Unopinionated Python SDK (
ManageBacClient,ManageBacDaemon):- Authenticate seamlessly via credentials or saved session cookies (
ManageBacClient.from_config()). - Programmatic access to tasks, submissions, grades, calendar feeds, weekly timetables, and MNN notifications.
- Clean separation of concerns: produces pure, typed data with zero vendor-specific assumptions or hardcoded push rules.
- Authenticate seamlessly via credentials or saved session cookies (
- Interactive CLI (
tahuti login,tahuti list,tahuti view,tahuti grades,tahuti submit,tahuti daemon):- Fast terminal workflows for everyday student tasks: listing assignments, viewing details, uploading files, inspecting grades, checking schedules, and managing background daemons.
- Smart output formatting: human-friendly colored tables on interactive TTYs, structured JSON when piped to files or other tools (
jq).
- MCP Server for AI Coding Assistants:
- Built-in Model Context Protocol server (
tahuti-mcp) with 14 tools for AI assistants like Claude Desktop, Gemini, and Cursor to inspect deadlines, grades, and coursework.
- Built-in Model Context Protocol server (
- Event Streaming & Webhook Engine:
- In-process async event streaming (
async for event in daemon.stream()) for Python bots and background tasks. - Background daemon service (
tahuti daemon run --webhook-url ...) dispatching typedMBEventpayloads to HTTP webhooks with HMAC-SHA256 signatures, exponential backoff retries, stealth jitter, and active-hours scheduling. - For the full event contract and JSON schema, see Event Stream Specification. For operational push notification setups (such as Bark for iOS), see Downstream Notifier Guide.
- In-process async event streaming (
Delivery is polling-based, not push. No WebSocket or server-sent-event transport exists in
tahuti, and the MNN hub endpoint ManageBac publishes is an HTTPS origin rather than a socket URL — so there is nothing to subscribe to. The daemon polls the ManageBac Notification Network (MNN) Hub REST API on a configurable interval —poll_interval_seconds(default 30s) plus a randompoll_jitter_seconds(default 0-5s) — and emits events from the delta between successive polls. Expect latency of roughly one poll interval after an event appears on ManageBac. The hub endpoint attribute is scraped from the notifications page and used only to obtain the hub JWT and to derive the REST base URL; it is not a socket URL. See the notification transport findings for the full investigation.
Disclaimer
Use at your own risk. This tool is an unofficial, community-maintained client and scraper. It is not affiliated with or endorsed by Faria Education Group or ManageBac. By using this tool, you acknowledge and accept the following:
Faria/ManageBac's legal documents restrict automated access:
- robots.txt (managebac.com): Disallows
/login,/admin,/apifor all user agents. - Terms of Use §1.2.6: "Accounts registered by 'bots' or screen scrapers and/or other automated means are not permitted and access will be terminated without notice."
- Terms of Service §5.5: "Misuse of the Service, including but not limited to reverse engineering... may result in permanent and/or temporary suspension or termination of the School's account."
- Terms of Service §1.4: Violations may result in account termination without notice.
- Terms of Service §9.4: Schools exceeding 200 GB/month bandwidth may face caps or additional invoices.
The authors bear no responsibility for any consequences resulting from its use, including account suspension or school-level penalties. You are solely responsible for ensuring your use complies with your school's policies and ManageBac's Terms of Service.
Installation
pip install .
The console commands are tahuti and tahuti-mcp. The pre-rename mb and
mb-mcp are kept as aliases, so existing scripts, shell aliases and MCP client
configs keep working — this document uses the new names throughout.
The tahuti-mcp MCP server needs one extra dependency. mcp is not a runtime
dependency of the tahuti CLI, so a plain pip install . leaves tahuti-mcp failing
with ModuleNotFoundError: No module named 'mcp':
pip install "tahuti[mcp]"
Or install in editable mode for local development:
pip install -e .
pip install -e ".[mcp]"
This is also a uv project, with the test
dependencies kept out of the published runtime environment in a dev
dependency group:
uv sync --group dev # pytest + requests-mock + the mcp extra
uv run pytest
Python SDK Quickstarts
📖 Full Library Reference: For complete method signatures, parameter types, status enums, MNN Hub integration, and production recipes, see docs/library.md.
1. Basic Client Usage (ManageBacClient)
Use ManageBacClient for synchronous fetching and actions:
from mb_cli import ManageBacClient
# Option A: Authenticate automatically from saved local CLI credentials
client = ManageBacClient.from_config()
# Option B: Explicit authentication
# client = ManageBacClient(school="your-school", domain="managebac.com")
# client.login("student@example.com", "your-password")
# 1. Fetch upcoming tasks and coursework
tasks_data = client.crawl_all(fetch_details=True)
for task in tasks_data.get("upcoming", []):
print(f"[{task.get('due_date')}] {task.get('title')} ({task.get('class_name')})")
# 2. View one task in detail
task_detail = client.get_task_detail("/student/classes/1000024/core_tasks/1000025")
if task_detail:
print(task_detail.get("description"))
# 3. Check class grades and computed expected scores
grades = client.get_class_grades(class_id="1000023")
print(f"Expected Grade: {grades.get('expected_grade')}")
# 4. View calendar events
events = client.get_calendar_events(start="2026-09-01", end="2026-09-07")
# 5. Fetch weekly timetable
timetable = client.get_timetable()
# 6. Upload homework file to assignment dropbox
client.submit_file(
class_id="1000023",
task_id="1000026",
file_path="homework.pdf",
)
2. Async Event Streaming (ManageBacDaemon)
Use ManageBacDaemon.stream() to consume ManageBac events asynchronously in your Python application. Events are produced by polling on the interval you configure, so treat the first event after a change as arriving within one poll cycle rather than instantly:
import asyncio
from mb_cli import ManageBacClient, ManageBacDaemon
async def main():
# Load authenticated client
client = ManageBacClient.from_config()
# Create daemon instance (crawling runs in worker threads, non-blocking)
daemon = ManageBacDaemon(client, poll_interval_seconds=60)
print("Subscribed to ManageBac event stream (Ctrl+C to stop)...")
async for event in daemon.stream():
print(f"\n[Event: {event.event} @ {event.timestamp}]")
if event.event == "task_created":
print(f" 📝 New Task: {event.data.get('title')}")
print(f" Class: {event.data.get('class_name')}")
print(f" Due: {event.data.get('due_date')}")
print(f" URL: {event.data.get('url')}")
elif event.event == "deadline_approaching":
print(f" ⏰ Deadline Warning: {event.data.get('title')}")
print(f" Threshold: {event.data.get('reminder_threshold')}")
print(f" Remaining: {event.data.get('time_remaining_minutes')} mins")
elif event.event == "task_graded":
print(f" 📊 Grade Released: {event.data.get('title')}")
print(f" Score: {event.data.get('grade_letter')} {event.data.get('grade_score')}")
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\nDisconnected from event stream.")
Event Webhook Engine
tahuti includes a background daemon that dispatches polled events to HTTP webhook receivers (e.g. local scripts, microservices, or custom bots):
# Run daemon in foreground with webhook dispatching
# Pass the secret via the environment so it does not appear in `ps` output
# or your shell history.
export MB_WEBHOOK_SECRET="your-hmac-secret"
tahuti daemon run --webhook-url http://127.0.0.1:8000/webhook --secret "$MB_WEBHOOK_SECRET"
# Or configure webhook URL persistently and run daemon.
# `start` only detaches when you pass -b/--background; without it the loop runs
# in the foreground and dies with your terminal.
tahuti daemon configure-webhook http://127.0.0.1:8000/webhook
tahuti daemon start -b --interval 1800 --active-hours-start 7 --active-hours-end 23
# Test the webhook connection with a mock ping
tahuti daemon test-webhook http://127.0.0.1:8000/webhook
Verify the signature. The daemon signs every payload with HMAC-SHA256, but a receiver that ignores
X-MB-Signatureaccepts forged events from anything that can reach its port. The bundled receiver inextras/mb-notifier/requires--secret(orMB_WEBHOOK_SECRET) and refuses unsigned, replayed, or stale pushes. Write your receiver the same way.
Webhook HTTP Contract
-
Method:
POST -
Headers:
Content-Type: application/json; charset=utf-8User-Agent: tahuti-daemon/1.0X-MB-Event: <event_type>(e.g.task_created,task_graded)
-
Signing:
X-MB-Signature: sha256=<hex_hmac>(when--secretis configured).BREAKING PROTOCOL CHANGE. The HMAC covers the timestamp and the body:
signed_material = f"{X-MB-Timestamp}.".encode("utf-8") + request_body expected = "sha256=" + hmac.new(secret, signed_material, hashlib.sha256).hexdigest()
Previously it covered the body alone, which left
X-MB-Timestampunauthenticated: anyone who captured a single POST could replay it indefinitely by rewriting that header, because the original digest still validated and the receiver's freshness check passed. Receivers built against the body-only construction reject every payload until they add the timestamp to the signed material. The bundled receiver inextras/mb-notifier/is updated in the same commit; check any receiver of your own against the construction above. The.delimiter keepsts=17+body="89ab"from colliding withts=1789+body="ab". -
Retry Mechanism: Exponential backoff (
1s,2s,4s) on network or server errors (5xx, 408, 429). Other 4xx are permanent and are not retried. -
Specification: See docs/events.md for full payload schemas and documentation.
Latency and polling interval. Events are detected by polling, so a receiver sees a new task or grade after the daemon's next cycle, not at the moment ManageBac publishes it. Inside an active window the cycle sleeps
poll_interval_seconds + random(0, poll_jitter_seconds)(defaults 30s and 5s, so 30-35s); the jitter keeps request timing irregular rather than a fixed cadence.tahuti daemon runsets this with--poll-interval,tahuti daemon startwith--interval. Lower it if you want tighter detection, but each cycle issues authenticated requests to ManageBac — an aggressive interval raises the risk of rate limiting or account flagging. There is no push channel to subscribe to instead; see the notification transport findings in docs/events.md.Active hours gate the polling, they do not throttle it. With no
active_windowsconfigured the daemon polls around the clock. Set--active-hours-start/--active-hours-end(or anactive_windowsentry in the daemon JSON) and the loop sleeps outside the window instead of polling — useful for keeping a school-hours-only notifier from hammering ManageBac overnight.--onceignores the window: one cycle always runs.
Interactive CLI Reference
# Authentication & Session
tahuti login --school your-school --domain managebac.com -e student@example.com
tahuti --version # print the installed version and exit
tahuti logout
# Tasks & Coursework
tahuti list # list upcoming tasks
tahuti list --view past # past tasks
tahuti list --subject "Math" # filter by class/subject
tahuti list --view overdue --details # overdue tasks with full descriptions
tahuti view 1000025 # view single task by ID
tahuti view "https://your-school.managebac.com/student/classes/1000024/core_tasks/1000025"
# File Submission
tahuti submit 1000026 homework.pdf # upload file to assignment dropbox
# Submission Lifecycle
tahuti submissions 1000026 --list # list current submissions for a task
tahuti submissions 1000026 --add hw.pdf # upload to the task dropbox
tahuti submissions 1000026 --delete hw.pdf # delete a submission by asset ID or filename
tahuti submissions 1000026 --check-feedback # teacher feedback for a task
tahuti submissions 1000026 --check-feedback hw.pdf # …narrowed to one submission (asset ID or filename)
tahuti download 1000026 # download every attachment + submission for a task
tahuti download 1000026 --no-attachments --output-dir ./math # student submissions only
tahuti download 1000026 --pages 5 # search 5 pages server-side when the task is not in snapshot.json
tahuti feedback 1000026 # fetch teacher feedback for a submitted task
> **`tahuti download` reports what it wrote.** Every run ends in one payload:
> `downloaded` and `failed` lists with `downloaded_count` / `failed_count`, the
> resolved `output_dir`, and the task title. A partial failure is still a success
> — exit 0 as long as at least one file landed — and exit 1 means nothing landed,
> or the task could not be resolved at all (`task_not_found`, `no_task_link`,
> `detail_fetch_failed`). Without `--output-dir` the files land in
> `./task_<id>_<slug>/` under the current directory.
# Grades & Analytics
tahuti grades # grades for every enrolled class
tahuti grades --class-id 1000023 # detailed task grades for one class
tahuti grades --subject "Physics" # fuzzy match class name
tahuti count-grade-freq # grade distribution across all classes
# Notifications & Feed
tahuti notifications # list MNN notifications (page 1)
tahuti notifications --unread-only # unread only
tahuti notifications --read 235151424 # mark notification as read
tahuti notifications --unread 235151424 # mark it unread again
tahuti notifications --read-all # mark all notifications read
# Schedule & Calendar
tahuti calendar # calendar events for next 7 days
tahuti calendar --today # today's events
tahuti calendar --ical -o calendar.ics # export raw iCal feed
tahuti timetable # view weekly class timetable
# Background Daemon
tahuti daemon run --webhook-url http://127.0.0.1:8000/webhook # foreground loop, Ctrl+C to stop
tahuti daemon start -b # detached background loop (-b / --background)
tahuti daemon start # foreground loop; dies with your terminal
tahuti daemon start --once # run one check cycle and exit
tahuti daemon stop # stop background loop
tahuti daemon status # show daemon process status
tahuti daemon install # register an auto-start service (launchd/systemd)
tahuti daemon uninstall # remove the auto-start service
tahuti daemon configure-channel qq 123456789 # deliver via a zeroclaw channel instead of HTTP
tahuti daemon startdoes not background by default. Without-b/--backgroundthe polling loop runs in the foreground and terminates when your terminal closes.start -bre-executes itself astahuti daemon runin a new session, writes the child's PID to~/.config/tahuti/daemon.pid, and appends output to~/.config/tahuti/daemon.log(both0600). Override either location with--pid-file/--log-file:startandstatusaccept both,stopaccepts only--pid-file, andinstallaccepts only--log-file.
--intervaland--poll-intervalare the same flag.daemon starthistorically spelled it--intervalanddaemon run--poll-interval; both names are now accepted on both commands, so a command copied between the two keeps working. They share one destination, so passing both is not an error — the last one on the line wins.start -bforwards whichever you used to the detached child as--poll-interval.
--daemon-configis not--config. The daemon subcommands above read their webhook URL, interval, and active-hours window from a separate JSON file selected with--daemon-config— it is not the ManageBacconfig.jsonthat--configselects, and the two are not interchangeable.
Output Formatting
- Interactive TTY: Formatted tables with color highlights.
- Piped / Non-TTY: Structured JSON output.
- Explicit Override: Add
--format prettyor--format jsonto any command. - Environment Override: Set
MB_CLI_FORMAT=jsonorMB_CLI_FORMAT=prettyto pin the shape for a script that runs sometimes with and sometimes without a terminal (cron, CI,tee).--formatstill wins over the environment, and the environment wins over the TTY probe. - Streams: Standard output (
stdout) is reserved for command data; logs and progress go to standard error (stderr).
Configuration Files
By default, tahuti stores credentials and daemon states in ~/.config/tahuti/:
config.json— School domain, preferences, and webhook settingssession.json— Authenticated session cookies and tokenscreds.json— Plaintext ManageBac password, stored to allow silent re-loginsnapshot.json— Coursework state cache for delta detectiondaemon.log/daemon.pid— Background daemon runtime filescache/— Cached HTTP responses, including grade pages and the MNN hub JWTdaemon_state.json— Notification/reminder dedup state
Every file holding a credential or personal data is written with 0600 and the
directory with 0700. On startup tahuti warns on stderr if creds.json,
session.json, or config.json is found group- or world-readable, since file
permissions are the only barrier protecting a cleartext password. Set
MB_CRAWLER_NO_PERM_WARN=1 to silence it.
creds.jsonholds your password in cleartext. It is only written when a password login succeeds without--temp. Usetahuti login --tempfor a one-off session that is not persisted — it writes nothing to disk at all: no password, no session cookie, and no response cache (the cache holds grade pages and the hub JWT, so persisting it would have quietly defeated the flag).
tahuti logoutdeletescreds.jsonand any OS-keychain entry, as well as clearing the session cookie and the response cache. Pass--keep-credentialsif you want silent re-login preserved instead.To avoid the cleartext file entirely, opt into the OS keychain:
tahuti login --keychain # or: MB_CRAWLER_KEYCHAIN=1 tahuti loginThis stores the password in the macOS Keychain or Linux Secret Service via the
security/secret-toolhelpers already on the system — no extra dependency, and nothing is stored if you do not ask for it. If the keychain is unavailable (for example a headless Linux box with no secret service),tahutifalls back tocreds.jsonwith a warning rather than losing the credential. See SECURITY.md for the limits of both backends.
--config <file> and --session-file <file> override the default config and
session paths, as do the environment variables MB_CRAWLER_CONFIG,
MB_CRAWLER_SESSION, and MB_CRAWLER_CREDS_PATH. These come from the shared
auth-flag helper, so they exist on the task, grades, calendar, and submission
commands — and on daemon run / daemon start, which do log in to ManageBac.
They are not on the purely process-level daemon commands, which act on the
daemon and its own JSON settings rather than on your login. Where those need a
path they take --daemon-config (on run, stop, test-webhook,
configure-webhook, and configure-channel) and/or --pid-file / --log-file
(start and status take both, stop takes only --pid-file, install takes
only --log-file); uninstall takes no path flag at all.
Secrets may also be supplied through the environment, which keeps them out of
your shell history and out of ps output:
MB_WEBHOOK_SECRET— HMAC secret for signing webhook payloads. Preferred over--secretwhen both are set.MB_CRAWLER_PASSWORD— ManageBac password.MB_CRAWLER_COOKIE—_managebac_sessioncookie value.MB_CRAWLER_KEYCHAIN— set to1to store the password in the OS keychain instead of cleartextcreds.json(equivalent totahuti login --keychain).MB_CRAWLER_NO_PERM_WARN— set to1to silence the loose-permission warning.
MB_CRAWLER_PASSWORD and MB_CRAWLER_COOKIE are read back as input as well
as exported into the daemon child, so a non-interactive run needs no prompt:
MB_CRAWLER_PASSWORD=... tahuti daemon run # no prompt, secret not in argv
MB_CRAWLER_COOKIE=... tahuti list --format json # cookie straight from the env
An explicit --password / --cookie takes precedence over the environment, and
an exported-but-empty value is treated as unset. tahuti daemon start -b still
copies them into the detached child's environment so the secret never travels in
argv. The trade-off: a leaked environment variable is now directly usable as a
credential, and a process's environment is readable by its own user.
MCP Server (AI Coding Assistants)
tahuti includes a built-in Model Context Protocol (MCP) server for integration with Claude Desktop, Cursor, Gemini, and other AI agents:
tahuti-mcp
Example Claude Desktop Configuration
Add to claude_desktop_config.json:
{
"mcpServers": {
"managebac": {
"command": "tahuti-mcp"
}
}
}
The MCP server exposes 14 tools: list_tasks, view_task, submit_file, delete_submission, get_teacher_feedback, get_notifications, mark_notification, mark_all_notifications_read, get_calendar_events, get_ical_feed, get_timetable, list_classes, get_class_grades, and count_grade_frequencies.
Downstream Integrations
tahuti intentionally avoids coupling itself to specific push providers, notification line limits, or personal course naming conventions. Instead, downstream consumers subscribe to events and apply customized logic:
- Event Stream Specification: Full specification of the event data contract, lifecycle states, and JSON payloads.
- Downstream Notifier Guide: Operational guide for deploying
extras/mb-notifier(Bark push alerts, 3-field / 4-line mobile screen budgeting, course aliases, and sound customization).
Stability Note
This tool interfaces with ManageBac via automated HTTP requests and HTML parsing. If ManageBac updates its frontend layout, CSS selectors, or internal API structures, scrapers may require updates.
Changelog & Security
Release history follows Keep a Changelog in CHANGELOG.md. For reporting a vulnerability privately, see SECURITY.md — it also documents exactly which credentials are stored on disk and in what form.
License
MIT
Metadata
Release files for tahuti 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| tahuti-0.4.1.tar.gz | 365.1 kB | With provenance | Details for tahuti-0.4.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| tahuti-0.4.1-py3-none-any.whl | Python 3 | none | any | Details for tahuti-0.4.1-py3-none-any.whl |
Total release size: 518.2 kB
Release files / tahuti-0.4.1.tar.gz
| Download URL | tahuti-0.4.1.tar.gz |
|---|---|
| Size | 365.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f0631288486dfdac891a695fc5558d59104f06d8774cde5af0cf1cfe57b08314
|
|
BLAKE2b-256 checksum How to use checksums |
a52b5881ee3f6921a6dc60f91c11cf26615ae0dc0499848f34c313de39e434ee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / tahuti-0.4.1-py3-none-any.whl
| Download URL | tahuti-0.4.1-py3-none-any.whl |
|---|---|
| Size | 153.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
962079e518d690d219ca64570997c3cc926937cb36e9f87d422820e52c002c19
|
|
BLAKE2b-256 checksum How to use checksums |
4d68a558c9b4839e85c39faa1c4bf29334dafc02f9a76982040597ac282196e7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logSecurity for tahuti 0.4.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for tahuti-0.4.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.