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.
@@ -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.4.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=6kS-E0NiyHwAfXbl-bB18YcCj2_nZ1mMp1qZaCQo59E,5687
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=VMU2AWXAVJVT0IqTI3E6XGBGn2rR80ViSN0jNyRf3E4,1916
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=MjVDhAXTz35HcCOVTsaWA--qvD_Qyn2SNG99CF6wctk,967
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.4.0.dist-info/METADATA,sha256=7LQgO7e31QQ8vmmP1PfqwXgaWnQRrLIIpengbcw8mf4,7475
125
- scaffold_ca_python-0.4.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
126
- scaffold_ca_python-0.4.0.dist-info/entry_points.txt,sha256=4PyYg6mNCvAlt2yF_o1-QR2CBXiowYGpNyqcP88tu6I,104
127
- scaffold_ca_python-0.4.0.dist-info/licenses/LICENSE,sha256=3ZZEP7DK_h3lEjS8r-DvQCec2q7zAqqq7HmSq6cuL0E,1068
128
- scaffold_ca_python-0.4.0.dist-info/RECORD,,
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,,