feat(server): integrate MCP queries and investigation messaging

Add project-bound OAuth MCP queries and controlled analysis with scoped evidence,
limits, timeouts and a nearby-model spatial query using EPSG:4326 inputs.
Default nearby searches to 200 meters and five results.

Integrate investigation messaging/presentation, health/readiness and typed history;
remove duplicate web search and fix online simulation and compressed baseline writes.
Include project migrations 023-025; keep local credentials and demo seeding excluded.

Validation: 319 focused pytest checks; real PostGIS temporary-table regression;
read-only Jinqian bridge radius comparison; cross-language MCP discovery/calls.
This commit is contained in:
2026-10-19 18:46:58 +08:00
parent 2b6fc5aa52
commit be6317eb48
117 changed files with 12703 additions and 638 deletions
+33 -3
View File
@@ -6,7 +6,7 @@ from pydantic import ValidationError
from app.auth.project_dependencies import ProjectContext
from app.api.v1.endpoints.operational_tasks import _task_permissions
from app.auth.permissions import SCADA_VIEW, SIMULATION_VIEW
from app.auth.permissions import SCADA_VIEW, SIMULATION_VIEW, permissions_for_context
from app.domain.schemas.operational_data import TaskDataQuery
from app.infra.integrations.operational_data import TaskDataError, TaskDataSettings, read_task_data
@@ -133,12 +133,42 @@ def test_query_is_a_finite_read_scope_with_bounded_time_and_device_count():
TaskDataQuery(source="peripheral_observations")
@pytest.mark.anyio
@pytest.mark.parametrize("source", ["scada_devices", "peripheral_work_orders"])
async def test_task_service_reads_with_existing_project_permissions(source):
ctx = ProjectContext(project_id=uuid4(), user_id=uuid4(), project_code="test",
project_role="member", system_role="service")
permissions = _task_permissions(ctx)
assert permissions == set(permissions_for_context(ctx))
assert {SCADA_VIEW, SIMULATION_VIEW} <= permissions
def handler(request):
assert request.method == "GET"
assert request.headers["Authorization"] == "Bearer AGENT"
assert request.headers["X-Project-Id"] == str(ctx.project_id)
return httpx.Response(200, json={"items": [], "project_id": str(ctx.project_id)})
snapshot = await read_task_data(
context=ctx, credential="Bearer AGENT", query=TaskDataQuery(source=source),
settings=settings(), transport=httpx.MockTransport(handler),
)
assert snapshot.source == source
@pytest.mark.anyio
async def test_service_without_project_read_permission_does_not_gain_access():
ctx = ProjectContext(project_id=uuid4(), user_id=uuid4(), project_code="test",
project_role="", system_role="service")
assert not {SCADA_VIEW, SIMULATION_VIEW} & _task_permissions(ctx)
with pytest.raises(TaskDataError, match="没有该数据源的读取权限"):
await read_task_data(context=ctx, credential="Bearer AGENT",
query=TaskDataQuery(source="scada_devices"), settings=settings())
@pytest.mark.anyio
async def test_task_service_uses_approved_delegation_only_during_source_read():
ctx = ProjectContext(project_id=uuid4(), user_id=uuid4(), project_code="test",
project_role="member", system_role="service")
assert SCADA_VIEW not in _task_permissions(ctx)
assert SIMULATION_VIEW not in _task_permissions(ctx)
query = TaskDataQuery(source="scada_devices")
with pytest.raises(TaskDataError, match="委托身份尚未配置"):
await read_task_data(context=ctx, credential="Bearer AGENT", query=query,