Cmdb Patterns logo

Cmdb Patterns

Organization
serac-labs
cmdb-patterns

Create ServiceNow CIs and cmdb_rel_ci relationships, walk upstream/downstream impact, detect orphan/stale CIs, and align discovered CIs with the proper sys_class_name hierarchy.

Overview

Publisherserac-labs
Repositoryserac
Skill namecmdb-patterns
Stars
78
Forks
26
Bundled files
Instructions only
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by serac-labs on GitHub. Read the source before you install it.

Installation

Install the Cmdb Patterns AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/serac-labs/serac.git /tmp/serac
mkdir -p .claude/skills
cp -r /tmp/serac/packages/skills/cmdb-patterns .claude/skills/cmdb-patterns
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Cmdb Patterns in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Cmdb Patterns on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Cmdb Patterns is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

CMDB Patterns for ServiceNow

The Configuration Management Database (CMDB) is the foundation of ServiceNow ITSM, tracking all Configuration Items (CIs) and their relationships.

CMDB Architecture

CI Class Hierarchy

cmdb (Base)
└── cmdb_ci (Configuration Item)
    ├── cmdb_ci_computer
    │   ├── cmdb_ci_server
    │   │   ├── cmdb_ci_linux_server
    │   │   ├── cmdb_ci_win_server
    │   │   └── cmdb_ci_unix_server
    │   └── cmdb_ci_pc_hardware
    ├── cmdb_ci_service
    │   ├── cmdb_ci_service_business
    │   ├── cmdb_ci_service_technical
    │   └── cmdb_ci_service_auto
    │       └── cmdb_ci_service_discovered
    ├── cmdb_ci_appl
    │   ├── cmdb_ci_app_server
    │   └── cmdb_ci_db_instance
    └── cmdb_ci_network_gear
        ├── cmdb_ci_netgear
        └── cmdb_ci_lb

Key CI Tables

TablePurposeKey Fields
cmdb_ciBase CI tablename, sys_class_name, operational_status
cmdb_ci_serverServersip_address, os, cpu_count, ram
cmdb_ci_serviceService (base class — Business Service is cmdb_ci_service_business)service_classification, busines_criticality
cmdb_ci_applApplicationsversion, install_directory
cmdb_rel_ciCI Relationshipsparent, child, type

Creating Configuration Items

Basic CI Creation (ES5)

javascript
// Create a new server CI
var ci = new GlideRecord("cmdb_ci_server")
ci.initialize()
ci.setValue("name", "PROD-WEB-001")
ci.setValue("ip_address", "10.0.1.100")
ci.setValue("os", "Linux Red Hat")
ci.setValue("os_version", "8.5")
ci.setValue("cpu_count", 8)
ci.setValue("ram", 32768)
ci.setValue("operational_status", 1) // Operational
ci.setValue("install_status", 1) // Installed
ci.setValue("used_for", "Production")
ci.setValue("owned_by", "sys_id_of_owner")
ci.setValue("support_group", "sys_id_of_group")
var sysId = ci.insert()

CI with Discovery Source

javascript
// CI from Discovery
var ci = new GlideRecord("cmdb_ci_linux_server")
ci.initialize()
ci.setValue("name", "discovered-server-001")
ci.setValue("discovery_source", "ServiceNow")
ci.setValue("first_discovered", new GlideDateTime())
ci.setValue("last_discovered", new GlideDateTime())
ci.setValue("ip_address", "10.0.2.50")

// Set classification
ci.setValue("classification", "Production")
ci.setValue("environment", "Production")

ci.insert()

CI Relationships

Relationship Types

TypeParent → ChildExample
Runs on::RunsApp → ServerERP runs on PROD-DB-01
Depends on::Used byService → AppHR Service depends on SAP
Contains::Contained byCluster → ServerCluster contains Node1
Hosted on::HostsVM → HypervisorVM01 hosted on ESX01
Members::Member ofCI → GroupServer member of Pool

Creating Relationships (ES5)

javascript
// Create relationship between CIs
function createCIRelationship(parentSysId, childSysId, relationType) {
  // Find relationship type
  var relType = new GlideRecord("cmdb_rel_type")
  relType.addQuery("name", relationType)
  relType.query()

  if (!relType.next()) {
    gs.error("Relationship type not found: " + relationType)
    return null
  }

  // Check if relationship already exists
  var existing = new GlideRecord("cmdb_rel_ci")
  existing.addQuery("parent", parentSysId)
  existing.addQuery("child", childSysId)
  existing.addQuery("type", relType.getUniqueValue())
  existing.query()

  if (existing.next()) {
    gs.info("Relationship already exists")
    return existing.getUniqueValue()
  }

  // Create new relationship
  var rel = new GlideRecord("cmdb_rel_ci")
  rel.initialize()
  rel.setValue("parent", parentSysId)
  rel.setValue("child", childSysId)
  rel.setValue("type", relType.getUniqueValue())
  return rel.insert()
}

// Usage
createCIRelationship(appSysId, serverSysId, "Runs on::Runs")

Querying Relationships

javascript
// Find all servers an application runs on
function getAppServers(appSysId) {
  var servers = []

  var rel = new GlideRecord("cmdb_rel_ci")
  rel.addQuery("parent", appSysId)
  rel.addQuery("type.name", "Runs on::Runs")
  rel.query()

  while (rel.next()) {
    var server = rel.child.getRefRecord()
    servers.push({
      sys_id: server.getUniqueValue(),
      name: server.getValue("name"),
      ip_address: server.getValue("ip_address"),
    })
  }

  return servers
}

// Find all dependencies of a service
function getServiceDependencies(serviceSysId) {
  var deps = []

  var rel = new GlideRecord("cmdb_rel_ci")
  rel.addQuery("parent", serviceSysId)
  rel.addQuery("type.name", "Depends on::Used by")
  rel.query()

  while (rel.next()) {
    deps.push({
      sys_id: rel.child.getUniqueValue(),
      name: rel.child.getDisplayValue(),
      class: rel.child.sys_class_name.toString(),
    })
  }

  return deps
}

Impact Analysis

Upstream/Downstream Analysis

javascript
// Get all CIs affected by a CI outage (downstream impact)
function getDownstreamImpact(ciSysId, depth) {
  if (typeof depth === "undefined") depth = 3

  var impacted = []
  var processed = {}

  function traverse(sysId, currentDepth) {
    if (currentDepth > depth || processed[sysId]) return
    processed[sysId] = true

    var rel = new GlideRecord("cmdb_rel_ci")
    rel.addQuery("child", sysId)
    rel.query()

    while (rel.next()) {
      var parentId = rel.parent.toString()
      if (!processed[parentId]) {
        impacted.push({
          sys_id: parentId,
          name: rel.parent.getDisplayValue(),
          depth: currentDepth,
        })
        traverse(parentId, currentDepth + 1)
      }
    }
  }

  traverse(ciSysId, 1)
  return impacted
}

// Get all CIs this CI depends on (upstream dependencies)
function getUpstreamDependencies(ciSysId, depth) {
  if (typeof depth === "undefined") depth = 3

  var dependencies = []
  var processed = {}

  function traverse(sysId, currentDepth) {
    if (currentDepth > depth || processed[sysId]) return
    processed[sysId] = true

    var rel = new GlideRecord("cmdb_rel_ci")
    rel.addQuery("parent", sysId)
    rel.query()

    while (rel.next()) {
      var childId = rel.child.toString()
      if (!processed[childId]) {
        dependencies.push({
          sys_id: childId,
          name: rel.child.getDisplayValue(),
          depth: currentDepth,
        })
        traverse(childId, currentDepth + 1)
      }
    }
  }

  traverse(ciSysId, 1)
  return dependencies
}

Business Service Impact

javascript
// Find all business services impacted by a CI
function getImpactedServices(ciSysId) {
  var services = []
  var processed = {}

  function findServices(sysId) {
    if (processed[sysId]) return
    processed[sysId] = true

    // Check if this CI is a service
    var ci = new GlideRecord("cmdb_ci")
    if (ci.get(sysId)) {
      if (ci.sys_class_name.toString().indexOf("cmdb_ci_service") === 0) {
        services.push({
          sys_id: sysId,
          name: ci.getValue("name"),
          criticality: ci.getValue("busines_criticality"),
        })
      }
    }

    // Traverse upstream
    var rel = new GlideRecord("cmdb_rel_ci")
    rel.addQuery("child", sysId)
    rel.query()

    while (rel.next()) {
      findServices(rel.parent.toString())
    }
  }

  findServices(ciSysId)
  return services
}

CMDB Health & Data Quality

Orphan CI Detection

javascript
// Find CIs without relationships
function findOrphanCIs(ciClass) {
  var orphans = []

  var ci = new GlideRecord(ciClass || "cmdb_ci")
  ci.addQuery("operational_status", 1) // Operational only
  ci.query()

  while (ci.next()) {
    var sysId = ci.getUniqueValue()

    // Check for any relationships
    var rel = new GlideRecord("cmdb_rel_ci")
    rel.addQuery("parent", sysId).addOrCondition("child", sysId)
    rel.setLimit(1)
    rel.query()

    if (!rel.hasNext()) {
      orphans.push({
        sys_id: sysId,
        name: ci.getValue("name"),
        class: ci.getValue("sys_class_name"),
      })
    }
  }

  return orphans
}

Stale CI Detection

javascript
// Find CIs not updated by discovery
function findStaleCIs(daysOld) {
  if (typeof daysOld === "undefined") daysOld = 30

  var stale = []
  var cutoff = new GlideDateTime()
  cutoff.addDaysLocalTime(-daysOld)

  var ci = new GlideRecord("cmdb_ci")
  ci.addQuery("operational_status", 1)
  ci.addQuery("last_discovered", "<", cutoff)
  ci.addNotNullQuery("last_discovered")
  ci.query()

  while (ci.next()) {
    stale.push({
      sys_id: ci.getUniqueValue(),
      name: ci.getValue("name"),
      last_discovered: ci.getValue("last_discovered"),
    })
  }

  return stale
}

MCP Tool Integration

Available CMDB Tools

ToolPurpose
snow_cmdb_identify_reconcileInsert/update CIs through IRE — identifies before it writes
snow_create_ciCreate new CI with proper class (writes straight to the class table)
snow_cmdb_searchSearch CIs with filters
snow_create_ci_relationshipCreate CI relationships
snow_impact_analysisAnalyze CI impact
snow_get_ci_detailsGet full CI information
snow_run_discoveryTrigger discovery

Write CIs through IRE, not by searching first

Searching for a CI and creating it when the search misses is the pattern that fills a CMDB with duplicates: it loses to any variance in name, IP or serial, and the second write carries no record of where the data came from. The Identification and Reconciliation Engine exists to do that matching properly. snow_cmdb_identify_reconcile posts the payload to /api/now/identifyreconcile, so each class's identifier rules run against it, an existing CI is updated instead of duplicated, reconciliation rules decide whether this data source may overwrite each attribute, and the write is recorded in Source [sys_object_source].

snow_create_ci and snow_update_ci write to a class table directly, which skips all of that. Use them for a CI you know is not in the CMDB and do not need attributed to a source. Ignore snow_reconcile_ci despite the name — it PUTs your fields onto base cmdb_ci and echoes its reconciliation_rule argument back untouched; no rule runs.

One payload can also carry relations between its own items, where type is a name field value from CI Relationship Type [cmdb_rel_type] and parent/child are indexes into items. That is how a source that discovers a host and the application on it submits both in a single call.

dry_run commits nothing, but permission is per tool rather than per argument, so it still counts as a write: on a production instance the dry run needs the same __confirmProd as the real call.

Example Workflow

javascript
// 1. Dry run first: IRE reports what it would do and commits nothing. On a
//    400-CI payload this is the only way to see the verdict before taking it.
await snow_cmdb_identify_reconcile({
  data_source: "ServiceNow", // must be a choice on cmdb_ci.discovery_source
  dry_run: true,
  items: [
    {
      className: "cmdb_ci_linux_server",
      values: { name: "PROD-WEB-002", ip_address: "10.0.1.101", serial_number: "VMW-42-8A" },
    },
  ],
})
// Read items[0].operation — INSERT, UPDATE, NO_CHANGE, UPDATE_WITH_UPGRADE,
// UPDATE_WITH_DOWNGRADE, UPDATE_WITH_SWITCH or DELETE — and
// items[0].identificationAttempts for the identifier rule that matched.

// 2. Same payload without dry_run commits it. IRE answers HTTP 200 even when
//    individual items fail, so check data.items[i].errors, which the tool
//    surfaces as a failure rather than a success.
const ire = await snow_cmdb_identify_reconcile({
  data_source: "ServiceNow",
  items: [
    {
      className: "cmdb_ci_linux_server",
      values: { name: "PROD-WEB-002", ip_address: "10.0.1.101", serial_number: "VMW-42-8A" },
    },
  ],
})
// sys_id of the CI IRE inserted or matched: ire.data.items[0].sysId

// 3. Create relationship (relationship_type is a cmdb_rel_type sys_id,
//    look it up via snow_query_table on cmdb_rel_type)
await snow_create_ci_relationship({
  parent_ci: appSysId,
  child_ci: serverSysId,
  relationship_type: runsOnRelTypeSysId, // sys_id of "Runs on::Runs"
})

// 4. Impact analysis (the CI argument is ci_id; there is no direction parameter).
//    include_services is left off on purpose: the business_services key it fills
//    is not impact — see the csdm-modeling skill.
await snow_impact_analysis({
  ci_id: serverSysId,
  depth: 3,
  include_services: false,
})

Best Practices

  1. Use Correct CI Class - Always use most specific class (cmdb_ci_linux_server, not cmdb_ci)
  2. Maintain Relationships - CIs without relationships have limited value
  3. Discovery Alignment - Align manual CIs with discovery patterns
  4. Operational Status - Keep status current (Operational, Retired, etc.)
  5. Unique Identifiers - Use serial_number, asset_tag for uniqueness
  6. Service Mapping - Connect CIs to business services
  7. Regular Cleanup - Archive retired CIs, remove orphans

Frequently asked questions

What does the Cmdb Patterns AI skill do?

Create ServiceNow CIs and cmdb_rel_ci relationships, walk upstream/downstream impact, detect orphan/stale CIs, and align discovered CIs with the proper sys_class_name hierarchy.

Why use Cmdb Patterns on TypingMind?

Because you install it once and use it with any model. Cmdb Patterns is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Cmdb Patterns in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/serac-labs/serac/tree/main/packages/skills/cmdb-patterns. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Cmdb Patterns?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Cmdb Patterns?

As many as you like. As long as a model supports skills, you can use Cmdb Patterns with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Cmdb Patterns AI skill free?

Yes. It is published on GitHub by serac-labs under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇