from __future__ import annotations from typing import Any from mcp.server.mcpserver import MCPServer from mcp.server.mcpserver.context import Context from connectors.mobilityops import MobilityOpsConnector from packages.connector_kit import ( DEFAULT_KNOWLEDGE_LOCALE, ConnectorContext, MobilityOpsClient, MobilityOpsFileTokenProvider, MobilityOpsSettings, ) from .common import ( connector_context, load_connector_identity_key, load_json_environment, request_headers, ) from .mcp_server import add_operational_routes, add_workflow_prompts, run_mcp def build_connector() -> tuple[MobilityOpsConnector, set[str]]: import os raw = load_json_environment("MOBILITYOPS_PROJECTS_JSON", list) projects = {str(item).strip().lower() for item in raw if str(item).strip()} if not projects: raise ValueError("at least one project must be registered for the MobilityOps connector") settings = MobilityOpsSettings( base_url=os.environ["MOBILITYOPS_ENDPOINT"], token_reference=os.environ.get( "MOBILITYOPS_SERVICE_TOKEN_FILE", "/run/secrets/mobilityops_service_token" ), ) client = MobilityOpsClient(settings, MobilityOpsFileTokenProvider()) return MobilityOpsConnector(client), projects def build_server() -> MCPServer[Any]: connector, projects = build_connector() signing_key = load_connector_identity_key() server: MCPServer[Any] = MCPServer( "itworx-mobilityops", title="Fleet Ops read-only connector", version="1.0.0", instructions=( "Only read-only Fleet Ops operational and knowledge tools are available. Start " "with the operations summary, narrow attention and vehicle reads, and require " "grounded knowledge citations before recommending an operational action." ), ) add_operational_routes(server, "mobilityops-connector", readiness=connector.readiness) add_workflow_prompts(server, "mobilityops") def current(context: Context[Any, Any]) -> ConnectorContext: return connector_context(request_headers(context), projects, signing_key=signing_key) @server.tool(name="mobilityops.operations.summary", structured_output=True) async def operations_summary(context: Context[Any, Any]) -> dict[str, Any]: return connector.operations_summary(current(context)) @server.tool(name="mobilityops.attention.list", structured_output=True) async def attention_list( context: Context[Any, Any], minimum_severity: str = "medium", date: str | None = None, limit: int = 20, ) -> dict[str, Any]: return connector.attention_list( current(context), minimum_severity=minimum_severity, date=date, limit=limit ) @server.tool(name="mobilityops.vehicle.get", structured_output=True) async def vehicle_get(vehicleRef: str, context: Context[Any, Any]) -> dict[str, Any]: return connector.vehicle_get(current(context), vehicleRef) @server.tool(name="mobilityops.knowledge.search", structured_output=True) async def knowledge_search( question: str, context: Context[Any, Any], max_sources: int = 4, locale: str = DEFAULT_KNOWLEDGE_LOCALE, ) -> dict[str, Any]: return connector.knowledge_search( current(context), question, max_sources=max_sources, locale=locale ) return server def run() -> None: run_mcp(build_server())