Skip to content

feat(dockhand): support build-time dependency constraints via build_with - #827

Closed
danbarr wants to merge 4 commits into
mainfrom
fix/adb-mysql-mcp-server-build-with
Closed

feat(dockhand): support build-time dependency constraints via build_with#827
danbarr wants to merge 4 commits into
mainfrom
fix/adb-mysql-mcp-server-build-with

Conversation

@danbarr

@danbarr danbarr commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a build_with field to spec.yaml, wired through dockhand's Dockerfile generation into ToolHive's existing templates.RuntimeConfig.BuildWith support (--build-with in the thv CLI). This lets a uvx:// server's spec pin PEP 508 constraints on transitive dependencies (e.g. mcp<2) that the upstream package itself leaves unbounded, without waiting on an upstream release.
  • Driving case: adb-mysql-mcp-server (the smoke test canary in build-containers.yml) has been failing to build with ModuleNotFoundError: No module named 'mcp.server.fastmcp'. Root cause is upstream: it depends on mcp[cli]>=1.8.0 with no upper bound, and mcp 2.0.0 (just released) removed the fastmcp module it imports at startup. This PR fixes it by pinning build_with: ["mcp<2"] in its spec.yaml.
  • build_with is uvx-only, matching ToolHive's own restriction: ToolHive rejects (rather than silently ignores) a non-empty BuildWith for npx:///go:// builds, and this rejection fires through dockhand too since it happens before dry-run Dockerfile generation. Verified with a throwaway npx spec.
  • The mcp-security-scan CI job scans the package independently of the built container image, by invoking uvx directly against the raw package. Pinning build_with alone didn't fix that job, since it never went through dockhand. Extended scripts/mcp-scan/generate_mcp_config.py to pass the same constraints via uvx --with, and fixed run_scan.py's argparse handling for --stdio-arg values that themselves start with - (same class of issue already worked around for npx's --yes).
  • Documented build_with in docs/adding-servers.md, including a Troubleshooting entry and a worked example based on the adb-mysql-mcp-server fix.

Note on overlap: #669 (branch feat/dep-overrides, refs #668) already implements a similar mechanism, a hand-rolled spec.constraints/spec.overrides field patched into the Dockerfile by content-matching. #741 carries a rebased copy of that same commit plus a follow-up that uses it for next-devtools-mcp. Both PRs are open and unmerged. This PR takes a different approach, wiring through ToolHive's own native --build-with flag instead of maintaining our own patching/escaping logic, which seems the more correct long-term direction — flagging the overlap so it's visible during review.

Test plan

  • go build ./cmd/dockhand/... succeeds
  • dockhand build -c uvx/adb-mysql-mcp-server/spec.yaml generates a Dockerfile containing uv tool install --with 'mcp<2' ...
  • docker build of the generated Dockerfile installs mcp==1.29.0 (not 2.0.0)
  • docker run --rm adb-test --help starts cleanly and activates tools/resources with no ModuleNotFoundError
  • Confirmed build_with on a non-uvx spec errors clearly rather than being silently ignored
  • generate_mcp_config.py for adb-mysql-mcp-server emits uvx --with mcp<2 adb-mysql-mcp-server@2.0.0
  • Ran run_scan.py against that config locally: scan completes (exit 0), all tools reported SAFE, no ModuleNotFoundError
  • Confirmed npx's --stdio-arg=--yes handling is unaffected by the run_scan.py change

🤖 Generated with Claude Code

danbarr added 2 commits August 5, 2026 09:32
Exposes ToolHive's --build-with mechanism (PEP 508 constraints on
uvx:// transitive dependencies, e.g. "mcp<2") through spec.yaml, so
Dockyard can pin dependencies that upstream packages leave unbounded
without waiting on an upstream release.
adb-mysql-mcp-server depends on mcp[cli]>=1.8.0 with no upper bound.
mcp 2.0.0 removed the mcp.server.fastmcp module this server imports
at startup, breaking the smoke test canary in build-containers.yml.
@toolhive-release-app

Copy link
Copy Markdown
Contributor

🛡️ Skill Security Scan Results

⚠️ No skills were scanned in this PR.

danbarr added 2 commits August 5, 2026 09:49
The security scan invokes uvx directly against the raw package,
bypassing the constrained container image entirely, so pinning
build_with in spec.yaml didn't help it. Pass the same PEP 508
constraints through to uvx's own --with flag so the scanner
exercises the same dependency set as the built image.

Also fixes --stdio-arg tokens that start with "-" (like "--with")
being misread as a new flag by argparse, the same problem npx's
--yes already worked around.
@toolhive-release-app

toolhive-release-app Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🔒 MCP Security Scan Results

✅ adb-mysql-mcp-server

  • Status: Passed
  • Tools scanned: 3
  • Result: No security issues detected

Summary: Scanned 1 MCP server(s), all passed security checks. ✅

@danbarr

danbarr commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #669, closing.

--build-with maps to uv tool install --with, which adds a requirement the resolver has to satisfy jointly. That works for the case here, since adb-mysql-mcp-server leaves mcp unbounded and there is no conflict to resolve around. It cannot force past a dependency the upstream package has explicitly pinned or capped, which is the case #668 exists for (for example mcp-clickhouse capping fastmcp <3.0.0 below the version that fixes a CVE). npm has no soft-constraint equivalent at all, only hard overrides.

#669 uses the real override mechanisms (uv tool install --overrides and npm overrides), which covers both that harder case and this one. The adb-mysql-mcp-server fix has moved there, along with the security scan fix from this PR.

@danbarr danbarr closed this Aug 5, 2026
@danbarr
danbarr deleted the fix/adb-mysql-mcp-server-build-with branch August 5, 2026 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant