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
@@ -10,6 +10,7 @@ from app.api.v1.endpoints import operational_tasks as endpoints
from app.auth.project_dependencies import ProjectContext
from app.domain.schemas.operational_data import TaskDataQuery, TaskDataSnapshot
from app.domain.schemas.operational_task import EvidenceRecord
from app.domain.schemas.operational_task import TaskPresentationEventCommand
from app.main import app
@@ -31,10 +32,34 @@ def test_dynamic_investigation_routes_are_in_the_public_contract():
assert "post" in paths["/api/v1/operational-tasks/{task_id}/actions/{action_id}/confirm-dispatch"]
assert "get" in paths["/api/v1/operational-tasks/{task_id}/reports"]
assert "get" in paths["/api/v1/operational-tasks/{task_id}/reports/{revision}"]
assert "post" in paths["/api/v1/operational-tasks/{task_id}/presentation-events"]
assert "get" in paths["/api/v1/operational-tasks/{task_id}/presentation-cursor"]
context_params = paths["/api/v1/operational-tasks/{task_id}/agent-context"]["get"]["parameters"]
assert any(item["name"] == "report_revision" and item["in"] == "query" for item in context_params)
@pytest.mark.anyio
async def test_presentation_routes_preserve_domain_rejections_and_do_not_create_runs(monkeypatch):
from app.domain.domain_errors import DomainError
service = SimpleNamespace(
publish_presentation_event=AsyncMock(side_effect=DomainError("SESSION_MISMATCH", "stale session", status=409)),
presentation_cursor=AsyncMock(return_value={"task_id": str(uuid4()), "execution_generation": 1,
"active_run": None, "event_cursor": 17}),
)
monkeypatch.setattr(endpoints, "_service", lambda _ctx, _conn: service)
with pytest.raises(HTTPException) as error:
await endpoints.publish_operational_task_presentation_event(
uuid4(), TaskPresentationEventCommand(request_id=uuid4(), run_id=uuid4(), source_session_id="stale",
command={"action": "clear_highlights"}),
ctx=_service_context(), conn=object(),
)
assert error.value.status_code == 409
assert error.value.detail["code"] == "SESSION_MISMATCH"
result = await endpoints.get_operational_task_presentation_cursor(uuid4(), ctx=_service_context(), conn=object())
assert result["event_cursor"] == 17 and result["active_run"] is None
@pytest.mark.anyio
async def test_data_query_requires_task_grant_before_source_request(monkeypatch):
service = SimpleNamespace(can_read_source=AsyncMock(return_value=False))