Quality & tooling
AI-assisted development (MCP)
The platform ships two MCP (Model Context Protocol) servers that connect your coding assistant (Claude Code, Cursor, or any MCP client) to the Okta standards and tooling — so your assistant follows the platform rules from the first line instead of discovering violations at PR review.
1) Local server — inside your repo (no account needed)
Every app repository scaffolded by the platform ships a ready local MCP
server in scripts/mcp/ with a root .mcp.json:
- Claude Code: auto-discovers it when you open the repo — zero setup.
- Cursor: add to
.cursor/mcp.json:
{ "mcpServers": { "okta-partner": { "command": "php", "args": ["scripts/mcp/server.php"] } } }
Requires only php on your PATH (no Composer, no network). Its tools wrap
the exact CI scanners (scan_code, validate_manifest,
lint_permission, component_catalog, feature_service,
search_standards, list_scopes) — whatever passes locally passes the
merge gate. See scripts/mcp/README.md in your repo.
2) Hosted server — from your account (consent, no tokens)
The hosted server adds live, identity-bound tools on top of the standards: your apps and versions, the live scope catalog, monitoring (app health and webhook deliveries), the app-preview simulator, and control / app-control-panel tools.
Connect (one-time):
# Claude Code
claude mcp add --transport http okta-partner https://partners.getokta.io/mcp
// Cursor — .cursor/mcp.json
{ "mcpServers": { "okta-partner": { "url": "https://partners.getokta.io/mcp" } } }
The first use opens your browser on the partner platform's consent screen: sign in with your normal account, review what the tool will be able to do, and approve. No token is ever copied or pasted — the grant is full OAuth (PKCE).
Write access: by default the link is read-only (your apps + standards + monitoring). To enable the control tools (submit for review, create a version, edit the changelog and release channel, set the account types, program the app control panel and webhook settings) tick "Allow write (app control)" on the consent screen. All writes go through the same guards as the portal UI (no bypassing publish state, no writing to a published version).
Key live tools:
| Tool | Purpose |
|---|---|
whoami / list_my_modules |
your identity and your tenant's apps |
get_module / list_versions |
app details + built manifest + versions |
module_health |
health report: status, versions, deployment checks, sandbox |
webhook_deliveries / delivery_stats / replay_delivery |
External delivery history, stats and replay |
notification_deliveries |
what became of your app's catalog notifications across every school that installed it: totals by status, channel and key, then the latest failures with their error text — the first place to look when a school says a notification never arrived |
preview_module / store_listing |
store-card & dashboard-placement simulation + preview page URL |
create_module |
create a new draft app — the first step, before create_version (write access required) |
submit_module / create_version / update_changelog / set_release_channel / sync_from_manifest |
lifecycle control (write access required) |
get/set_developer_ui / get/set_webhook_config |
program the app control panel & settings (write access required) |
set_landing_widgets |
declare your embedded app's landing-page widgets directly — or as a per-version override — without going through manifest.json. The list you pass replaces what is stored; [] clears the declaration (on a version that drops the override, restoring inheritance). The rules are okta-web's own publish rules (write access required) |
get/set_notification_config |
read and write the notification/payment provider block — at module level or as a per-version override (writing requires write access) |
list_notifications / create_notification / update_notification / delete_notification |
the catalogue of notifications a version dispatches (writing requires write access) |
discover_notifications / import_notifications |
scan the repo for undeclared notification keys and import them (importing requires write access) |
list_account_types / get_account_types / set_account_types |
the account-type catalog, what your versions declare, and setting it (write access required) |
list_shared_data |
what other apps share with other apps — the list you pick from before asking for access. Read from each app's latest published version, so anything still in a draft does not appear |
list_app_links / request_app_access / answer_access_request / revoke_app_access |
cross-app links: list them in both directions, request access, answer a request, and revoke a grant you gave (writes require write access) |
set_shared_data |
declare what your app shares with others, on an editable version — the first step, since nobody can request what you have not declared. Whole-list replace; [] clears it. Labels and descriptions are written for the school, not the developer (write access required) |
get_app_dependencies / set_app_dependencies |
what your app needs from other apps: what it declares today, what it COULD declare (granted to you and still published), and setting it on an editable version. Whole-list replace, and no entry without a live grant behind it (setting requires write access) |
get_org_profile / set_org_profile |
your organization and developer profile as the store shows it, plus your interface language. The account password is never readable or writable through MCP, and the logo is uploaded from the portal only (writing requires write access) |
optimize_screenshots |
re-encode a version's stored screenshots as WebP and scale down anything wider than needed. New uploads are already converted, so this is for an older library (write access required) |
Account types over MCP
The version editor's "Account types" tab is fully reachable from your assistant:
list_account_types— the live catalog (the same list the portal offers): each type has akey, atarget(role= a tenant role,portal= a cross-tenant portal) and Arabic/English labels. A key outside the list is accepted as a custom tenant role; the portals are a closed set (student|guardian).get_account_types— what your app actually declares. Account types are declared per version (there is no module-level value to inherit), so the report lists every version; pass{version}to narrow it.set_account_types— a whole-list replace on an editable version, exactly like the version editor's save. Read the current list first and resend it complete with your edit;account_types: []clears it.
Each entry: key (must equal the role name or the portal) + kind
(primary | dependent) + exactly one target: roles (one role) or
portal + at least one surface: web_route (a Laravel route name such as
school-app.admin — not a URL and not a /path) and/or mobile_entry (in
the shape the version's mobile mode requires) — and, on a native row with a
mobile_entry, an optional min_contract (integer ≥ 1: this type's own
host-contract floor; omit it to inherit the block default). This single
declaration is what
builds menu.audiences[] in okta-web and mobile.audiences[] in the Okta
app, and the tool refuses up front whatever the publish-time validator would
refuse.
Custom tenant roles. A role key outside the account-type catalog is
allowed — tenants define their own roles — but it is declared explicitly: the
audience it produces carries "custom": true in the manifest, and
set_account_types warns you when it stores one. Publishing an out-of-catalog
key without that marker is rejected, on purpose. The platform matches a role
key as plain text against the roles users actually hold, so an invented or
mistyped key matches nobody: the version publishes, the manifest looks
healthy, and every page of that audience is then refused for every user by the
deny-by-default audience guard. If you meant a known type, pick its exact key
(list_account_types); if you really do mean a tenant-defined role, make sure
the tenant has it under exactly that name. custom is never valid on a
portal audience.
Security note: the token is bound to you and your tenant only — it can never see or touch another tenant's apps, and webhook secrets are never readable through any tool. You can re-link at any time; write consent is your call on every link.