Skip to content
Guides & References

Hourly network change reconciliation against Jira with Claude Code and rConfig MCP

Every hour, an AI agent matches new config versions in rConfig with approved Jira change tickets and escalates changes no ticket covers.

Iris: AI CenterReference designUpdated
On this page

Hourly network change reconciliation against Jira with Claude Code and rConfig MCP

Change control has a gap between “approved” and “proven”. A ticket says what may change and when; rConfig records what did change. This job closes the gap every hour. It picks up config versions rConfig recorded since the last run, looks for an approved Jira change that covers each one, and escalates the ones nothing covers, with the diff attached, while the person who made the change can still be asked.

Best for: regulated estates, security operations, change managers, MSPs that must show every customer change was requested. Tools: rConfig, Claude Code (claude -p) on a systemd timer, the Atlassian MCP server for Jira, a chat webhook for escalations. rConfig tools used: get_devices, get_config_history, get_config_diff.

Uplink guideFind unauthorised network changes with Splunk or Elastic MCP and rConfigMatch config changes in rConfig with Splunk or Elastic login logs and approved tickets, and flag the changes nobody can account for.Read the guide

How it flows

sequenceDiagram
    participant T as systemd timer (hourly)
    participant S as reconcile.sh
    participant C as claude -p
    participant R as rConfig MCP
    participant J as Jira MCP
    T->>S: Start
    S->>C: Window: since last run (from state file)
    C->>R: get_devices (scope) → last_config_at in window
    C->>R: get_config_history (changed devices)
    C->>J: Approved changes for these devices and times
    C->>R: get_config_diff (uncovered changes only)
    C-->>S: JSON: covered, uncovered, errors
    S->>S: Update state, skip versions already reported
    S-->>T: Post uncovered changes, if any

Connect both servers

Add Jira next to rConfig in the job’s .mcp.json. Use the connection settings from Atlassian’s documentation for its MCP server, with an API token for a Jira account that can read your change project and nothing more. Your Atlassian organisation admin has to allow API tokens for the MCP server; see ServiceNow or Jira.

Pre-approve rConfig’s tools and only the Jira tools that read, in one comma-separated --allowedTools list. A tool that isn’t allowed fails instead of waiting for a person.


The job

reconcile.sh
#!/usr/bin/env bash
set -euo pipefail
cd /opt/netops/reconcile
STATE=state.json
[ -f "$STATE" ] || echo '{"since": null, "reported": []}' > "$STATE"
SINCE=$(jq -r '.since // empty' "$STATE")
SINCE=${SINCE:-$(date -u -d '2 hours ago' +%FT%TZ)}
UNTIL=$(date -u +%FT%TZ)
PROMPT=$(cat <<EOF
Reconcile network config changes with approved change tickets.
Window: $SINCE to $UNTIL (UTC).
1. Using rConfig, list devices in groups Core, Distribution and WAN whose
last_config_at falls in the window. For each, use get_config_history,
filtered to the running-config command, to get every version created in
the window and the version before it.
2. Using Jira, search project CHG for tickets in status Approved or
Implementing whose "Devices" field contains the device and whose change
window (fields "Window start", "Window end") includes the version's
backup time.
3. A version is covered if such a ticket exists. For every uncovered version,
call get_config_diff against the previous version and summarise the change
in one sentence.
Output only JSON:
{"window": ["$SINCE", "$UNTIL"],
"covered": [{"device", "version", "ticket"}],
"uncovered": [{"device", "version", "backup_at", "summary", "diff_excerpt"}]}
Keep diff_excerpt to the 20 most relevant changed lines.
If either server fails, output {"error": "<which and why>"} and nothing else.
EOF
)
claude -p "$PROMPT" --allowedTools "mcp__rconfig__*,mcp__jira__search,mcp__jira__get_issue" \
--output-format json | jq -r '.result' > run.json
if jq -e 'has("error")' run.json > /dev/null; then
curl -fsS -X POST -H 'Content-Type: application/json' \
-d "$(jq -n --arg t "Change reconciliation FAILED: $(jq -r .error run.json)" '{text: $t}')" "$ESCALATION_WEBHOOK_URL"
exit 1 # state is not advanced, so the next run covers this window again
fi
# Report each uncovered version once, even though windows overlap after a failure.
NEW=$(jq --slurpfile st "$STATE" '[.uncovered[] | select(.version as $v | $st[0].reported | index($v) | not)]' run.json)
if [ "$(echo "$NEW" | jq length)" -gt 0 ]; then
text=$(echo "$NEW" | jq -r '"Config changes with no approved change ticket:\n" +
([.[] | "• \(.device) at \(.backup_at) (v\(.version)): \(.summary)\n\(.diff_excerpt)"] | join("\n"))')
curl -fsS -X POST -H 'Content-Type: application/json' \
-d "$(jq -n --arg t "$text" '{text: $t}')" "$ESCALATION_WEBHOOK_URL"
fi
jq --arg until "$UNTIL" --argjson new "$NEW" \
'.since = $until | .reported = ((.reported + [$new[].version]) | .[-500:])' "$STATE" > "$STATE.tmp" && mv "$STATE.tmp" "$STATE"

The Jira tool names (mcp__jira__search, mcp__jira__get_issue) are placeholders: use the names your Jira MCP server lists in /mcp, and allow only the ones that read.

/etc/systemd/system/netops-reconcile.timer
[Unit]
Description=Hourly config change reconciliation
[Timer]
OnCalendar=hourly
RandomizedDelaySec=120
Persistent=true
[Install]
WantedBy=timers.target
/etc/systemd/system/netops-reconcile.service
[Unit]
Description=Reconcile rConfig changes with Jira
[Service]
Type=oneshot
User=netops
EnvironmentFile=/etc/netops/reconcile.env
ExecStart=/opt/netops/reconcile/reconcile.sh
TimeoutStartSec=20min

Persistent=true runs a missed hour when the server comes back, and the state file means that run covers the whole gap.

An illustrative escalation:

Config changes with no approved change ticket:
• lon-dist-02 at 2026-11-12T14:07Z (v19233): local user "temp-admin" added with privilege 15
+ username temp-admin privilege 15 secret <obfuscated by rConfig>

Making the matching reliable

The agent can only match what the ticket states. Matching gets reliable when change tickets carry:

  • Device names exactly as rConfig has them, in a dedicated field, not only in the description.
  • A change window as two date-time fields, so “covers the backup time” is a comparison, not an interpretation.
  • A status that means approved. Tell the agent which statuses count.

Where tickets are free text, ask the agent for a third list, possible, for tickets that mention the device but don’t clearly cover the time, and treat it as “check by hand”.


Limits to know

  • Backup frequency sets the speed. The job reconciles what rConfig has recorded. Devices backed up nightly are reconciled the next morning.
  • Several changes between two backups look like one. If two changes happen between backups, rConfig records one version containing both, and one ticket may cover only part of it. The diff summary helps a person see that.
  • Not who, only what. rConfig doesn’t record which person made a change. For the “who”, add your SIEM’s MCP server, as in Unauthorised change hunt.

Guardrails

  • The agent reads rConfig and Jira only. Escalations are posted by the script, not the agent.
  • The rConfig token belongs to a service user scoped to the reconciled groups; the Jira account can read the change project and nothing else.
  • Diff excerpts can contain obfuscated secrets in their masked form. Post escalations to a restricted channel.

Variations

  • ServiceNow instead of Jira. Swap the Jira server for a ServiceNow MCP server, and tell the agent the change table’s field names.
  • Daily summary of covered changes. Keep each run’s covered list and post one daily line: “41 changes, all covered by 12 tickets”. It’s the evidence that the control ran.
  • Close the loop on the ticket. Once you trust the matching, a separate step (not the agent) can add a comment to each covering ticket with the version IDs and backup times, as in Change window verification.

Frequently asked questions

How quickly does this catch an unapproved change?

Within an hour of rConfig backing the device up. The backup is the limit: a device backed up nightly is reconciled the next morning, so put devices under strict change control on a frequent backup schedule.

What counts as covered by a ticket?

A Jira change ticket in an approved or implementing state that names the device and whose change window includes the backup time. Anything else is reported as uncovered, with the diff, for a person to judge.

Does the agent update the Jira ticket?

Not in this playbook. The agent reads Jira and rConfig only; the script posts the result. Keeping the scheduled agent read-only everywhere is the safest default.

Type to search.