--- name: jumpcloud-saas-spend description: Manage JumpCloud SaaS Management — review newly discovered shadow IT, approve or restrict applications, find unassigned and wasted licenses, calculate reclaimable spend, resolve app owners, and audit who actually logs into which SaaS app. Use when the user asks about SaaS spend, license waste, renewals, shadow IT, unapproved or newly discovered apps, app owners, or wants to cut software cost. license: MIT compatibility: Requires the JumpCloud MCP server (https://mcp.jumpcloud.com/v1) with SaaS Management enabled on the organization. metadata: author: jumpcloud version: "1.1.0" --- # SaaS Management SaaS Management discovers the applications an organization actually uses — including ones nobody approved — and tracks accounts, licenses, and cost against them. It is where the money is, and it is a separate system from SSO. ## SaaS applications are not SSO applications They have **different IDs**. Passing an SSO application ID to a SaaS tool fails or returns the wrong app. To cross from one to the other, bridge by name: 1. `application_get` on the SSO app → read `displayName` / `displayLabel`. 2. `saas_applications_list` → find the SaaS app with that name. 3. Use **that** app's ID for `saas_application_accounts_list`. Match on `displayName`, not `name`: SSO records carry a slug in `name` (`15five`) and the human form in `displayName` (`15Five`), while SaaS records use the human form. A SaaS record also carries an `ssoApps` array when a link already exists — check it before searching by name. SaaS app IDs come in two shapes and both are valid: catalog slugs (`flyio`, `adobe-illustrator`) for known vendors, and 24-character hex IDs for custom apps. ## Status and the review workflow Every discovered app carries a status: | Status | Meaning | |---|---| | `NEWLY_DISCOVERED` | Found and not yet triaged. This is the shadow-IT inbox. | | `APPROVED` | Sanctioned. | | `UNAPPROVED` | Known and not sanctioned. **Only this status enforces access restrictions.** | | `IGNORED` | Deliberately out of scope. | Triage flow: `saas_applications_list` → filter to `NEWLY_DISCOVERED` → for each, `saas_application_get` for detail and `saas_application_accounts_list` for who is using it → recommend a status → `saas_application_review` once the user decides. An access restriction (`WARNING`, `BLOCK`, `DISMISSIBLE_WARNING`) set on an app that is not `UNAPPROVED` does nothing. If someone asks to block an app, the status has to move to `UNAPPROVED` in the same review call or the block is silently inert. Say so. ### `saas_application_review` is a full PUT Every field is required on every call, even when changing one thing. Omitting a field clears it. Required: `applicationId`, `status`, `owner`, `accessRestriction`, `alternativeButton`, `alternativeLink`, `customRestrictionMessage`, `restrictionExcludedGroups`. **Always `saas_application_get` first**, then merge the requested change over the current values and send the complete object. Constraints: - `status` — `APPROVED`, `UNAPPROVED`, or `IGNORED`. **Cannot** be set to `NEWLY_DISCOVERED`; triage is one-way. - `accessRestriction` — `NO_ACTION`, `WARNING`, `BLOCK`, `DISMISSIBLE_WARNING`, or `DEFAULT_ACTION`. Use `NO_ACTION` when there is no restriction; do not omit it. - `owner` — a 24-hex **JumpCloud user ID**. - Empty string for unset text fields, empty array for `restrictionExcludedGroups`. ### Owners are end users, not admins A SaaS app's owner is a regular directory user. Resolve a name or email with `users_list` using `searchTerm`. **Never** use `admin_list` — console admins are a different population and an admin's ID will be rejected or attach the wrong person. If the user does not name an owner, carry forward the existing one from `saas_application_get`. Do not guess. ## Licenses and waste `saas_licenses_list` covers every app; `saas_application_licenses_list` scopes to one. Each record carries: - `licenses` — `total`, `assigned`, `unassigned` - `costs` — `estimatedCost`, `contractCost`, `savingOpportunityCost` - `renewalDate`, `currency`, and the app's `status` `savingOpportunityCost` is the platform's own estimate of reclaimable spend — lead with it rather than recomputing from seat counts. Watch for the inconsistent case: an app can report `assigned` seats above `total`, which means license counts were never configured, not that the org is over-provisioned. Say "license totals aren't configured for this app" instead of reporting a negative surplus. Renewals are leverage. An app with idle seats and a renewal inside 60 days is the one worth raising first, because the saving is actionable now. ## Accounts and real usage `saas_application_accounts_list` returns per-account detail: - **`loginMethods`** — how the account is actually accessed. `"SSO"` present means they have logged in through SSO; `"APPLICATION"` means direct login to the vendor, outside SSO. An account with no `"SSO"` entry is an app-level login your SSO logs will never see. - `status` (e.g. `AUTHORIZED`), `lastLogin`, `lastUsed`, `licenseCount`, `assignable` - `discoverMethods` — how it was found, such as `BROWSER_EXTENSION` / `BROWSER_LOGIN` - `user` — the linked JumpCloud user, when there is one **Accounts with no linked user are the important finding.** They survive offboarding: the directory account is gone, so a user-centric review shows nothing, but the SaaS account still authenticates and still costs money. Always check from the SaaS side. For a full picture of who is really using an app, read both `loginMethods` here and `di_events_get` with service `sso`. DI shows SSO attempts; SaaS accounts show direct logins and provisioned access. Neither alone is complete. ## Deletion `saas_application_delete` and `saas_account_delete` are permanent and take the tracked history — accounts, license records, discovery data — with them. Deleting an app you merely want to stop seeing is the wrong tool: set the status to `IGNORED` instead, which preserves the record. Offer that before deleting. ## Reporting Lead with money and name the action: > 3 newly discovered apps this week. ElevenLabs has 12 accounts and no owner — nobody is > accountable for it. Adobe Illustrator shows 15 assigned seats with license totals never > configured, so the $0 cost figure isn't real. Biggest reclaim is Slack: 12 unassigned > seats, $1,440/year, renewing in 5 weeks.