Skip to content
Open
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
20 changes: 20 additions & 0 deletions src/okta_mcp_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,28 @@
from mcp.server.fastmcp import FastMCP

from okta_mcp_server.utils.auth.auth_manager import OktaAuthManager
from okta_mcp_server.utils.okta_compat import apply_okta_model_compat
from okta_mcp_server.utils.scope_guard import get_disabled_tools, get_startup_scopes, prune_tools_by_scope
from okta_mcp_server.utils.serialization import json_response
from okta_mcp_server.utils.tolerant_deserialization import install_tolerant_deserialization

# Okta SDK compatibility, applied at import time so it is guaranteed to run
# before any SDK deserialization: every tool module reaches the SDK only after
# doing ``from okta_mcp_server.server import mcp``, which executes this module
# top-to-bottom first.
#
# 1. apply_okta_model_compat() relaxes specific over-strict generated
# models that reject valid API responses.
# 2. install_tolerant_deserialization() is the general net for future spec
# drift: it drops and reports individual
# bad items in a list response instead of
# failing the whole request.
#
# See okta_mcp_server.utils.okta_compat and
# okta_mcp_server.utils.tolerant_deserialization for the rationale and the
# upstream issues that would make each removable.
apply_okta_model_compat()
install_tolerant_deserialization()

LOG_FILE = os.environ.get("OKTA_LOG_FILE")

Expand Down
30 changes: 14 additions & 16 deletions src/okta_mcp_server/tools/system_logs/system_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,24 @@

from okta_mcp_server.server import mcp
from okta_mcp_server.utils.client import get_okta_client
from okta_mcp_server.utils.okta_compat import apply_okta_model_compat
from okta_mcp_server.utils.pagination import build_query_params, create_paginated_response, extract_after_cursor, paginate_all_results
from okta_mcp_server.utils.scope_guard import require_scopes
from okta_mcp_server.utils.serialization import json_response

# Workaround for SDK v3.1.0 bug: when Behavior Detection is enabled the Okta API returns
# `userBehaviors` as List[dict], but LogSecurityContext expects List[StrictStr], which
# causes a ValidationError that crashes every get_logs call on sign-on/DENY events.
# Fix: relax the annotation to Optional[List[Any]] and force a Pydantic schema rebuild.
try:
import typing as _typing
from okta.models.log_security_context import LogSecurityContext as _LogSecurityContext

_patched_type = _typing.Optional[_typing.List[_typing.Any]]
_LogSecurityContext.__annotations__["user_behaviors"] = _patched_type
if "user_behaviors" in _LogSecurityContext.model_fields:
_LogSecurityContext.model_fields["user_behaviors"].annotation = _patched_type
_LogSecurityContext.model_rebuild(force=True)
logger.debug("Applied userBehaviors type workaround for LogSecurityContext (SDK v3.1.0 bug)")
except Exception as _patch_err:
logger.warning(f"Could not apply userBehaviors workaround: {_patch_err}")
# The `LogSecurityContext.user_behaviors` workaround that used to live inline here
# now lives with every other Okta SDK model patch in
# `okta_mcp_server.utils.okta_compat`. It is applied by `apply_okta_model_compat()`,
# which `okta_mcp_server.server` runs at import time — i.e. before this module's
# `from okta_mcp_server.server import mcp` above returns, and therefore before any
# `get_logs` call can deserialize a response. The behavior is unchanged: when
# Behavior Detection is enabled the API returns `userBehaviors` as List[dict] while
# the generated model declares List[StrictStr], which crashed every `get_logs` call
# covering sign-on/DENY events.
#
# Belt-and-braces for direct imports of this module in tests or scripts that never
# touch `server`: applying the patch set again is idempotent and cheap.
apply_okta_model_compat()


@mcp.tool()
Expand Down
Loading