From ace87c6de23524b858dc554655c0ccd40e7d5589 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 17:56:03 +0000 Subject: [PATCH 1/3] chore: point repository URLs and Go module paths at patsypppe/toolgauge Merge after the GitHub repository is renamed from sentinel to toolgauge. Until then the new URLs 404; after it, GitHub redirects the old ones. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Pdv7R172x4XFq6AxwmmBBF --- .github/workflows/release.yml | 2 +- .golangci.yml | 2 +- .pre-commit-hooks.yaml | 2 +- Dockerfile | 2 +- README.md | 6 ++-- ...modelcontextprotocol-servers-filesystem.md | 4 +-- .../issue-drafts/02-firecrawl-mcp-server.md | 4 +-- broker/Dockerfile | 2 +- broker/cmd/broker/main.go | 28 +++++++++---------- broker/go.mod | 6 ++-- broker/internal/tools/ops/apply.go | 6 ++-- .../tools/ops/ops_integration_test.go | 8 +++--- broker/internal/tools/ops/plan.go | 4 +-- broker/internal/tools/warehouse/describe.go | 4 +-- .../internal/tools/warehouse/format_test.go | 2 +- broker/internal/tools/warehouse/query.go | 4 +-- .../tools/warehouse/query_integration_test.go | 6 ++-- broker/internal/version/version.go | 2 +- ci/gitlab/toolgauge.gitlab-ci.yml | 2 +- docs/KIT.md | 2 +- docs/OVERVIEW.md | 10 +++---- docs/USING.md | 2 +- fleet/cmd/toolgauge-server/main.go | 4 +-- fleet/go.mod | 2 +- fleet/internal/api/api.go | 8 +++--- fleet/internal/api/api_test.go | 2 +- fleet/internal/api/sort.go | 2 +- fleet/internal/api/views.go | 4 +-- fleet/internal/dashboard/dashboard.go | 2 +- fleet/internal/posture/posture.go | 2 +- fleet/internal/posture/posture_test.go | 2 +- fleet/internal/store/memory.go | 2 +- fleet/internal/store/memory_test.go | 4 +-- fleet/internal/store/postgres.go | 2 +- fleet/internal/store/postgres_test.go | 4 +-- fleet/internal/store/store.go | 2 +- fleet/internal/store/storetest/storetest.go | 4 +-- harness/pyproject.toml | 6 ++-- harness/src/toolgauge/probe/transport.py | 2 +- harness/src/toolgauge/report/sarif.py | 2 +- mcp/audit/audit_integration_test.go | 2 +- mcp/audit/chain.go | 2 +- mcp/audit/redact.go | 2 +- mcp/authz/audience.go | 2 +- mcp/authz/http.go | 4 +-- mcp/authz/scopes.go | 2 +- mcp/example/cmd/main.go | 2 +- mcp/example/example.go | 6 ++-- mcp/go.mod | 2 +- mcp/handles/gc.go | 2 +- mcp/handles/mint.go | 2 +- mcp/handles/resolve.go | 4 +-- mcp/handles/resolve_integration_test.go | 6 ++-- mcp/mrtr/engine.go | 4 +-- mcp/mrtr/engine_integration_test.go | 6 ++-- mcp/mrtr/idempotency.go | 2 +- mcp/otlp/end_to_end_test.go | 8 +++--- mcp/otlp/otlp.go | 4 +-- mcp/otlp/otlp_test.go | 2 +- mcp/registry/manifest.go | 4 +-- mcp/registry/manifest_test.go | 2 +- mcp/registry/registry.go | 2 +- mcp/registry/registry_test.go | 2 +- mcp/server/auth.go | 6 ++-- mcp/server/server.go | 8 +++--- mcp/server/server_test.go | 8 +++--- mcp/transport/audit.go | 6 ++-- mcp/transport/cacheable_test.go | 4 +-- mcp/transport/dispatch.go | 2 +- mcp/transport/handlers.go | 2 +- mcp/transport/headers.go | 2 +- mcp/transport/headers_test.go | 2 +- mcp/transport/http.go | 4 +-- mcp/transport/http_test.go | 4 +-- mcp/transport/span.go | 2 +- mcp/transport/stdio.go | 2 +- mcp/transport/stdio_test.go | 2 +- mcp/transport/toolscall.go | 8 +++--- site/assets/js/migration.js | 2 +- site/index.html | 22 +++++++-------- 80 files changed, 163 insertions(+), 163 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0159be7..c7a9ac2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,7 @@ # # Verify a release: # cosign verify ghcr.io/patsypppe/toolgauge: \ -# --certificate-identity-regexp 'https://github.com/patsypppe/sentinel/.github/workflows/release.yml@.*' \ +# --certificate-identity-regexp 'https://github.com/patsypppe/toolgauge/.github/workflows/release.yml@.*' \ # --certificate-oidc-issuer https://token.actions.githubusercontent.com # # PyPI: Uses PyPI trusted publishing, so no token is stored: diff --git a/.golangci.yml b/.golangci.yml index f1e8d0b..45ae137 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -41,4 +41,4 @@ formatters: settings: goimports: local-prefixes: - - github.com/patsypppe/sentinel + - github.com/patsypppe/toolgauge diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 7cc6251..890f002 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,7 +1,7 @@ # Hooks for teams that scan a locally-running MCP server before they push. # # repos: -# - repo: https://github.com/patsypppe/sentinel +# - repo: https://github.com/patsypppe/toolgauge # rev: v0.4.0 # hooks: # - id: toolgauge-scan diff --git a/Dockerfile b/Dockerfile index 03fc22c..17ce966 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ RUN pip install --no-cache-dir build \ && python -m build --wheel --outdir /dist /src/harness FROM python:3.12-slim -LABEL org.opencontainers.image.source="https://github.com/patsypppe/sentinel" \ +LABEL org.opencontainers.image.source="https://github.com/patsypppe/toolgauge" \ org.opencontainers.image.description="Conformance harness for MCP 2026-07-28" \ org.opencontainers.image.licenses="MIT" RUN useradd --uid 10001 --no-create-home --shell /usr/sbin/nologin toolgauge diff --git a/README.md b/README.md index cfda17a..b0c4eea 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ No clone, no setup, nothing to configure. This starts two practice servers on yo with deliberate mistakes, one correct), scans both, and shows you the difference: ```bash -uvx --from "git+https://github.com/patsypppe/sentinel@main#subdirectory=harness" toolgauge demo +uvx --from "git+https://github.com/patsypppe/toolgauge@main#subdirectory=harness" toolgauge demo ``` ``` @@ -33,7 +33,7 @@ uvx --from "git+https://github.com/patsypppe/sentinel@main#subdirectory=harness" verdict: PASS (exit code 0) ``` -Prefer a clone? `git clone https://github.com/patsypppe/sentinel && cd sentinel && uv run toolgauge demo`. +Prefer a clone? `git clone https://github.com/patsypppe/toolgauge && cd sentinel && uv run toolgauge demo`. ## Lint any server, including stdio ones @@ -52,7 +52,7 @@ rule fails, so it drops straight into CI. No clone needed: ```bash -uvx --from "git+https://github.com/patsypppe/sentinel@main#subdirectory=harness" \ +uvx --from "git+https://github.com/patsypppe/toolgauge@main#subdirectory=harness" \ toolgauge scan --endpoint https://your-server.example/mcp --gate must ``` diff --git a/benchmarks/issue-drafts/01-modelcontextprotocol-servers-filesystem.md b/benchmarks/issue-drafts/01-modelcontextprotocol-servers-filesystem.md index bfe2d36..80358b1 100644 --- a/benchmarks/issue-drafts/01-modelcontextprotocol-servers-filesystem.md +++ b/benchmarks/issue-drafts/01-modelcontextprotocol-servers-filesystem.md @@ -9,7 +9,7 @@ is already tracked. Post it from your own account; it is written in your voice. Hi, and thanks for maintaining this server. -I'm building [toolgauge](https://github.com/patsypppe/sentinel), a linter for MCP servers, and +I'm building [toolgauge](https://github.com/patsypppe/toolgauge), a linter for MCP servers, and ran it on `@modelcontextprotocol/server-filesystem` 2.0.0 (also checked against the source at `51b6382`). Most parameters carry a `.describe()`, but five do not, and a client or model reading the tool schema gets no explanation for them: @@ -38,6 +38,6 @@ glob semantics.) Why it matters: two 2026 studies of MCP tool descriptions ([arXiv 2602.14878](https://arxiv.org/abs/2602.14878), [2602.18914](https://arxiv.org/abs/2602.18914)) found missing parameter semantics are common and change which tool an LLM selects. -Reproduce: `uvx --from "git+https://github.com/patsypppe/sentinel@main#subdirectory=harness" toolgauge lint --command "npx -y @modelcontextprotocol/server-filesystem /tmp"` +Reproduce: `uvx --from "git+https://github.com/patsypppe/toolgauge@main#subdirectory=harness" toolgauge lint --command "npx -y @modelcontextprotocol/server-filesystem /tmp"` Happy to send a PR with the descriptions if that's welcome. diff --git a/benchmarks/issue-drafts/02-firecrawl-mcp-server.md b/benchmarks/issue-drafts/02-firecrawl-mcp-server.md index 30abd53..1c1433a 100644 --- a/benchmarks/issue-drafts/02-firecrawl-mcp-server.md +++ b/benchmarks/issue-drafts/02-firecrawl-mcp-server.md @@ -8,7 +8,7 @@ limited to what that shows. **Body:** -Hi, thanks for the MCP server. I ran [toolgauge](https://github.com/patsypppe/sentinel), a linter for MCP +Hi, thanks for the MCP server. I ran [toolgauge](https://github.com/patsypppe/toolgauge), a linter for MCP servers, on `firecrawl-mcp` 3.34.1 over stdio. Tool descriptions are detailed (they explain when to use each tool), but in the `inputSchema` of the tools 71 of 194 parameters have no `description`, and they are not documented in the tool description text either. A model choosing arguments from the schema sees only a name and a type. @@ -23,7 +23,7 @@ Two 2026 studies of MCP tool descriptions ([arXiv 2602.14878](https://arxiv.org/abs/2602.14878), [2602.18914](https://arxiv.org/abs/2602.18914)) found missing parameter semantics are common and measurably change which tool and arguments an LLM picks. -Reproduce: `FIRECRAWL_API_KEY=x uvx --from "git+https://github.com/patsypppe/sentinel@main#subdirectory=harness" toolgauge lint --command "npx -y firecrawl-mcp" --env FIRECRAWL_API_KEY=x` +Reproduce: `FIRECRAWL_API_KEY=x uvx --from "git+https://github.com/patsypppe/toolgauge@main#subdirectory=harness" toolgauge lint --command "npx -y firecrawl-mcp" --env FIRECRAWL_API_KEY=x` (`tools/list` works with a dummy key; no request is made to your API.) If the schemas are generated from Zod, adding `.describe()` to those fields would cover it. Happy to help. diff --git a/broker/Dockerfile b/broker/Dockerfile index c739af9..64d6c5a 100644 --- a/broker/Dockerfile +++ b/broker/Dockerfile @@ -18,7 +18,7 @@ COPY broker ./broker WORKDIR /src/broker ARG VERSION=0.1.0-dev RUN CGO_ENABLED=0 go build -trimpath \ - -ldflags "-s -w -X github.com/patsypppe/sentinel/broker/internal/version.Version=${VERSION}" \ + -ldflags "-s -w -X github.com/patsypppe/toolgauge/broker/internal/version.Version=${VERSION}" \ -o /out/broker ./cmd/broker FROM alpine:3.20 diff --git a/broker/cmd/broker/main.go b/broker/cmd/broker/main.go index 51e1786..2f4a5eb 100644 --- a/broker/cmd/broker/main.go +++ b/broker/cmd/broker/main.go @@ -20,20 +20,20 @@ import ( "syscall" "time" - "github.com/patsypppe/sentinel/broker/internal/config" - "github.com/patsypppe/sentinel/broker/internal/tools/ops" - "github.com/patsypppe/sentinel/broker/internal/tools/warehouse" - "github.com/patsypppe/sentinel/broker/internal/version" - "github.com/patsypppe/sentinel/mcp/audit" - "github.com/patsypppe/sentinel/mcp/authz" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/handles" - "github.com/patsypppe/sentinel/mcp/mrtr" - "github.com/patsypppe/sentinel/mcp/otlp" - "github.com/patsypppe/sentinel/mcp/registry" - "github.com/patsypppe/sentinel/mcp/server" - "github.com/patsypppe/sentinel/mcp/store" - "github.com/patsypppe/sentinel/mcp/transport" + "github.com/patsypppe/toolgauge/broker/internal/config" + "github.com/patsypppe/toolgauge/broker/internal/tools/ops" + "github.com/patsypppe/toolgauge/broker/internal/tools/warehouse" + "github.com/patsypppe/toolgauge/broker/internal/version" + "github.com/patsypppe/toolgauge/mcp/audit" + "github.com/patsypppe/toolgauge/mcp/authz" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/handles" + "github.com/patsypppe/toolgauge/mcp/mrtr" + "github.com/patsypppe/toolgauge/mcp/otlp" + "github.com/patsypppe/toolgauge/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/server" + "github.com/patsypppe/toolgauge/mcp/store" + "github.com/patsypppe/toolgauge/mcp/transport" ) func main() { diff --git a/broker/go.mod b/broker/go.mod index 86537f1..71481f3 100644 --- a/broker/go.mod +++ b/broker/go.mod @@ -1,11 +1,11 @@ -module github.com/patsypppe/sentinel/broker +module github.com/patsypppe/toolgauge/broker go 1.23.0 require ( github.com/google/uuid v1.6.0 github.com/jackc/pgx/v5 v5.7.4 - github.com/patsypppe/sentinel/mcp v0.0.0 + github.com/patsypppe/toolgauge/mcp v0.0.0 ) require ( @@ -22,4 +22,4 @@ require ( // The kit lives beside the broker in this repository. A replace directive (as well // as go.work) lets `go build` work from the broker directory alone, as the Docker // build does. -replace github.com/patsypppe/sentinel/mcp => ../mcp +replace github.com/patsypppe/toolgauge/mcp => ../mcp diff --git a/broker/internal/tools/ops/apply.go b/broker/internal/tools/ops/apply.go index e5ec2f7..972d14f 100644 --- a/broker/internal/tools/ops/apply.go +++ b/broker/internal/tools/ops/apply.go @@ -9,9 +9,9 @@ import ( "github.com/google/uuid" "github.com/jackc/pgx/v5" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/mrtr" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/mrtr" + "github.com/patsypppe/toolgauge/mcp/registry" ) // Resolver resolves a principal-bound handle. Declared where it is used. diff --git a/broker/internal/tools/ops/ops_integration_test.go b/broker/internal/tools/ops/ops_integration_test.go index b910dab..c135078 100644 --- a/broker/internal/tools/ops/ops_integration_test.go +++ b/broker/internal/tools/ops/ops_integration_test.go @@ -12,10 +12,10 @@ import ( "github.com/jackc/pgx/v5/pgxpool" - "github.com/patsypppe/sentinel/mcp/handles" - "github.com/patsypppe/sentinel/mcp/mrtr" - "github.com/patsypppe/sentinel/mcp/registry" - "github.com/patsypppe/sentinel/mcp/store" + "github.com/patsypppe/toolgauge/mcp/handles" + "github.com/patsypppe/toolgauge/mcp/mrtr" + "github.com/patsypppe/toolgauge/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/store" ) // The §10 (WP-6) definition of done, end to end through the real tools: diff --git a/broker/internal/tools/ops/plan.go b/broker/internal/tools/ops/plan.go index c77320a..e3acb72 100644 --- a/broker/internal/tools/ops/plan.go +++ b/broker/internal/tools/ops/plan.go @@ -13,8 +13,8 @@ import ( "fmt" "time" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" ) // Minter mints principal-bound state handles. Declared here, where it is used. diff --git a/broker/internal/tools/warehouse/describe.go b/broker/internal/tools/warehouse/describe.go index 72fe090..ad11573 100644 --- a/broker/internal/tools/warehouse/describe.go +++ b/broker/internal/tools/warehouse/describe.go @@ -9,8 +9,8 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" ) // DescribeTool is warehouse.describe: the schema of the tables this principal diff --git a/broker/internal/tools/warehouse/format_test.go b/broker/internal/tools/warehouse/format_test.go index 7f85cbd..a8aeef7 100644 --- a/broker/internal/tools/warehouse/format_test.go +++ b/broker/internal/tools/warehouse/format_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/registry" ) func sampleRows(n int) Rows { diff --git a/broker/internal/tools/warehouse/query.go b/broker/internal/tools/warehouse/query.go index 7dd704c..4fd618e 100644 --- a/broker/internal/tools/warehouse/query.go +++ b/broker/internal/tools/warehouse/query.go @@ -10,8 +10,8 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" ) // Minter mints a server-minted, principal-bound state handle. diff --git a/broker/internal/tools/warehouse/query_integration_test.go b/broker/internal/tools/warehouse/query_integration_test.go index 3b64d98..b35fba5 100644 --- a/broker/internal/tools/warehouse/query_integration_test.go +++ b/broker/internal/tools/warehouse/query_integration_test.go @@ -13,9 +13,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" - "github.com/patsypppe/sentinel/mcp/store" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/store" ) // These tests run against a real Postgres because the SQL guard's whole design diff --git a/broker/internal/version/version.go b/broker/internal/version/version.go index 65ff034..58fe3bc 100644 --- a/broker/internal/version/version.go +++ b/broker/internal/version/version.go @@ -4,7 +4,7 @@ package version // Version is the broker's semantic version. Overridden at build time with -// -ldflags "-X github.com/patsypppe/sentinel/broker/internal/version.Version=..." +// -ldflags "-X github.com/patsypppe/toolgauge/broker/internal/version.Version=..." var Version = "0.1.0-dev" // Name is the server identity reported in serverInfo. Stable across releases; diff --git a/ci/gitlab/toolgauge.gitlab-ci.yml b/ci/gitlab/toolgauge.gitlab-ci.yml index c2b4f76..7dac627 100644 --- a/ci/gitlab/toolgauge.gitlab-ci.yml +++ b/ci/gitlab/toolgauge.gitlab-ci.yml @@ -1,7 +1,7 @@ # GitLab CI template for the toolgauge MCP conformance scan. # # include: -# - remote: https://raw.githubusercontent.com/patsypppe/sentinel/main/ci/gitlab/toolgauge.gitlab-ci.yml +# - remote: https://raw.githubusercontent.com/patsypppe/toolgauge/main/ci/gitlab/toolgauge.gitlab-ci.yml # # toolgauge: # extends: .toolgauge-scan diff --git a/docs/KIT.md b/docs/KIT.md index 5c1e7ab..0bebce9 100644 --- a/docs/KIT.md +++ b/docs/KIT.md @@ -4,7 +4,7 @@ `2026-07-28`, extracted from `broker/internal/` so you can import them. ```go -import "github.com/patsypppe/sentinel/mcp/server" +import "github.com/patsypppe/toolgauge/mcp/server" ``` ## The smallest server diff --git a/docs/OVERVIEW.md b/docs/OVERVIEW.md index 214cb32..31e2055 100644 --- a/docs/OVERVIEW.md +++ b/docs/OVERVIEW.md @@ -2,8 +2,8 @@ **Check whether an MCP server follows the current MCP specification — and a reference server that does.** -[![ci](https://github.com/patsypppe/sentinel/actions/workflows/ci.yml/badge.svg)](https://github.com/patsypppe/sentinel/actions/workflows/ci.yml) -[![conformance](https://github.com/patsypppe/sentinel/actions/workflows/conformance.yml/badge.svg)](https://github.com/patsypppe/sentinel/actions/workflows/conformance.yml) +[![ci](https://github.com/patsypppe/toolgauge/actions/workflows/ci.yml/badge.svg)](https://github.com/patsypppe/toolgauge/actions/workflows/ci.yml) +[![conformance](https://github.com/patsypppe/toolgauge/actions/workflows/conformance.yml/badge.svg)](https://github.com/patsypppe/toolgauge/actions/workflows/conformance.yml) **[Live showcase → patsypppe.github.io/toolgauge](https://patsypppe.github.io/toolgauge/)**: an interactive viewer over real `toolgauge` output, with rule-by-rule findings, the deprecation timeline and the @@ -27,7 +27,7 @@ and tells you which rules failed, what it saw, what to change, and which page of comes from. It works on any server in any language, because it only speaks HTTP to it. ```bash -uvx --from "git+https://github.com/patsypppe/sentinel@main#subdirectory=harness" \ +uvx --from "git+https://github.com/patsypppe/toolgauge@main#subdirectory=harness" \ toolgauge scan --endpoint https://your-server.example/mcp --gate must ``` @@ -211,7 +211,7 @@ No clone needed. Until the first release is published to PyPI (`toolgauge`, via the `release` workflow), install from git: ```bash -uvx --from "git+https://github.com/patsypppe/sentinel@main#subdirectory=harness" \ +uvx --from "git+https://github.com/patsypppe/toolgauge@main#subdirectory=harness" \ toolgauge scan --endpoint https://your-server.example/mcp --gate must ``` @@ -230,7 +230,7 @@ permissions: security-events: write # only needed for the code-scanning upload steps: - run: ./start-my-server.sh & - - uses: patsypppe/sentinel@v0.4.0 + - uses: patsypppe/toolgauge@v0.4.0 with: endpoint: http://localhost:3000/mcp gate: must # fail the job on any MUST violation diff --git a/docs/USING.md b/docs/USING.md index 93b1c76..de2d962 100644 --- a/docs/USING.md +++ b/docs/USING.md @@ -156,7 +156,7 @@ than one from a single scan. | Where | How | |---|---| -| GitHub | `uses: patsypppe/sentinel@v0.4.0`. Inputs: `endpoint`, `gate`, `config`, `baseline`, `snapshot`, `targets`, `safe`, `no-exec`, `token`. Writes a Markdown job summary, uploads SARIF. | +| GitHub | `uses: patsypppe/toolgauge@v0.4.0`. Inputs: `endpoint`, `gate`, `config`, `baseline`, `snapshot`, `targets`, `safe`, `no-exec`, `token`. Writes a Markdown job summary, uploads SARIF. | | GitLab | `include:` [`ci/gitlab/toolgauge.gitlab-ci.yml`](../ci/gitlab/toolgauge.gitlab-ci.yml) and `extends: .toolgauge-scan`. Publishes JUnit. | | Any CI | `docker run ghcr.io/patsypppe/toolgauge scan --endpoint ...` | | pre-commit | the `toolgauge-scan` hook (`pre-push`, so a few hundred requests do not run on every commit) | diff --git a/fleet/cmd/toolgauge-server/main.go b/fleet/cmd/toolgauge-server/main.go index 443f353..0021879 100644 --- a/fleet/cmd/toolgauge-server/main.go +++ b/fleet/cmd/toolgauge-server/main.go @@ -28,8 +28,8 @@ import ( "syscall" "time" - "github.com/patsypppe/sentinel/fleet/internal/api" - "github.com/patsypppe/sentinel/fleet/internal/store" + "github.com/patsypppe/toolgauge/fleet/internal/api" + "github.com/patsypppe/toolgauge/fleet/internal/store" ) // version is overridden at build time with -ldflags "-X main.version=...". diff --git a/fleet/go.mod b/fleet/go.mod index 77b3cd2..21235e6 100644 --- a/fleet/go.mod +++ b/fleet/go.mod @@ -1,4 +1,4 @@ -module github.com/patsypppe/sentinel/fleet +module github.com/patsypppe/toolgauge/fleet go 1.23.0 diff --git a/fleet/internal/api/api.go b/fleet/internal/api/api.go index c060b1c..d6f4105 100644 --- a/fleet/internal/api/api.go +++ b/fleet/internal/api/api.go @@ -24,10 +24,10 @@ import ( "strings" "time" - "github.com/patsypppe/sentinel/fleet/internal/dashboard" - "github.com/patsypppe/sentinel/fleet/internal/model" - "github.com/patsypppe/sentinel/fleet/internal/posture" - "github.com/patsypppe/sentinel/fleet/internal/store" + "github.com/patsypppe/toolgauge/fleet/internal/dashboard" + "github.com/patsypppe/toolgauge/fleet/internal/model" + "github.com/patsypppe/toolgauge/fleet/internal/posture" + "github.com/patsypppe/toolgauge/fleet/internal/store" ) // MaxReportBytes bounds a published report. A full v2 report is tens of kilobytes; diff --git a/fleet/internal/api/api_test.go b/fleet/internal/api/api_test.go index f25f001..49700c3 100644 --- a/fleet/internal/api/api_test.go +++ b/fleet/internal/api/api_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/patsypppe/sentinel/fleet/internal/store" + "github.com/patsypppe/toolgauge/fleet/internal/store" ) const ( diff --git a/fleet/internal/api/sort.go b/fleet/internal/api/sort.go index d9d5f35..7047aab 100644 --- a/fleet/internal/api/sort.go +++ b/fleet/internal/api/sort.go @@ -3,7 +3,7 @@ package api import ( "sort" - "github.com/patsypppe/sentinel/fleet/internal/posture" + "github.com/patsypppe/toolgauge/fleet/internal/posture" ) func sortWaivers(w []posture.ExpiringWaiver) { diff --git a/fleet/internal/api/views.go b/fleet/internal/api/views.go index 7619314..6d1a8ca 100644 --- a/fleet/internal/api/views.go +++ b/fleet/internal/api/views.go @@ -5,8 +5,8 @@ import ( "strconv" "time" - "github.com/patsypppe/sentinel/fleet/internal/dashboard" - "github.com/patsypppe/sentinel/fleet/internal/posture" + "github.com/patsypppe/toolgauge/fleet/internal/dashboard" + "github.com/patsypppe/toolgauge/fleet/internal/posture" ) func humanAge(d time.Duration) string { diff --git a/fleet/internal/dashboard/dashboard.go b/fleet/internal/dashboard/dashboard.go index b4083b6..5ec6818 100644 --- a/fleet/internal/dashboard/dashboard.go +++ b/fleet/internal/dashboard/dashboard.go @@ -16,7 +16,7 @@ import ( "strings" "time" - "github.com/patsypppe/sentinel/fleet/internal/posture" + "github.com/patsypppe/toolgauge/fleet/internal/posture" ) //go:embed templates/*.html diff --git a/fleet/internal/posture/posture.go b/fleet/internal/posture/posture.go index cc3ca58..57941c4 100644 --- a/fleet/internal/posture/posture.go +++ b/fleet/internal/posture/posture.go @@ -9,7 +9,7 @@ import ( "sort" "time" - "github.com/patsypppe/sentinel/fleet/internal/model" + "github.com/patsypppe/toolgauge/fleet/internal/model" ) const day = 24 * time.Hour diff --git a/fleet/internal/posture/posture_test.go b/fleet/internal/posture/posture_test.go index 4447cba..e46e0f9 100644 --- a/fleet/internal/posture/posture_test.go +++ b/fleet/internal/posture/posture_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/patsypppe/sentinel/fleet/internal/model" + "github.com/patsypppe/toolgauge/fleet/internal/model" ) var t0 = time.Date(2026, 10, 1, 12, 0, 0, 0, time.UTC) diff --git a/fleet/internal/store/memory.go b/fleet/internal/store/memory.go index dcc34b8..e71ae57 100644 --- a/fleet/internal/store/memory.go +++ b/fleet/internal/store/memory.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "github.com/patsypppe/sentinel/fleet/internal/model" + "github.com/patsypppe/toolgauge/fleet/internal/model" ) // Memory is a Store held in process memory. Everything is lost on restart, which is diff --git a/fleet/internal/store/memory_test.go b/fleet/internal/store/memory_test.go index f21058e..8c18467 100644 --- a/fleet/internal/store/memory_test.go +++ b/fleet/internal/store/memory_test.go @@ -3,8 +3,8 @@ package store_test import ( "testing" - "github.com/patsypppe/sentinel/fleet/internal/store" - "github.com/patsypppe/sentinel/fleet/internal/store/storetest" + "github.com/patsypppe/toolgauge/fleet/internal/store" + "github.com/patsypppe/toolgauge/fleet/internal/store/storetest" ) func TestMemoryStoreSatisfiesTheContract(t *testing.T) { diff --git a/fleet/internal/store/postgres.go b/fleet/internal/store/postgres.go index 575432d..4186c26 100644 --- a/fleet/internal/store/postgres.go +++ b/fleet/internal/store/postgres.go @@ -13,7 +13,7 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/patsypppe/sentinel/fleet/internal/model" + "github.com/patsypppe/toolgauge/fleet/internal/model" ) //go:embed migrations/*.sql diff --git a/fleet/internal/store/postgres_test.go b/fleet/internal/store/postgres_test.go index 2a47d99..b3fb097 100644 --- a/fleet/internal/store/postgres_test.go +++ b/fleet/internal/store/postgres_test.go @@ -12,8 +12,8 @@ import ( "github.com/jackc/pgx/v5" - "github.com/patsypppe/sentinel/fleet/internal/store" - "github.com/patsypppe/sentinel/fleet/internal/store/storetest" + "github.com/patsypppe/toolgauge/fleet/internal/store" + "github.com/patsypppe/toolgauge/fleet/internal/store/storetest" ) func adminURL(t *testing.T) string { diff --git a/fleet/internal/store/store.go b/fleet/internal/store/store.go index 1abf008..bd9ca9c 100644 --- a/fleet/internal/store/store.go +++ b/fleet/internal/store/store.go @@ -11,7 +11,7 @@ import ( "errors" "time" - "github.com/patsypppe/sentinel/fleet/internal/model" + "github.com/patsypppe/toolgauge/fleet/internal/model" ) // ErrNotFound is returned when a target has no scans. diff --git a/fleet/internal/store/storetest/storetest.go b/fleet/internal/store/storetest/storetest.go index 84c0af6..90af67d 100644 --- a/fleet/internal/store/storetest/storetest.go +++ b/fleet/internal/store/storetest/storetest.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "github.com/patsypppe/sentinel/fleet/internal/model" - "github.com/patsypppe/sentinel/fleet/internal/store" + "github.com/patsypppe/toolgauge/fleet/internal/model" + "github.com/patsypppe/toolgauge/fleet/internal/store" ) var ( diff --git a/harness/pyproject.toml b/harness/pyproject.toml index 08585d6..eb07679 100644 --- a/harness/pyproject.toml +++ b/harness/pyproject.toml @@ -26,9 +26,9 @@ classifiers = [ ] [project.urls] -Homepage = "https://github.com/patsypppe/sentinel" -Repository = "https://github.com/patsypppe/sentinel" -Issues = "https://github.com/patsypppe/sentinel/issues" +Homepage = "https://github.com/patsypppe/toolgauge" +Repository = "https://github.com/patsypppe/toolgauge" +Issues = "https://github.com/patsypppe/toolgauge/issues" [project.scripts] toolgauge = "toolgauge.cli:app" diff --git a/harness/src/toolgauge/probe/transport.py b/harness/src/toolgauge/probe/transport.py index 6abf67f..1368582 100644 --- a/harness/src/toolgauge/probe/transport.py +++ b/harness/src/toolgauge/probe/transport.py @@ -42,7 +42,7 @@ #: Sent on every request, so the operator of a scanned server can see what is #: talking to it and where to read about it. Overridable, never omitted. -DEFAULT_USER_AGENT = "toolgauge (+https://github.com/patsypppe/sentinel)" +DEFAULT_USER_AGENT = "toolgauge (+https://github.com/patsypppe/toolgauge)" _CONTEXTS: dict[tuple[str, str], ssl.SSLContext] = {} diff --git a/harness/src/toolgauge/report/sarif.py b/harness/src/toolgauge/report/sarif.py index b85ec3e..59a0519 100644 --- a/harness/src/toolgauge/report/sarif.py +++ b/harness/src/toolgauge/report/sarif.py @@ -193,7 +193,7 @@ def render(report: ScanReport, *, anchor: str = DEFAULT_ANCHOR) -> dict[str, Any "driver": { "name": "toolgauge", "version": __version__, - "informationUri": "https://github.com/patsypppe/sentinel", + "informationUri": "https://github.com/patsypppe/toolgauge", "semanticVersion": __version__, "rules": rules, } diff --git a/mcp/audit/audit_integration_test.go b/mcp/audit/audit_integration_test.go index 8dfd1d1..d53f7d8 100644 --- a/mcp/audit/audit_integration_test.go +++ b/mcp/audit/audit_integration_test.go @@ -14,7 +14,7 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/patsypppe/sentinel/mcp/store" + "github.com/patsypppe/toolgauge/mcp/store" ) const ( diff --git a/mcp/audit/chain.go b/mcp/audit/chain.go index c796356..2f19708 100644 --- a/mcp/audit/chain.go +++ b/mcp/audit/chain.go @@ -19,7 +19,7 @@ import ( "strings" "time" - "github.com/patsypppe/sentinel/mcp/canonical" + "github.com/patsypppe/toolgauge/mcp/canonical" ) // GenesisHash is the prev_hash of the first row in a tenant's chain. diff --git a/mcp/audit/redact.go b/mcp/audit/redact.go index 3acf976..ecd923b 100644 --- a/mcp/audit/redact.go +++ b/mcp/audit/redact.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/patsypppe/sentinel/mcp/canonical" + "github.com/patsypppe/toolgauge/mcp/canonical" ) // Argument redaction. diff --git a/mcp/authz/audience.go b/mcp/authz/audience.go index d6eef1a..2ca13a8 100644 --- a/mcp/authz/audience.go +++ b/mcp/authz/audience.go @@ -23,7 +23,7 @@ import ( "github.com/golang-jwt/jwt/v5" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/registry" ) // Failure modes. They are distinct types internally so logs can tell them diff --git a/mcp/authz/http.go b/mcp/authz/http.go index 238c183..20411ff 100644 --- a/mcp/authz/http.go +++ b/mcp/authz/http.go @@ -5,8 +5,8 @@ import ( "strings" "time" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" ) // TokenAuthenticator adapts Validate to the transport's Authenticator diff --git a/mcp/authz/scopes.go b/mcp/authz/scopes.go index 3901868..c1b507e 100644 --- a/mcp/authz/scopes.go +++ b/mcp/authz/scopes.go @@ -3,7 +3,7 @@ package authz import ( "strings" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) // Scope handling. diff --git a/mcp/example/cmd/main.go b/mcp/example/cmd/main.go index 2762ab6..740f8d5 100644 --- a/mcp/example/cmd/main.go +++ b/mcp/example/cmd/main.go @@ -9,7 +9,7 @@ import ( "os/signal" "syscall" - "github.com/patsypppe/sentinel/mcp/example" + "github.com/patsypppe/toolgauge/mcp/example" ) func main() { diff --git a/mcp/example/example.go b/mcp/example/example.go index 4aecec1..7b61a1d 100644 --- a/mcp/example/example.go +++ b/mcp/example/example.go @@ -12,9 +12,9 @@ import ( "fmt" "strings" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" - "github.com/patsypppe/sentinel/mcp/server" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/server" ) // Scope the single tool requires. diff --git a/mcp/go.mod b/mcp/go.mod index 827f43d..bb4c8b6 100644 --- a/mcp/go.mod +++ b/mcp/go.mod @@ -1,4 +1,4 @@ -module github.com/patsypppe/sentinel/mcp +module github.com/patsypppe/toolgauge/mcp go 1.23.0 diff --git a/mcp/handles/gc.go b/mcp/handles/gc.go index 6d611d5..380d5ad 100644 --- a/mcp/handles/gc.go +++ b/mcp/handles/gc.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/registry" ) // Handle lifecycle beyond resolution: revocation and collection. diff --git a/mcp/handles/mint.go b/mcp/handles/mint.go index c24ddbf..03c42e3 100644 --- a/mcp/handles/mint.go +++ b/mcp/handles/mint.go @@ -21,7 +21,7 @@ import ( "github.com/jackc/pgx/v5/pgxpool" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/registry" ) // Prefix makes a handle self-describing in a log or an error message. diff --git a/mcp/handles/resolve.go b/mcp/handles/resolve.go index 8880ebe..d828088 100644 --- a/mcp/handles/resolve.go +++ b/mcp/handles/resolve.go @@ -8,8 +8,8 @@ import ( "github.com/jackc/pgx/v5" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" ) // This file is the ONLY place a handle becomes usable (docs/HANDOFF.md §5). diff --git a/mcp/handles/resolve_integration_test.go b/mcp/handles/resolve_integration_test.go index efaad8d..c4bad43 100644 --- a/mcp/handles/resolve_integration_test.go +++ b/mcp/handles/resolve_integration_test.go @@ -13,9 +13,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" - "github.com/patsypppe/sentinel/mcp/store" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/store" ) const ( diff --git a/mcp/mrtr/engine.go b/mcp/mrtr/engine.go index a21713a..1c495ba 100644 --- a/mcp/mrtr/engine.go +++ b/mcp/mrtr/engine.go @@ -11,8 +11,8 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" ) // The MRTR state machine, docs/HANDOFF.md §8.5. diff --git a/mcp/mrtr/engine_integration_test.go b/mcp/mrtr/engine_integration_test.go index fe966f3..bf558f4 100644 --- a/mcp/mrtr/engine_integration_test.go +++ b/mcp/mrtr/engine_integration_test.go @@ -14,9 +14,9 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" - "github.com/patsypppe/sentinel/mcp/store" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/store" ) // Every test in the §8.5 table lives here, by the name the spec gives it. diff --git a/mcp/mrtr/idempotency.go b/mcp/mrtr/idempotency.go index e5cf9d5..6529750 100644 --- a/mcp/mrtr/idempotency.go +++ b/mcp/mrtr/idempotency.go @@ -6,7 +6,7 @@ import ( "encoding/json" "fmt" - "github.com/patsypppe/sentinel/mcp/canonical" + "github.com/patsypppe/toolgauge/mcp/canonical" ) // Argument hashing for retry verification. diff --git a/mcp/otlp/end_to_end_test.go b/mcp/otlp/end_to_end_test.go index c03dfb5..36c35b2 100644 --- a/mcp/otlp/end_to_end_test.go +++ b/mcp/otlp/end_to_end_test.go @@ -11,10 +11,10 @@ import ( "sync" "testing" - "github.com/patsypppe/sentinel/mcp/example" - "github.com/patsypppe/sentinel/mcp/otlp" - "github.com/patsypppe/sentinel/mcp/registry" - "github.com/patsypppe/sentinel/mcp/server" + "github.com/patsypppe/toolgauge/mcp/example" + "github.com/patsypppe/toolgauge/mcp/otlp" + "github.com/patsypppe/toolgauge/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/server" ) // A real kit server, the real exporter, and a stand-in collector: the trace a client diff --git a/mcp/otlp/otlp.go b/mcp/otlp/otlp.go index a5614d5..10e5edf 100644 --- a/mcp/otlp/otlp.go +++ b/mcp/otlp/otlp.go @@ -35,7 +35,7 @@ import ( "sync/atomic" "time" - "github.com/patsypppe/sentinel/mcp/transport" + "github.com/patsypppe/toolgauge/mcp/transport" ) const ( @@ -43,7 +43,7 @@ const ( batchSize = 64 flushEvery = 2 * time.Second postTimeout = 5 * time.Second - scopeName = "github.com/patsypppe/sentinel/mcp" + scopeName = "github.com/patsypppe/toolgauge/mcp" spanKindServ = 2 ) diff --git a/mcp/otlp/otlp_test.go b/mcp/otlp/otlp_test.go index f78b2bf..f91a84f 100644 --- a/mcp/otlp/otlp_test.go +++ b/mcp/otlp/otlp_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/patsypppe/sentinel/mcp/transport" + "github.com/patsypppe/toolgauge/mcp/transport" ) func quiet() *slog.Logger { return slog.New(slog.NewTextHandler(io.Discard, nil)) } diff --git a/mcp/registry/manifest.go b/mcp/registry/manifest.go index 174374a..46d5290 100644 --- a/mcp/registry/manifest.go +++ b/mcp/registry/manifest.go @@ -8,8 +8,8 @@ import ( "fmt" "sort" - "github.com/patsypppe/sentinel/mcp/canonical" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/canonical" + "github.com/patsypppe/toolgauge/mcp/envelope" ) // Deterministic manifest construction, docs/HANDOFF.md §8.3: diff --git a/mcp/registry/manifest_test.go b/mcp/registry/manifest_test.go index ae56f3d..bb24669 100644 --- a/mcp/registry/manifest_test.go +++ b/mcp/registry/manifest_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) // fakeTool is a fully-declared tool for exercising the registry. It declares diff --git a/mcp/registry/registry.go b/mcp/registry/registry.go index c8a0af9..0a8ab87 100644 --- a/mcp/registry/registry.go +++ b/mcp/registry/registry.go @@ -12,7 +12,7 @@ import ( "encoding/json" "fmt" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) // Reversibility rates a tool by how recoverable its effect is. It drives diff --git a/mcp/registry/registry_test.go b/mcp/registry/registry_test.go index e7d9a59..4ae2e0a 100644 --- a/mcp/registry/registry_test.go +++ b/mcp/registry/registry_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) // TestToolMustDeclareAllSixProperties. §7.3: enforced in the interface, not in diff --git a/mcp/server/auth.go b/mcp/server/auth.go index 430a965..157431e 100644 --- a/mcp/server/auth.go +++ b/mcp/server/auth.go @@ -5,9 +5,9 @@ import ( "net/http" "os" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" - "github.com/patsypppe/sentinel/mcp/transport" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/transport" ) func logOutput() io.Writer { return os.Stderr } diff --git a/mcp/server/server.go b/mcp/server/server.go index b47f4d2..e2563ca 100644 --- a/mcp/server/server.go +++ b/mcp/server/server.go @@ -25,10 +25,10 @@ import ( "net/http" "time" - "github.com/patsypppe/sentinel/mcp/audit" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" - "github.com/patsypppe/sentinel/mcp/transport" + "github.com/patsypppe/toolgauge/mcp/audit" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/transport" ) // Options configures New. diff --git a/mcp/server/server_test.go b/mcp/server/server_test.go index c94c89c..7bdc2ca 100644 --- a/mcp/server/server_test.go +++ b/mcp/server/server_test.go @@ -9,10 +9,10 @@ import ( "strings" "testing" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/example" - "github.com/patsypppe/sentinel/mcp/registry" - "github.com/patsypppe/sentinel/mcp/server" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/example" + "github.com/patsypppe/toolgauge/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/server" ) const meta = `"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28",` + diff --git a/mcp/transport/audit.go b/mcp/transport/audit.go index 2421598..a50d694 100644 --- a/mcp/transport/audit.go +++ b/mcp/transport/audit.go @@ -5,9 +5,9 @@ import ( "encoding/json" "time" - "github.com/patsypppe/sentinel/mcp/audit" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/audit" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" ) // AuditSink records an invocation. The transport depends on this rather than on diff --git a/mcp/transport/cacheable_test.go b/mcp/transport/cacheable_test.go index 232f649..27d680f 100644 --- a/mcp/transport/cacheable_test.go +++ b/mcp/transport/cacheable_test.go @@ -7,8 +7,8 @@ import ( "net/http/httptest" "testing" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" ) // fullServer registers every method the broker implements, so the cacheable diff --git a/mcp/transport/dispatch.go b/mcp/transport/dispatch.go index 6c2b17a..4b82ebf 100644 --- a/mcp/transport/dispatch.go +++ b/mcp/transport/dispatch.go @@ -7,7 +7,7 @@ import ( "context" "encoding/json" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) // Handler serves one MCP method. It returns a Result — an interface satisfiable diff --git a/mcp/transport/handlers.go b/mcp/transport/handlers.go index 7310218..8464451 100644 --- a/mcp/transport/handlers.go +++ b/mcp/transport/handlers.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) // DiscoverHandler answers server/discover. diff --git a/mcp/transport/headers.go b/mcp/transport/headers.go index f7931c9..0fea344 100644 --- a/mcp/transport/headers.go +++ b/mcp/transport/headers.go @@ -7,7 +7,7 @@ import ( "sort" "strings" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) // The header contract, docs/HANDOFF.md §8.2. diff --git a/mcp/transport/headers_test.go b/mcp/transport/headers_test.go index ac76773..7b10549 100644 --- a/mcp/transport/headers_test.go +++ b/mcp/transport/headers_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) func req(method, params string) envelope.Request { diff --git a/mcp/transport/http.go b/mcp/transport/http.go index 92083c4..5e07fff 100644 --- a/mcp/transport/http.go +++ b/mcp/transport/http.go @@ -10,8 +10,8 @@ import ( "strings" "time" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" ) // maxBodyBytes caps a request body. A protocol server with one endpoint has no diff --git a/mcp/transport/http_test.go b/mcp/transport/http_test.go index 6235eab..0bc1cfe 100644 --- a/mcp/transport/http_test.go +++ b/mcp/transport/http_test.go @@ -11,8 +11,8 @@ import ( "strings" "testing" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/registry" ) func testServer(t *testing.T) *httptest.Server { diff --git a/mcp/transport/span.go b/mcp/transport/span.go index ad3a70e..73935a4 100644 --- a/mcp/transport/span.go +++ b/mcp/transport/span.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) // Span is what the transport reports about one handled request, for a tracing diff --git a/mcp/transport/stdio.go b/mcp/transport/stdio.go index 86e9e2e..da7d4d8 100644 --- a/mcp/transport/stdio.go +++ b/mcp/transport/stdio.go @@ -7,7 +7,7 @@ import ( "fmt" "io" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) // Stdio is a newline-delimited JSON-RPC adapter over the same dispatch path the diff --git a/mcp/transport/stdio_test.go b/mcp/transport/stdio_test.go index 6a542d7..0bd7aa4 100644 --- a/mcp/transport/stdio_test.go +++ b/mcp/transport/stdio_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/patsypppe/sentinel/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/envelope" ) func TestStdioSharesTheDispatchPath(t *testing.T) { diff --git a/mcp/transport/toolscall.go b/mcp/transport/toolscall.go index 2ab7051..c0736af 100644 --- a/mcp/transport/toolscall.go +++ b/mcp/transport/toolscall.go @@ -7,10 +7,10 @@ import ( "fmt" "net/http" - "github.com/patsypppe/sentinel/mcp/envelope" - "github.com/patsypppe/sentinel/mcp/handles" - "github.com/patsypppe/sentinel/mcp/mrtr" - "github.com/patsypppe/sentinel/mcp/registry" + "github.com/patsypppe/toolgauge/mcp/envelope" + "github.com/patsypppe/toolgauge/mcp/handles" + "github.com/patsypppe/toolgauge/mcp/mrtr" + "github.com/patsypppe/toolgauge/mcp/registry" ) // Authenticator turns a request into a validated principal. diff --git a/site/assets/js/migration.js b/site/assets/js/migration.js index a823a8f..6910c91 100644 --- a/site/assets/js/migration.js +++ b/site/assets/js/migration.js @@ -2,7 +2,7 @@ import { h, mount, replaceContent } from "./dom.js"; import { shortRuleId } from "./model.js"; -const DOCS = "https://github.com/patsypppe/sentinel/blob/main/docs/MIGRATION.md"; +const DOCS = "https://github.com/patsypppe/toolgauge/blob/main/docs/MIGRATION.md"; function change(c) { const state = c.outstanding ? "outstanding" : c.confidence === "unknown" ? "unknown" : "done"; diff --git a/site/index.html b/site/index.html index 271d24c..37bf909 100644 --- a/site/index.html +++ b/site/index.html @@ -57,11 +57,11 @@

Grades any MCP server against the spec, clause by clause,

Everything below is rendered from JSON that toolgauge wrote, scanned . Nothing is typed in by hand. - How it was generated. + How it was generated.

Open the report - Source on GitHub + Source on GitHub

@@ -90,7 +90,7 @@

The harness, measured

Scan wall-clock
62 ms p50, conformant fixture, loopback

- Quoted from MEASUREMENTS.md, + Quoted from MEASUREMENTS.md, regenerated by make measure. The recall denominator comes from the fixture's own SEEDED_VIOLATIONS list, not from the scanner.

@@ -158,7 +158,7 @@

Five MUSTs a scan cannot settle, and does not pretend to<

So who does check them?

The broker's own test suite, which has the access a scan lacks. Per the - README: + README:

  • TestTokenForAnotherAudienceRejected
  • @@ -215,7 +215,7 @@

    A rule ID never changes meaning

    All three corrections in 0.2.0 went the same way: toolgauge had been demanding more than the specification does. Reasons are quoted from the - README. Outcomes are from + README. Outcomes are from a replay scan of the unmigrated fixture.

    @@ -271,7 +271,7 @@

    oracle fixtures Python

    Broker claims are summarized from the - README. + README. The rule count is the length of the scan reports on this page.
    @@ -307,7 +307,7 @@

    Regenerating this page's data

    The script header lists the prerequisites: the broker, both fixtures, and a dev token from broker mint-token.

    -

    Commands quoted from the repository README.

    +

    Commands quoted from the repository README.

    @@ -318,12 +318,12 @@

    Regenerating this page's data

    disagree, the specification wins and this repository is wrong.

    - + From 85e0b846234fcb170b9ca7b6c80f8ce74e5deba9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 18:06:12 +0000 Subject: [PATCH 2/3] docs: pip-first README, CONTRIBUTING.md and issue templates Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Pdv7R172x4XFq6AxwmmBBF --- .github/ISSUE_TEMPLATE/config.yml | 1 + .github/ISSUE_TEMPLATE/false-positive.yml | 23 +++++ .github/ISSUE_TEMPLATE/new-rule.yml | 16 ++++ CONTRIBUTING.md | 103 ++++++++++++++++++++++ README.md | 28 +++--- 5 files changed, 161 insertions(+), 10 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/false-positive.yml create mode 100644 .github/ISSUE_TEMPLATE/new-rule.yml create mode 100644 CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..0086358 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: true diff --git a/.github/ISSUE_TEMPLATE/false-positive.yml b/.github/ISSUE_TEMPLATE/false-positive.yml new file mode 100644 index 0000000..70a86b9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/false-positive.yml @@ -0,0 +1,23 @@ +name: Wrong finding (false positive or false negative) +description: toolgauge reported something that is not true, or missed something that is. +labels: ["bug", "area: precision"] +body: + - type: input + id: rule + attributes: + label: Rule id + placeholder: MCP/2026-07-28/MUST/... or TOOLGAUGE/STYLE/... + validations: + required: true + - type: textarea + id: observed + attributes: + label: What toolgauge said, and what is actually true + description: Paste the finding. For a spec rule, quote the specification sentence that shows it is wrong. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: How to reproduce + description: The command you ran. Do not paste real credentials. diff --git a/.github/ISSUE_TEMPLATE/new-rule.yml b/.github/ISSUE_TEMPLATE/new-rule.yml new file mode 100644 index 0000000..b511911 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-rule.yml @@ -0,0 +1,16 @@ +name: New rule +description: Propose a check toolgauge does not do yet. +labels: ["enhancement", "area: rules"] +body: + - type: textarea + id: requirement + attributes: + label: The requirement + description: For a spec rule, quote the sentence and link the page and heading. Otherwise, explain why it matters. + validations: + required: true + - type: textarea + id: check + attributes: + label: How it can be checked from outside the server + description: What request to send, what answer is a pass, a fail, and "can't tell". diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..7bcdebb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,103 @@ +# Contributing to toolgauge + +Thanks for helping. toolgauge checks MCP servers against the specification, so most changes are +one of four kinds: a new or corrected rule, a new server in a benchmark, a fix to how a finding is +reported, or documentation. Each is described below with the exact files to touch. + +## Pick something to work on + +The [issue tracker](https://github.com/patsypppe/toolgauge/issues) holds scoped tasks, each with what to +change and how to know it is done. + +- `good first issue`: small and self-contained, a few hours at most. A good place to start. +- `help wanted`: larger, or needing judgement about the specification. +- Area labels (`area: rules`, `area: benchmarks`, `area: precision`, `area: docs`, `area: transport`) + say which part of the code is involved. + +Comment on an issue before starting so two people do not do the same work. If an issue is unclear, +ask in the issue; that is a useful contribution too. + +## Set up + +You need Python 3.12 or newer and [uv](https://docs.astral.sh/uv/). Go 1.23 or newer is only needed +for the Go parts (`broker/`, `mcp/`, `fleet/`); most harness work never touches them. + +```bash +git clone https://github.com/patsypppe/toolgauge +cd toolgauge +uv sync +uv run toolgauge demo # should print one FAIL verdict and one PASS verdict +``` + +## Test + +Python only (what most changes need): + +```bash +uv run ruff format . # format +uv run ruff check . # lint +uv run mypy # type-check (strict) +uv run pytest tests -m unit -q -n auto +``` + +Everything, including Go (what CI runs): + +```bash +make check +``` + +All of it must pass before a pull request is merged. CI runs the same commands on every push. + +## Where things live + +| Path | What it is | +|---|---| +| `harness/src/toolgauge/catalog/must/`, `should/` | Rules that restate the MCP specification (`MCP//MUST/...`, `SHOULD/...`) | +| `harness/src/toolgauge/catalog/beyond/` | Rules that go beyond the spec (`TOOLGAUGE/STYLE`, `SECURITY`, `OPS`) | +| `harness/src/toolgauge/probe/` | The HTTP and stdio transports. Requests are built by hand on purpose; no MCP SDK | +| `harness/src/toolgauge/report/` | Text, JSON, SARIF, HTML, JUnit and Markdown output | +| `fixtures/server/` | Practice servers: `nonconformant` (seeded violations), `conformant`, `partial` | +| `tests/harness/` | Tests. Most run the practice servers in-process | +| `benchmarks/` | Measurements against servers this project did not write | + +## Adding or changing a rule + +1. **Find the sentence first.** A spec rule restates a sentence in the specification. Quote it in the + pull request, with the page and heading. If you cannot find one, the rule belongs in `beyond/` with a + `rationale`, not in the spec namespace. +2. **Match the severity to the text.** MUST in the spec is a `Severity.MUST` rule; SHOULD is SHOULD. +3. **Never fail what you cannot see.** If the wire cannot tell a violation from an allowed behaviour, + return `RuleResult.indeterminate(...)` and say why. A false FAIL costs more than a missed one. +4. **Rule ids are permanent.** Never rename or delete one. To change a rule's meaning or severity, set + `deprecated_in` and `superseded_by` on the old rule and add a new one. +5. **Test both directions.** The `nonconformant` fixture should fail it (tag the violation with a + `# VIOLATES ` comment and add the id to `SEEDED_VIOLATIONS`), and the `conformant` fixture + should pass it. `tests/harness/test_fixture_oracle.py` checks both. + +## Adding a server to a benchmark + +`benchmarks/popular_servers.py` has a `SERVERS` list. Add an entry with the start command from the +server's README (`npx -y ...` or `uvx ...`) and a dummy value for any credential it needs just to +start. Run `uv run python benchmarks/popular_servers.py`, then `uv run python benchmarks/chart.py`, and +commit the regenerated `POPULAR_SERVERS.md`, `.json` and `.svg`. Never add a server's real credentials. + +## Checking precision + +A finding is only useful if it is right. `benchmarks/PRECISION.md` records hand-checked samples. To add +one: draw findings at random with a fixed seed, read each against the specification text or the tool's +actual description, and record each verdict (correct, low value or wrong) with a one-line reason. Wrong +verdicts are the most valuable result: open an issue for each. + +## Sending a change + +- One change per pull request, on a branch. +- Commit messages: `: `, where type is one of `feat`, `fix`, `refactor`, `docs`, + `test`, `chore`, `perf`, `ci`. +- In the description, say what changed, why, and how you tested it. For a rule, quote the spec sentence. +- Do not scan servers you do not own or have permission to test, and never publish another project's + findings before its maintainers have seen them. + +## Code of conduct + +Be kind and assume good faith. Disagreements about what the specification means are normal; settle +them by quoting the text. diff --git a/README.md b/README.md index b0c4eea..1a68de5 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,20 @@ You give it a server's URL. It sends the server test requests, checks each answe rulebook, and prints a pass/fail list. Every failure says what it saw, what to change, and which page of the spec the rule comes from. -## Try it in 30 seconds - -No clone, no setup, nothing to configure. This starts two practice servers on your machine (one -with deliberate mistakes, one correct), scans both, and shows you the difference: +## Install ```bash -uvx --from "git+https://github.com/patsypppe/toolgauge@main#subdirectory=harness" toolgauge demo +pip install toolgauge # or: pipx install toolgauge +toolgauge demo ``` +No install at all: `uvx toolgauge demo`. Needs Python 3.12 or newer. + +## Try it in 30 seconds + +`toolgauge demo` starts two practice servers on your machine (one with deliberate mistakes, one +correct), scans both, and shows you the difference: + ``` === Server A: has deliberate mistakes === required rules (MUST): 4 pass, 33 fail, 7 could not be checked from outside @@ -33,7 +38,7 @@ uvx --from "git+https://github.com/patsypppe/toolgauge@main#subdirectory=harness verdict: PASS (exit code 0) ``` -Prefer a clone? `git clone https://github.com/patsypppe/toolgauge && cd sentinel && uv run toolgauge demo`. +From source: `git clone https://github.com/patsypppe/toolgauge && cd toolgauge && uv run toolgauge demo`. ## Lint any server, including stdio ones @@ -49,11 +54,8 @@ rule fails, so it drops straight into CI. ## Scan your own server -No clone needed: - ```bash -uvx --from "git+https://github.com/patsypppe/toolgauge@main#subdirectory=harness" \ - toolgauge scan --endpoint https://your-server.example/mcp --gate must +toolgauge scan --endpoint https://your-server.example/mcp --gate must ``` If the server needs a login token, add `--token "$TOKEN"`. Only scan servers you own or have @@ -192,6 +194,12 @@ recommendations, so they are reported but never fail the gate. The rule names ca toolgauge never imports the server code. It only talks HTTP, so it can be pointed at anything. +## Contributing + +There is a list of concrete, scoped tasks on the +[issue tracker](https://github.com/patsypppe/toolgauge/issues), many labelled `good first issue`. +[`CONTRIBUTING.md`](CONTRIBUTING.md) explains how to set up, test and send a change. + ## Test toolgauge itself ```bash From 4657ec5d4f62bfb415100b1ff96c3e88d10a7ab9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 18:10:36 +0000 Subject: [PATCH 3/3] fix: build the wheel from the sdist with the practice servers bundled; PyPI README The static force-include of ../fixtures/server worked from a checkout but not when the wheel is built from the sdist (how PyPI releases are built), which would have failed the release. A build hook now finds the practice servers in either layout and ships them in both artifacts. harness/README.md, the PyPI page, is rewritten pip-first. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Pdv7R172x4XFq6AxwmmBBF --- harness/README.md | 55 +++++++++++++++++++++++++++--------------- harness/hatch_build.py | 28 +++++++++++++++++++++ harness/pyproject.toml | 3 +-- 3 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 harness/hatch_build.py diff --git a/harness/README.md b/harness/README.md index 5a85d86..40f9584 100644 --- a/harness/README.md +++ b/harness/README.md @@ -1,29 +1,44 @@ -# toolgauge — MCP conformance +# toolgauge -A conformance checker for **MCP servers**, published as `toolgauge`. +**Check whether an MCP server follows the MCP specification, and whether its tools are documented well +enough for a model to use them.** -Scans any MCP server, grades it against the normative requirements with a spec citation per rule, -reports which MUSTs it **cannot** verify black-box rather than scoring them as passes, and -inventories which deprecated features the target still depends on with their earliest removal dates. +[MCP](https://modelcontextprotocol.io) (Model Context Protocol) is how AI apps connect to tools. +toolgauge sends a server real requests, checks every answer against the specification, and lints its +tool descriptions. Each finding says what it saw, what to change, and which part of the spec it comes from. + +## Install + +```bash +pip install toolgauge # or: pipx install toolgauge +``` + +Or run it without installing: `uvx toolgauge demo`. Needs Python 3.12 or newer. + +## Use ```bash -uvx toolgauge scan --endpoint http://localhost:8080/mcp --gate must -toolgauge scan --endpoint https://someone-elses.example/mcp --safe # never invokes a non-read-only tool -toolgauge registry-scan --out-dir scan # every remote server in the MCP registry -uv run toolgauge scan --endpoint http://localhost:8080/mcp --gate must --format sarif --out scan.sarif -uv run toolgauge deprecations --endpoint http://localhost:8080/mcp --as-of 2026-09-23 -uv run toolgauge catalog validate +toolgauge demo # scan two practice servers, see the difference +toolgauge lint --command "npx -y @modelcontextprotocol/server-filesystem /tmp" # lint a stdio server +toolgauge scan --endpoint https://your-server.example/mcp --gate must # grade an HTTP server +toolgauge scan --endpoint https://your-server.example/mcp --format sarif --out scan.sarif ``` -`scan --gate must` exits **1** if any MUST rule FAILs and **0** otherwise. `INDETERMINATE` never -fails a gate but is always printed. Every other subcommand reserves non-zero for harness errors, so -CI can distinguish "the server is wrong" from "the scanner broke". +`--gate must` exits **1** if a required rule fails and **0** otherwise, so it drops into CI. A requirement +that cannot be checked from outside the server is reported as `INDETERMINATE`, never as a pass. Use +`--safe` on a server you do not own: it never invokes a tool that is not annotated read-only. + +## What it has measured + +Every remote server in the official MCP registry (15,451 endpoints), 28 widely used published servers, +and the MCP maintainers' own broken test servers, with hand-checked precision samples and a comparison +against Cisco's mcp-scanner. Details and methodology: +[github.com/patsypppe/toolgauge/tree/main/benchmarks](https://github.com/patsypppe/toolgauge/tree/main/benchmarks). -The probe is a deliberately literal MCP client — requests are built by hand from the specification, -never through an SDK, because an SDK that helpfully adds `Mcp-Method` would make the rule requiring -`Mcp-Method` untestable. +## Links -Responses framed as `text/event-stream` are read the same as bare JSON: the transport allows both, -and grading an SSE reply as "no JSON-RPC response" would be the scanner's bug, not the server's. +- Source, issues and contributing guide: [github.com/patsypppe/toolgauge](https://github.com/patsypppe/toolgauge) +- Usage guide (waivers, baselines, CI, report formats): [docs/USING.md](https://github.com/patsypppe/toolgauge/blob/main/docs/USING.md) +- Changelog: [CHANGELOG.md](https://github.com/patsypppe/toolgauge/blob/main/CHANGELOG.md) -See the repository root for the full project, including `broker`, the server this harness grades. +MIT licensed. diff --git a/harness/hatch_build.py b/harness/hatch_build.py new file mode 100644 index 0000000..9126260 --- /dev/null +++ b/harness/hatch_build.py @@ -0,0 +1,28 @@ +"""Bundle the practice servers (`fixtures/server`) into both the sdist and the wheel. + +They live at the repository root, outside this package's directory, so a static +force-include works from a checkout but not when the wheel is built from the sdist, +which is how PyPI releases are built. This hook finds them in either layout. +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any + +from hatchling.builders.hooks.plugin.interface import BuildHookInterface + + +class CustomBuildHook(BuildHookInterface): # type: ignore[type-arg] + def initialize(self, version: str, build_data: dict[str, Any]) -> None: + root = Path(self.root) + for source in (root.parent / "fixtures" / "server", root / "fixtures" / "server"): + if source.is_dir(): + target = ( + "toolgauge/_fixtures/server" + if self.target_name == "wheel" + else "fixtures/server" + ) + build_data["force_include"][str(source)] = target + return + raise FileNotFoundError("the practice servers (fixtures/server) were not found") diff --git a/harness/pyproject.toml b/harness/pyproject.toml index eb07679..9d8065e 100644 --- a/harness/pyproject.toml +++ b/harness/pyproject.toml @@ -40,5 +40,4 @@ build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/toolgauge"] -[tool.hatch.build.targets.wheel.force-include] -"../fixtures/server" = "toolgauge/_fixtures/server" +[tool.hatch.build.hooks.custom]