Skip to content

Commit a54ab89

Browse files
committed
feat: expose mcp contributor docs resources
1 parent 12865e2 commit a54ab89

11 files changed

Lines changed: 161 additions & 14 deletions

File tree

docs/backlog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ Evidence:
6969
- [Clean-download release evidence](releases/v0.1.1-evidence.md)
7070
- [Release verification guide](release-verification.md)
7171
- [CLI doctor troubleshooting guide](cli-doctor-troubleshooting.md)
72+
- [Read-only MCP contributor docs resources](mcp.md)
7273

7374
Do not create new first-release tasks unless a future release target changes
7475
artifact requirements or public safety claims.

docs/contributor-issue-board.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ Use the GitHub labels when choosing work:
2020

2121
## Help Wanted
2222

23-
- [#23 Expand read-only MCP strategy resources](https://github.com/zero-intel/zero/issues/23)
2423
- [#24 Design public Network empty and stale states](https://github.com/zero-intel/zero/issues/24)
2524

2625
## Completed Seed Issues
2726

27+
- [#23 Expand read-only MCP strategy resources](https://github.com/zero-intel/zero/issues/23) -
28+
delivered in [ZERO MCP Server](mcp.md).
2829
- [#25 Add release evidence reader docs](https://github.com/zero-intel/zero/issues/25) -
2930
delivered in [Release Verification Guide](release-verification.md).
3031
- [#20 Add CLI doctor troubleshooting examples](https://github.com/zero-intel/zero/issues/20) -

docs/launch-issues.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ Labels: `help wanted`, `mcp`, `docs`, `agent-eligible`
118118

119119
GitHub: [#23](https://github.com/zero-intel/zero/issues/23)
120120

121+
Status: delivered in [ZERO MCP Server](mcp.md).
122+
121123
Improve the read-only MCP resources so coding agents can discover strategy
122124
runner docs, strategy plugin docs, and market-data adapter docs without reading
123125
the entire repository.

docs/launch-scorecard.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ reserved for ZERO Intelligence.
6565
SBOM/provenance metadata, Homebrew formula drift, and clean-download evidence
6666
- CLI doctor troubleshooting guide for missing tokens, stopped paper API, and
6767
fail-closed live preflight warnings
68+
- Read-only MCP markdown resources for strategy runner, strategy plugin, and
69+
market-data adapter contributor docs
6870
- Public boundary audit from the private repo
6971

7072
## Paper-Only

docs/llms-full.txt

Lines changed: 17 additions & 6 deletions
Large diffs are not rendered by default.

docs/mcp.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ status, health, journal tail, rejection audit, local memory snapshots and stats,
77
genesis proposal classifications, evolve gate status, research command-chain
88
reports, the public decision stack, immune status, deterministic backtest
99
summary, hash-only evidence bundle, safety catalog, and the demo proof-pack
10-
manifest.
10+
manifest. It also exposes contributor-facing strategy runner, strategy plugin,
11+
and market-data adapter docs as markdown resources so coding agents can find
12+
the right extension path without scanning the full repository.
1113

1214
It does not expose live execution, order placement, approval, wallet, secret, or
1315
venue-write tools. The server is for inspection and local development until live
@@ -52,7 +54,8 @@ stats, inspect plan-only genesis proposals, inspect paper-only evolve gates,
5254
inspect paper-only research reports, inspect the lens/layer/modifier decision
5355
stack, inspect production-parity OODA reports, inspect
5456
journal/rejection/immune/evidence/backtest surfaces, list resources, and read
55-
the proof pack without gaining any live execution capability.
57+
the proof pack and contributor docs without gaining any live execution
58+
capability.
5659

5760
## Tools
5861

@@ -105,6 +108,9 @@ All tools declare `canPlaceOrders=false`, `canChangeRuntimeState=false`, and
105108
| `zero://backtest/report` | Deterministic paper backtest report without PnL claims. |
106109
| `zero://evidence/bundle` | Hash-only public-safe evidence bundle. |
107110
| `zero://mcp/safety` | Safety classification for every public MCP tool. |
111+
| `zero://docs/strategy-runner` | Markdown docs for declarative paper strategy runners. |
112+
| `zero://docs/strategy-plugin` | Markdown docs for deterministic paper strategy plugins. |
113+
| `zero://docs/market-data-adapters` | Markdown docs for deterministic market-data adapters. |
108114

109115
## Smoke Contract
110116

docs/mcp/transcript.jsonl

Lines changed: 4 additions & 1 deletion
Large diffs are not rendered by default.

docs/private-engine-capability-gap-audit.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,12 @@ Exit gate:
249249

250250
Current public status:
251251

252-
- `zero-mcp` now exposes 20 read-only tools and 19 resources for strategies,
252+
- `zero-mcp` now exposes 20 read-only tools and 22 resources for strategies,
253253
runtime status, health, paper results, positions, journal tail, rejection
254254
audit, proof pack, memory snapshot/stats, genesis proposals, evolve status,
255255
research report, decision stack, immune status, backtest report, evidence
256-
bundle, and safety catalog.
256+
bundle, safety catalog, and contributor docs for strategy runners, strategy
257+
plugins, and market-data adapters.
257258
- Every public tool declares `safetyClass: read_only_public`,
258259
`canPlaceOrders: false`, `canChangeRuntimeState: false`, and
259260
`canReadSecrets: false`.

engine/src/zero_engine/mcp.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,44 @@ def genesis_proposals_path() -> Path:
237237
return repo_root() / "examples" / "genesis" / "proposals.jsonl"
238238

239239

240+
DOC_RESOURCES: tuple[JsonMap, ...] = (
241+
{
242+
"uri": "zero://docs/strategy-runner",
243+
"name": "Strategy Runner Docs",
244+
"description": "Read-only contributor docs for declarative paper strategy runners.",
245+
"path": "examples/strategy-runner/README.md",
246+
"fallback": (
247+
"# Strategy Runner Docs\n\n"
248+
"Declarative strategy runners are paper-only examples for coding agents and "
249+
"contributors. Use `examples/strategy-runner/close-strength.yaml` as the "
250+
"reference fixture and keep new runners deterministic.\n"
251+
),
252+
},
253+
{
254+
"uri": "zero://docs/strategy-plugin",
255+
"name": "Strategy Plugin Docs",
256+
"description": "Read-only contributor docs for deterministic paper strategy plugins.",
257+
"path": "examples/strategy-plugin/README.md",
258+
"fallback": (
259+
"# Strategy Plugin Docs\n\n"
260+
"Strategy plugins are the smallest paper-only contributor path. Keep plugin "
261+
"fixtures deterministic, avoid credentials, and include a focused smoke test.\n"
262+
),
263+
},
264+
{
265+
"uri": "zero://docs/market-data-adapters",
266+
"name": "Market Data Adapter Docs",
267+
"description": "Read-only contributor docs for deterministic market-data adapters.",
268+
"path": "examples/market-data-adapter/README.md",
269+
"fallback": (
270+
"# Market Data Adapter Docs\n\n"
271+
"Market-data adapters should load public-safe deterministic fixtures for tests "
272+
"and examples. Do not add exchange credentials or live-only dependencies.\n"
273+
),
274+
},
275+
)
276+
277+
240278
def load_demo_scenario() -> Any:
241279
root = find_repo_root()
242280
if root is None:
@@ -746,7 +784,7 @@ def tool_definitions() -> list[JsonMap]:
746784

747785

748786
def resource_definitions() -> list[JsonMap]:
749-
return [
787+
resources = [
750788
{
751789
"uri": "zero://paper/scenario",
752790
"name": "Bundled Paper Scenario",
@@ -862,6 +900,16 @@ def resource_definitions() -> list[JsonMap]:
862900
"mimeType": "application/json",
863901
},
864902
]
903+
resources.extend(
904+
{
905+
"uri": str(resource["uri"]),
906+
"name": str(resource["name"]),
907+
"description": str(resource["description"]),
908+
"mimeType": "text/markdown",
909+
}
910+
for resource in DOC_RESOURCES
911+
)
912+
return resources
865913

866914

867915
def read_resource(uri: str) -> str:
@@ -906,6 +954,19 @@ def read_resource(uri: str) -> str:
906954
return json.dumps(get_evidence_bundle(), indent=2, sort_keys=True)
907955
if uri == "zero://mcp/safety":
908956
return json.dumps(safety_catalog(), indent=2, sort_keys=True)
957+
for resource in DOC_RESOURCES:
958+
if uri == resource["uri"]:
959+
root = find_repo_root()
960+
if root is None:
961+
return str(resource["fallback"])
962+
return (root / str(resource["path"])).read_text(encoding="utf-8")
963+
raise KeyError(uri)
964+
965+
966+
def resource_mime_type(uri: str) -> str:
967+
for resource in resource_definitions():
968+
if resource["uri"] == uri:
969+
return str(resource["mimeType"])
909970
raise KeyError(uri)
910971

911972

@@ -969,11 +1030,12 @@ def handle_request(request: JsonMap) -> JsonMap | None:
9691030
uri = str(params.get("uri", ""))
9701031
try:
9711032
text = read_resource(uri)
1033+
mime_type = resource_mime_type(uri)
9721034
except KeyError:
9731035
return error_response(request_id, -32602, f"unknown ZERO resource: {uri}")
9741036
return result_response(
9751037
request_id,
976-
{"contents": [{"uri": uri, "mimeType": "application/json", "text": text}]},
1038+
{"contents": [{"uri": uri, "mimeType": mime_type, "text": text}]},
9771039
)
9781040
return error_response(request_id, -32601, f"method not found: {method}")
9791041

engine/tests/test_mcp.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ def test_resources_list_and_read() -> None:
116116
"zero://backtest/report",
117117
"zero://evidence/bundle",
118118
"zero://mcp/safety",
119+
"zero://docs/strategy-runner",
120+
"zero://docs/strategy-plugin",
121+
"zero://docs/market-data-adapters",
119122
}
120123
assert read is not None
121124
proof = json.loads(read["result"]["contents"][0]["text"])
@@ -138,6 +141,29 @@ def test_resources_list_and_read() -> None:
138141
assert network_proof["verification"]["ok"] is True
139142

140143

144+
def test_strategy_docs_resources_are_markdown_and_read_only() -> None:
145+
for uri, expected in {
146+
"zero://docs/strategy-runner": "Strategy Runner",
147+
"zero://docs/strategy-plugin": "Strategy Plugin",
148+
"zero://docs/market-data-adapters": "Market Data Adapter",
149+
}.items():
150+
response = mcp.handle_request(
151+
{
152+
"jsonrpc": "2.0",
153+
"id": 52,
154+
"method": "resources/read",
155+
"params": {"uri": uri},
156+
}
157+
)
158+
159+
assert response is not None
160+
content = response["result"]["contents"][0]
161+
assert content["mimeType"] == "text/markdown"
162+
assert expected in content["text"]
163+
assert "sk_live_" not in content["text"].lower()
164+
assert "0x1234567890" not in content["text"].lower()
165+
166+
141167
def test_memory_snapshot_is_public_safe() -> None:
142168
called = mcp.handle_request(
143169
{
@@ -436,6 +462,7 @@ def test_installed_package_fallback_stays_read_only(monkeypatch) -> None:
436462
evidence = mcp.get_evidence_bundle()
437463
catalog = mcp.safety_catalog()
438464
scenario_text = mcp.read_resource("zero://paper/scenario")
465+
strategy_runner_docs = mcp.read_resource("zero://docs/strategy-runner")
439466

440467
assert paper["mode"] == "paper"
441468
assert paper["fills"] == 2
@@ -463,3 +490,4 @@ def test_installed_package_fallback_stays_read_only(monkeypatch) -> None:
463490
assert evidence["paper_only"] is True
464491
assert catalog["risk_increasing_tools"] == []
465492
assert "paper-launch-smoke" in scenario_text
493+
assert "Strategy Runner" in strategy_runner_docs

0 commit comments

Comments
 (0)