Add otel-enable flag for mcp (#583)
This commit is contained in:
parent
413ff2b915
commit
710cd2b48a
3 changed files with 46 additions and 5 deletions
|
|
@ -227,6 +227,9 @@ def mcp(
|
||||||
False, "--reload", help="Enable auto-reload on code changes (HTTP mode only)"
|
False, "--reload", help="Enable auto-reload on code changes (HTTP mode only)"
|
||||||
),
|
),
|
||||||
debug: bool = typer.Option(False, "--debug", help="Enable debug mode with verbose logging"),
|
debug: bool = typer.Option(False, "--debug", help="Enable debug mode with verbose logging"),
|
||||||
|
otel_enable: bool = typer.Option(
|
||||||
|
False, "--otel-enable", help="Send logs to OpenTelemetry", show_default=True
|
||||||
|
),
|
||||||
env_file: Optional[str] = typer.Option(None, "--env-file", help="Path to environment file"),
|
env_file: Optional[str] = typer.Option(None, "--env-file", help="Path to environment file"),
|
||||||
name: Optional[str] = typer.Option(None, "--name", help="Server name"),
|
name: Optional[str] = typer.Option(None, "--name", help="Server name"),
|
||||||
version: Optional[str] = typer.Option(None, "--version", help="Server version"),
|
version: Optional[str] = typer.Option(None, "--version", help="Server version"),
|
||||||
|
|
@ -250,7 +253,10 @@ def mcp(
|
||||||
# Add optional arguments
|
# Add optional arguments
|
||||||
cmd.extend(["--host", host])
|
cmd.extend(["--host", host])
|
||||||
cmd.extend(["--port", str(port)])
|
cmd.extend(["--port", str(port)])
|
||||||
cmd.append("--debug")
|
if debug:
|
||||||
|
cmd.append("--debug")
|
||||||
|
if otel_enable:
|
||||||
|
cmd.append("--otel-enable")
|
||||||
if tool_package:
|
if tool_package:
|
||||||
cmd.extend(["--tool-package", tool_package])
|
cmd.extend(["--tool-package", tool_package])
|
||||||
if discover_installed:
|
if discover_installed:
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,11 @@ Auto-discovery looks for Python files with @tool decorated functions in:
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Enable debug mode with verbose logging",
|
help="Enable debug mode with verbose logging",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--otel-enable",
|
||||||
|
action="store_true",
|
||||||
|
help="Send logs to OpenTelemetry",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--env-file",
|
"--env-file",
|
||||||
help="Path to environment file",
|
help="Path to environment file",
|
||||||
|
|
@ -336,6 +341,7 @@ Auto-discovery looks for Python files with @tool decorated functions in:
|
||||||
port=args.port,
|
port=args.port,
|
||||||
reload=args.reload,
|
reload=args.reload,
|
||||||
debug=args.debug,
|
debug=args.debug,
|
||||||
|
otel_enable=args.otel_enable,
|
||||||
tool_package=args.tool_package,
|
tool_package=args.tool_package,
|
||||||
discover_installed=args.discover_installed,
|
discover_installed=args.discover_installed,
|
||||||
show_packages=args.show_packages,
|
show_packages=args.show_packages,
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,15 @@ Creates a FastAPI application that exposes both Arcade Worker endpoints and
|
||||||
MCP Server endpoints over HTTP/SSE. MCP is always enabled in this integrated mode.
|
MCP Server endpoints over HTTP/SSE. MCP is always enabled in this integrated mode.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
from collections.abc import AsyncGenerator, AsyncIterator
|
from collections.abc import AsyncGenerator, AsyncIterator
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from arcade_core.catalog import ToolCatalog
|
from arcade_core.catalog import ToolCatalog
|
||||||
|
from arcade_serve.fastapi.telemetry import OTELHandler
|
||||||
from arcade_serve.fastapi.worker import FastAPIWorker
|
from arcade_serve.fastapi.worker import FastAPIWorker
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
@ -74,6 +77,7 @@ def create_arcade_mcp(
|
||||||
catalog: ToolCatalog,
|
catalog: ToolCatalog,
|
||||||
mcp_settings: MCPSettings | None = None,
|
mcp_settings: MCPSettings | None = None,
|
||||||
debug: bool = False,
|
debug: bool = False,
|
||||||
|
otel_enable: bool = False,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> FastAPI:
|
) -> FastAPI:
|
||||||
"""
|
"""
|
||||||
|
|
@ -87,12 +91,28 @@ def create_arcade_mcp(
|
||||||
if secret is None:
|
if secret is None:
|
||||||
secret = "dev" # noqa: S105
|
secret = "dev" # noqa: S105
|
||||||
|
|
||||||
|
otel_handler = OTELHandler(
|
||||||
|
enable=otel_enable,
|
||||||
|
log_level=logging.DEBUG if debug else logging.INFO,
|
||||||
|
)
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
||||||
async with create_lifespan(catalog, mcp_settings, **kwargs) as components:
|
try:
|
||||||
app.state.mcp_server = components["mcp_server"]
|
logger.debug(f"Server lifespan startup. OTEL enabled: {otel_enable}")
|
||||||
app.state.session_manager = components["session_manager"]
|
async with create_lifespan(catalog, mcp_settings, **kwargs) as components:
|
||||||
yield
|
app.state.mcp_server = components["mcp_server"]
|
||||||
|
app.state.session_manager = components["session_manager"]
|
||||||
|
yield
|
||||||
|
except (asyncio.CancelledError, KeyboardInterrupt):
|
||||||
|
logger.debug("Server lifespan cancelled.")
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
logger.debug(f"Server lifespan shutdown. OTEL enabled: {otel_enable}")
|
||||||
|
if otel_enable and otel_handler:
|
||||||
|
otel_handler.shutdown()
|
||||||
|
await logger.complete()
|
||||||
|
logger.debug("Server lifespan shutdown complete.")
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title=(mcp_settings.server.title or mcp_settings.server.name),
|
title=(mcp_settings.server.title or mcp_settings.server.name),
|
||||||
|
|
@ -103,12 +123,14 @@ def create_arcade_mcp(
|
||||||
lifespan=lifespan,
|
lifespan=lifespan,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
otel_handler.instrument_app(app)
|
||||||
|
|
||||||
# Worker endpoints
|
# Worker endpoints
|
||||||
worker = FastAPIWorker(
|
worker = FastAPIWorker(
|
||||||
app=app,
|
app=app,
|
||||||
secret=secret,
|
secret=secret,
|
||||||
disable_auth=mcp_settings.arcade.auth_disabled,
|
disable_auth=mcp_settings.arcade.auth_disabled,
|
||||||
|
otel_meter=otel_handler.get_meter(),
|
||||||
)
|
)
|
||||||
worker.catalog = catalog
|
worker.catalog = catalog
|
||||||
|
|
||||||
|
|
@ -191,6 +213,7 @@ def create_arcade_mcp_factory() -> FastAPI:
|
||||||
|
|
||||||
# Read configuration from env vars that were set before running the server
|
# Read configuration from env vars that were set before running the server
|
||||||
debug = os.environ.get("ARCADE_MCP_DEBUG", "false").lower() == "true"
|
debug = os.environ.get("ARCADE_MCP_DEBUG", "false").lower() == "true"
|
||||||
|
otel_enable = os.environ.get("ARCADE_MCP_OTEL_ENABLE", "false").lower() == "true"
|
||||||
tool_package = os.environ.get("ARCADE_MCP_TOOL_PACKAGE")
|
tool_package = os.environ.get("ARCADE_MCP_TOOL_PACKAGE")
|
||||||
discover_installed = os.environ.get("ARCADE_MCP_DISCOVER_INSTALLED", "false").lower() == "true"
|
discover_installed = os.environ.get("ARCADE_MCP_DISCOVER_INSTALLED", "false").lower() == "true"
|
||||||
show_packages = os.environ.get("ARCADE_MCP_SHOW_PACKAGES", "false").lower() == "true"
|
show_packages = os.environ.get("ARCADE_MCP_SHOW_PACKAGES", "false").lower() == "true"
|
||||||
|
|
@ -216,6 +239,8 @@ def create_arcade_mcp_factory() -> FastAPI:
|
||||||
raise RuntimeError("No tools found")
|
raise RuntimeError("No tools found")
|
||||||
|
|
||||||
logger.info(f"Total tools loaded: {total_tools}")
|
logger.info(f"Total tools loaded: {total_tools}")
|
||||||
|
if otel_enable:
|
||||||
|
logger.info("OpenTelemetry is enabled")
|
||||||
|
|
||||||
# Build kwargs for server creation
|
# Build kwargs for server creation
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
@ -228,6 +253,7 @@ def create_arcade_mcp_factory() -> FastAPI:
|
||||||
catalog=catalog,
|
catalog=catalog,
|
||||||
mcp_settings=None,
|
mcp_settings=None,
|
||||||
debug=debug,
|
debug=debug,
|
||||||
|
otel_enable=otel_enable,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -238,6 +264,7 @@ def run_arcade_mcp(
|
||||||
port: int = 7777,
|
port: int = 7777,
|
||||||
reload: bool = False,
|
reload: bool = False,
|
||||||
debug: bool = False,
|
debug: bool = False,
|
||||||
|
otel_enable: bool = False,
|
||||||
tool_package: str | None = None,
|
tool_package: str | None = None,
|
||||||
discover_installed: bool = False,
|
discover_installed: bool = False,
|
||||||
show_packages: bool = False,
|
show_packages: bool = False,
|
||||||
|
|
@ -253,6 +280,7 @@ def run_arcade_mcp(
|
||||||
if reload:
|
if reload:
|
||||||
# Set env vars for the app factory to read later
|
# Set env vars for the app factory to read later
|
||||||
os.environ["ARCADE_MCP_DEBUG"] = str(debug)
|
os.environ["ARCADE_MCP_DEBUG"] = str(debug)
|
||||||
|
os.environ["ARCADE_MCP_OTEL_ENABLE"] = str(otel_enable)
|
||||||
if tool_package:
|
if tool_package:
|
||||||
os.environ["ARCADE_MCP_TOOL_PACKAGE"] = tool_package
|
os.environ["ARCADE_MCP_TOOL_PACKAGE"] = tool_package
|
||||||
os.environ["ARCADE_MCP_DISCOVER_INSTALLED"] = str(discover_installed)
|
os.environ["ARCADE_MCP_DISCOVER_INSTALLED"] = str(discover_installed)
|
||||||
|
|
@ -278,6 +306,7 @@ def run_arcade_mcp(
|
||||||
app = create_arcade_mcp(
|
app = create_arcade_mcp(
|
||||||
catalog=catalog,
|
catalog=catalog,
|
||||||
debug=debug,
|
debug=debug,
|
||||||
|
otel_enable=otel_enable,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue