{"name":"io.github.unsoldgroup/mail-index","slug":"unsoldgroup-mail-index","title":"mail-index","description":"Local-first, read-only MCP server for recall over your Gmail: search, contacts, threads, digests.","url":"https://mcp.market/server/unsoldgroup-mail-index","rating":null,"grade":"A","score":91,"certified":false,"status":"active","category":"email","tags":["email","search"],"presence":{"score":31,"stars":4,"forks":1,"downloads_week":59,"last_push_at":"2026-09-14T14:39:23.000Z","license":"MIT"},"uptime":null,"claimed":false,"transport":"npm","callable_via_gateway":false,"default_price_micros":0,"repository":"https://github.com/unsoldgroup/mail-index","website":null,"version":"1.5.1","remotes":[],"packages":[{"registryType":"npm","identifier":"mail-index","version":"1.5.1","runtimeHint":"npx","transport":{"type":"stdio"},"runtimeArguments":[{"type":"named","name":"-y"},{"value":"mail-index","type":"named","name":"-p"},{"value":"mail-index-mcp","type":"positional"}]}],"tools":[{"name":"archive_message","description":"OPT-IN WRITE — MUTATES the mailbox. Archive one message (remove its INBOX label), given its <account:message-id> ref. Requires a gmail.modify grant; the default read-only install refuses with the exact re-auth command. Use only when the user explicitly asked to archive.","write_action":false,"price_micros":0,"input_schema":null},{"name":"cadence","description":"Inbound frequency per sender BRAND (registrable domain): volume, distinct senders, first/last seen, messages-per-month — over the whole index or since a time (30d, 1mo, ISO). Pass \"category\" to scope to one entity category you assigned via save_domain_category (e.g. all \"expedition-operator\" senders at once). Deterministic — use this instead of hand-writing SQL over senders.","write_action":false,"price_micros":0,"input_schema":null},{"name":"catch_up","description":"The \"what did I miss\" briefing since a time (30d, 2w, 12h, 1mo, or ISO): new mail from curated-important contacts, new replies in your threads, and interest-keyword hits. Compact rows + a body command handback. If the index is stale it returns now and spawns a background sync.","write_action":false,"price_micros":0,"input_schema":null},{"name":"digest_sources","description":"Newsletter/list senders ranked by engagement + interest, with unread/unsummarized issue counts — the digest routine worklist. Stale index returns now and spawns a background sync.","write_action":false,"price_micros":0,"input_schema":null},{"name":"domains_to_categorize","description":"PROPOSE domains with back-and-forth contacts (Correspondents) plus sample senders/subjects as context, so you can assign an entity category. Then save_domain_category.","write_action":false,"price_micros":0,"input_schema":null},{"name":"find_person","description":"Fuzzy contact resolution from a vague hint (name fragment, handle, or domain). Ranks Correspondents (people you have written to) first. The entry point for \"who was that contact from last spring?\".","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_contact","description":"One contact by address: stats, curation, recent threads. A near-miss returns ranked candidates rather than nothing.","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_message","description":"One message by <account:id>. level: \"summary\" (default — agent summary or snippet), \"body\" (distilled body; inline-enriches a still-meta row once, the only provider fetch), or \"meta\". Bodies are opt-in.","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_thread","description":"A thread by <account:thread-id>: metadata, its messages (compact, snippet-first), and the thread summary if present.","write_action":false,"price_micros":0,"input_schema":null},{"name":"graph_communities","description":"Detected social circles (Louvain communities) with top members by centrality. Returns a build command handback when no graph exists yet.","write_action":false,"price_micros":0,"input_schema":null},{"name":"graph_neighbors","description":"Co-recipiency neighbours of a contact, ranked by shared threads. On a miss, returns ranked near-miss contacts so the answer is never empty.","write_action":false,"price_micros":0,"input_schema":null},{"name":"interest_get","description":"Read back the curated interest profile: curated contacts/domains + keywords.","write_action":false,"price_micros":0,"input_schema":null},{"name":"interest_propose","description":"The curation SEED: a ranked shortlist of top contacts + domains by engagement, each with a suggested action (important/muted/none). Present it, take fuzzy edits, then interest_set.","write_action":false,"price_micros":0,"input_schema":null},{"name":"interest_set","description":"Persist the curation disposition: contact/domain curation labels (important/muted/blocked, or null to clear) and freeform interest keywords (replaces the set). This profile drives which bodies get enriched.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_contacts","description":"Ranked contacts. sort: engagement (default) | volume | recency | community. filter: \"correspondent\" (people you have written to) | important | muted | blocked.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_labeled","description":"Messages carrying a Gmail label, newest-first (label membership, not full-text). Use label \"INBOX\" to answer \"what is in my inbox right now\" — INBOX membership is reconciled on every sync, so it reflects the mailbox now (archived mail is dropped). \"UNREAD\" answers \"what is unread\", \"STARRED\" what is starred; any user label filters to it. Compact, snippet-first like search.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_threads","description":"Conversations by contact (address) OR by query (FTS). Compact thread shapes.","write_action":false,"price_micros":0,"input_schema":null},{"name":"modify_labels","description":"OPT-IN WRITE — MUTATES the mailbox. Add and/or remove Gmail labels on one message (system ids like STARRED/UNREAD, or existing user-label NAMES; creating new labels is not supported). Requires a gmail.modify grant; the default read-only install refuses with the exact re-auth command. Use only when the user explicitly asked to change labels.","write_action":false,"price_micros":0,"input_schema":null},{"name":"refresh_inbox","description":"Answer \"what is in my inbox right now\" with live-accurate membership. Reconciles INBOX against the live mailbox (archived mail drops, new inbox mail is pulled) THEN returns the current inbox — prefer this over list_labeled INBOX whenever freshness matters. One bounded provider round-trip (inbox-sized); degrades to the indexed inbox (refreshed=false) when no creds are wired.","write_action":false,"price_micros":0,"input_schema":null},{"name":"relay_menu_status","description":"Read-only menu/status payload for local MCP relay hosts: compact state, SF Symbol names, detail rows, and quick actions expressed as ordinary MCP tool calls. Safe for frequent polling; it never starts syncs.","write_action":false,"price_micros":0,"input_schema":null},{"name":"save_domain_category","description":"PERSIST an entity category you assigned to a domain (open vocabulary: client, vendor, travel operator, finance, publisher, …). Used as a filter/grouping axis.","write_action":false,"price_micros":0,"input_schema":null},{"name":"save_summary","description":"Persist your summary of a message or thread (provenance-marked, FTS-indexed). level: \"message\" (default) or \"thread\". For bulk/non-curated mail this makes the body eligible for demotion after a grace window.","write_action":false,"price_micros":0,"input_schema":null},{"name":"search","description":"Ranked fuzzy full-text recall over the indexed mail (subject/sender/snippet/body/summaries), porter-stemmed so word forms match (refund≈refunds). Snippet-first and compact (each row already has sender/subject/date) — open a full body with get_message only when you need details. Recall, not lookup: a half-remembered phrase still surfaces ranked neighbours. Use it for things like \"what did I buy / o","write_action":false,"price_micros":0,"input_schema":null},{"name":"setup_instructions","description":"SETUP MODE (no config yet). Return the exact steps + commands to finish onboarding mail-index, flagging the human steps (browser OAuth consent; manual install when Homebrew is unavailable). The server is advisory only — run `mail-index setup --account <email>` in your shell, then restart the server.","write_action":false,"price_micros":0,"input_schema":null},{"name":"setup_status","description":"SETUP MODE (no config yet). Report what is installed / authenticated / configured for mail-index — read-only observation (PATH + filesystem). Tells you whether onboarding is complete and the server can be restarted into full recall mode.","write_action":false,"price_micros":0,"input_schema":null},{"name":"sync_now","description":"Quick action for relay/menu hosts. Starts a detached incremental sync for one account, or all configured/indexed accounts when omitted, and returns immediately with the equivalent CLI command handback.","write_action":false,"price_micros":0,"input_schema":null},{"name":"sync_status","description":"Per-account freshness (index_as_of), whether a sync is running, message counts, and the meta/full/summary-only body-ladder split.","write_action":false,"price_micros":0,"input_schema":null},{"name":"trigger_rule_delete","description":"Delete a Trigger rule from this remote Deployment.","write_action":true,"price_micros":0,"input_schema":null},{"name":"trigger_rule_list","description":"List Trigger rules on this remote Deployment.","write_action":false,"price_micros":0,"input_schema":null},{"name":"trigger_rule_save","description":"Create or update a remote Deployment Trigger rule.","write_action":true,"price_micros":0,"input_schema":null},{"name":"webhook_consumer_delete","description":"Delete a signed-webhook consumer from this remote Deployment.","write_action":true,"price_micros":0,"input_schema":null},{"name":"webhook_consumer_register","description":"Register a signed-webhook consumer for Trigger rule deliveries.","write_action":false,"price_micros":0,"input_schema":null}],"scan":{"score":91,"grade":"A","scanned_at":"2026-09-24T05:06:14.921Z","report":{"scannerVersion":"0.1.9","scannedAt":"2026-09-24T05:06:14.891Z","components":{"code":{"score":25,"max":25,"notes":["64 source files scanned"]},"reliability":{"score":-1,"max":20,"notes":["no gateway calls yet and no remote to probe"]},"poisoning":{"score":-1,"max":15,"notes":["tools not inspected (local package is not executed); not counted"]},"auth":{"score":12,"max":15,"notes":["local package, no credentials required"]},"maintenance":{"score":15,"max":15,"notes":["last push 10 days ago"]},"identity":{"score":7,"max":10,"notes":["registry namespace matches repository owner"]}},"findings":[],"inputs":{"packages":[{"registryType":"npm","identifier":"mail-index","version":"1.5.1","found":true,"license":"MIT","hasInstallScripts":false,"dependencyCount":5,"publishedAt":"2026-08-05T10:27:15.870Z","repositoryUrl":"git+https://github.com/unsoldgroup/mail-index.git","weeklyDownloads":59}],"repo":{"found":true,"owner":"unsoldgroup","repo":"mail-index","archived":false,"pushedAt":"2026-09-14T14:39:23Z","stars":4,"forks":1,"openIssues":14,"ownerType":"Organization","ownerAvatarUrl":"https://avatars.githubusercontent.com/u/301315600?v=4","ownerCreatedAt":"2026-07-08T12:32:50Z","license":"MIT"},"icon":{"url":null,"source":"none"},"presence":{"stars":4,"forks":1,"downloadsWeek":59,"license":"MIT","lastPushAt":"2026-09-14T14:39:23.000Z","score":31}}}},"grade_history":[],"reviews":[]}