scaffold-ca-python 0.4.0__py3-none-any.whl → 0.5.0__py3-none-any.whl
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.
- scaffold_ca_python/factory/entry_points/ep_mcp.py +13 -0
- scaffold_ca_python/templates/entry_point/mcp/app.py.jinja2 +2 -1
- scaffold_ca_python/templates/entry_point/mcp/routes.py.jinja2 +25 -0
- scaffold_ca_python/templates/entry_point/mcp/test_app.py.jinja2 +17 -0
- scaffold_ca_python/templates/entry_point/mcp/test_routes.py.jinja2 +25 -0
- {scaffold_ca_python-0.4.0.dist-info → scaffold_ca_python-0.5.0.dist-info}/METADATA +1 -1
- {scaffold_ca_python-0.4.0.dist-info → scaffold_ca_python-0.5.0.dist-info}/RECORD +10 -8
- {scaffold_ca_python-0.4.0.dist-info → scaffold_ca_python-0.5.0.dist-info}/WHEEL +0 -0
- {scaffold_ca_python-0.4.0.dist-info → scaffold_ca_python-0.5.0.dist-info}/entry_points.txt +0 -0
- {scaffold_ca_python-0.4.0.dist-info → scaffold_ca_python-0.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -29,7 +29,9 @@ class EntryPointMcp(EntryPointFactory):
|
|
|
29
29
|
Always queues:
|
|
30
30
|
- src/infrastructure/entry_points/mcp_server/__init__.py
|
|
31
31
|
- src/infrastructure/entry_points/mcp_server/tools.py
|
|
32
|
+
- src/infrastructure/entry_points/mcp_server/routes.py
|
|
32
33
|
- tests/infrastructure/entry_points/mcp_server/test_tools.py
|
|
34
|
+
- tests/infrastructure/entry_points/mcp_server/test_routes.py
|
|
33
35
|
|
|
34
36
|
Conditionally queues (when builder params are True):
|
|
35
37
|
- resources.py + test_resources.py (with_resources=True)
|
|
@@ -66,12 +68,23 @@ class EntryPointMcp(EntryPointFactory):
|
|
|
66
68
|
builder.render(f"{base}/tools.py.jinja2", ctx_dict),
|
|
67
69
|
template_name=f"{base}/tools.py.jinja2",
|
|
68
70
|
)
|
|
71
|
+
builder.add_file(
|
|
72
|
+
src_dir / "routes.py",
|
|
73
|
+
builder.render(f"{base}/routes.py.jinja2", ctx_dict),
|
|
74
|
+
template_name=f"{base}/routes.py.jinja2",
|
|
75
|
+
)
|
|
69
76
|
builder.add_file(
|
|
70
77
|
test_dir / "test_tools.py",
|
|
71
78
|
builder.render(f"{base}/test_tools.py.jinja2", ctx_dict),
|
|
72
79
|
template_name=f"{base}/test_tools.py.jinja2",
|
|
73
80
|
is_test=True,
|
|
74
81
|
)
|
|
82
|
+
builder.add_file(
|
|
83
|
+
test_dir / "test_routes.py",
|
|
84
|
+
builder.render(f"{base}/test_routes.py.jinja2", ctx_dict),
|
|
85
|
+
template_name=f"{base}/test_routes.py.jinja2",
|
|
86
|
+
is_test=True,
|
|
87
|
+
)
|
|
75
88
|
|
|
76
89
|
# Optional: resources
|
|
77
90
|
if with_resources:
|
|
@@ -9,7 +9,7 @@ from starlette.applications import Starlette
|
|
|
9
9
|
from starlette.routing import Mount
|
|
10
10
|
|
|
11
11
|
from {{ project.python_package }}.application.config.container import Container
|
|
12
|
-
from {{ project.python_package }}.infrastructure.entry_points.mcp_server import tools
|
|
12
|
+
from {{ project.python_package }}.infrastructure.entry_points.mcp_server import routes, tools
|
|
13
13
|
{%- if with_resources %}
|
|
14
14
|
from {{ project.python_package }}.infrastructure.entry_points.mcp_server import resources
|
|
15
15
|
{%- endif %}
|
|
@@ -18,6 +18,7 @@ from {{ project.python_package }}.infrastructure.entry_points.mcp_server import
|
|
|
18
18
|
{%- endif %}
|
|
19
19
|
|
|
20
20
|
mcp: FastMCP = FastMCP("{{ project.name }}", stateless_http=True)
|
|
21
|
+
routes.bind_routes(mcp)
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
@asynccontextmanager
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""MCP custom routes binding — register non-MCP HTTP routes such as the health check."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
from starlette.requests import Request
|
|
7
|
+
from starlette.responses import JSONResponse, Response
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def bind_routes(mcp: FastMCP) -> None:
|
|
11
|
+
"""
|
|
12
|
+
Bind routes to the FastMCP instance.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@mcp.custom_route(name="health_check", path="/api/v1/health", methods=["GET"])
|
|
16
|
+
async def health_check(_request: Request) -> Response:
|
|
17
|
+
"""
|
|
18
|
+
Health check endpoint to verify the server is running.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
JSON response indicating the server is healthy.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
response_data = {"status": "ok"}
|
|
25
|
+
return JSONResponse(response_data)
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from mcp.server.fastmcp import FastMCP
|
|
6
6
|
from starlette.applications import Starlette
|
|
7
|
+
from starlette.testclient import TestClient
|
|
7
8
|
|
|
8
9
|
from {{ project.python_package }}.application import app as app_module
|
|
9
10
|
|
|
@@ -28,3 +29,19 @@ def test_start_server_has_routes() -> None:
|
|
|
28
29
|
def test_lifespan_is_callable() -> None:
|
|
29
30
|
"""lifespan must be a callable (async context manager factory)."""
|
|
30
31
|
assert callable(app_module.lifespan)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_health_route_registered_once() -> None:
|
|
35
|
+
"""Repeated start_server() calls must not duplicate the health route."""
|
|
36
|
+
app_module.start_server()
|
|
37
|
+
server = app_module.start_server()
|
|
38
|
+
health = [r for r in server.routes[0].routes if getattr(r, "path", None) == "/api/v1/health"]
|
|
39
|
+
assert len(health) == 1
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_health_route_reachable_through_start_server() -> None:
|
|
43
|
+
"""The health route is not shadowed by the Mount('/')."""
|
|
44
|
+
client = TestClient(app_module.start_server())
|
|
45
|
+
response = client.get("/api/v1/health")
|
|
46
|
+
assert response.status_code == 200
|
|
47
|
+
assert response.json() == {"status": "ok"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Tests for {{ project.python_package }}.infrastructure.entry_points.mcp_server.routes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
from starlette.testclient import TestClient
|
|
7
|
+
|
|
8
|
+
from {{ project.python_package }}.infrastructure.entry_points.mcp_server.routes import bind_routes
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _client() -> TestClient:
|
|
12
|
+
mcp = FastMCP("test")
|
|
13
|
+
bind_routes(mcp)
|
|
14
|
+
return TestClient(mcp.streamable_http_app())
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_health_check_returns_ok() -> None:
|
|
18
|
+
response = _client().get("/api/v1/health")
|
|
19
|
+
assert response.status_code == 200
|
|
20
|
+
assert response.json() == {"status": "ok"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_health_check_rejects_post() -> None:
|
|
24
|
+
response = _client().post("/api/v1/health")
|
|
25
|
+
assert response.status_code == 405
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: scaffold-ca-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Scaffolder to generate Clean Architecture apps
|
|
5
5
|
Project-URL: Homepage, https://github.com/bancolombia/scaffold-clean-architecture-py
|
|
6
6
|
Project-URL: Repository, https://github.com/bancolombia/scaffold-clean-architecture-py
|
|
@@ -35,7 +35,7 @@ scaffold_ca_python/factory/entry_points/__init__.py,sha256=ylyCH8R8iORQjHkx-bzy4
|
|
|
35
35
|
scaffold_ca_python/factory/entry_points/base.py,sha256=DOhL2jniv-LdiNIgFINgXX-i6krRO2EAvJAi7L_ZisA,1892
|
|
36
36
|
scaffold_ca_python/factory/entry_points/ep_agent.py,sha256=ODql04XDXya3ANYez38M3AYoUeRh9OmW7nxGmWHRgIY,3410
|
|
37
37
|
scaffold_ca_python/factory/entry_points/ep_generic.py,sha256=L51mO007f6LtgBzOAmBlW4dClLK9IzQf3rJjNUbd9xk,2823
|
|
38
|
-
scaffold_ca_python/factory/entry_points/ep_mcp.py,sha256=
|
|
38
|
+
scaffold_ca_python/factory/entry_points/ep_mcp.py,sha256=TVJrEFRrviB5tw6RbVtOIbzzgr-hmN0s1XQs5qIJguA,6244
|
|
39
39
|
scaffold_ca_python/factory/entry_points/ep_restapi.py,sha256=PFkt9TH1aN6YO8w4zAeoqFOuibC8oc7qJUbFfX9YI_U,5951
|
|
40
40
|
scaffold_ca_python/factory/pipelines/__init__.py,sha256=0YPpPGsZ6A3-udHTSZDyUHleLFLQQh1V5IL6AB0qJ6g,557
|
|
41
41
|
scaffold_ca_python/factory/pipelines/pipeline_factory.py,sha256=s4fpuyhdxXiwko-LlM6Pv-QJdRGQJb17KVrC-DW6ojo,842
|
|
@@ -70,13 +70,15 @@ scaffold_ca_python/templates/entry_point/generic/entrypoint_main.py.jinja2,sha25
|
|
|
70
70
|
scaffold_ca_python/templates/entry_point/generic/handler.py.jinja2,sha256=glCst1soTiKG_gDdq5C-OogzljOJ_QooaW_boYt7gsg,417
|
|
71
71
|
scaffold_ca_python/templates/entry_point/generic/test_handler.py.jinja2,sha256=gRhvVabkylQ8z06JZ9L2CHdTa83oxL1X4ldR3_DZQWg,1195
|
|
72
72
|
scaffold_ca_python/templates/entry_point/mcp/__init__.py.jinja2,sha256=8J_YmIXHUh26PkaxeM8z5lf-dtK0da1T4haOM8U6Y9A,38
|
|
73
|
-
scaffold_ca_python/templates/entry_point/mcp/app.py.jinja2,sha256=
|
|
73
|
+
scaffold_ca_python/templates/entry_point/mcp/app.py.jinja2,sha256=2Kfi3AjRvJi7ee1vO0PyUa4pfX8j1GcsHO89GmQy6JU,1948
|
|
74
74
|
scaffold_ca_python/templates/entry_point/mcp/prompts.py.jinja2,sha256=PUFRtuWlEzR7bg7zY0O8dx-5tNrEZXrNZ5XgBoUMulQ,765
|
|
75
75
|
scaffold_ca_python/templates/entry_point/mcp/resources.py.jinja2,sha256=ZV_2WwklhSGhVFel3stS00zmPJuTlznfvFrpiM9dDKo,840
|
|
76
|
+
scaffold_ca_python/templates/entry_point/mcp/routes.py.jinja2,sha256=_JRXwnCMSJ-YBhEsxs-2JuVVQQ5sLeDDnkGB_8E4UJs,752
|
|
76
77
|
scaffold_ca_python/templates/entry_point/mcp/server.py.jinja2,sha256=gVzJm_ugY9E7-1RYIfbEKduDR71gluq4l3OB_aTRr1w,656
|
|
77
|
-
scaffold_ca_python/templates/entry_point/mcp/test_app.py.jinja2,sha256=
|
|
78
|
+
scaffold_ca_python/templates/entry_point/mcp/test_app.py.jinja2,sha256=mvP2BXW_OK4HvPjl1dfHs-y7r9FIYViJ3BCBtAqX8iY,1643
|
|
78
79
|
scaffold_ca_python/templates/entry_point/mcp/test_prompts.py.jinja2,sha256=Y9C7z7jpjoTKANlFB6hNJUUw2abt5qF2C4Nm7MkvDvA,1566
|
|
79
80
|
scaffold_ca_python/templates/entry_point/mcp/test_resources.py.jinja2,sha256=2jHfPqyJ4TJZJupxe8T7gVz2ysaqZHcAog6186ie2CI,1763
|
|
81
|
+
scaffold_ca_python/templates/entry_point/mcp/test_routes.py.jinja2,sha256=aYVWQXiRguziMtOnvZc5uwnhgYhsH87yrIDPM4gz95g,753
|
|
80
82
|
scaffold_ca_python/templates/entry_point/mcp/test_tool_stub.py.jinja2,sha256=Tq4uDcZc77bCC9gCVidmM5HmtjPfACaHBoQW0qEhIWo,295
|
|
81
83
|
scaffold_ca_python/templates/entry_point/mcp/test_tools.py.jinja2,sha256=bd_8nvLuqPApKIYQoBhEokMYZrPYFUn1pnDEIOVxwCo,1550
|
|
82
84
|
scaffold_ca_python/templates/entry_point/mcp/tool_stub.py.jinja2,sha256=VaGWAUF-BFQ7EeOHXUHQ2H-Mzr1UfbfziDsS7EVWUCc,289
|
|
@@ -121,8 +123,8 @@ scaffold_ca_python/templates/project/application/config/resource_container.py.ji
|
|
|
121
123
|
scaffold_ca_python/templates/project/application/config/usecases_container.py.jinja2,sha256=3fEHe6mkXkDHlfO5nhLqzLB7ccTpqwtjHZfLyfysbro,527
|
|
122
124
|
scaffold_ca_python/templates/use_case/test_use_case.py.jinja2,sha256=spomnRHeXWiWjy3QHQt839yIMFDUqA7h-Wq4KfZxAyg,372
|
|
123
125
|
scaffold_ca_python/templates/use_case/use_case.py.jinja2,sha256=KPtaK1wvZ-7qHdDZyuVfqXTPYwJdNUMPXQZGuKZFA7Y,217
|
|
124
|
-
scaffold_ca_python-0.
|
|
125
|
-
scaffold_ca_python-0.
|
|
126
|
-
scaffold_ca_python-0.
|
|
127
|
-
scaffold_ca_python-0.
|
|
128
|
-
scaffold_ca_python-0.
|
|
126
|
+
scaffold_ca_python-0.5.0.dist-info/METADATA,sha256=LzgbMKXGUltT5VqAjcy4iM1WLe3EzI65blOTxX8N6Cw,7475
|
|
127
|
+
scaffold_ca_python-0.5.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
128
|
+
scaffold_ca_python-0.5.0.dist-info/entry_points.txt,sha256=4PyYg6mNCvAlt2yF_o1-QR2CBXiowYGpNyqcP88tu6I,104
|
|
129
|
+
scaffold_ca_python-0.5.0.dist-info/licenses/LICENSE,sha256=3ZZEP7DK_h3lEjS8r-DvQCec2q7zAqqq7HmSq6cuL0E,1068
|
|
130
|
+
scaffold_ca_python-0.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|