All notable changes to this project will be documented in this file.
- Renamed the self-hosted server to "Okta Open Source MCP Server" and updated all existing references from "Okta MCP Server" to "Okta Open Source MCP Server" (#91).
- Standardized MCP tool responses to valid JSON per RFC 8259 across all 109 tools; fixes #14. Tool responses no longer leak raw Python
reprforApplicationSignOnMode.SAML_2_0,OktaAPIResponseobjects, SDK tuples, or other non-JSON types. - Standardized error return shape for
@validate_ids/@validate_os_version_paramsand every tool that previously emitted[f"Error: {e}"]— errors are now returned as[{"error": "..."}]/[{"exception": "..."}]so callers always parse valid JSON. - Fixed
create_custom_domainandcreate_email_domainto handle Okta's 204 / empty-body response by refetching the newly created resource vialist_custom_domains/list_email_domains. FQDN comparison is case-insensitive per RFC 1035, applied consistently across both the pre-create duplicate check and the post-create refetch lookup in both tools (all four comparisons were audited; the email-domains fallback and both modules' duplicate-check loops were previously case-sensitive). - Fixed the
create_email_customizationduplicate-language pre-check incustom_templates.pyto compare BCP 47 (RFC 5646) language tags case-insensitively, matching the FQDN fix above. - Extended the
(None, response, None)empty-body guard — previously applied only to the customization tool family — to every otherget_*/create_*/update_*/replace_*/verify_*tool that unpacks an Okta SDK 3-tuple and dereferences the result:applications.py(get_application,create_application,update_application),groups.py(get_group,create_group,update_group),users.py(get_user,create_user,update_user),policies.py(all 6 ofget_policy,create_policy,update_policy,get_policy_rule,create_policy_rule,update_policy_rule),device_assurance.py(get_device_assurance_policy,create_device_assurance_policy,replace_device_assurance_policy— the latter two previously returned bareNoneinstead of an error dict), andcustom_domains.py(verify_custom_domain). Two of these (groups.create_group,users.create_user) previously crashed with an unguardedAttributeErrorongroup.id/user.idwhen the SDK quirk fired, masked into a confusing{"exception": "'NoneType' object has no attribute 'id'"}. - Fixed the dynamically-registered scope-info stub tools in
utils/scope_stubs.py(registered viamcp.tool()(stub_fn)rather than@mcp.tool()decorator syntax) to also pass through@json_response— previously the only MCP-tool-registered callables in the server that bypassed the single JSON boundary.
- Added
okta_mcp_server.utils.serializationas the single normalization boundary for tool returns.to_jsonable()flattens Pydantic v2 models (model_dump(by_alias=True, exclude_none=True, mode="json")), Okta SDK v2 models (to_dict()),Enumvalues (.value), and drops transport-onlyOktaAPIResponse/ApiResponseobjects.Enumunwrapping is checked before the scalar branch so(str, Enum)/(int, Enum)mixins (e.g.ApplicationSignOnMode) serialize to their.value. - Added
@json_responsedecorator, applied innermost on every@mcp.toolso every response passes through the canonical serializer exactly once. Note: the decorator catches any exception raised while running the wrapped tool, not only a serialization failure — see the module and decorator docstrings inserialization.pyfor the resultingisError=Falsetradeoff. - Added
okta_mcp_server.utils.serialization.none_body_error(), a shared helper for the(None, response, None)guard above — builds the{"error": "..."}envelope and logs a warning in one call instead of repeating the same six lines at every call site. - Added a structured failure envelope (
{"ok": false, "error": {...}, "status_code": null, "raw": {}}) returned when serialization itself raises, so callers always receive valid JSON. The full traceback is written to the server log vialogger.exception; the last 4096 chars can additionally be surfaced to the caller asraw.traceback_tailby settingOKTA_MCP_INCLUDE_RAW=1(accepted truthy values:1,true,yes,on). Default is off to avoid leaking server-side stack frames to MCP clients. - Centralized JSON normalization in
create_paginated_response()— the paginated payload is returned untouched and flattened once by the outer@json_responsedecorator, removing the redundant lazy import back intoserialization.py. - Removed redundant per-tool
_serialize_*helpers inbrands.py,custom_domains.py,email_domains.py, andthemes.py.custom_templates.pyintentionally retains its local_serialize()helper because the SDK'sto_dict()drops server-readOnly preview fields (e.g.EmailPreview.body,.subject) thatmodel_dump()preserves.custom_pages.pyretains its helper to preserve the legacy{}return for legitimate empty-body responses on preview endpoints; the helper now usesmode="json"so nesteddatetime/UUID/Enumfields still meet the RFC 3339 guarantee. - Reworked
create_device_assurance_policyandreplace_device_assurance_policysopolicy_datais typed asDict[str, Any]at the FastMCP boundary and anyPolicyDataInputvalidation error is surfaced as{"error": "..."}instead of leaking a plain-textpydantic.ValidationError. - Added
tests/test_serialization.py(44 tests),tests/test_custom_domains.py(7 tests),tests/test_none_body_guards.py(28 tests, covering all None-body guards across 9 modules), andtests/test_scope_stubs.py(4 tests). Extendedtests/test_device_assurance.py(136 tests total) andtests/test_custom_templates.py(51 tests total).
- Upgraded Okta SDK from 3.4.1 → 3.4.4 to fix an upstream deserialization error on
GET /api/v1/policies/{policyId}/rules. The 3.4.1 SDK modeled theAccessPolicyConstraint.methods/AccessPolicyConstraint.typesenums as uppercase-only (PASSWORD,PUSH,SECURITY_KEY, …), while the live API returns lowercase values (password,push,security_key, …), causing thelist_policy_rulesMCP tool to fail on Access Policy rules that carry authenticator constraints.
- PyPI Release changes
- GA Release changes
- Added Device Assurance Policy tools (
list_device_assurance_policies,get_device_assurance_policy,create_device_assurance_policy,replace_device_assurance_policy,delete_device_assurance_policy) with support for Android, iOS, macOS, Windows, and ChromeOS platforms. - Upgraded Okta SDK dependency to v3.4.1.
- Added customization tools for brands, custom domains, custom pages, custom templates, email domains, and themes.
- Added scope-based tool loading — tools are now dynamically enabled based on the OAuth scopes available to the configured API token.
- Added
login_failuressystem log tool for querying recent authentication failures.
- Fixed pagination bug introduced by Okta SDK v3 upgrade.
- Fixed
add_user_to_groupto be idempotent (no longer errors if user is already a member). - Fixed
get_logsto support filtering byDENYoutcome. - Added
fetch_allsupport tolist_applications.
- Pagination improvements with better handling of large result sets.
- Initial release of the self hosted okta-mcp-server.