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.
- segment_mcp-0.1.0/.editorconfig +21 -0
- segment_mcp-0.1.0/.env.example +25 -0
- segment_mcp-0.1.0/.gitattributes +8 -0
- segment_mcp-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +55 -0
- segment_mcp-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
- segment_mcp-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +29 -0
- segment_mcp-0.1.0/.github/dependabot.yml +23 -0
- segment_mcp-0.1.0/.github/pull_request_template.md +26 -0
- segment_mcp-0.1.0/.github/workflows/ci.yml +47 -0
- segment_mcp-0.1.0/.github/workflows/release.yml +134 -0
- segment_mcp-0.1.0/.github/workflows/zizmor.yml +37 -0
- segment_mcp-0.1.0/.gitignore +40 -0
- segment_mcp-0.1.0/.pre-commit-config.yaml +16 -0
- segment_mcp-0.1.0/AGENTS.md +85 -0
- segment_mcp-0.1.0/BUILD-PLAN.md +368 -0
- segment_mcp-0.1.0/CHANGELOG.md +158 -0
- segment_mcp-0.1.0/CLAUDE.md +1 -0
- segment_mcp-0.1.0/CODE_OF_CONDUCT.md +84 -0
- segment_mcp-0.1.0/CONTRIBUTING.md +53 -0
- segment_mcp-0.1.0/LICENSE +21 -0
- segment_mcp-0.1.0/PKG-INFO +175 -0
- segment_mcp-0.1.0/README.md +153 -0
- segment_mcp-0.1.0/SECURITY.md +30 -0
- segment_mcp-0.1.0/docs/decisions/0000-template.md +78 -0
- segment_mcp-0.1.0/docs/decisions/0001-record-architecture-decisions.md +90 -0
- segment_mcp-0.1.0/docs/decisions/0002-tier-1-permanently-unreachable.md +117 -0
- segment_mcp-0.1.0/docs/plans/going-public-checklist.md +55 -0
- segment_mcp-0.1.0/docs/what-this-refuses-to-do.md +111 -0
- segment_mcp-0.1.0/pyproject.toml +113 -0
- segment_mcp-0.1.0/src/segment_mcp/__init__.py +8 -0
- segment_mcp-0.1.0/src/segment_mcp/client/__init__.py +0 -0
- segment_mcp-0.1.0/src/segment_mcp/client/profile_api.py +269 -0
- segment_mcp-0.1.0/src/segment_mcp/client/public_api.py +547 -0
- segment_mcp-0.1.0/src/segment_mcp/client/regions.py +112 -0
- segment_mcp-0.1.0/src/segment_mcp/modes.py +222 -0
- segment_mcp-0.1.0/src/segment_mcp/py.typed +0 -0
- segment_mcp-0.1.0/src/segment_mcp/server.py +343 -0
- segment_mcp-0.1.0/src/segment_mcp/tools/__init__.py +0 -0
- segment_mcp-0.1.0/src/segment_mcp/tools/_shared.py +238 -0
- segment_mcp-0.1.0/src/segment_mcp/tools/governance.py +123 -0
- segment_mcp-0.1.0/src/segment_mcp/tools/health.py +313 -0
- segment_mcp-0.1.0/src/segment_mcp/tools/profiles.py +5 -0
- segment_mcp-0.1.0/src/segment_mcp/tools/routing.py +393 -0
- segment_mcp-0.1.0/tests/__init__.py +0 -0
- segment_mcp-0.1.0/tests/client/__init__.py +0 -0
- segment_mcp-0.1.0/tests/client/test_date_parsing.py +55 -0
- segment_mcp-0.1.0/tests/client/test_profile_api.py +215 -0
- segment_mcp-0.1.0/tests/client/test_public_api_client.py +265 -0
- segment_mcp-0.1.0/tests/client/test_rate_limiter.py +176 -0
- segment_mcp-0.1.0/tests/client/test_regions.py +81 -0
- segment_mcp-0.1.0/tests/fixtures/README.md +57 -0
- segment_mcp-0.1.0/tests/fixtures/__init__.py +0 -0
- segment_mcp-0.1.0/tests/fixtures/eu/free_tier_403.json +7 -0
- segment_mcp-0.1.0/tests/fixtures/eu/insufficient_permissions_403.json +7 -0
- segment_mcp-0.1.0/tests/fixtures/eu/malformed_response.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/eu/profile/traits_200.json +8 -0
- segment_mcp-0.1.0/tests/fixtures/eu/profile/unauthorized_401.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/eu/rate_limited_429_no_signal.json +9 -0
- segment_mcp-0.1.0/tests/fixtures/eu/rate_limited_429_with_retry_after.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/eu/rate_limited_429_without_retry_after.json +14 -0
- segment_mcp-0.1.0/tests/fixtures/eu/source_no_connected_destinations_200.json +20 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/connected_destinations_one_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/connected_warehouses_one_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/delivery_metrics_200.json +16 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/destination_detail_200.json +15 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/destination_subscriptions_403.json +7 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/destination_subscriptions_one_200.json +18 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_emitting_200.json +15 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_empty_200.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_historical_active_200.json +15 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_many_emitters_200.json +20 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_recent_mixed_200.json +16 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/events_volume_recent_zero_one_200.json +7 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/source_settings_allow_200.json +30 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/source_settings_block_200.json +30 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/source_single_200.json +9 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/source_single_block_200.json +9 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/source_single_new_200.json +9 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/sources_two_200.json +13 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plan_rules_match_200.json +19 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plan_rules_no_match_200.json +19 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plan_sources_with_src1_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plan_sources_with_src2_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plans_empty_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tools/tracking_plans_one_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/eu/tracking_plan_no_sources_200.json +20 -0
- segment_mcp-0.1.0/tests/fixtures/eu/unauthorized_401.json +7 -0
- segment_mcp-0.1.0/tests/fixtures/eu/workspaces_200.json +18 -0
- segment_mcp-0.1.0/tests/fixtures/http.py +71 -0
- segment_mcp-0.1.0/tests/fixtures/us/free_tier_403.json +7 -0
- segment_mcp-0.1.0/tests/fixtures/us/insufficient_permissions_403.json +7 -0
- segment_mcp-0.1.0/tests/fixtures/us/list_response_200.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/us/malformed_response.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/us/no_data_envelope_200.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/us/profile/events_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/us/profile/external_ids_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/us/profile/links_200.json +8 -0
- segment_mcp-0.1.0/tests/fixtures/us/profile/malformed_200.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/us/profile/metadata_200.json +12 -0
- segment_mcp-0.1.0/tests/fixtures/us/profile/not_found_404.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/us/profile/rate_limited_429.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/us/profile/traits_200.json +8 -0
- segment_mcp-0.1.0/tests/fixtures/us/profile/unauthorized_401.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/us/rate_limited_429_no_signal.json +9 -0
- segment_mcp-0.1.0/tests/fixtures/us/rate_limited_429_with_retry_after.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/us/rate_limited_429_without_retry_after.json +14 -0
- segment_mcp-0.1.0/tests/fixtures/us/source_no_connected_destinations_200.json +20 -0
- segment_mcp-0.1.0/tests/fixtures/us/sources_page1_200.json +18 -0
- segment_mcp-0.1.0/tests/fixtures/us/sources_page2_200.json +15 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/connected_destinations_one_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/connected_warehouses_one_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/delivery_metrics_200.json +16 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/destination_detail_200.json +15 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/destination_subscriptions_403.json +7 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/destination_subscriptions_one_200.json +18 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_emitting_200.json +15 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_empty_200.json +5 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_historical_active_200.json +15 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_many_emitters_200.json +20 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_recent_mixed_200.json +16 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/events_volume_recent_zero_one_200.json +7 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/source_settings_allow_200.json +30 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/source_settings_block_200.json +30 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/source_single_200.json +9 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/source_single_block_200.json +9 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/source_single_new_200.json +9 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/sources_two_200.json +13 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plan_rules_match_200.json +19 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plan_rules_no_match_200.json +19 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plan_sources_with_src1_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plan_sources_with_src2_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plans_empty_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/us/tools/tracking_plans_one_200.json +10 -0
- segment_mcp-0.1.0/tests/fixtures/us/tracking_plan_no_sources_200.json +20 -0
- segment_mcp-0.1.0/tests/fixtures/us/unauthorized_401.json +7 -0
- segment_mcp-0.1.0/tests/fixtures/us/workspaces_200.json +18 -0
- segment_mcp-0.1.0/tests/test_modes.py +137 -0
- segment_mcp-0.1.0/tests/test_package.py +7 -0
- segment_mcp-0.1.0/tests/test_server.py +288 -0
- segment_mcp-0.1.0/tests/test_tier1_unreachable.py +99 -0
- segment_mcp-0.1.0/tests/tools/__init__.py +0 -0
- segment_mcp-0.1.0/tests/tools/_helpers.py +27 -0
- segment_mcp-0.1.0/tests/tools/test_governance.py +86 -0
- segment_mcp-0.1.0/tests/tools/test_health.py +160 -0
- segment_mcp-0.1.0/tests/tools/test_routing.py +204 -0
- segment_mcp-0.1.0/uv.lock +1077 -0
- 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,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
|