ZeroClawAgent's picture
Upload 10 files
d984ed3 verified
Raw
History Blame Contribute Delete
4.15 kB
"""
HTTP entry point for the fabric-config-oracle MCP server.
This module exposes the same FastMCP server as `main.py`, but uses the
**streamable HTTP** transport instead of stdio. It is designed to run
behind a uvicorn ASGI server so the MCP can be hosted on platforms like
Hugging Face Spaces, Fly.io, Railway, or any container host that exposes
a public HTTP port.
Run locally:
uvicorn app:asgi_app --host 0.0.0.0 --port 7860
Or directly:
python app.py
Hugging Face Spaces:
The Dockerfile in this repo starts `python app.py`. The Space's
public URL (e.g. `https://<space-name>.hf.space/mcp`) is the MCP
endpoint clients configure in their `mcp.json`.
Security note:
The server is **fully open / public** by design. There is no bearer
token, no OAuth, no DNS-rebinding protection. Anyone with the URL
can call the tools. This is acceptable for a read-only oracle that
only queries public upstream APIs (Mojang / Fabric Meta / Modrinth).
"""
from __future__ import annotations
import logging
import os
import sys
# Make sibling modules importable when running as `python app.py`.
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from mcp.server.fastmcp import FastMCP # noqa: E402
from mcp.server.transport_security import TransportSecuritySettings # noqa: E402
from mcp_server import mcp as _configured_mcp # noqa: E402
from mcp_server import get_server # noqa: E402
logger = logging.getLogger("fabric-config-oracle.http")
def build_http_app() -> "FastMCP":
"""
Return a FastMCP instance configured for public HTTP hosting.
We rebuild the FastMCP server (rather than mutating the singleton
from `mcp_server.py`) so the stdio entry point in `main.py` keeps
its conservative localhost-only defaults. The HTTP variant:
* binds to 0.0.0.0 (required by HF Spaces, Fly.io, Docker)
* listens on port 7860 (HF Spaces convention)
* exposes the MCP endpoint at `/mcp` (FastMCP default)
* **disables DNS rebinding protection** — public hosts have
arbitrary Host headers by design.
* enables stateless HTTP mode so any client can POST a single
JSON-RPC request without first opening a long-lived session.
This is the friendliest mode for public MCP endpoints and
matches the official `streamable-http` examples.
* enables `json_response=True` so responses are returned as
Content-Type: application/json rather than text/event-stream.
Both are spec-compliant; JSON is simpler for clients that
don't want to parse SSE.
"""
server = get_server()
# Re-apply settings that matter for public hosting. We mutate the
# existing Settings object so tools/resources registered on the
# module-level `mcp` singleton keep working.
server.settings.host = os.environ.get("FASTMCP_HOST", "0.0.0.0")
server.settings.port = int(os.environ.get("FASTMCP_PORT", "7860"))
server.settings.streamable_http_path = "/mcp"
server.settings.stateless_http = True
server.settings.json_response = True
server.settings.transport_security = TransportSecuritySettings(
enable_dns_rebinding_protection=False,
allowed_hosts=[],
allowed_origins=[],
)
return server
# Module-level ASGI app for `uvicorn app:asgi_app` invocation.
_http_server = build_http_app()
asgi_app = _http_server.streamable_http_app()
def main() -> None:
"""Start the fabric-config-oracle MCP server over streamable HTTP."""
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
stream=sys.stderr,
)
host = _http_server.settings.host
port = _http_server.settings.port
logger.info(
"Starting fabric-config-oracle MCP server (streamable HTTP) "
"on http://%s:%s/mcp",
host,
port,
)
import uvicorn
uvicorn.run(
asgi_app,
host=host,
port=port,
log_level="info",
access_log=False, # MCP traffic is verbose; suppress per-request logs.
)
if __name__ == "__main__":
main()