diff --git a/src/grist_mcp/session.py b/src/grist_mcp/session.py index 8bccc4b..1577ee9 100644 --- a/src/grist_mcp/session.py +++ b/src/grist_mcp/session.py @@ -53,3 +53,21 @@ class SessionTokenManager: self._tokens[token_str] = session return session + + def validate_token(self, token: str) -> SessionToken | None: + """Validate a session token. + + Returns the SessionToken if valid and not expired, None otherwise. + Also removes expired tokens lazily. + """ + session = self._tokens.get(token) + if session is None: + return None + + now = datetime.now(timezone.utc) + if session.expires_at < now: + # Token expired, remove it + del self._tokens[token] + return None + + return session diff --git a/tests/unit/test_session.py b/tests/unit/test_session.py index fb3d834..5c104a1 100644 --- a/tests/unit/test_session.py +++ b/tests/unit/test_session.py @@ -37,3 +37,19 @@ def test_create_token_caps_ttl_at_maximum(): # Should be capped at 3600 seconds (1 hour) max_expires = datetime.now(timezone.utc) + timedelta(seconds=3610) assert token.expires_at < max_expires + + +def test_validate_token_returns_session_for_valid_token(): + manager = SessionTokenManager() + created = manager.create_token( + agent_name="test-agent", + document="sales", + permissions=["read"], + ttl_seconds=300, + ) + + session = manager.validate_token(created.token) + + assert session is not None + assert session.document == "sales" + assert session.agent_name == "test-agent"