{
  "markdown": "# segment-mcp\n\n<!-- mcp-name: io.github.katekruger/segment-mcp -->\n\n[![CI](https://github.com/katekruger/segment-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/katekruger/segment-mcp/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/segment-mcp.svg)](https://pypi.org/project/segment-mcp/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\n**A read-first MCP server for Twilio Segment.** Answers which destinations\nget which events, which sources are dead, and which are governed by\nnothing — the questions nobody can answer without clicking through forty\nscreens.\n\n## Read-only by default\n\n`SEGMENT_MCP_MODE` defaults to `read`, and every tool this server ships\ntoday is a read. Shipping with zero write tools is a feature, not a\nlimitation — see `BUILD-PLAN.md` §2. `write` and `admin` modes exist in\nthe tier model (`src/segment_mcp/modes.py`) for when gated writes land;\nright now there is nothing for them to unlock.\n\n## What this refuses to do — permanently, not \"for now\"\n\n`POST /regulations` and `POST /regulations/sources/{id}` — workspace-scoped,\nirreversible deletion or suppression of user data across every source —\nare **unreachable in every mode, with no configuration path to enable\nthem.** Three independent things enforce this: the mode-authorization\nlayer refuses it before even checking the current mode, the API client\nrefuses to send the request before it reaches the network, and no tool\nthis server registers references it in any form.\n\nThis isn't a gate waiting for the right permission level. It's a line,\nbecause these endpoints accept an array of subjects and one malformed or\nhallucinated call can permanently delete thousands of profiles with no\nundo. Full reasoning: **[docs/what-this-refuses-to-do.md](docs/what-this-refuses-to-do.md)**.\n\n## Quick start\n\nRequires a Segment workspace on Team or Business tier and a Public API\ntoken (see [Prerequisites](#prerequisites) below).\n\n```bash\ngh repo clone katekruger/segment-mcp\ncd segment-mcp\nuv sync\ncp .env.example .env      # fill in SEGMENT_API_TOKEN and SEGMENT_REGION\nuv run segment-mcp\n```\n\nPoint an MCP client (Claude Desktop, Claude Code, etc.) at it over stdio.\nThe server refuses to start — loudly, with a clear message — if the\ntoken, region, or workspace tier isn't right; see\n[Startup checks](#startup-checks).\n\n## The five tools\n\nEach composes several Public API calls into one structured answer, not a\nraw endpoint dump:\n\n| Tool | Question it answers |\n|---|---|\n| `audit_event_routing` | Which destinations get which events? |\n| `trace_event` | Given an event name: where does it go, and is it governed by anything? |\n| `find_stale_sources` | Which sources have no recent data — dead instrumentation vs. simply new? |\n| `check_delivery_health` | Is this destination silently failing? |\n| `find_ungoverned_sources` | Which sources are governed by nothing, or allowing unplanned events through? |\n\n## Prerequisites\n\n- **Team or Business tier.** The Public API is not available on Free or\n  Add-on plans. There is no workaround, and the server's startup checks\n  fail with a clear message rather than a raw 403 if your workspace\n  doesn't qualify.\n- **A Public API token.** Only a Workspace Owner can mint one: Segment App\n  → Workspace Settings → Access Management → Tokens → Create Token →\n  Public API (not Config API).\n\n## Region configuration\n\n```\nSEGMENT_REGION=us   # or eu\n```\n\nThere is **no default** — you must set this explicitly. An EU workspace\nwhose API calls are pointed at the US endpoint doesn't error; it just\nsilently returns nothing, which is a far worse failure mode than a crash.\nThis server's startup checks call the API once with your configured\nregion and fail loudly if the token doesn't actually belong to it,\nnaming the region that does.\n\n## Startup checks\n\nAll fatal — the server refuses to start rather than fail confusingly on\nthe first tool call:\n\n1. `SEGMENT_REGION` is set and one of `us`/`eu`.\n2. `SEGMENT_API_TOKEN` is present and actually authenticates against that\n   region.\n3. The workspace's tier supports the Public API — a Free-tier workspace\n   gets a clear \"requires Team or Business tier\" message, not a raw 403.\n\n## Modes\n\n```\nSEGMENT_MCP_MODE = read (default) | write | admin\n```\n\n- **`read`** — every tool above. No mutation reachable, at any mode.\n- **`write`** — would add Tier 3 replace-semantics changes (none shipped\n  yet), each echoed back for confirmation before executing.\n- **`admin`** — would add Tier 2 deletes (none shipped yet), gated behind\n  a *typed* confirmation naming the exact resource — not just\n  `confirm=true`.\n\nSee `src/segment_mcp/modes.py` for the full tier model and\n[docs/what-this-refuses-to-do.md](docs/what-this-refuses-to-do.md) for\nwhat stays out of scope regardless of mode.\n\n## Profile API — a separate, higher trust tier\n\nThe Profile API returns PII on named individuals — traits, external IDs,\nevent history, and identity links for a specific person. This is the most\nprivacy-sensitive read anywhere in this server's surface, so it is walled\noff from everything else:\n\n- **A separate credential**, `SEGMENT_PROFILE_TOKEN` — never the main\n  `SEGMENT_API_TOKEN`. Also requires `SEGMENT_PROFILE_SPACE_ID` (your\n  Unify Space ID, not your workspace ID).\n- **Explicit opt-in.** If `SEGMENT_PROFILE_TOKEN` is unset, no profile\n  tool is registered — the capability doesn't exist for that server\n  instance.\n- **Every lookup is logged** — collection, id_type, and the caller —\n  before the request is even sent, via `client/profile_api.py`'s\n  `segment_mcp.profile_api` logger. The log records *that* a lookup\n  happened and *which* profile, as a truncated SHA-256 digest of the\n  lookup key, never the raw identifier — this client never logs the raw\n  identifier itself, and it silences `httpx`'s own request-URL log\n  process-wide at construction so the identifier doesn't leak that way\n  either, since the Profile API puts it in the URL path. The digest\n  cannot be reversed back to the identifier, but repeated lookups of the\n  same profile are still correlatable across log lines for auditing.\n- **Lookups are case-sensitive.** The wrong case returns an empty result,\n  not an error — this client lowercases every lookup value at its\n  boundary and logs a warning when it had to.\n\nNo profile-lookup MCP tool is wired into `server.py` yet — this is the\nclient and trust-boundary machinery a future tool will be built on, per\n`BUILD-PLAN.md`'s v0.2 scope.\n\n## Development\n\n```bash\nuv sync\nuv run pre-commit install\nuv run ruff check . && uv run ruff format --check . && uv run pyright && uv run pytest\n```\n\nSee `CONTRIBUTING.md` and `AGENTS.md`.\n\n## See also\n\nEvery project here shares one idea: a GTM system should refuse to act on data it cannot verify.\n\n[campaign-preflight](https://github.com/katekruger/campaign-preflight) — the same refusal to coerce missing evidence into a pass. `insufficient_data` is its own state in both.\n\n[pipeline-waterfall](https://github.com/katekruger/pipeline-waterfall) — downstream of this. Reconciles the bookings and pipeline waterfall, and fails the build rather than reporting a bridge that does not tie out.\n\n## License\n\nMIT — see `LICENSE`.\n",
  "bytes": 7218,
  "sha": "db1fa6dbaa832ed3c071713b54e1840bd1654736d07b4c8fd941f469c9cf25d0",
  "repo_slug": "katekruger/segment-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_katekruger_segment_mcp_0e56b300/readme"
}