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:
Vendored
+101
@@ -0,0 +1,101 @@
|
||||
"""Owned loopback HTTP fixture for cross-language SDK smoke tests. No database/auth service."""
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import json
|
||||
import socket
|
||||
import sys
|
||||
from contextlib import asynccontextmanager
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from uuid import UUID
|
||||
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "unit"))
|
||||
from test_mcp_query import Repository, Connection, Verifier, PERMISSIONS
|
||||
from app.mcp import application, contexts, query_service
|
||||
from app.mcp.application import McpQueryApplication
|
||||
from app.mcp.config import McpSettings
|
||||
from app.mcp.dispatch import McpDispatchMiddleware
|
||||
from app.mcp.models import QueryResult
|
||||
|
||||
application.McpQueryRepository = contexts.McpQueryRepository = Repository
|
||||
contexts.permissions_for_context = query_service.permissions_for_context = (
|
||||
lambda project: PERMISSIONS
|
||||
)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def query_scope(identity, project):
|
||||
yield SimpleNamespace(
|
||||
project=SimpleNamespace(project_id=UUID(project)), connection=Connection()
|
||||
)
|
||||
|
||||
|
||||
async def execute(scope, binding, tool, args):
|
||||
return QueryResult(
|
||||
source=tool.name,
|
||||
source_ref="fixture:query",
|
||||
queried_at=datetime.now(timezone.utc),
|
||||
semantics=tool.semantics,
|
||||
data=[{"id": "P-1"}],
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
sock = socket.socket()
|
||||
sock.bind(("127.0.0.1", 0))
|
||||
port = sock.getsockname()[1]
|
||||
settings = McpSettings(
|
||||
enabled=True,
|
||||
issuer_uri="http://localhost/realms/test",
|
||||
resource_uri=f"http://127.0.0.1:{port}/mcp",
|
||||
_env_file=None,
|
||||
)
|
||||
|
||||
class JwtFixtureVerifier(Verifier):
|
||||
async def verify_token(self, token):
|
||||
try:
|
||||
payload = json.loads(
|
||||
base64.urlsafe_b64decode(token.split(".")[1] + "==")
|
||||
)
|
||||
value = self.identities.get(payload.get("fixture"))
|
||||
if value is not None:
|
||||
value.subject = payload["sub"]
|
||||
value.claims = {"sub": value.subject}
|
||||
return value
|
||||
except (ValueError, IndexError, KeyError):
|
||||
return None
|
||||
|
||||
verifier = JwtFixtureVerifier()
|
||||
for identity in verifier.identities.values():
|
||||
identity.resource = settings.resource_uri
|
||||
runtime = McpQueryApplication(
|
||||
settings, verifier=verifier, scope_factory=query_scope, executor=execute
|
||||
)
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app):
|
||||
async with runtime.lifespan():
|
||||
yield
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
app.state.mcp_runtime = runtime
|
||||
app.include_router(runtime.router, prefix="/api/v1")
|
||||
app.add_middleware(McpDispatchMiddleware, runtime=runtime)
|
||||
server = uvicorn.Server(uvicorn.Config(app, log_level="error", access_log=False))
|
||||
serving = asyncio.create_task(server.serve(sockets=[sock]))
|
||||
while not server.started:
|
||||
if serving.done():
|
||||
await serving
|
||||
raise RuntimeError("MCP fixture exited before listening")
|
||||
await asyncio.sleep(0.01)
|
||||
print(f"http://127.0.0.1:{port}/mcp", flush=True)
|
||||
await serving
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user