97 lines
3.4 KiB
Python
97 lines
3.4 KiB
Python
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())
|