forcedotcom/sf-skills2 files

Service Itsm Agentic Setup Cmdb Configure

Enable the CMDB (Configuration Management Database) feature in Service Cloud ITSM against a production or sandbox org: verify the CMDB org SKU, provision the CMDB tenant, and enable the service-cloud-itsm-cmdb-integration feature that lifts the CMDB access gate. Use when the user asks to enable CMDB, turn on the Configuration Management Database, provision the CMDB tenant, enable the CMDB feature, or fix a CMDB 403 FUNCTIONALITY_NOT_ENABLED error. Triggers on: enable CMDB feature, provision CMDB tenant, turn on CMDB, CMDB not enabled, CMDB 403 error, service-cloud-itsm-cmdb-integration. DO NOT TRIGGER when: the user only wants to assign CMDB permission sets to users, only install a CMDB content bundle, or work with CMDB records directly.

Specification
Skill ID
forcedotcom/sf-skills/service-itsm-agentic-setup-cmdb-configure
Publisher
forcedotcom
Repository
sf-skills
Installs
0
Files
2
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

forcedotcom/sf-skills/service-itsm-agentic-setup-cmdb-configureInstalls these files
  • SKILL.md
  • references/mcp-invocation.md

What this skill tells the agent

Enable the CMDB Feature (Service Cloud ITSM)

Takes an org from "CMDB off" to "CMDB feature enabled" by walking the first three layers of the CMDB prerequisite stack in order. Every call runs through the Salesforce-hosted Headless-360 MCP server (server key headless-360) via its four meta-tools (discover, describe, dispatch_readonly, dispatch). The org is derived from the OAuth JWT bound to the current MCP session — the skill never handles an org id, alias, or credentials — so this works identically against production and sandbox with no per-user MCP install.

This skill covers Layers 0–2. User access (Layer 3) and content bundles (Layer 4) are separate skills — see the end of this file.

The gate this skill lifts

Every CMDB Connect API checks:

orgHasCMDBEnabled = orgHasCMDBPermission (org perm ITSrvcsCnfgMgmnt)  &&  OrgPreferences.CMDBEnabled

Until orgHasCMDBEnabled is true, CMDB APIs return 403 FUNCTIONALITY_NOT_ENABLED. CMDBEnabled is NOT a directly settable preference — it is flipped as a side effect of enabling the feature in Layer 2. This skill's job is to make that gate return true.

Necessary but not always sufficient for a given user. Lifting this org gate does not by itself let a specific user read CMDB data. Some CMDB reads (e.g. bundleListView) also enforce user-level CMDB access and return the same 403 FUNCTIONALITY_NOT_ENABLED ("not enabled for this user") when the running user holds no CMDB permission sets — even though the feature is correctly ENABLED. That is Layer 3 (service-itsm-agentic-setup-cmdb-access-assign), not a failure of this skill. Confirm this skill's success via the feature status == ENABLED, never via a CMDB data read.

Scope

  • In scope: verifying the CMDB org permission (Layer 0), triggering + polling CMDB tenant provisioning (Layer 1), pre-checking + enabling + verifying the CMDB feature (Layer 2).
  • Out of scope: permission-set assignment (Layer 3 — service-itsm-agentic-setup-cmdb-access-assign), bundle installation (Layer 4 — service-itsm-agentic-setup-cmdb-bundle-deploy), CMDB record CRUD, Discovery / Service Graph Connector, identification rules.

Mechanism

All operations dispatch through headless-360 MCP tools. Reads go through mcp__headless-360__dispatch_readonly, writes through mcp__headless-360__dispatch — both take raw HTTP: {"url": "<path>", "method": "GET|POST", "body"?: {...}, "queryParams"?: {...}} — not {operation_id, arguments}. See references/mcp-invocation.md for the exact url / method / body of every call. The four tools:

  • mcp__headless-360__discover — semantic search over the indexed operation catalog. The Setup/Connect routes this skill uses are not always ranked first (or indexed), so a miss does not mean the route is absent — dispatch the exact path directly (see references/mcp-invocation.md).
  • mcp__headless-360__describe — pull the full input schema and canonical route before any POST.
  • mcp__headless-360__dispatch_readonly — the dispatcher for every read (GET).
  • mcp__headless-360__dispatch — the dispatcher for every write (POST/PATCH).

The skill never handles credentials — the org is bound to the current OAuth session. If a dispatch* call returns an auth error, tell the user to re-authenticate the headless-360 MCP connection (and confirm the session points at the intended org), then stop.


Clarifying questions

Ask only what you cannot infer from conversation:

  • Which org? Confirm the target org and state plainly that this org will be modified (tenant provisioning and feature enable are writes). For production, get explicit confirmation.

Do not re-ask for anything the user already provided; pre-populate and note "(from conversation)".


Workflow

All steps are sequential and gated — do not advance past a failed layer. Always read before you write: run the read-only check before every mutation.

Layer 0 — Verify the CMDB org SKU (read-only, hard gate)

CMDB requires the org permission ITSrvcsCnfgMgmnt, granted only by edition / license / org template. No API can set it. The most reliable, universally-available way to verify the org carries the CMDB SKU is to probe for the CMDB permission-set license — it exists only in orgs provisioned with that license:

dispatch_readonly({ "url": "/services/data/v63.0/query", "method": "GET", "queryParams": { "q": "SELECT Id FROM PermissionSetLicense WHERE DeveloperName = 'ItSrvcCnfgItmReadPsl'" } })
→ totalSize == 1  (licensed)   |   totalSize == 0  (not licensed)
  • totalSize == 1 → org is CMDB-licensed; proceed to Layer 1.
  • totalSize == 0 → STOP. Tell the user in plain language (no developer names or API references in the message they see): > This org isn't licensed for CMDB. CMDB availability is determined by the org's edition or > license and can't be turned on through setup — it has to be included when the org is > provisioned. Please have the org set up with CMDB (or use one that already has it), then run > this again.

The core Connect API GET /services/data/v63.0/setup/org/permissions/ITSrvcsCnfgMgmnt ({"isPermissionEnabled": true|false}) is an alternative, but it 404s on some org types (including orgfarm test orgs), so prefer the PSL probe above. See references/mcp-invocation.md for the details. If no probe resolves, report that the org perm could not be verified and ask the user to confirm the org has CMDB licensed before continuing.

Layer 1 — Provision the CMDB tenant

CMDB runs on a dedicated tenant (the CMDB tenant) that must reach status PROVISIONED (asynchronous).

  1. Check current status (read): ``text dispatch_readonly({ "url": "/services/data/v67.0/connect/tenantProvisioningStatus", "method": "GET" }) ` Branch on status`:
  • PROVISIONED → already done; skip to Layer 2 (no trigger, no poll).
  • UNPROVISIONED → no job has run; go to step 2 and trigger it. Do not wait or poll on this state — waiting never starts provisioning and just burns the budget.
  • PROVISIONING_IN_PROGRESS → a job is already running; skip the trigger and go straight to the poll in step 3.
  • FAILED → treat as the FAILED case in step 3 (surface the reason; do not retry via API).
  1. Trigger provisioning (write) — only when step 1 showed UNPROVISIONED. Confirm with the user first: ``text dispatch({ "url": "/services/data/v67.0/connect/tenantProvisioningStatus", "method": "POST" }) `` After the trigger returns, tell the user provisioning has started and typically takes 2+ minutes, so there is nothing to check yet.
  2. Poll the GET until status == PROVISIONED. This is async and reliably takes 2+ minutes (measured completion clusters right around ~2 min 40 s), so an immediate poll is a guaranteed no-op. Anchor all timing on the response's `triggeredAt`, not on when this run started — the job may have been triggered by an earlier run (the PROVISIONING_IN_PROGRESS entry from step 1). Parse triggeredAt as a UTC epoch and compute elapsed = max(0, now − triggeredAt) — clamp to ≥ 0 so a clock skew (or a server-vs-agent timezone mismatch) can't yield a negative elapsed (waits forever) or a false timeout. If `triggeredAt` is missing or unparseable (the trigger POST response may not have populated it yet, or an in-progress row from an earlier run may omit it), fall back to anchoring on this run's start — treat elapsed = 0 and wait the full ~2-min floor, so a branch always fires deterministically. Then:
  • elapsed < ~2 min → wait until ~2 min after triggeredAt before the first check (skips the guaranteed-useless early polls), then poll every 30 seconds.
  • ~2 min ≤ elapsed < 10 min → poll immediately (the initial wait has already passed — do not

More from forcedotcom

Use this skill in a project

Open any RiverX project, search the Skills panel for forcedotcom/sf-skills/service-itsm-agentic-setup-cmdb-configure, and install it in one click.

Use this skill