probe-research 0.6.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.
- probe_research-0.6.0/.gitignore +7 -0
- probe_research-0.6.0/PKG-INFO +308 -0
- probe_research-0.6.0/README.md +280 -0
- probe_research-0.6.0/deploy/mcp/README.md +45 -0
- probe_research-0.6.0/fixtures/rl_offpolicy/README.md +45 -0
- probe_research-0.6.0/plugins/probe-research/README.md +46 -0
- probe_research-0.6.0/plugins/probe-research/skills/manage-research-asset/SKILL.md +16 -0
- probe_research-0.6.0/plugins/probe-research/skills/manage-research-asset/agents/openai.yaml +4 -0
- probe_research-0.6.0/plugins/probe-research/skills/publish-experiment/SKILL.md +18 -0
- probe_research-0.6.0/plugins/probe-research/skills/publish-experiment/agents/openai.yaml +4 -0
- probe_research-0.6.0/plugins/probe-research/skills/track-experiment/SKILL.md +16 -0
- probe_research-0.6.0/plugins/probe-research/skills/track-experiment/agents/openai.yaml +4 -0
- probe_research-0.6.0/pyproject.toml +72 -0
- probe_research-0.6.0/skills/manage-research-asset/SKILL.md +16 -0
- probe_research-0.6.0/skills/manage-research-asset/agents/openai.yaml +4 -0
- probe_research-0.6.0/skills/publish-experiment/SKILL.md +18 -0
- probe_research-0.6.0/skills/publish-experiment/agents/openai.yaml +4 -0
- probe_research-0.6.0/skills/track-experiment/SKILL.md +16 -0
- probe_research-0.6.0/skills/track-experiment/agents/openai.yaml +4 -0
- probe_research-0.6.0/src/probe/__init__.py +31 -0
- probe_research-0.6.0/src/probe/_generated/__init__.py +0 -0
- probe_research-0.6.0/src/probe/_generated/models.py +1189 -0
- probe_research-0.6.0/src/probe/cli/__init__.py +16 -0
- probe_research-0.6.0/src/probe/cli/main.py +1266 -0
- probe_research-0.6.0/src/probe/client.py +5 -0
- probe_research-0.6.0/src/probe/config.py +13 -0
- probe_research-0.6.0/src/probe/connectors/__init__.py +2 -0
- probe_research-0.6.0/src/probe/connectors/harbor.py +289 -0
- probe_research-0.6.0/src/probe/errors.py +3 -0
- probe_research-0.6.0/src/probe/mcp/__init__.py +6 -0
- probe_research-0.6.0/src/probe/mcp/contract.py +95 -0
- probe_research-0.6.0/src/probe/mcp/server.py +380 -0
- probe_research-0.6.0/src/probe/mcp/service.py +537 -0
- probe_research-0.6.0/src/probe/mcp/source.py +209 -0
- probe_research-0.6.0/src/probe/models.py +112 -0
- probe_research-0.6.0/src/probe/run.py +5 -0
- probe_research-0.6.0/src/probe/sdk/__init__.py +23 -0
- probe_research-0.6.0/src/probe/sdk/assets.py +169 -0
- probe_research-0.6.0/src/probe/sdk/client.py +727 -0
- probe_research-0.6.0/src/probe/sdk/config.py +118 -0
- probe_research-0.6.0/src/probe/sdk/defaults.py +98 -0
- probe_research-0.6.0/src/probe/sdk/device.py +164 -0
- probe_research-0.6.0/src/probe/sdk/errors.py +94 -0
- probe_research-0.6.0/src/probe/sdk/events.py +102 -0
- probe_research-0.6.0/src/probe/sdk/run.py +486 -0
- probe_research-0.6.0/src/probe/sdk/sessions.py +156 -0
- probe_research-0.6.0/src/probe/sdk/snapshot.py +145 -0
- probe_research-0.6.0/src/probe/sdk/spool.py +86 -0
- probe_research-0.6.0/src/probe/sdk/transport.py +209 -0
- probe_research-0.6.0/src/probe/snapshot.py +5 -0
- probe_research-0.6.0/src/probe/spool.py +5 -0
- probe_research-0.6.0/src/probe/transport.py +5 -0
- probe_research-0.6.0/tests/conftest.py +630 -0
- probe_research-0.6.0/tests/test_cli.py +123 -0
- probe_research-0.6.0/tests/test_device_login.py +94 -0
- probe_research-0.6.0/tests/test_harbor_connector.py +188 -0
- probe_research-0.6.0/tests/test_lifecycle.py +336 -0
- probe_research-0.6.0/tests/test_mcp.py +616 -0
- probe_research-0.6.0/tests/test_mcp_hosted.py +260 -0
- probe_research-0.6.0/tests/test_mcp_oauth_discovery.py +103 -0
- probe_research-0.6.0/tests/test_mcp_token.py +207 -0
- probe_research-0.6.0/tests/test_onboarding.py +203 -0
- probe_research-0.6.0/tests/test_parity.py +528 -0
- probe_research-0.6.0/tests/test_sdk.py +406 -0
- probe_research-0.6.0/tests/test_snapshot.py +73 -0
- probe_research-0.6.0/tests/test_surfaces.py +112 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: probe-research
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: CLI + SDK + read-only MCP client for Probe Research (experiment tracking).
|
|
5
|
+
Project-URL: Homepage, https://github.com/prbe-ai/research-os-agent
|
|
6
|
+
Project-URL: Repository, https://github.com/prbe-ai/research-os-agent
|
|
7
|
+
Author-email: Probe <team@prbe.ai>
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: experiment-tracking,mcp,mlops,probe-research,reinforcement-learning
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: httpx>=0.27
|
|
19
|
+
Requires-Dist: mcp<2,>=1.23
|
|
20
|
+
Requires-Dist: pydantic>=2.9
|
|
21
|
+
Requires-Dist: typer<1.0,>=0.12
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: datamodel-code-generator>=0.25; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
25
|
+
Provides-Extra: mcp-http
|
|
26
|
+
Requires-Dist: uvicorn>=0.30; extra == 'mcp-http'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# probe-research (`probe` SDK/CLI + `probe-research` plugin)
|
|
30
|
+
|
|
31
|
+
CLI + SDK client for **Probe Research**, Probe's experiment-tracking platform. It is a
|
|
32
|
+
thin client over the v3 ingestion contract (`CONTRACT.md` in the Probe Research backend).
|
|
33
|
+
Implemented experiment calls map onto real endpoints. Target asset-registry
|
|
34
|
+
methods are present as an explicit client contract but fail closed with a
|
|
35
|
+
capability error until the backend routes exist.
|
|
36
|
+
|
|
37
|
+
## Two client surfaces
|
|
38
|
+
|
|
39
|
+
Probe Research exposes experiment tracking through two separate surfaces over the same backend, for two different workflows:
|
|
40
|
+
|
|
41
|
+
- **`probe` — SDK + CLI (non-agent).** A Python library (`import probe`) and the `probe` command-line tool for integrating with existing setups and manual experimentation. Drop it into a training script or pipeline to record runs, metrics, spans, and artifacts. No agent required.
|
|
42
|
+
- **`probe-research` — plugin: skills + MCP (agent-centric).** Installed into a coding agent (e.g. Claude Code). Its skills teach the agent the experiment workflow, its read-only MCP server lets the agent query experiment state, and writes flow through the `probe` CLI. This is the surface for agent-driven research loops such as Anthrogen.
|
|
43
|
+
|
|
44
|
+
Same backend, two entry points: humans-in-code reach for the SDK/CLI; agents-in-the-loop use the plugin.
|
|
45
|
+
|
|
46
|
+
## Package boundaries
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
src/probe/
|
|
50
|
+
├── sdk/ # typed client, uploads, local capture, session adapter ABI
|
|
51
|
+
├── cli/ # `probe`: thin shell over the SDK
|
|
52
|
+
└── mcp/ # `probe-research-mcp`: strictly read-only tools and resources
|
|
53
|
+
skills/
|
|
54
|
+
├── track-experiment/
|
|
55
|
+
├── manage-research-asset/
|
|
56
|
+
└── publish-experiment/
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The SDK is the implementation. The CLI, MCP source adapter, future hooks,
|
|
60
|
+
Python experiments, and passive platform integrations all use it. CLI and SDK
|
|
61
|
+
therefore have capability parity; they differ only in ergonomics.
|
|
62
|
+
|
|
63
|
+
| Surface | SDK | CLI | Intended caller |
|
|
64
|
+
|---|---|---|---|
|
|
65
|
+
| Experiment upload | `Client.run`, `Run.log/span/log_artifact/snapshot/link/execute`, `Client.events`, `Client.promote` | `run`, `log`, `span`, `artifact`, `snapshot`, `link`, `exec`, `event`, `promote` | Researchers, agents, notebooks, training/platform code |
|
|
66
|
+
| Session adapter | `Client.sessions.attach/checkpoint/detach` | `probe hook session ...` | **Future deterministic hooks/broker only** |
|
|
67
|
+
| Asset read/selection | `Client.assets.resolve`, normally behind MCP | No normal read verb | Agent through read-only MCP |
|
|
68
|
+
| Asset effects | `Client.assets.materialize/fork/propose/promote` | `probe asset ...` | Agent/researcher after selecting an exact asset ref |
|
|
69
|
+
| Passive ingestion | `Client.ingest` | No convenience command yet | Install-once platform integration |
|
|
70
|
+
| Read plane | SDK reads used by `probe.mcp` | `get`/`bundle` diagnostics | MCP for agents; CLI for humans/scripts |
|
|
71
|
+
|
|
72
|
+
Session commands do not upload metrics or experiment outputs. They correlate a
|
|
73
|
+
coding-agent session with a run and checkpoint redacted transcript metadata.
|
|
74
|
+
Conversely, `event add` is normal experiment knowledge upload even when a hook
|
|
75
|
+
eventually calls it. No hooks are installed in this release.
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install -e ".[dev]" # from this directory
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Auth
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
probe login # browser device flow (RFC 8628 + PKCE) — the default; nothing to paste
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Air-gap paste path: `probe login --token probe_pat_xxxxxxxx` (verified via `GET /v1/me`);
|
|
90
|
+
`probe login --endpoint-only --base-url …` saves the endpoint without minting a token.
|
|
91
|
+
Both write `~/.config/probe/config.json`.
|
|
92
|
+
Or set env: `PROBE_BASE_URL`, `PROBE_TOKEN` (user token, `/v1`), `PROBE_INGEST_TOKEN`
|
|
93
|
+
(ingest token, `/ingest`), `PROBE_HMAC_SECRET` (optional body-signature secret).
|
|
94
|
+
|
|
95
|
+
You can also skip `probe login` entirely: the first `client.run()` /
|
|
96
|
+
`probe run start` with no token triggers the same browser approval inline (TTY only)
|
|
97
|
+
and persists the result. Disable with `PROBE_AUTO_LOGIN=0`; headless/CI keeps the
|
|
98
|
+
crisp `AuthError` and should set `PROBE_TOKEN`.
|
|
99
|
+
|
|
100
|
+
The MCP server prefers `PROBE_MCP_TOKEN`, which should be a separately minted
|
|
101
|
+
read-only token. It falls back to `PROBE_TOKEN` for local development, but exposes
|
|
102
|
+
no mutation tools.
|
|
103
|
+
|
|
104
|
+
On rented compute (RunPod) with no standing config, the `/track-experiment` skill seeds
|
|
105
|
+
`PROBE_TOKEN` at session start.
|
|
106
|
+
|
|
107
|
+
## SDK (agent-driven / interactive)
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
import probe
|
|
111
|
+
|
|
112
|
+
client = probe.Client() # resolves creds from env / `probe login`
|
|
113
|
+
|
|
114
|
+
run = client.run(experiment="dockq-sweep", hypothesis="temp 0.7 wins", name="run-1",
|
|
115
|
+
project="folding", source="runpod", external_id="rp-9931")
|
|
116
|
+
# …or with zero identity args: `client.run()` defaults experiment to the git repo /
|
|
117
|
+
# script name, name to a timestamp (the server adds a petname short_id), and a NEW
|
|
118
|
+
# experiment gets a marked "[auto] …" hypothesis composed from context. Set the real
|
|
119
|
+
# one later: client.update_experiment(id, hypothesis="…") / probe experiment set.
|
|
120
|
+
|
|
121
|
+
run.snapshot() # non-disruptive git + deps + GPU capture
|
|
122
|
+
run.link(wandb_run_id="abc123", s3_prefix="s3://x/y")
|
|
123
|
+
|
|
124
|
+
for step in range(100):
|
|
125
|
+
run.log({"loss": ..., "dockq": ...}, step=step) # POST /v1/runs/{id}/metrics
|
|
126
|
+
|
|
127
|
+
sid = run.span("rollout", name="rollout-0", step_index=1) # trajectory span
|
|
128
|
+
run.log_artifact("final.sif", uri="r2://bucket/final.sif", kind="artifact")
|
|
129
|
+
run.finish() # flushes spool, sets status+ended_at
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Structured knowledge and local process capture use the same SDK:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
run.execute(["python", "train.py", "--config", "dockq.yaml"])
|
|
136
|
+
client.events.add(run.id, "decision", "Use DockQ scorer v3", evidence_refs=["tool:91"])
|
|
137
|
+
report = client.check_run(run.id)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Data writes are **fail-open** by default: on failure they spool to disk
|
|
141
|
+
(`~/.local/state/probe/spool`) and return, never blocking the training loop. `run.finish()`
|
|
142
|
+
(or `probe flush`) replays the spool. Pass `strict=True` to make a write raise.
|
|
143
|
+
|
|
144
|
+
## SDK (install-once / passive push)
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
client.ingest(
|
|
148
|
+
experiment_slug="dockq", experiment_hypothesis="...",
|
|
149
|
+
run={"name": "r1", "source": "temporal", "external_id": "wf-1", "status": "running"},
|
|
150
|
+
metrics=[{"kind": "model", "key": "loss", "value": 0.5, "step_index": 1}],
|
|
151
|
+
batch_id="deadbeef", # idempotent redelivery
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
One idempotent push (bearer ingest token + optional HMAC), keyed on
|
|
156
|
+
`(customer_id, source, external_id)`.
|
|
157
|
+
|
|
158
|
+
## CLI (`probe`)
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
RUN=$(probe run start --experiment dockq --hypothesis "temp 0.7 wins" --name run-1 \
|
|
162
|
+
--project folding --source runpod --external-id rp-9931)
|
|
163
|
+
probe snapshot $RUN
|
|
164
|
+
probe link $RUN --set wandb_run_id=abc --set gpu_job=rp-9931
|
|
165
|
+
probe log $RUN loss=0.42 dockq=0.71 --step 42
|
|
166
|
+
probe span add $RUN --type rollout --name rollout-0 --step 1
|
|
167
|
+
probe artifact add $RUN ./final.sif --kind artifact
|
|
168
|
+
probe event add $RUN --kind decision --statement "Use DockQ scorer v3" --evidence tool:91
|
|
169
|
+
probe exec $RUN -- python train.py --config dockq.yaml
|
|
170
|
+
probe run check $RUN
|
|
171
|
+
probe run end $RUN --status completed
|
|
172
|
+
probe bundle $RUN # read: run + series + artifacts
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The following commands are reserved for future hook configuration and are not
|
|
176
|
+
part of the normal researcher workflow:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
probe hook session attach RUN --session-id SESSION --transcript-path PATH --cwd DIR
|
|
180
|
+
probe hook session checkpoint RUN --session-id SESSION --transcript-path PATH --reason pre_compact
|
|
181
|
+
probe hook session detach RUN --session-id SESSION --reason session_end
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
They currently encode session links in `run.metadata.agent.sessions[]` and
|
|
185
|
+
transcript checkpoints as redacted local-reference artifacts. Until managed
|
|
186
|
+
artifact upload exists, transcript portability remains explicitly false.
|
|
187
|
+
|
|
188
|
+
## Read-only MCP server
|
|
189
|
+
|
|
190
|
+
Run the stdio server with `probe-research-mcp`. It exposes exactly six tools:
|
|
191
|
+
|
|
192
|
+
| Tool | Function |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `research_context` | Project/session bootstrap, prior experiments, active runs, capability warnings |
|
|
195
|
+
| `research_search` | One-index exact+semantic backend search (`POST /v1/search`, corpora: assets/procedures → files, documents → github+files, transcripts unsupported); keyword fallback on pre-search backends |
|
|
196
|
+
| `research_get` | Progressive card, handoff, reproduction, lineage, metrics, and artifact views |
|
|
197
|
+
| `research_compare` | Server-side comparison of runs, experiments, and future asset versions |
|
|
198
|
+
| `research_resolve` | Compatible asset resolution; honest partial result on API v3 |
|
|
199
|
+
| `research_trace_file` | Producer-consumer and cleanup lineage; partial until trace indexing lands |
|
|
200
|
+
|
|
201
|
+
MCP reads through the Probe Research API—never directly from Postgres or R2. Its
|
|
202
|
+
logical sources are control identity/tenant scope, the structured experiment
|
|
203
|
+
store, the asset/manifest registry, the one-index search door (`POST /v1/search`:
|
|
204
|
+
exact SQL channel + the KB engine's semantic channel; search capabilities are
|
|
205
|
+
discovered against the live backend with one cached probe), and object-store
|
|
206
|
+
resource pointers returned by the API. W&B, RunPod, Kubernetes,
|
|
207
|
+
Git, and local transcript paths are not live MCP sources; adapters upload their
|
|
208
|
+
identifiers and evidence first.
|
|
209
|
+
|
|
210
|
+
## Skills
|
|
211
|
+
|
|
212
|
+
- `experiment` mentally boxes result-producing work and uploads concise evidence.
|
|
213
|
+
- `manage-research-asset` resolves before create, reuses exact versions, forks
|
|
214
|
+
immutable bases, and proposes candidates without filename-based sprawl.
|
|
215
|
+
- `publish-experiment` requires explicit approval and refuses to imitate official
|
|
216
|
+
promotion when manifest/asset capabilities are unavailable.
|
|
217
|
+
|
|
218
|
+
Asset-reuse hooks are deliberately deferred. The track-experiment skill contains the
|
|
219
|
+
reuse-before-create rule; deterministic enforcement can be added later without
|
|
220
|
+
changing the SDK, CLI, MCP, or skill contracts.
|
|
221
|
+
|
|
222
|
+
## What maps to what (v3 endpoints)
|
|
223
|
+
|
|
224
|
+
| Client call | Endpoint |
|
|
225
|
+
|---|---|
|
|
226
|
+
| `client.run()` / `run.child()` | `POST /v1/experiments`, `POST /v1/experiments/{id}/runs` |
|
|
227
|
+
| `run.log()` / `run.log_hw()` | `POST /v1/runs/{id}/metrics` |
|
|
228
|
+
| `run.span()` / `run.step()` | `POST /v1/runs/{id}/spans` \| `/steps` |
|
|
229
|
+
| `run.log_artifact()` | `POST /v1/runs/{id}/artifacts` |
|
|
230
|
+
| `run.link()` | `PATCH /v1/runs/{id}` (merges `metadata.foreign_keys`) |
|
|
231
|
+
| `run.finish()` | `PATCH /v1/runs/{id}` |
|
|
232
|
+
| `client.events.add()` | `POST /v1/runs/{id}/artifacts` (`kind=research_event`, v3 encoding) |
|
|
233
|
+
| `client.sessions.*` | `PATCH /v1/runs/{id}` + transcript artifact metadata (hook ABI) |
|
|
234
|
+
| `client.ingest()` | `POST /ingest/v1/runs` |
|
|
235
|
+
| `client.run_bundle()` / `run_lineage()` | `GET /v1/runs/{id}/bundle` \| `/lineage` |
|
|
236
|
+
| `client.search()` (used by `research_search`) | `POST /v1/search` (exact+semantic, sectioned) |
|
|
237
|
+
|
|
238
|
+
## v0.4.0.0 ingestion fold-in (Phase 1)
|
|
239
|
+
|
|
240
|
+
Most earlier gaps are closed by Probe Research v0.4 (PR #13). Now wired:
|
|
241
|
+
|
|
242
|
+
- **Real metric dimensions.** `log_hw(..., device=3, host="n1")` sends `dimensions`
|
|
243
|
+
(fold #9); `log(..., dimensions={...})`. No more key-encoding.
|
|
244
|
+
- **Presign artifact upload.** `log_artifact(path=...)` runs presign → PUT to R2 →
|
|
245
|
+
confirm (fold #16), carrying `kind`/`meta` so byte uploads are labeled like
|
|
246
|
+
reference artifacts (Harbor-ownership Phase 0). Fails open to a reference on error.
|
|
247
|
+
- **Execution records.** `snapshot()` posts a content-addressed `execution-record`
|
|
248
|
+
(fold #7); `client.execution_record(...)`.
|
|
249
|
+
- **Asset registry.** `client.assets.register()` + `add_version()` + `resolve()`
|
|
250
|
+
(fold #5). The aspirational fork/propose/promote-candidate surface was dropped
|
|
251
|
+
(promotion tiers rejected upstream).
|
|
252
|
+
- **Experiment versions.** `client.experiment_version()` mints the immutable manifest
|
|
253
|
+
(fold #6). This replaces the removed run-level `promote`.
|
|
254
|
+
- **Lineage edges.** `client.add_edge()` / `run.edges()` (fold #2).
|
|
255
|
+
- **foreign_keys.** first-class on the ingest path (`run['foreign_keys']`, fold #8) and
|
|
256
|
+
surfaced on reads (`run.foreign_keys`, `run.short_id`).
|
|
257
|
+
- **Events read.** `client.events.list()` / `for_run()` (server-emitted lifecycle log).
|
|
258
|
+
Research notes moved to `client.notes.add()` (stored as `kind="note"` artifacts).
|
|
259
|
+
|
|
260
|
+
### Remaining
|
|
261
|
+
|
|
262
|
+
- **MCP semantic/KB search** is now wired to `POST /v1/search` (workspaces+kb
|
|
263
|
+
fold-in) with an honest keyword fallback on older backends; transcript
|
|
264
|
+
evidence is not indexed yet. **Session hooks** remain later work.
|
|
265
|
+
- **Harbor-native ownership Phases 1–3** (trial capture connector, capture-at-source,
|
|
266
|
+
platform surface): see `docs/2026-07-15-harbor-native-ownership-plan.md`.
|
|
267
|
+
|
|
268
|
+
(Previously listed here and since shipped: `RunPatch` `foreign_keys`/`env_ref` parity,
|
|
269
|
+
asset `materialize`, upload `kind`/`meta`, and server-side artifact list filters
|
|
270
|
+
`?kind=&step_from=&step_to=`.)
|
|
271
|
+
|
|
272
|
+
## Typed models (generated from the OpenAPI contract)
|
|
273
|
+
|
|
274
|
+
Request/response models are generated from the backend's OpenAPI schema, not
|
|
275
|
+
hand-written, so the client cannot silently drift from the contract. The write
|
|
276
|
+
paths (`log`/`span`/`log_artifact`/`ingest`/`assets`/`edges`/`execution-records`)
|
|
277
|
+
build their payloads through the generated models, so a renamed or removed field
|
|
278
|
+
fails client-side instead of as a server 422. `/ingest/v1/runs` is now declared in
|
|
279
|
+
the schema too (Probe Research PR #12), so the passive push is generated and validated
|
|
280
|
+
like every other path.
|
|
281
|
+
|
|
282
|
+
- `schema/openapi.json` - a snapshot of Probe Research's FastAPI schema.
|
|
283
|
+
- `src/probe/_generated/models.py` - generated, never hand-edited.
|
|
284
|
+
- `src/probe/models.py` - the stable import seam the SDK uses.
|
|
285
|
+
|
|
286
|
+
Refresh when the contract moves:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
make regen # dump-openapi (RESEARCH_OS=../../research-os) + gen-models
|
|
290
|
+
# or step by step:
|
|
291
|
+
RESEARCH_OS=/path/to/research-os python scripts/dump_openapi.py
|
|
292
|
+
python scripts/gen_models.py
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`RESEARCH_OS` points at a local checkout of the Probe Research backend source repo
|
|
296
|
+
(directory name `research-os`); it is only used to regenerate the schema snapshot.
|
|
297
|
+
|
|
298
|
+
## CLI grammar note
|
|
299
|
+
|
|
300
|
+
The CLI is built on **typer**. Connection flags are global and go *before* the
|
|
301
|
+
command: `probe --token probe_pat_x log RUN loss=0.1`. `probe login` also accepts them
|
|
302
|
+
directly (`probe login --token ...`).
|
|
303
|
+
|
|
304
|
+
## Tests
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
pytest # 29 mocked/unit tests + a real-git snapshot test; no live server
|
|
308
|
+
```
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# probe-research (`probe` SDK/CLI + `probe-research` plugin)
|
|
2
|
+
|
|
3
|
+
CLI + SDK client for **Probe Research**, Probe's experiment-tracking platform. It is a
|
|
4
|
+
thin client over the v3 ingestion contract (`CONTRACT.md` in the Probe Research backend).
|
|
5
|
+
Implemented experiment calls map onto real endpoints. Target asset-registry
|
|
6
|
+
methods are present as an explicit client contract but fail closed with a
|
|
7
|
+
capability error until the backend routes exist.
|
|
8
|
+
|
|
9
|
+
## Two client surfaces
|
|
10
|
+
|
|
11
|
+
Probe Research exposes experiment tracking through two separate surfaces over the same backend, for two different workflows:
|
|
12
|
+
|
|
13
|
+
- **`probe` — SDK + CLI (non-agent).** A Python library (`import probe`) and the `probe` command-line tool for integrating with existing setups and manual experimentation. Drop it into a training script or pipeline to record runs, metrics, spans, and artifacts. No agent required.
|
|
14
|
+
- **`probe-research` — plugin: skills + MCP (agent-centric).** Installed into a coding agent (e.g. Claude Code). Its skills teach the agent the experiment workflow, its read-only MCP server lets the agent query experiment state, and writes flow through the `probe` CLI. This is the surface for agent-driven research loops such as Anthrogen.
|
|
15
|
+
|
|
16
|
+
Same backend, two entry points: humans-in-code reach for the SDK/CLI; agents-in-the-loop use the plugin.
|
|
17
|
+
|
|
18
|
+
## Package boundaries
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
src/probe/
|
|
22
|
+
├── sdk/ # typed client, uploads, local capture, session adapter ABI
|
|
23
|
+
├── cli/ # `probe`: thin shell over the SDK
|
|
24
|
+
└── mcp/ # `probe-research-mcp`: strictly read-only tools and resources
|
|
25
|
+
skills/
|
|
26
|
+
├── track-experiment/
|
|
27
|
+
├── manage-research-asset/
|
|
28
|
+
└── publish-experiment/
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The SDK is the implementation. The CLI, MCP source adapter, future hooks,
|
|
32
|
+
Python experiments, and passive platform integrations all use it. CLI and SDK
|
|
33
|
+
therefore have capability parity; they differ only in ergonomics.
|
|
34
|
+
|
|
35
|
+
| Surface | SDK | CLI | Intended caller |
|
|
36
|
+
|---|---|---|---|
|
|
37
|
+
| Experiment upload | `Client.run`, `Run.log/span/log_artifact/snapshot/link/execute`, `Client.events`, `Client.promote` | `run`, `log`, `span`, `artifact`, `snapshot`, `link`, `exec`, `event`, `promote` | Researchers, agents, notebooks, training/platform code |
|
|
38
|
+
| Session adapter | `Client.sessions.attach/checkpoint/detach` | `probe hook session ...` | **Future deterministic hooks/broker only** |
|
|
39
|
+
| Asset read/selection | `Client.assets.resolve`, normally behind MCP | No normal read verb | Agent through read-only MCP |
|
|
40
|
+
| Asset effects | `Client.assets.materialize/fork/propose/promote` | `probe asset ...` | Agent/researcher after selecting an exact asset ref |
|
|
41
|
+
| Passive ingestion | `Client.ingest` | No convenience command yet | Install-once platform integration |
|
|
42
|
+
| Read plane | SDK reads used by `probe.mcp` | `get`/`bundle` diagnostics | MCP for agents; CLI for humans/scripts |
|
|
43
|
+
|
|
44
|
+
Session commands do not upload metrics or experiment outputs. They correlate a
|
|
45
|
+
coding-agent session with a run and checkpoint redacted transcript metadata.
|
|
46
|
+
Conversely, `event add` is normal experiment knowledge upload even when a hook
|
|
47
|
+
eventually calls it. No hooks are installed in this release.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install -e ".[dev]" # from this directory
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Auth
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
probe login # browser device flow (RFC 8628 + PKCE) — the default; nothing to paste
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Air-gap paste path: `probe login --token probe_pat_xxxxxxxx` (verified via `GET /v1/me`);
|
|
62
|
+
`probe login --endpoint-only --base-url …` saves the endpoint without minting a token.
|
|
63
|
+
Both write `~/.config/probe/config.json`.
|
|
64
|
+
Or set env: `PROBE_BASE_URL`, `PROBE_TOKEN` (user token, `/v1`), `PROBE_INGEST_TOKEN`
|
|
65
|
+
(ingest token, `/ingest`), `PROBE_HMAC_SECRET` (optional body-signature secret).
|
|
66
|
+
|
|
67
|
+
You can also skip `probe login` entirely: the first `client.run()` /
|
|
68
|
+
`probe run start` with no token triggers the same browser approval inline (TTY only)
|
|
69
|
+
and persists the result. Disable with `PROBE_AUTO_LOGIN=0`; headless/CI keeps the
|
|
70
|
+
crisp `AuthError` and should set `PROBE_TOKEN`.
|
|
71
|
+
|
|
72
|
+
The MCP server prefers `PROBE_MCP_TOKEN`, which should be a separately minted
|
|
73
|
+
read-only token. It falls back to `PROBE_TOKEN` for local development, but exposes
|
|
74
|
+
no mutation tools.
|
|
75
|
+
|
|
76
|
+
On rented compute (RunPod) with no standing config, the `/track-experiment` skill seeds
|
|
77
|
+
`PROBE_TOKEN` at session start.
|
|
78
|
+
|
|
79
|
+
## SDK (agent-driven / interactive)
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import probe
|
|
83
|
+
|
|
84
|
+
client = probe.Client() # resolves creds from env / `probe login`
|
|
85
|
+
|
|
86
|
+
run = client.run(experiment="dockq-sweep", hypothesis="temp 0.7 wins", name="run-1",
|
|
87
|
+
project="folding", source="runpod", external_id="rp-9931")
|
|
88
|
+
# …or with zero identity args: `client.run()` defaults experiment to the git repo /
|
|
89
|
+
# script name, name to a timestamp (the server adds a petname short_id), and a NEW
|
|
90
|
+
# experiment gets a marked "[auto] …" hypothesis composed from context. Set the real
|
|
91
|
+
# one later: client.update_experiment(id, hypothesis="…") / probe experiment set.
|
|
92
|
+
|
|
93
|
+
run.snapshot() # non-disruptive git + deps + GPU capture
|
|
94
|
+
run.link(wandb_run_id="abc123", s3_prefix="s3://x/y")
|
|
95
|
+
|
|
96
|
+
for step in range(100):
|
|
97
|
+
run.log({"loss": ..., "dockq": ...}, step=step) # POST /v1/runs/{id}/metrics
|
|
98
|
+
|
|
99
|
+
sid = run.span("rollout", name="rollout-0", step_index=1) # trajectory span
|
|
100
|
+
run.log_artifact("final.sif", uri="r2://bucket/final.sif", kind="artifact")
|
|
101
|
+
run.finish() # flushes spool, sets status+ended_at
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Structured knowledge and local process capture use the same SDK:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
run.execute(["python", "train.py", "--config", "dockq.yaml"])
|
|
108
|
+
client.events.add(run.id, "decision", "Use DockQ scorer v3", evidence_refs=["tool:91"])
|
|
109
|
+
report = client.check_run(run.id)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Data writes are **fail-open** by default: on failure they spool to disk
|
|
113
|
+
(`~/.local/state/probe/spool`) and return, never blocking the training loop. `run.finish()`
|
|
114
|
+
(or `probe flush`) replays the spool. Pass `strict=True` to make a write raise.
|
|
115
|
+
|
|
116
|
+
## SDK (install-once / passive push)
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
client.ingest(
|
|
120
|
+
experiment_slug="dockq", experiment_hypothesis="...",
|
|
121
|
+
run={"name": "r1", "source": "temporal", "external_id": "wf-1", "status": "running"},
|
|
122
|
+
metrics=[{"kind": "model", "key": "loss", "value": 0.5, "step_index": 1}],
|
|
123
|
+
batch_id="deadbeef", # idempotent redelivery
|
|
124
|
+
)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
One idempotent push (bearer ingest token + optional HMAC), keyed on
|
|
128
|
+
`(customer_id, source, external_id)`.
|
|
129
|
+
|
|
130
|
+
## CLI (`probe`)
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
RUN=$(probe run start --experiment dockq --hypothesis "temp 0.7 wins" --name run-1 \
|
|
134
|
+
--project folding --source runpod --external-id rp-9931)
|
|
135
|
+
probe snapshot $RUN
|
|
136
|
+
probe link $RUN --set wandb_run_id=abc --set gpu_job=rp-9931
|
|
137
|
+
probe log $RUN loss=0.42 dockq=0.71 --step 42
|
|
138
|
+
probe span add $RUN --type rollout --name rollout-0 --step 1
|
|
139
|
+
probe artifact add $RUN ./final.sif --kind artifact
|
|
140
|
+
probe event add $RUN --kind decision --statement "Use DockQ scorer v3" --evidence tool:91
|
|
141
|
+
probe exec $RUN -- python train.py --config dockq.yaml
|
|
142
|
+
probe run check $RUN
|
|
143
|
+
probe run end $RUN --status completed
|
|
144
|
+
probe bundle $RUN # read: run + series + artifacts
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The following commands are reserved for future hook configuration and are not
|
|
148
|
+
part of the normal researcher workflow:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
probe hook session attach RUN --session-id SESSION --transcript-path PATH --cwd DIR
|
|
152
|
+
probe hook session checkpoint RUN --session-id SESSION --transcript-path PATH --reason pre_compact
|
|
153
|
+
probe hook session detach RUN --session-id SESSION --reason session_end
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
They currently encode session links in `run.metadata.agent.sessions[]` and
|
|
157
|
+
transcript checkpoints as redacted local-reference artifacts. Until managed
|
|
158
|
+
artifact upload exists, transcript portability remains explicitly false.
|
|
159
|
+
|
|
160
|
+
## Read-only MCP server
|
|
161
|
+
|
|
162
|
+
Run the stdio server with `probe-research-mcp`. It exposes exactly six tools:
|
|
163
|
+
|
|
164
|
+
| Tool | Function |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `research_context` | Project/session bootstrap, prior experiments, active runs, capability warnings |
|
|
167
|
+
| `research_search` | One-index exact+semantic backend search (`POST /v1/search`, corpora: assets/procedures → files, documents → github+files, transcripts unsupported); keyword fallback on pre-search backends |
|
|
168
|
+
| `research_get` | Progressive card, handoff, reproduction, lineage, metrics, and artifact views |
|
|
169
|
+
| `research_compare` | Server-side comparison of runs, experiments, and future asset versions |
|
|
170
|
+
| `research_resolve` | Compatible asset resolution; honest partial result on API v3 |
|
|
171
|
+
| `research_trace_file` | Producer-consumer and cleanup lineage; partial until trace indexing lands |
|
|
172
|
+
|
|
173
|
+
MCP reads through the Probe Research API—never directly from Postgres or R2. Its
|
|
174
|
+
logical sources are control identity/tenant scope, the structured experiment
|
|
175
|
+
store, the asset/manifest registry, the one-index search door (`POST /v1/search`:
|
|
176
|
+
exact SQL channel + the KB engine's semantic channel; search capabilities are
|
|
177
|
+
discovered against the live backend with one cached probe), and object-store
|
|
178
|
+
resource pointers returned by the API. W&B, RunPod, Kubernetes,
|
|
179
|
+
Git, and local transcript paths are not live MCP sources; adapters upload their
|
|
180
|
+
identifiers and evidence first.
|
|
181
|
+
|
|
182
|
+
## Skills
|
|
183
|
+
|
|
184
|
+
- `experiment` mentally boxes result-producing work and uploads concise evidence.
|
|
185
|
+
- `manage-research-asset` resolves before create, reuses exact versions, forks
|
|
186
|
+
immutable bases, and proposes candidates without filename-based sprawl.
|
|
187
|
+
- `publish-experiment` requires explicit approval and refuses to imitate official
|
|
188
|
+
promotion when manifest/asset capabilities are unavailable.
|
|
189
|
+
|
|
190
|
+
Asset-reuse hooks are deliberately deferred. The track-experiment skill contains the
|
|
191
|
+
reuse-before-create rule; deterministic enforcement can be added later without
|
|
192
|
+
changing the SDK, CLI, MCP, or skill contracts.
|
|
193
|
+
|
|
194
|
+
## What maps to what (v3 endpoints)
|
|
195
|
+
|
|
196
|
+
| Client call | Endpoint |
|
|
197
|
+
|---|---|
|
|
198
|
+
| `client.run()` / `run.child()` | `POST /v1/experiments`, `POST /v1/experiments/{id}/runs` |
|
|
199
|
+
| `run.log()` / `run.log_hw()` | `POST /v1/runs/{id}/metrics` |
|
|
200
|
+
| `run.span()` / `run.step()` | `POST /v1/runs/{id}/spans` \| `/steps` |
|
|
201
|
+
| `run.log_artifact()` | `POST /v1/runs/{id}/artifacts` |
|
|
202
|
+
| `run.link()` | `PATCH /v1/runs/{id}` (merges `metadata.foreign_keys`) |
|
|
203
|
+
| `run.finish()` | `PATCH /v1/runs/{id}` |
|
|
204
|
+
| `client.events.add()` | `POST /v1/runs/{id}/artifacts` (`kind=research_event`, v3 encoding) |
|
|
205
|
+
| `client.sessions.*` | `PATCH /v1/runs/{id}` + transcript artifact metadata (hook ABI) |
|
|
206
|
+
| `client.ingest()` | `POST /ingest/v1/runs` |
|
|
207
|
+
| `client.run_bundle()` / `run_lineage()` | `GET /v1/runs/{id}/bundle` \| `/lineage` |
|
|
208
|
+
| `client.search()` (used by `research_search`) | `POST /v1/search` (exact+semantic, sectioned) |
|
|
209
|
+
|
|
210
|
+
## v0.4.0.0 ingestion fold-in (Phase 1)
|
|
211
|
+
|
|
212
|
+
Most earlier gaps are closed by Probe Research v0.4 (PR #13). Now wired:
|
|
213
|
+
|
|
214
|
+
- **Real metric dimensions.** `log_hw(..., device=3, host="n1")` sends `dimensions`
|
|
215
|
+
(fold #9); `log(..., dimensions={...})`. No more key-encoding.
|
|
216
|
+
- **Presign artifact upload.** `log_artifact(path=...)` runs presign → PUT to R2 →
|
|
217
|
+
confirm (fold #16), carrying `kind`/`meta` so byte uploads are labeled like
|
|
218
|
+
reference artifacts (Harbor-ownership Phase 0). Fails open to a reference on error.
|
|
219
|
+
- **Execution records.** `snapshot()` posts a content-addressed `execution-record`
|
|
220
|
+
(fold #7); `client.execution_record(...)`.
|
|
221
|
+
- **Asset registry.** `client.assets.register()` + `add_version()` + `resolve()`
|
|
222
|
+
(fold #5). The aspirational fork/propose/promote-candidate surface was dropped
|
|
223
|
+
(promotion tiers rejected upstream).
|
|
224
|
+
- **Experiment versions.** `client.experiment_version()` mints the immutable manifest
|
|
225
|
+
(fold #6). This replaces the removed run-level `promote`.
|
|
226
|
+
- **Lineage edges.** `client.add_edge()` / `run.edges()` (fold #2).
|
|
227
|
+
- **foreign_keys.** first-class on the ingest path (`run['foreign_keys']`, fold #8) and
|
|
228
|
+
surfaced on reads (`run.foreign_keys`, `run.short_id`).
|
|
229
|
+
- **Events read.** `client.events.list()` / `for_run()` (server-emitted lifecycle log).
|
|
230
|
+
Research notes moved to `client.notes.add()` (stored as `kind="note"` artifacts).
|
|
231
|
+
|
|
232
|
+
### Remaining
|
|
233
|
+
|
|
234
|
+
- **MCP semantic/KB search** is now wired to `POST /v1/search` (workspaces+kb
|
|
235
|
+
fold-in) with an honest keyword fallback on older backends; transcript
|
|
236
|
+
evidence is not indexed yet. **Session hooks** remain later work.
|
|
237
|
+
- **Harbor-native ownership Phases 1–3** (trial capture connector, capture-at-source,
|
|
238
|
+
platform surface): see `docs/2026-07-15-harbor-native-ownership-plan.md`.
|
|
239
|
+
|
|
240
|
+
(Previously listed here and since shipped: `RunPatch` `foreign_keys`/`env_ref` parity,
|
|
241
|
+
asset `materialize`, upload `kind`/`meta`, and server-side artifact list filters
|
|
242
|
+
`?kind=&step_from=&step_to=`.)
|
|
243
|
+
|
|
244
|
+
## Typed models (generated from the OpenAPI contract)
|
|
245
|
+
|
|
246
|
+
Request/response models are generated from the backend's OpenAPI schema, not
|
|
247
|
+
hand-written, so the client cannot silently drift from the contract. The write
|
|
248
|
+
paths (`log`/`span`/`log_artifact`/`ingest`/`assets`/`edges`/`execution-records`)
|
|
249
|
+
build their payloads through the generated models, so a renamed or removed field
|
|
250
|
+
fails client-side instead of as a server 422. `/ingest/v1/runs` is now declared in
|
|
251
|
+
the schema too (Probe Research PR #12), so the passive push is generated and validated
|
|
252
|
+
like every other path.
|
|
253
|
+
|
|
254
|
+
- `schema/openapi.json` - a snapshot of Probe Research's FastAPI schema.
|
|
255
|
+
- `src/probe/_generated/models.py` - generated, never hand-edited.
|
|
256
|
+
- `src/probe/models.py` - the stable import seam the SDK uses.
|
|
257
|
+
|
|
258
|
+
Refresh when the contract moves:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
make regen # dump-openapi (RESEARCH_OS=../../research-os) + gen-models
|
|
262
|
+
# or step by step:
|
|
263
|
+
RESEARCH_OS=/path/to/research-os python scripts/dump_openapi.py
|
|
264
|
+
python scripts/gen_models.py
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`RESEARCH_OS` points at a local checkout of the Probe Research backend source repo
|
|
268
|
+
(directory name `research-os`); it is only used to regenerate the schema snapshot.
|
|
269
|
+
|
|
270
|
+
## CLI grammar note
|
|
271
|
+
|
|
272
|
+
The CLI is built on **typer**. Connection flags are global and go *before* the
|
|
273
|
+
command: `probe --token probe_pat_x log RUN loss=0.1`. `probe login` also accepts them
|
|
274
|
+
directly (`probe login --token ...`).
|
|
275
|
+
|
|
276
|
+
## Tests
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
pytest # 29 mocked/unit tests + a real-git snapshot test; no live server
|
|
280
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Hosting the read-only Research OS MCP
|
|
2
|
+
|
|
3
|
+
The hosted MCP is a **stateless** streamable-HTTP service. It holds no tenant token —
|
|
4
|
+
each request carries the caller's read-scoped `ros_pat` as `Authorization: Bearer …`,
|
|
5
|
+
and the server forwards that identity to the Research OS API (tenant isolation is the
|
|
6
|
+
API's existing RLS). So it scales horizontally and stores nothing.
|
|
7
|
+
|
|
8
|
+
Endpoint: `https://mcp.research.prbe.ai/mcp` · health: `/healthz`.
|
|
9
|
+
|
|
10
|
+
## Run locally (dev / self-host)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install -e ".[mcp-http]"
|
|
14
|
+
PROBE_BASE_URL=https://api.research.prbe.ai probe-research-mcp-http # serves :8080/mcp
|
|
15
|
+
curl -s localhost:8080/healthz # {"status":"ok"}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or stdio (single-tenant): `probe-research-mcp`. It takes the token from
|
|
19
|
+
`PROBE_MCP_TOKEN`, falling back to the `mcp_token` that `probe mcp token set` stores.
|
|
20
|
+
(The pre-rename `ROS_MCP_TOKEN` still works, deprecated, with a warning.)
|
|
21
|
+
|
|
22
|
+
## Deploy to DOKS (Phase B — production; gated)
|
|
23
|
+
|
|
24
|
+
Prereqs already on the `research` cluster: ingress-nginx, cert-manager (`letsencrypt-prod`).
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# 1. build + push the image (GHCR)
|
|
28
|
+
docker build -f deploy/mcp/Dockerfile -t ghcr.io/prbe-ai/research-os-mcp:0.5.0 .
|
|
29
|
+
docker push ghcr.io/prbe-ai/research-os-mcp:0.5.0
|
|
30
|
+
|
|
31
|
+
# 2. DNS: point mcp.research.prbe.ai at the same ingress LB IP as api.research.prbe.ai
|
|
32
|
+
|
|
33
|
+
# 3. apply
|
|
34
|
+
kubectl apply -f deploy/mcp/k8s.yaml
|
|
35
|
+
kubectl -n research rollout status deploy/research-os-mcp
|
|
36
|
+
|
|
37
|
+
# 4. verify (cert may take a minute)
|
|
38
|
+
curl -s https://mcp.research.prbe.ai/healthz
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Notes:
|
|
42
|
+
- `k8s.yaml` points `PROBE_BASE_URL` at the in-cluster API service to avoid an LB hairpin;
|
|
43
|
+
swap to `https://api.research.prbe.ai` if you prefer the public endpoint.
|
|
44
|
+
- No secrets in the manifest — auth is per-request. Rate-limit at the ingress if needed.
|
|
45
|
+
- This is a **production change**; do it only with explicit go-ahead.
|