New issue?
Feature
get_scope_status() reports the configured OKTA_SCOPES list (scope_guard.get_configured_scopes, sourced purely from the env var string), not the scopes actually present on the cached/granted token. So a scope that's genuinely granted server-side but whose tool is still disabled looks identical, from inside a session, to a scope that was never granted at all.
Additional information
Concretely: we had okta.logs.read granted to our Okta API Services application (confirmed in the admin console), but it was missing from the OKTA_SCOPES env var in our MCP client config. get_scope_status() correctly listed get_logs/get_login_failures as disabled, but there was no way to tell from the session whether that was because the scope wasn't granted, or because it just wasn't requested. Both look the same from configured_scopes/disabled_tools.
This interacts with #68 (open): even after adding okta.logs.read to OKTA_SCOPES and restarting, prune_tools_by_scope re-enables the tool immediately because it only checks the configured string. The actual cached/refreshed token won't carry the new scope until a fresh device-authorization grant runs (a refresh-token exchange can't widen scopes), which #68 is exactly designed to force. Until #68 (or equivalent) lands, adding a scope to config can silently expose a tool whose calls then fail with a 401/403 from Okta. That's a confusing state to debug from inside a session: the tool looks enabled, get_scope_status() says everything's fine, but the actual API call fails.
Suggested improvement: have get_scope_status() (or a variant) decode the actual cached access token's scp/scope claim (the same JWT parsing #76 already added for expiry) and report the granted scopes too, flagging any tool that's "configured but not actually granted yet" separately from "granted" and from "not configured at all." That would make this class of gap self-diagnosing from inside a session instead of needing an external admin-console cross-check.
New issue?
Feature
get_scope_status()reports the configuredOKTA_SCOPESlist (scope_guard.get_configured_scopes, sourced purely from the env var string), not the scopes actually present on the cached/granted token. So a scope that's genuinely granted server-side but whose tool is still disabled looks identical, from inside a session, to a scope that was never granted at all.Additional information
Concretely: we had
okta.logs.readgranted to our Okta API Services application (confirmed in the admin console), but it was missing from theOKTA_SCOPESenv var in our MCP client config.get_scope_status()correctly listedget_logs/get_login_failuresas disabled, but there was no way to tell from the session whether that was because the scope wasn't granted, or because it just wasn't requested. Both look the same fromconfigured_scopes/disabled_tools.This interacts with #68 (open): even after adding
okta.logs.readtoOKTA_SCOPESand restarting,prune_tools_by_scopere-enables the tool immediately because it only checks the configured string. The actual cached/refreshed token won't carry the new scope until a fresh device-authorization grant runs (a refresh-token exchange can't widen scopes), which #68 is exactly designed to force. Until #68 (or equivalent) lands, adding a scope to config can silently expose a tool whose calls then fail with a 401/403 from Okta. That's a confusing state to debug from inside a session: the tool looks enabled,get_scope_status()says everything's fine, but the actual API call fails.Suggested improvement: have
get_scope_status()(or a variant) decode the actual cached access token'sscp/scopeclaim (the same JWT parsing #76 already added for expiry) and report the granted scopes too, flagging any tool that's "configured but not actually granted yet" separately from "granted" and from "not configured at all." That would make this class of gap self-diagnosing from inside a session instead of needing an external admin-console cross-check.