Spaces:
Sleeping
Sleeping
| # app.py | |
| import json | |
| import logging | |
| from datetime import datetime, timezone | |
| from typing import Dict | |
| import gradio as gr | |
| # -------------------------- | |
| # Logging setup (quiet + precise) | |
| # -------------------------- | |
| class JsonFormatter(logging.Formatter): | |
| def format(self, record: logging.LogRecord) -> str: | |
| payload = { | |
| "ts": datetime.now(timezone.utc).isoformat(timespec="milliseconds"), | |
| "level": record.levelname, | |
| "logger": record.name, | |
| "message": record.getMessage(), | |
| } | |
| # Attach any extra dict passed via logger.extra | |
| for k, v in getattr(record, "__dict__", {}).items(): | |
| if k not in payload and k not in ( | |
| "args", "asctime", "created", "exc_info", "exc_text", "filename", | |
| "funcName", "levelno", "lineno", "module", "msecs", "msg", | |
| "name", "pathname", "process", "processName", "relativeCreated", | |
| "stack_info", "thread", "threadName" | |
| ): | |
| payload[k] = v | |
| return json.dumps(payload, ensure_ascii=False) | |
| def setup_logging() -> logging.Logger: | |
| root = logging.getLogger() | |
| root.setLevel(logging.WARNING) # default higher; opt-in with our logger | |
| # Silence noisy libs | |
| logging.getLogger("httpx").setLevel(logging.ERROR) # suppress asset GETs | |
| logging.getLogger("uvicorn").setLevel(logging.WARNING) | |
| logging.getLogger("uvicorn.access").setLevel(logging.ERROR) # access logs off/noisy | |
| handler = logging.StreamHandler() | |
| handler.setFormatter(JsonFormatter()) | |
| app_logger = logging.getLogger("app") | |
| app_logger.setLevel(logging.INFO) | |
| # Avoid duplicate handlers if reloaded | |
| if not any(isinstance(h, logging.StreamHandler) for h in app_logger.handlers): | |
| app_logger.addHandler(handler) | |
| app_logger.propagate = False | |
| return app_logger | |
| log = setup_logging() | |
| # -------------------------- | |
| # Time function (LLM-friendly JSON) | |
| # -------------------------- | |
| def _time_payload_utc() -> Dict[str, object]: | |
| """ | |
| Return multiple canonical time representations for LLMs/tools. | |
| All times are UTC. | |
| """ | |
| now = datetime.now(timezone.utc) | |
| iso8601 = now.isoformat(timespec="milliseconds").replace("+00:00", "Z") | |
| # ISO week/date parts | |
| iso_year, iso_week, iso_weekday = now.isocalendar() | |
| yday = int(now.strftime("%j")) | |
| return { | |
| # Canonical machine formats | |
| "utc_iso8601": iso8601, # e.g., 2025-11-11T04:15:30.123Z | |
| "unix_seconds": int(now.timestamp()), # e.g., 1762824930 | |
| "unix_milliseconds": int(now.timestamp() * 1000), | |
| # Human-friendly hints (still machine-parseable) | |
| "date_utc": now.strftime("%Y-%m-%d"), # 2025-11-11 | |
| "time_utc": now.strftime("%H:%M:%S.%f")[:-3], # 04:15:30.123 | |
| "weekday_utc": now.strftime("%A"), # Tuesday | |
| "rfc_1123_utc": now.strftime("%a, %d %b %Y %H:%M:%S GMT"), | |
| # Components | |
| "year": now.year, | |
| "month": now.month, | |
| "day": now.day, | |
| "hour": now.hour, | |
| "minute": now.minute, | |
| "second": now.second, | |
| "millisecond": int(now.microsecond / 1000), | |
| # ISO calendar context | |
| "iso_year": iso_year, | |
| "iso_week": iso_week, | |
| "iso_weekday": iso_weekday, # 1=Mon ... 7=Sun | |
| "yearday": yday, | |
| # Provenance | |
| "timezone": "UTC", | |
| "source": "Datetime MCP Server", | |
| "note": "All values are UTC. Prefer 'utc_iso8601' for portability." | |
| } | |
| def now_utc(): | |
| """ | |
| Return current time (UTC) as a rich JSON object. | |
| Designed to be easily consumed by LLMs/tools. | |
| """ | |
| payload = _time_payload_utc() | |
| # Application-level audit log (one line per query) | |
| log.info("time_requested", extra={"route": "predict", "out_keys": list(payload.keys())}) | |
| return payload | |
| # -------------------------- | |
| # Gradio Interface | |
| # -------------------------- | |
| demo = gr.Interface( | |
| fn=now_utc, | |
| inputs=None, | |
| outputs=gr.JSON(label="UTC time (LLM-friendly JSON)"), | |
| title="Datetime MCP Server", | |
| description="Returns the current UTC datetime in multiple canonical formats." | |
| ) | |
| # -------------------------- | |
| # Middleware to log only meaningful hits | |
| # -------------------------- | |
| def attach_selective_logging_middleware(app): | |
| """ | |
| Add a lightweight middleware that logs only when an API/predict-like | |
| endpoint is hit; skips UI asset routes (/_app, /assets, /favicon, etc). | |
| """ | |
| from fastapi import Request | |
| from starlette.responses import Response | |
| ASSET_PREFIXES = ("/_app", "/assets", "/static", "/favicon", "/logo", "/file=") | |
| INTERESTING_PREFIXES = ("/run", "/queue", "/predict", "/api", "/chat", "/submit") | |
| async def selective_access_log(request: Request, call_next): | |
| path = request.url.path or "/" | |
| # Skip common asset paths | |
| if path.startswith(ASSET_PREFIXES): | |
| return await call_next(request) | |
| # Log only interesting interactive endpoints | |
| if path.startswith(INTERESTING_PREFIXES) or path == "/": | |
| ua = request.headers.get("user-agent", "") | |
| client_ip = getattr(request.client, "host", None) | |
| log.info( | |
| "incoming_request", | |
| extra={ | |
| "method": request.method, | |
| "path": path, | |
| "client_ip": client_ip, | |
| "user_agent": ua[:300], | |
| }, | |
| ) | |
| response: Response = await call_next(request) | |
| return response | |
| # Attach middleware to the FastAPI app Gradio runs under | |
| # Gradio 4.x exposes the FastAPI app at demo.server.app | |
| try: | |
| attach_selective_logging_middleware(demo.server.app) # type: ignore[attr-defined] | |
| except Exception: | |
| # Fallback for older/newer Gradio internals; safe no-op if unavailable. | |
| pass | |
| if __name__ == "__main__": | |
| # quiet=True reduces banner/noise; mcp_server=True exposes the MCP SSE endpoint. | |
| demo.launch( | |
| mcp_server=True, | |
| quiet=True, # suppresses gradio banner/extra logs | |
| show_error=True, # still useful while developing | |
| server_name="0.0.0.0", | |
| server_port=None, | |
| ) | |
| # https://nimo007-mcp-datetime-server.hf.space/gradio_api/mcp/ | |