sdcmodeler 4.2.1__tar.gz → 4.3.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.
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/PKG-INFO +2 -1
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/README.md +1 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/pyproject.toml +1 -1
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler/__init__.py +1 -1
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler/mcp_server.py +41 -1
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler.egg-info/PKG-INFO +2 -1
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/tests/test_mcp_server.py +23 -1
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/LICENSE +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/setup.cfg +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler/cli.py +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler/client.py +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler/pricing.py +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler.egg-info/SOURCES.txt +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler.egg-info/dependency_links.txt +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler.egg-info/entry_points.txt +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler.egg-info/requires.txt +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/src/sdcmodeler.egg-info/top_level.txt +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/tests/test_cli.py +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/tests/test_client.py +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/tests/test_pricing.py +0 -0
- {sdcmodeler-4.2.1 → sdcmodeler-4.3.0}/tests/test_registry_launch.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sdcmodeler
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Summary: The modeler's client for SDCStudio: search the catalog, estimate, assemble and publish SDC4 data models, generate applications; a CLI and an MCP server over one client
|
|
5
5
|
Author: Axius SDC, Inc.
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -110,6 +110,7 @@ A JSON-RPC 2.0 stdio server, no SDK, the way sdcvalidator and sdcgovernance are
|
|
|
110
110
|
| `estimate_model` | what a tree would reuse, mint and cost; returns `estimate_id` | no |
|
|
111
111
|
| `build_model` | assemble, publish and generate (or `publish: false` for a draft); needs the `estimate_id` the person saw; refuses past the key's limit | yes |
|
|
112
112
|
| `job_status` | one job by id: status, step, cost so far, the model's links | no |
|
|
113
|
+
| `download_package` | a model's package ZIP with the key, including a model in a private project; builds a missing package first (free, once) unless `generate` is false | no |
|
|
113
114
|
| `generate_app` | generate and download the application; needs `confirm_charge`; 1,500 credits the first time, free after | yes |
|
|
114
115
|
| `wallet` | balance in credits and the key's policy | no |
|
|
115
116
|
|
|
@@ -84,6 +84,7 @@ A JSON-RPC 2.0 stdio server, no SDK, the way sdcvalidator and sdcgovernance are
|
|
|
84
84
|
| `estimate_model` | what a tree would reuse, mint and cost; returns `estimate_id` | no |
|
|
85
85
|
| `build_model` | assemble, publish and generate (or `publish: false` for a draft); needs the `estimate_id` the person saw; refuses past the key's limit | yes |
|
|
86
86
|
| `job_status` | one job by id: status, step, cost so far, the model's links | no |
|
|
87
|
+
| `download_package` | a model's package ZIP with the key, including a model in a private project; builds a missing package first (free, once) unless `generate` is false | no |
|
|
87
88
|
| `generate_app` | generate and download the application; needs `confirm_charge`; 1,500 credits the first time, free after | yes |
|
|
88
89
|
| `wallet` | balance in credits and the key's policy | no |
|
|
89
90
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sdcmodeler"
|
|
7
|
-
version = "4.
|
|
7
|
+
version = "4.3.0"
|
|
8
8
|
description = "The modeler's client for SDCStudio: search the catalog, estimate, assemble and publish SDC4 data models, generate applications; a CLI and an MCP server over one client"
|
|
9
9
|
license = "Apache-2.0"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -17,7 +17,7 @@ servers: sdcvalidator validates, sdcgovernance governs, sdcreader reads,
|
|
|
17
17
|
sdcmodeler models. It also names the concept SDCStudio already has: every user
|
|
18
18
|
is a Modeler, and the Modeler's projects scope every call.
|
|
19
19
|
"""
|
|
20
|
-
__version__ = "4.
|
|
20
|
+
__version__ = "4.3.0"
|
|
21
21
|
|
|
22
22
|
from sdcmodeler.client import ( # noqa: E402
|
|
23
23
|
AssemblyRefused,
|
|
@@ -157,6 +157,25 @@ TOOLS: list[dict[str, Any]] = [
|
|
|
157
157
|
),
|
|
158
158
|
"inputSchema": {"type": "object", "properties": {"job_ct_id": {"type": "string"}}, "required": ["job_ct_id"]},
|
|
159
159
|
},
|
|
160
|
+
{
|
|
161
|
+
"name": "download_package",
|
|
162
|
+
"description": (
|
|
163
|
+
"Download a model's package (the ZIP: schema, XML template and JSON instance of the "
|
|
164
|
+
"Governed Data Record, JSON-LD, HTML, RDF, SHACL, GQL) into the server's output directory, "
|
|
165
|
+
"with this key: it works for a model in a private project, such as the one build_model "
|
|
166
|
+
"just made, where get_model and the public catalog do not. When the model is published "
|
|
167
|
+
"and its package not yet built, it builds it first (free, once; it needs the right to "
|
|
168
|
+
"publish the model); generate false only downloads. Free."
|
|
169
|
+
),
|
|
170
|
+
"inputSchema": {
|
|
171
|
+
"type": "object",
|
|
172
|
+
"properties": {
|
|
173
|
+
"ct_id": {"type": "string"},
|
|
174
|
+
"generate": {"type": "boolean", "default": True},
|
|
175
|
+
},
|
|
176
|
+
"required": ["ct_id"],
|
|
177
|
+
},
|
|
178
|
+
},
|
|
160
179
|
{
|
|
161
180
|
"name": "generate_app",
|
|
162
181
|
"description": (
|
|
@@ -290,6 +309,25 @@ def _handle_job_status(args: dict) -> Any:
|
|
|
290
309
|
return get_client().job_status(_string(args, "job_ct_id", required=True))
|
|
291
310
|
|
|
292
311
|
|
|
312
|
+
def _handle_download_package(args: dict) -> Any:
|
|
313
|
+
ct_id = _string(args, "ct_id", required=True)
|
|
314
|
+
generate = args.get("generate", True)
|
|
315
|
+
if not isinstance(generate, bool):
|
|
316
|
+
raise ValueError("`generate` must be a boolean.")
|
|
317
|
+
c = get_client()
|
|
318
|
+
built_now = False
|
|
319
|
+
try:
|
|
320
|
+
path = c.download_package(ct_id, _output_dir)
|
|
321
|
+
except SDCStudioError as exc:
|
|
322
|
+
# 404 until the package exists (and for a draft); a published model's package is built once, free.
|
|
323
|
+
if exc.status != 404 or not generate:
|
|
324
|
+
raise
|
|
325
|
+
c.generate_package(ct_id)
|
|
326
|
+
built_now = True
|
|
327
|
+
path = c.download_package(ct_id, _output_dir)
|
|
328
|
+
return {"path": str(path), "built_now": built_now}
|
|
329
|
+
|
|
330
|
+
|
|
293
331
|
def _handle_generate_app(args: dict) -> Any:
|
|
294
332
|
ct_id = _string(args, "ct_id", required=True)
|
|
295
333
|
profile = _string(args, "profile", default="lightweight") or "lightweight"
|
|
@@ -316,6 +354,7 @@ TOOL_HANDLERS = {
|
|
|
316
354
|
"estimate_model": _handle_estimate_model,
|
|
317
355
|
"build_model": _handle_build_model,
|
|
318
356
|
"job_status": _handle_job_status,
|
|
357
|
+
"download_package": _handle_download_package,
|
|
319
358
|
"generate_app": _handle_generate_app,
|
|
320
359
|
"wallet": _handle_wallet,
|
|
321
360
|
}
|
|
@@ -364,7 +403,8 @@ def _handle_request(request: Any) -> dict | None:
|
|
|
364
403
|
"The modeler's client for SDCStudio. Search the public catalog first and reuse published "
|
|
365
404
|
"components; estimate before you build and show the person the cost; build only with the "
|
|
366
405
|
"estimate_id you showed; prefer the model package over the generated application when the "
|
|
367
|
-
"feature lives inside an existing application
|
|
406
|
+
"feature lives inside an existing application; fetch a model's package, including one you just built "
|
|
407
|
+
"in a private project, with download_package. A 402 means the person funds the wallet at "
|
|
368
408
|
f"{get_client().base_url}/app/settings?tab=wallet; never try to pay. "
|
|
369
409
|
+ ("A key is configured, so building and downloading are available within its policy."
|
|
370
410
|
if has_key else "No key is configured: the catalog and estimates work; building needs SDCSTUDIO_API_KEY.")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sdcmodeler
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Summary: The modeler's client for SDCStudio: search the catalog, estimate, assemble and publish SDC4 data models, generate applications; a CLI and an MCP server over one client
|
|
5
5
|
Author: Axius SDC, Inc.
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -110,6 +110,7 @@ A JSON-RPC 2.0 stdio server, no SDK, the way sdcvalidator and sdcgovernance are
|
|
|
110
110
|
| `estimate_model` | what a tree would reuse, mint and cost; returns `estimate_id` | no |
|
|
111
111
|
| `build_model` | assemble, publish and generate (or `publish: false` for a draft); needs the `estimate_id` the person saw; refuses past the key's limit | yes |
|
|
112
112
|
| `job_status` | one job by id: status, step, cost so far, the model's links | no |
|
|
113
|
+
| `download_package` | a model's package ZIP with the key, including a model in a private project; builds a missing package first (free, once) unless `generate` is false | no |
|
|
113
114
|
| `generate_app` | generate and download the application; needs `confirm_charge`; 1,500 credits the first time, free after | yes |
|
|
114
115
|
| `wallet` | balance in credits and the key's policy | no |
|
|
115
116
|
|
|
@@ -42,7 +42,7 @@ def test_initialize_and_tools_list():
|
|
|
42
42
|
assert r["result"]["protocolVersion"] == "2025-06-18"
|
|
43
43
|
assert "wallet" in r["result"]["instructions"]
|
|
44
44
|
tools = rpc("tools/list")["result"]["tools"]
|
|
45
|
-
assert [t["name"] for t in tools] == ["search_components", "list_models", "get_model", "estimate_model", "build_model", "job_status", "generate_app", "wallet"]
|
|
45
|
+
assert [t["name"] for t in tools] == ["search_components", "list_models", "get_model", "estimate_model", "build_model", "job_status", "download_package", "generate_app", "wallet"]
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def test_build_needs_the_estimate_the_person_saw(bound):
|
|
@@ -114,3 +114,25 @@ def test_build_model_passes_the_flags_and_names_what_to_poll(bound):
|
|
|
114
114
|
sent = json.loads([c for c in bound.calls if c.url.path.endswith("/assemble/")][-1].content)
|
|
115
115
|
assert sent["generate"] is False and "publish" not in sent
|
|
116
116
|
assert built["requested"] == {"publish": True, "generate": False} and "j9" in built["next"]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_download_package_fetches_a_private_models_package_with_the_key(bound, tmp_path):
|
|
120
|
+
bound.on("GET", "/api/v1/dmgen/dms/m1/download_package/", body={"download_url": "https://storage.example/dm-m1.zip", "filename": "dm-m1.zip"})
|
|
121
|
+
bound.on("GET", "https://storage.example/dm-m1.zip", body=b"PK")
|
|
122
|
+
out, _ = call("download_package", {"ct_id": "m1"})
|
|
123
|
+
assert out == {"path": str(tmp_path / "dm-m1.zip"), "built_now": False} and (tmp_path / "dm-m1.zip").exists()
|
|
124
|
+
assert "authorization" not in bound.calls[-1].headers, "the storage pointer is followed without the key"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_download_package_builds_a_missing_package_first_unless_told_not_to(bound, tmp_path):
|
|
128
|
+
bound.on("GET", "/api/v1/dmgen/dms/m2/download_package/", status=404, body={"error": "No package"})
|
|
129
|
+
_, err = call("download_package", {"ct_id": "m2", "generate": False})
|
|
130
|
+
assert err and "404" in err
|
|
131
|
+
|
|
132
|
+
def built(_request):
|
|
133
|
+
bound.on("GET", "/api/v1/dmgen/dms/m2/download_package/", body=b"PK", headers={"content-disposition": 'attachment; filename="dm-m2.zip"'})
|
|
134
|
+
return {"success": True}
|
|
135
|
+
|
|
136
|
+
bound.on("POST", "/api/v1/dmgen/dms/m2/generate/", body=built)
|
|
137
|
+
out, _ = call("download_package", {"ct_id": "m2"})
|
|
138
|
+
assert out["built_now"] is True and (tmp_path / "dm-m2.zip").exists()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|