feat: deploy jarca ledger network mcp service
- add Docker deployment for independent jarca-ledger container - enable FastMCP streamable-http runtime configuration - document dcy-013, REQ-019, and BAT-015 verification evidence - update structure/runtime baselines and deployment runbook
This commit is contained in:
@@ -1,13 +1,80 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
from . import tool_contracts as contracts
|
||||
|
||||
load_dotenv(Path(__file__).resolve().parents[3] / ".env")
|
||||
mcp = FastMCP("jarca-ledger-mcp")
|
||||
|
||||
McpTransport = Literal["stdio", "sse", "streamable-http"]
|
||||
_ALLOWED_TRANSPORTS: set[str] = {"stdio", "sse", "streamable-http"}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class McpRuntimeConfig:
|
||||
transport: McpTransport = "stdio"
|
||||
host: str = "127.0.0.1"
|
||||
port: int = 8000
|
||||
streamable_http_path: str = "/mcp"
|
||||
sse_path: str = "/sse"
|
||||
message_path: str = "/messages/"
|
||||
stateless_http: bool = False
|
||||
|
||||
|
||||
def _env_bool(name: str, default: bool = False) -> bool:
|
||||
raw = os.getenv(name)
|
||||
if raw is None or raw == "":
|
||||
return default
|
||||
return raw.strip().lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
|
||||
def _env_int(name: str, default: int) -> int:
|
||||
raw = os.getenv(name)
|
||||
if raw is None or raw == "":
|
||||
return default
|
||||
try:
|
||||
return int(raw)
|
||||
except ValueError as exc:
|
||||
raise ValueError(f"{name} must be an integer, got {raw!r}") from exc
|
||||
|
||||
|
||||
def _env_path(name: str, default: str) -> str:
|
||||
raw = os.getenv(name, default).strip() or default
|
||||
return raw if raw.startswith("/") else f"/{raw}"
|
||||
|
||||
|
||||
def get_mcp_runtime_config() -> McpRuntimeConfig:
|
||||
transport = os.getenv("JARCA_MCP_TRANSPORT", "stdio").strip() or "stdio"
|
||||
if transport not in _ALLOWED_TRANSPORTS:
|
||||
allowed = ", ".join(sorted(_ALLOWED_TRANSPORTS))
|
||||
raise ValueError(f"JARCA_MCP_TRANSPORT must be one of: {allowed}; got {transport!r}")
|
||||
return McpRuntimeConfig(
|
||||
transport=transport, # type: ignore[arg-type]
|
||||
host=os.getenv("JARCA_MCP_HOST", "127.0.0.1").strip() or "127.0.0.1",
|
||||
port=_env_int("JARCA_MCP_PORT", 8000),
|
||||
streamable_http_path=_env_path("JARCA_MCP_STREAMABLE_HTTP_PATH", "/mcp"),
|
||||
sse_path=_env_path("JARCA_MCP_SSE_PATH", "/sse"),
|
||||
message_path=_env_path("JARCA_MCP_MESSAGE_PATH", "/messages/"),
|
||||
stateless_http=_env_bool("JARCA_MCP_STATELESS_HTTP", False),
|
||||
)
|
||||
|
||||
|
||||
_runtime_config = get_mcp_runtime_config()
|
||||
mcp = FastMCP(
|
||||
"jarca-ledger-mcp",
|
||||
host=_runtime_config.host,
|
||||
port=_runtime_config.port,
|
||||
streamable_http_path=_runtime_config.streamable_http_path,
|
||||
sse_path=_runtime_config.sse_path,
|
||||
message_path=_runtime_config.message_path,
|
||||
stateless_http=_runtime_config.stateless_http,
|
||||
)
|
||||
|
||||
@mcp.tool()
|
||||
def ledger_health_check() -> dict:
|
||||
@@ -290,7 +357,7 @@ def delete_ledger_category(category: str, subcategory: str = "", hard_delete: bo
|
||||
return contracts.delete_ledger_category(category, subcategory, hard_delete, force)
|
||||
|
||||
def main() -> None:
|
||||
mcp.run()
|
||||
mcp.run(transport=_runtime_config.transport)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user