create-caspian-app 0.4.0-rc.0 → 0.4.0-rc.1

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 (2) hide show
  1. package/dist/main.py +75 -1
  2. package/package.json +1 -1
package/dist/main.py CHANGED
@@ -19,6 +19,8 @@ from fastapi import (
19
19
  )
20
20
  from fastapi.responses import RedirectResponse, FileResponse, HTMLResponse
21
21
  from starlette.datastructures import MutableHeaders
22
+ from starlette.middleware import Middleware
23
+ from starlette.middleware.cors import CORSMiddleware
22
24
  from starlette.middleware.sessions import SessionMiddleware
23
25
  from starlette.types import ASGIApp, Receive, Scope, Send
24
26
  from starlette.exceptions import HTTPException as StarletteHTTPException
@@ -57,6 +59,78 @@ from casp.runtime_security import (
57
59
  load_dotenv()
58
60
  cfg = get_config()
59
61
 
62
+ # ====
63
+ # CORS configuration (shared .env convention, mirrors casp.rpc origin checks)
64
+ # ====
65
+
66
+
67
+ def _csv_env(name: str) -> list[str]:
68
+ return [item.strip() for item in os.getenv(name, "").split(",") if item.strip()]
69
+
70
+
71
+ def _bool_env(name: str, default: bool = False) -> bool:
72
+ raw = os.getenv(name)
73
+ if raw is None:
74
+ return default
75
+ return raw.strip().lower() in {"1", "true", "yes", "on"}
76
+
77
+
78
+ def _configured_cors_origins() -> list[str]:
79
+ """Browser origins allowed to call the app, per the .env convention."""
80
+ origins: list[str] = []
81
+ for raw in (*_csv_env("CORS_ALLOWED_ORIGINS"), os.getenv("APP_BASE_URL", "")):
82
+ value = (raw or "").strip().rstrip("/")
83
+ if value and value not in origins:
84
+ origins.append(value)
85
+ return origins
86
+
87
+
88
+ def _build_mcp_cors_middleware() -> "Middleware":
89
+ """Build the MCP CORS layer from .env, adding MCP-required headers.
90
+
91
+ Browser MCP clients (e.g. MCP Inspector "Direct") send an OPTIONS preflight
92
+ and rely on the mcp-session-id / mcp-protocol-version headers, which are not
93
+ in the generic CORS_ALLOWED_HEADERS list, so they are merged in here.
94
+ """
95
+ origins = _configured_cors_origins()
96
+ allow_credentials = _bool_env("CORS_ALLOW_CREDENTIALS")
97
+
98
+ if not origins:
99
+ # The CORS spec forbids "*" together with credentials, so when no
100
+ # explicit origin is configured fall back to open + no credentials.
101
+ origins = ["*"]
102
+ allow_credentials = False
103
+
104
+ methods = _csv_env("CORS_ALLOWED_METHODS") or [
105
+ "GET", "POST", "DELETE", "OPTIONS"]
106
+
107
+ headers = _csv_env("CORS_ALLOWED_HEADERS")
108
+ for required in ("Content-Type", "Accept", "Authorization",
109
+ "mcp-session-id", "mcp-protocol-version"):
110
+ if required.lower() not in {h.lower() for h in headers}:
111
+ headers.append(required)
112
+
113
+ expose = _csv_env("CORS_EXPOSE_HEADERS")
114
+ for required in ("mcp-session-id", "mcp-protocol-version"):
115
+ if required.lower() not in {h.lower() for h in expose}:
116
+ expose.append(required)
117
+
118
+ try:
119
+ max_age = int(os.getenv("CORS_MAX_AGE", "600"))
120
+ except ValueError:
121
+ max_age = 600
122
+
123
+ return Middleware(
124
+ CORSMiddleware,
125
+ allow_origins=origins,
126
+ allow_credentials=allow_credentials,
127
+ allow_methods=methods,
128
+ allow_headers=headers,
129
+ expose_headers=expose,
130
+ max_age=max_age,
131
+ )
132
+
133
+
60
134
  # ====
61
135
  # MCP SERVER (mounted into this app so one deploy serves web + MCP)
62
136
  # ====
@@ -66,7 +140,7 @@ if cfg.mcp:
66
140
  # caspian.config.json, so suppress the static "module not found" check.
67
141
  from src.lib.mcp.mcp_server import mcp # type: ignore[import-not-found]
68
142
  # Inner path "/" so the mount prefix below is the full endpoint path.
69
- mcp_app = mcp.http_app(path="/")
143
+ mcp_app = mcp.http_app(path="/", middleware=[_build_mcp_cors_middleware()])
70
144
 
71
145
  # ====
72
146
  # AUTH CONFIGURATION (App behavior - customize here)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-caspian-app",
3
- "version": "0.4.0-rc.0",
3
+ "version": "0.4.0-rc.1",
4
4
  "description": "Scaffold a new Caspian project (FastAPI-powered reactive Python framework).",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",