An MCP server that gives Claude tools to sync your Audible library to StoryGraph.
aud2sg exposes your Audible listening history and StoryGraph read shelf as tools that Claude can use. You can ask Claude to:
- Fetch your Audible library with completion data
- Search StoryGraph for books
- Mark books as read (with finish dates) on StoryGraph
- Bulk-import your Audible history into StoryGraph
| Tool | Description |
|---|---|
setup_audible |
Initialize the Audible client from a local auth file |
setup_storygraph |
Initialize the StoryGraph client from credentials stored in the system keychain. No parameters — credentials must be saved first via CLI (setup_storygraph_auth.py or make credentials) |
get_audible_library |
Fetch your Audible library. Returns slim list: asin, title, author, percent_complete, is_finished, finished_at. Params: min_percent (int, default 0), only_unfinished (bool, default false). Call get_audible_book for full detail. |
get_audible_book |
Fetch full detail for a single book by ASIN. Returns: asin, isbn, title, author, narrator, percent_complete, is_finished, finished_at, purchase_date |
get_storygraph_read_books |
Fetch all books marked as read on StoryGraph. Returns list of book_id, title, author, finished_on |
search_storygraph |
Search StoryGraph by title/author. Returns list of book_id, title, url. Param: best_match (bool) to return only the top canonical result |
get_storygraph_book |
Get a book's current status on StoryGraph. Returns book_id, title, status where status is "read", "currently-reading", "to-read", or null |
mark_storygraph_read |
Mark a book as read (with optional finish date) |
mark_storygraph_currently_reading |
Mark a book as currently reading |
- Python 3.11 or later — the code uses union type hints (
str | None, introduced in Python 3.10) and the declared minimum is 3.11+ - macOS, Linux, or Windows
- An Audible account (for Audible tools)
- A StoryGraph account (for StoryGraph tools)
make installThis creates .venv/ and installs all dependencies into it.
Run the interactive setup script. It prompts for your Amazon email and password using getpass so the password is never echoed or stored in shell history. It saves a token file to ~/.audible/auth.json:
.venv/bin/python setup_audible_auth.pySecurity:
~/.audible/auth.jsoncontains long-lived Amazon OAuth tokens with access to your full Amazon account — not just Audible. Keep it private. The file is written withchmod 600permissions. Never copy it into the repo directory.
Once done, tell Claude to call setup_audible (or it will auto-initialize on server start).
The default locale is us. To use a different locale, edit both setup_audible_auth.py and setup_credentials.py and change locale="us" to your region code (e.g., "uk", "de", "au"). Both files hardcode this value — if you use make credentials without changing setup_credentials.py, it will create a US-locale token regardless.
Tip: Instead of running steps 2 and 3 separately, you can run both in one go:
make credentialsThis runs
setup_credentials.py, which checks whether each credential is already set up and only prompts for what's missing. Safe to run multiple times.
Run the interactive setup script. It prompts for your credentials using getpass so the password is never echoed or stored in shell history:
.venv/bin/python setup_storygraph_auth.pyCredentials are stored in the OS keychain (macOS Keychain, Windows Credential Manager, or a Linux Secret Service backend — see Linux keyring note below). The server auto-initializes on start using these stored credentials.
Security: The
setup_storygraphMCP tool reads credentials from the keychain only — it does not accept a password as an argument. Passwords never pass through the LLM context window.
Copy .mcp.json.example to .mcp.json and replace the placeholder paths with the absolute paths on your machine:
cp .mcp.json.example .mcp.jsonThen edit .mcp.json. The command must point to the venv Python (which has all dependencies installed), not the system Python:
{
"mcpServers": {
"aud2sg": {
"command": "/Users/yourname/repos/aud2sg/.venv/bin/python",
"args": ["/Users/yourname/repos/aud2sg/server.py"]
}
}
}Use pwd in the repo directory to get the absolute path:
pwd
# /Users/yourname/repos/aud2sgNote:
.mcp.jsonis gitignored because it contains machine-specific absolute paths. Each user creates their own.
Place .mcp.json in the repo directory and run claude from that directory. Claude Code discovers .mcp.json automatically.
Claude Desktop uses a different config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS). Add the same mcpServers block to that file instead of using .mcp.json.
With Claude Code, you can confirm the server loaded by running /mcp and checking that aud2sg appears in the list. If it doesn't, check the MCP server logs — startup errors (e.g., missing auth file, keyring failure) are printed to stderr.
- Ask Claude to fetch your Audible library:
get_audible_library(min_percent=90) - Ask Claude to search StoryGraph for each book and collect IDs
- Ask Claude to write a local plan file (markdown table: title | finish date | StoryGraph ID) and review it
- Ask Claude to execute from the plan sequentially, one book at a time
Building a plan file first prevents duplicates if the session is interrupted and gives you a human-reviewable record before any writes happen.
make install # create .venv and install dependencies
make credentials # set up Audible and StoryGraph credentials (idempotent)
make test # run pytest
make lint # run ruff
make run # start the MCP server directlyThe StoryGraph client uses a single shared cloudscraper session. Date-setting POSTs follow a per-book edit link resolved from each book's own page — the session must complete each request before starting the next. Parallel calls can corrupt the session state and set dates on the wrong books. Always call one book at a time and wait for each response before the next.
Marking a book read twice creates a duplicate entry in StoryGraph that requires manual cleanup in the UI. The tool guards against this, but if you're unsure whether a book is already marked, call get_storygraph_book(book_id) first to check its status.
search_storygraph returns box sets, dramatized adaptations, and study guides alongside the canonical entry. Pick the result whose title most closely matches just the book title and author. Avoid entries containing: "Boxed Set", "Collection", "Dramatized Adaptation", "Summary", "Study Guide", or titles with "volume " followed by a number. Use best_match=True to have the tool do this automatically.
Trust is_finished as the authoritative completion signal. A book may show percent_complete: 0 but is_finished: true (Audible tracking inconsistency). Conversely, a book at 98% may have is_finished: false — do not assume completion from percent alone.
Some older Audible books have is_finished: true but finished_at: null. Mark these as read on StoryGraph without a date by calling mark_storygraph_read(book_id) with no finish_date.
StoryGraph tools say "not initialized" on startup The server logs the reason to stderr. Check your MCP host's server logs. Common causes: keyring unavailable, wrong stored password, or a network error during auto-login.
setup_storygraph / setup_storygraph_auth.py says "Login failed"
Verify your email and password by logging in at app.thestorygraph.com in a browser.
get_audible_library returns an empty list
Your Audible auth token may have expired. Re-run python setup_audible_auth.py to get a fresh token.
mark_storygraph_read returns "Failed to mark as read"
The StoryGraph session may have expired. The client automatically re-logs in when it detects a session expiry during a request — retry the operation once before intervening. If it continues to fail, call setup_storygraph again (or restart the server, which will auto-login from the keychain).
book_id validation error
StoryGraph book IDs look like some-title-by-author-abc123. If you see a validation error, the ID came from an unexpected source — re-run search_storygraph to get a fresh ID.
Linux keyring errors (NoKeyringError)
On Linux, keyring requires a Secret Service backend. Install one:
- GNOME:
sudo apt install gnome-keyring libsecret-1-0(usually already present on GNOME desktops) - Headless/CI:
pip install keyrings.alt(stores in a file; less secure — not recommended for production use)
MIT — see LICENSE