MCP Server
Active
1
0

Import UAPF package: incident-triage.uapf

This commit is contained in:
2026-06-01 18:25:37 +00:00
commit 7fe0fda7a5
26 changed files with 2509 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
kind: uapf.algorithm.card
id: algo.incident_triage.classify_incident
version: 1.0.0
name: Incident classifier
intent: |
Reads the normalised payload and picks one taxonomy code from a fixed
closed list. The classifier is LLM-backed at runtime (Claude via the
LLM gateway) and falls back to a deterministic keyword matcher when
the gateway is unreachable. The taxonomy code is the primary driver
for the priority and routing DMN decisions; downstream rules treat
this output as authoritative.
algorithm_kind: classifier
io:
inputs:
- id: payload
type: object
cardinality: single
documentation: |
The normalized_payload from the upstream intake.normalize step.
At minimum {title, description?, host?, severity?}.
- id: text
type: string
cardinality: single
documentation: |
Optional pre-flattened text. If absent, the host derives it from
payload.title + payload.description + payload.host.
outputs:
- id: taxonomy_code
type: string
constraints:
enum:
- network.outage.link_down
- network.degradation
- network.routing
- network.dns
- security.incident
- facility.power
- storage.capacity
- service.customer_request
- unknown.uncategorized
documentation: The chosen taxonomy code from the closed list above.
- id: confidence
type: probability
constraints:
minimum: 0
maximum: 1
documentation: Model-reported confidence; the stub fallback returns 0.75 for matched / 0.20 for unmatched.
- id: reasoning
type: string
documentation: One-sentence justification (English). Persisted with the AI decision; not shown to operator by default.
- id: label_hint
type: string
documentation: Human-friendly short label derived from the taxonomy code (e.g. "link_down").
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/ai.classify@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: ai.classify@1
note: |
Host-fulfilled UAPF-IP capability backed by the LLM gateway
(default Anthropic). When LLM_PROVIDER is unavailable, the host
falls back to a regex-driven keyword matcher that produces the
same output shape.
determinism: stochastic
side_effects: pure
complexity:
typical_latency_ms: 800
max_latency_ms: 30000
failure_mode: |
Returns taxonomy_code='unknown.uncategorized' with confidence<=0.25.
Triage continues; the DMN priority table treats unknown as P4 default.
reference:
legal: |
Latvijas Republikas Datu valsts inspekcijas vadlīnijas par
automatizētu lēmumu pieņemšanu — operators may override at any time.
standard: |
ITIL 4 — Incident Management practice; ISO/IEC 20000-1 — service
management taxonomy alignment.
limitations:
- Closed taxonomy of 9 codes — broader incident types fall to unknown.uncategorized.
- Latvian and English input supported; mixed-locale text may degrade confidence.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: bgp-flap-network-routing
description: |
Edge router BGP session flapping — the classifier should pick
network.routing, not the broader network.outage.link_down.
inputs:
payload:
title: "BGP session flapping rtr-core-02 → AS6939"
host: "rtr-core-02.lvrtc.lv"
description: "BGP peer 198.51.100.1 toggled UP/DOWN 7 times in 12 minutes."
severity: "high"
expected_outputs:
taxonomy_code: "network.routing"
- name: customer-bandwidth-request
description: |
Latvian customer email asking for a bandwidth uplift — a
service.customer_request, not a network outage.
inputs:
payload:
title: "Klients SIA Latvija Tev: lūgums palielināt joslas platumu"
description: "Mūsu uzņēmumam nepieciešams palielināt internet pieslēguma joslas platumu no 100 Mbps uz 500 Mbps."
severity: "average"
expected_outputs:
taxonomy_code: "service.customer_request"
- name: ddos-volumetric
description: |
Volumetric UDP flood pattern — security.incident takes precedence
over generic network classifications even when the symptom is
network-shaped.
inputs:
payload:
title: "DDoS attack pattern detected on edge"
description: "Volumetric UDP flood, 4.2 Gbps inbound to 192.0.2.0/24."
severity: "critical"
expected_outputs:
taxonomy_code: "security.incident"

View File

@@ -0,0 +1,123 @@
kind: uapf.algorithm.card
id: algo.incident_triage.draft_response
version: 1.0.0
name: Customer response drafter
intent: |
Drafts a customer-facing incident notification in parallel Latvian
and English on behalf of LVRTC. Output is a PROPOSED AIDecision —
never auto-sent. Operator approval in the GUI is required before any
message leaves the system (guardrail approval.human_required_for
enforces this at runtime).
Tone: professional, calm, factual. Acknowledges the problem, states
that the team is investigating, gives an ETA only if known. Does NOT
promise specific resolutions. Uses proper Latvian diacritics. Bodies
capped at ~90 words each.
algorithm_kind: transformer
io:
inputs:
- id: case_id
type: string
cardinality: single
constraints:
pattern: "^[0-9a-fA-F-]{36}$"
- id: locale
type: string
constraints:
enum: [lv, en, auto]
documentation: |
'lv' or 'en' forces a single primary locale; 'auto' produces
both bodies and lets the operator choose. Default 'lv' for LVRTC.
- id: what_happened
type: string
documentation: One-line summary of the incident (used in subject + opening).
- id: eta_minutes
type: integer
cardinality: single
constraints:
minimum: 0
documentation: 'Optional. When provided, surfaced in body as \"Aptuvenais risināšanas laiks: X min\".'
outputs:
- id: subject_lv
type: string
- id: subject_en
type: string
- id: body_lv
type: string
documentation: Latvian body, proper diacritics, <=~90 words.
- id: body_en
type: string
documentation: English body, <=~90 words.
- id: locale
type: string
documentation: The locale code echoed back; informational.
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/ai.draft_response@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: ai.draft_response@1
note: |
Host-fulfilled UAPF-IP capability backed by the LLM gateway with
Anthropic as the default provider. The host enforces that the
resulting AIDecision row is PROPOSED (never AUTO_APPLIED) for
this capability. Operator approval moves it to APPROVED before
any outbound transport adapter is invoked.
determinism: stochastic
side_effects: pure
complexity:
typical_latency_ms: 1500
max_latency_ms: 60000
failure_mode: |
Returns deterministic stub drafts ("Mūsu komanda ir saņēmusi
paziņojumu...") with locale='lv' and a flag indicating LLM unavailability.
Operator can edit before approving.
reference:
legal: |
GDPR 2016/679 Article 13 — information to data subjects; LVRTC
customer-communication standards.
standard: |
ITIL 4 — Communication and Awareness practice during major incidents.
limitations:
- Cap of ~90 words per body — long incident narratives are truncated.
- Does not yet support Russian or other locales beyond lv/en.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: link-down-with-eta
description: |
Edge link down with an ETA of 30 minutes. Both bodies should
acknowledge the outage and surface the ETA.
inputs:
case_id: "33333333-3333-3333-3333-333333333333"
locale: "auto"
what_happened: "Tīkla pārtraukums rtr-r1"
eta_minutes: 30
expected_outputs:
locale: "lv"
subject_lv: "[LVRTC] Informējam par incidentu"
subject_en: "[LVRTC] Incident notification"
- name: customer-request-no-eta
description: |
Customer-initiated request acknowledgement without an ETA.
inputs:
case_id: "44444444-4444-4444-4444-444444444444"
locale: "auto"
what_happened: "Klienta pieprasījums par joslas platumu"
expected_outputs:
locale: "lv"
subject_lv: "[LVRTC] Informējam par incidentu"
subject_en: "[LVRTC] Incident notification"

View File

@@ -0,0 +1,120 @@
kind: uapf.algorithm.card
id: algo.incident_triage.emit_event
version: 1.0.0
name: Case event emitter
intent: |
Appends a CaseEvent row to the case timeline. This is the canonical
way the BPMN signals "I completed a step" to the operator UI and to
the audit pipeline. Each event ends up as one row in case_events and
is also a candidate for VC signing (the VeriDocs SDK wraps a subset
of event types as Verifiable Credentials in Phase 1).
algorithm_kind: emitter
io:
inputs:
- id: case_id
type: string
cardinality: single
constraints:
pattern: "^[0-9a-fA-F-]{36}$"
- id: type
type: string
constraints:
enum:
- signal_attached
- status_changed
- triaged
- classified
- prioritized
- routed
- assigned
- ai_decision_recorded
- dmn_evaluated
- comment_added
- escalated
- resolved
- closed
documentation: Canonical event type. Used by the timeline filter pills in the operator UI.
- id: payload
type: object
documentation: |
Type-specific payload. For 'routed': {classification, priority,
ownership, group_slug}. For 'classified': {taxonomy_code,
confidence}. The schema per type is documented in the OpenITSM
case-events module.
- id: actor_label
type: string
documentation: |
Free-form actor identifier. For UAPF-driven events this is
typically 'uapf:lv.itsm.incident.triage'. For operator actions
it's 'operator:<user-slug>'.
outputs:
- id: event_id
type: string
documentation: UUID of the new CaseEvent row.
- id: recorded_at
type: string
documentation: ISO-8601 timestamp of the row insert (server clock).
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/event.emit@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: event.emit@1
note: |
Host-fulfilled UAPF-IP capability. Append-only — the host's
CaseEvent table has no UPDATE or DELETE paths exposed to UAPF
callers. The same row may later have a VC reference attached
by the VeriDocs SDK pipeline.
determinism: deterministic
side_effects: writes_state
complexity:
typical_latency_ms: 8
max_latency_ms: 2000
failure_mode: |
Throws on unknown event type or missing case_id. Caller in the
triage BPMN treats failure as soft — the case still ends in its
decided state, just without the closing 'routed' marker.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: routed-event
description: |
Standard 'routed' event at the end of triage. Payload echoes the
classification, priority, ownership, group_slug decided upstream.
inputs:
case_id: "99999999-9999-9999-9999-999999999999"
type: "routed"
payload:
classification: "security.incident"
priority: "P1"
ownership: "lvrtc"
group_slug: "soc-l2"
actor_label: "uapf:lv.itsm.incident.triage"
expected_outputs:
recorded_at: "any-iso-timestamp"
- name: ai-decision-recorded
description: |
AI decision recording — payload carries the AIDecision row id so
operators can click through to the proposal.
inputs:
case_id: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
type: "ai_decision_recorded"
payload:
capability: "ai.draft_response"
decision_id: "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
confidence: 0.7
requires_human_approval: true
actor_label: "uapf:lv.itsm.incident.triage"
expected_outputs:
recorded_at: "any-iso-timestamp"

View File

@@ -0,0 +1,111 @@
kind: uapf.algorithm.card
id: algo.incident_triage.evaluate_dmn
version: 1.0.0
name: DMN decision evaluator
intent: |
Wraps a DMN 1.3 decision-table evaluation as a callable UAPF-IP
capability. The triage BPMN invokes the three DMN tables in this
package (priority, ownership, routing) natively via businessRuleTask;
this card describes the dmn.evaluate@1 capability for cases where the
same evaluation is needed outside the BPMN runtime (operator manual
re-evaluation, batch backfill, regression testing).
Engine-internal DMN evaluation and this capability call return the
same output for the same input — that property is the only thing
this card asserts.
algorithm_kind: rule_table
io:
inputs:
- id: package_id
type: string
documentation: UAPF package id whose dmn/ cornerstone holds the table.
- id: decision_id
type: string
constraints:
enum: [priority, ownership, routing]
documentation: Which DMN decision to evaluate in this package.
- id: inputs
type: object
documentation: |
Decision-input columns as a flat object. For priority:
{severity, service_tier, ai_suggested_priority, classification}.
For ownership: {classification, host_domain, source}.
For routing: {classification, priority, ownership}.
outputs:
- id: output
type: object
documentation: |
Object with the decision-output columns. priority -> {priority}.
ownership -> {ownership}. routing -> {group_slug}.
- id: hit_rule_ids
type: array
documentation: Rule ids that matched (FIRST hit-policy produces 1; ANY may produce N).
- id: hit_policy
type: string
constraints:
enum: [FIRST, UNIQUE, ANY, PRIORITY, OUTPUT_ORDER, COLLECT, RULE_ORDER]
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/dmn.evaluate@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: dmn.evaluate@1
note: |
Host-fulfilled UAPF-IP capability. The OpenITSM host reads the
DMN file from the package's dmn/ cornerstone and applies the
DMN 1.3 hit-policy semantics. Same decision artifact, same
output as the in-process engine evaluation.
determinism: deterministic
side_effects: pure
complexity:
typical_latency_ms: 5
max_latency_ms: 5000
failure_mode: |
Throws if the requested package_id is not loaded by the runtime, or
if the named decision_id is not present in that package's dmn/
directory. Triage callers fall back to safe defaults (P4, lvrtc,
helpdesk-l1) so the case still completes.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: priority-critical-tier1
description: |
The priority table should rule P1 for a critical+tier_1 input
regardless of AI suggestion.
inputs:
package_id: "lv.itsm.incident.triage"
decision_id: "priority"
inputs:
severity: "critical"
service_tier: "tier_1"
ai_suggested_priority: "P3"
classification: "network.outage.link_down"
expected_outputs:
output:
priority: "P1"
hit_policy: "FIRST"
- name: routing-security-to-soc
description: |
Routing of a security.incident at P1 should land on soc-l2.
inputs:
package_id: "lv.itsm.incident.triage"
decision_id: "routing"
inputs:
classification: "security.incident"
priority: "P1"
ownership: "lvrtc"
expected_outputs:
output:
group_slug: "soc-l2"
hit_policy: "FIRST"

View File

@@ -0,0 +1,91 @@
kind: uapf.algorithm.card
id: algo.incident_triage.normalize_signal
version: 1.0.0
name: Signal normalizer
intent: |
Reads a freshly-received Signal row by id and folds the
source-specific payload (Zabbix event, IMAP email, Jira DC issue,
manual entry) into a single normalised shape with the fields
downstream cards expect: title, description, host, severity, source
hint, optional contact metadata, and a content-hash for dedupe.
Idempotent; safe to re-run.
algorithm_kind: transformer
io:
inputs:
- id: signal_id
type: string
cardinality: single
constraints:
pattern: "^[0-9a-fA-F-]{36}$"
documentation: UUID of the Signal row to normalise.
outputs:
- id: normalized_payload
type: object
documentation: |
Adapter-agnostic dictionary. Stable keys: title, description,
host, severity (one of disaster|high|critical|average|warning|low|info),
source, source_event_id, optional received_at, optional contact_email.
- id: dedupe_hash
type: string
documentation: SHA-256 hex over (source, source_event_id, host) used to suppress repeat signals.
- id: source_kind
type: string
documentation: One of zabbix|email|jira_dc|jira_cloud|manual|cti.
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/intake.normalize@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: intake.normalize@1
note: |
Host-fulfilled UAPF-IP capability. The OpenITSM host's
intake.normalize handler implements one normaliser per source.
Hash is a placeholder until the runtime publishes the
implementation hash.
determinism: deterministic
side_effects: pure
complexity:
typical_latency_ms: 30
max_latency_ms: 2000
failure_mode: |
Returns source_kind='unknown' with whatever raw_payload was available
on the signal row. Triage continues with best-effort classification.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: zabbix-link-down
description: |
Zabbix event for a transport-link outage on an edge router.
Title, host and severity are normalised from the raw event shape.
inputs:
signal_id: "11111111-1111-1111-1111-111111111111"
expected_outputs:
source_kind: "zabbix"
normalized_payload:
title: "Link down on edge router rtr-r1"
host: "rtr-r1.lvrtc.lv"
severity: "high"
source: "zabbix"
- name: email-customer-lv
description: |
Customer-facing Latvian-language email about a bandwidth uplift
request. Subject becomes title; body becomes description.
inputs:
signal_id: "22222222-2222-2222-2222-222222222222"
expected_outputs:
source_kind: "email"
normalized_payload:
title: "Klients SIA Latvija Tev: lūgums palielināt joslas platumu"
severity: "average"
source: "email"

View File

@@ -0,0 +1,98 @@
kind: uapf.algorithm.card
id: algo.incident_triage.suggest_priority
version: 1.0.0
name: Priority suggester
intent: |
Reads the classified incident plus its service-tier and reported
severity and proposes a priority on the P1..P4 scale. This output is
a SOFT suggestion only — the priority DMN table makes the binding
decision, with this suggestion as one of its four input columns.
Separating "AI suggestion" from "binding decision" keeps the AI
contestable and the auditor's job tractable.
algorithm_kind: classifier
io:
inputs:
- id: severity
type: string
constraints:
enum: [disaster, critical, high, average, warning, low, info]
documentation: Source-reported severity (Zabbix verbatim; normalised for other adapters).
- id: service_tier
type: string
constraints:
enum: [tier_1, tier_2, best_effort]
documentation: Service tier of the affected service (from the connection or host catalog).
- id: classification
type: string
documentation: |
Taxonomy code from ai.classify@1. The suggester applies a fixed
elevation for security.incident regardless of severity.
outputs:
- id: priority
type: string
constraints:
enum: [P1, P2, P3, P4]
- id: reason
type: string
documentation: One-sentence justification (English). Visible to operator.
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/ai.suggest_priority@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: ai.suggest_priority@1
note: |
Host-fulfilled UAPF-IP capability. The OpenITSM host can answer
via either the LLM gateway or a deterministic rule. The DMN
priority table downstream applies a binding decision on top.
determinism: stochastic
side_effects: pure
complexity:
typical_latency_ms: 600
max_latency_ms: 30000
failure_mode: |
Returns priority='P4' with reason='LLM unavailable, defaulted'. The
binding DMN can still raise the priority based on severity x tier.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: disaster-on-tier1-is-p1
description: |
Disaster severity on a tier_1 service is always P1 regardless of
classification.
inputs:
severity: "disaster"
service_tier: "tier_1"
classification: "network.outage.link_down"
expected_outputs:
priority: "P1"
- name: security-incident-elevates
description: |
Security incidents elevate to P1 regardless of severity field.
inputs:
severity: "average"
service_tier: "tier_2"
classification: "security.incident"
expected_outputs:
priority: "P1"
- name: customer-request-low
description: |
A customer service request on best_effort tier is P4 unless
escalated by an operator.
inputs:
severity: "info"
service_tier: "best_effort"
classification: "service.customer_request"
expected_outputs:
priority: "P4"

View File

@@ -0,0 +1,122 @@
kind: uapf.algorithm.card
id: algo.incident_triage.update_incident
version: 1.0.0
name: Incident updater
intent: |
Applies a patch to the Case row (priority, ownership, assigned_group_id,
taxonomy_code) and optionally transitions the FSM (open -> triaged,
triaged -> in_progress, etc). Every transition emits a CaseEvent so
the timeline reflects the change. Refuses illegal transitions per the
state machine in OpenITSM's services/incident.py module — the engine
treats the resulting error as a hard failure.
algorithm_kind: emitter
io:
inputs:
- id: case_id
type: string
cardinality: single
constraints:
pattern: "^[0-9a-fA-F-]{36}$"
- id: patch
type: object
documentation: |
Subset of writable case fields. Allowed keys: priority,
ownership, assigned_group_id, taxonomy_code, sla_breached,
operator_notes. Unknown keys are rejected.
- id: status
type: string
constraints:
enum: [open, triaged, in_progress, waiting_customer, waiting_third_party, resolved, closed, cancelled]
documentation: |
Optional target status. If absent, only field updates are applied.
If present, an FSM transition is attempted; illegal transitions
raise.
- id: reason
type: string
documentation: Human-readable reason recorded on the CaseEvent.
outputs:
- id: case_id
type: string
- id: new_status
type: string
documentation: The case status after the call (unchanged if status input was absent).
- id: success
type: boolean
- id: event_ids
type: array
documentation: CaseEvent row ids emitted by this call.
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/incident.update@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: incident.update@1
note: |
Host-fulfilled UAPF-IP capability. The OpenITSM host validates
the patch against the writable-field allowlist and runs FSM
transitions through services/incident.transition_case. All
mutations happen in a single DB transaction with the emitted
CaseEvent rows.
determinism: deterministic
side_effects: writes_state
complexity:
typical_latency_ms: 25
max_latency_ms: 5000
failure_mode: |
Illegal FSM transition or unknown patch key throws and the entire
call is rolled back. Triage callers do NOT proceed past this step on
failure — the case stays at its prior status.
reference:
legal: |
Latvian National Standard for ITSM (LVS EN ISO/IEC 20000-1).
standard: |
ITIL 4 — Incident Management state machine.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: open-to-triaged-with-priority
description: |
Open case gets a priority + assigned group and transitions to triaged.
inputs:
case_id: "55555555-5555-5555-5555-555555555555"
patch:
priority: "P1"
ownership: "lvrtc"
assigned_group_id: "66666666-6666-6666-6666-666666666666"
status: "triaged"
reason: "Auto-triaged by lv.itsm.incident.triage v1.0.0"
expected_outputs:
success: true
new_status: "triaged"
- name: patch-only-no-transition
description: |
Patch without a status input updates fields but leaves the FSM
state alone.
inputs:
case_id: "77777777-7777-7777-7777-777777777777"
patch:
operator_notes: "Customer called to follow up"
reason: "Operator note added"
expected_outputs:
success: true
- name: illegal-transition-throws
description: |
Closed cases cannot return to in_progress; the capability fails.
inputs:
case_id: "88888888-8888-8888-8888-888888888888"
status: "in_progress"
reason: "Attempting to reopen a closed case"
expected_outputs:
success: false