segment-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. segment_mcp-0.1.0/.editorconfig +21 -0
  2. segment_mcp-0.1.0/.env.example +25 -0
  3. segment_mcp-0.1.0/.gitattributes +8 -0
  4. segment_mcp-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +55 -0
  5. segment_mcp-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
  6. segment_mcp-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +29 -0
  7. segment_mcp-0.1.0/.github/dependabot.yml +23 -0
  8. segment_mcp-0.1.0/.github/pull_request_template.md +26 -0
  9. segment_mcp-0.1.0/.github/workflows/ci.yml +47 -0
  10. segment_mcp-0.1.0/.github/workflows/release.yml +134 -0
  11. segment_mcp-0.1.0/.github/workflows/zizmor.yml +37 -0
  12. segment_mcp-0.1.0/.gitignore +40 -0
  13. segment_mcp-0.1.0/.pre-commit-config.yaml +16 -0
  14. segment_mcp-0.1.0/AGENTS.md +85 -0
  15. segment_mcp-0.1.0/BUILD-PLAN.md +368 -0
  16. segment_mcp-0.1.0/CHANGELOG.md +158 -0
  17. segment_mcp-0.1.0/CLAUDE.md +1 -0
  18. segment_mcp-0.1.0/CODE_OF_CONDUCT.md +84 -0
  19. segment_mcp-0.1.0/CONTRIBUTING.md +53 -0
  20. segment_mcp-0.1.0/LICENSE +21 -0
  21. segment_mcp-0.1.0/PKG-INFO +175 -0
  22. segment_mcp-0.1.0/README.md +153 -0
  23. segment_mcp-0.1.0/SECURITY.md +30 -0
  24. segment_mcp-0.1.0/docs/decisions/0000-template.md +78 -0
  25. segment_mcp-0.1.0/docs/decisions/0001-record-architecture-decisions.md +90 -0
  26. segment_mcp-0.1.0/docs/decisions/0002-tier-1-permanently-unreachable.md +117 -0
  27. segment_mcp-0.1.0/docs/plans/going-public-checklist.md +55 -0
  28. segment_mcp-0.1.0/docs/what-this-refuses-to-do.md +111 -0
  29. segment_mcp-0.1.0/pyproject.toml +113 -0
  30. segment_mcp-0.1.0/src/segment_mcp/__init__.py +8 -0
  31. segment_mcp-0.1.0/src/segment_mcp/client/__init__.py +0 -0
  32. segment_mcp-0.1.0/src/segment_mcp/client/profile_api.py +269 -0
  33. segment_mcp-0.1.0/src/segment_mcp/client/public_api.py +547 -0
  34. segment_mcp-0.1.0/src/segment_mcp/client/regions.py +112 -0
  35. segment_mcp-0.1.0/src/segment_mcp/modes.py +222 -0
  36. segment_mcp-0.1.0/src/segment_mcp/py.typed +0 -0
  37. segment_mcp-0.1.0/src/segment_mcp/server.py +343 -0
  38. segment_mcp-0.1.0/src/segment_mcp/tools/__init__.py +0 -0
  39. segment_mcp-0.1.0/src/segment_mcp/tools/_shared.py +238 -0
  40. segment_mcp-0.1.0/src/segment_mcp/tools/governance.py +123 -0
  41. segment_mcp-0.1.0/src/segment_mcp/tools/health.py +313 -0
  42. segment_mcp-0.1.0/src/segment_mcp/tools/profiles.py +5 -0
  43. segment_mcp-0.1.0/src/segment_mcp/tools/routing.py +393 -0
  44. segment_mcp-0.1.0/tests/__init__.py +0 -0
  45. segment_mcp-0.1.0/tests/client/__init__.py +0 -0
  46. segment_mcp-0.1.0/tests/client/test_date_parsing.py +55 -0
  47. segment_mcp-0.1.0/tests/client/test_profile_api.py +215 -0
  48. segment_mcp-0.1.0/tests/client/test_public_api_client.py +265 -0
  49. segment_mcp-0.1.0/tests/client/test_rate_limiter.py +176 -0
  50. segment_mcp-0.1.0/tests/client/test_regions.py +81 -0
  51. segment_mcp-0.1.0/tests/fixtures/README.md +57 -0
  52. segment_mcp-0.1.0/tests/fixtures/__init__.py +0 -0
  53. segment_mcp-0.1.0/tests/fixtures/eu/free_tier_403.json +7 -0
  54. segment_mcp-0.1.0/tests/fixtures/eu/insufficient_permissions_403.json +7 -0
  55. segment_mcp-0.1.0/tests/fixtures/eu/malformed_response.json +5 -0
  56. segment_mcp-0.1.0/tests/fixtures/eu/profile/traits_200.json +8 -0
  57. segment_mcp-0.1.0/tests/fixtures/eu/profile/unauthorized_401.json +5 -0
  58. segment_mcp-0.1.0/tests/fixtures/eu/rate_limited_429_no_signal.json +9 -0
  59. segment_mcp-0.1.0/tests/fixtures/eu/rate_limited_429_with_retry_after.json +10 -0
  60. segment_mcp-0.1.0/tests/fixtures/eu/rate_limited_429_without_retry_after.json +14 -0
  61. segment_mcp-0.1.0/tests/fixtures/eu/source_no_connected_destinations_200.json +20 -0
  62. segment_mcp-0.1.0/tests/fixtures/eu/tools/connected_destinations_one_200.json +10 -0
  63. segment_mcp-0.1.0/tests/fixtures/eu/tools/connected_warehouses_one_200.json +10 -0
  64. segment_mcp-0.1.0/tests/fixtures/eu/tools/delivery_metrics_200.json +16 -0
  65. segment_mcp-0.1.0/tests/fixtures/eu/tools/destination_detail_200.json +15 -0
  66. segment_mcp-0.1.0/tests/fixtures/eu/tools/destination_subscriptions_403.json +7 -0
  67. segment_mcp-0.1.0/tests/fixtures/eu/tools/destination_subscriptions_one_200.json +18 -0
  68. segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_emitting_200.json +15 -0
  69. segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_empty_200.json +5 -0
  70. segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_historical_active_200.json +15 -0
  71. segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_many_emitters_200.json +20 -0
  72. segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_recent_mixed_200.json +16 -0
  73. segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_recent_zero_one_200.json +7 -0
  74. segment_mcp-0.1.0/tests/fixtures/eu/tools/source_settings_allow_200.json +30 -0
  75. segment_mcp-0.1.0/tests/fixtures/eu/tools/source_settings_block_200.json +30 -0
  76. segment_mcp-0.1.0/tests/fixtures/eu/tools/source_single_200.json +9 -0
  77. segment_mcp-0.1.0/tests/fixtures/eu/tools/source_single_block_200.json +9 -0
  78. segment_mcp-0.1.0/tests/fixtures/eu/tools/source_single_new_200.json +9 -0
  79. segment_mcp-0.1.0/tests/fixtures/eu/tools/sources_two_200.json +13 -0
  80. segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plan_rules_match_200.json +19 -0
  81. segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plan_rules_no_match_200.json +19 -0
  82. segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plan_sources_with_src1_200.json +10 -0
  83. segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plan_sources_with_src2_200.json +10 -0
  84. segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plans_empty_200.json +10 -0
  85. segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plans_one_200.json +10 -0
  86. segment_mcp-0.1.0/tests/fixtures/eu/tracking_plan_no_sources_200.json +20 -0
  87. segment_mcp-0.1.0/tests/fixtures/eu/unauthorized_401.json +7 -0
  88. segment_mcp-0.1.0/tests/fixtures/eu/workspaces_200.json +18 -0
  89. segment_mcp-0.1.0/tests/fixtures/http.py +71 -0
  90. segment_mcp-0.1.0/tests/fixtures/us/free_tier_403.json +7 -0
  91. segment_mcp-0.1.0/tests/fixtures/us/insufficient_permissions_403.json +7 -0
  92. segment_mcp-0.1.0/tests/fixtures/us/list_response_200.json +5 -0
  93. segment_mcp-0.1.0/tests/fixtures/us/malformed_response.json +5 -0
  94. segment_mcp-0.1.0/tests/fixtures/us/no_data_envelope_200.json +5 -0
  95. segment_mcp-0.1.0/tests/fixtures/us/profile/events_200.json +10 -0
  96. segment_mcp-0.1.0/tests/fixtures/us/profile/external_ids_200.json +10 -0
  97. segment_mcp-0.1.0/tests/fixtures/us/profile/links_200.json +8 -0
  98. segment_mcp-0.1.0/tests/fixtures/us/profile/malformed_200.json +5 -0
  99. segment_mcp-0.1.0/tests/fixtures/us/profile/metadata_200.json +12 -0
  100. segment_mcp-0.1.0/tests/fixtures/us/profile/not_found_404.json +5 -0
  101. segment_mcp-0.1.0/tests/fixtures/us/profile/rate_limited_429.json +5 -0
  102. segment_mcp-0.1.0/tests/fixtures/us/profile/traits_200.json +8 -0
  103. segment_mcp-0.1.0/tests/fixtures/us/profile/unauthorized_401.json +5 -0
  104. segment_mcp-0.1.0/tests/fixtures/us/rate_limited_429_no_signal.json +9 -0
  105. segment_mcp-0.1.0/tests/fixtures/us/rate_limited_429_with_retry_after.json +10 -0
  106. segment_mcp-0.1.0/tests/fixtures/us/rate_limited_429_without_retry_after.json +14 -0
  107. segment_mcp-0.1.0/tests/fixtures/us/source_no_connected_destinations_200.json +20 -0
  108. segment_mcp-0.1.0/tests/fixtures/us/sources_page1_200.json +18 -0
  109. segment_mcp-0.1.0/tests/fixtures/us/sources_page2_200.json +15 -0
  110. segment_mcp-0.1.0/tests/fixtures/us/tools/connected_destinations_one_200.json +10 -0
  111. segment_mcp-0.1.0/tests/fixtures/us/tools/connected_warehouses_one_200.json +10 -0
  112. segment_mcp-0.1.0/tests/fixtures/us/tools/delivery_metrics_200.json +16 -0
  113. segment_mcp-0.1.0/tests/fixtures/us/tools/destination_detail_200.json +15 -0
  114. segment_mcp-0.1.0/tests/fixtures/us/tools/destination_subscriptions_403.json +7 -0
  115. segment_mcp-0.1.0/tests/fixtures/us/tools/destination_subscriptions_one_200.json +18 -0
  116. segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_emitting_200.json +15 -0
  117. segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_empty_200.json +5 -0
  118. segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_historical_active_200.json +15 -0
  119. segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_many_emitters_200.json +20 -0
  120. segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_recent_mixed_200.json +16 -0
  121. segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_recent_zero_one_200.json +7 -0
  122. segment_mcp-0.1.0/tests/fixtures/us/tools/source_settings_allow_200.json +30 -0
  123. segment_mcp-0.1.0/tests/fixtures/us/tools/source_settings_block_200.json +30 -0
  124. segment_mcp-0.1.0/tests/fixtures/us/tools/source_single_200.json +9 -0
  125. segment_mcp-0.1.0/tests/fixtures/us/tools/source_single_block_200.json +9 -0
  126. segment_mcp-0.1.0/tests/fixtures/us/tools/source_single_new_200.json +9 -0
  127. segment_mcp-0.1.0/tests/fixtures/us/tools/sources_two_200.json +13 -0
  128. segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plan_rules_match_200.json +19 -0
  129. segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plan_rules_no_match_200.json +19 -0
  130. segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plan_sources_with_src1_200.json +10 -0
  131. segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plan_sources_with_src2_200.json +10 -0
  132. segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plans_empty_200.json +10 -0
  133. segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plans_one_200.json +10 -0
  134. segment_mcp-0.1.0/tests/fixtures/us/tracking_plan_no_sources_200.json +20 -0
  135. segment_mcp-0.1.0/tests/fixtures/us/unauthorized_401.json +7 -0
  136. segment_mcp-0.1.0/tests/fixtures/us/workspaces_200.json +18 -0
  137. segment_mcp-0.1.0/tests/test_modes.py +137 -0
  138. segment_mcp-0.1.0/tests/test_package.py +7 -0
  139. segment_mcp-0.1.0/tests/test_server.py +288 -0
  140. segment_mcp-0.1.0/tests/test_tier1_unreachable.py +99 -0
  141. segment_mcp-0.1.0/tests/tools/__init__.py +0 -0
  142. segment_mcp-0.1.0/tests/tools/_helpers.py +27 -0
  143. segment_mcp-0.1.0/tests/tools/test_governance.py +86 -0
  144. segment_mcp-0.1.0/tests/tools/test_health.py +160 -0
  145. segment_mcp-0.1.0/tests/tools/test_routing.py +204 -0
  146. segment_mcp-0.1.0/uv.lock +1077 -0
  147. segment_mcp-0.1.0/zizmor.yml +3 -0
@@ -0,0 +1,21 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+ indent_style = space
9
+ indent_size = 4
10
+
11
+ [*.py]
12
+ indent_size = 4
13
+
14
+ [*.{yml,yaml,json,toml}]
15
+ indent_size = 2
16
+
17
+ [*.md]
18
+ trim_trailing_whitespace = false
19
+
20
+ [Makefile]
21
+ indent_style = tab
@@ -0,0 +1,25 @@
1
+ # Public API token. Only a Workspace Owner can mint one:
2
+ # Segment App -> Workspace Settings -> Access Management -> Tokens ->
3
+ # Create Token -> Public API (not Config API). Requires Team or Business
4
+ # tier — the Public API is not available on Free or Add-on plans.
5
+ SEGMENT_API_TOKEN=
6
+
7
+ # Workspace region. Changes every base URL. An EU workspace token used
8
+ # against the US base URL fails silently (BUILD-PLAN.md §0.6) — get this
9
+ # right, don't leave it to default.
10
+ SEGMENT_REGION=us
11
+
12
+ # read (default) | write | admin. Tier 1 (regulation/deletion creation) is
13
+ # unreachable in every mode — see docs/decisions/0002-tier-1-permanently-unreachable.md.
14
+ SEGMENT_MCP_MODE=read
15
+
16
+ # Separate credential for the Profile API (v0.2+). Distinct trust tier from
17
+ # SEGMENT_API_TOKEN — returns PII on named individuals. Absent = no profile
18
+ # tool is registered at all. See README.md's Profile API section and
19
+ # client/profile_api.py.
20
+ SEGMENT_PROFILE_TOKEN=
21
+
22
+ # The Unify Space ID the Profile API looks up profiles in — not the same
23
+ # as your workspace ID. Find it in Segment App -> Unify -> Spaces -> (your
24
+ # space) -> Settings. Required only if SEGMENT_PROFILE_TOKEN is set.
25
+ SEGMENT_PROFILE_SPACE_ID=
@@ -0,0 +1,8 @@
1
+ * text=auto eol=lf
2
+
3
+ *.png binary
4
+ *.jpg binary
5
+ *.jpeg binary
6
+ *.gif binary
7
+ *.ico binary
8
+ *.pdf binary
@@ -0,0 +1,55 @@
1
+ name: Bug report
2
+ description: Something is broken or behaving incorrectly.
3
+ labels: ["bug"]
4
+ body:
5
+ - type: textarea
6
+ id: what-happened
7
+ attributes:
8
+ label: What happened?
9
+ description: A clear description of the bug, including what you expected instead.
10
+ validations:
11
+ required: true
12
+
13
+ - type: textarea
14
+ id: repro
15
+ attributes:
16
+ label: How to reproduce
17
+ description: Minimal steps to reproduce, including your `SEGMENT_MCP_MODE` and `SEGMENT_REGION` (redact your token).
18
+ validations:
19
+ required: true
20
+
21
+ - type: input
22
+ id: version
23
+ attributes:
24
+ label: segment-mcp version
25
+ placeholder: e.g. 0.1.0
26
+ validations:
27
+ required: true
28
+
29
+ - type: input
30
+ id: python-version
31
+ attributes:
32
+ label: Python version
33
+ placeholder: e.g. 3.12.4
34
+ validations:
35
+ required: true
36
+
37
+ - type: dropdown
38
+ id: region
39
+ attributes:
40
+ label: Workspace region
41
+ options:
42
+ - US
43
+ - EU
44
+ - Not sure / not applicable
45
+ validations:
46
+ required: false
47
+
48
+ - type: textarea
49
+ id: logs
50
+ attributes:
51
+ label: Relevant output / traceback
52
+ description: This will be rendered as a code block — do not paste your API token or any real Segment workspace data.
53
+ render: shell
54
+ validations:
55
+ required: false
@@ -0,0 +1 @@
1
+ blank_issues_enabled: false
@@ -0,0 +1,29 @@
1
+ name: Feature request
2
+ description: Propose something new — a tool, a composed question, a capability.
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: What problem does this solve?
9
+ description: What can't you answer today that this would enable? Prefer a concrete workspace scenario over an abstract capability.
10
+ validations:
11
+ required: true
12
+
13
+ - type: textarea
14
+ id: proposal
15
+ attributes:
16
+ label: Proposed approach
17
+ description: If you have one. It's fine to leave this blank and just describe the problem.
18
+ validations:
19
+ required: false
20
+
21
+ - type: checkboxes
22
+ id: checks
23
+ attributes:
24
+ label: Before submitting
25
+ options:
26
+ - label: I checked BUILD-PLAN.md to see if this is already scoped for a future version, deferred, or explicitly a non-goal.
27
+ required: true
28
+ - label: This is not a request to expose Tier 1 (regulation/deletion creation) — see docs/decisions/0002-tier-1-permanently-unreachable.md. Requests to relax that line will be closed.
29
+ required: true
@@ -0,0 +1,23 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ groups:
8
+ python-dependencies:
9
+ patterns:
10
+ - "*"
11
+ cooldown:
12
+ default-days: 7
13
+
14
+ - package-ecosystem: "github-actions"
15
+ directory: "/"
16
+ schedule:
17
+ interval: "weekly"
18
+ groups:
19
+ github-actions:
20
+ patterns:
21
+ - "*"
22
+ cooldown:
23
+ default-days: 7
@@ -0,0 +1,26 @@
1
+ ## What and why
2
+
3
+ <!-- What does this change, and what problem does it solve? -->
4
+
5
+ ## AI disclosure
6
+
7
+ <!-- Required. See CONTRIBUTING.md § Use of AI. -->
8
+
9
+ - AI-assisted: <!-- yes/no -->
10
+ - Model / harness: <!-- e.g. Claude Sonnet 5 via Claude Code -->
11
+ - What was and wasn't AI-generated:
12
+
13
+ ## New dependencies
14
+
15
+ <!-- One line of justification per new dependency, or "None". -->
16
+
17
+ ## Checklist
18
+
19
+ - [ ] `uv run ruff check . && uv run ruff format --check . && uv run pyright && uv run pytest` passes locally
20
+ - [ ] `CHANGELOG.md` has an entry under `## Unreleased`
21
+ - [ ] New behavior has a test, or this PR explains why not
22
+ - [ ] An ADR is included if this decision is expensive to reverse
23
+ - [ ] This PR does not add any path — in any mode — that reaches Tier 1 (regulation/deletion creation)
24
+ - [ ] Any new write tool is gated by `SEGMENT_MCP_MODE` and echoes the change back for confirmation before executing
25
+ - [ ] Region is resolved explicitly wherever this change makes an API call
26
+ - [ ] I reviewed the full diff myself before opening this PR
@@ -0,0 +1,47 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_call:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ # A real Python version matrix. This was a single job while the repo was
14
+ # private, since Actions minutes are metered there; the repo is public
15
+ # now, so there's real value in catching a version-specific regression
16
+ # instead of only ever testing 3.12. See docs/plans/going-public-checklist.md.
17
+ check:
18
+ name: lint, format, types, tests
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ python-version: ["3.12", "3.13"]
24
+ steps:
25
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
+ with:
27
+ persist-credentials: false
28
+
29
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
30
+ with:
31
+ enable-cache: true
32
+ python-version: ${{ matrix.python-version }}
33
+
34
+ - name: Install dependencies
35
+ run: uv sync --locked --all-groups
36
+
37
+ - name: Ruff check
38
+ run: uv run ruff check .
39
+
40
+ - name: Ruff format check
41
+ run: uv run ruff format --check .
42
+
43
+ - name: Pyright
44
+ run: uv run pyright
45
+
46
+ - name: Pytest
47
+ run: uv run pytest
@@ -0,0 +1,134 @@
1
+ name: Release
2
+
3
+ # Tag-triggered, on the parsedmarc pattern: verify the tag matches the
4
+ # package version -> verify CHANGELOG.md has a section for it -> run the
5
+ # full CI suite -> build -> publish to PyPI via Trusted Publishing (OIDC,
6
+ # no API token secret anywhere) into an environment that requires manual
7
+ # approval -> create the GitHub Release from the CHANGELOG section.
8
+ #
9
+ # Set up the PyPI Trusted Publisher BEFORE pushing the first tag -- PyPI
10
+ # needs to already trust this repo/workflow/environment combination, or the
11
+ # publish step has nothing to authenticate against. See
12
+ # docs/plans/going-public-checklist.md.
13
+
14
+ on:
15
+ push:
16
+ tags:
17
+ - "v*.*.*"
18
+
19
+ permissions:
20
+ contents: read
21
+
22
+ jobs:
23
+ verify:
24
+ name: verify tag matches version, CHANGELOG has a section
25
+ runs-on: ubuntu-latest
26
+ permissions:
27
+ contents: read
28
+ steps:
29
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
30
+ with:
31
+ persist-credentials: false
32
+
33
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
34
+ with:
35
+ # This workflow publishes release artifacts; a cache poisoned by
36
+ # an earlier run must never be trusted here, so caching is off
37
+ # for this workflow specifically (unlike ci.yml, which runs far
38
+ # more often and has more to gain from it).
39
+ enable-cache: false
40
+
41
+ - name: Verify tag matches package version
42
+ run: |
43
+ tag_version="${GITHUB_REF_NAME#v}"
44
+ pkg_version="$(uvx hatch version)"
45
+ if [ "$tag_version" != "$pkg_version" ]; then
46
+ echo "::error::tag $GITHUB_REF_NAME (version $tag_version) does not match" \
47
+ "package version $pkg_version (src/segment_mcp/__init__.py)"
48
+ exit 1
49
+ fi
50
+ echo "OK: tag $GITHUB_REF_NAME matches package version $pkg_version"
51
+
52
+ - name: Verify CHANGELOG.md has a section for this version
53
+ run: |
54
+ pkg_version="$(uvx hatch version)"
55
+ if ! grep -qE "^## \[?${pkg_version//./\\.}\]?" CHANGELOG.md; then
56
+ echo "::error::CHANGELOG.md has no '## $pkg_version' section"
57
+ exit 1
58
+ fi
59
+ echo "OK: CHANGELOG.md has a section for $pkg_version"
60
+
61
+ ci:
62
+ name: run CI
63
+ needs: verify
64
+ uses: ./.github/workflows/ci.yml
65
+
66
+ build:
67
+ name: build sdist and wheel
68
+ needs: ci
69
+ runs-on: ubuntu-latest
70
+ permissions:
71
+ contents: read
72
+ steps:
73
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
74
+ with:
75
+ persist-credentials: false
76
+
77
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
78
+ with:
79
+ enable-cache: false # see the same note in the verify job above
80
+
81
+ - name: Build
82
+ run: uv build
83
+
84
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
85
+ with:
86
+ name: dist
87
+ path: dist/
88
+
89
+ publish:
90
+ name: publish to PyPI
91
+ needs: build
92
+ runs-on: ubuntu-latest
93
+ environment:
94
+ name: release
95
+ url: https://pypi.org/project/segment-mcp/
96
+ permissions:
97
+ id-token: write # required for PyPI Trusted Publishing (OIDC); no API token anywhere
98
+ contents: read
99
+ steps:
100
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
101
+ with:
102
+ name: dist
103
+ path: dist/
104
+
105
+ # PEP 740 attestations are generated and uploaded automatically here
106
+ # under Trusted Publishing -- no extra configuration -- and show up
107
+ # as a provenance badge on the PyPI project page.
108
+ - name: Publish to PyPI
109
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
110
+
111
+ github-release:
112
+ name: create GitHub Release
113
+ needs: publish
114
+ runs-on: ubuntu-latest
115
+ permissions:
116
+ contents: write
117
+ steps:
118
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
119
+ with:
120
+ persist-credentials: false
121
+
122
+ - name: Extract this version's CHANGELOG section
123
+ run: |
124
+ pkg_version="$(sed -n 's/^__version__ = "\(.*\)"/\1/p' src/segment_mcp/__init__.py)"
125
+ awk -v ver="$pkg_version" '
126
+ $0 ~ "^## \\[?" ver { found=1; next }
127
+ found && /^## / { exit }
128
+ found { print }
129
+ ' CHANGELOG.md > release-notes.md
130
+
131
+ - name: Create GitHub Release
132
+ env:
133
+ GH_TOKEN: ${{ github.token }}
134
+ run: gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --notes-file release-notes.md
@@ -0,0 +1,37 @@
1
+ name: zizmor
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: ["**"]
8
+
9
+ jobs:
10
+ zizmor:
11
+ name: zizmor GitHub Actions security audit
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: read
15
+ security-events: write
16
+ actions: read
17
+ steps:
18
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
19
+ with:
20
+ persist-credentials: false
21
+
22
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
23
+
24
+ - name: Run zizmor
25
+ run: uvx zizmor --format sarif . > results.sarif
26
+ env:
27
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28
+
29
+ - name: Upload SARIF file
30
+ # The repo is public now and code scanning (CodeQL default setup)
31
+ # is on — see docs/plans/going-public-checklist.md — so this no
32
+ # longer needs the continue-on-error it had while private and
33
+ # GHAS-gated.
34
+ uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
35
+ with:
36
+ sarif_file: results.sarif
37
+ category: zizmor
@@ -0,0 +1,40 @@
1
+ # Secrets — never commit
2
+ .env
3
+ *.sqlite
4
+ *.sqlite3
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+ *.so
11
+ .Python
12
+ build/
13
+ dist/
14
+ *.egg-info/
15
+ .eggs/
16
+
17
+ # Virtual environments
18
+ .venv/
19
+ venv/
20
+ env/
21
+
22
+ # Testing / coverage
23
+ .pytest_cache/
24
+ .coverage
25
+ .coverage.*
26
+ htmlcov/
27
+ .hypothesis/
28
+
29
+ # Type checkers
30
+ .mypy_cache/
31
+ .pyright/
32
+ .pytype/
33
+
34
+ # Tooling
35
+ .ruff_cache/
36
+
37
+ # Editors / OS
38
+ .DS_Store
39
+ .idea/
40
+ *.swp
@@ -0,0 +1,16 @@
1
+ # Fast, local checks only. Types (pyright) and tests (pytest) run in CI, not
2
+ # here — a slow pre-commit gets disabled. See AGENTS.md.
3
+ repos:
4
+ - repo: https://github.com/astral-sh/ruff-pre-commit
5
+ rev: v0.16.5
6
+ hooks:
7
+ - id: ruff-check
8
+ args: [--fix]
9
+ - id: ruff-format
10
+
11
+ - repo: https://github.com/pre-commit/pre-commit-hooks
12
+ rev: v6.0.0
13
+ hooks:
14
+ - id: end-of-file-fixer
15
+ - id: trailing-whitespace
16
+ - id: check-yaml
@@ -0,0 +1,85 @@
1
+ # AGENTS.md
2
+
3
+ ## Stop and read this before you write code
4
+
5
+ This repo has conventions. Violating them wastes a review cycle.
6
+
7
+ ## Commands
8
+
9
+ - Install: `uv sync`
10
+ - Test: `uv run pytest`
11
+ - Lint: `uv run ruff check . && uv run ruff format --check .`
12
+ - Types: `uv run pyright`
13
+ - All of it: `uv run ruff check . && uv run ruff format --check . && uv run pyright && uv run pytest`
14
+
15
+ ## Layout
16
+
17
+ - `src/segment_mcp/` — the package. `tests/` mirrors it. Never put tests inside the package.
18
+ - `docs/decisions/` — ADRs (MADR 4). Permanent, numbered, never renumbered.
19
+ - `docs/plans/` — dated design plans. Disposable once executed.
20
+ - `.env.example` — every var the server reads, no values. Copy to `.env` to use.
21
+
22
+ ## Non-negotiable
23
+
24
+ 1. **Never commit directly to `main`.** Branch, commit, open a PR. Even for a typo.
25
+ 2. **Tests before implementation.** If you are adding behavior, the failing test comes first.
26
+ 3. **No secrets in the repo, ever** — not in tests, not in fixtures, not in examples. Use env vars and `.env.example`.
27
+ 4. **Never re-type a file's contents from tool output.** Output can be truncated. Edit in place.
28
+ 5. **Every dependency added needs a one-line justification in the PR body.**
29
+ 6. **If a decision is expensive to reverse, write an ADR in the same PR.**
30
+ 7. **Linter versions are pinned exactly.** Do not float them to fix a failure — fix the code, or bump deliberately in its own PR.
31
+
32
+ ## Project-specific non-negotiables
33
+
34
+ 1. **READ-ONLY IS THE DEFAULT.** `SEGMENT_MCP_MODE` defaults to `read`.
35
+ Shipping v1 with zero write tools is a FEATURE, not a limitation — it
36
+ beats everything currently on the market. See BUILD-PLAN.md §2.
37
+ 2. **TIER 1 IS PERMANENTLY UNREACHABLE.** `POST /regulations` and
38
+ `POST /regulations/sources/{id}` permanently destroy user data with no
39
+ undo. They must not be callable in ANY mode. This is a line, not a v3
40
+ feature waiting for the right gate. See
41
+ `docs/decisions/0002-tier-1-permanently-unreachable.md` and
42
+ `docs/what-this-refuses-to-do.md`. `tests/test_tier1_unreachable.py`
43
+ must keep passing, deliberately redundantly (modes.py, the client, and
44
+ tool introspection each refuse it independently) — never simplify that
45
+ test down to one check.
46
+ 3. **`PUT`-replace endpoints (`tracking-plans/{id}/rules`,
47
+ `sources/{id}/labels`) are not exposed before v0.3, and only then with
48
+ the full-set-diff requirement in `docs/what-this-refuses-to-do.md`
49
+ implemented, not skipped as scope creep.**
50
+ 4. **NEVER auto-generate tools from the OpenAPI spec.** It would produce
51
+ ~200 flat tools including every `DELETE`, with no notion of blast
52
+ radius. Hand-pick and COMPOSE.
53
+ 5. **Compose reads into QUESTIONS, not endpoints.** The value is the join.
54
+ An LLM chaining four endpoint calls per source hits rate limits and
55
+ loses the thread. See BUILD-PLAN.md §5.
56
+ 6. **Resolve region EXPLICITLY on every call. Never default.** An EU
57
+ workspace hitting the wrong base URL fails silently — no error, data
58
+ simply never appears or never arrives. See BUILD-PLAN.md §0.6.
59
+ 7. **No live API calls in tests.** Recorded fixtures only, under
60
+ `tests/fixtures/{us,eu}/`.
61
+ 8. **The Profile API is a separate trust tier.** Never construct
62
+ `ProfileAPIClient` implicitly or fall back to `SEGMENT_API_TOKEN` for
63
+ it. A profile-lookup tool must not be registered when
64
+ `SEGMENT_PROFILE_TOKEN` is unset, and every lookup must be logged. See
65
+ `client/profile_api.py` and README.md's Profile API section.
66
+
67
+ ## Before opening a PR
68
+
69
+ - [ ] The full check command above passes locally
70
+ - [ ] `CHANGELOG.md` has an entry under `## Unreleased`
71
+ - [ ] No new file lacks a test, or the PR says why
72
+ - [ ] The PR body discloses: model, harness, and that it was AI-assisted
73
+ - [ ] You showed the human the full diff and got approval
74
+
75
+ ## What gets rejected
76
+
77
+ - Direct commits to `main`
78
+ - Reformatting unrelated code
79
+ - New dependencies without justification
80
+ - "Improvements" nobody asked for, bundled into an unrelated PR
81
+ - Removing a test to make CI pass
82
+ - Anything that makes an approval gate optional
83
+ - Any tool call, mode, or code path that can reach Tier 1 (regulation/deletion creation)
84
+ - Live API calls in tests
85
+ - Auto-generated tool surfaces from the OpenAPI spec