Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion src/cmcp_gateway/mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
if TYPE_CHECKING:
from cmcp_gateway.audit.chain import AuditChain
from cmcp_gateway.session.manager import SessionManager
from cmcp_gateway.session.state import SessionState

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -81,11 +82,13 @@ def __init__(
session_manager: SessionManager | None = None,
audit_chain: AuditChain | None = None,
bearer_token: str | None = None,
max_request_bytes: int = 1 * 1024 * 1024,
session: SessionState | None = None,
max_request_bytes: int = 1_000_000,
) -> None:
self._proxy = proxy
self._session_manager = session_manager
self._audit_chain = audit_chain
self._session = session
self._max_request_bytes = max_request_bytes
self._kernel = StatelessKernel()
middleware = (
Expand All @@ -104,6 +107,11 @@ def __init__(
methods=["GET"],
),
Route("/audit/export", self._audit_export, methods=["GET"]),
Route(
"/sessions/{session_id}/reset",
self._session_reset,
methods=["POST"],
),
],
middleware=middleware,
)
Expand Down Expand Up @@ -315,3 +323,33 @@ async def _audit_export(self, request: Request) -> Response:
{"error": "audit chain integrity check failed"}, status_code=500
)
return JSONResponse(bundle)

async def _session_reset(self, request: Request) -> Response:
"""POST /sessions/{session_id}/reset — operator-only session sensitivity reset."""
if self._session is None or self._audit_chain is None:
return JSONResponse(
{"error": "session management not configured"}, status_code=501
)
session_id: str = request.path_params["session_id"]
if session_id != self._session.session_id:
return JSONResponse(
{"error": f"session_id={session_id} not found"}, status_code=404
)
old_id, new_id = self._session.reset(
reason="operator reset via API",
authorized_by="api",
)
self._audit_chain.append(
"session_reset",
call_id=None,
tool_name=None,
policy_decision="n/a",
session_sensitivity_before=self._session.max_sensitivity,
session_sensitivity_after=self._session.max_sensitivity,
)
return JSONResponse({
"old_session_id": old_id,
"new_session_id": new_id,
"status": "reset",
"attestation_stale": False,
})
Empty file added tests/conformance/__init__.py
Empty file.
17 changes: 17 additions & 0 deletions tests/conformance/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""
Conformance test conftest.

Ensures the worktree's src/ directory takes precedence over any installed
editable package, so tests exercise the code in this branch.
"""

from __future__ import annotations

import sys
from pathlib import Path

# Insert worktree src/ at the front of sys.path so imports resolve to this
# branch's code rather than the installed editable package.
_src = str(Path(__file__).parent.parent.parent / "src")
if _src not in sys.path:
sys.path.insert(0, _src)
Loading
Loading