cxone-wfm-intraday-mcp

cxone-wfm-intraday-mcp

An MCP server that exposes governed CXone WFM Intraday tools to an AI assistant, enabling real-time analyst workflows like risk assessment and SLA recovery using synthetic data.

Category
访问服务器

README

CXone WFM Intraday MCP (Sparkathon Prototype)

An MCP server that exposes governed CXone WFM Intraday tools to an AI assistant. It proves one Real-Time Analyst workflow — "Why is Billing Support at risk today, and what can we do to recover SLA?" — grounded entirely in structured tool calls over deterministic synthetic data. Simulation only; no live systems, no write-back.

Design spec: docs/superpowers/specs/2026-07-15-cxone-wfm-intraday-mcp-design.md.

Tools

Tool Purpose
list_intraday_metrics Catalog of queryable Intraday metrics
resolve_intraday_entity Fuzzy name → canonical queue
get_intraday_snapshot Current state of all/selected queues + highest-risk queue
get_forecast_vs_actual Per-interval forecast vs actual variance
get_intraday_risk_drivers Ranked reasons a queue is at risk
get_available_capacity Donor queues with spare capacity + trade-offs
simulate_recovery_action Before/after impact of moving N agents for Y minutes
summarise_intraday_recommendation Plain-English what / why / action / impact / trade-offs
load_intraday_data Replace the dataset with your own queues (bring-your-own-data)
reset_intraday_data Restore the built-in demo dataset

Ways to run it

Pick the path that fits — all three end with the same 10 tools available to your assistant:

  • Fastest, no install → Quick start (uv): one uvx command, nothing to clone.
  • Local dev / offline / to change the data → Setup: clone + venv + pip install.
  • Share via a URL (teammates install nothing) → Run as a remote HTTP MCP: deploy to Cloud Run.

Then, in your assistant, ask "Which queues are at risk right now?" and follow the Demo script.

Quick start (no clone, via uv)

If you have uv installed and access to this repo, register the server with a single command — no git clone, no venv, no pip install. uv fetches, installs, and runs it in an ephemeral environment straight from the repo.

Claude Code:

claude mcp add cxone-wfm-intraday -- uvx --from git+https://github.com/ssolanky/cxone-wfm-intraday-mcp cxone-wfm-intraday

Claude Desktop — add to claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "cxone-wfm-intraday": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/ssolanky/cxone-wfm-intraday-mcp", "cxone-wfm-intraday"]
    }
  }
}

Notes: this is a local server — uv runs it on your machine, it just removes the manual clone/install steps. Because the repo is private, uv needs Git access to it (a credential helper via gh auth setup-git, or a token). No uv? Use the full Setup below instead.

Setup

git clone https://github.com/ssolanky/cxone-wfm-intraday-mcp.git
cd cxone-wfm-intraday-mcp
python -m venv .venv
# Windows:  .venv\Scripts\activate       macOS/Linux:  source .venv/bin/activate
python -m pip install -e ".[dev]"
python -m pytest -q          # all tests should pass (64 tests)

NICE machines: pip here defaults to a private CodeArtifact index that 401s and lacks mcp. Install from public PyPI instead: python -m pip install --index-url https://pypi.org/simple/ -e ".[dev]" (or add index-url = https://pypi.org/simple/ under [global] in .venv/pip.ini).

Run against Claude

Claude Code:

claude mcp add cxone-wfm-intraday -- python -m cxone_wfm_intraday_mcp.server

(run from this project directory, using the interpreter where the package is installed).

Claude Desktop: copy the block from claude_desktop_config.example.json into your claude_desktop_config.json (Settings → Developer → Edit Config), fixing command to your Python interpreter, then restart Claude Desktop.

Run as a remote HTTP MCP (optional)

The same server can serve the Streamable HTTP transport instead of stdio, so teammates add just a URL — nothing installed locally. It's zero config: the server serves HTTP automatically when a PORT env var is present (as on Google Cloud Run) or MCP_TRANSPORT=http is set, and otherwise stays stdio for local use. HTTP endpoint path is /mcp.

Test it locally:

# macOS/Linux
MCP_TRANSPORT=http PORT=8080 python -m cxone_wfm_intraday_mcp.server
# Windows PowerShell
$env:MCP_TRANSPORT="http"; $env:PORT="8080"; python -m cxone_wfm_intraday_mcp.server
# now serving at http://127.0.0.1:8080/mcp

Deploy to Google Cloud Run (free tier, scales to zero) — from the repo root, using the included Dockerfile:

gcloud run deploy cxone-wfm-intraday --source . --region <region>

Cloud Run builds the image, injects PORT, and the server serves HTTP automatically. It prints a service URL; register it with any assistant:

claude mcp add --transport http cxone-wfm-intraday https://<service-url>/mcp

Auth: a public URL serving even mock data should be protected. Cloud Run requires IAM auth by default (omit --allow-unauthenticated); for other hosts put a token/proxy in front. Authentication was deliberately deferred by the Sparkathon spec — add it before any non-demo use.

Demo script

  1. "Which queues are at risk right now?" → snapshot flags Billing Support.
  2. "Why is Billing Support at risk?" → volume +18%, AHT +11%, staffing −3.
  3. "What can we do to recover?" → move 2 agents from General Enquiries.
  4. "What would the impact be?" → SLA 72% → ~81%, General Enquiries stays above target.

What you can ask (and what you'll get back)

You never call tools by name — just ask in plain language and the assistant picks the right tool(s). The tables below list example prompts, the tool(s) that fire, and the answer you can expect on the built-in demo data (swap in your own data and the same questions work — see Bring your own data).

Reading current state

Ask something like… Tool(s) Expected answer (demo data)
"What metrics can I query?" / "What data do you have?" list_intraday_metrics The 11 Intraday metrics with definitions/units: SLA, SLA target, forecast/actual volume, forecast/actual AHT, planned/actual staffing, occupancy, backlog, risk status.
"Which queues are at risk right now?" / "Give me the intraday snapshot" get_intraday_snapshot All 5 queues with SLA/target, volume, AHT, staffing, occupancy, backlog, risk — and highest risk: Billing Support (72% vs 80% target).
"How is Sales doing?" / "Show me just Billing and General Enquiries" resolve_intraday_entity → get_intraday_snapshot The selected queue(s) only. Sales → 83% vs 80% target, watch.
"Does 'GE' / 'the billing queue' / 'tech support' map to a real queue?" resolve_intraday_entity Resolves fuzzy names/aliases → General Enquiries / Billing Support / Technical Support (with a confidence level).

Explaining risk

Ask something like… Tool(s) Expected answer (demo data)
"Why is Billing Support at risk?" resolve_intraday_entity → get_intraday_risk_drivers Primary: volume +18% above forecast. Also AHT +11%, staffing 3 below plan, occupancy 92%, backlog 47. Severity: high.
"How are actuals tracking vs forecast for Billing this morning?" get_forecast_vs_actual Six 30-min intervals (08:00–10:30); variance widens from ~0% to +18% volume / +11% AHT, staffing drifts 15 → 12.
"Which queue is under the most pressure and why?" get_intraday_snapshot → get_intraday_risk_drivers Billing Support, driven by the volume spike + AHT + staffing gap (as above).

Finding capacity & recovering

Ask something like… Tool(s) Expected answer (demo data)
"Is there spare capacity to help Billing?" / "Who can lend agents?" get_available_capacity General Enquiries can spare 2 (SLA 94% → ~92%, still above target); Complaints can spare 1. Sales & Technical Support: none (staffed to plan).
"What's the best recovery action?" / "How do we recover Billing's SLA?" summarise_intraday_recommendation Move 2 agents from General Enquiries to Billing Support for 60 min → Billing SLA 72% → ~81.2%, GE stays above target. Trade-off + "monitor AHT and backlog" included.
"What happens if I move 2 agents from General Enquiries to Billing for 60 minutes?" simulate_recovery_action Billing 72% → 81.2% (occupancy 92 → 79); GE 94% → 91.9% (occupancy 71 → 81), stays above its 80% target; no new risk.
"What if I move 3 instead?" simulate_recovery_action Billing → 84.6%, GE → 90.6% — but flagged: 3 exceeds GE's spare capacity of 2, so GE drops below its own plan.
"Can I pull 2 from Sales instead?" simulate_recovery_action Billing → 81.2%, but Sales falls 83% → ~71%, below its 80% target — not a safe donor (risk created).

Bring-your-own-data & housekeeping

Ask something like… Tool(s) Expected answer
"Load this intraday data, then tell me which queue is at risk: {…}" load_intraday_data "Loaded N queues; highest risk: <id>." Every later question then analyzes your data.
"Reset to the demo data" reset_intraday_data Restores the built-in 5-queue demo for your session.

A full worked conversation (demo data)

You: Which queues are at risk right now? Assistant: Five queues are live. Billing Support is the highest risk — SLA 72% against an 80% target. General Enquiries (94%), Technical Support (88%) and Complaints (91%) are healthy; Sales (83%) is on watch.

You: Why is Billing Support at risk? Assistant: Three things are compounding: volume is 18% above forecast, AHT is 11% higher than expected, and staffing is 3 agents below plan — pushing occupancy to 92% with a backlog of 47. The variance has widened steadily since 08:00.

You: What can we do to recover? Assistant: General Enquiries has 2 agents of spare capacity (it's 2 over plan at 94% SLA). Complaints could lend 1; Sales and Technical Support have none.

You: What would moving 2 from General Enquiries do? Assistant: Billing Support SLA improves from 72% to ~81.2% and its occupancy eases from 92% to ~79%. General Enquiries dips to ~91.9% — still comfortably above its 80% target. No new risk is created. Recommend the move for 60 minutes and keep watching AHT and backlog.

When something's off (error responses)

Ask something like… What you'll get
"Analyze the Refunds queue" An error naming the valid queues: "Could not resolve 'Refunds'… Valid queues: Billing Support, Complaints, General Enquiries, Sales, Technical Support."
"Move 50 agents from General Enquiries to Billing" "Cannot move 50 agents from General Enquiries; only 16 are staffed."
Loading data with a missing/invalid field A clear message (e.g. "queue 'x': missing required field 'sla_current'" or "risk_status must be one of ('at_risk','watch','healthy')"), and the current dataset is left untouched.

Where the data comes from

All data is synthetic and hardcoded in cxone_wfm_intraday_mcp/data.py — a fixed in-memory list of 5 queues loaded once at startup. There is no database, API, CXone tenant, network call, or randomness, so every run returns identical numbers (a repeatable demo by design). This matches the spec: for the prototype, mocked data is acceptable; the point is proving the pattern of grounded, tool-driven answers.

What's stored by hand vs. computed at call time:

  • Stored (the demo "facts"), per queue in data.py: sla_current, sla_target, volume_actual/forecast, aht_actual/forecast, staffing_planned/actual, occupancy, backlog, a risk_status label (at_risk / watch / healthy), and a 6-interval forecast-vs-actual history.
  • Computed from those stored fields at call time — this is the real "analysis":
    • get_intraday_snapshot ranks queues by stored risk_status (analysis.py).
    • get_forecast_vs_actual / get_intraday_risk_drivers compute volume/AHT/staffing variance from the stored actual-vs-forecast values and flag drivers over thresholds (analysis.py).
    • get_available_capacity derives spare agents (surplus over plan, validated against the SLA curve).
    • simulate_recovery_action recomputes SLA for both queues via the formula below (simulation.py).

So "Billing Support is at risk" is a hand-authored label, but "…because volume is +18%, AHT +11%, staffing −3, and moving 2 agents lifts SLA 72→81.2%" is genuinely calculated from the stored fields — never invented by the assistant.

Change the scenario (make a different queue at risk, tweak SLA/volume/staffing, add a queue) by editing the literals in data.py — nothing else needs to change. In a real product, data.py would be replaced by live CXone WFM as the source of truth; the tool contracts stay identical.

Bring your own data

Consumers can analyze their own Intraday data without editing any code, via two tools:

  • load_intraday_data — replace the dataset with your own queues; every other tool then analyzes your data.
  • reset_intraday_data — restore the built-in demo.

Just ask the assistant (e.g. "Load this Intraday data and tell me which queue is at risk") and provide queues shaped like:

{
  "queues": [
    {"id": "payments", "name": "Payments", "sla_current": 64, "sla_target": 85,
     "volume_forecast": 200, "volume_actual": 260, "aht_forecast": 280, "aht_actual": 320,
     "staffing_planned": 18, "staffing_actual": 14, "occupancy": 94, "backlog": 40,
     "risk_status": "at_risk", "aliases": ["pay"]},
    {"id": "onboarding", "name": "Onboarding", "sla_current": 97, "sla_target": 80,
     "volume_forecast": 90, "volume_actual": 80, "aht_forecast": 300, "aht_actual": 290,
     "staffing_planned": 7, "staffing_actual": 10, "occupancy": 55, "backlog": 0,
     "risk_status": "healthy"}
  ]
}

Per-queue fields: id, name, sla_current, sla_target, volume_forecast, volume_actual, aht_forecast, aht_actual, staffing_planned, staffing_actual (≥ 1 — the SLA curve divides by it), occupancy, backlog, risk_status (at_risk | watch | healthy). Optional: aliases (list) and intervals (list of {interval, volume_forecast, volume_actual, aht_forecast, aht_actual, staffing_planned, staffing_actual}). Percentages are 0–100, times are seconds, staffing is agent counts. Invalid input is rejected with a clear message and leaves the current dataset untouched.

Isolation: loaded data is scoped to your MCP session — on a shared HTTP server each conversation gets its own dataset (one client's load never affects another). reset_intraday_data, or the session ending, restores the demo for that session only. stdio has a single session (just you). Nothing is persisted to disk; the dataset lives in memory for the life of the session.

The simulation model

Rule-based and deterministic. Each queue has a staffing→SLA response curve sla(n) = 100 − (100 − sla_current)·decay^(n − staff_actual), with decay = exp(−2.4 / staff_actual), anchored so sla(actual) = current SLA. Every number the assistant reports comes from this formula given tool inputs — not from the model's own reasoning. See §6 of the design spec.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选