Skip to content

mcp-server-smartsheet-rm changelog

io.github.christianclaudio/smartsheet-rm

15 changes recorded. MCP Harbor re-reads this listing from the official registry every six hours and asks the live server what it exposes, and keeps what changed between one look and the next.

  • server.json 1 field changed
    version
    2.3.0 2.3.1

  • server.json 1 field changed
    version
    2.2.0 2.3.0

  • AGENTS.md +4 −3

    https://raw.githubusercontent.com/christianclaudio/mcp-server-smartsheet-rm/HEAD/AGENTS.md

    − - `.github/workflows/` — `ci.yml`, `release.yml` (on a `v*` tag: build with full history, check the wheel version matches the tag, build the release notes, publish to PyPI, create the GitHub Release from `scripts/release_notes.py`, stamp the tag version into `server.json` and publish to the MCP Registry, then push the Docker image only after that job succeeds), `rm-drift-monitor.yml`, `dependabot-automerge.yml` (squash auto-merge only for Dependabot PRs whose highest update is minor or patch; ma−    - Trust stores follow FastMCP's [certificate authority incident notice (October 2026)](https://github.com/PrefectHQ/fastmcp/blob/main/SECURITY.md#certificate-authority-incident-october-2026): both Docker stages upgrade the OS `ca-certificates` package and fail the build below the patched Debian version ([DSA-6572-1](https://security-tracker.debian.org/tracker/DSA-6572-1); `ARG CA_CERTIFICATES_FLOOR`, declared once), the builder does so before any pip runs because pip trusts the OS store as we− CI is defined in `.github/workflows/ci.yml` (jobs: lint and types, tests on Python 3.10–3.14 at 100% coverage, tool contract, OpenAPI drift, build + `scripts/check_version.py` + `twine check`, protocol and conformance, `docker-build` (checks that the builder stage fails at its `ca-certificates` floor check, before any pip step, when the floor is above the installed version; it does not build or push the full image), CodeQL). Jobs that install or build the package check out with `fetch-depth: 0`,+ - `.github/workflows/` — `ci.yml`, `release.yml` (on a `v*` tag: build with full history, check the wheel version matches the tag, build the release notes, publish to PyPI, create the GitHub Release from `scripts/release_notes.py`, stamp the tag version into `server.json` and publish to the MCP Registry, then push the Docker image only after that job succeeds), `dependabot-automerge.yml` (squash auto-merge only for Dependabot PRs whose highest update is minor or patch; major updates wait for a h+    - Trust stores follow FastMCP's [certificate authority incident notice (October 2026)](https://github.com/PrefectHQ/fastmcp/blob/main/SECURITY.md#certificate-authority-incident-october-2026): both Docker stages upgrade the OS `ca-certificates` package and fail the build below the patched Debian version ([DSA-6572-1](https://security-tracker.debian.org/tracker/DSA-6572-1); `ARG CA_CERTIFICATES_FLOOR`, declared once), the builder does so before any pip runs because pip trusts the OS store as we+    - Runtime dependencies install from `uv.lock`, so the image runs the versions CI tests: the builder installs uv from `docker/uv-bootstrap.txt` (hash-pinned, `--require-hashes`, into `/tmp/uv` outside the venv, so the runtime image has no uv), runs `uv export --locked --no-dev --no-emit-project --format requirements.txt` (base dependencies only, a hash for every package, and the build fails if `uv.lock` is stale), installs that file with pip in hash-checking mode, then installs the project bui+ CI is defined in `.github/workflows/ci.yml` (jobs: lint and types, tests on Python 3.11–3.14 at 100% coverage, tool contract, OpenAPI drift, build + `scripts/check_version.py` + `twine check`, protocol and conformance, `docker-build` (builds the full image without pushing it, checks that the runtime image has the project and none of the build-only packages (runtime dependencies such as `packaging` stay), checks that the image answers `initialize` and `tools/list` over stdio with no network, and 

  • server.json 1 field changed
    version
    2.1.1 2.2.0

  • server.json 1 field changed
    version
    2.1.0 2.1.1
  • AGENTS.md +14 −5

    https://raw.githubusercontent.com/christianclaudio/mcp-server-smartsheet-rm/HEAD/AGENTS.md

    − | **Smartsheet RM Developer Portal** | [`https://developer.smartsheet.com/10000ft-api/`](https://developer.smartsheet.com/10000ft-api/) | Smartsheet RM (10,000ft) REST API reference, endpoints, schemas, authentication |− This is `mcp-server-smartsheet-rm` — an enterprise Python Model Context Protocol (MCP) server for **Resource Management by Smartsheet** (formerly 10,000ft API), built on its public REST API. What is covered is defined by the tool catalog and checked by `scripts/check_openapi_drift.py` (the client against the endpoint list in `ENDPOINT_TO_METHOD`, and against an OpenAPI spec when given `--spec-file` or `--spec-url`); do not claim complete API coverage. The default `full` profile lists every tool;− # Upstream OpenAPI / route drift check− uv run python scripts/check_openapi_drift.py− CI is defined in `.github/workflows/ci.yml` (jobs: lint and types, tests on Python 3.10–3.13 at 100% coverage, tool contract, OpenAPI drift, build + `scripts/check_version.py` + `twine check`, protocol and conformance, CodeQL). Jobs that install or build the package check out with `fetch-depth: 0`, because a shallow checkout has no reachable tag and reports `0.0.1.devN`. The Docker image is built only in `release.yml`, with `UV_DYNAMIC_VERSIONING_BYPASS` set to the tag version (the build context+ | **Smartsheet Developer Portal** | [`https://developers.smartsheet.com/llms.txt`](https://developers.smartsheet.com/llms.txt) | Resource Management API reference ([`/api/resource_management/openapi`](https://developers.smartsheet.com/api/resource_management/openapi), OpenAPI JSON at [`/_spec/api/resource_management/openapi.json`](https://developers.smartsheet.com/_spec/api/resource_management/openapi.json)), authentication and base URLs, changelog |+ This is `mcp-server-smartsheet-rm` — an enterprise Python Model Context Protocol (MCP) server for **Resource Management by Smartsheet** (formerly 10,000ft API), built on its public REST API. 46 method and path pairs the client calls are in neither the current API reference nor the archived 10,000ft docs; they stay (never drop or re-point a tool on this public repo), each says "Undocumented route" in its client and tool docstrings, and `README.md` lists them. What is covered is defined by the too+ Smartsheet Gov (`GOV_BASE_URL` in `client.py`, from the RM API Introduction and Authentication pages) is opt-in: never make it a default or add it to `DEFAULT_ALLOWED_HOSTS`. Its extra Smartsheet token goes out as `Authorization: Bearer` only when the base URL host is `GOV_HOST`, and a per-request RM token is never paired with the environment Gov token.+    - Trust stores follow FastMCP's [certificate authority incident notice (October 2026)](https://github.com/PrefectHQ/fastmcp/blob/main/SECURITY.md#certificate-authority-incident-october-2026): both Docker stages upgrade the OS `ca-certificates` package and fail the build below the patched Debian version ([DSA-6572-1](https://security-tracker.debian.org/tracker/DSA-6572-1); `ARG CA_CERTIFICATES_FLOOR`, declared once), the builder does so before any pip runs because pip trusts the OS store as we+    - The pip bootstrap is hash-pinned in `docker/pip-bootstrap.txt` and installed with `--require-hashes`, so it does not depend on the TLS trust of the base image's older pip and certifi; whoever raises the pip floor updates that pin and its sha256 from PyPI (`https://pypi.org/pypi/pip/<version>/json`, the `py3-none-any` wheel's `digests.sha256`), because Dependabot does not track the file.+    - Lowering `CA_CERTIFICATES_FLOOR` with `--build-arg` is unsupported; the build-arg exists only so CI can raise it for the negative floor check, and CI and the release workflow never pass it otherwise.+ 6. **Outbound Requests**:+    - Outbound requests must enforce HTTPS, disallow loopback/private/internal IP addresses, and offload DNS hostname verification to worker threads via `asyncio.to_thread` in `SSRFSafeAsyncTransport` to protect event loop responsiveness. The transport then connects only to an address it checked, trying the next checked address if a connect fails while the request's connect timeout lasts (one deadline shared by all the addresses, not a fresh timeout for each), and keeps the hostname in the URL fo+ uv 0.13.4 or later is required (`[tool.uv] required-version` in `pyproject.toml`, from FastMCP's certificate authority incident notice); older uv refuses to run, and CI's `setup-uv` installs from that setting.+ # Upstream OpenAPI / route drift check (a spec is required; exit 1 = drift, 2 = any other failure)+ uv run python scripts/check_openapi_drift.py --spec-url https://developers.smartsheet.com/_spec/api/resource_management/openapi.json+ # Offline, against the paths-only fixture the tests use+ uv run python scripts/check_openapi_drift.py --spec-file tests/fixtures/rm-openapi-paths.json+ CI is defined in `.github/workflows/ci.yml` (jobs: lint and types, tests on Python 3.10–3.14 at 100% coverage, tool contract, OpenAPI drift, build + `scripts/check_version.py` + `twine check`, protocol and conformance, `docker-build` (checks that the builder stage fails at its `ca-certificates` floor check, before any pip step, when the floor is above the installed version; it does not build or push the full image), CodeQL). Jobs that install or build the package check out with `fetch-depth: 0`,

  • server.json 2 fields changed
    env_vars
    SMARTSHEET_RM_API_TOKEN, SMARTSHEET_RM_BASE_URL SMARTSHEET_RM_API_TOKEN, SMARTSHEET_RM_BASE_URL, SMARTSHEET_RM_GOV_SMARTSHEET_API_TOKEN, SMARTSHEET_RM_ALLOWED_HOSTS
    version
    2.0.4 2.1.0

  • server.json 1 field changed
    version
    2.0.3 2.0.4

  • server.json 1 field changed
    version
    2.0.2 2.0.3
  • AGENTS.md +9 −9

    https://raw.githubusercontent.com/christianclaudio/mcp-server-smartsheet-rm/HEAD/AGENTS.md

    − This is `mcp-server-smartsheet-rm` — an enterprise Python Model Context Protocol (MCP) server covering the entire REST API surface for **Resource Management by Smartsheet** (formerly 10,000ft API). The default `full` profile lists all 100 tools; the two bulk-destructive tools are listed but refused at call time unless enabled. Expected per-profile counts live in `scripts/check_tool_contract.py`.− - `src/smartsheet_rm_mcp/common.py` — client resolution, `@rm_tool` decorator, `_destructive_gate`, secret redaction, structured logging. `middleware.py` — gateway audit middleware, the annotation-driven `ReadOnlyGateMiddleware`, and domain guardrails (including the bulk gate).− - `src/smartsheet_rm_mcp/client.py` — async HTTP client (`SmartsheetRMClient`). `errors.py` — structured exceptions and redaction. `config.py` — `SMARTSHEET_RM_*` settings.− - `tests/` — unit tests are offline; live network tests live in `tests/test_e2e_live.py` (run via `-m e2e`). Default pytest `addopts` deselect that marker with `-m 'not e2e'`; `test_all_discovered_tools_live` skips unless `SMARTSHEET_RM_API_TOKEN` is set. Offline modules: `tests/test_client.py`, `tests/test_errors.py`, `tests/test_server.py`, `tests/test_layered.py`, `tests/test_profiles.py`, `tests/test_protocol.py`, `tests/test_scripts.py`, `tests/test_version.py`, `tests/test_release_notes.py−   - Tool Search (`regex` or `bm25`) and experimental Code Mode attach only on `full`, never both; on other profiles they log a warning and keep the flat list. `search_tools`, `search` and `get_schema` are annotated `readOnlyHint=True`; Code Mode `execute` is refused under read-only.−    - Every destructive tool (e.g. deletion, bulk removal, or permanent deallocation) must accept `confirm: bool = False` and invoke `_destructive_gate`. If `False`, return a structured error document requiring explicit confirmation before executing side effects. Non-destructive mutations (creates, updates, workflows) are protected by `ReadOnlyGateMiddleware` without requiring interactive confirmation.−    - Error messages, logs, and tracebacks must pass through regex redaction (`_redact_secrets`) stripping Bearer tokens, passwords, and API keys.− uv sync --locked --extra dev   # or uv pip install -e ".[dev]"− CI is defined in `.github/workflows/ci.yml` (jobs: lint and types, tests on Python 3.10–3.13 at 100% coverage, tool contract, OpenAPI drift, build + `scripts/check_version.py` + `twine check`, protocol and conformance, CodeQL). Jobs that install or build the package check out with `fetch-depth: 0`, because a shallow checkout has no reachable tag and reports `0.0.1.devN`. The Docker image is built only in `release.yml`, with `UV_DYNAMIC_VERSIONING_BYPASS` set to the tag version (the build context+ This is `mcp-server-smartsheet-rm` — an enterprise Python Model Context Protocol (MCP) server for **Resource Management by Smartsheet** (formerly 10,000ft API), built on its public REST API. What is covered is defined by the tool catalog and checked by `scripts/check_openapi_drift.py` (the client against the endpoint list in `ENDPOINT_TO_METHOD`, and against an OpenAPI spec when given `--spec-file` or `--spec-url`); do not claim complete API coverage. The default `full` profile lists every tool;+ - `src/smartsheet_rm_mcp/common.py` — client resolution, `@rm_tool` decorator (a failed call raises FastMCP `ToolError` with the redacted `{"error": ...}` JSON, so `isError: true`), `_destructive_gate`, secret redaction, structured logging. `middleware.py` — gateway audit middleware, the annotation-driven `ReadOnlyGateMiddleware`, and domain guardrails (including the bulk gate).+ - `src/smartsheet_rm_mcp/client.py` — async HTTP client (`SmartsheetRMClient`). `auth.py` — `SMARTSHEET_RM_MCP_AUTH_TOKEN` bearer verifier and the tokenless non-localhost HTTP bind refusal. `errors.py` — structured exceptions and redaction. `config.py` — `SMARTSHEET_RM_*` settings.+ - `tests/` — unit tests are offline; live network tests live in `tests/test_e2e_live.py` (run via `-m e2e`). Default pytest `addopts` deselect that marker with `-m 'not e2e'`; `test_all_discovered_tools_live` skips unless `SMARTSHEET_RM_API_TOKEN` is set. Offline modules: `tests/test_client.py`, `tests/test_errors.py`, `tests/test_server.py`, `tests/test_layered.py`, `tests/test_profiles.py`, `tests/test_protocol.py`, `tests/test_scripts.py`, `tests/test_version.py`, `tests/test_release_notes.py+   - Tool Search (`regex` or `bm25`) and experimental Code Mode attach only on `full`, never both; on other profiles they log a warning and keep the flat list. Code Mode needs `pydantic-monty`, shipped by the public `code-mode` extra (`fastmcp[code-mode]`, same floor as `dev`); Code Mode tests call `pytest.importorskip("pydantic_monty")`, and `tests/conftest.py` aborts a CI session (`CI` set) without it, so CI syncs `--extra dev --extra code-mode`; when it is missing, Code Mode is skipped with a +    - Every destructive tool (e.g. deletion, bulk removal, or permanent deallocation) must accept `confirm: bool = False` and invoke `_destructive_gate`. If `False`, `_destructive_gate` returns the confirm prompt as a normal result (`isError: false`, `"status": "confirmation_required"`, re-call with `confirm=true`) and the tool executes no side effects. Input validation failures call `_invalid_request(...)`, which raises `ToolError` with the redacted payload (`isError: true`); do not use it for t+    - Error messages, logs, and tracebacks must pass through regex redaction (`_redact_secrets`) stripping Bearer tokens, passwords, and API keys. A PEM block runs from `-----BEGIN <label>-----` to the `-----END <label>-----` with the same label (any case; an END with another label does not end it, and a `-----BEGIN` with no matching END is masked to the end of the text). A `{...}`/`[...]`/`(...)` value after a credential key or a token key, quoted or not (a Python `repr` such as `{'api_key': ['x+ uv sync --locked --extra dev --extra code-mode   # or uv pip install -e ".[dev,code-mode]"+ CI is defined in `.github/workflows/ci.yml` (jobs: lint and types, tests on Python 3.10–3.13 at 100% coverage, tool contract, OpenAPI drift, build + `scripts/check_version.py` + `twine check`, protocol and conformance, CodeQL). Jobs that install or build the package check out with `fetch-depth: 0`, because a shallow checkout has no reachable tag and reports `0.0.1.devN`. The Docker image is built only in `release.yml`, with `UV_DYNAMIC_VERSIONING_BYPASS` set to the tag version (the build context

  • server.json 1 field changed
    version
    2.0.1 2.0.2
  • server.json 1 field changed
    version
    2.0.0 2.0.1

  • AGENTS.md +47 −64

    https://raw.githubusercontent.com/christianclaudio/mcp-server-smartsheet-rm/HEAD/AGENTS.md

    − This is `mcp-server-smartsheet-rm` — an enterprise Python Model Context Protocol (MCP) server exposing 98 tools by default (100 with bulk-destructive operations enabled) covering the entire REST API surface for **Resource Management by Smartsheet** (formerly 10,000ft API).− ## 🏗️ Architecture Blueprint− ```− mcp-server-smartsheet-rm/− ├── src/smartsheet_rm_mcp/− │   ├── __init__.py               # Package version (__version__) and public exports− │   ├── server.py                 # FastMCP 4 root gateway, composition mounting, profiles, tool search− │   ├── middleware.py             # Gateway audit/readonly middleware & domain guardrails− │   ├── common.py                 # Client resolution, decorator (@rm_tool), secret redaction, structured logging− │   ├── config.py                 # Pydantic settings with SMARTSHEET_RM_* env bindings− │   ├── client.py                 # Async HTTP client (httpx.AsyncClient, retries, jitter, auth headers)− │   ├── errors.py                 # Structured API exceptions and regex secret redaction− │   └── tools/                    # Modular domain sub-servers− │       ├── __init__.py           # Sub-server and tool function re-exports− │       ├── time.py               # Time tracking, suggestions, approval, timesheets sub-server− │       ├── projects.py           # Projects, phases, assignments, placeholders sub-server− │       └── admin.py              # Users, roles, clients, expenses, tags, reports sub-server− ├── scripts/− │   ├── check_conformance.sh      # MCP Protocol conformance suite verification script− │   ├── check_tool_contract.py    # AST/reflection contract testing total tool & annotation counts− │   ├── check_openapi_drift.py    # AST visitor checking client methods against upstream API routes− │   └── determine_bump.py         # SemVer release bump recommendation based on git log− ├── tests/− │   ├── conftest.py               # Shared fixtures and mock HTTP transports (offline only)− │   ├── test_client.py            # Unit tests for HTTP client, retries, headers, and error handling− │   ├── test_determine_bump.py    # Unit tests for determine_bump.py SemVer calculation− │   ├── test_server.py            # Tests for tool execution, parameter validation, and confirmation gating− │   ├── test_layered.py           # FastMCP 4 composition, profiles, middleware, and domain guard tests− │   ├── test_errors.py            # Tests for error formatting and regex credential redaction− │   ├── test_scripts.py           # Unit tests for contract and drift validation scripts− │   └── test_protocol.py          # FastMCP 4 in-memory & stdio/streamable HTTP protocol verification− ├── .github/workflows/− │   ├── ci.yml                    # Multi-job matrix: lint, py3.10-3.13 tests, contracts, CodeQL, docker build− │   ├── release.yml               # Automated release on v* tags: wheels, sdist, CycloneDX SBOM, GHCR docker− │   └── drift-monitor.yml         # Scheduled upstream schema drift check− ├── Dockerfile                    # Multi-stage container build running as non-root USER mcp− ├── fastmcp.json                  # FastMCP 4 deployment and execution manifest− ├── conformance-baseline.yml      # Conformance suite baseline expected failures− ├── server.json                   # MCP Registry catalog metadata (runtimeHint: uvx, stdio transport)− ├── pyproject.toml                # Packaging metadata, entrypoint CLI, dependency pinning+ This is `mcp-server-smartsheet-rm` — an enterprise Python Model Context Protocol (MCP) server covering the entire REST API surface for **Resource Management by Smartsheet** (formerly 10,000ft API). The default `full` profile lists all 100 tools; the two bulk-destructive tools are listed but refused at call time unless enabled. Expected per-profile counts live in `scripts/check_tool_contract.py`.+ ## 🏗️ Key Paths+ - `src/smartsheet_rm_mcp/server.py` — FastMCP 4 root gateway (`create_server`): domain mounts, job allowlists, read-only filter, Tool Search / Code Mode (full only).+ - `src/smartsheet_rm_mcp/profiles.py` — `PROFILES` (each with a one-line `job`), `FULL_ONLY_TOOLS`, `ReadOnlyToolFilter`, `is_read_only_tool`.+ - `src/smartsheet_rm_mcp/tools/{time,projects,admin}.py` — domain sub-servers; re-exported from `tools/__init__.py`.+ - `src/smartsheet_rm_mcp/common.py` — client resolution, `@rm_tool` decorator, `_destructive_gate`, secret redaction, structured logging. `middleware.py` — gateway audit middleware, the annotation-driven `ReadOnlyGateMiddleware`, and domain guardrails (including the bulk gate).+ - `src/smartsheet_rm_mcp/client.py` — async HTTP client (`SmartsheetRMClient`). `errors.py` — structured exceptions and redaction. `config.py` — `SMARTSHEET_RM_*` settings.+ - `scripts/check_tool_contract.py` — source of truth for expected tool counts and annotations. Do not hard-code tool counts elsewhere.+ - `scripts/check_openapi_drift.py`, `scripts/check_conformance.sh` + `conformance-baseline.yml`.+ - `scripts/release_notes.py` — release body from squash commits since the previous `v*` tag. `scripts/check_version.py` — runs after the build and reads the version from the single wheel in `dist/` (the file that ships, as release.yml's tag check does); fails on `0.0.0` (no git metadata) or `0.0.1.devN` (no reachable tag, a shallow checkout).+ - `tests/` — unit tests are offline; live network tests live in `tests/test_e2e_live.py` (run via `-m e2e`). Default pytest `addopts` deselect that marker with `-m 'not e2e'`; `test_all_discovered_tools_live` skips unless `SMARTSHEET_RM_API_TOKEN` is set. Offline modules: `tests/test_client.py`, `tests/test_errors.py`, `tests/test_server.py`, `tests/test_layered.py`, `tests/test_profiles.py`, `tests/test_protocol.py`, `tests/test_scripts.py`, `tests/test_version.py`, `tests/test_release_notes.py+ - `.github/workflows/` — `ci.yml`, `release.yml` (on a `v*` tag: build with full history, check the wheel version matches the tag, build the release notes, publish to PyPI, create the GitHub Release from `scripts/release_notes.py`, stamp the tag version into `server.json` and publish to the MCP Registry, then push the Docker image only after that job succeeds), `rm-drift-monitor.yml`, `dependabot-automerge.yml` (squash auto-merge only for Dependabot PRs whose highest update is minor or patch; ma+ - `server.json` (MCP Registry metadata), `Dockerfile`, `fastmcp.json`, `pyproject.toml`.+ - Call `await self._request("METHOD", path, params=..., json_data=...)`.+ - Destructive delete and bulk-delete tools **must** accept `confirm: bool = False` and call `_destructive_gate`. Creates, updates, and other non-destructive mutations do not take `confirm`.+   - `destructiveHint`: `True` for delete and bulk-delete tools; `False` otherwise (archive-field updates stay non-destructive).+   - `idempotentHint`: `True` for GET/list and for explicitly idempotent writes; `False` for creations, other updates, and deletes.+   - Profiles (`--profile` / `SMARTSHEET_RM_PROFILE`, defined in `profiles.py`): domain-mount profiles `full`, `time`, `projects`, `admin`, `readonly` select mounts; job profiles `timesheets`, `staffing`, `org_setup`, `portfolio` mount every domain and expose an explicit tool-name allowlist via the public `root.disable(components={"tool"})` then `root.enable(names=..., components={"tool"})`. Never `enable(only=True)` (it hides prompts and resources) and never private FastMCP attributes. Unknown p+   - A new tool must go in at least one job profile or in `FULL_ONLY_TOOLS`; `tests/test_profiles.py` fails otherwise. Update the counts in `scripts/check_tool_contract.py` and the README profile table.+   - Read-only: `readOnlyHint=True` is the only signal. `--profile readonly` or `SMARTSHEET_RM_READONLY=1` adds `ReadOnlyToolFilter`; `ReadOnlyGateMiddleware` refuses any real non-read-only tool (`SafetyViolationError`, a FastMCP `ToolError`, so `isError: true`). Unknown names pass through to FastMCP's `Unknown tool`. `call_tool` is unwrapped only when it is a real tool (Tool Search on); otherwise it is `Unknown tool: 'call_tool'`. No server context means refuse.+   - Bulk protection: `time_bulk_delete_time_entries` and `projects_bulk_delete_assignments` are listed in `full`; the time/projects domain guards refuse them at call time unless `SMARTSHEET_RM_ALLOW_BULK_DESTRUCTIVE=1`.+   - Tool Search (`regex` or `bm25`) and experimental Code Mode attach only on `full`, never both; on other profiles they log a warning and keep the flat list. `search_tools`, `search` and `get_schema` are annotated `readOnlyHint=True`; Code Mode `execute` is refused under read-only.+ - **Zero live network calls in the default suite.** Tests must run 100% offline in CI. The opt-in live module is `tests/test_e2e_live.py` (`-m e2e`, skips unless `SMARTSHEET_RM_API_TOKEN` is set).+ - Update expected tool and profile counts in `scripts/check_tool_contract.py` and the `README.md` profile table, and place the tool in a job profile or `FULL_ONLY_TOOLS`.+ - Ensure test statement coverage remains at **100.0%** (`--cov-fail-under=100`). Branch coverage is not enabled.+ 5. **Git Safety & Releases**:+    - **The git tag is the version.** `uv-dynamic-versioning` reads the `vX.Y.Z` tag at build time; `pyproject.toml` declares `dynamic = ["version"]`, `__version__` comes from `importlib.metadata`, and `server.json` commits `0.0.0` (the release workflow stamps the tag version into it). PRs never edit a version: no bump in `pyproject.toml`, `src/smartsheet_rm_mcp/__init__.py`, `server.json`, `uv.lock`, or `CHANGELOG.md`. Untagged builds report `X.Y.(Z+1).devN+<sha>`; a build with no git metadata r+    - **Breaking changes:** every `feat!` / `fix!` PR (any `type!:` title) carries a `BREAKING CHANGE:` footer as the final paragraph of the PR body, and the footer text must include the migration steps. `BREAKING CHANGE:` (or its synonym `BREAKING-CHANGE:`) is the only footer token; do not add a separate migration token. `scripts/release_notes.py` stops at the CodeRabbit marker line (outside a code fence) `<!-- This is an auto-generated comment: release notes by coderabbit.ai -->` and ignores ev+    - **Squash merges use the PR body as the commit message** (repo settings: PR title as squash title, PR body as squash message). Keep the PR body accurate up to the merge, because `scripts/release_notes.py` reads it from the squash commit.+    - **`CHANGELOG.md` is frozen** as of 1.2.2. GitHub Releases are the changelog: `scripts/release_notes.py` builds each release body from the squash commits since the previous tag (every `BREAKING CHANGE:` footer verbatim, then the commit subjects). Do not add CHANGELOG entries.+    - `skills/smartsheet-rm/SKILL.md` carries no version: the [Agent Skills specification](https://agentskills.io/specification) has no top-level `version` field. Do not add one. Update the skill only when its operator guidance changes.+    - **README is outside the release version ceremony.** Do not add or chase `README.md` `==X.Y.Z` install pins. Update `README.md` only when project behavior, install method, config, or commands actually change. Prefer unpinned install examples (`uvx --from mcp-server-smartsheet-rm smartsheet-rm-mcp`) or point readers to GitHub Releases.+ uv sync --locked --extra dev   # or uv pip install -e ".[dev]"+ # Build version guard (reads the single wheel in dist/; rejects 0.0.0 and the untagged 0.0.1.devN)+ rm -rf dist && uv build && uv run python scripts/check_version.py+ ## 🔄 CI & Releases+ CI is defined in `.github/workflows/ci.yml` (jobs: lint and types, tests on Python 3.10–3.13 at 100% coverage, tool contract, OpenAPI drift, build + `scripts/check_version.py` + `twine check`, protocol and conformance, CodeQL). Jobs that install or build the package check out with `fetch-depth: 0`, because a shallow checkout has no reachable tag and reports `0.0.1.devN`. The Docker image is built only in `release.yml`, with `UV_DYNAMIC_VERSIONING_BYPASS` set to the tag version (the build context+ Do not create tags or releases unless the maintainer asks. There is no release PR: merged commits accumulate on `main`, and releases go out on any weekday on the maintainer's go; no fixed release day. Before the tag:+ - The release owner previews the release body on an up-to-date `main` with full history and tags (`git fetch --tags && python3 scripts/release_notes.py`) and posts it with the release Ask.+ - The reviewer checks the proposed version against the commit types since the last tag (`!` / `BREAKING CHANGE:` → major, `feat` → minor, otherwise patch), that every breaking commit carries its footer with migration steps, and that the version is unused in all three places it could already exist:

    Showing the first 40 of each.

  • server.json 1 field changed
    version
    1.2.2 2.0.0

  • server.json 1 field changed
    version
    1.2.1 1.2.2