mrpd 0.1.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 (57) hide show
  1. mrpd-0.1.0/MANIFEST.in +1 -0
  2. mrpd-0.1.0/PKG-INFO +104 -0
  3. mrpd-0.1.0/README.md +88 -0
  4. mrpd-0.1.0/mrpd/__init__.py +1 -0
  5. mrpd-0.1.0/mrpd/adapters/__init__.py +1 -0
  6. mrpd-0.1.0/mrpd/api/app.py +8 -0
  7. mrpd-0.1.0/mrpd/api/routes.py +200 -0
  8. mrpd-0.1.0/mrpd/cli.py +182 -0
  9. mrpd-0.1.0/mrpd/commands/__init__.py +1 -0
  10. mrpd-0.1.0/mrpd/commands/bridge_mcp.py +189 -0
  11. mrpd-0.1.0/mrpd/commands/bridge_openapi.py +279 -0
  12. mrpd-0.1.0/mrpd/commands/init_provider.py +176 -0
  13. mrpd-0.1.0/mrpd/commands/mrpify_mcp.py +80 -0
  14. mrpd-0.1.0/mrpd/commands/mrpify_openapi.py +80 -0
  15. mrpd-0.1.0/mrpd/commands/publish.py +102 -0
  16. mrpd-0.1.0/mrpd/commands/route.py +126 -0
  17. mrpd-0.1.0/mrpd/commands/run.py +169 -0
  18. mrpd-0.1.0/mrpd/commands/serve.py +13 -0
  19. mrpd-0.1.0/mrpd/commands/validate.py +71 -0
  20. mrpd-0.1.0/mrpd/core/__init__.py +1 -0
  21. mrpd-0.1.0/mrpd/core/artifacts.py +39 -0
  22. mrpd-0.1.0/mrpd/core/config.py +26 -0
  23. mrpd-0.1.0/mrpd/core/defaults.py +7 -0
  24. mrpd-0.1.0/mrpd/core/envelopes.py +29 -0
  25. mrpd-0.1.0/mrpd/core/errors.py +37 -0
  26. mrpd-0.1.0/mrpd/core/evidence.py +17 -0
  27. mrpd-0.1.0/mrpd/core/models.py +37 -0
  28. mrpd-0.1.0/mrpd/core/provider.py +100 -0
  29. mrpd-0.1.0/mrpd/core/registry.py +115 -0
  30. mrpd-0.1.0/mrpd/core/schema.py +64 -0
  31. mrpd-0.1.0/mrpd/core/scoring.py +92 -0
  32. mrpd-0.1.0/mrpd/core/util.py +27 -0
  33. mrpd-0.1.0/mrpd/spec/__init__.py +1 -0
  34. mrpd-0.1.0/mrpd/spec/fixtures/README.md +8 -0
  35. mrpd-0.1.0/mrpd/spec/fixtures/invalid/bad_msg_type.json +8 -0
  36. mrpd-0.1.0/mrpd/spec/fixtures/invalid/missing_required_fields.json +7 -0
  37. mrpd-0.1.0/mrpd/spec/fixtures/valid/discover.json +13 -0
  38. mrpd-0.1.0/mrpd/spec/fixtures/valid/error.json +13 -0
  39. mrpd-0.1.0/mrpd/spec/fixtures/valid/offer.json +20 -0
  40. mrpd-0.1.0/mrpd/spec/schemas/envelope.schema.json +102 -0
  41. mrpd-0.1.0/mrpd/spec/schemas/manifest.schema.json +52 -0
  42. mrpd-0.1.0/mrpd/spec/schemas/payloads/discover.schema.json +23 -0
  43. mrpd-0.1.0/mrpd/spec/schemas/payloads/error.schema.json +15 -0
  44. mrpd-0.1.0/mrpd/spec/schemas/payloads/evidence.schema.json +17 -0
  45. mrpd-0.1.0/mrpd/spec/schemas/payloads/execute.schema.json +14 -0
  46. mrpd-0.1.0/mrpd/spec/schemas/payloads/negotiate.schema.json +15 -0
  47. mrpd-0.1.0/mrpd/spec/schemas/payloads/offer.schema.json +30 -0
  48. mrpd-0.1.0/mrpd/spec/schemas/types/artifact.schema.json +15 -0
  49. mrpd-0.1.0/mrpd/spec/schemas/types/input.schema.json +20 -0
  50. mrpd-0.1.0/mrpd.egg-info/PKG-INFO +104 -0
  51. mrpd-0.1.0/mrpd.egg-info/SOURCES.txt +55 -0
  52. mrpd-0.1.0/mrpd.egg-info/dependency_links.txt +1 -0
  53. mrpd-0.1.0/mrpd.egg-info/entry_points.txt +2 -0
  54. mrpd-0.1.0/mrpd.egg-info/requires.txt +7 -0
  55. mrpd-0.1.0/mrpd.egg-info/top_level.txt +1 -0
  56. mrpd-0.1.0/pyproject.toml +31 -0
  57. mrpd-0.1.0/setup.cfg +4 -0
mrpd-0.1.0/MANIFEST.in ADDED
@@ -0,0 +1 @@
1
+ recursive-include mrpd/spec *.json *.md
mrpd-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: mrpd
3
+ Version: 0.1.0
4
+ Summary: Moltrouter Protocol Daemon: serve, route, bridge, validate
5
+ Author: Thor
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: fastapi>=0.110
10
+ Requires-Dist: uvicorn[standard]>=0.27
11
+ Requires-Dist: pydantic>=2.6
12
+ Requires-Dist: httpx>=0.27
13
+ Requires-Dist: typer>=0.12
14
+ Requires-Dist: pyyaml>=6.0
15
+ Requires-Dist: jsonschema>=4.21
16
+
17
+ # mrpd — Moltrouter Protocol Daemon
18
+
19
+ One installable package that can:
20
+ - **serve** MRP endpoints for local tools (`mrpd serve`)
21
+ - **route** intents as a client (`mrpd route ...`) (v0: registry query + ranking)
22
+ - **bridge** other tool ecosystems (OpenAPI/MCP) into MRP (`mrpd bridge ...`)
23
+ - **mrpify** existing systems with a guided flow (`mrpd mrpify ...`)
24
+ - **validate** MRP envelopes against schemas/fixtures (`mrpd validate`) (working)
25
+
26
+ ## Status
27
+ Minimal server + validation are working.
28
+
29
+ Implemented:
30
+ - Bundled canonical JSON Schemas + fixtures
31
+ - `mrpd validate` (including `--fixtures`)
32
+ - `mrpd route` (v0: query + rank + fetch manifests)
33
+ - `mrpd bridge openapi` + `mrpd mrpify openapi`
34
+ - `mrpd bridge mcp` (scaffold) + `mrpd mrpify mcp`
35
+
36
+ Planned next:
37
+ - negotiate/execute
38
+ - MCP bridge execute (stdio)
39
+
40
+ ## Dev
41
+ ```bash
42
+ python -m venv .venv
43
+ . .venv/bin/activate # (Windows PowerShell: .\.venv\Scripts\Activate.ps1)
44
+ pip install -e .
45
+
46
+ mrpd serve --reload
47
+ ```
48
+
49
+ ## Bridge / Mrpify (v0)
50
+ OpenAPI:
51
+ ```bash
52
+ mrpd bridge openapi --spec openapi.yaml --out-dir ./mrp-openapi-bridge
53
+ mrpd mrpify openapi --spec openapi.yaml --out-dir ./mrp-openapi-bridge \
54
+ --provider-id service:openapi/bridge --public-base-url https://YOUR_DOMAIN
55
+ ```
56
+
57
+ MCP (stdio scaffold):
58
+ ```bash
59
+ mrpd bridge mcp --tools-json tools.json --mcp-command node --mcp-arg server.js
60
+ mrpd mrpify mcp --tools-json tools.json --mcp-command node --mcp-arg server.js \
61
+ --provider-id service:mcp/bridge --public-base-url https://YOUR_DOMAIN
62
+ ```
63
+
64
+ ## Validate
65
+ Run the bundled conformance fixtures:
66
+ ```bash
67
+ mrpd validate --fixtures
68
+ ```
69
+
70
+ Validate a single envelope JSON file:
71
+ ```bash
72
+ mrpd validate --path path/to/envelope.json
73
+ ```
74
+
75
+ ## Route (v0)
76
+ `mrpd route` queries a registry and prints ranked candidates. **Registry entries must include `manifest_url`.**
77
+
78
+ Defaults:
79
+ - Registry base URL: `https://www.moltrouter.dev`
80
+ - No raw bootstrap fallback is used unless explicitly configured (see below).
81
+
82
+ Examples:
83
+ ```bash
84
+ mrpd route "inspect router capability" --capability router --policy no_pii --limit 5
85
+ ```
86
+
87
+ Optional bootstrap (mostly for offline/dev):
88
+
89
+ - Env var: `MRP_BOOTSTRAP_REGISTRY_RAW` (supports `file://...`)
90
+ - Or CLI: `mrpd route ... --bootstrap-raw file:///C:/path/to/registry.json`
91
+
92
+ Example:
93
+ ```bash
94
+ mrpd route "inspect router capability" --capability router --policy no_pii \
95
+ --bootstrap-raw "file:///C:/path/to/registry.json"
96
+ ```
97
+
98
+ ## Endpoints (initial)
99
+ - `GET /.well-known/mrp.json`
100
+ - `GET /mrp/manifest`
101
+ - `POST /mrp/hello`
102
+ - `POST /mrp/discover`
103
+ - `POST /mrp/negotiate`
104
+ - `POST /mrp/execute`
mrpd-0.1.0/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # mrpd — Moltrouter Protocol Daemon
2
+
3
+ One installable package that can:
4
+ - **serve** MRP endpoints for local tools (`mrpd serve`)
5
+ - **route** intents as a client (`mrpd route ...`) (v0: registry query + ranking)
6
+ - **bridge** other tool ecosystems (OpenAPI/MCP) into MRP (`mrpd bridge ...`)
7
+ - **mrpify** existing systems with a guided flow (`mrpd mrpify ...`)
8
+ - **validate** MRP envelopes against schemas/fixtures (`mrpd validate`) (working)
9
+
10
+ ## Status
11
+ Minimal server + validation are working.
12
+
13
+ Implemented:
14
+ - Bundled canonical JSON Schemas + fixtures
15
+ - `mrpd validate` (including `--fixtures`)
16
+ - `mrpd route` (v0: query + rank + fetch manifests)
17
+ - `mrpd bridge openapi` + `mrpd mrpify openapi`
18
+ - `mrpd bridge mcp` (scaffold) + `mrpd mrpify mcp`
19
+
20
+ Planned next:
21
+ - negotiate/execute
22
+ - MCP bridge execute (stdio)
23
+
24
+ ## Dev
25
+ ```bash
26
+ python -m venv .venv
27
+ . .venv/bin/activate # (Windows PowerShell: .\.venv\Scripts\Activate.ps1)
28
+ pip install -e .
29
+
30
+ mrpd serve --reload
31
+ ```
32
+
33
+ ## Bridge / Mrpify (v0)
34
+ OpenAPI:
35
+ ```bash
36
+ mrpd bridge openapi --spec openapi.yaml --out-dir ./mrp-openapi-bridge
37
+ mrpd mrpify openapi --spec openapi.yaml --out-dir ./mrp-openapi-bridge \
38
+ --provider-id service:openapi/bridge --public-base-url https://YOUR_DOMAIN
39
+ ```
40
+
41
+ MCP (stdio scaffold):
42
+ ```bash
43
+ mrpd bridge mcp --tools-json tools.json --mcp-command node --mcp-arg server.js
44
+ mrpd mrpify mcp --tools-json tools.json --mcp-command node --mcp-arg server.js \
45
+ --provider-id service:mcp/bridge --public-base-url https://YOUR_DOMAIN
46
+ ```
47
+
48
+ ## Validate
49
+ Run the bundled conformance fixtures:
50
+ ```bash
51
+ mrpd validate --fixtures
52
+ ```
53
+
54
+ Validate a single envelope JSON file:
55
+ ```bash
56
+ mrpd validate --path path/to/envelope.json
57
+ ```
58
+
59
+ ## Route (v0)
60
+ `mrpd route` queries a registry and prints ranked candidates. **Registry entries must include `manifest_url`.**
61
+
62
+ Defaults:
63
+ - Registry base URL: `https://www.moltrouter.dev`
64
+ - No raw bootstrap fallback is used unless explicitly configured (see below).
65
+
66
+ Examples:
67
+ ```bash
68
+ mrpd route "inspect router capability" --capability router --policy no_pii --limit 5
69
+ ```
70
+
71
+ Optional bootstrap (mostly for offline/dev):
72
+
73
+ - Env var: `MRP_BOOTSTRAP_REGISTRY_RAW` (supports `file://...`)
74
+ - Or CLI: `mrpd route ... --bootstrap-raw file:///C:/path/to/registry.json`
75
+
76
+ Example:
77
+ ```bash
78
+ mrpd route "inspect router capability" --capability router --policy no_pii \
79
+ --bootstrap-raw "file:///C:/path/to/registry.json"
80
+ ```
81
+
82
+ ## Endpoints (initial)
83
+ - `GET /.well-known/mrp.json`
84
+ - `GET /mrp/manifest`
85
+ - `POST /mrp/hello`
86
+ - `POST /mrp/discover`
87
+ - `POST /mrp/negotiate`
88
+ - `POST /mrp/execute`
@@ -0,0 +1 @@
1
+ __all__ = []
@@ -0,0 +1 @@
1
+ __all__ = []
@@ -0,0 +1,8 @@
1
+ from __future__ import annotations
2
+
3
+ from fastapi import FastAPI
4
+
5
+ from mrpd.api.routes import router
6
+
7
+ app = FastAPI(title="mrpd (Moltrouter Protocol Daemon)")
8
+ app.include_router(router)
@@ -0,0 +1,200 @@
1
+ from __future__ import annotations
2
+
3
+ from fastapi import APIRouter
4
+
5
+ from mrpd.core.errors import mrp_error
6
+ from mrpd.core.schema import validate_envelope
7
+
8
+ router = APIRouter()
9
+
10
+
11
+ def response_envelope(envelope: dict, *, msg_type: str, payload: dict) -> dict:
12
+ """Build a response envelope.
13
+
14
+ IMPORTANT: response message ids must be new, and in_reply_to must reference
15
+ the triggering request msg_id.
16
+ """
17
+
18
+ from mrpd.core.util import utc_now_rfc3339
19
+ import uuid
20
+
21
+ sender_id = (envelope.get("sender") or {}).get("id")
22
+ req_msg_id = envelope.get("msg_id")
23
+
24
+ resp = {
25
+ "mrp_version": envelope.get("mrp_version", "0.1"),
26
+ "msg_id": str(uuid.uuid4()),
27
+ "msg_type": msg_type,
28
+ "timestamp": utc_now_rfc3339(),
29
+ "sender": {"id": "service:mrpd"},
30
+ "payload": payload,
31
+ }
32
+ if sender_id:
33
+ resp["receiver"] = {"id": sender_id}
34
+ if req_msg_id:
35
+ resp["in_reply_to"] = req_msg_id
36
+ return resp
37
+
38
+
39
+ @router.get("/.well-known/mrp.json")
40
+ async def well_known() -> dict:
41
+ return {
42
+ "mrp_version": "0.1",
43
+ "capabilities": ["summarize_url"],
44
+ "manifest_url": "/mrp/manifest",
45
+ }
46
+
47
+
48
+ @router.get("/mrp/manifest")
49
+ async def manifest() -> dict:
50
+ # Provider manifest for the built-in demo capability.
51
+ # NOTE: this endpoint returns relative URLs; clients may prefer absolute.
52
+ # Our `mrpd run` prefers manifests from registry entries (absolute endpoints).
53
+ return {
54
+ "capability_id": "capability:mrp/summarize_url",
55
+ "capability": "summarize_url",
56
+ "version": "0.1",
57
+ "tags": ["mrp", "summarize", "web"],
58
+ "inputs": [{"type": "url"}],
59
+ "outputs": [{"type": "markdown"}, {"type": "artifact"}],
60
+ "constraints": {"policy": ["no_pii"]},
61
+ "proofs_required": [],
62
+ "endpoints": {
63
+ "discover": "/mrp/discover",
64
+ "negotiate": "/mrp/negotiate",
65
+ "execute": "/mrp/execute",
66
+ },
67
+ }
68
+
69
+
70
+ @router.post("/mrp/hello")
71
+ async def hello(envelope: dict) -> dict:
72
+ try:
73
+ validate_envelope(envelope)
74
+ except Exception as e:
75
+ sender_id = (envelope.get("sender") or {}).get("id")
76
+ msg_id = envelope.get("msg_id")
77
+ return mrp_error(
78
+ msg_id=msg_id,
79
+ timestamp=envelope.get("timestamp"),
80
+ receiver_id=sender_id,
81
+ in_reply_to=msg_id,
82
+ code="MRP_INVALID_REQUEST",
83
+ message=str(e),
84
+ retryable=False,
85
+ )
86
+
87
+ return response_envelope(
88
+ envelope,
89
+ msg_type="HELLO",
90
+ payload={"ok": True, "schemas": ["0.1"]},
91
+ )
92
+
93
+
94
+ @router.post("/mrp/discover")
95
+ async def discover(envelope: dict) -> dict:
96
+ try:
97
+ validate_envelope(envelope)
98
+ except Exception as e:
99
+ sender_id = (envelope.get("sender") or {}).get("id")
100
+ msg_id = envelope.get("msg_id")
101
+ return mrp_error(
102
+ msg_id=msg_id,
103
+ timestamp=envelope.get("timestamp"),
104
+ receiver_id=sender_id,
105
+ in_reply_to=msg_id,
106
+ code="MRP_INVALID_REQUEST",
107
+ message=str(e),
108
+ retryable=False,
109
+ )
110
+
111
+ from mrpd.core.provider import offers_for_discover
112
+
113
+ offers = offers_for_discover(envelope.get("payload") or {})
114
+ return response_envelope(
115
+ envelope,
116
+ msg_type="OFFER",
117
+ payload={"offers": offers},
118
+ )
119
+
120
+
121
+ @router.post("/mrp/negotiate")
122
+ async def negotiate(envelope: dict) -> dict:
123
+ try:
124
+ validate_envelope(envelope)
125
+ except Exception as e:
126
+ sender_id = (envelope.get("sender") or {}).get("id")
127
+ msg_id = envelope.get("msg_id")
128
+ return mrp_error(
129
+ msg_id=msg_id,
130
+ timestamp=envelope.get("timestamp"),
131
+ receiver_id=sender_id,
132
+ in_reply_to=msg_id,
133
+ code="MRP_INVALID_REQUEST",
134
+ message=str(e),
135
+ retryable=False,
136
+ )
137
+
138
+ return response_envelope(
139
+ envelope,
140
+ msg_type="NEGOTIATE",
141
+ payload={"accepted": False, "reason": "not implemented"},
142
+ )
143
+
144
+
145
+ @router.post("/mrp/execute")
146
+ async def execute(envelope: dict) -> dict:
147
+ try:
148
+ validate_envelope(envelope)
149
+ except Exception as e:
150
+ sender_id = (envelope.get("sender") or {}).get("id")
151
+ msg_id = envelope.get("msg_id")
152
+ return mrp_error(
153
+ msg_id=msg_id,
154
+ timestamp=envelope.get("timestamp"),
155
+ receiver_id=sender_id,
156
+ in_reply_to=msg_id,
157
+ code="MRP_INVALID_REQUEST",
158
+ message=str(e),
159
+ retryable=False,
160
+ )
161
+
162
+ from mrpd.core.provider import execute_summarize_url
163
+
164
+ payload = envelope.get("payload") or {}
165
+ route_id = payload.get("route_id")
166
+ inputs = payload.get("inputs") or []
167
+ job_id = (payload.get("job") or {}).get("id")
168
+
169
+ try:
170
+ if route_id == "route:mrpd/summarize_url@0.1":
171
+ evidence = await execute_summarize_url(inputs)
172
+ else:
173
+ sender_id = (envelope.get("sender") or {}).get("id")
174
+ msg_id = envelope.get("msg_id")
175
+ return mrp_error(
176
+ msg_id=msg_id,
177
+ timestamp=envelope.get("timestamp"),
178
+ receiver_id=sender_id,
179
+ in_reply_to=msg_id,
180
+ code="MRP_INVALID_REQUEST",
181
+ message=f"Unknown route_id: {route_id}",
182
+ retryable=False,
183
+ )
184
+ except Exception as e:
185
+ sender_id = (envelope.get("sender") or {}).get("id")
186
+ msg_id = envelope.get("msg_id")
187
+ return mrp_error(
188
+ msg_id=msg_id,
189
+ timestamp=envelope.get("timestamp"),
190
+ receiver_id=sender_id,
191
+ in_reply_to=msg_id,
192
+ code="MRP_INTERNAL_ERROR",
193
+ message=str(e),
194
+ retryable=False,
195
+ )
196
+
197
+ response_payload = {"route_id": route_id, **evidence}
198
+ if job_id:
199
+ response_payload["job_id"] = job_id
200
+ return response_envelope(envelope, msg_type="EVIDENCE", payload=response_payload)
mrpd-0.1.0/mrpd/cli.py ADDED
@@ -0,0 +1,182 @@
1
+ from __future__ import annotations
2
+
3
+ import typer
4
+
5
+ from mrpd.commands.bridge_mcp import bridge_mcp
6
+ from mrpd.commands.bridge_openapi import bridge_openapi
7
+ from mrpd.commands.init_provider import init_provider
8
+ from mrpd.commands.mrpify_mcp import mrpify_mcp
9
+ from mrpd.commands.mrpify_openapi import mrpify_openapi
10
+ from mrpd.commands.publish import publish
11
+ from mrpd.commands.route import route
12
+ from mrpd.commands.run import run
13
+ from mrpd.commands.serve import serve
14
+ from mrpd.commands.validate import validate
15
+
16
+ app = typer.Typer(add_completion=False)
17
+
18
+ bridge_app = typer.Typer(help="Generate MRP provider wrappers (bridges)")
19
+ app.add_typer(bridge_app, name="bridge")
20
+
21
+ mrpify_app = typer.Typer(help="MRP-ify existing systems with a guided flow")
22
+ app.add_typer(mrpify_app, name="mrpify")
23
+
24
+
25
+ @app.command()
26
+ def version() -> None:
27
+ """Print version."""
28
+ typer.echo("mrpd 0.1.0")
29
+
30
+
31
+ @app.command(name="serve")
32
+ def serve_cmd(
33
+ host: str = typer.Option("127.0.0.1", "--host"),
34
+ port: int = typer.Option(8787, "--port"),
35
+ reload: bool = typer.Option(False, "--reload"),
36
+ ) -> None:
37
+ """Run the MRP HTTP server."""
38
+ serve(host=host, port=port, reload=reload)
39
+
40
+
41
+ @app.command(name="validate")
42
+ def validate_cmd(
43
+ path: str = typer.Option("-", "--path", help="JSON file path or '-' for stdin"),
44
+ fixtures: bool = typer.Option(False, "--fixtures", help="Validate bundled fixtures (valid must pass, invalid must fail)"),
45
+ ) -> None:
46
+ """Validate an MRP envelope against the bundled JSON Schemas."""
47
+ validate(path, fixtures=fixtures)
48
+
49
+
50
+ @app.command(name="route")
51
+ def route_cmd(
52
+ intent: str = typer.Argument(..., help="High-level intent (human text)"),
53
+ capability: str | None = typer.Option(None, "--capability", help="Capability filter"),
54
+ policy: str | None = typer.Option(None, "--policy", help="Policy filter"),
55
+ registry: str | None = typer.Option(None, "--registry", help="Registry base URL (default: https://www.moltrouter.dev)"),
56
+ bootstrap_raw: str | None = typer.Option(None, "--bootstrap-raw", help="Override fallback raw registry JSON (URL or file://path)"),
57
+ limit: int = typer.Option(10, "--limit", min=1, max=50),
58
+ ) -> None:
59
+ """Query registry + rank candidates for an intent."""
60
+ route(intent=intent, capability=capability, policy=policy, registry=registry, limit=limit, bootstrap_raw=bootstrap_raw)
61
+
62
+
63
+ @app.command(name="run")
64
+ def run_cmd(
65
+ intent: str = typer.Argument(..., help="High-level intent (human text)"),
66
+ url: str = typer.Option(..., "--url", help="URL input"),
67
+ capability: str = typer.Option("summarize_url", "--capability", help="Capability to request"),
68
+ policy: str | None = typer.Option(None, "--policy", help="Policy requirement"),
69
+ registry: str | None = typer.Option(None, "--registry", help="Registry base URL (default: https://www.moltrouter.dev)"),
70
+ manifest_url: str | None = typer.Option(None, "--manifest-url", help="Skip registry and use this provider manifest URL (useful for local testing)"),
71
+ max_tokens: int | None = typer.Option(None, "--max-tokens", help="Soft max context tokens (constraint hint)"),
72
+ max_cost: float | None = typer.Option(None, "--max-cost", help="Max cost (constraint hint)"),
73
+ ) -> None:
74
+ """End-to-end: DISCOVER -> EXECUTE against the best matching provider."""
75
+ run(intent=intent, url=url, capability=capability, policy=policy, registry=registry, manifest_url=manifest_url, max_tokens=max_tokens, max_cost=max_cost)
76
+
77
+
78
+ @app.command(name="publish")
79
+ def publish_cmd(
80
+ manifest_url: str = typer.Option(..., "--manifest-url", help="Provider manifest URL to self-register"),
81
+ registry: str | None = typer.Option(None, "--registry", help="Registry base URL (default: https://www.moltrouter.dev)"),
82
+ poll_seconds: float = typer.Option(5.0, "--poll-seconds", min=1.0, max=60.0),
83
+ ) -> None:
84
+ """Self-register a provider in the public registry using HTTP-01 challenge."""
85
+ publish(manifest_url=manifest_url, registry=registry, poll_seconds=poll_seconds)
86
+
87
+
88
+ @app.command(name="init-provider")
89
+ def init_provider_cmd(
90
+ out_dir: str = typer.Option("./mrp-provider", "--out-dir", help="Output directory"),
91
+ capability: str = typer.Option(..., "--capability", help="Capability name (e.g. summarize_url)"),
92
+ provider_id: str = typer.Option("service:example/provider", "--provider-id", help="Provider sender id"),
93
+ name: str = typer.Option("MRP Provider", "--name"),
94
+ description: str = typer.Option("", "--description"),
95
+ policy: list[str] = typer.Option([], "--policy", help="Policy strings (repeatable)")
96
+ ) -> None:
97
+ """Scaffold a minimal FastAPI MRP provider wrapper."""
98
+ init_provider(out_dir=out_dir, capability=capability, provider_id=provider_id, name=name, description=description, policy=policy)
99
+
100
+
101
+ @bridge_app.command(name="openapi")
102
+ def bridge_openapi_cmd(
103
+ spec: str = typer.Option(..., "--spec", help="Path to OpenAPI spec (json/yaml)"),
104
+ out_dir: str = typer.Option("./mrp-openapi-bridge", "--out-dir", help="Output directory"),
105
+ provider_id: str = typer.Option("service:openapi/bridge", "--provider-id", help="Provider sender id"),
106
+ backend_base_url: str | None = typer.Option(None, "--backend-base-url", help="Override OpenAPI servers[0].url"),
107
+ capability_prefix: str | None = typer.Option(None, "--capability-prefix", help="Prefix for generated capabilities (e.g. svc_)"),
108
+ ) -> None:
109
+ """Generate an MRP provider wrapper from an OpenAPI spec."""
110
+ bridge_openapi(spec=spec, out_dir=out_dir, provider_id=provider_id, backend_base_url=backend_base_url, capability_prefix=capability_prefix)
111
+
112
+
113
+ @bridge_app.command(name="mcp")
114
+ def bridge_mcp_cmd(
115
+ tools_json: str = typer.Option(..., "--tools-json", help="Path to MCP tools list JSON"),
116
+ out_dir: str = typer.Option("./mrp-mcp-bridge", "--out-dir", help="Output directory"),
117
+ provider_id: str = typer.Option("service:mcp/bridge", "--provider-id", help="Provider sender id"),
118
+ mcp_command: str = typer.Option(..., "--mcp-command", help="MCP server command (stdio)"),
119
+ mcp_args: list[str] = typer.Option([], "--mcp-arg", help="MCP server argument (repeatable)"),
120
+ ) -> None:
121
+ """Generate an MRP provider wrapper scaffold from an MCP tool list."""
122
+ bridge_mcp(tools_json=tools_json, out_dir=out_dir, provider_id=provider_id, mcp_command=mcp_command, mcp_args=mcp_args)
123
+
124
+
125
+ @mrpify_app.command(name="openapi")
126
+ def mrpify_openapi_cmd(
127
+ spec: str = typer.Option(..., "--spec", help="Path to OpenAPI spec (json/yaml)"),
128
+ out_dir: str = typer.Option("./mrp-openapi-bridge", "--out-dir", help="Output directory"),
129
+ provider_id: str = typer.Option("service:openapi/bridge", "--provider-id", help="Provider sender id"),
130
+ backend_base_url: str | None = typer.Option(None, "--backend-base-url", help="Override OpenAPI servers[0].url"),
131
+ capability_prefix: str | None = typer.Option(None, "--capability-prefix", help="Prefix for generated capabilities (e.g. svc_)"),
132
+ public_base_url: str | None = typer.Option(None, "--public-base-url", help="Deployed public base URL, e.g. https://api.example.com"),
133
+ publish_now: bool = typer.Option(False, "--publish", help="Run mrpd publish for each generated manifest URL (requires public HTTPS)"),
134
+ yes: bool = typer.Option(False, "--yes", help="Skip confirmation prompts"),
135
+ registry: str | None = typer.Option(None, "--registry", help="Registry base URL (default: https://www.moltrouter.dev)"),
136
+ poll_seconds: float = typer.Option(5.0, "--poll-seconds", min=1.0, max=60.0),
137
+ ) -> None:
138
+ """Guided flow: OpenAPI -> MRP bridge -> (optional) publish commands."""
139
+ mrpify_openapi(
140
+ spec=spec,
141
+ out_dir=out_dir,
142
+ provider_id=provider_id,
143
+ backend_base_url=backend_base_url,
144
+ capability_prefix=capability_prefix,
145
+ public_base_url=public_base_url,
146
+ do_publish=publish_now,
147
+ yes=yes,
148
+ registry=registry,
149
+ poll_seconds=poll_seconds,
150
+ )
151
+
152
+
153
+ @mrpify_app.command(name="mcp")
154
+ def mrpify_mcp_cmd(
155
+ tools_json: str = typer.Option(..., "--tools-json", help="Path to MCP tools list JSON"),
156
+ out_dir: str = typer.Option("./mrp-mcp-bridge", "--out-dir", help="Output directory"),
157
+ provider_id: str = typer.Option("service:mcp/bridge", "--provider-id", help="Provider sender id"),
158
+ mcp_command: str = typer.Option(..., "--mcp-command", help="MCP server command (stdio)"),
159
+ mcp_args: list[str] = typer.Option([], "--mcp-arg", help="MCP server argument (repeatable)"),
160
+ public_base_url: str | None = typer.Option(None, "--public-base-url", help="Deployed public base URL, e.g. https://api.example.com"),
161
+ publish_now: bool = typer.Option(False, "--publish", help="Run mrpd publish for each generated manifest URL (requires public HTTPS)"),
162
+ yes: bool = typer.Option(False, "--yes", help="Skip confirmation prompts"),
163
+ registry: str | None = typer.Option(None, "--registry", help="Registry base URL (default: https://www.moltrouter.dev)"),
164
+ poll_seconds: float = typer.Option(5.0, "--poll-seconds", min=1.0, max=60.0),
165
+ ) -> None:
166
+ """Guided flow: MCP -> MRP bridge -> (optional) publish commands."""
167
+ mrpify_mcp(
168
+ tools_json=tools_json,
169
+ out_dir=out_dir,
170
+ provider_id=provider_id,
171
+ mcp_command=mcp_command,
172
+ mcp_args=mcp_args,
173
+ public_base_url=public_base_url,
174
+ do_publish=publish_now,
175
+ yes=yes,
176
+ registry=registry,
177
+ poll_seconds=poll_seconds,
178
+ )
179
+
180
+
181
+ if __name__ == "__main__":
182
+ app()
@@ -0,0 +1 @@
1
+ __all__ = []