Commit db5a1c9
Feat/openapi client cli with tests (#120)
* feat: add OpenAPI spec, mcp-v8-client crate, mcp-v8-cli binary, and release workflows
## Server — OpenAPI (utoipa)
- Add utoipa, utoipa-axum, utoipa-swagger-ui to server/Cargo.toml
- Annotate all api.rs structs with #[derive(ToSchema)]
- Annotate all handlers with #[utoipa::path(...)]
- Add ApiDoc struct with #[derive(OpenApi)] listing all paths + schemas
- Add --print-openapi CLI flag: prints spec to stdout and exits
- Add /swagger-ui and /api-doc/openapi.json routes on http/sse transports
- Commit initial openapi.json (regenerate with: server --print-openapi > openapi.json)
## mcp-v8-client crate
- New crate at mcp-v8-client/ with progenitor build.rs codegen
- build.rs reads openapi.json and generates typed Client at compile time
- src/lib.rs includes the generated code, re-exports Client + types
- src/main.rs: mcp-v8-cli binary with clap subcommands:
exec, executions list/get/output/cancel
- --url flag (env: MCP_V8_URL, default: http://localhost:3000)
- --json flag for raw JSON output
## Workspace
- Root Cargo.toml workspace including server + mcp-v8-client
- Nix build (server/ standalone) is unaffected
## GitHub Actions
- release.yml: extended with build-server -> generate-openapi -> build-cli pipeline
- CLI built for linux-x86_64, linux-arm64, macos-x86_64, macos-arm64
- CLI binaries gzipped and attached as release assets alongside server binary
- openapi.json regenerated from built server binary on every release
- mcp-v8-client published to crates.io on tag push (CARGO_REGISTRY_TOKEN secret)
- publish-client.yml: new workflow mirroring ip-allocator publish-rust-sdk.yml
## install.sh
- Now installs both mcp-v8 and mcp-v8-cli by default
- MCP_V8_INSTALL=cli or MCP_V8_INSTALL=server to install one
## README
- New sections: HTTP API & OpenAPI, mcp-v8-cli, mcp-v8-client crate
- --print-openapi documented under Command Line Arguments
- Updated Installation to mention mcp-v8-cli and crates.io
* fix: restore install.sh unchanged; add install-cli.sh for CLI-only installs
- Revert install.sh to its original state (server binary only, unchanged)
- Add install-cli.sh: mirrors install.sh style but targets mcp-v8-cli
release assets (mcp-v8-cli-linux, mcp-v8-cli-linux-arm64,
mcp-v8-cli-macos, mcp-v8-cli-macos-arm64)
- Update README Installation section to reference install-cli.sh
- Update mcp-v8-client/README.md to reference install-cli.sh
* test: add CLI e2e tests (bash + Rust) and GHA workflow
- .github/workflows/cli-e2e.yml: builds server + CLI via nix develop,
starts the server in stateless HTTP mode, runs the bash test script,
then runs the Rust integration test suite (each test spawns its own server).
- tests/cli-e2e.sh: bash script with 7 end-to-end tests covering:
exec, executions list/get/output/cancel, poll-until-complete, and
the --json flag. Env vars CLI_BIN and SERVER_URL are configurable.
- mcp-v8-client/tests/cli_e2e.rs: Rust integration tests mirroring the
same 7 test cases. Each test starts an independent server process on a
free port (via CARGO_BIN_EXE_mcp-v8-cli + SERVER_BIN env var),
runs CLI subcommands as child processes, and asserts exit codes and output.
- mcp-v8-client/Cargo.toml: add reqwest (blocking) as a dev-dependency
for server health-polling inside the Rust tests.
* fix: add IntoParams derive to OutputQuery for utoipa compatibility
* fix: add anyhow dep to mcp-v8-client; use fake hash to get correct Nix vendor hash
* fix: drop utoipa-swagger-ui, serve openapi.json via plain axum route
* fix: restore correct Nix hash; fix cli-e2e binary paths in workflow
* fix: update binary paths and Nix hash for workspace layout
The workspace Cargo.toml at the root causes cargo to output binaries
to <workspace_root>/target/ instead of server/target/. Updated all
references in Dockerfile, workflows, tests, and scripts. Regenerated
server/Cargo.lock to include utoipa dependencies and updated the Nix
vendor hash.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use console.log in CLI E2E test 5 so output is captured
The executions output endpoint returns console output, not expression
return values. Changed `2+2` to `console.log(2+2)` so the test can
verify the output contains '4'.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: 4 bugs from PR review
- ExecutionSummary: add #[serde(rename = "execution_id")] so list table
shows IDs instead of '-' (field was named 'id', spec expected 'execution_id')
- Move [patch.crates-io] deno_core fork from server/Cargo.toml to workspace
root Cargo.toml; Cargo silently ignores [patch] in non-root members,
so the heap-leak fix was not being applied in workspace builds
- Remove unused openapi_router() fn and utoipa-axum dep from server;
the server uses api_router() directly
- Regenerate openapi.json from current server binary (utoipa 5 -> 4 downgrade
restores OpenAPI 3.0.3 output compatible with progenitor 0.8); fix
CLI arg heap_memory_max_mb: Option<i64> -> Option<u64> to match generated
client type (progenitor emits u64 when schema has minimum: 0)
* ci: add openapi-drift workflow to catch uncommitted spec changes
Builds the server binary, regenerates openapi.json via --print-openapi,
then fails with a helpful message if either openapi.json or
mcp-v8-client/openapi.json differs from what's committed.
* fix: update Nix vendor hash for utoipa 4 downgrade
- Regenerate server/Cargo.lock resolving utoipa to 4.2.3 (was 5.4.0)
The Nix build vendors ./server standalone at a fixed hash; utoipa 5
was not in the vendored store since the PR originally had utoipa 5 at
commit time but we downgraded to 4 for progenitor 0.8 compat.
- Set flake.nix cargoDeps hash to fake value (sha256-AAAA...) so the
Nix build fails with the correct expected hash on first CI run.
Update the hash once CI reports the correct value.
- Add workspace Cargo.lock (tracks mcp-v8-client deps separately)
* fix: update Nix vendor hash for utoipa 4 (sha256-L1IGe3...)
* fix: update test_list_executions to use execution_id field
The ExecutionSummary rename (id -> execution_id via serde rename)
broke the existing server integration test that was still asserting
on the old 'id' field name in the list response.
* docs: add http-api-and-client.md covering CLI, Rust SDK, and code generation
- Full CLI reference and walkthrough with real command output
- Rust SDK examples (submit, poll, cancel, paginate output) verified
compiling and running against a live server
- Step-by-step guide for generating clients in TS, Python, Go, or any
OpenAPI-supported language from openapi.json
- Documents the openapi-drift CI workflow and how to resolve failures
* feat: add /api/cli endpoints to serve CLI downloads for the running version
GET /api/cli
Returns a JSON index listing download URLs for all supported platforms.
Version is taken from CARGO_PKG_VERSION, URLs point to the matching
GitHub Release asset so the CLI always matches the server binary.
GET /api/cli/{platform} (linux-x86_64 | linux-aarch64 | macos-x86_64 | macos-aarch64)
HTTP 307 redirect to the CLI binary on GitHub Releases.
Add ?gz=1 for the gzip-compressed variant.
Also:
- Fix release.yml asset naming: use explicit {bin_name} for CLI artifacts
(was the confusing $bin-$dir_trimmed pattern, now mcp-v8-cli-linux-x86_64 etc.)
- Regenerate openapi.json to include the new /api/cli paths
* feat: embed CLI binaries in server binary for direct download
Server now serves the CLI binary directly from memory — no redirect,
no GitHub dependency at download time.
How it works:
- server/build.rs reads MCP_V8_CLI_{PLATFORM} env vars at compile time
and copies the binaries into OUT_DIR; include_bytes! embeds them.
- In dev/local builds (no env vars) placeholder empty files are written,
and /api/cli/{platform} returns 404 with a helpful message.
- The release workflow adds a 'rebuild-server' job that runs after
'build-cli', downloads the matching CLI artifact, and rebuilds each
server binary with the env var set.
New endpoints:
GET /api/version — { "version": "0.1.0" }
GET /api/cli — index with per-platform URLs + available flag
GET /api/cli/{platform} — streams the binary (Content-Disposition: attachment)
Usage from any host pointing at your server:
curl -L http://my-server:3000/api/cli/linux-x86_64 -o mcp-v8-cli
chmod +x mcp-v8-cli
* test: add integration tests for /api/version and /api/cli endpoints
9 tests covering:
- /api/version returns a non-empty semver string matching CARGO_PKG_VERSION
- /api/cli index shape (version, assets array, required fields per asset)
- /api/cli covers all 4 expected platforms
- /api/cli version matches /api/version
- /api/cli asset urls point back to this server's /api/cli/{platform}
- /api/cli/{unknown} returns 404 with error listing valid platforms
- /api/cli/{platform} returns 200 ELF binary (release build) or
404 with 'not embedded / set MCP_V8_CLI_*' message (dev build)
- available flag in index is consistent with actual download status
Tests pass in both dev builds (available=false, 404 path) and
release builds with MCP_V8_CLI_LINUX_X86_64 set (available=true, 200 + ELF).
* test: add E2E test that downloads CLI from API and runs console.log(1+1)
Downloads mcp-v8-cli from /api/cli/linux-x86_64, writes it to a temp
file, chmod +x, then:
1. Runs: mcp-v8-cli --json exec 'console.log(1+1)'
2. Polls: mcp-v8-cli --json executions get <id> until completed
3. Reads: mcp-v8-cli executions output <id>
4. Asserts output contains '2'
The entire flow uses only the binary downloaded from the server — the
same one that was embedded at build time. Skips gracefully with a clear
message in dev builds where the binary is not embedded.
* docs: update http-api-and-client.md with version, CLI download, and embedding guide
- Add full API reference table including /api/version and /api/cli
- Document /api/version and /api/cli endpoints with real output
- Update CLI installation section: server-hosted download is now the
recommended method (always matches the running version)
- Add one-liner download+exec+output example (console.log(1+1) → 2)
- Add 'How the CLI binary embedding works' section explaining the
two-pass release build, dev build behaviour, and how to test
the full flow locally with MCP_V8_CLI_LINUX_X86_64
---------
Co-authored-by: OpenClaw Agent <agent@openclaw.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: OpenClaw <fixes@openclaw>1 parent 3d12942 commit db5a1c9
31 files changed
Lines changed: 11235 additions & 727 deletions
File tree
- .github/workflows
- docs
- loadtest
- mcp-v8-client
- src
- tests
- server
- src
- engine
- tests
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
62 | | - | |
| 62 | + | |
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
| 66 | + | |
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
| |||
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
79 | | - | |
| 79 | + | |
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
0 commit comments