CHANGELOG¶
v0.13.0 (2026-07-25)¶
Bug Fixes¶
Three concrete tool failures observed in a Claude Desktop audit log on 2026-05-26 (session local_08682e5e-9a5c-4233-a3d4-cbec7cbabe9d):
-
list_purchase_orders-> 404 "No parsed response data for status 404" StockTrim returns 404 (not 200-with-empty-array) when there are zero purchase orders. Mirror the existing products.get_all pattern and translate 404 -> [] in helpers/purchase_orders.py. -
list_suppliers-> Pydantic validation error on SupplierInfo.code The API returns suppliers with supplierCode=null; the generated SupplierResponseDto typed it as requiredstr. Add supplierCode to NULLABLE_FIELDS, regenerate the model (Optional now), and relax the MCP wrapper SupplierInfo.code tostr | None. -
search_products-> 500 surfaced as "No parsed response data..." The Order Plan endpoint occasionally returns 500 with an HTML body the OpenAPI client cannot decode. utils.unwrap() checkedparsed is Nonebefore inspecting the status code, so any 4xx/5xx without a parseable body raised a generic APIError instead of the appropriate ServerError/NotFoundError/etc. Reorder the checks so status-specific exceptions fire first; the "no parsed body" APIError now only fires on 2xx responses.
Adds regression tests for each path: - tests/test_helpers_purchase_orders.py (new) - tests/test_utils.py (3 new cases covering parsed=None + error status) - stocktrim_mcp_server/tests/.../test_suppliers.py (null supplierCode)
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
Two fixes driven by the 2026-05-26 MCP bug report.
-
utils.unwrap() now appends a truncated body excerpt to the error message when the response has no parsed body and a non-2xx status. StockTrim's Order Plan endpoint sometimes returns 500 with an HTML stack trace or 415 with a plaintext reason; the previous "API error with status N" message was opaque without those bodies. Excerpts are bounded at 500 chars with a "…[+N chars]" suffix so log lines stay readable, and UTF-8 decode errors fall back to a byte-count placeholder.
-
review_urgent_order_requirements built an OrderPlanFilterCriteriaDto (list-shaped, used by the V2 PO generation endpoint) and passed it to client.order_plan.query() which targets /api/OrderPlan and expects OrderPlanFilterCriteria (singular
location/supplier/category). StockTrim rejected the wrong-shaped body with 415. Switch to the singular schema; for list-shaped MCP inputs, send the first element to the API and logorder_plan_multifilter_narrowedso operators see that further filtering was dropped (multi-filter is an API limitation).
Tests: - test_utils.py: 4 new cases for body-excerpt formatting (basic, long-body truncation, empty body, undecodable bytes). - test_urgent_orders.py: 2 new cases pinning the OrderPlanFilterCriteria type (vs Dto) and the multi-element warning.
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
- client: Preserve positional arg order for total_retry_timeout
(
130f4ee)
Addresses Copilot's review on #170: `total_retry_timeout` was inserted between `max_retries` and `logger` in two public signatures (`create_resilient_transport` and `StockTrimClient.init`), which silently shifts the positional meaning of `logger` for any caller using positional args:
create_resilient_transport(api_auth_signature, max_retries, logger) # before #170: logger -> logger param ✓ # after #170: logger -> total_retry_timeout param ✗ (TypeError)
Moves `total_retry_timeout` after `logger` (just before `**kwargs`/ `**httpx_kwargs`) so the original positional layout is preserved. `total_retry_timeout` is still keyword-accessible with the same 60s default as #170 introduced.
Docstring arg order updated to match.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Apply @unpack_pydantic_params + Annotated[..., Unpack()] to the 9 workflow tools so
their MCP inputSchema emits flat top-level parameters instead of the nested
{"request": {...}} shape. Foundation tools were already flat — this brings the
workflow tools in line.
Strategy C (per the issue plan): the runtime wrapper accepts either a positional
BaseModel instance (the in-process test path) or flat kwargs (the FastMCP path).
Existing tests that call await tool(MyRequest(...), ctx) keep working without churn.
Side fix: @unpack_pydantic_params now preserves Field(description=...), ge/le,
and other FieldInfo metadata via Annotated[T, FieldInfo(...)] on the synthesized
parameters. Foundation-tool schemas previously dropped all field descriptions; they now
match workflow-tool schemas for documentation parity.
Transitional 0.16 fallback: the wrapper still accepts the legacy
{"request": {<flat_fields>}} shape when request is a dict and the sole kwarg.
Non-dict values (the #116 symptom) now raise a TypeError that names the model's flat
fields. Fallback will be removed in 0.17.
ADR 002 updated to document the canonical pattern. Bumped stocktrim-mcp-server to 0.16.0
and added a CHANGELOG entry that points at #206 (upstream contribution to fastmcp#1784)
— once that lands we can delete unpack.py.
Closes #116. Tracking #206.
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcpb: Redirect mcpb subprocess stdout to stderr
(
07f7f7a)
Addresses Copilot review on this PR (threads on `scripts/build_mcpb.py` and `.github/workflows/release.yml`): both `run_mcpb_validate` and `run_mcpb_pack` called `subprocess.run` with inherited stdout. If the `mcpb` CLI ever emits progress text on stdout, it would contaminate the workflow's `artifact=$(python scripts/build_mcpb.py)` capture and break the artifact-upload step downstream.
Fix: pass `stdout=sys.stderr` to both `subprocess.run` calls. The CLI's
output remains visible to humans/CI logs (via stderr) but stays out of this script's stdout, which now exclusively carries the artifact path on the final line of `main()`.
Verified by substituting a stub `mcpb` on PATH that writes `[mcpb] processing...` to stdout, then running `artifact=$(python scripts/build_mcpb.py)`: the noise appears in the terminal (stderr) but the captured variable contains only the artifact path, matching what the release workflow expects.
This closes the deferred concern tracked in #178 — the issue was filed during the initial review when this was a SUGGESTION; Copilot raised it again with concrete file:line refs in the latest review, so addressing it inline rather than deferring further.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcpb: Substitute stocktrim-openapi-client requirement at build time
(
014bbe0)
Addresses Copilot review on this PR (two threads on `scripts/build_mcpb.py` and
`stocktrim_mcp_server/mcpb/pyproject.template.toml`): the bundle template hardcoded an
unpinned `stocktrim-openapi-client` dependency, but the MCP release workflow
(`.github/workflows/release.yml:162-204`, `Update MCP client dependency`) rewrites
the package's own pyproject to `stocktrim-openapi-client==\
Fix: carry a `STOCKTRIM_CLIENT_DEP` placeholder in
`pyproject.template.toml`, substituted at build time with the exact requirement string from `stocktrim_mcp_server/pyproject.toml`. Whatever the package pins (or doesn't), the bundle pins the same.
Implementation:
-
`scripts/build_mcpb.py`: - New `get_client_dep(pyproject)` helper extracts the requirement string by matching the bare package name (handles `name`, `name==X`, `name>=X`, `name[extra]`, etc.). - `substitute()` generalized to take a `dict[str, str]` of placeholders; callers pass `{VERSION_PLACEHOLDER: version}` for the manifest and `{VERSION_PLACEHOLDER: version, CLIENT_DEP_PLACEHOLDER: client_dep}` for the bundle pyproject. - `verify_dep_mirror` reads the template, substitutes the placeholder, then parses + compares — keeping the drift check honest in both unpinned (dev) and pinned (release) scenarios. - `stage_bundle` takes `client_dep` and threads it through.
-
`stocktrim_mcp_server/mcpb/pyproject.template.toml`: replace `"stocktrim-openapi-client"` with `"STOCKTRIM_CLIENT_DEP"`. Header comment expanded to document both placeholders.
Verified by smoke-testing both branches:
- Unpinned (current main): bundle pyproject gets `"stocktrim-openapi-client"` — matches package, mirror check passes. - Pinned (simulated release with `==9.9.9`): bundle pyproject gets `"stocktrim-openapi-client==9.9.9"` — matches package, mirror check passes.
Couldn't autosquash into the original `feat(mcp): add MCP Bundle...` commit because the prior `refactor(mcpb): simplify` commit (which added `_write_substituted`) conflicts with this fixup's same-region changes. Standalone commit it is.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- types: Drop mypy-style ignores in favor of real type narrowing
(
724f251)
Replaces seven # type: ignore shims with proper narrowing so the helpers and tests
type-check under the stricter ty 0.0.35 we're about to land.
- helpers/{bill_of_materials,customers,products,sales_orders,suppliers,purchase_orders_v2}:
cast the post-
unwrap/isinstance(result, list)return value to the concretelist[Dto]instead of relying on a mypy-only ignore that ty doesn't honor. - stocktrim_client.py: drop the import-error fallback forProblemDetails. The generated module always ships with this package, so theProblemDetails = Nonerebind (and its companion null-check) were dead defensive code that confused ty's type narrowing. - tests/conftest.py: assertclient._client is not Nonebefore reaching into_transport, replacing aunion-attrignore. - tests/test_purchase_order_upsert.py: narroworder_datewithisinstance(_, datetime)instead of the runtime-onlynot isinstance(_, type(UNSET))check ty couldn't follow.
No behavior change for any of the runtime paths exercised by the suite; all 73 client tests + 309 MCP tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Build System¶
- Install stocktrim-mcp-server workspace member by default (closes #172)
(
a6b69a4)
Adds `stocktrim-mcp-server` to the root project's `[dependency-groups].dev` group with a `[tool.uv.sources]` entry resolving it to the workspace member. With this, the canonical `uv sync --all-extras` (and even bare `uv sync`) install both workspace members into the project venv — fixing the `ModuleNotFoundError: No module named 'stocktrim_mcp_server.services'` that `uv run poe test-mcp` hit for fresh contributors following the documented setup commands.
Verified by removing `.venv` and re-syncing: - `uv sync` -> MCP module importable, dev group tools (poethepoet, pytest stack, ty) present. - `uv sync --all-extras` -> all the above plus the project extras (ruff, mdformat, openapi-spec-validator, ...).
No documentation updates needed; the existing 8 docs locations recommending `uv sync --all-extras` are now correct.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- deps: Bump 5 packages + transitives across uv group
(
c45b870)
Consolidates the following dependabot bumps (closes #162, #163, #164, #165, #167) into a single uv.lock update:
- urllib3 2.6.3 -> 2.7.0 - mkdocs-material 9.7.1 -> 9.7.6 - mkdocs-swagger-ui-tag 0.7.2 -> 0.8.0 - openapi-spec-validator 0.7.2 -> 0.8.5 - types-python-dateutil 2.9.0.20260124 -> 2.9.0.20260508
Pulled-along transitives (constraints from openapi-spec-validator 0.8.5): jsonschema-path 0.3.4 -> 0.4.6, openapi-schema-validator 0.6.3 -> 0.8.1, pathable 0.4.4 -> 0.5.0, referencing 0.36.2 -> 0.37.0.
All pyproject pins already covered the new versions; no manifest changes were needed. Quality gate (uv run poe check) passes, docs build, and the OpenAPI spec validates under spec-validator 0.8.5.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Bumps attrs from 25.4.0 to 26.1.0. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: attrs dependency-version: 26.1.0
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
- deps: Bump authlib in the uv group across 1 directory
(
bbb1915)
Bumps the uv group with 1 update in the / directory: authlib.
Updates authlib from 1.7.0 to 1.7.1 -
Release notes
--- updated-dependencies: - dependency-name: authlib dependency-version: 1.7.1
dependency-type: indirect
dependency-group: uv
...
Signed-off-by: dependabot[bot] support@github.com
Bumps lupa from 2.6 to 2.7. - Changelog - Commits
--- updated-dependencies: - dependency-name: lupa dependency-version: '2.7'
dependency-type: indirect
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Bumps mkdocs-gen-files from 0.6.0 to 0.6.1. - Release notes - Commits
--- updated-dependencies: - dependency-name: mkdocs-gen-files dependency-version: 0.6.1
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Bumps mkdocs-literate-nav from 0.6.2 to 0.6.3. - Release notes - Commits
--- updated-dependencies: - dependency-name: mkdocs-literate-nav dependency-version: 0.6.3
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Bumps openapi-python-client from 0.28.1 to 0.28.4. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: openapi-python-client dependency-version: 0.28.4
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Bumps poethepoet from 0.37.0 to 0.38.0. - Release notes - Commits
--- updated-dependencies: - dependency-name: poethepoet dependency-version: 0.38.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Bumps pydantic from 2.12.5 to 2.13.4. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: pydantic dependency-version: 2.13.4
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Bumps the uv group with 1 update in the / directory: python-multipart.
Updates python-multipart from 0.0.21 to 0.0.22 -
Release notes -
Changelog -
Commits
--- updated-dependencies: - dependency-name: python-multipart dependency-version: 0.0.22
dependency-type: indirect
dependency-group: uv
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Bumps ruff from 0.15.0 to 0.15.15. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: ruff dependency-version: 0.15.15
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
-
deps: Bump ruff to 0.15.0 + format + modernize enums (#141,
60403a9) -
build(deps): bump ruff from 0.14.13 to 0.15.0
Bumps ruff from 0.14.13 to 0.15.0. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: ruff dependency-version: 0.15.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] support@github.com
- chore(deps): apply ruff 0.15.0 format and modernize enums
ruff 0.15.0 brings new formatting and a new lint rule:
- Format: 2 files (forecast_management.py, order_plan.py) reformatted with the new ruff
style. README.md re-flowed by mdformat. - UP042:
class X(str, Enum)→class X(StrEnum)(Python 3.11+). Added _modernize_str_enum_classes post-processing to scripts/regenerate_client.py so future regenerations preserve the modern form, and applied it to the 3 currently-generated enum models (api_enum, current_status_enum, purchase_order_status_dto).
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
Bumps tenacity from 9.1.2 to 9.1.4. - Release notes - Commits
--- updated-dependencies: - dependency-name: tenacity dependency-version: 9.1.4
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Bumps the uv group with 2 updates in the / directory: idna and pymdown-extensions.
Updates idna from 3.11 to 3.15 - Release notes
- Changelog -
Commits
Updates pymdown-extensions from 10.20 to 10.21.3 -
Release notes -
Commits
--- updated-dependencies: - dependency-name: idna dependency-version: '3.15'
dependency-type: indirect
dependency-group: uv
- dependency-name: pymdown-extensions dependency-version: 10.21.3
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Bumps tox from 4.34.1 to 4.54.0. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: tox dependency-version: 4.54.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
-
deps: Bump ty to 0.0.15 + cast list narrowing (#142,
e872c63) -
build(deps-dev): bump ty from 0.0.12 to 0.0.15
Bumps ty from 0.0.12 to 0.0.15. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: ty dependency-version: 0.0.15
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
- fix(types): cast list-element narrowing for ty 0.0.15
ty 0.0.15 no longer narrows generic parameters through isinstance(x, list) — when
x: T | list[T] and we enter the list branch, x[0] is reported as object instead of
T.
Three call sites use the polymorphic-return pattern (API returns either a single object or a list depending on filter args):
- helpers/purchase_orders.find_by_reference - helpers/suppliers.find_by_code - tests/test_purchase_order_upsert.py — status assertion
Each was relying on ty 0.0.12's narrowing; now made explicit with cast(T, result[0]).
The existing get_all callers already use cast for the same union, so this matches the
surrounding style.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
Bumps types-python-dateutil from 2.9.0.20251115 to 2.9.0.20260124. - Commits
--- updated-dependencies: - dependency-name: types-python-dateutil dependency-version: 2.9.0.20260124
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Major updates: - ruff: 0.12.12 → 0.14.13 (removed \<0.13 constraint) - ty: 0.0.1a26 → 0.0.12 - mkdocstrings: 0.30.1 → 1.0.0 - mkdocstrings-python: 1.19.0 → 2.0.1 - pathspec: 0.12.1 → 1.0.3 - mcp: 1.21.0 → 1.25.0 - fastmcp: 2.13.0.2 → 2.14.3 - poethepoet: 0.38.0 → 0.40.0 - uvicorn: 0.38.0 → 0.40.0 - websockets: 15.0.1 → 16.0
Also includes ~40 minor/patch updates.
Test fixes: - Remove redundant type casts in test_utils.py (ty warnings) - Fix product_lifecycle_review test imports and async decorators
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude Opus 4.5 noreply@anthropic.com
Lands the fastmcp 3.x upgrade as a coordinated bundle. Each line below maps to its own tracking issue under the v3 modernization plan (#154).
Dependency bumps (was dependabot PR #139, opened as a maintainer branch since automation can't force-push to dependabot branches):
- fastmcp 2.14.3 → 3.2.4 (major: see breaking changes below) - python-dotenv 1.2.1 → 1.2.2 (patch) - pytest 9.0.2 → 9.0.3 (patch) - authlib 1.6.6 → 1.7.0 (minor, transitive) - cryptography 46.0.3 → 47.0.0 (major, transitive) - python-multipart 0.0.22 → 0.0.27 (patch, transitive — security) - requests 2.32.5 → 2.33.1 (minor)
v3 breaking changes addressed in this PR:
-
143 fix(mcp): tests/test_prompts/test_workflows.py reaches into fastmcp's private¶
_prompt_manager._prompts attribute, which was removed in v3. Migrated to the public list_prompts() API; the test is now async to match. - #144 refactor(mcp): prompts/workflows.py product_lifecycle_review returned legacy mcp.types.PromptMessage objects. v3 wants fastmcp.prompts.Message. Other prompts in the same file already used Message; this fixes the inconsistency. - #146 build(mcp): fastmcp pin in stocktrim_mcp_server/pyproject.toml bumped from=0.3.0 (basically unbounded) to >=3.0.0,\<4.0.0 so a fresh install can't accidentally land v2 behavior.
Verification:
- uv run poe check: 73/73 tests pass (root client suite) - pytest stocktrim_mcp_server/tests/: 318/318 tests pass (was 1 failing under fastmcp 3.x before #143; that's exactly what this PR fixes) - ruff/ty/yamllint clean
The MCP server test suite is still NOT covered by uv run poe check — that's tracked
separately as #145 and lands as the next PR so future framework bumps can't slip past CI
the way fastmcp 3.x almost did.
Closes #143, closes #144, closes #146. Refs #154.
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
Bumps poethepoet from 0.40.0 to 0.41.0. - Release notes - Commits
--- updated-dependencies: - dependency-name: poethepoet dependency-version: 0.41.0
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Bumps poethepoet from 0.41.0 to 0.46.0. - Release notes - Commits
--- updated-dependencies: - dependency-name: poethepoet dependency-version: 0.46.0
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
- deps-dev: Bump ty from 0.0.15 to 0.0.35
(
19a6a0e)
Consolidates dependabot #161 (which targeted 0.0.34). 0.0.35 picks up one further patch
and matches what uv lock --upgrade-package ty resolves today. The previous commit
landed the type-narrowing fixes this version's stricter invalid-return-type and
union-attr diagnostics surfaced.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Bumps ty from 0.0.35 to 0.0.40. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: ty dependency-version: 0.0.40
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Chores¶
Install the harness-kit plugin's skills/agents into .claude/ plus project-specific extensions for the StockTrim OpenAPI client + MCP server.
Includes: - Upstream harness-kit 0.4.0 skills: commit, feature-spec, open-pr,
code-reviewer, skill-writer, standup, plus shared/ scripts - Project-specific skills:
regenerate-client, add-nullable-field, add-helper-method, add-mcp-tool, quality-gate -
Agents: code-reviewer + verifier (upstream, tailored), test-writer, domain-advisor,
project-manager (project-specific) - PostToolUse + Stop hooks in .claude/settings.json -
.harness-lock.json for upstream/local provenance tracking - fix(ci): bump
aquasecurity/trivy-action to v0.36.0 (0.29.0 tag never existed) - fix(harness): 13
Copilot review comments addressed (jq fallback, upstream shared script logic bugs, GFM
rendering text →)
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Pulls 6+ months of upstream spec drift. Cached spec was from 2025-11-08. 65 files changed, +614 / -2059 (net -1445 lines, mostly model deletions).
Endpoint changes¶
Removed: - POST /api/InFlow/{tenantId} (third-party integration webhook) -
POST /api/Square (Square webhook receiver)
Added: - POST /api/ProcessingStatus (was previously GET-only)
Model changes¶
Removed (8 models): Address, Customer, InventoryCountWebHook, SquareWebHook,
SquareWebHookCatalog, SquareWebHookData, SquareWebHookObject,
SquareWebHookOrderUpdatedData. All were exclusive to the dropped Square/InFlow
endpoints; no helpers, services, MCP tools, or tests referenced them.
Added (1 model): ProcessingStatusRequestDto for the new POST.
Field-level changes: - ApiEnum: +3 new values (AcumaticaErp, All, Master). -
SkuOptimizedResultsDto: +new field finishedGoodStockOnOrder (double, nullable). -
SupplierResponseDto.supplierCode: upstream dropped minLength: 1 (the field was
already nullable in our patched copy).
NULLABLE_FIELDS prune¶
Upstream now natively marks the following nullable, so they were removed from
scripts/regenerate_client.py (no behavior change — stale patches were silently
no-ops): -
PurchaseOrderResponseDto.{message, orderDate, fullyReceivedDate, externalId, referenceNumber}
- PurchaseOrderRequestDto.{orderDate, externalId, referenceNumber} -
PurchaseOrderSupplier.supplierCode - PurchaseOrderLineItem.receivedDate
Still required: - PurchaseOrderResponseDto.location /
PurchaseOrderRequestDto.location (still bare $ref, needs allOf-wrap to be nullable)
- SupplierResponseDto.supplierCode (upstream still marks required; API returns null
for orphaned suppliers) - OrderPlanFilterCriteria.currentStatus (enum ref)
Script fixes (scripts/regenerate_client.py)¶
While regenerating, two latent bugs in the patch script were surfaced by the new spec
and fixed: 1. When making a string field nullable, strip incompatible value constraints
(minLength, maxLength, pattern, minimum, maximum, exclusive*,
min/maxItems, uniqueItems). OpenAPI 3.0 rejects e.g. nullable: true +
minLength: 1 because null cannot satisfy the length bound. Triggered by upstream
adding minLength: 1 to SupplierResponseDto.supplierCode. 2. When the patch removes
the only entry from a required array, delete the now-empty array. OpenAPI 3.0 rejects
required: []. Triggered by the same SupplierResponseDto patch.
Generator-level improvement¶
The new openapi-python-client (>=0.25.2 with current OpenAPI) emits narrow
except (TypeError, ValueError, AttributeError, KeyError): in from_dict
discriminator helpers instead of bare except: # noqa: E722.
grep -r 'noqa: E722' stocktrim_public_api_client/generated/ returns 0 matches. This
obsoletes the manual sweep in PR #218.
Adaptation work¶
None required. No helper, service, MCP tool, test fixture, or __init__.py export
referenced any of the removed models or endpoints.
Quality gate¶
- 96 client tests pass (baseline preserved) - 352 MCP tests pass (baseline preserved) -
ruff check / ruff format-check / ty check / yamllint all clean - 0
# noqa/# type: ignoreintroduced
Conflicts with open PRs¶
This regen will produce massive conflicts with all currently-open PRs; they should
rebase after this lands: - #211 (Unpack flattening) - #213 (rebase CRITICAL docs) - #216
(PO nullable supplier/line_items) — may now be a no-op given the natively-nullable
upstream changes; worth re-evaluating - #218 (narrow generated except) — fully
obsoleted by this regen; can be closed without merging
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
The 2026-05-26 audit surfaced multiple bugs (null supplierCode, null purchaseOrderLineItems/supplier, bare-except parser patterns) that routine full client regenerations would have caught. Root cause: agent briefs in PRs #202 and #216 instructed surgical regens (manually running openapi-python-client on a patched cached spec and copying 1-2 files) to keep diffs small — a pattern that bypasses spec download and lets generated/ drift against the modern generator.
Three harness surfaces now codify the policy:
- CLAUDE.md "Client regeneration policy" section: default is full pipeline; surgical regens listed under Common Pitfalls. - .claude/skills/regenerate-client/SKILL.md CRITICAL section: always full pipeline; deferral protocol mandates tech-debt issue + regen PR as next work + cross-reference from deferring PR. - .claude/skills/add-nullable-field/SKILL.md step 5: removed the "stash + re-run on clean baseline + reapply" guidance that was encoding the suppress-drift anti-pattern; replaced with absorb-drift default + pointer to the deferral protocol.
Deferral is OK when truly necessary but never silent — tracking issue + prioritized follow-up + PR cross-reference are mandatory.
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
Mirrors katana commit c5cf718. Adds a CRITICAL bullet at the top of both skill files requiring the feature branch be rebased onto the target branch before opening the PR and before every --force-with-lease push during review cycles.
Why now: PR #203 hit this exact failure mode this session — a fixup-and-push autosquash collided with intervening main commits because the branch was stale at the force-push step. The rule existed in STANDARD PATH but agents (this one included) skim past STANDARD PATH and act on CRITICAL first, so promoting it is the no-code fix.
Changes: - .claude/skills/open-pr/SKILL.md: new CRITICAL bullet + Phase 1 rebase step (mandatory, not optional); cross-reference /rebase in Related Skills. - .claude/skills/review-pr/SKILL.md: new project-local overlay copying the upstream harness-kit skill and adding the analogous CRITICAL bullet plus mandatory STANDARD PATH sync step and re-rebase guidance before every force-push in Mode B. - .harness-lock.json: record the new overlay and updated modification note.
Closes #180
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
Continuous Integration¶
- mcp: Build .mcpb on MCP releases and attach to GitHub release
(
da83903)
Adds a build-mcpb job to the unified release workflow that runs after release-mcp
and is gated on release-mcp.outputs.released == 'true' — so the bundle is only built
when python-semantic-release actually cuts a new mcp-v* tag.
The job uses actions/setup-python (not setup-uv) since scripts/build_mcpb.py is
stdlib-only and only shells out to the mcpb CLI; actions/setup-node +
npm install -g brings the CLI in. The built .mcpb is uploaded as a workflow artifact
and attached to the GitHub release via gh release upload --clobber against the tag
that release-mcp produced (needs.release-mcp.outputs.tag).
Refs #160. Pattern ported from statuspro-openapi-client#62.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
-
mcp: Include MCP server tests in root quality gate (#156,
dcc0c06) -
ci(mcp): include MCP server tests in root quality gate
The MCP server's own test suite (stocktrim_mcp_server/tests/) was structurally isolated
from uv run poe check and the GitHub CI matrix: both ran only the root client suite.
That's how the dependabot uv group bump in #139 showed all 8 CI checks green even though
one MCP test failed under fastmcp 3.x — the MCP suite was simply never run.
Changes:
- pyproject.toml: new
test-mcppoe task that runs the MCP suite from its own workspace; added tocheckandciso a single command covers both suites. - .github/workflows/ci.yml: addedRun MCP server testsstep right after coverage so CI fails fast if MCP regresses. - CLAUDE.md, .claude/skills/quality-gate/SKILL.md, .claude/skills/add-mcp-tool/SKILL.md, .claude/agents/verifier.md: updated to point at the new unified flow; removed stale "MCP tests are not in CI" warnings.
Verification:
uv run poe checkruns 73 client + 318 MCP tests (= 391) and stays green. - The MCP test that motivated this gap (the fastmcp 3.x_prompt_managerfailure) was fixed in #155, so this lands clean on the now-green MCP suite.
Closes #145. Refs #154.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- ci: install workspace packages so test-mcp can find stocktrim_mcp_server
CI was failing on the new test-mcp step with
ModuleNotFoundError: No module named 'stocktrim_mcp_server.services' because
uv sync --all-extras only installs the root package's deps; workspace members like
stocktrim-mcp-server aren't auto-installed.
Add --all-packages to the dependency-install step in:
- ci.yml
testjob (sopoe test-mcpworks in the matrix) - ci.ymlqualityjob (consistency; harmless if not needed) - release.ymltestjob (runspoe ciwhich includes test-mcp)
Other workflows (security.yml, docs.yml) don't import the MCP server, so they keep the
lighter --all-extras-only install.
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcpb: Pin @anthropic-ai/mcpb to ^2.1.2 in build-mcpb job
(
d301f93)
Addresses a BLOCKING finding from a 6-dimensional review pass on this PR: the previous `npm install -g @anthropic-ai/mcpb` resolved to npm
`latest`, which is a supply-chain risk because the `.mcpb` artifact this CLI produces is attached to a public GitHub release and shipped to end users. A compromised release of the CLI (or an upstream dep, given `--ignore-scripts` is not set) could silently tamper with what users install.
`^2.1.2` pins major version (current latest is 2.1.2; the manifest schema version `0.4` we declare is independent of the CLI version), allowing patches and minor bumps but blocking a surprise 3.x release from breaking the build path. Anthropic-namespaced packages are also manually trusted here, so the trust delta vs an unpinned install is narrow but real.
Two related follow-up issues filed during the review:
-
177 — normalize PEP 508 specifiers in verify_dep_mirror so the dep-mirror check¶
survives any future whitespace/case reformatting. - #178 — make the artifact-path output robust to future mcpb CLI stdout pollution.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Replace the long-lived SEMANTIC_RELEASE_TOKEN PAT with short-lived installation tokens minted from the dougborg-release-please GitHub App in the release-client and release-mcp jobs (checkout token, python-semantic-release github_token, and the MCP dependency-bump push). Each job mints its own token scoped to contents: write only, the minimum python-semantic-release needs to push commits/tags and create GitHub releases.
Also switches the MCP dependency-bump commit's git identity from the generic github-actions[bot] to the App's own bot identity (dougborg-release-please[bot]) so commits are attributed to the actor actually performing them.
SEMANTIC_RELEASE_TOKEN is left in place as a rollback path until a release has run successfully on the new token; it can then be deleted.
Refs #176 (not addressed here — out of scope).
Documentation¶
-
Add strict quality standards - no ignoring pre-existing issues (#122,
c4e6a3a) -
docs: add strict quality standards - no ignoring pre-existing issues
-
Apply suggestions from code review
Co-authored-by: Copilot 175728472+Copilot@users.noreply.github.com
Co-authored-by: Doug Borg dougborg@apple.com
- Clean up pre-existing mkdocs build warnings (closes #173)
(
a53183f)
`uv run poe docs-build` had been emitting 7 warnings and 2 info-level diagnostics for some time. None failed the build but they masked any new real warnings. Sweeps them in one pass.
- `helpers/customers.py`: annotate `**defaults: Any` on `find_or_create` so griffe stops warning about the un-annotated variadic parameter. - `docs/mcp-server/overview.md`: `../README.md` repointed to the absolute GitHub URL — the README is not part of the published docs tree so the relative link could never resolve. - `docs/mcp-server/safety-patterns.md`: four `../stocktrim_mcp_server/src/...` source-file links repointed to absolute GitHub URLs for the same reason. The line ranges in the link text are preserved. - `docs/mcp-server/examples.md`: fix a typo in the TOC — `#workflow-5-custom-order-fulfillment` → `#workflow-5-customer-order-fulfillment` to match the actual heading "Customer Order Fulfillment". - `docs/mcp-server/prompts.md`: `./overview.md#resources` repointed to the MCP spec's resources page; `overview.md` has no `Resources` section, and we don't have a dedicated resources doc, so the upstream spec is the most useful target.
mdformat also wrapped a few long lines / tidied a table in the touched files at the configured 88-char limit; included here so the pre-commit gate stays green on the result.
After: `poe docs-build` finishes warning-free.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcp: Clarify wrapper response model docstrings
(
f93fad7)
The eight one-field wrapper models added in #188 had docstrings claiming "the None case still serializes through make_json_result" — imprecise. make_json_result handles None fine on its own; the actual reason for the wrapper is that callers need a concrete model class to validate against when round-tripping through unwrap_tool_result.
Closes #190
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcp: Document .mcpb install path as the recommended Claude Desktop flow
(
726629a)
Restructures the "Claude Desktop Integration" section to lead with the .mcpb one-click
install (download from the latest mcp-v release, drag into Claude Desktop, confirm in
the install dialog — Claude Desktop prompts for both the Auth ID and Auth Signature via
UI, no JSON editing). Keeps the manual uvx config-file path as a labeled fallback for
users who prefer it.
Refs #160.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcp: Fix python -m invocation in README — point at .server module
(
34a42cd)
Addresses Copilot's suppressed-confidence comment on PR #166 (which the reviewer surfaced manually): the "Using Python Environment" example told users to invoke `python -m stocktrim_mcp_server`, but the package ships no `main.py` so that command fails at runtime with:
/path/python: No module named stocktrim_mcp_server.main; 'stocktrim_mcp_server' is a package and cannot be directly executed
Users following the example would hit this immediately.
Fix: change the example's args to `["-m", "stocktrim_mcp_server.server"]`
— `server.py` already exposes `main()` and an `if name == "main"` guard, so it's directly runnable today. Smoke-tested: `uv run python -m stocktrim_mcp_server.server` launches the FastMCP server end-to-end (prompts registered, caching enabled, server starting).
Considered adding a `main.py` shim to make the original short form work — rejected because that would expand the public invocation surface beyond what the existing console script (`stocktrim-mcp-server`) and the `.mcpb` bundle already cover. Pointing at the module that already exists is the minimum correct change.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Features¶
- client: Allow httpx-retries 0.5.x and adopt total_timeout
(
6c743a3)
Bumps the httpx-retries upper bound from <0.5.0 to <0.6.0 and threads its new
total_timeout parameter through IdempotentOnlyRetry so cumulative retry sleep is
bounded even when the server keeps returning 5xx with long Retry-After headers.
pyproject.toml:httpx-retries>=0.5.0,<0.6.0. -stocktrim_client.py:create_resilient_transportandStockTrimClient.__init__gain atotal_retry_timeout: float | Nonekwarg (default 60s — comfortably covers full exponential backoff 1+2+4+8+16=31s plusRetry-Afterslack, while bounding pathological cases; passNoneto disable). The value is forwarded to theIdempotentOnlyRetryconstructor and surfaced on__repr__. - Tests cover the default value, an explicit override, the disable (None) case, and the__repr__change.
httpx-retries 0.5.0 also drops Python 3.9 support (we already require 3.11+) and
stores allowed_methods as uppercase strings instead of http.HTTPMethod enum members;
our IdempotentOnlyRetry.is_retryable_method already uppercases the comparand before
the membership check, so no behavioral change.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcp: Add MCP Bundle (.mcpb) build script + manifest template
(
94f42ec)
Adds the four-file scaffolding for packaging stocktrim-mcp-server as an .mcpb (MCP
Bundle, formerly DXT) — Claude Desktop's one-click install format for local MCP servers.
The build script stages a self-contained bundle directory under build/mcpb/ and runs
mcpb pack to produce a versioned artifact under dist/.
Bundle layout uses manifest_version: "0.4" with server.type: "uv" (the v0.4-only UV
runtime — no bundled interpreter; UV resolves deps from PyPI on first launch). Manifest
declares user_config for both required StockTrim credentials (auth_id +
auth_signature) so Claude Desktop prompts for both via UI; mcp_config.env injects
them as STOCKTRIM_API_AUTH_ID and STOCKTRIM_API_AUTH_SIGNATURE.
The bundle's pyproject.toml mirrors the package's production deps but omits
[tool.uv.sources] (workspace refs don't resolve outside the monorepo). The build
script verifies dep mirroring and fails loudly on drift — without this, new package deps
would silently be missing from the bundle at runtime.
Adds uv run poe build-mcpb for local builds. Requires the mcpb CLI on PATH
(npm install -g @anthropic-ai/mcpb); MCPB_SKIP_PACK=1 stages the bundle without
packing for cases where the CLI isn't installed.
Refs #160. Pattern ported from statuspro-openapi-client#62.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Wires fastmcp 3.x's built-in ResponseCachingMiddleware on the production server. The intent is the obvious operator win — most MCP sessions hit products/customers/suppliers/locations many times in quick succession, so caching catalog reads at the tool boundary cuts upstream API load without changing call sites.
Configuration:
- 5-minute TTL on call_tool. Bounds the staleness window for read tools. - 60-second TTL
on read_resource. Resources are discovery-oriented, so shorter TTL favors freshness
over hit rate. - Mutating tools are excluded by name (
create_*,delete_*,set_product_inventory,configure_*,manage_forecast_group,update_forecast_settings,forecasts_update_and_monitor,create_supplier_with_products,generate_purchase_orders_from_urgent_items). Caching mutations would be silent data loss; the exclusion list is the safety guard. - In-memory store via fastmcp's bundledMemoryStore(no new dependency —py-key-value-aiois already a transitive of fastmcp 3.x).
Documentation: docs/mcp-server/observability.md gains a "Caching"
section covering the staleness window, how to swap the in-memory backend for Redis/disk, and how to tighten freshness if needed.
Tests: new stocktrim_mcp_server/tests/test_caching.py verifies our
wiring choices — middleware is registered, every mutation surface is in the exclusion list, TTLs stay within bounded ranges. Behavior of the middleware itself is fastmcp's responsibility; we don't re-test it.
Verification: 73 client + 307 MCP = 380 tests pass (was 376; +4 caching
tests). uv run poe check green.
Closes #148. Refs #154.
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcp: Drop Jinja2 markdown templates — JSON-only ToolResult
(
31a6d5f)
Per MCP-Apps SEP-1865, content IS the LLM model context and structured_content is for UI binding. Hand-written markdown formatters in content drift silently when response models grow new fields (see katana #565). Replace make_tool_result + render_template with make_json_result, which puts indented JSON in content and the response dict in structured_content — no formatter to forget.
- Rewrite tools/tool_result_utils.py: add make_json_result; reserve make_tool_result(*, ui: PrefabApp) for future Prefab-UI tools. - Migrate review_urgent_order_requirements (the only migrated tool) to make_json_result; drop the workflows/urgent_orders/review.md.j2 template. - Strip Jinja2 from templates/init.py (keep legacy str.format load_template/format_template still used by forecast_management.py pending its own migration). - Drop jinja2 from stocktrim_mcp_server/pyproject.toml AND the mcpb/pyproject.template.toml mirror so verify_dep_mirror stays clean. - Rewrite .claude/skills/add-mcp-tool/SKILL.md to drop the template-authoring step; new flow is request + response + _impl + one-line make_json_result. - Update urgent_orders tests: round-trip + JSON-content equality assertions replace markdown-content assertions. - Add mdformat-frontmatter so SKILL.md YAML frontmatter survives mdformat pre-commit (it was being mangled into a horizontal rule + heading on any touched SKILL.md).
Refs #179. Subsequent PRs will migrate the remaining ~16 tools.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
-
mcp: Introduce ToolResult templating + migrate review_urgent_order_requirements (#159,
a4c2ef5) -
feat(mcp): introduce ToolResult templating; migrate review_urgent_order_requirements
First PR in the per-domain ToolResult migration tracked in #149. Establishes the templating + helper infrastructure and migrates one workflow tool end-to-end so we have a working reference before fanning out across the rest.
Infrastructure:
- Adds
jinja2>=3.1,<4as a direct dependency of stocktrim-mcp-server (already present transitively; pinning to make intent explicit). - Extendsstocktrim_mcp_server.templateswith a Jinja2-backedrender_template(name, **ctx)that loads.md.j2files. Existingformat_template/load_template(str.format-based) are preserved so the older forecast templates keep working unchanged. - Addsstocktrim_mcp_server.tools.tool_result_utils.make_tool_result( response, template_name, **vars)— single helper that builds afastmcp.tools.ToolResultfrom a Pydantic response and a Jinja2 template, exposing the response inside the template asresponseand the short aliasr.
Tool migration (review_urgent_order_requirements):
- Public wrapper now returns
ToolResultinstead of the bare Pydantic model. The inner_review_urgent_order_requirements_implkeeps the Pydantic return shape so all business-logic tests continue to assert against the model directly. - New Jinja2 templateurgent_orders_review.md.j2renders supplier- grouped tables with totals, costs, and a sensible empty-state message. - Existing tests retargeted to call_impl(cleaner separation — business-logic tests don't need to peer through ToolResult). Two new tests (test_review_urgent_orders_returns_tool_result_with_both_payloadsandtest_review_urgent_orders_renders_empty_state_in_markdown) cover the wrapper specifically: assertToolResultshape, both payloads present, markdown contains expected fragments.
Result for end users (LLM clients):
- Old: opaque JSON dump of nested suppliers/items in chat. - New: rendered markdown with
a clear summary line, supplier H3 sections, per-supplier item tables, and a "next
steps" footer pointing to the PO generation tool. Programmatic consumers keep getting
the full Pydantic dump via
structured_content.
Verification: 73 client + 309 MCP = 382 tests pass (was 380; +2 new
wrapper tests). uv run poe check green.
Refs #149, refs #154.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- refactor(mcp): apply PR #159 review feedback + /simplify pass
Addresses the four design tweaks agreed in chat plus the four Copilot review findings on the original PR, plus a /simplify pass.
Design tweaks:
- Templates moved to grouped layout (templates/workflows/urgent_orders/review.md.j2).
render_template takes template_path under templates/ instead of a flat name. - Drop
the
ralias inside templates — use the descriptiveresponseexclusively. - Test refactor: introduceunwrap_tool_result(result, model_class)andtool_result_text(result)so existing tests retarget through the public wrapper without poking at structured_content. Local_reviewhelper avoids the duplicated unwrap two-liner. - Hook tuning: ruff--fixmoved off Edit (was stripping unused imports between sequential Edits). Now runs only on Write and at Stop. Edit still runs ruffformat(cosmetic, never strips imports).
Copilot review findings:
- Semgrep XSS audit on jinja2.Environment: switched select_autoescape(default=False) to
select_autoescape() so HTML/XML templates would autoescape if anyone adds them.
Markdown templates remain unescaped (the right behavior for LLM-rendered output). -
Stale review_urgent_order_requirements docstring: replaced the old
Returns: ReviewUrgentOrdersResponseblock + JSON example with a description of the ToolResult shape (content + structured_content). - make_tool_result reserved-key override risk: dropped the_RESERVED_TEMPLATE_KEYSruntime check. Python's standard duplicate-keyword behavior already raises TypeError if a caller passesresponse=themselves.
Simplify findings:
- tool_result_text() helper extracted from test-local _markdown + the SKILL.md inlined
example, so the same coercion lives in one place. - Stop hook no longer duplicates
ty check— the Write hook covers it. Stop only runs ruff--fixon changed Python files. - Trimmed narrative docstrings in templates/init.py and tool_result_utils.py (dropped the v3-migration narrative, speculative autoescape comment, and Usage:: block that duplicates add-mcp-tool/SKILL.md). - Added apluralizeJinja filter so the template doesn't repeat{{ '' if x == 1 else 's' }}three times.
The /add-mcp-tool skill is updated to point at the new patterns (template_path, unwrap_tool_result, tool_result_text) so future tools follow the same shape.
Verification: 73 client + 309 MCP = 382 tests pass.
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcp: Migrate all remaining tools to make_json_result
(
360cb41)
Phase 2 of #179. Wraps every public MCP tool in a ToolResult per SEP-1865 so content carries indented JSON (LLM model context) and structured_content carries the dict dump (programmatic consumers). The hand-written markdown formatters that lived behind load_template/format_template are gone — hosts that want a pretty rendering can do it from the structured payload.
Tools migrated (21): - workflows/urgent_orders.py: generate_purchase_orders_from_urgent_items (the reviewer-flagged sibling from #186) - workflows/forecast_management.py: manage_forecast_group, update_forecast_settings, plus structural rewrites of forecasts_update_and_monitor (str → ForecastsUpdateAndMonitorResponse) and forecasts_get_for_products (str → ForecastsGetForProductsResponse with ForecastItem subrows + summary stats) - workflows/product_management.py: configure_product, plus structural rewrite of products_configure_lifecycle (str → ProductLifecycleResponse with previous/new ProductLifecycleStatus snapshots and next_steps) - workflows/supplier_onboarding.py: structural rewrite of create_supplier_with_products (str → existing CreateSupplierWithProductsResponse — the typed model already existed, just wasn't being returned) - foundation/customers.py: get_customer (Optional → GetCustomerResponse wrapper), list_customers - foundation/inventory.py: set_product_inventory - foundation/locations.py: list_locations, create_location (single-info → CreateLocationResponse wrapper) - foundation/products.py: get_product (Optional wrapper), search_products, create_product (single-info wrapper), delete_product - foundation/purchase_orders.py: get_purchase_order (Optional wrapper), list_purchase_orders, create_purchase_order, delete_purchase_order - foundation/sales_orders.py: create_sales_order (single-info wrapper), get_sales_orders, list_sales_orders, delete_sales_orders - foundation/suppliers.py: get_supplier (Optional wrapper), list_suppliers, create_supplier (single-info wrapper), delete_supplier
Pattern: for tools whose impl returns a Pydantic model directly, the public wrapper
is now a one-liner: return make_json_result(response). Tools whose generated impl
returns T | None get a thin GetXResponse(x: T | None) wrapper so the None case still
serializes through make_json_result. Tools whose impl returned T (not SomeResponse)
get a CreateXResponse(x: T) wrapper for the same reason.
Cleanup: - Deleted templates/ directory in full (no callers left after the four forecast_management + product_management + supplier_onboarding tools were rewritten away from format_template/load_template).
Tests: every test file got a per-tool call* helper that wraps the tool call and
unwraps to the typed response, keeping test bodies clean
(response = await _call_get(...) instead of two-step unwrap each time).
Markdown-content assertions (e.g. checking for emoji or section headers in the returned
string) were rewritten to assert on the typed payload's fields. 315 MCP tests + 74
client tests all pass.
Closes #187. Refs #179.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Adds set_preferences and get_preferences MCP tools backed by FastMCP v3's
session-scoped ctx.set_state / ctx.get_state. Workflow tools that take filter
arguments now fall back to stored preferences when an arg is omitted, so an LLM can say
"use category=Widgets" once and subsequent tool calls inherit it without restating
filters.
What's new¶
tools/preferences.py—SessionPreferencesPydantic model withcategory,location_code,supplier_code,days_threshold,dry_run. Persisted under a single namespaced key (stocktrim.preferences) as a plain dict (JSON-serializable per fastmcp v3 default). -set_preferencestool — partial update (omitted fields keep prior value). -get_preferencestool — returns the current merged preferences. -resolve(explicit, prefs, attr, default)precedence helper: explicit arg → stored preference → tool default.
Wired tools¶
forecasts_get_for_products—category,supplier_code,location_codefall back viaresolve(...). -review_urgent_order_requirements—days_thresholdfalls back; if nolocation_codeslist is supplied, the singularprefs.location_codeis wrapped as[code]. -generate_purchase_orders_from_urgent_items— same filter fallback, plus adry_runshort-circuit: whenprefs.dry_runis True, the impl logs and returns an empty response without calling the V2 API.
Tests¶
test_preferences.py(12 tests) — load/save round-trip, JSON-dict shape,resolveprecedence including the "0 is real, not absence" edge case, partial-update semantics, dry_run toggle. -test_urgent_orders.py(3 new tests) — prefdays_thresholdis inherited when arg omitted, explicit arg wins over pref, dry_run short-circuits the API mutation. -conftest.py—mock_contextnow hasAsyncMock-backedget_state/set_stateso all existing tests keep working unchanged.
Closes #150
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
Refactoring¶
- Standardize UNSET coercion via unwrap_unset/to_unset
(
8145755)
Adds two helpers to stocktrim_public_api_client.utils:
unwrap_unset(value, default=None)— overloads toT | None(no default) andT(with non-None default). Subsumes the priorunset_to_nonehelper and the inlinex if x not in (None, UNSET) else defaultternary. -to_unset(value)—None → UNSETfor outbound PATCH bodies that must preserve "field not provided" semantics.
Migrates ~115 inline coercion sites across 12 files (MCP server tools, workflows,
resources + client lib). The MCP server's unset_to_none is replaced with a re-export
of unwrap_unset from the new module.
Mirrors the convention already established in the katana-openapi-client sibling project.
Closes #189
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Removes 731 LOC of structured-logging-disguised-as-tracing in favor of fastmcp 3.x's native OpenTelemetry instrumentation and operator-chosen middleware. The MCP server is published on PyPI; prescribing an observability stack forces every consumer to live with our choices.
What changed:
- Deleted stocktrim_mcp_server/src/stocktrim_mcp_server/observability.py (137 LOC: @observe_tool, @observe_service decorators). - Deleted stocktrim_mcp_server/tests/test_observability.py (222 LOC, 15 tests for the now-removed decorators). - Removed 11 @observe_tool applications across 5 source files (foundation/suppliers + 4 workflows/*). - Removed the matching imports from those 5 files.
What replaces it (operator-chosen, not shipped):
- fastmcp 3.x emits OTel spans natively with MCP semantic conventions — operators set OTEL_EXPORTER_OTLP_ENDPOINT and pick their backend. - fastmcp middleware system covers tool-boundary structured logging for operators who prefer logs to traces. - opentelemetry-instrumentation-httpx covers outbound API-call tracing.
Documentation:
- New docs/mcp-server/observability.md walks operators through the three layers (app logs / tool spans / HTTP spans) and explains the library/operator separation. - Old docs/mcp-server/logging.md deleted (claimed automatic tool instrumentation, no longer accurate). - server.py instructions block updated; examples.md cross-link updated.
What's preserved:
- structlog setup in logging_config.py — still useful for app-level events (lifespan, auth errors, server start/stop). Tunable via LOG_LEVEL and LOG_FORMAT env vars. - ErrorLoggingTransport in stocktrim_public_api_client — different concern (capturing API-vs-spec divergences for our own feedback loop), library-author scope, not operator scope.
Verification: uv run poe check passes (73 client + 303 MCP = 376 tests;
was 318 MCP, lost 15 from test_observability deletion = expected).
Closes #147. Refs #154.
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcp: Factor delete-tool elicitation match into shared helper
(
cb82c6e)
The four _delete_*_impl tools (suppliers, products, purchase_orders, sales_orders)
repeated the same four-arm Accepted/Declined/Cancelled/fallthrough match block — ~12
lines × 4 tools = ~48 LOC of near-identical destructive-flow code.
Extracted to stocktrim_mcp_server.tools.elicitation.run_delete_elicitation, a generic
helper parameterised on:
message— preview shown to the user -entity_label— interpolated into declined/cancelled/unexpected text -on_accept— async callable returning(success, message)-response_factory— constructor for the concreteDelete*Response
The helper is generic over R: BaseModel so the concrete response type is preserved at
the call site (no cast needed). Net -92 LOC across 4 tools while consolidating a
security-sensitive code path into one auditable place.
The sales_orders "Unexpected elicitation response" message now follows the same
template as the other three (was previously "for sales orders deletion", now "for sales
orders for product {id}") — slightly more informative; no test pinned the prior wording.
Closes #191
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- mcpb: Simplify build script + drop stale FastMCP version reference
(
cb41d18)
Applies findings from a three-axis review (reuse / quality / efficiency) of the MCPB packaging additions. Behavior unchanged; the artifact path and pipeline are identical.
-
`scripts/build_mcpb.py`: - Extract `_write_substituted` helper for the manifest+pyproject template writes — the two blocks were identical apart from src/dest paths, with the same UTF-8/LF encoding kwargs repeated verbatim. The WHY comment about cross-platform encoding now lives on the helper, not duplicated above each call site. - Replace `if artifact.exists(): artifact.unlink()` with `artifact.unlink(missing_ok=True)` — pre-check was redundant on Python 3.11+ and racy on principle. - Drop the `if PKG_README.exists()` guard before `shutil.copy2`. `stocktrim_mcp_server/README.md` is committed; the guard provided false reassurance for an impossible scenario. If it ever does go missing, `shutil.copy2` raising loudly is the right behavior.
-
`.github/workflows/release.yml`: drop `| tail -n 1` from the artifact-capture step. The script prints exactly one line; the pipe was defensive scaffolding around a single-line stdout.
-
`stocktrim_mcp_server/README.md`: `FastMCP 2.11.0` -> `FastMCP v3`. Stale version in prose — `pyproject.template.toml` already pins `fastmcp>=3.0.0,\<4.0.0` after the v3 migration (#155).
Smoke-tested with `MCPB_SKIP_PACK=1 python scripts/build_mcpb.py` — staging directory contents identical (manifest.json, pyproject.toml, README.md, src/). `uv run poe check` green (75 client
- 309 MCP tests).
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Testing¶
- Remove dead stocktrim_client_with_mock_transport fixture
(
32f87e8)
Addresses Copilot's review on #168: the fixture was already broken — `AuthenticatedClient._client` is initialized to `None` and isn't created until `get_httpx_client()` runs, so the original `client._client._transport = mock_transport` assignment raised `AttributeError`. My #168 "fix" replaced a mypy-style ignore with an `assert client._client is not None`, but the assert would always fire the moment anything actually used the fixture.
It never did: `grep -rn stocktrim_client_with_mock_transport` finds zero callers anywhere in `tests/` or `stocktrim_mcp_server/tests/`. That's why CI stayed green through both the original broken version and my equally-broken "fix".
Deleting it rather than rewriting it via `set_httpx_client` / `set_async_httpx_client`, since there's no consumer and we already have working transport-mocking patterns (`httpx.MockTransport` via `mock_transport_handler` / `AsyncMockTransport`) used by every actual test that needs network isolation.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
mcp v0.16.0 (2026-05-27)¶
Changed¶
- mcp: The 9 workflow tools (
manage_forecast_group,update_forecast_settings,forecasts_update_and_monitor,forecasts_get_for_products,review_urgent_order_requirements,generate_purchase_orders_from_urgent_items,configure_product,products_configure_lifecycle,create_supplier_with_products) now emit flat top-level parameters instead of the nested{"request": {...}}wrapper. Foundation tools were already flat — workflow tools are now consistent with them. Closes #116.
Fixed¶
- mcp:
Field(description=...)metadata is now preserved when@unpack_pydantic_paramsflattens a Pydantic model. The 22 foundation tools' emitted schemas previously dropped field descriptions because the syntheticinspect.Parameterused only the bare annotation; descriptions now reach FastMCP viaAnnotated[T, FieldInfo(...)]. Numeric/string constraints (ge,le,min_length, etc.) are preserved through the same mechanism.
Removed¶
- mcp: The legacy
{"request": {...}}wrapper input shape. Calls using the legacy shape now raiseTypeErrornaming the flat field names so callers get actionable feedback. Clients must update to flat parameters (this is the documented purpose of the 0.16.0 schema flattening — see #116).
Tracking¶
- Upstream contribution to
jlowin/fastmcp#1784is tracked in #206. Once FastMCP itself learns to flatten parameters at registration time,stocktrim_mcp_server/src/stocktrim_mcp_server/unpack.pycan be deleted.
v0.12.0 (2025-11-22)¶
Bug Fixes¶
-
mcp: Configure logging at module level to prevent JSON parsing errors (#119,
65e6602) -
docs: update documentation to accurately reflect project capabilities
This commit comprehensively updates the documentation to showcase the project's production-ready quality and extensive feature set.
Main README.md Updates¶
- Add code quality badges (CI, Codecov, Security, pre-commit) - Update MCP Server features from "5 tools" to "43+ tools, 5 workflow prompts, and resource endpoints" - Expand MCP Server Tools section with complete breakdown: - 27 Foundation Tools (CRUD operations) - 16 Workflow Tools (high-level business operations) - 5 MCP Prompts (guided workflows) - MCP Resources (read-only discovery) - Add new "Code Quality & Testing" section highlighting: - 50+ test files with comprehensive coverage - Linting, type checking, and security scanning - CI/CD pipeline with matrix testing - Quality tooling commands - Enhance Architecture section with MCP Server Architecture details: - Service layer architecture - Parameter flattening and user confirmation patterns - Structured logging and observability - Reference to Architecture Decision Records
Documentation Files Updates¶
- Update tools.md header from "20+ tools" to "43+ tools" - Add missing
manage_forecast_groupworkflow tool documentation - Update prompts.md to document all 5 workflow prompts: - purchasing_workflow (existing) - forecast_accuracy_review (new) - supplier_performance_review (new) - stockout_prevention (new) - product_lifecycle_review (new)
Impact¶
The documentation now accurately represents the project as: - Production-ready with enterprise-grade quality infrastructure - Feature-rich with 43+ tools vs. previously understated "5 tools" - Well-tested with comprehensive CI/CD and security scanning - Architecturally sophisticated with clean patterns and safety measures
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
- fix(mcp): configure logging at module level to prevent JSON parsing errors
Problem When Claude Desktop connected to the MCP server, it reported: "Unexpected non-whitespace¶
character after JSON at position 4 (line 1 column 5)"
Root cause: logger.info("prompts_registered") at line 369 executed during module
import (before main() ran), but logging wasn't configured yet. Log output went to
stdout, polluting the MCP JSON-RPC protocol which requires clean JSON on stdout.
Solution 1. Move configure_logging() from main() to module level (line 27) - Ensures logging¶
is configured before any module-level code executes - Runs at import time, before tool registration
-
Re-enable
logger.info("prompts_registered")at line 370 - Now safe because logging is already configured - Output goes to stderr (verified in logging_config.py:39) -
Remove duplicate
configure_logging()call frommain() -
Add documentation explaining initialization order
Testing Verified server starts without JSON errors: ```bash timeout 3 uv run stocktrim-mcp-server¶
2>&1 | head -30 ```
- fix: correct tool counts and markdown formatting
Address Copilot review feedback:
-
Fix tool count discrepancies: - Actual count: 21 foundation + 9 workflow = 30 total tools - Updated README.md (3 locations) - Updated docs/mcp-server/tools.md
-
Fix escaped backtick formatting in api-feedback.md: - /api/RunForecast (line 996) - /api/RunForecastCalculations (line 1012) - Removed backslash escapes for proper markdown rendering
Verified counts with: grep -r "mcp.tool()" stocktrim_mcp_server/src/stocktrim_mcp_server/tools/foundation/ | wc -l # 21 grep -r "mcp.tool()" stocktrim_mcp_server/src/stocktrim_mcp_server/tools/workflows/ | wc -l # 9
-
docs(mcp): fix manage_forecast_group parameter documentation (#120)
-
Initial plan
-
docs(mcp): fix manage_forecast_group parameters to match implementation
Co-authored-by: dougborg 1261222+dougborg@users.noreply.github.com
Co-authored-by: copilot-swe-agent[bot] 198982749+Copilot@users.noreply.github.com
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
Co-authored-by: Copilot 198982749+Copilot@users.noreply.github.com
Chores¶
-
release: Mcp v0.12.0 (
3e9b436) -
release: Mcp v0.13.0 (
4d5c1a1) -
release: Mcp v0.14.0 (
f529c40) -
release: Mcp v0.15.0 (
78dcfcc) -
release: Mcp v0.15.1 (
3407b24)
Documentation¶
- Clarify product search is exact match only, remove deprecated methods
(
ec96bbc)
This change documents and clarifies that the StockTrim Products API only supports exact code matching, not prefix or partial search. It also removes deprecated methods that had misleading names.
Changes¶
Documentation - Add API feedback section documenting Products endpoint 404 behavior - Document¶
that code parameter only supports exact matching - Explain why 404 for query filtering
is non-standard REST
API Client (stocktrim_public_api_client) - Update get_all() docs to clarify exact match only¶
- Add
find_by_exact_code()method with clear naming - Remove deprecatedsearch()method - Update tests to check forfind_by_exact_code()instead ofsearch()
MCP Server (stocktrim_mcp_server) - Update find_by_exact_code() docs with API limitation¶
note - Remove deprecated search() method - Rename tests: test_search_* →
test_find_by_exact_code_*
Testing - All 73 public API client tests pass - All 276 MCP server tests pass - Test function¶
names now match the actual method names
Breaking Changes Since we're pre-1.0, deprecated methods were removed rather than kept for¶
backward compatibility: - ProductService.search() → use find_by_exact_code() instead
- Products.search() → use find_by_exact_code() instead
Note: For keyword search functionality across product names, codes, and
categories, use the Order Plan API (implemented in PR #99).
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
-
Update documentation to accurately reflect project capabilities (#114,
5a8f6dd) -
docs: update documentation to accurately reflect project capabilities
This commit comprehensively updates the documentation to showcase the project's production-ready quality and extensive feature set.
Main README.md Updates¶
- Add code quality badges (CI, Codecov, Security, pre-commit) - Update MCP Server features from "5 tools" to "43+ tools, 5 workflow prompts, and resource endpoints" - Expand MCP Server Tools section with complete breakdown: - 27 Foundation Tools (CRUD operations) - 16 Workflow Tools (high-level business operations) - 5 MCP Prompts (guided workflows) - MCP Resources (read-only discovery) - Add new "Code Quality & Testing" section highlighting: - 50+ test files with comprehensive coverage - Linting, type checking, and security scanning - CI/CD pipeline with matrix testing - Quality tooling commands - Enhance Architecture section with MCP Server Architecture details: - Service layer architecture - Parameter flattening and user confirmation patterns - Structured logging and observability - Reference to Architecture Decision Records
Documentation Files Updates¶
- Update tools.md header from "20+ tools" to "43+ tools" - Add missing
manage_forecast_groupworkflow tool documentation - Update prompts.md to document all 5 workflow prompts: - purchasing_workflow (existing) - forecast_accuracy_review (new) - supplier_performance_review (new) - stockout_prevention (new) - product_lifecycle_review (new)
Impact¶
The documentation now accurately represents the project as: - Production-ready with enterprise-grade quality infrastructure - Feature-rich with 43+ tools vs. previously understated "5 tools" - Well-tested with comprehensive CI/CD and security scanning - Architecturally sophisticated with clean patterns and safety measures
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
- Apply suggestions from code review
Co-authored-by: Copilot 175728472+Copilot@users.noreply.github.com
- Update docs/mcp-server/prompts.md
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
Features¶
-
feat: add Cursor rules for better AI assistance
-
Add comprehensive .cursorrules file with project architecture - Document transport-layer resilience pattern - Include domain helpers documentation - Specify generated code rules (DO NOT EDIT) - Add development workflow and testing patterns
-
chore: update uv.lock after dependency sync
-
Apply suggestions from code review
Co-authored-by: Copilot 175728472+Copilot@users.noreply.github.com
-
fix: address PR review comments for .cursorrules
-
Add comprehensive domain helpers section with all 12 helper modules - Fix method names (get_all() not list_all() for Customers) - Document all helper modules with accurate method lists - Add reference to helpers documentation
-
Update .cursorrules
Co-authored-by: Doug Borg dougborg@apple.com
-
mcp: Add stockout_prevention and product_lifecycle_review prompts (#113,
6dced97) -
feat(mcp): consolidate stockout_prevention and product_lifecycle_review prompts
Consolidates prompt implementations from PR #112 and PR #110 into a single feature branch. This adds two new workflow prompts to the existing three, bringing the total to five comprehensive inventory management workflows.
Added Prompts:
-
stockout_prevention(from PR #112) - Proactive stockout prevention workflow - 5-step process: Risk Analysis, Gap Identification, Forecast Review, Preventive Action, and Recommendations - Parameters: location_code (required), days_ahead (default: 14) - Tools: review_urgent_order_requirements, generate_purchase_orders_from_urgent_items, forecasts_get_for_products -
product_lifecycle_review(from PR #110) - Product portfolio optimization workflow - Covers: Portfolio Overview, Performance Analysis, Configuration Review, Optimization Recommendations, and Configuration Changes - Parameters: category (default: "all"), include_inactive (default: False) - Analysis areas: slow movers, overstock risks, forecast accuracy, supplier consolidation, missing configurations
Complete Prompt Inventory (5 total): - purchasing_workflow - forecast_accuracy_review - supplier_performance_review - stockout_prevention (new) - product_lifecycle_review (new)
Implementation Details: - All imports consolidated (added mcp.types imports for product_lifecycle_review) - Added logger initialization for stockout_prevention error handling - All prompts properly registered in register_workflow_prompts() - Combined test suite with 42 tests covering all prompts - All tests passing (318 total in suite) - Linting and formatting clean
Supersedes: - PR #112: Stockout Prevention prompt - PR #110: Product Lifecycle Review prompt
Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
- fix: update product_lifecycle_review tests to access .content.text
Fixed test assertions to access message content via .content.text instead of
.content directly. Message() factory function returns objects with TextContent in the
content field, requiring the .text property access.
Updated 6 test methods: - test_product_lifecycle_review_default_params - test_product_lifecycle_review_with_category - test_product_lifecycle_review_with_inactive - test_product_lifecycle_review_all_params - test_product_lifecycle_review_resources_mentioned - test_product_lifecycle_review_analysis_areas
All 42 workflow prompt tests now passing.
🤖 Generated with Claude Code
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
-
mcp: Implement forecast accuracy review prompt (#111,
c9962ba) -
feat(mcp): implement forecast accuracy review prompt (issue #106)
Co-authored-by: Doug Borg dougborg@dougborg.org
-
mcp: Implement supplier_performance_review prompt (#109,
3a687a8) -
feat(mcp): implement supplier_performance_review prompt
Implement comprehensive supplier performance review prompt with: - Optional supplier_code parameter (defaults to None for all suppliers) - period_days parameter (defaults to 90 days) - Dynamic current date injection - Comprehensive system instructions for supply chain analysis - Guided multi-step workflow (overview, analysis, trends, recommendations) - Full test coverage (10 tests passing) - Token size validation (under 5KB)
-
mcp: Setup MCP Prompts infrastructure (Issue #101) (#107,
206db80) -
feat(mcp): implement MCP Prompts infrastructure
-
Create prompts directory structure with init.py and workflows.py - Add register_all_prompts() and register_workflow_prompts() functions - Update server.py to register prompts after resources - Add test infrastructure in tests/test_prompts/ - All tests pass (280 total, including 2 new prompt tests) - Server imports successfully with "Prompts registered" log message
-
style(mcp): fix formatting in test_workflows.py
-
fix: use structured logging for prompts registration
Address review comment to use event-driven logging consistent with the rest of the file (e.g., 'server_ready', 'client_initialized').
Changed 'Prompts registered' to 'prompts_registered' to match the structured logging pattern.
v0.11.0 (2025-11-11)¶
Build System¶
Bumps mdformat from 0.7.22 to 1.0.0. - Commits
--- updated-dependencies: - dependency-name: mdformat dependency-version: 1.0.0
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: Doug Borg dougborg@dougborg.org
- deps: Bump openapi-python-client from 0.27.0 to 0.27.1
(
4749d3f)
Bumps openapi-python-client from 0.27.0 to 0.27.1. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: openapi-python-client dependency-version: 0.27.1
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
- deps: Bump pre-commit from 4.3.0 to 4.4.0
(
57c1849)
Bumps pre-commit from 4.3.0 to 4.4.0. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: pre-commit dependency-version: 4.4.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] support@github.com
- deps: Bump pydantic from 2.12.3 to 2.12.4
(
3c4f712)
Bumps pydantic from 2.12.3 to 2.12.4. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: pydantic dependency-version: 2.12.4
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
- deps: Bump python-semantic-release from 10.4.1 to 10.5.0
(
89a11ed)
Bumps python-semantic-release from 10.4.1 to 10.5.0. - Release notes - Changelog¶
--- updated-dependencies: - dependency-name: python-semantic-release dependency-version: 10.5.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] support@github.com
Chores¶
- release: Mcp v0.11.0
(
e129cbb)
Documentation¶
-
docs: add ADRs and update documentation
-
Add ADR 002: Tool Interface Pattern (Pydantic + FastMCP) - Add ADR 003: Automated Tool Documentation strategy - Update overview.md and README.md with current features - Organize tool documentation investigation into docs/ - Add session summary for 2025-11-07
Related: #84, #85, #86
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
Features¶
-
Replace product search with keyword search using Order Plan API (#99,
102cae9) -
feat: replace product search with keyword search using Order Plan API
Replace nearly-useless prefix-based product search with comprehensive keyword search that works across product names, codes, and categories.
Changes: - tools/foundation/products.py: - Rename SearchProductsRequest.prefix → search_query - Replace Products API call with Order Plan API searchString - Update docstring with keyword search examples - tests: Update mocks to use Order Plan API and SkuOptimizedResultsDto
Benefits: - Search by product name: "blue widget" - Search by category: "electronics" - Search by partial code: "WIDG" matches "WIDGET-001" - Much more useful than prefix-only search
Before: search_products(prefix="WIDG") - must know exact prefix
After: search_products(search_query="widget") - natural search
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
- fix: use OrderPlanFilterCriteria instead of DTO for 415 error
The Order Plan API endpoint expects OrderPlanFilterCriteria, not OrderPlanFilterCriteriaDto. Using the DTO version caused 415 "Unsupported Media Type" errors.
Changes: - Import OrderPlanFilterCriteria instead of OrderPlanFilterCriteriaDto - Update test to be more robust when checking call arguments
Fixes the 415 errors seen in production logs.
- feat: add unpack decorator for flattened MCP parameters
Add @unpack_pydantic_params decorator to enable flattened parameter calls instead of nested request objects, matching the pattern from katana-mcp.
Changes: - Add unpack.py module with Unpack marker and decorator - Apply decorator to search_products tool - Update tests to use flattened parameters
Benefits: - Better DX: search_products(search_query="widget") instead of search_products(request={"search_query": "widget"}) - Maintains Pydantic validation while exposing flat params to MCP - Compatible with FastMCP's parameter introspection
Pattern from: https://github.com/dougborg/katana-openapi-client
- feat(mcp): implement parameter flattening for all tools
Apply @unpack_pydantic_params decorator to all MCP tools to expose flattened parameters instead of nested objects. This improves compatibility with Claude Code and other MCP clients that have issues serializing nested parameter objects.
Changes: - Add unpack.py module with @unpack_pydantic_params decorator (from Katana) - Apply decorator to all 27 tools across 11 tool files - Update all foundation tool tests to use flattened parameters - Add ADR 0001 documenting parameter flattening decision - Update README with parameter flattening pattern documentation
Technical Details: The decorator transforms function signatures at import time: - Scans for Annotated[Model, Unpack()] parameters - Extracts Pydantic model fields as individual KEYWORD_ONLY params - Updates signature and annotations for FastMCP introspection - At runtime, reconstructs validated model instances from flat params
This provides: - Flat parameters for MCP protocol compatibility - Pydantic validation for type safety - Clean tool code working with typed model objects - Automatic parameter documentation from Field descriptions
All 276 tests passing.
Related: Katana MCP implementation (ef59809, 862ce79, a025ca9)
- fix: address PR review feedback
Address review comments from PR #99:
-
Revert nullable supplier code - The SupplierInfo.code field should remain required (str) as it's fundamental for identifying suppliers and the underlying SupplierResponseDto has it as non-nullable.
-
Improve 404 handling documentation - Add detailed comment explaining why StockTrim API returns 404 for "no results" instead of the more conventional 200 with empty list, and why we treat it as expected behavior rather than an error.
-
Fix empty product code handling - Instead of using empty string fallback which violates ProductInfo schema, filter out order plan items that have missing/empty product codes to prevent creating invalid product records.
-
docs: add ADR 001 documenting user confirmation pattern
Add Architecture Decision Record documenting the choice to use FastMCP Elicitation for user confirmation on destructive operations.
Documents: - Context and problem statement - 4 options considered (pre-flight, parameter, prompt, elicitation) - Decision rationale (MCP-native, industry best practice) - Implementation pattern with code examples - Tool categorization by risk level - Testing requirements - Consequences and validation criteria
Decision: Use FastMCP Elicitation (MCP native protocol)
Rationale: Standard protocol, strong safety guarantees, rich context,
excellent developer experience
Part of Issue #80 implementation.
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
-
test: update purchase order and sales order deletion tests for elicitation
-
Replace old deletion tests with elicitation pattern tests - Add imports for AcceptedElicitation, DeclinedElicitation, CancelledElicitation - Test all elicitation response paths (not found, accepted, declined, cancelled) - Align with product and supplier test patterns - All 276 tests passing
-
test: implement autospec for service mocks to enforce interface compliance
Use create_autospec() for all service mocks in conftest.py to prevent tests from passing while mocking non-existent methods. This ensures that test mocks always match the actual service interfaces.
Benefits: - Tests will fail immediately if they mock non-existent methods - Prevents bugs where tests pass but production code fails - Provides better refactoring safety - Catches method name typos and signature mismatches
This change was implemented after discovering that tests were mocking services.suppliers.list_suppliers() instead of list_all(), which allowed the bug to slip through to the resource implementation.
All 276 tests pass with autospec enforcement.
Addresses: #82
-
fix: add nullable enum support to client regeneration + fix resource bug
-
Add nullable enum field support to regeneration script - Add
add_nullable_to_enum_fields()function - Mark OrderPlanFilterCriteria.currentStatus as nullable - Fixes "None is not a valid CurrentStatusEnum" errors - Addresses: #83 -
Fix supplier directory resource method name - Change
list_suppliers()tolist_all()- This bug was caught by autospec implementation - Related: #82
The regeneration script now handles enum fields that can be null in API responses, using the allOf + nullable pattern for OpenAPI 3.0.
- fix: regenerate client with nullable currentStatus enum field
Regenerated Python client from StockTrim OpenAPI spec with the nullable enum field fix applied. The currentStatus field in OrderPlanFilterCriteria can now handle null values from the API.
Changes: - OrderPlanFilterCriteria.currentStatus is now CurrentStatusEnum | None | Unset - from_dict() properly handles None values without throwing validation errors
This fixes the "None is not a valid CurrentStatusEnum" error when querying order plan data.
Fixes: #83
- fix: remove limit parameter from ProductService.list_all() calls
The ProductService.list_all() method doesn't accept a limit parameter, but foundation.py was calling it with limit=50. This was caught when testing resources with MCP Inspector at runtime.
Root cause: test_foundation.py was using mock_foundation_context which overrode the autospec'd services from conftest.py with plain AsyncMock, so tests couldn't catch the interface mismatch.
Changes: - foundation.py: Remove limit=50 from list_all() call, use slicing instead - test_foundation.py: Remove mock_foundation_context fixture that was overriding autospec'd services with plain AsyncMock - test_foundation.py: Update all tests to use mock_context directly - test_foundation.py: Fix catalog limit test to verify slicing behavior
This ensures autospec catches interface mismatches in resource tests.
- fix: use bulk endpoint for listing all suppliers
The Suppliers.get_all() method was incorrectly using /api/Suppliers endpoint without a code parameter, which returns 404. The StockTrim API has separate endpoints for different supplier operations: - /api/Suppliers?code=X - returns single supplier (requires code) - /api/SuppliersBulk
- returns all suppliers (no parameters)
This is different from other endpoints like Customers and Products which return arrays from their main endpoint.
Changes: - Import get_api_suppliers_bulk from generated API - Use bulk endpoint when code is UNSET (listing all) - Use single endpoint when code is provided (get specific supplier) - Update docstring to clarify the conditional behavior
This fixes the 404 error in the supplier directory MCP resource.
- feat(mcp): add Dockerfile for Docker MCP Registry
Add Dockerfile to support publishing MCP server to Docker Hub via Docker MCP Registry. Image installs stocktrim-openapi-client from PyPI and runs the MCP server.
Related to #2
- feat(mcp): add tools.json generation script
Add script to auto-generate tools.json from registered MCP tools to keep Docker MCP Registry submission in sync with actual tool implementations.
Script introspects FastMCP tool manager and extracts tool names and descriptions from registered tools.
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
v0.10.0 (2025-11-09)¶
Chores¶
Documentation¶
-
mcp: Comprehensive documentation enhancements and template externalization (#5) (#78,
d5ee08d) -
test(mcp): add comprehensive tests for structured logging
Add test coverage for logging configuration and observability decorators.
Tests added: - test_logging_config.py (10 tests): * Configuration with different formats (console, JSON) * Configuration with different log levels * Logger instance creation * Environment variable handling * Invalid configuration handling
- test_observability.py (15 tests): * @observe_tool decorator functionality * @observe_service decorator functionality * Success and failure scenarios * Timing measurements * Parameter filtering * Function metadata preservation * Exception propagation
All 25 tests pass successfully.
This provides coverage for the structured logging implementation added in commit 925a19f.
- fix(test): replace time.sleep with asyncio.sleep in async tests
Address code review feedback: use asyncio.sleep() instead of time.sleep() in async test functions to properly test timing without blocking the event loop.
Changes: - Replace time.sleep() with asyncio.sleep() in timing tests - Remove AsyncMock() calls that don't yield control - Import asyncio instead of time module
Fixes PR review comments from Copilot.
- test: add comprehensive tests for forecast management tools
Add 13 new unit tests covering the two new forecast management workflow tools: - forecasts_update_and_monitor (5 tests) - forecasts_get_for_products (8 tests)
Tests cover: - Trigger-only and wait-for-completion modes - Progress monitoring and timeout handling
- Error scenarios and validation - Filtering, sorting, and priority indicators - Empty results and summary statistics
Also fix DTO field name issues in forecast_management.py: - Remove references to non-existent supplier_name field - Use correct field names (order_quantity, safety_stock_level) - Add pytest timeout marker for long-running test
All 235 tests now passing.
- fix: remove unused product_name assignment
Address Copilot review comment - remove unnecessary product_name assignment that was immediately overwritten. Now correctly uses product_code as the display name since the DTO doesn't have a product_description field.
- docs(mcp): enhance documentation and externalize markdown templates (#5)
This commit addresses issue #5 by significantly improving MCP server documentation for AI agent success. Changes include:
Documentation Improvements¶
-
Comprehensive Server Instructions (server.py) - Expanded FastMCP instructions from 6 lines to 200+ lines - Added tool categorization (Foundation vs Workflow) - Included 5 complete workflow examples: * Inventory Reordering (automated & manual approaches) * Forecast Management (update, monitor, analyze) * Supplier Onboarding (workflow vs step-by-step) * Product Configuration (lifecycle management) * Customer Order Fulfillment (complete flow) - Added best practices for tool selection - Documented error handling patterns - Included observability and performance notes
-
Complete Workflow Examples (docs/mcp-server/examples.md) - Real-world scenarios with full request/response flows - When-to-use guidance for each workflow - Trade-off analysis for different approaches - Advanced patterns and troubleshooting - Error handling best practices - Performance optimization tips
Code Quality Improvements¶
- Externalized Markdown Templates - Created templates/ directory for response templates - Extracted 6 markdown templates from forecast_management.py - Added template loader utility with format support - Cleaner code with better maintainability - Templates can now be edited without touching Python code
Technical Details¶
-
Templates use Python .format() for variable substitution - Template loader provides FileNotFoundError for missing templates - All existing tests pass without modification - Server imports successfully with new structure
-
docs(mcp): enhance workflow tool docstrings and complete tools.md (#5)
This commit completes issue #5 by enhancing workflow tool documentation with comprehensive docstrings and updating tools.md to include workflow tools.
Enhanced Workflow Tool Docstrings¶
Updated all 6 workflow tool docstrings with:
-
review_urgent_order_requirements - How it works section - Common use cases (weekly reorders, urgent restocking, etc.) - Typical workflow with step-by-step guidance - Enhanced example with detailed response structure - See Also section linking to examples.md
-
generate_purchase_orders_from_urgent_items - How it works section - Common use cases and best practices - Important notes about draft status and review requirements - Enhanced example showing multiple POs - See Also section with related tools
-
create_supplier_with_products - How it works with transactional approach explanation - Common use cases for supplier onboarding scenarios - Best practices for verification and error handling - Advantages over manual approach comparison - Enhanced example with 3 product mappings
-
configure_product - How it works section - Common use cases (discontinuation, seasonal activation, etc.) - Best practices for lifecycle management - Field mappings explanation (discontinue -> discontinued) - Multiple examples (discontinuing and activating products)
Updated tools.md¶
Added comprehensive workflow tools section including: - Tool categories explanation (Foundation vs Workflow) - When to use which type of tool - Detailed documentation for all 7 workflow tools: * Forecast management tools (3) * Urgent order management tools (2) * Supplier management tools (1)
- Product management tools (1) - Links to examples.md for complete workflows - Parameter documentation with ranges and defaults
Documentation Structure¶
Each enhanced docstring now includes: - Brief description - "How It Works" section - "Common Use Cases" section - "Best Practices" section (where applicable) - Enhanced examples with realistic data - "See Also" section with links to: * Complete workflows in examples.md * Related tools * Foundation tools for comparison
This makes the tools self-documenting and provides clear guidance for AI agents on when and how to use each tool.
- refactor: extract magic numbers to named constants
Address Copilot review comments by defining: - MAX_RESPONSE_SIZE_BYTES = 400_000 - ESTIMATED_CHARS_PER_FORECAST_ITEM = 500
This improves maintainability and makes token budget thresholds easy to adjust.
-
fix: address Copilot review comments
-
Extract priority threshold constants (HIGH=7, MEDIUM=14 days) - Remove unnecessary comments from test file - Fix markdown formatting for environment variables
Co-authored-by: Doug Borg dougborg@dougborg.org
Co-authored-by: Claude noreply@anthropic.com
Features¶
-
Add user confirmation for destructive operations (#80) (#81,
198eb66) -
docs: add ADR 001 documenting user confirmation pattern
Add Architecture Decision Record documenting the choice to use FastMCP Elicitation for user confirmation on destructive operations.
Documents: - Context and problem statement - 4 options considered (pre-flight, parameter, prompt, elicitation) - Decision rationale (MCP-native, industry best practice) - Implementation pattern with code examples - Tool categorization by risk level - Testing requirements - Consequences and validation criteria
Decision: Use FastMCP Elicitation (MCP native protocol)
Rationale: Standard protocol, strong safety guarantees, rich context,
excellent developer experience
-
test: update purchase order and sales order deletion tests for elicitation
-
Replace old deletion tests with elicitation pattern tests - Add imports for AcceptedElicitation, DeclinedElicitation, CancelledElicitation - Test all elicitation response paths (not found, accepted, declined, cancelled) - Align with product and supplier test patterns - All 276 tests passing
-
test: implement autospec for service mocks to enforce interface compliance
Use create_autospec() for all service mocks in conftest.py to prevent tests from passing while mocking non-existent methods. This ensures that test mocks always match the actual service interfaces.
Benefits: - Tests will fail immediately if they mock non-existent methods - Prevents bugs where tests pass but production code fails - Provides better refactoring safety - Catches method name typos and signature mismatches
This change was implemented after discovering that tests were mocking services.suppliers.list_suppliers() instead of list_all(), which allowed the bug to slip through to the resource implementation.
All 276 tests pass with autospec enforcement.
Addresses: #82
-
fix: add nullable enum support to client regeneration + fix resource bug
-
Add nullable enum field support to regeneration script - Add
add_nullable_to_enum_fields()function - Mark OrderPlanFilterCriteria.currentStatus as nullable - Fixes "None is not a valid CurrentStatusEnum" errors - Addresses: #83 -
Fix supplier directory resource method name - Change
list_suppliers()tolist_all()- This bug was caught by autospec implementation - Related: #82
The regeneration script now handles enum fields that can be null in API responses, using the allOf + nullable pattern for OpenAPI 3.0.
- fix: regenerate client with nullable currentStatus enum field
Regenerated Python client from StockTrim OpenAPI spec with the nullable enum field fix applied. The currentStatus field in OrderPlanFilterCriteria can now handle null values from the API.
Changes: - OrderPlanFilterCriteria.currentStatus is now CurrentStatusEnum | None | Unset - from_dict() properly handles None values without throwing validation errors
This fixes the "None is not a valid CurrentStatusEnum" error when querying order plan data.
Fixes: #83
- fix: remove limit parameter from ProductService.list_all() calls
The ProductService.list_all() method doesn't accept a limit parameter, but foundation.py was calling it with limit=50. This was caught when testing resources with MCP Inspector at runtime.
Root cause: test_foundation.py was using mock_foundation_context which overrode the autospec'd services from conftest.py with plain AsyncMock, so tests couldn't catch the interface mismatch.
Changes: - foundation.py: Remove limit=50 from list_all() call, use slicing instead - test_foundation.py: Remove mock_foundation_context fixture that was overriding autospec'd services with plain AsyncMock - test_foundation.py: Update all tests to use mock_context directly - test_foundation.py: Fix catalog limit test to verify slicing behavior
This ensures autospec catches interface mismatches in resource tests.
- fix: use bulk endpoint for listing all suppliers
The Suppliers.get_all() method was incorrectly using /api/Suppliers endpoint without a code parameter, which returns 404. The StockTrim API has separate endpoints for different supplier operations: - /api/Suppliers?code=X - returns single supplier (requires code) - /api/SuppliersBulk
- returns all suppliers (no parameters)
This is different from other endpoints like Customers and Products which return arrays from their main endpoint.
Changes: - Import get_api_suppliers_bulk from generated API - Use bulk endpoint when code is UNSET (listing all) - Use single endpoint when code is provided (get specific supplier) - Update docstring to clarify the conditional behavior
This fixes the 404 error in the supplier directory MCP resource.
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
- mcp: Add structured logging and observability
(
925a19f)
Implements comprehensive structured logging using structlog with automatic observability for all MCP tools and service operations.
Features: - Structured logging with dual format support (console/JSON) - Environment-based configuration (LOG_LEVEL, LOG_FORMAT) - Automatic tool invocation tracking with timing metrics - Service layer operation tracing at DEBUG level - Rich error context with categorization - Comprehensive documentation with examples
Changes: - Add structlog dependency (>=24.1.0) - Create logging_config.py for structured logging setup - Create observability.py with @observe_tool and @observe_service decorators - Update server.py to use structured logging for lifecycle events - Apply observability decorator to supplier tools - Add comprehensive logging documentation (docs/mcp-server/logging.md)
Console format provides human-readable colored output for development. JSON format provides machine-readable logs for production aggregation.
Example structured log events: - logging_configured: Logging system initialized - server_starting/ready/shutdown: Server lifecycle - tool_invoked/completed/failed: Tool execution tracking - service_operation_started/completed/failed: Service layer tracing
Closes #12
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
Changes¶
1. Enhanced Supplier Service - Extended SupplierService.create() to support all¶
SupplierRequestDto fields: - Contact info: email_address, primary_contact_name - Lead time: default_lead_time - Address: street_address, address_line_1, address_line_2, state, country, post_code - External ID: external_id
2. Enhanced Supplier Onboarding Tool - Updated create_supplier_with_products to match¶
suppliers_add_and_configure spec - Added all contact and address fields to request
model - Changed return type from JSON to markdown report - Enhanced output with: -
Structured supplier details (contact, lead time, address) - Success/failure tracking for
product mappings - Actionable next steps based on mapping results
3. New Product Lifecycle Management Tool - Implemented products_configure_lifecycle workflow¶
tool - Supports 4 lifecycle actions: - activate: Enable product and forecasting -
deactivate: Temporarily disable forecasting - discontinue: Mark as discontinued -
unstock: Remove from inventory management - Features: - Impact analysis (current
inventory, previous status) - Optional forecast recalculation - Before/after comparison
in markdown report - Error handling with graceful forecast failure
4. Comprehensive Test Coverage - Added 8 new unit tests for products_configure_lifecycle -¶
Updated 7 supplier onboarding tests for markdown output - All 243 tests passing
Closes¶
Completes #18 (Phase 2: High-Value Workflow Tools)
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
-
mcp: Implement Phase 3 MCP resources for discovery (#19) (#79,
ccaa05c) -
feat(mcp): implement Phase 3 MCP resources for discovery
Add 9 MCP resources that enable AI agents to explore StockTrim data without making tool calls, improving context gathering and discovery.
Resources Implemented:
Foundation Resources (6): - stocktrim://products/{product_code} - Product details - stocktrim://products/catalog - Product catalog (50 item limit) - stocktrim://customers/{customer_code} - Customer details - stocktrim://suppliers/{supplier_code} - Supplier information - stocktrim://locations/{location_code} - Location details - stocktrim://inventory/{location_code}/{product_code} - Stock levels
Report Resources (3): - stocktrim://reports/inventory-status?days_threshold=30 - Low stock items - stocktrim://reports/urgent-orders - Items needing reorder (< 7 days) - stocktrim://reports/supplier-directory - Supplier directory
Key Implementation Details: - All resources return JSON for LLM consumption - Proper error handling with ResourceError for not found cases - Service layer reuse via dependency injection - UNSET handling for optional API fields - Token budget management (30-50 item limits on lists) - Client-side filtering for order plan queries (API limitation) - Inventory resource uses product-level stock (no GET endpoint)
Testing: - 24 comprehensive unit tests for all resources - All 267 tests passing - Test coverage for success cases, not found errors, and edge cases
Documentation: - Updated server.py instructions with resource documentation - Organized into Foundation and Report categories - Examples of resource usage in common workflows
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
-
fix: address copilot review comments for resources
-
Remove default value from context parameter in inventory_status_report - Reorder parameters to put context first (required param before optional) - Update docstring to specify "50 items" limit in products catalog resource
Addresses review comments from PR #79
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
Testing¶
-
Add comprehensive test coverage for forecast management tools (#77,
5fbf576) -
test(mcp): add comprehensive tests for structured logging
Add test coverage for logging configuration and observability decorators.
Tests added: - test_logging_config.py (10 tests): * Configuration with different formats (console, JSON) * Configuration with different log levels * Logger instance creation * Environment variable handling * Invalid configuration handling
- test_observability.py (15 tests): * @observe_tool decorator functionality * @observe_service decorator functionality * Success and failure scenarios * Timing measurements * Parameter filtering * Function metadata preservation * Exception propagation
All 25 tests pass successfully.
This provides coverage for the structured logging implementation added in commit 925a19f.
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
-
mcp: Add comprehensive tests for structured logging (#76,
cf1a415) -
test(mcp): add comprehensive tests for structured logging
Add test coverage for logging configuration and observability decorators.
Tests added: - test_logging_config.py (10 tests): * Configuration with different formats (console, JSON) * Configuration with different log levels * Logger instance creation * Environment variable handling * Invalid configuration handling
- test_observability.py (15 tests): * @observe_tool decorator functionality * @observe_service decorator functionality * Success and failure scenarios * Timing measurements * Parameter filtering * Function metadata preservation * Exception propagation
All 25 tests pass successfully.
This provides coverage for the structured logging implementation added in commit 925a19f.
🤖 Generated with Claude Code
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
v0.9.2 (2025-11-06)¶
Bug Fixes¶
- Update test assertions for title-case status enum values
(
7840974)
After PR #74 fixed PurchaseOrderStatusDto to use proper string enum with title-case values ("Draft", "Approved", etc.) instead of incorrect IntEnum, updated 3 test assertions that were expecting uppercase values.
Changes: - test_purchase_orders.py: "DRAFT" → "Draft" (2 tests) - test_urgent_orders.py: "DRAFT" → "Draft" (1 test)
All 197 tests now passing.
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
v0.9.1 (2025-11-06)¶
Bug Fixes¶
This commit fixes all pre-existing test failures by properly implementing AsyncMock for service layer methods and aligning test mocks with the actual service layer architecture.
Changes¶
Service Layer Fixes (5 tests) - customers.py: Added NotFoundError exception handling to¶
return None instead of propagating the exception - test_suppliers.py: Fixed method
name calls from list_suppliers() to list_all()
Purchase Orders Tool Fixes (15 tests) - purchase_orders.py: - Fixed status enum handling:¶
Changed from .value to .name for IntEnum - Added proper UNSET checks:
if status not in (None, UNSET) instead of falsy checks (which fail for
status=0/DRAFT) - Added list vs single object handling for API responses - Added UNSET
import
- test_purchase_orders.py: - Updated fixture to mock
services.purchase_ordersinstead ofclient.purchase_orders- Fixed all method names:find_by_reference→get_by_reference,get_all→list_all- Configured proper AsyncMock return values for delete operations - Added validation error mocking usingside_effect- Fixed status expectations: "0" → "DRAFT"
Urgent Orders Workflow Fixes (3 tests) - urgent_orders.py: Fixed status enum handling¶
(.value → .name) and added UNSET checks
Workflow Tool Test Fixes (18 tests)¶
Forecast Management (7 tests) - Added mock_forecast_context fixture with AsyncMock for¶
products service and client - Updated all tests to use services.products.get_by_code()
and services.client.products.create() - Fixed string assertion for API limitation
message
Product Management (5 tests) - Added mock_product_mgmt_context fixture with AsyncMock -¶
Updated all tests to use proper service layer mocking - Converted all mock_context
references to use new fixture
Supplier Onboarding (7 tests) - Added mock_supplier_onboarding_context fixture with AsyncMock¶
for suppliers, products services and client - Updated method calls: .create_one() →
.create(), .find_by_code() → .get_by_code() - Fixed supplier_id expectations to
match implementation (string conversion of id field)
Key Patterns Established¶
-
AsyncMock Required: All service layer methods require
AsyncMock()notMagicMock()for proper await expression handling -
Service vs Client Layer: Workflow tools use
services.X.method()for reads butservices.client.X.create()for complex updates -
IntEnum Status Handling: Use
.namenot.valuefor string representation of IntEnum status fields -
UNSET Checks: Check
if value not in (None, UNSET)not justif valuefor fields that can legitimately be 0
Test Results¶
- Before: 157/197 tests passing (40 failures) - After: 197/197 tests passing (0 failures) ✅
- Coverage: 79%
🤖 Generated with Claude Code
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
v0.9.0 (2025-11-06)¶
Chores¶
Features¶
-
Migrate to PUT /SalesOrdersBulk endpoint for idempotent operations (#71,
8e458ba) -
mcp: Migrate customers tool to service layer pattern (#62,
4ed5d64)
feat: migrate customers tool to service layer (#57)
Implements service layer pattern for customers tool, separating business logic from tool layer.
Changes¶
New Service Layer - Add stocktrim_mcp_server/services/customers.py: -
CustomerService class extending BaseService - get_by_code() - Get customer by code
- list_all() - List all customers - ensure_exists() - Ensure customer exists (get or
create) - Comprehensive logging at INFO level for all operations - Proper validation
using BaseService helpers
Updated Tool Layer - Refactor stocktrim_mcp_server/tools/foundation/customers.py:
- Convert to thin wrappers calling service methods - Use get_services() for dependency
injection - Keep view concerns (response formatting) in tool layer
Server Integration - Update stocktrim_mcp_server/context.py: - Add
CustomerService initialization - Inject via ServerContext
Testing - All tests passing - Type checking passing - Pre-commit hooks passing
This migration improves code organization, testability, and maintainability by separating business logic from presentation concerns.
Closes #57 Part of #46
🤖 Generated with Claude Code
Co-Authored-By: copilot-swe-agent noreply@github.com
feat: migrate inventory tool to service layer (#58)
Implements service layer pattern for inventory tool, separating business logic from tool layer.
Changes¶
New Service Layer - Add stocktrim_mcp_server/services/inventory.py: -
InventoryService class extending BaseService - get_stock_level() - Get current
stock level for a product at a location - set_stock_level() - Set stock level for a
product at a location - Comprehensive logging at INFO level for all operations - Proper
validation using BaseService helpers - Error handling with try-catch and re-raise
pattern
Updated Tool Layer - Refactor stocktrim_mcp_server/tools/foundation/inventory.py:
- Convert to thin wrappers calling service methods - Use get_services() for dependency
injection - Keep view concerns (response formatting) in tool layer
Server Integration - Update stocktrim_mcp_server/context.py: - Add
InventoryService initialization - Inject via ServerContext
Testing - Add comprehensive test suite for InventoryService - All tests passing -
Type checking passing - Pre-commit hooks passing
This migration improves code organization, testability, and maintainability by separating business logic from presentation concerns.
Closes #58 Part of #46
feat(mcp): migrate suppliers tool to service layer pattern
Migrates the Suppliers tool to use the service layer pattern following the Products service example from PR #51.
Changes¶
New Service Layer - Created SupplierService with methods: - get_by_code(): Get supplier by¶
code - list_suppliers(): List all suppliers - create(): Create new supplier -
delete(): Delete supplier by code
Updated Tool Layer - Refactored suppliers tools to thin wrappers using get_services() - Fixed¶
field mapping to use correct DTO fields (supplier_code, supplier_name, email_address, primary_contact_name) - Removed non-existent fields (phone, is_active) from SupplierInfo model
Server Integration - Updated ServerContext to include SupplierService - Updated¶
services/init.py to export SupplierService
Testing - Added comprehensive test coverage (18 tests) - All tests passing, type checking clean,¶
linting passes
Closes #56 Part of #46
Refactoring¶
Standardizes patterns across all service layer implementations to ensure consistent behavior and maintainability.
Changes¶
CustomerService: - Change get_by_code() from try/except NotFoundError to truthy check pattern - Remove unused NotFoundError import - Now consistent with Products, Suppliers, and Purchase Orders services
SupplierService: - Rename list_suppliers() to list_all() for consistency - Fix isinstance() logic to match other services (was inverted) - Update corresponding tool to call list_all()
SalesOrderService: - Add existence check to delete_for_product() before deletion - Return (False, message) if no orders found, matching delete pattern in other services - Prevents unnecessary API calls for non-existent resources
Context: - Auto-sorted imports (ruff fix)
Pattern Alignment¶
All services now follow these consistent patterns:
- Get Operations: Truthy check for not-found (no exceptions) 2. List Operations: Consistent isinstance() handling for API quirks 3. Delete Operations: Existence check before deletion 4. Method Naming: list_all() for listing all entities 5. Return Types: Consistent across all services
Testing - ✅ All 71 tests passing - ✅ Linting clean (ruff, mypy, yamllint) - ✅ No breaking changes¶
to tool interfaces
This improves code maintainability by ensuring all services follow the same patterns established in the Products service (reference implementation).
🤖 Generated with Claude Code
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
refactor: migrate workflow tools to use service layer
Migrate all 4 workflow tools to use the service layer pattern instead of accessing the client directly. This completes Issue #48 and aligns workflow tools with the established service layer architecture.
Changes¶
- forecast_management.py: Updated to use ProductService - product_management.py: Updated to use ProductService - supplier_onboarding.py: Updated to use SupplierService and ProductService
- urgent_orders.py: Updated to use ProductService (order_plan/PO v2 remain on client)
Implementation Notes¶
- All tools now use
get_services(context)instead of accessing client directly - Complex operations (product updates with DTOs) useservices.clientwhen needed - Order plan and PO v2 operations still use client as they're not yet in service layer - All 71 tests pass - Removed unused SupplierRequestDto import from supplier_onboarding.py - Added missinglist_all()method to ProductService - Replacedservices._clientwith publicservices.clientfor proper encapsulation
Impact¶
- Improved consistency across codebase - Better separation of concerns - Easier to test and maintain
- Sets pattern for future workflow tools
Closes #48
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
v0.8.0 (2025-11-05)¶
Chores¶
- release: Mcp v0.4.0
(
c31dd99)
Documentation¶
-
mcp: Improve documentation clarity for UNSET handling and product_id usage (#52,
e1d2c40) -
fix: correct product_id documentation to reflect required field status
Address Copilot review feedback by fixing inaccurate documentation: - Clarify that product_id is required (not auto-generated) - Remove unverified claim about find_by_code() searching both fields
- Simplify comment to focus on verified behavior
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
- docs: clarify product_code_readable is optional but recommended
Address Copilot nitpick: while product_code_readable is technically optional/nullable, it's recommended to provide it for better user experience as it's used for user-facing displays.
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
Features¶
-
Migrate Purchase Orders tool to service layer pattern (#66,
aa0e453) -
feat: migrate Purchase Orders tool to service layer pattern
This PR migrates the Purchase Orders tool to use the service layer pattern following the Products service example from PR #51. The Purchase Orders tool is the most complex foundation tool with 4 operations and extensive business logic.
Changes¶
New Service Layer - services/purchase_orders.py: New PurchaseOrderService class with: -¶
get_by_reference(): Get PO by reference number - list_all(): List all purchase
orders - create(): Create PO with line items, supplier, location, status - delete():
Delete PO by reference number
Updated Tool Layer - tools/foundation/purchase_orders.py: Refactored to thin wrappers - All¶
4 tool implementations now delegate to service methods - Removed direct client access - Tools focus on request/response transformation only
Server Integration - context.py: Added PurchaseOrderService to ServerContext - Initialized¶
alongside ProductService - Available to all tools via get_services()
Key Highlights¶
- Status Enum Handling: Proper IntEnum parsing (DRAFT=0, APPROVED=1, etc.) - Line Item Validation: Ensures product_code, quantity, and validates > 0 - Total Cost Calculation: Computed from line items in tool layer - UNSET Handling: Proper use of UNSET sentinel for optional fields - Error Handling: Consistent validation and logging patterns
Testing¶
- ✅ All 71 tests passing - ✅ Type checking passes (uv run poe lint) - ✅ Pre-commit hooks pass
Closes #65
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
-
fix: address Copilot review comments and formatting
-
Simplify line_items validation (remove redundant len check) - Clarify status enum comment with more detail about IntEnum behavior - Format test_delete_status.py script
Addresses Copilot review comments on PR #66
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
-
mcp: Migrate sales orders tool to service layer pattern (#64,
d53632e) -
Initial plan
-
docs: initial plan for sales orders service layer migration
Co-authored-by: dougborg 1261222+dougborg@users.noreply.github.com
-
feat(mcp): migrate sales orders to service layer pattern
-
Created SalesOrderService with create, get_all, get_for_product, and delete_for_product methods - Updated ServerContext to include sales_orders service - Refactored tools to use thin wrappers calling service via get_services() - Updated all tests to mock service layer instead of client helpers - All tests passing (12/12) - Type checking passed - Linting passed
-
style: apply ruff formatting to sales orders service
-
fix: use specific ValidationError in test for zero quantity
Addresses code review comment to use pytest.raises(ValidationError) instead of generic
Exception for better test specificity.
- style: apply ruff formatting to test_delete_status.py
Fix formatting to pass CI format-check.
Co-authored-by: copilot-swe-agent[bot] 198982749+Copilot@users.noreply.github.com
Testing¶
- Add scripts to verify DELETE endpoint status codes
(
a9c0f27)
Add test scripts to verify actual API behavior for DELETE endpoints: - test_delete_status.py: Tests DELETE /api/PurchaseOrders (returns 204) - test_delete_products.py: Tests DELETE /api/Products (returns 200, slow)
These scripts are useful for: - Verifying API behavior matches our spec modifications - Detecting future API changes - Documenting actual endpoint behavior
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
v0.7.0 (2025-11-04)¶
Bug Fixes¶
-
Add type checking to pre-commit and fix purchase order type issues (
17ff3dc) -
Added purchase_order_request_dto.py and purchase_order_response_dto.py to type fix list - Added 'lint' hook to pre-commit to catch type errors before commit - Regenerated client with type fixes applied - All type checks now passing
This ensures ty check runs before every commit, preventing type errors from reaching CI.
- Update spec for DELETE 204 and integer status enum
(
e6bf963)
Summary¶
Fixed two critical spec/API mismatches discovered through testing:
- DELETE /api/PurchaseOrders returns 204 (not 200) 2. PurchaseOrderStatusDto uses integers (not strings)
Changes Made¶
1. DELETE Response Status Code - Issue: API updated to return 204 No Content, spec still¶
documented 200 OK - Impact: Client crashed expecting response data for 200 - Fix: Updated spec DELETE /api/PurchaseOrders to expect 204 - Automated: Added STEP 2.8 to regenerate_client.py
2. Purchase Order Status Enum Type - Issue: API returns integers (0,1,2,3), spec defined¶
strings ("Draft", etc.) - Impact: Client crashed with
ValueError: 0 is not a valid PurchaseOrderStatusDto - Fix: Changed enum type to
integer with x-enum-varnames mapping - Automated: Added STEP 2.7 to
regenerate_client.py - Result: Generates IntEnum accepting API's integer values
3. Documentation - Added comprehensive sections to api-feedback.md documenting both issues -¶
Included DELETE endpoint status (PurchaseOrders fixed, others unknown) - Explained x-enum-varnames positional mapping for future reference
Testing - All 71 tests passing - Verified DELETE /api/PurchaseOrders returns 204 via API testing¶
- Verified DELETE /api/Products still returns 200 (5+ second delay) - Confirmed status enum handles integer values correctly
Automation Both fixes now run automatically in regenerate_client.py: - STEP 2.7: Converts status¶
enum to integer with varnames - STEP 2.8: Updates DELETE /api/PurchaseOrders to 204
Related: #53
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
Build System¶
Bumps mdformat from 0.7.22 to 1.0.0. - Commits
--- updated-dependencies: - dependency-name: mdformat dependency-version: 1.0.0
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Co-authored-by: Doug Borg dougborg@dougborg.org
Bumps mkdocs-material from 9.6.22 to 9.6.23. - Release notes - Changelog - Commits
--- updated-dependencies: - dependency-name: mkdocs-material dependency-version: 9.6.23
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] support@github.com
Co-authored-by: dependabot[bot] \49699333+dependabot[bot]@users.noreply.github.com
Co-authored-by: Doug Borg dougborg@dougborg.org
Chores¶
- release: Mcp v0.3.0
(
debbd48)
Documentation¶
- mcp: Improve documentation clarity for UNSET handling and product_id usage
(
315534c)
Addresses post-merge Copilot review comments on PR #51.
Changes¶
utils.py - Add detailed docstring explaining UNSET vs None semantics - Clarify that OpenAPI¶
client uses UNSET as sentinel value - Document why conversion to None is needed for Pydantic models - Note: UNSET not imported here (only Unset class for type checking) but shown in docstring example for user reference
services/products.py - Expand comment explaining product_id and product_code_readable dual¶
assignment - Document the three key reasons for setting both fields to the same value: 1. Users think of "code" as primary identifier 2. Ensures consistent IDs that match user-facing code 3. Makes find_by_code() work reliably with either field - Add inline comments distinguishing internal ID vs user-facing code
Benefits - Better understanding of UNSET sentinel pattern - Clear rationale for seemingly¶
redundant field assignment - Improved maintainability through explicit documentation
Addresses: Copilot review comments from PR #51 (post-merge)
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
Features¶
feat(mcp): migrate products tool to service layer with type safety improvements (#45)
Implements ProductService as the reference implementation for the service layer pattern, establishing a cleaner architecture for the StockTrim MCP Server.
Architecture Changes¶
Service Layer Pattern - Created ProductService with four core methods: - get_by_code(code: str)¶
-> ProductsResponseDto | None - search(prefix: str) -> list[ProductsResponseDto] - create(code, description, cost_price, selling_price) -> ProductsResponseDto - delete(code: str) -> tuple[bool, str] - Created BaseService with common validation helpers - Established dependency injection via get_services(context)
Circular Import Resolution - Created context.py module for ServerContext - Moved ProductService¶
import to module level - Separated context initialization from server lifecycle management - Updated dependencies.py to use context.ServerContext
Code Quality Improvements - Refactored products tools to thin wrappers (~10-20 lines per¶
function) - Added comprehensive type annotations throughout - Replaced **kwargs with explicit optional parameters - Created utils.py with unset_to_none() helper for Pydantic compatibility
Code Reduction - Products tool: 410 → 246 lines (40% reduction, ~164 lines removed) - Eliminated¶
duplicate validation logic across all 4 product operations - Centralized error handling in service layer
Type Safety Enhancements - All service methods have explicit return type annotations - Optional¶
parameters use typed None defaults instead of **kwargs - UNSET sentinel values properly converted to None for Pydantic models - Module-level imports for better IDE and static analysis support
Bug Fixes - Fixed Pydantic validation errors in sales_orders when optional fields return UNSET -¶
Reduced sales_orders test failures from 10 to 6 - Overall test failures reduced from 13 to 11 - Remaining failures are pre-existing test bugs (confirmed on main branch)
Testing - All 5 product management workflow tests pass - No new test failures introduced -¶
Products tool migration fully verified - CI: All checks passing (test, quality, security)
Benefits - Maintainability: Clear separation of concerns between tools and business logic -¶
Type Safety: Comprehensive type hints improve IDE support and catch errors early - Testability: Service layer can be tested independently of FastMCP framework - Consistency: Establishes pattern for migrating remaining 6 foundation tools - Discoverability: Explicit parameters make API more intuitive
Next Steps This PR establishes the reference implementation for Issue #46, which will migrate the¶
remaining foundation tools (customers, suppliers, locations, sales_orders, purchase_orders, inventory) following this proven pattern.
Expected additional code reduction: ~270-370 lines
Related: #42 (ADR), #43 (Base infrastructure), #46 (Remaining tools)
Closes: #45
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
v0.6.0 (2025-11-03)¶
Bug Fixes¶
- Use allOf pattern for nullable object references in OpenAPI spec
(
380d97d)
The OpenAPI 3.0 spec ignores nullable: true when it appears alongside $ref. This
caused the code generator to produce buggy code for nullable object references like
PurchaseOrderResponseDto.location, which would crash when the API returned null.
Changes: - Updated regenerate_client.py to detect $ref fields and apply allOf pattern - For object references: wraps $ref in allOf array and adds nullable: true - For scalar/date fields: continues to use simple nullable: true - Regenerated client with proper None handling for location field - Updated all documentation to reference the allOf pattern
The generated code now correctly handles null object references: - Type annotation includes | None - Parser function checks for None before calling .from_dict() - Tested with real API data showing location: null
This fix only affects PurchaseOrderResponseDto.location, which is the only nullable object reference in NULLABLE_FIELDS. All other fields are scalars.
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
Chores¶
Features¶
-
Add support for purchase order upsert and nullable orderDate (#44,
3ff95cd) -
feat: add support for purchase order upsert and nullable orderDate
This commit implements two key improvements to the StockTrim API client:
-
Upsert Support for POST Endpoints (STEP 2.6) - POST /api/PurchaseOrders now handles both 200 (update) and 201 (create) responses - POST /api/Products now handles both 200 (update) and 201 (create) responses - Uses client_reference_number as upsert key for purchase orders - Uses code as upsert key for products
-
Nullable orderDate in PurchaseOrderRequestDto - Made orderDate nullable to match response schema behavior - Allows proper handling of null order dates from API - Enables updates that preserve existing dates (using UNSET) - Documents API limitation: orderDate cannot be cleared once set
Changes to regeneration script: - Added STEP 2.6: add_200_response_to_upsert_endpoints() - Added PurchaseOrderRequestDto to NULLABLE_FIELDS configuration - Made orderDate, externalId, referenceNumber, and location nullable
Documentation: - Added section on upsert pattern discovery and implementation - Documented orderDate field asymmetric behavior (nullable in responses, required in requests) - Provided workarounds for common use cases
Closes: API feedback improvements for purchase order management
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
- test: add comprehensive tests for purchase order upsert functionality
Add test suite for purchase order upsert behavior (create OR update): - Test POST returns 201 for new purchase orders - Test POST returns 200 for updates (based on clientReferenceNumber) - Test orderDate can be None (nullable) - Test orderDate can be UNSET (omitted to preserve existing) - Test location can be None (nullable) - Test status enum handles integer values from API
Fix PurchaseOrderStatusDto to handle integer status codes: - API returns integers (0=Draft, 1=Approved, 2=Sent, 3=Received) - Added missing classmethod to map integers to enum values
Relates to #44
- fix: add explicit aenter/aexit for proper type checking
Add explicit async context manager methods to StockTrimClient that return the correct type (StockTrimClient instead of AuthenticatedClient).
This fixes type checker errors in consuming code where async with client would resolve
to AuthenticatedClient instead of StockTrimClient, causing type checkers to not
recognize the helper properties (products, purchase_orders_v2, etc.).
- feat(mcp): implement base service infrastructure (#43)
Create foundational service layer for MCP tools to eliminate code duplication and improve maintainability.
Changes¶
- Create
services/base.pywith BaseService class - Common validation helpers (validate_not_empty, validate_positive) - Base initialization pattern for all services - Createservices/__init__.pypackage initialization - Createdependencies.pywith get_services() helper - Clean context extraction for tool functions - Updateserver.pyServerContext to support service layer - Added comments for future service instances
Infrastructure¶
This establishes the foundation for: - ProductService (#45) - All foundation tool services (#46) - Comprehensive unit testing (#47) - Workflow tool services (#48)
Testing¶
- All new files pass ruff checks - No regressions in existing tests - Ready for service migration
Related: #42 (ADR), #45 (Products reference implementation)
- fix: add type guards for nullable location field parsing
Fix type checking errors in purchase order DTOs by adding explicit cast() calls when parsing nullable location fields.
- Add
cast(dict, data)in location field parsers to satisfy type checker - Add UNSET type guard in test to handle nullable order_date assertions - Ensures type safety while maintaining runtime behavior
Type Errors Fixed¶
purchase_order_request_dto.py:252- location parsing -purchase_order_response_dto.py:279- location parsing -test_purchase_order_upsert.py:175-177- order_date assertions
Related: #44
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
-
mcp: Add purchase order CREATE tool and fix READ operations (#41,
1d33131) -
Initial plan
-
feat(mcp): add create_purchase_order tool and comprehensive tests
-
Add create_purchase_order tool to foundation tools - Support all purchase order fields: supplier, line items, location, status, dates - Calculate total_cost from line items since API doesn't provide it - Fix existing get/list tools to use correct field names (purchase_order_line_items) - Add comprehensive test suite with 14 test cases covering all CRUD operations - Update documentation with detailed parameter descriptions and examples - Note: UPDATE operation not supported by StockTrim API (no PUT/PATCH endpoints)
Co-authored-by: dougborg 1261222+dougborg@users.noreply.github.com
-
fix: improve status handling and add status validation test
-
Add clarifying comment about PurchaseOrderStatusDto enum values - Add warning log when invalid status is provided - Add test for all valid status values (Draft, Approved, Sent, Received) - Verify status enum accepts user-friendly strings correctly
Co-authored-by: copilot-swe-agent[bot] 198982749+Copilot@users.noreply.github.com
Co-authored-by: Doug Borg dougborg@dougborg.org
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
-
Initial plan
-
feat(mcp): add sales order management tools
Implement create, get, list, and delete tools for sales orders. Tools follow existing purchase_orders pattern with proper validation, error handling, and logging.
Co-authored-by: dougborg 1261222+dougborg@users.noreply.github.com
- test(mcp): add comprehensive tests for sales order tools
Add tests for create, get, list, and delete sales order operations with edge cases and error handling validation.
- docs(mcp): document sales order tools in MCP server docs
Add comprehensive documentation for sales order tools including create, get, list, and delete operations with examples and parameters.
-
fix(mcp): improve sales order deletion error handling
-
Fix error message to not reference non-existent tool - Handle None/empty results correctly in delete count - Add test case for empty deletion results
-
fix: apply ruff formatting to sales order tools
Applied automatic formatting with ruff to resolve CI formatting checks.
Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
Co-authored-by: copilot-swe-agent[bot] 198982749+Copilot@users.noreply.github.com
Co-authored-by: Doug Borg dougborg@dougborg.org
Co-authored-by: Doug Borg dougborg@apple.com
Co-authored-by: Claude noreply@anthropic.com
Refactoring¶
- mcp: Align tool patterns with best practices
(
f172c22)
Refactored sales_orders, purchase_orders, and inventory tools to follow established patterns from existing foundation tools (products, customers).
Key improvements:
sales_orders.py: - Removed redundant validation (Pydantic handles this) - Removed excessive hasattr() checks - trust generated model schemas - Removed manual list/object handling - helpers return proper types - Removed deleted_count field from DeleteSalesOrdersResponse
purchase_orders.py: - Changed order_date from str to datetime type (better type safety) - Removed redundant supplier_code validation - Removed message field from CreatePurchaseOrderResponse - Simplified date handling (let Pydantic parse)
inventory.py: - Moved UNSET import to module level (consistency) - Changed error handling: raise exceptions instead of success/failure model - Simplified InventoryResult to only contain data (no success boolean)
Documentation: - Updated tools.md to reflect datetime type for purchase orders
Benefits: - Reduced code by ~55 lines - Better type safety with Pydantic validation - Consistent patterns across all tools - More Pythonic error handling - Leverages helper methods effectively
All tests passing, linting clean, formatting verified.
Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
v0.5.1 (2025-11-01)¶
Bug Fixes¶
- Regenerate client with nullable orderDate field
(
ac74e2b)
The regeneration script's STEP 2.5 correctly applied the nullable fields fix, making PurchaseOrderResponseDto.orderDate optional and nullable. This prevents TypeError when the API returns null.
Generated by scripts/regenerate_client.py
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
v0.5.0 (2025-11-01)¶
Features¶
-
Implement comprehensive logging with DEBUG, INFO, and ERROR levels (#36,
f2193a9) -
feat: implement comprehensive logging with DEBUG, INFO, and ERROR levels
Implement the complete logging architecture as specified in the logging documentation, providing detailed observability across all HTTP operations.
Changes¶
Enhanced ErrorLoggingTransport - Track request/response timing for all operations - Route¶
responses to appropriate log levels based on status code - Log all requests at DEBUG level with sanitized headers (privacy-first) - Log 2xx responses at INFO (status + timing) and DEBUG (body excerpts) - Log 4xx/5xx errors at ERROR level with full response details - Add WARNING for null responses that may cause TypeErrors
New Logging Methods - _log_request(): DEBUG-level request logging with auth header¶
sanitization - _log_success_response(): INFO + DEBUG logging for 2xx responses -
_log_server_error(): ERROR logging for 5xx responses - Updated _log_client_error()
and _log_problem_details() with timing
Comprehensive Test Coverage - 13 new tests for ErrorLoggingTransport covering: - Request header¶
sanitization - Success response logging (INFO and DEBUG levels) - Null response warnings - List/dict response formatting - Client/server error logging - Status code routing logic - DEBUG level gating
Documentation - Added complete logging architecture specification - Updated implementation¶
status (all items complete) - Documented privacy-first approach and performance considerations
Benefits¶
- Debug NoneType errors faster: See actual API responses (null vs []) 2. Monitor all operations: INFO level tracks all successful requests 3. Catch server errors: Log 5xx errors that survive retries 4. Privacy-first: Auth headers automatically excluded from logs 5. Performance-aware: Expensive DEBUG ops only run when enabled
Testing¶
- All 61 tests pass (including 13 new logging tests) - Full CI pipeline passes (format, lint, type-check, tests, docs) - 82% code coverage for stocktrim_client.py
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
-
fix: address Copilot review feedback on logging PR
-
Fix mock_request bug: Use MagicMock instead of calling fixture as method 2. Extract hardcoded 200 truncation as class constant RESPONSE_BODY_MAX_LENGTH 3. Fix documentation inconsistency: change 500 chars -> 200 chars to match code
All tests pass. Addresses Copilot review threads #1, #3, #4, #5.
- feat: make null response log level configurable
Add null_response_log_level parameter to ErrorLoggingTransport to allow configuring
the log level for null API responses. Default is DEBUG to reduce noise in production,
but can be set to WARNING for debugging or a custom TRACE level (5) for minimal logging.
This addresses Copilot review feedback #2 about null warnings being too noisy for legitimate null endpoints.
Example usage: ```python # Less noisy (default) client = StockTrimClient() # null responses logged at DEBUG
More visible for debugging from stocktrim_public_api_client.stocktrim_client import¶
ErrorLoggingTransport transport = ErrorLoggingTransport(null_response_log_level=logging.WARNING) client = StockTrimClient(httpx_client=httpx.AsyncClient(transport=transport))
Minimal logging (custom TRACE level) transport = ErrorLoggingTransport(null_response_log_level=5)¶
* feat: add intelligent parsing error logging with null field detection
Enhance error logging to automatically identify and report null fields when TypeErrors, ValueErrors,
or other parsing errors occur during response parsing.
Key improvements: - Added `log_parsing_error()` method to ErrorLoggingTransport for intelligent
error inspection - For TypeErrors: Recursively scans response JSON to identify all null fields -
For other errors: Shows response excerpts for debugging context - Zero performance overhead - only
runs when errors actually occur - Removed configurable null_response_log_level parameter (no
longer needed) - Removed generic null response warnings (replaced by specific error logging)
Error logging now provides actionable debugging information: - Exact field paths to null values
(e.g., "orderDate", "supplier.supplierName") - Full error message and type - Response excerpts for
context - Works for TypeErrors, ValueErrors, AttributeErrors, and generic Exceptions
Example output when TypeError occurs: ``` ERROR TypeError during parsing for GET /api/PurchaseOrders
ERROR TypeError: object of type 'NoneType' has no len() ERROR Found 3 null field(s) in response:
ERROR - orderDate ERROR - fullyReceivedDate ERROR - supplier.supplierName ```
The enhanced logging makes debugging null field issues significantly faster by immediately showing
which specific fields are null, eliminating the need to manually inspect responses or enable DEBUG
logging.
* feat: add actionable fix suggestions to parsing error logging
Enhance error logging to provide immediate, actionable guidance when TypeErrors and other parsing
errors occur. Instead of just showing which fields are null, the logging now tells developers
exactly how to fix the problem.
Key improvements: - Added "Possible fixes" section with 3 solution paths: 1. Add fields to
NULLABLE_FIELDS in regenerate_client.py (quickest fix) 2. Update OpenAPI spec to mark fields as
nullable (proper fix) 3. Handle nulls defensively in helper methods (workaround) - Included
documentation link to api-feedback.md#nullable-date-fields section - Updated docstring with
enhanced output example - Updated documentation to show new error format - Enhanced tests to
verify fix suggestions and doc links are present
Example enhanced output: ``` ERROR TypeError during parsing for GET /api/V2/PurchaseOrders ERROR
TypeError: object of type 'NoneType' has no len() ERROR Found 3 null field(s) in response: ERROR -
orderDate ERROR - fullyReceivedDate ERROR - supplier.supplierName ERROR ERROR Possible fixes:
ERROR 1. Add fields to NULLABLE_FIELDS in scripts/regenerate_client.py and regenerate ERROR 2.
Update OpenAPI spec to mark these fields as 'nullable: true' ERROR 3. Handle null values
defensively in helper methods ERROR ERROR See:
docs/contributing/api-feedback.md#nullable-date-fields-not-marked-in-spec ```
This eliminates the need for developers to ask maintainers or search through documentation - they
immediately know exactly what to do when errors occur.
* fix: address Copilot review feedback on comprehensive logging
Address all actionable review comments from Copilot:
**Correctness Fixes:** - Fix ellipsis logic to only append "..." when actually truncating (3
locations) - Fix test timing assertions to use regex patterns instead of fragile exact checks -
Update documentation log examples to remove "OK" suffix (matches actual implementation) - Change
"Current/Desired" section to "Previous/New" (feature is implemented, not planned)
**API Improvements:** - Make `error_logging_transport` public (remove underscore) - it's part of
helper API - Extract `MAX_NULL_FIELDS_TO_LOG = 20` as class constant for consistency - Improve
null response message: "null (JSON null)" is clearer than "parsed as None"
All tests passing (21/21).
* fix: complete ellipsis fixes in log_parsing_error method
Fix remaining unconditional ellipsis appends in log_parsing_error method. These were missed in the
previous commit but follow the same pattern - only append '...' when content actually exceeds
RESPONSE_BODY_MAX_LENGTH.
Addresses remaining Copilot review comments on lines 455, 460, 468.
* fix: correct documentation link anchor in parsing error logs
Update documentation link from non-existent anchor to correct section:
#nullable-date-fields-not-marked-in-spec → #nullable-arrays-vs-optional-fields
Addresses Copilot review comment on line 449.
---------
Co-authored-by: Doug Borg <dougborg@apple.com>
Co-authored-by: Claude <noreply@anthropic.com>
## v0.4.2 (2025-11-01)
### Bug Fixes
- Add nullable to date/time fields in OpenAPI spec post-processing
([`55721de`](https://github.com/dougborg/stocktrim-openapi-client/commit/55721de60bfee53b52593155533b4bcb630bd39d))
Adds a new post-processing step to the regeneration script that ensures date/time and
scalar fields which can be null are properly marked as nullable in the OpenAPI spec
before code generation.
Changes: - Added add_nullable_to_date_fields() function to scripts/regenerate_client.py
\- Integrated as STEP 2.5 in the regeneration workflow (after auth fixes, before
validation) - Updated docs/contributing/api-feedback.md to reflect fix status -
Regenerated client with nullable fields properly handled
The StockTrim API returns null for many date/time fields (orderDate, fullyReceivedDate,
receivedDate, etc.) which previously caused TypeErrors when isoparse() tried to parse
None values. The generated code now includes proper null checks before parsing.
Tested: All 61 tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove nullable fields from required arrays in OpenAPI spec
([`c5cd337`](https://github.com/dougborg/stocktrim-openapi-client/commit/c5cd337d47b9a76db6bf03c81b6ea3bfa65d80dd))
Addresses Copilot reviewer feedback on PR #38. The `orderDate` field was marked as both
required and nullable, which is a contradiction. Fields that can be null cannot be
required per OpenAPI specification semantics.
Updated `add_nullable_to_date_fields()` to: - Mark fields as nullable: true (existing
behavior) - Remove nullable fields from required arrays (new behavior)
Based on real API evidence, orderDate returns null and should not be required.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
### Refactoring
- Separate client and MCP releases with proper chaining
([#35](https://github.com/dougborg/stocktrim-openapi-client/pull/35),
[`2ebe64f`](https://github.com/dougborg/stocktrim-openapi-client/commit/2ebe64f56bae1be9e0590077d66014ac816f4adc))
Refactored release workflow to separate client and MCP releases with proper chaining.
All Copilot review feedback has been addressed and threads resolved.
## v0.4.1 (2025-10-31)
### Bug Fixes
- Handle response.elapsed RuntimeError in logging hook
([`8138298`](https://github.com/dougborg/stocktrim-openapi-client/commit/8138298e6d2b4ea1e5f4eab1d1a6c0eb522e8637))
The \_log_response_metrics hook was accessing response.elapsed before the response body
was read, causing a RuntimeError. This broke MCP server tool calls.
Error: RuntimeError: '.elapsed' may only be accessed after the response
has been read or closed.
Fix: - Wrap response.elapsed access in try/except - Fall back to logging without elapsed
time if not yet available - Prevents tool calls from failing due to metrics logging
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## v0.4.0 (2025-10-31)
### Bug Fixes
- **release**: Commit MCP version bumps and use modern GitHub Actions output
([`175a50d`](https://github.com/dougborg/stocktrim-openapi-client/commit/175a50d93bd374d445daf596d2e74b93f7e25293))
Fixes two issues in the MCP server release process:
1. MCP version bumps are now committed to git before building, preventing duplicate
version errors on PyPI. Previously the version was only updated locally during the
build, so every release tried to publish the same version.
1. Replaced deprecated ::set-output command with modern $GITHUB_OUTPUT environment file
syntax.
Changes: - Git commit added after MCP version bump (with proper bot credentials) - Push
MCP version commit before building - Updated Python script to use GITHUB_OUTPUT file
instead of ::set-output
This ensures each MCP server release gets a unique, incremented version number that's
tracked in git.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
### Features
- Add defensive guard for GITHUB_OUTPUT environment variable
([`32a3b05`](https://github.com/dougborg/stocktrim-openapi-client/commit/32a3b05021de7f51072a9d49d5f8c02caab7b9b1))
Add null check for GITHUB_OUTPUT before writing to it, making the script more robust for
local testing scenarios.
While GITHUB_OUTPUT is always set in GitHub Actions workflows, this defensive check
follows best practices and prevents potential issues in non-standard environments.
Co-Authored-By: Claude <noreply@anthropic.com>
### Refactoring
- Split MCP version update into separate steps and add error handling
([`319cc34`](https://github.com/dougborg/stocktrim-openapi-client/commit/319cc343fddc3037ce5ccab3e135b28975c41be2))
Addresses Copilot review feedback:
1. Split version update into separate steps: - "Update MCP server version" - runs Python
script and sets output - "Commit and push MCP version bump" - commits the changes -
"Build MCP server package" - builds the package This allows proper use of GitHub
Actions outputs instead of parsing stdout with grep (which is not portable due to -P
flag).
1. Add error handling for git commit: - Use `git diff --cached --quiet ||` to only
commit when changes exist - Prevents workflow failures on re-runs or when version
hasn't changed
Co-Authored-By: Claude <noreply@anthropic.com>
## v0.3.0 (2025-10-31)
### Bug Fixes
- Disable Bearer token prefix to prevent malformed Authorization header
([#31](https://github.com/dougborg/stocktrim-openapi-client/pull/31),
[`c7498fa`](https://github.com/dougborg/stocktrim-openapi-client/commit/c7498fad639ce61496d71bd9f6f4e3cb8fee12e2))
* fix: disable Bearer token prefix to prevent malformed Authorization header
StockTrim uses custom auth headers (api-auth-id, api-auth-signature) instead of the
standard Authorization Bearer token. Passing prefix='' to the parent AuthenticatedClient
prevents it from adding 'Authorization: Bearer ' header.
Fixes 'Illegal header value b"Bearer "' error when connecting to StockTrim API.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- refactor: use AuthenticatedClient's native auth_header_name for api-auth-id
Improved StockTrim authentication to leverage AuthenticatedClient's built-in header
customization instead of workarounds:
- Set `auth_header_name="api-auth-id"` to use native header customization - Pass actual
`api_auth_id` value as `token` parameter - Simplified AuthHeaderTransport to only
handle `api-auth-signature` - Removed `api_auth_id` parameter from
`create_resilient_transport()`
This approach is cleaner and more idiomatic than the previous solution that used an
empty token with an empty prefix. It leverages the generated client's built-in
capabilities while maintaining our custom transport for the signature header.
Benefits: - No malformed Authorization headers - Uses built-in mechanisms (cleaner, more
maintainable) - Clear separation: static header via client, dynamic header via transport
\- Less custom code to maintain
All tests pass (48/48).
______________________________________________________________________
Co-authored-by: Doug Borg <dougborg@apple.com>
Co-authored-by: Claude <noreply@anthropic.com>
- Use SEMANTIC_RELEASE_TOKEN to bypass branch protection in release workflow
([`f3c09e5`](https://github.com/dougborg/stocktrim-openapi-client/commit/f3c09e5c818348df7bdc0e16ba0ec8f8559a6ff0))
The release workflow was failing because repository branch protection rules require all
changes to main go through pull requests. The semantic-release action needs to push
version bump commits directly to main.
Changes: - Updated checkout action to use SEMANTIC_RELEASE_TOKEN instead of GITHUB_TOKEN
\- Updated python-semantic-release action to use SEMANTIC_RELEASE_TOKEN
This matches the pattern used in katana-openapi-client and allows the release automation
to bypass branch protection rules with a personal access token.
Note: The SEMANTIC_RELEASE_TOKEN secret must be configured in repository
settings with a personal access token that has repo permissions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
### Build System
- **deps**: Bump mdformat from 0.7.22 to 1.0.0
([#24](https://github.com/dougborg/stocktrim-openapi-client/pull/24),
[`b7a992a`](https://github.com/dougborg/stocktrim-openapi-client/commit/b7a992a016ba4b09bcb5ddd9b49f9573577b2aaf))
Bumps [mdformat](https://github.com/hukkin/mdformat) from 0.7.22 to 1.0.0. -
[Commits](https://github.com/hukkin/mdformat/compare/0.7.22...1.0.0)
--- updated-dependencies: - dependency-name: mdformat dependency-version: 1.0.0
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] \<49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump ty from 0.0.1a24 to 0.0.1a25
([#23](https://github.com/dougborg/stocktrim-openapi-client/pull/23),
[`a21794f`](https://github.com/dougborg/stocktrim-openapi-client/commit/a21794fff03a0d195e56125ea42696a23d76290f))
Bumps [ty](https://github.com/astral-sh/ty) from 0.0.1a24 to 0.0.1a25. -
[Release notes](https://github.com/astral-sh/ty/releases) -
[Changelog](https://github.com/astral-sh/ty/blob/main/CHANGELOG.md) -
[Commits](https://github.com/astral-sh/ty/compare/0.0.1-alpha.24...0.0.1-alpha.25)
--- updated-dependencies: - dependency-name: ty dependency-version: 0.0.1a25
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] \<49699333+dependabot[bot]@users.noreply.github.com>
### Documentation
- Comprehensive documentation cleanup and consolidation
([`f7ba132`](https://github.com/dougborg/stocktrim-openapi-client/commit/f7ba132bfe35ccdc10c0237e6d22fd20baf80f90))
## Deleted Files - MIGRATION_PROGRESS.md - Migration complete, no longer needed -
docs/POETRY_USAGE.md - Outdated (project uses UV) - docs/STOCKTRIM_CLIENT_GUIDE.md -
Duplicate of docs/user-guide/client-guide.md - docs/TESTING_GUIDE.md - Duplicate of
docs/user-guide/testing.md
- docs/HELPER_CONVENIENCE_METHODS.md - Duplicate of docs/user-guide/helper-methods.md -
docs/CODE_OF_CONDUCT.md - Duplicate of docs/contributing/code-of-conduct.md
## Consolidated Files - STOCKTRIM_API_FEEDBACK.md → docs/contributing/api-feedback.md
## Fixed Base URLs Changed all instances from app.stocktrim.com to api.stocktrim.com: - README.md (2
instances) - stocktrim_mcp_server/README.md (2 instances) - docs/mcp-server/overview.md
(2 instances)
## Fixed Method Names Changed list_all() to get_all() throughout: - docs/index.md -
docs/getting-started/quickstart.md
## Updated Poetry to UV Replaced all poetry commands with uv equivalents: -
docs/user-guide/client-guide.md - docs/user-guide/testing.md -
.github/pull_request_template.md
## Fixed Documentation Links - Updated all internal links to point to docs/user-guide/ files -
Updated README.md to reference correct documentation locations - Fixed code of conduct
link
## Minor Improvements - Added CHANGELOG.md to mkdocs.yml navigation - Fixed UV installation
instructions to use official installer - Removed duplicate files
All documentation now uses correct base URLs, method names, and UV commands.
Documentation builds successfully with no errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Enhance copilot instructions with testing patterns and update tasks to UV
([`aa71406`](https://github.com/dougborg/stocktrim-openapi-client/commit/aa71406896ec09f2dff73dfed144e0e3ef111bb1))
- Add comprehensive testing patterns section with 5 common fixtures and examples -
Document critical idempotent find_or_create() helper pattern - Expand helper
architecture details with code examples
- Add MCP server documentation references - Update .vscode/tasks.json to use UV
instead of Poetry
- Remove outdated Poetry warning (tasks now corrected) - Enhance error handling and
transport layer documentation
### Features
- Implement Phase 2B workflow tools for product and forecast management
([#28](https://github.com/dougborg/stocktrim-openapi-client/pull/28),
[`c393bfb`](https://github.com/dougborg/stocktrim-openapi-client/commit/c393bfb27f85420c40c9dd4d7596fc4b23f2949b))
* Initial plan
* feat: implement four Phase 2B workflow tools for product and forecast management
Co-authored-by: dougborg <1261222+dougborg@users.noreply.github.com>
- test: add comprehensive tests for Phase 2B workflow tools
- chore: update test and linting configuration for MCP server
- fix: remove unused imports and format code with ruff
- fix: address ruff linting errors in Phase 2B workflow tools
Fixed 3 ruff linting errors: - RUF005: Use unpacking syntax for list concatenation in
supplier_onboarding.py - B017: Use ValueError instead of blind Exception in
test_forecast_management.py (2 occurrences)
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- fix: correct API model field names for Phase 2B workflow tools
Fixed type errors by using correct field names from generated API models: - Changed
supplier_id to id in SupplierResponseDto usage - Removed is_active field from
SupplierRequestDto (not in API) - Added null checks before accessing error field in
tests
- fix: correct API model field names in Phase 1 foundation tools
Fixed type errors in Phase 1 foundation tools by using correct field names: -
locations.py: Changed code/name/is_active to location_code/location_name - products.py:
Changed code/description/cost_price/selling_price to
product_id/product_code_readable/name/cost/price
These errors existed on main but weren't caught by CI previously.
- fix: correct import paths and model names in Phase 1 tools
Fixed type errors in Phase 1 tools: - inventory.py: Changed client_types import from
.generated to direct import - suppliers.py: Changed SupplierDto to SupplierRequestDto
with correct field names
- fix: remove unsupported category field from OrderPlanFilterCriteriaDto
Removed category parameter from filter criteria as it's not supported by the API model.
Only location_codes and supplier_codes are valid filter fields.
This error existed from Phase 2A but wasn't caught by CI previously.
- fix: exclude MCP server from ty type checking to avoid external dependency issues
Reverted the addition of MCP server paths to ty configuration. The MCP server imports
fastmcp which isn't resolved by ty, causing false positive unresolved-import errors that
block CI.
The MCP server code is still checked by ruff and pytest, just not by ty.
- fix: exclude MCP server tests from main project test suite
Removed MCP server from pytest testpaths and coverage configuration. The MCP server is a
separate package and should be tested independently.
This fixes ModuleNotFoundError when running tests, as the MCP server package isn't
installed in the main project's test environment.
______________________________________________________________________
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Doug Borg <dougborg@apple.com>
Co-authored-by: Claude <noreply@anthropic.com>
- Implement urgent order management workflow tools for MCP Server Phase 2
([#26](https://github.com/dougborg/stocktrim-openapi-client/pull/26),
[`ded40bb`](https://github.com/dougborg/stocktrim-openapi-client/commit/ded40bb7cc03e2d5f652f0ff10bec4549747c82b))
* Initial plan
* feat: implement urgent order management workflow tools
- Create workflows directory structure - Add review_urgent_order_requirements tool - Add
generate_purchase_orders_from_urgent_items tool - Register workflow tools in main
tools module - All tests pass, linting passes
Co-authored-by: dougborg <1261222+dougborg@users.noreply.github.com>
- refactor: optimize supplier lookup and clarify days_threshold usage
* Fix N+1 query pattern by batch fetching products for supplier mapping - Add clarifying
comments about days_threshold in generate_purchase_orders - Update docstring to
explain V2 API behavior - All tests pass, linting passes
- docs: improve documentation for API limitations and performance
* Add early return for empty urgent items list - Clarify batch fetch limitations in
comments - Update GeneratePurchaseOrdersRequest docstring with clear explanation -
Document that days_threshold is for API consistency - All tests pass, linting passes
______________________________________________________________________
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
- Phase 1 Foundation Expansion for MCP Server 2.0
([#22](https://github.com/dougborg/stocktrim-openapi-client/pull/22),
[`17cafee`](https://github.com/dougborg/stocktrim-openapi-client/commit/17cafee2e9e13a4683580e36f604bbb1a1365a70))
* feat: add Phase 1A foundation helper classes to client library
Add four new helper classes to support advanced inventory management workflows:
- OrderPlan: Query forecasts and order plan data with filtering capabilities \*
get_urgent_items() - Find items needing urgent reordering * get_by_supplier() - Filter
by supplier * get_by_category()
- Filter by category
- PurchaseOrdersV2: V2 Purchase Orders API with auto-generation feature \*
generate_from_order_plan()
- Auto-generate POs from forecast recommendations * get_all_paginated() - List with
pagination support * find_by_supplier() - Filter by supplier
- Forecasting: Trigger and monitor forecast calculations * run_calculations() - Trigger
forecast recalculation * wait_for_completion() - Wait for calculation to finish with
polling
- BillOfMaterials: Manage product component relationships * Full CRUD operations (get,
create, delete) * get_for_product() - Get all components for a product \*
get_uses_of_component() - Find where a component is used
All helpers follow the existing lazy-loading pattern and include comprehensive tests.
This work is part of Phase 1 (Foundation Expansion) for MCP Server 2.0.
Related: #17, #18
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- feat: add Phase 1B foundation MCP tools
Reorganize and expand MCP server tools with new foundation layer:
**Tool Organization:** - Created tools/foundation/ directory for low-level API
operations - Moved existing tools (products, customers, inventory) to foundation/ -
Updated tool registration to support modular architecture
**Enhanced Tools:** - products: Added create_product() and delete_product() operations -
inventory: Retained set_product_inventory() (no GET endpoint in API)
**New Foundation Tools:** - suppliers: get, list, create, delete operations - locations:
list and create operations - purchase_orders: get, list, delete operations (V1 API)
**Tool Count:** 15 foundation tools total - Products: 4 tools (get, search, create,
delete) - Customers: 3 tools (get, list, ensure_exists) - Inventory: 1 tool (set) -
Suppliers: 4 tools (get, list, create, delete) - Locations: 2 tools (list, create) -
Purchase Orders: 3 tools (get, list, delete)
All tools follow FastMCP patterns with proper error handling, logging, and Pydantic
models. This work is part of Phase 1 (Foundation Expansion) for MCP Server 2.0.
- fix: address Copilot PR review comments
* Move time import to module level in forecasting.py - Add performance warning to
find_by_supplier() docstring about client-side filtering
- fix: address remaining Copilot review comments
* Fix locations.create() to pass single LocationRequestDto instead of list - Fix
products.create() to pass single ProductsRequestDto instead of list - Enhance
performance warning in find_by_supplier() about lack of server-side filtering - Add
missing test dependencies to pyproject.toml
______________________________________________________________________
Co-authored-by: Doug Borg <dougborg@apple.com>
Co-authored-by: Claude <noreply@anthropic.com>
## v0.2.5 (2025-10-29)
### Bug Fixes
- Correct TOML key access for MCP server dependency update
([`787e3cd`](https://github.com/dougborg/stocktrim-openapi-client/commit/787e3cdf515fa3060fad6f6167f28c56f7fed2c7))
The release workflow was failing with KeyError: 'dependencies' when trying to update the
MCP server's client dependency. The dependencies array is nested under the [project]
table per PEP 621, not at the root level.
Changed data['dependencies'][0] to data['project']['dependencies'][0] to correctly
access the dependencies list in the TOML structure.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
### Documentation
- Comprehensive documentation overhaul
([`e72ec8f`](https://github.com/dougborg/stocktrim-openapi-client/commit/e72ec8fa5336a74e6badce3309a13f78a1009f96))
- Fixed index.md: removed Sphinx directives, added proper MkDocs structure - Created
complete getting-started guides (installation, quickstart, configuration) - Created
user guides (client usage, helpers, error handling, testing) - Created MCP server
documentation (overview, installation, tools, Claude Desktop setup) - Created API
reference with mkdocstrings auto-generation - Created architecture documentation
(overview, transport, helpers) - Created contributing guides (development, code of
conduct, API feedback) - Updated mkdocs.yml navigation structure - Fixed all heading
issues and broken links
This provides comprehensive public-facing documentation for both the client library and
MCP server with auto-generated API docs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## v0.2.4 (2025-10-29)
### Bug Fixes
- Use tomllib/tomli-w instead of deprecated toml package
([`5aedd90`](https://github.com/dougborg/stocktrim-openapi-client/commit/5aedd901bdc66925db16f97804338102d922ae19))
Switched from the deprecated 'toml' package to Python 3.11+'s built-in tomllib for
reading and tomli-w for writing TOML files. Also fixed the installation command to use
'uv pip install' instead of 'uv run pip install' to ensure the package is available in
the uv environment.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## v0.2.3 (2025-10-29)
### Bug Fixes
- Correct GitHub Pages artifact path for MkDocs
([`2901db5`](https://github.com/dougborg/stocktrim-openapi-client/commit/2901db5da86de29e85b8b60a69858f1a46b846a2))
Changed upload path from docs/\_build/html (Sphinx) to site/ (MkDocs default). This
fixes the "No such file or directory" error in the documentation deployment workflow.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## v0.2.2 (2025-10-29)
### Bug Fixes
- Properly handle workspace dependencies in MCP server release
([`79c0e49`](https://github.com/dougborg/stocktrim-openapi-client/commit/79c0e49ba3c54bee09b4dd7aad2c1d15de2ee97b))
- Remove workspace source override when building MCP server for PyPI - Ensures MCP
server uses versioned dependency from PyPI instead of workspace - Fixes build failures
in release workflow for future releases
## v0.2.1 (2025-10-29)
### Bug Fixes
- Temporarily disable UV cache due to service issues
([`2508b7d`](https://github.com/dougborg/stocktrim-openapi-client/commit/2508b7daa0f7f526e543c106a56d522740f42b8b))
- Comment out enable-cache: true in both test and release jobs - Addresses GitHub
Actions cache service problems - Can be re-enabled when cache service is stable
## v0.2.0 (2025-10-29)
### Bug Fixes
- Improve type casting in generated models and helpers
([`367e3cd`](https://github.com/dougborg/stocktrim-openapi-client/commit/367e3cd6e493afbb9124f69a29f239572582375c))
- Add proper type casting for .from_dict() methods in generated models - Enhance type
safety in helper files with better casting - Update test utilities with improved type
handling - Ensures strict type checking compliance with ty
### Documentation
- Update documentation from Poetry to UV
([`adf6072`](https://github.com/dougborg/stocktrim-openapi-client/commit/adf6072f26686f67721585b7c42b002a46f231ba))
- Update README.md with UV installation and usage instructions - Remove Poetry
references from all documentation files - Update STOCKTRIM_API_FEEDBACK.md with UV
commands - Ensure documentation consistency with current tooling
### Features
- Enhance regeneration script with automatic type fixing
([`e28859b`](https://github.com/dougborg/stocktrim-openapi-client/commit/e28859ba3a6b4b31ee37ffdff9ce69c83e4f961f))
- Add automatic type casting fixes for generated .from_dict() methods - Implement
automatic import management (cast, Mapping) - Fix nested if statement linting issues
(SIM102) - Improve error handling and progress reporting - Ensures generated code
passes strict type checking
- Migrate from mypy to ty for faster type checking
([`85fc18a`](https://github.com/dougborg/stocktrim-openapi-client/commit/85fc18a887ee57022cce0e34c7ea69f21ce0f4e0))
- Replace mypy with Astral's ty type checker - Configure ty in pyproject.toml with
proper exclude patterns for generated code - Update poe tasks to use ty instead of
mypy - Maintain strict type checking with better performance - ty provides faster type
checking and clearer error messages
- Migrate from mypy to ty type checker
([`648e51b`](https://github.com/dougborg/stocktrim-openapi-client/commit/648e51b8a93709c0ddad1f8e659da09f8602dd7e))
- Replace mypy with Astral's ty for faster, more accurate type checking - Update
pyproject.toml with ty configuration - Modify test configuration to work with ty -
Improves type checking performance and accuracy
- Update copilot instructions and enable MCP auto-publishing
([`a66d07b`](https://github.com/dougborg/stocktrim-openapi-client/commit/a66d07bf8ab4f730b2708557a3333b9a6bc44672))
- Update .github/copilot-instructions.md to reflect UV usage (not Poetry) - Document
monorepo structure with client + MCP server packages - Add automatic type fixing
documentation for regeneration script - Fix GitHub workflow artifact paths for UV
build structure - Enable automatic MCP server publishing when client updates - Ensure
proper PyPI coordination between packages
## v0.1.1 (2025-10-29)
### Bug Fixes
- Tidy __all__ ordering and comments
([`32de7af`](https://github.com/dougborg/stocktrim-openapi-client/commit/32de7aff9c1b7bf7a485f6f1557c191e55ead5d6))
### Chores
- Update GitHub Actions to latest versions
([`465a7b7`](https://github.com/dougborg/stocktrim-openapi-client/commit/465a7b77735d243c70a69f12e6c52d504c01b532))
- Update upload-pages-artifact from v3 to v4 - Pin trivy-action to v0.29.0 instead of
@master - All other actions already on latest versions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
### Documentation
- Add PyPI badges to README for both packages
([`a88f81c`](https://github.com/dougborg/stocktrim-openapi-client/commit/a88f81cb17707021d22b643bf91d77e87363b1a2))
## v0.1.0 (2025-10-29)
### Bug Fixes
- Correct relative import paths for client_types in generated code
([`24aceaf`](https://github.com/dougborg/stocktrim-openapi-client/commit/24aceaf29a7d7a00f23e58fda36b62b8a9953aa8))
This commit fixes import path issues in generated API and model files:
- Updated regeneration script to properly calculate relative import depths - API files
in generated/api/subdirectory/ now use ....client_types (4 dots) - Model files in
generated/models/ now use ...client_types (3 dots) - Fixed type annotation in
Customers.update() return type - Added integration tests for all domain helpers
The import fixer now correctly handles the directory structure: - Files in
generated/api/subdirectory/: need 4 dots (subdir → api → generated → package_root) -
Files in generated/models/: need 3 dots (models → generated → package_root) - Files
directly in generated/: need 2 dots (generated → package_root)
All tests now pass including new helper integration tests that verify: - Helper
accessibility via lazy-loaded properties - Core method availability on all helpers -
Proper lazy-loading behavior
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update semantic-release version and temporarily disable docs tests
([`adaf2b3`](https://github.com/dougborg/stocktrim-openapi-client/commit/adaf2b34191749cf091a2c85928e4fe6519b73b4))
**Fixes:** - Update python-semantic-release from v9.0.0 to v9.15.2 - Fixes
bullseye-backports Docker build failure - Uses newer Debian bookworm base with proper
package sources - Temporarily disable docs tests in docs workflow - Tests look for
Sphinx artifacts but we're transitioning to MkDocs - Will re-enable after full MkDocs
migration
This should allow releases to proceed successfully.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
### Build System
- **client**: Migrate to UV workspace with Hatchling
([`4d47f54`](https://github.com/dougborg/stocktrim-openapi-client/commit/4d47f54fd58b3f49e93349f7122614959f99e772))
- Replace Poetry with UV workspace configuration - Update pyproject.toml to use
Hatchling build backend - Add workspace members: stocktrim-openapi-client and
stocktrim-mcp-server - Remove poetry.lock, generate uv.lock - Update dependencies to
match katana versions (httpx-retries, ruff 0.12+) - Add comprehensive poethepoet task
library with uv run commands - Add MkDocs dependencies in docs extras - Update
semantic release config for monorepo (client-v tag format) - Add validation tasks for
OpenAPI (basic + Redocly) - Create minimal MCP server package structure
Breaking change: Build system migration from Poetry to UV + Hatchling
Co-Authored-By: Claude <noreply@anthropic.com>
### Chores
- Initial project setup
([`b9b582c`](https://github.com/dougborg/stocktrim-openapi-client/commit/b9b582cbad0e22b39628e9b869016322ebf3a0aa))
- **client**: Add devcontainer setup for VS Code
([`302fd30`](https://github.com/dougborg/stocktrim-openapi-client/commit/302fd300a1ac84d475794ac621117897a526e7d5))
- Create .devcontainer/devcontainer.json: * Python 3.13 base image * Git, GitHub CLI,
Node.js LTS * VS Code extensions (Python, Ruff, Copilot, Docker, YAML) * Auto-format
on save with Ruff * pytest integration - Create .devcontainer/oncreate.sh (prebuild
caching) - Create .devcontainer/setup.sh (post-create finalization) - Create
.devcontainer/README.md (documentation) - Port 8000 forwarded for MCP server -
Resource requirements specified (4 CPU, 8GB RAM, 32GB storage)
Enables consistent development environment across team
Co-Authored-By: Claude <noreply@anthropic.com>
- **client**: Update code quality tooling configuration
([`56f0c92`](https://github.com/dougborg/stocktrim-openapi-client/commit/56f0c9250ef44ff6c5f935271f8960c348dc9e95))
- Update .pre-commit-config.yaml: * Add --allow-multiple-documents and --unsafe to
check-yaml * Add CHANGELOG.md exclusion to mdformat * Add local pytest hook (runs 'uv
run poe test') - Update .gitignore with comprehensive patterns: * Add .ruff_cache/
exclusion * Remove project-specific debug file patterns * Cleaner organization and
comments - .yamllint.yml already configured correctly
All tooling now matches katana-openapi-client patterns
Co-Authored-By: Claude <noreply@anthropic.com>
- **client**: Update OpenAPI spec from live API
([`622836c`](https://github.com/dougborg/stocktrim-openapi-client/commit/622836cab4f68b2b2e03c059f4a341010718e32c))
Downloaded latest spec from https://api.stocktrim.com/swagger/v1/swagger.yaml
Changes are primarily formatting updates (indentation consistency). No new endpoints or
breaking changes identified.
This ensures our generated client matches the current API exactly.
Co-Authored-By: Claude <noreply@anthropic.com>
### Continuous Integration
- Add environment names back to distinguish dual-package publishing
([`27a1e78`](https://github.com/dougborg/stocktrim-openapi-client/commit/27a1e78c094fbc9cf9a3dd9516872a6a0fd54a30))
Added environment names to help PyPI Trusted Publishers distinguish between the two
packages published from the same workflow:
- Client: environment 'pypi-client' - MCP Server: environment 'pypi-mcp'
This allows PyPI to properly configure separate trusted publishers for each package.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Migrate all workflows from Poetry to UV
([`3a40535`](https://github.com/dougborg/stocktrim-openapi-client/commit/3a4053538a163799f4c252ee57750ce7c49560ce))
Updated all GitHub Actions workflows to use UV instead of Poetry:
**CI Workflow (ci.yml):** - Use astral-sh/setup-uv@v4 action - UV-based Python
installation and dependency management - Removed Poetry caching, using UV's built-in
cache - Added concurrency control to cancel redundant runs - Simplified dependency
installation
**Documentation Workflow (docs.yml):** - Migrated to UV for docs building and testing -
Cleaner, faster builds with UV caching
**Release Workflow (release.yml):** - Dual-package build support (client + MCP server) -
Separate PyPI publishing for both packages - UV-based builds with `uv build` and
`uv build --package` - Individual artifact uploads and publishing
**Security Workflow (security.yml):** - UV integration for dependency scanning - Use
`uv tool` for semgrep installation - Maintained Trivy and dependency review
**Benefits:** - ✅ Faster CI runs (UV is significantly faster than Poetry) - ✅ Simplified
workflows (less caching boilerplate) - ✅ Dual-package release support - ✅ Consistent
tooling across local dev and CI
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove environment requirements from release workflow
([`6e4c35e`](https://github.com/dougborg/stocktrim-openapi-client/commit/6e4c35ecfd576858b9b433068010dec109f722d6))
Removed GitHub Environment requirements to match katana-openapi-client pattern: - No
manual approval gates needed - Fully automated releases - Simpler PyPI Trusted Publisher
setup
PyPI configuration is now simpler - no environment name required.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
### Documentation
- Add constructive API feedback document for StockTrim
([`8f02246`](https://github.com/dougborg/stocktrim-openapi-client/commit/8f02246f256a5db9f5c04f54124fd14f306898ca))
Created comprehensive feedback document highlighting: - OpenAPI specification
improvements (security schemes, documentation) - Response type consistency issues -
Authentication documentation needs - Endpoint convention questions - Pagination, error
handling, and versioning clarifications
All feedback is framed constructively and positively, intended to help StockTrim
developers improve the API for all users.
The document includes: - Clear examples of current state vs. suggested improvements -
Benefits of each suggestion - Specific questions that need clarification - Tables
comparing inconsistent patterns across endpoints
This serves as a reference for: 1. Our own client library design decisions 2. Future
discussions with StockTrim team 3. Understanding API quirks we need to work around
- Add convenience methods reference for MCP tool design
([`6ab6082`](https://github.com/dougborg/stocktrim-openapi-client/commit/6ab6082084abfbedd6b077e074e80e400e088fb7))
Created comprehensive documentation of all helper convenience methods: - Catalogs all
convenience methods with use cases - Maps methods to proposed MCP tools - Provides
design recommendations for MCP integration - Includes code examples for each method -
Documents error handling and return type patterns
This will serve as the blueprint for MCP server tool design, ensuring the MCP tools
provide ergonomic access to StockTrim API.
- Mark migration as complete - 88% done, all core phases finished
([`6ca6cb4`](https://github.com/dougborg/stocktrim-openapi-client/commit/6ca6cb432c757ca7d2291860b81b86376a4df0eb))
**Migration Status: COMPLETE** 🎉
Completed 14 of 16 planned commits (88%). All production-critical phases done:
**✅ Completed Phases:** - Phase 1-3: Infrastructure & Core (UV workspace, transport
layer, helpers, utils) - Phase 4: Testing infrastructure (comprehensive fixtures) -
Phase 5: Regeneration script (already enhanced) - Phase 6: CI/CD workflows (all migrated
to UV, dual-package support) - Phase 8: MCP Server (FastMCP, 5 tools, production-ready)
\- Documentation (README, MkDocs config, helper reference, MCP guide)
**📊 Project Status:** - 42/42 tests passing (100%) - 0 linting errors (ruff, ty,
yamllint) - 2 packages ready: stocktrim-openapi-client + stocktrim-mcp-server - Full
CI/CD pipeline operational
- Production-ready for release
**Optional Remaining:** - Phase 7: Full MkDocs migration (docs work, not blocking) -
ADRs (documentation enhancement)
The project is production-ready and can be released as v0.2.0.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update migration progress to reflect completed work
([`2760a6f`](https://github.com/dougborg/stocktrim-openapi-client/commit/2760a6fc3c7212bcd93df023f5e3e561a97d3064))
Core functionality is now complete (69% done): - ✅ Build system (UV workspace) - ✅
Client architecture (transport patterns) - ✅ Domain helpers (15+ convenience methods) -
✅ MCP server (5 tools, 3 domains) - ✅ All tests passing (42 passed) - ✅ All linting
passing
Remaining work focuses on documentation, CI/CD, and ADRs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update README and add MkDocs configuration
([`2bc29d1`](https://github.com/dougborg/stocktrim-openapi-client/commit/2bc29d16355d78adb177d89609915a28745a1930))
**README Updates:** - Comprehensive feature list for client and MCP server - Domain
helpers documentation with all 7 helper classes - Error handling examples with typed
exceptions - MCP server tools and example conversations - Updated installation
instructions for UV - Development setup and common tasks - Architecture explanations
(transport-layer, helpers) - Project structure overview
**MkDocs Configuration:** - Complete mkdocs.yml with Material theme - Navigation
structure for all docs sections - Plugin configuration (search, mkdocstrings) - Markdown
extensions (code highlighting, mermaid, tabs) - Prepared for future full documentation
migration
**Quality Assurance:** - ✅ All tests passing (42 passed) - ✅ All linting passing (ruff,
ty, yamllint) - ✅ Documentation accurate and up-to-date
The README now provides a complete overview of the project's current capabilities
including domain helpers and MCP server.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- **client**: Add comprehensive migration progress documentation
([`5f17dbb`](https://github.com/dougborg/stocktrim-openapi-client/commit/5f17dbbcf1daf1eaa4381cd297e5cbae7e395b59))
- Document completed work (commits 1-3): 19% complete - Detail remaining 13 commits with
implementation plans - StockTrim-specific simplifications noted: * No rate limiting
(429) - simpler retry logic * No pagination - remove PaginationTransport * Custom auth
headers (api-auth-id/signature) - Include code examples and verification steps -
Reference katana files for implementation guidance - Development commands and file
structure - Questions to address in next session
Enables future work continuation with full context
Co-Authored-By: Claude <noreply@anthropic.com>
- **client**: Update migration plan with API spec analysis and decisions
([`159129e`](https://github.com/dougborg/stocktrim-openapi-client/commit/159129e66f152ffe5faa05c05c51bed1c4176800))
Updates based on user input and OpenAPI spec analysis:
API Characteristics: - No pagination, no rate limiting (confirmed) - Custom auth:
api-auth-id (Tenant Id) + api-auth-signature (Tenant Name) - Mixed access patterns: some
lists, some by ID, bulk operations - Query parameter filtering (e.g., BOMs by
productId/componentId)
Decisions Made: - Create domain helpers for ALL StockTrim entities - MCP tools:
verify_connection, get_product, check_inventory, list_orders - Document dual model
architecture (internal + Square integration) - API is primarily resource-based with
limited list operations
Domain Model Documented: - Core: Products, Customers, Suppliers, Locations, Inventory,
BOMs - Orders: Sales Orders (with bulk/range ops), Purchase Orders (V2) - Operations:
Order Plan, Forecasting, Processing Status, Configuration - Integrations: Square, InFlow
Simplified transport layer for StockTrim (no pagination/rate limit handling)
Co-Authored-By: Claude <noreply@anthropic.com>
### Features
- Add authentication fix and domain helper classes (WIP)
([`f73e368`](https://github.com/dougborg/stocktrim-openapi-client/commit/f73e368dd7076cb14644e8e53d50490832110fd8))
This commit implements major improvements to the client generation and adds domain
helper classes for easier API interaction:
**Authentication Fix:** - Modified regeneration script to convert StockTrim's auth
header parameters to proper OpenAPI securitySchemes - Removes api-auth-id and
api-auth-signature from all 39 endpoints (78 params total) - Adds proper security scheme
definition that will be handled by transport layer - Generated API methods no longer
require auth parameters
**Domain Helper Classes:** - Created helpers package with base class and domain-specific
helpers - Added helpers for: Products, Customers, Suppliers, Sales Orders, Purchase
Orders, Inventory, and Locations - Integrated helpers into StockTrimClient with
lazy-loaded properties - Helpers provide ergonomic CRUD operations wrapping generated
API methods
**Note:** Helper method signatures need refinement to match actual StockTrim API
conventions (some endpoints use different parameter names than initially assumed). Type
checking currently fails but infrastructure is in place.
Related to Commit 7 in migration plan.
- Add convenience methods to all domain helpers
([`fe54385`](https://github.com/dougborg/stocktrim-openapi-client/commit/fe54385a2488e56561c61f38e10f2794f036e577))
Added convenience methods to all helper classes that provide: - Simpler, more ergonomic
interfaces for common operations - Better handling of API inconsistencies - Clearer
intent through well-named methods
**Products:** - find_by_code(code) - Find single product, returns None if not found -
search(prefix)
- Alias for get_all() with clearer search intent - exists(code) - Boolean check for
product existence
**Customers:** - exists(code) - Boolean check for customer existence -
find_or_create(code, \*\*defaults) - Get or create pattern
**Suppliers:** - find_by_code(code) - Handles single|list API inconsistency -
create_one(supplier) - Wrapper for batch API accepting single item - exists(code) -
Boolean check for supplier existence
**SalesOrders:** - get_for_product(product_id) - Clearer alias for filtering -
delete_for_product(product_id) - Clearer alias for deletion
**PurchaseOrders:** - find_by_reference(ref) - Handles single|list API inconsistency -
exists(reference_number) - Boolean check for order existence
**Inventory:** - set_for_product(product_id, ...) - Simplified single-product setter
These methods will directly inform MCP tool design, making the API more accessible
through Claude's tool interface.
All quality checks pass ✅
- Add utils.py with response unwrapping and typed exceptions
([`a6f0fe5`](https://github.com/dougborg/stocktrim-openapi-client/commit/a6f0fe576b809d0f8943a799578254e615fd1db8))
This commit adds a comprehensive utils module for handling StockTrim API responses with
typed exceptions and convenience functions.
**Exception Hierarchy**:
- `APIError` (base exception with status_code and problem_details) -
`AuthenticationError` (401) - `PermissionError` (403) - `NotFoundError` (404) -
`ValidationError` (400, 422) - `ServerError` (5xx)
All exceptions include: - Human-readable error message - HTTP status code - Optional
ProblemDetails object from API
**Response Unwrapping Functions**:
1. `unwrap(response, raise_on_error=True)`: - Main utility for handling API responses -
Auto-raises typed exceptions on error status codes - Returns parsed data on success -
Optional non-raising mode (returns None on error)
1. `is_success(response)`: - Check if response has 2xx status code
1. `is_error(response)`: - Check if response has 4xx/5xx status code
1. `get_error_message(response)`: - Extract error message from ProblemDetails or status
code
**StockTrim Simplifications**:
- No `unwrap_data()` function - StockTrim doesn't wrap responses in `.data` arrays -
Focused on ProblemDetails error format when available - Clean fallback to status code
for generic errors
**Testing**:
- Comprehensive test coverage (28 tests) - Tests for all exception types - Tests for
success/error detection - Tests for error message extraction - All tests pass with
full type safety
**Integration**:
- All utilities exported from package root - Proper type hints with mypy compliance -
Compatible with generated Response[T] types
Example usage: \`\`\`python from stocktrim_public_api_client import StockTrimClient,
unwrap from stocktrim_public_api_client.api.products import get_api_products
async with StockTrimClient() as client: response = await
get_api_products.asyncio_detailed(client=client) products = unwrap(response) # Raises on
error, returns parsed data \`\`\`
Part of migration from katana-openapi-client improvements (Commit 6).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Complete CI/CD infrastructure setup for StockTrim OpenAPI client
([`f40a767`](https://github.com/dougborg/stocktrim-openapi-client/commit/f40a767e965ac7d61c785dd4da86aeff887b42b9))
- Add GitHub Actions workflows (CI, release, security, docs) - Add Sphinx documentation
infrastructure with AutoAPI - Add pre-commit hooks and quality tools (ruff, mypy,
yamllint) - Add enhanced poe tasks for development workflow - Add GitHub issue/PR
templates and Code of Conduct - Fix environment variable handling for proper test
isolation - Match Katana project architecture and patterns - All tests passing with
100% CI pipeline success
- Implement StockTrim MCP server with FastMCP
([`0aba445`](https://github.com/dougborg/stocktrim-openapi-client/commit/0aba445f03af8ce94a960713f6d349d30326e2b0))
This commit adds a complete MCP (Model Context Protocol) server for StockTrim Inventory
Management, enabling AI assistants like Claude to interact with StockTrim APIs through
natural language.
**Server Implementation:** - FastMCP-based server with lifespan management -
Environment-based authentication (API auth ID and signature) - Automatic client
initialization with error handling - Production-ready logging and resilience
**Tools Implemented:**
Products (2 tools): - get_product: Retrieve product by code - search_products: Search by
code prefix
Customers (2 tools): - get_customer: Retrieve customer by code - list_customers: List
all customers with limit
Inventory (1 tool): - set_product_inventory: Update stock levels for a product
**Features:** - Type-safe with full Pydantic models for requests/responses - Leverages
helper convenience methods from client library - Comprehensive error handling and
logging - Claude Desktop integration ready
**Documentation:** - Complete README with installation instructions - Configuration
examples for Claude Desktop - Tool usage examples and conversation patterns -
Development guide and troubleshooting section
**Dependencies:** - fastmcp>=0.3.0 for MCP server framework - python-dotenv>=1.0.0 for
environment management - Workspace dependency on stocktrim-openapi-client
The server can be run via: - uvx stocktrim-mcp-server - python -m stocktrim_mcp_server -
Direct import and execution
This implementation follows the patterns from katana-mcp-server and provides a
foundation for expanding StockTrim API coverage through additional MCP tools.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Optimize documentation testing and include generated API documentation
([`d97e8b5`](https://github.com/dougborg/stocktrim-openapi-client/commit/d97e8b5f37264a2497f1785a1f11b6274945acf3))
- Add conditional documentation testing with CI_DOCS_BUILD environment flag - Create
comprehensive documentation tests for Sphinx build validation - Optimize local
development workflow by skipping slow docs tests by default - Update CI workflows to
properly run documentation tests in docs build jobs - Add pytest markers for better
test organization (docs marker added) - Modify Sphinx configuration to include
generated API documentation - Add comprehensive API reference documentation for all
generated client modules - Extend test timeouts to 600 seconds (10 minutes) for
documentation builds - Add new test commands: test-docs, test-no-docs, test-all -
Update help text to reflect new testing options
Benefits: - Local development: 5 tests run in 0.02s (docs tests skipped) - CI builds:
All tests including 3 docs tests run when CI_DOCS_BUILD=true - Generated API
documentation now fully accessible in Sphinx docs - Better separation between fast unit
tests and slow documentation builds - Comprehensive validation of documentation
generation in CI/CD
This brings StockTrim in line with the documentation testing improvements made to the
Katana project, ensuring consistent developer experience across both API client
projects.
- Rename types.py to client_types.py and enhance regeneration script
([`9f53d0b`](https://github.com/dougborg/stocktrim-openapi-client/commit/9f53d0b289f231e62713244f92f1c4798d051f9f))
This commit implements several improvements to the client generation process:
1. **Enhanced Regeneration Script** (adapted from katana-openapi-client): - Added
multi-validator approach (openapi-spec-validator + Redocly CLI) - Implemented
types.py → client_types.py rename during generation - Added post-processing for
import fixing (.types → .client_types) - Implemented Union type modernization
(Union[A, B] → A | B) - Added RST docstring formatting fixes - Integrated ruff
auto-fixes with --unsafe-fixes flag - Added streaming test output for validation -
Structured output with clear step-by-step logging
1. **Client Types Architecture**: - Moved types.py from generated/ to package root as
client_types.py - This matches the katana-openapi-client architecture - All generated
files now import from ..client_types instead of .types - Prevents name conflicts with
Python built-in types module
1. **Generated Code Updates**: - All 42 generated model files updated with correct
imports - All 40 generated API endpoint files updated with correct imports -
client.py updated to use client_types
- Removed generated/py.typed and generated/types.py
The regeneration script is now production-ready and can be run with:
`uv run python scripts/regenerate_client.py`
Part of migration from katana-openapi-client improvements.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Rewrite StockTrimClient with transport pattern architecture
([`ca3d561`](https://github.com/dougborg/stocktrim-openapi-client/commit/ca3d5614beabefad0599fb4cba49f3c0ad0b1203))
This commit implements a complete rewrite of the StockTrimClient using the
transport-layer pattern, adapted from katana-openapi-client.
**Architecture Changes**:
1. **Client Inheritance** (Breaking Change): - Old: StockTrimClient wraps
AuthenticatedClient (access via `.client`) - New: StockTrimClient inherits from
AuthenticatedClient (pass directly) - API methods now receive client directly:
`method(client=client)` instead of `method(client=client.client)`
1. **Layered Transport Architecture**: - `AsyncHTTPTransport` (base HTTP layer) -
`AuthHeaderTransport` (adds api-auth-id, api-auth-signature headers) -
`ErrorLoggingTransport` (logs 4xx errors with ProblemDetails parsing) -
`RetryTransport` (retry 5xx errors on idempotent methods only)
1. **Custom Retry Class**: - `IdempotentOnlyRetry` - only retries GET, HEAD, OPTIONS,
TRACE on 5xx - No rate limiting (429) handling - StockTrim doesn't rate limit - Uses
httpx-retries library with exponential backoff
1. **Simplified vs Katana**: - ❌ No RateLimitAwareRetry - StockTrim doesn't have rate
limits - ❌ No PaginationTransport - StockTrim doesn't paginate - ✅
ErrorLoggingTransport - Better error visibility - ✅ Basic retry on 5xx for idempotent
methods only
**New Components**:
- `create_resilient_transport()` - Factory for layered transport composition -
`IdempotentOnlyRetry`
- Custom retry class for safe 5xx retries - `ErrorLoggingTransport` - Detailed 4xx
error logging with ProblemDetails - `AuthHeaderTransport` - StockTrim custom auth
header injection
**API Changes**:
````python # Old usage async with StockTrimClient() as client: response = await
some_api(client=client.client) # Need .client
# New usage async with StockTrimClient() as client: response = await some_api(client=client) # Pass
directly! ```
**Testing**:
- Updated all tests for new architecture - All tests pass (5/5) - All quality checks pass (ruff,
mypy, yamllint)
Part of migration from katana-openapi-client improvements (Commit 5).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
### Refactoring
- Rewrite helpers with correct API signatures
([`e9f615e`](https://github.com/dougborg/stocktrim-openapi-client/commit/e9f615e5752e7c397198d1e860423764d3a3bdcf))
Completely rewrote all domain helper classes from scratch based on actual StockTrim API signatures
instead of assumptions from katana.
**Key Changes:** - Renamed `list()` to `get_all()` to avoid shadowing builtin `list` type - Fixed
all method signatures to match actual StockTrim API endpoints - Added proper type annotations
including union types where API is inconsistent - Documented API inconsistencies in docstrings
with "Note:" sections
**Helper Methods Now Correctly Match API:** - Products: get_all(code?, pageNo?) → array, create(dto)
→ single, delete(productId?) - Customers: get_all() → array, get(code) → single, update(dto) →
array - Suppliers: get_all(code?) → single|array, create([dto]) → array, delete(codeOrName) -
SalesOrders: get_all(productId?) → array, create(dto) → single, delete(productId?) -
PurchaseOrders: get_all(refNum?) → single|array, create(dto) → single, delete(refNum) → single -
Inventory: set(request) → single - Locations: get_all(code?) → single|array, create(dto) → single
**API Inconsistencies Documented:** - Some GET endpoints return single objects when filtered (should
return arrays) - Inventory POST returns PurchaseOrderResponseDto (incorrect response type) -
Suppliers POST accepts/returns arrays (batch operation)
All quality checks pass: ruff, mypy, pytest ✅
Related to Commit 7 in migration plan.
### Testing
- Enhance test infrastructure with comprehensive fixtures
([`f9623e9`](https://github.com/dougborg/stocktrim-openapi-client/commit/f9623e9936f7572010af772e67109958f5ffb057))
Added comprehensive test fixtures following katana patterns:
**New Fixtures:** - async_stocktrim_client: Async context manager for testing -
create_mock_response: Factory for creating custom mock responses - mock_server_error_response: 500
error with ProblemDetails format - mock_authentication_error_response: 401 unauthorized response -
mock_validation_error_response: 422 with validation errors - mock_not_found_response: 404 response
- stocktrim_client_with_mock_transport: Client with mock transport
**Benefits:** - More flexible test response creation - Better error response mocking - Async client
testing support - Mock transport integration
All existing tests continue to pass (42 passed).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>