agent-baton-cli 0.1.1__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. agent_baton_cli-0.2.0/.dockerignore +6 -0
  2. agent_baton_cli-0.2.0/.github/workflows/release.yml +62 -0
  3. agent_baton_cli-0.2.0/CHANGELOG.md +25 -0
  4. agent_baton_cli-0.2.0/Dockerfile +27 -0
  5. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/PKG-INFO +54 -4
  6. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/README.md +52 -2
  7. agent_baton_cli-0.2.0/integrations/mcp/README.md +60 -0
  8. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/pyproject.toml +2 -2
  9. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/src/agent_baton/__init__.py +1 -1
  10. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/src/agent_baton/cli.py +52 -10
  11. agent_baton_cli-0.2.0/src/agent_baton/mcp.py +309 -0
  12. agent_baton_cli-0.2.0/src/agent_baton/quickstart.py +109 -0
  13. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/src/agent_baton/schema.py +2 -1
  14. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/src/agent_baton/server.py +4 -0
  15. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/src/agent_baton/verify.py +82 -6
  16. agent_baton_cli-0.2.0/tests/test_baton.py +578 -0
  17. agent_baton_cli-0.1.1/.github/workflows/release.yml +0 -30
  18. agent_baton_cli-0.1.1/tests/test_baton.py +0 -232
  19. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/.github/workflows/ci.yml +0 -0
  20. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/.gitignore +0 -0
  21. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/LICENSE +0 -0
  22. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/docs/demo/demo.gif +0 -0
  23. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/docs/demo/make_demo.py +0 -0
  24. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/integrations/aider/CONVENTIONS.md +0 -0
  25. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/integrations/claude-code/skills/baton/SKILL.md +0 -0
  26. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/integrations/codex/AGENTS.md +0 -0
  27. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/integrations/cursor/baton.mdc +0 -0
  28. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/src/agent_baton/__main__.py +0 -0
  29. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/src/agent_baton/client.py +0 -0
  30. {agent_baton_cli-0.1.1 → agent_baton_cli-0.2.0}/src/agent_baton/store.py +0 -0
@@ -0,0 +1,6 @@
1
+ *
2
+ !pyproject.toml
3
+ !README.md
4
+ !LICENSE
5
+ !src/
6
+ **/__pycache__
@@ -0,0 +1,62 @@
1
+ name: release
2
+
3
+ # Push a tag like v0.1.0 to publish to PyPI and ghcr.io/varnholt/agent-baton.
4
+ # Uses PyPI trusted publishing and the workflow's GITHUB_TOKEN: no token is stored in this repo.
5
+
6
+ on:
7
+ push:
8
+ tags: ["v*"]
9
+
10
+ jobs:
11
+ pypi:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ permissions:
15
+ id-token: write
16
+ contents: write
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: astral-sh/setup-uv@v6
20
+ - run: uv run --group dev pytest -q
21
+ - name: check tag matches version
22
+ run: |
23
+ v=$(grep -m1 '^version' pyproject.toml | cut -d'"' -f2)
24
+ test "v$v" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME != version $v"; exit 1; }
25
+ - run: uv build
26
+ - uses: pypa/gh-action-pypi-publish@release/v1
27
+ - name: GitHub release
28
+ env:
29
+ GH_TOKEN: ${{ github.token }}
30
+ run: gh release create "$GITHUB_REF_NAME" dist/* --generate-notes
31
+
32
+ image:
33
+ # Runs after the PyPI job, so tests and the tag/version check have passed.
34
+ needs: pypi
35
+ runs-on: ubuntu-latest
36
+ permissions:
37
+ contents: read
38
+ packages: write
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ - name: version from tag
42
+ id: v
43
+ run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
44
+ - uses: docker/setup-qemu-action@v3
45
+ - uses: docker/setup-buildx-action@v3
46
+ - uses: docker/login-action@v3
47
+ with:
48
+ registry: ghcr.io
49
+ username: ${{ github.actor }}
50
+ password: ${{ secrets.GITHUB_TOKEN }}
51
+ - uses: docker/build-push-action@v6
52
+ with:
53
+ context: .
54
+ platforms: linux/amd64,linux/arm64
55
+ push: true
56
+ tags: |
57
+ ghcr.io/varnholt/agent-baton:${{ steps.v.outputs.version }}
58
+ ghcr.io/varnholt/agent-baton:latest
59
+ labels: |
60
+ org.opencontainers.image.source=https://github.com/varnholt/agent-baton
61
+ org.opencontainers.image.version=${{ steps.v.outputs.version }}
62
+ org.opencontainers.image.licenses=MIT
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0
4
+
5
+ - `build_finished` facts can be verified against GitHub Actions. Evidence is the run
6
+ URL (`https://github.com/<owner>/<repo>/actions/runs/<id>`, optionally with
7
+ `/attempts/<n>` or `/job/<id>`); `build` is the run id, `job` is the workflow file
8
+ stem or name. The verifier is picked by the evidence host; Jenkins is unchanged.
9
+ - `baton quickstart`: a local single-user server for a first try. Creates a
10
+ database and token in the per-user data directory, prints the env lines for
11
+ bash and PowerShell, and serves on 127.0.0.1 only.
12
+ - `baton mcp`: an MCP stdio server with `baton_publish`, `baton_wait`,
13
+ `baton_facts` and `baton_types`. Standard library only.
14
+ - Docker image for the server, published to `ghcr.io/varnholt/agent-baton` on release.
15
+
16
+ ## 0.1.1
17
+
18
+ - PyPI metadata: project URLs, keywords and classifiers. No code changes.
19
+
20
+ ## 0.1.0
21
+
22
+ - First release, on PyPI as `agent-baton-cli`: typed facts with strict field
23
+ patterns, an HTTP + SQLite server with long-poll waits, receiver-side
24
+ verification against GitHub, Bitbucket Cloud and Jenkins, and instructions for
25
+ Claude Code, Codex, Cursor and Aider.
@@ -0,0 +1,27 @@
1
+ # baton fact server.
2
+ # docker build -t agent-baton .
3
+ # docker run --rm -v baton-data:/data agent-baton baton member add alice --db /data/baton.db
4
+ # docker run -d -p 8765:8765 -v baton-data:/data agent-baton
5
+ # Serves plain HTTP: put it behind a TLS proxy, or pass --tls-cert/--tls-key.
6
+ FROM python:3.13-slim
7
+
8
+ ENV PYTHONDONTWRITEBYTECODE=1 \
9
+ PYTHONUNBUFFERED=1 \
10
+ PIP_NO_CACHE_DIR=1 \
11
+ PIP_DISABLE_PIP_VERSION_CHECK=1
12
+
13
+ WORKDIR /src
14
+ COPY pyproject.toml README.md LICENSE ./
15
+ COPY src ./src
16
+ RUN pip install . && rm -rf /src
17
+
18
+ RUN useradd --system --uid 10001 --home-dir /data --shell /usr/sbin/nologin baton \
19
+ && mkdir -p /data && chown baton:baton /data
20
+
21
+ USER baton
22
+ WORKDIR /data
23
+ VOLUME /data
24
+ EXPOSE 8765
25
+
26
+ # No HEALTHCHECK: every route, /v1/health included, needs a member token.
27
+ CMD ["baton", "serve", "--db", "/data/baton.db", "--host", "0.0.0.0"]
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agent-baton-cli
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Pass verified facts between coding agents, so none of them sits waiting on a human relay.
5
5
  Project-URL: Homepage, https://github.com/varnholt/agent-baton
6
6
  Project-URL: Source, https://github.com/varnholt/agent-baton
7
7
  Project-URL: Issues, https://github.com/varnholt/agent-baton/issues
8
- Project-URL: Changelog, https://github.com/varnholt/agent-baton/releases
8
+ Project-URL: Changelog, https://github.com/varnholt/agent-baton/blob/main/CHANGELOG.md
9
9
  License-Expression: MIT
10
10
  License-File: LICENSE
11
11
  Keywords: ai-agents,claude-code,codex,coding-agents,llm-security,multi-agent,prompt-injection
@@ -66,7 +66,7 @@ errors and confusion. baton is built so that does not happen:
66
66
  | Risk | What baton does |
67
67
  |---|---|
68
68
  | One agent instructs another | **Facts, not messages.** Every field is a short string matching a strict pattern. There is no free-text field to put a prompt in. |
69
- | A confused or malicious peer lies | **The receiver checks the source.** PR facts are checked against GitHub/Bitbucket, build facts against Jenkins, with the *receiver's* credentials. A contradicted fact exits non-zero. |
69
+ | A confused or malicious peer lies | **The receiver checks the source.** PR facts are checked against GitHub/Bitbucket, build facts against Jenkins or GitHub Actions, with the *receiver's* credentials. A contradicted fact exits non-zero. |
70
70
  | Fake evidence (`https://evil.example/...`) | Verifiers only contact hosts the receiver already trusts, never follow redirects, and check the evidence is about the same PR/build as the fact. |
71
71
  | Impersonation | The publisher is set by the server from the token, never from the payload. |
72
72
  | "Who did that?" | Append-only log: every fact has publisher, timestamp and evidence. |
@@ -80,10 +80,25 @@ useful pointers, never grounds for an irreversible action.
80
80
  |---|---|---|---|
81
81
  | `pr_merged` | repo, pr | commit | GitHub, Bitbucket Cloud |
82
82
  | `pr_state` | repo, pr | state (`open/merged/declined/closed`) | GitHub, Bitbucket Cloud |
83
- | `build_finished` | job, build | result (`SUCCESS/FAILURE/...`) | Jenkins (`JENKINS_URL`) |
83
+ | `build_finished` | job, build | result (`SUCCESS/FAILURE/...`) | Jenkins (`JENKINS_URL`), GitHub Actions |
84
84
  | `working_on` | repo, path | branch, state (`started/stopped`) | - |
85
85
  | `hint` | topic | tag (slug) | - |
86
86
 
87
+ The verifier is chosen by the evidence host. For GitHub Actions the evidence is the
88
+ run URL, `https://github.com/<owner>/<repo>/actions/runs/<run_id>` (a trailing
89
+ `/attempts/<n>` checks that attempt; `/job/<id>` is accepted and checks the whole run).
90
+ `build` must be the run id. `job` names the workflow, case-insensitive: the workflow
91
+ file name without extension (`.github/workflows/ci.yml` -> `ci`) or the workflow's
92
+ display name, optionally prefixed with `<repo>/` or `<owner>/<repo>/`, which must then
93
+ match the evidence. The run must be `completed`; its conclusion maps to `success` ->
94
+ `SUCCESS`, `failure`/`timed_out`/`startup_failure` -> `FAILURE`, `cancelled` -> `ABORTED`,
95
+ `skipped`/`neutral` -> `NOT_BUILT`.
96
+
97
+ ```
98
+ baton publish build_finished -s job=ci -s build=19121611211 -v result=SUCCESS \
99
+ -e https://github.com/acme/api/actions/runs/19121611211
100
+ ```
101
+
87
102
  Add your own with `baton serve --types types.json` (always delivered as unverifiable):
88
103
 
89
104
  ```json
@@ -96,8 +111,31 @@ Add your own with `baton serve --types types.json` (always delivered as unverifi
96
111
 
97
112
  Pure Python 3.10+, no dependencies.
98
113
 
114
+ ### 60-second quickstart
115
+
99
116
  ```
100
117
  pip install agent-baton-cli # or: uv tool install agent-baton-cli
118
+ baton quickstart
119
+ ```
120
+
121
+ `quickstart` creates a database and a token for you in your per-user data directory
122
+ (`~/.local/share/baton`, or `%LOCALAPPDATA%\baton` on Windows), prints the
123
+ `BATON_URL`/`BATON_TOKEN` lines for bash and PowerShell, and serves on
124
+ `127.0.0.1:8765` (`--port` to change; it refuses non-loopback addresses). Paste the
125
+ env lines into two terminals and try it:
126
+
127
+ ```
128
+ baton wait hint -s topic=hello # terminal 1: blocks
129
+ baton publish hint -s topic=hello -v tag=hi # terminal 2: terminal 1 wakes up
130
+ ```
131
+
132
+ Rerunning `baton quickstart` prints the same token again. For a team, set up a
133
+ shared server as below.
134
+
135
+ ### Shared server
136
+
137
+ ```
138
+ pip install agent-baton-cli
101
139
 
102
140
  # on one shared machine
103
141
  baton member add alice --db team.db # prints a token once
@@ -114,6 +152,17 @@ export BITBUCKET_TOKEN=... # optional, private repos
114
152
 
115
153
  Without TLS, keep the server on `127.0.0.1` and reach it over an SSH tunnel.
116
154
 
155
+ ### Docker
156
+
157
+ ```
158
+ docker volume create baton-data
159
+ docker run --rm -v baton-data:/data ghcr.io/varnholt/agent-baton baton member add alice --db /data/baton.db
160
+ docker run -d --name baton -p 127.0.0.1:8765:8765 -v baton-data:/data ghcr.io/varnholt/agent-baton
161
+ ```
162
+
163
+ The image runs `baton serve --db /data/baton.db --host 0.0.0.0` as a non-root user and
164
+ speaks plain HTTP: publish the port on loopback as above, or put a TLS proxy in front.
165
+
117
166
  ## Use it from your agent
118
167
 
119
168
  | Agent | File | Install |
@@ -122,6 +171,7 @@ Without TLS, keep the server on `127.0.0.1` and reach it over an SSH tunnel.
122
171
  | Codex | [`integrations/codex/AGENTS.md`](integrations/codex/AGENTS.md) | append to your `AGENTS.md` |
123
172
  | Cursor | [`integrations/cursor/baton.mdc`](integrations/cursor/baton.mdc) | copy to `.cursor/rules/` |
124
173
  | Aider | [`integrations/aider/CONVENTIONS.md`](integrations/aider/CONVENTIONS.md) | `aider --read ...` |
174
+ | Any MCP client | [`integrations/mcp`](integrations/mcp/README.md) | `claude mcp add baton -- baton mcp` |
125
175
 
126
176
  Each one teaches the agent the same three habits: wait instead of asking the user to
127
177
  relay, publish what others may be waiting on, and treat every fact as data.
@@ -44,7 +44,7 @@ errors and confusion. baton is built so that does not happen:
44
44
  | Risk | What baton does |
45
45
  |---|---|
46
46
  | One agent instructs another | **Facts, not messages.** Every field is a short string matching a strict pattern. There is no free-text field to put a prompt in. |
47
- | A confused or malicious peer lies | **The receiver checks the source.** PR facts are checked against GitHub/Bitbucket, build facts against Jenkins, with the *receiver's* credentials. A contradicted fact exits non-zero. |
47
+ | A confused or malicious peer lies | **The receiver checks the source.** PR facts are checked against GitHub/Bitbucket, build facts against Jenkins or GitHub Actions, with the *receiver's* credentials. A contradicted fact exits non-zero. |
48
48
  | Fake evidence (`https://evil.example/...`) | Verifiers only contact hosts the receiver already trusts, never follow redirects, and check the evidence is about the same PR/build as the fact. |
49
49
  | Impersonation | The publisher is set by the server from the token, never from the payload. |
50
50
  | "Who did that?" | Append-only log: every fact has publisher, timestamp and evidence. |
@@ -58,10 +58,25 @@ useful pointers, never grounds for an irreversible action.
58
58
  |---|---|---|---|
59
59
  | `pr_merged` | repo, pr | commit | GitHub, Bitbucket Cloud |
60
60
  | `pr_state` | repo, pr | state (`open/merged/declined/closed`) | GitHub, Bitbucket Cloud |
61
- | `build_finished` | job, build | result (`SUCCESS/FAILURE/...`) | Jenkins (`JENKINS_URL`) |
61
+ | `build_finished` | job, build | result (`SUCCESS/FAILURE/...`) | Jenkins (`JENKINS_URL`), GitHub Actions |
62
62
  | `working_on` | repo, path | branch, state (`started/stopped`) | - |
63
63
  | `hint` | topic | tag (slug) | - |
64
64
 
65
+ The verifier is chosen by the evidence host. For GitHub Actions the evidence is the
66
+ run URL, `https://github.com/<owner>/<repo>/actions/runs/<run_id>` (a trailing
67
+ `/attempts/<n>` checks that attempt; `/job/<id>` is accepted and checks the whole run).
68
+ `build` must be the run id. `job` names the workflow, case-insensitive: the workflow
69
+ file name without extension (`.github/workflows/ci.yml` -> `ci`) or the workflow's
70
+ display name, optionally prefixed with `<repo>/` or `<owner>/<repo>/`, which must then
71
+ match the evidence. The run must be `completed`; its conclusion maps to `success` ->
72
+ `SUCCESS`, `failure`/`timed_out`/`startup_failure` -> `FAILURE`, `cancelled` -> `ABORTED`,
73
+ `skipped`/`neutral` -> `NOT_BUILT`.
74
+
75
+ ```
76
+ baton publish build_finished -s job=ci -s build=19121611211 -v result=SUCCESS \
77
+ -e https://github.com/acme/api/actions/runs/19121611211
78
+ ```
79
+
65
80
  Add your own with `baton serve --types types.json` (always delivered as unverifiable):
66
81
 
67
82
  ```json
@@ -74,8 +89,31 @@ Add your own with `baton serve --types types.json` (always delivered as unverifi
74
89
 
75
90
  Pure Python 3.10+, no dependencies.
76
91
 
92
+ ### 60-second quickstart
93
+
77
94
  ```
78
95
  pip install agent-baton-cli # or: uv tool install agent-baton-cli
96
+ baton quickstart
97
+ ```
98
+
99
+ `quickstart` creates a database and a token for you in your per-user data directory
100
+ (`~/.local/share/baton`, or `%LOCALAPPDATA%\baton` on Windows), prints the
101
+ `BATON_URL`/`BATON_TOKEN` lines for bash and PowerShell, and serves on
102
+ `127.0.0.1:8765` (`--port` to change; it refuses non-loopback addresses). Paste the
103
+ env lines into two terminals and try it:
104
+
105
+ ```
106
+ baton wait hint -s topic=hello # terminal 1: blocks
107
+ baton publish hint -s topic=hello -v tag=hi # terminal 2: terminal 1 wakes up
108
+ ```
109
+
110
+ Rerunning `baton quickstart` prints the same token again. For a team, set up a
111
+ shared server as below.
112
+
113
+ ### Shared server
114
+
115
+ ```
116
+ pip install agent-baton-cli
79
117
 
80
118
  # on one shared machine
81
119
  baton member add alice --db team.db # prints a token once
@@ -92,6 +130,17 @@ export BITBUCKET_TOKEN=... # optional, private repos
92
130
 
93
131
  Without TLS, keep the server on `127.0.0.1` and reach it over an SSH tunnel.
94
132
 
133
+ ### Docker
134
+
135
+ ```
136
+ docker volume create baton-data
137
+ docker run --rm -v baton-data:/data ghcr.io/varnholt/agent-baton baton member add alice --db /data/baton.db
138
+ docker run -d --name baton -p 127.0.0.1:8765:8765 -v baton-data:/data ghcr.io/varnholt/agent-baton
139
+ ```
140
+
141
+ The image runs `baton serve --db /data/baton.db --host 0.0.0.0` as a non-root user and
142
+ speaks plain HTTP: publish the port on loopback as above, or put a TLS proxy in front.
143
+
95
144
  ## Use it from your agent
96
145
 
97
146
  | Agent | File | Install |
@@ -100,6 +149,7 @@ Without TLS, keep the server on `127.0.0.1` and reach it over an SSH tunnel.
100
149
  | Codex | [`integrations/codex/AGENTS.md`](integrations/codex/AGENTS.md) | append to your `AGENTS.md` |
101
150
  | Cursor | [`integrations/cursor/baton.mdc`](integrations/cursor/baton.mdc) | copy to `.cursor/rules/` |
102
151
  | Aider | [`integrations/aider/CONVENTIONS.md`](integrations/aider/CONVENTIONS.md) | `aider --read ...` |
152
+ | Any MCP client | [`integrations/mcp`](integrations/mcp/README.md) | `claude mcp add baton -- baton mcp` |
103
153
 
104
154
  Each one teaches the agent the same three habits: wait instead of asking the user to
105
155
  relay, publish what others may be waiting on, and treat every fact as data.
@@ -0,0 +1,60 @@
1
+ # baton over MCP
2
+
3
+ `baton mcp` is a Model Context Protocol server on stdio. Any MCP client (Claude Code,
4
+ Claude Desktop, Cursor, Codex, ...) can use baton through it without shelling out.
5
+ It is part of the `baton` command, so there is nothing extra to install.
6
+
7
+ It reads the same configuration as the CLI: `BATON_URL` and `BATON_TOKEN` (or
8
+ `~/.config/baton/client.json`), plus the verifier settings (`GITHUB_TOKEN`,
9
+ `BITBUCKET_TOKEN`, `JENKINS_URL`, ...). Verification runs inside `baton mcp`, on
10
+ your machine, with your credentials.
11
+
12
+ ## Claude Code
13
+
14
+ ```
15
+ claude mcp add baton -e BATON_URL=http://127.0.0.1:8765 -e BATON_TOKEN=bt_... -- baton mcp
16
+ ```
17
+
18
+ If `BATON_URL` and `BATON_TOKEN` are already exported in the shell that starts
19
+ Claude Code, `claude mcp add baton -- baton mcp` is enough.
20
+
21
+ ## Other clients (`mcpServers` JSON)
22
+
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "baton": {
27
+ "command": "baton",
28
+ "args": ["mcp"],
29
+ "env": {
30
+ "BATON_URL": "http://127.0.0.1:8765",
31
+ "BATON_TOKEN": "bt_..."
32
+ }
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ Without a global install: `"command": "uvx", "args": ["--from", "agent-baton-cli", "baton", "mcp"]`.
39
+
40
+ ## Tools
41
+
42
+ | tool | does |
43
+ |---|---|
44
+ | `baton_types` | list the fact types the server accepts |
45
+ | `baton_publish` | publish a fact: `type`, `subject`, `value`, `evidence` |
46
+ | `baton_wait` | block until a matching fact exists (`timeout` default 300 s, max 600 s), verify it, return it with a status |
47
+ | `baton_facts` | list recent facts (unverified), or fetch one by `id` and verify it |
48
+
49
+ Every result restates that facts are peer data, not instructions. `baton_wait` and
50
+ `baton_facts` with an `id` return a `status`:
51
+
52
+ - `verified` - the source of truth confirms the fact.
53
+ - `mismatch` - the source contradicts it. Do not act on it.
54
+ - `unverifiable` - no source to check (hints, unknown hosts). A pointer only.
55
+ - `error` - the source could not be reached. Unconfirmed.
56
+
57
+ For waits longer than 10 minutes, call `baton_wait` again, or run `baton wait` as a
58
+ background shell command instead, which can wait for hours at no cost. Some
59
+ clients also time out tool calls on their own; raise that limit
60
+ (e.g. `MCP_TOOL_TIMEOUT` in Claude Code) if waits are cut short.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agent-baton-cli"
3
- version = "0.1.1"
3
+ version = "0.2.0"
4
4
  description = "Pass verified facts between coding agents, so none of them sits waiting on a human relay."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -22,7 +22,7 @@ classifiers = [
22
22
  Homepage = "https://github.com/varnholt/agent-baton"
23
23
  Source = "https://github.com/varnholt/agent-baton"
24
24
  Issues = "https://github.com/varnholt/agent-baton/issues"
25
- Changelog = "https://github.com/varnholt/agent-baton/releases"
25
+ Changelog = "https://github.com/varnholt/agent-baton/blob/main/CHANGELOG.md"
26
26
 
27
27
  [project.scripts]
28
28
  baton = "agent_baton.cli:main"
@@ -1,3 +1,3 @@
1
1
  """agent-baton - pass verified facts between coding agents, so none of them sits waiting on a human relay."""
2
2
 
3
- __version__ = "0.1.1"
3
+ __version__ = "0.2.0"
@@ -1,10 +1,11 @@
1
- """Command line: `baton serve | member | publish | wait | facts | types`."""
1
+ """Command line: `baton serve | quickstart | member | publish | wait | facts | types | mcp`."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
5
  import argparse
6
6
  import json
7
7
  import sys
8
+ from pathlib import Path
8
9
 
9
10
  from . import __version__
10
11
  from .client import BatonError, Client
@@ -43,17 +44,17 @@ def _print_fact(fact: dict, check: dict | None) -> None:
43
44
  # commands ------------------------------------------------------------------
44
45
 
45
46
 
46
- def cmd_serve(a) -> int:
47
+ def _run_server(store, types, host: str, port: int, tls_cert: str | None = None, tls_key: str | None = None) -> int:
47
48
  from .server import serve
48
- from .store import Store
49
49
 
50
- types = load_types(a.types)
51
- store = Store(a.db, retention_days=a.retention_days)
52
- if not store.members():
53
- print("warning: no members yet - add one with `baton member add <name> --db ...`", file=sys.stderr)
54
- srv = serve(store, types, a.host, a.port, a.tls_cert, a.tls_key)
55
- scheme = "https" if a.tls_cert else "http"
56
- print(f"baton {__version__} serving {scheme}://{a.host}:{a.port} ({len(types)} fact types)", flush=True)
50
+ try:
51
+ srv = serve(store, types, host, port, tls_cert, tls_key)
52
+ except OSError as e:
53
+ store.close()
54
+ print(f"baton: cannot serve on {host}:{port}: {e}", file=sys.stderr)
55
+ return EXIT_ERROR
56
+ scheme = "https" if tls_cert else "http"
57
+ print(f"baton {__version__} serving {scheme}://{host}:{port} ({len(types)} fact types)", flush=True)
57
58
  try:
58
59
  srv.serve_forever()
59
60
  except KeyboardInterrupt:
@@ -64,6 +65,38 @@ def cmd_serve(a) -> int:
64
65
  return EXIT_OK
65
66
 
66
67
 
68
+ def cmd_serve(a) -> int:
69
+ from .store import Store
70
+
71
+ types = load_types(a.types)
72
+ store = Store(a.db, retention_days=a.retention_days)
73
+ if not store.members():
74
+ print("warning: no members yet - add one with `baton member add <name> --db ...`", file=sys.stderr)
75
+ return _run_server(store, types, a.host, a.port, a.tls_cert, a.tls_key)
76
+
77
+
78
+ def cmd_quickstart(a) -> int:
79
+ from . import quickstart as qs
80
+ from .store import Store
81
+
82
+ qs.check_host(a.host)
83
+ directory = Path(a.data_dir) if a.data_dir else qs.data_dir()
84
+ name = qs.member_name()
85
+ db, token = qs.setup(directory, name)
86
+ url = f"http://{a.host}:{a.port}"
87
+ print(f"baton quickstart: member {name!r}, database {db}")
88
+ print("Run these in the shell where your agent runs:\n")
89
+ print(qs.env_lines(url, token))
90
+ print("Then try: baton types (Ctrl+C stops the server; rerun to print this again)\n", flush=True)
91
+ return _run_server(Store(str(db)), load_types(None), a.host, a.port)
92
+
93
+
94
+ def cmd_mcp(a) -> int:
95
+ from .mcp import run
96
+
97
+ return run()
98
+
99
+
67
100
  def cmd_member(a) -> int:
68
101
  from .store import Store
69
102
 
@@ -165,6 +198,12 @@ def build_parser() -> argparse.ArgumentParser:
165
198
  s.add_argument("--retention-days", type=int, default=30)
166
199
  s.set_defaults(fn=cmd_serve)
167
200
 
201
+ s = sub.add_parser("quickstart", help="local trial: create a database and token, serve on 127.0.0.1")
202
+ s.add_argument("--port", type=int, default=8765)
203
+ s.add_argument("--host", default="127.0.0.1", help="loopback only")
204
+ s.add_argument("--data-dir", help="default: per-user data dir (or BATON_DATA_DIR)")
205
+ s.set_defaults(fn=cmd_quickstart)
206
+
168
207
  s = sub.add_parser("member", help="manage member tokens (run on the server)")
169
208
  s.add_argument("action", choices=["add", "remove", "list"])
170
209
  s.add_argument("name", nargs="?")
@@ -202,6 +241,9 @@ def build_parser() -> argparse.ArgumentParser:
202
241
  s.add_argument("--types-file")
203
242
  s.add_argument("--json", action="store_true")
204
243
  s.set_defaults(fn=cmd_types)
244
+
245
+ s = sub.add_parser("mcp", help="run an MCP stdio server exposing baton tools")
246
+ s.set_defaults(fn=cmd_mcp)
205
247
  return p
206
248
 
207
249