aria-service-desk-assistant
π€ ARIA β Internal Knowledge & Ticketing Assistant
MCP-connected chatbot for IT operations teams β RAG answers from Confluence, writes new articles when nothing exists, and turns chat into clean Jira tickets, streamed live
ARIA sits in front of six internal teams β Service Desk, Command Center, Network, Linux, Database, and Windows Engineering β as one chat interface. A LangChain probing layer reads each question first: if it's too vague to search on, ARIA asks one clarifying question instead of guessing. Once the issue is clear, a ChromaDB-backed retrieval loop finds the best matching Confluence article β rewording and re-searching if the first pass isn't confident β and streams a grounded answer token-by-token. If nothing matches, ARIA drafts and publishes a new article, so the knowledge base grows itself. A "Log as Ticket" action turns the raw back-and-forth of the chat into a well-formed Jira Service Desk ticket, not a dump of the conversation.
β¨ Features
Feature | Description |
ποΈ Six team spaces | Service Desk Β· Command Center Β· Network Β· Linux Β· Database Β· Windows Engineering, each with its own Confluence space + Jira project |
π§ LangChain probing layer | Reads the question before anything else runs; a genuinely vague report ("my thing is broken") gets one clarifying question instead of a guess |
π RAG retrieval (ChromaDB) | Articles are chunked and embedded, not dumped whole into the prompt; retrieval is confidence-scored and re-tried with a reworded query when the first pass is weak |
βοΈ Self-healing KB | No confident match β ARIA drafts an article, publishes it, and indexes it immediately β the next person with the same question finds it already there |
π Real-time streaming | Answers stream token-by-token over SSE with a "Musingβ¦" / "Readingβ¦" / "Draftingβ¦" status and a blinking cursor, like a live typed reply |
π‘οΈ temperature=0.2 everywhere | Every completion call β decision, synthesis, drafting, ticket writing β is grounded and low-temperature on purpose; prompts explicitly forbid inventing steps not in the source |
π‘οΈ Guardrails | Hard-blocks high-sensitivity PHI/PII (SSN, card numbers, MRNs), sexual/nudity content, and security-risk requests (credential theft, bypassing auth) before anything reaches an LLM, Confluence, or Jira |
π Soft PII redaction | Low-sensitivity contact info (email, phone, IP) is masked but still passed through β a ticket needs a reporter's email |
π« Log as Ticket | Converts the chat's raw comments into a clean summary + description + priority and files it in Jira Service Desk |
π MCP server | Every capability is also exposed as an MCP tool for Claude Desktop / Claude Code |
π§ͺ Offline-first | Local JSON knowledge base + ticket store, fully demo-able with no Jira/Confluence/Okta tenant; flip one flag to go live |
ποΈ Project Structure
aria-service-desk-assistant/
βββ main.py β FastAPI app: /api/chat, /api/chat/stream (SSE), /api/ticket, /api/teams
βββ mcp_server.py β Same capabilities exposed over MCP (stdio)
βββ config.py β Settings: mock mode, temperature, Jira/Confluence/Okta creds
βββ models.py β Team enum + Pydantic request/response models
βββ prompts.py β Prompt templates (decision, synthesis, drafting, ticket writer)
βββ probe_agent.py β LangChain layer: clarity check + search-intent reword loop
βββ vector_store.py β ChromaDB chunking/embedding/retrieval
βββ knowledge_agent.py β Orchestrates probe β retrieve β synthesize/draft, streamed
βββ ticket_writer.py β Chat transcript β clean ticket summary/description
βββ guardrails.py β Hard blocks: PHI/PII, sexual content, security risk
βββ phi_filter.py β Soft redaction: email/phone/IP, masked not blocked
βββ confluence_client.py β Search + publish articles (mock JSON β real Confluence REST)
βββ jira_client.py β Create tickets (mock JSON β real Jira REST)
βββ okta_client.py β Requester lookup, name/department only (mock β real Okta REST)
βββ kb_store.py β Local JSON-backed knowledge base (the mock/offline Confluence)
βββ data/
β βββ mock_kb.json β Seed articles per team (Troubleshooting/How-To/Runbook)
β βββ chroma/ (runtime) β Local vector index
β βββ mock_tickets.json (runtime) β Filed tickets in mock mode
βββ static/
βββ index.html β Chat UI: musing status, streaming text, blinking cursorπ Chat Flow
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β USER picks a team, asks a question β
ββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
βΌ
POST /api/chat/stream β guardrails.screen() β blocked? refuse & stop
β
βΌ
probe_agent.assess() (LangChain: ChatOpenAI + prompt template)
β
clear enough to search?
βββββββββββββββββββ΄ββββββββββββββββββ
NO YES
β β
stream back ONE clarifying vector_store: chunk + embed (ChromaDB)
question, wait for the reply β
best_articles_context(team, intent)
β
LLM: found? confidence 0.0β1.0
low confidence β probe_agent.reword()
β re-embed search β retry (max 2 rounds)
ββββββββββ΄βββββββββ
confident match still nothing
β β
stream synthesis from stream a drafted
the one matched article article, publish +
(source: knowledge_base) index it immediately
β (source: drafted_article)
ββββββββββββ¬βββββββββββ
βΌ
ChatResponse events β SSE β
UI renders with blinking cursorπ« Log as Ticket Flow
USER clicks "Log as Ticket" on the current conversation
β
βΌ
POST /api/ticket β guardrails.screen(transcript) β blocked? 400, no ticket filed
β
βΌ
phi_filter.redact_history() (soft-mask email/phone/IP)
β
βΌ
ticket_writer.build_ticket(team, history)
LLM (temperature=0.2) turns raw comments into:
{ summary, description, priority }
β
βΌ
okta_client.lookup_user(email) β requester name/department only
β
βΌ
jira_client.create_ticket(...) β files in the team's Jira project
β
βΌ
TicketResponse: { ticket_key, ticket_url, summary, description }π Quick Start
git clone https://github.com/emran-Automation-Techlead/aria-service-desk-assistant.git
cd aria-service-desk-assistant
pip install -r requirements.txt
cp .env.example .env # keep ARIA_MOCK_MODE=true to run fully offline
python main.pyGoing live against real Jira / Confluence / Okta
Set ARIA_MOCK_MODE=false in .env and fill in:
JIRA_BASE_URL=https://yourcompany.atlassian.net
JIRA_EMAIL=aria-bot@yourcompany.com
JIRA_API_TOKEN=...
CONFLUENCE_BASE_URL=https://yourcompany.atlassian.net
CONFLUENCE_EMAIL=aria-bot@yourcompany.com
CONFLUENCE_API_TOKEN=...
OKTA_DOMAIN=yourcompany.okta.com
OKTA_API_TOKEN=...No code changes required β confluence_client.py, jira_client.py, and okta_client.py switch from the local JSON stores to live REST calls automatically. The vector index (vector_store.py) indexes whatever confluence_client.search() returns either way, so RAG retrieval works identically against mock or live content.
Running the MCP server
python mcp_server.pyPoint any MCP host (Claude Desktop, Claude Code) at this command to give it search_knowledge_base, create_confluence_article, create_jira_ticket, and lookup_okta_user as tools β each screened by the same guardrails as the chat UI.
π‘οΈ Guardrails vs. soft redaction
Two distinct layers, on purpose:
guardrails.pyβ hard refusal. Runs before anything reaches an LLM, Confluence, or Jira. Blocks outright: high-sensitivity PHI/PII (SSNs, card numbers, medical record numbers, via regex), sexual/nudity content (OpenAI Moderation API, scored against thresholds tuned tighter than the API's ownflaggeddefault β validated so ordinary IT phrasing like "kill the process" scores near zero), and security-risk requests (credential theft, bypassing MFA/auth, malware, exfiltration β a curated keyword heuristic plus the moderation model's illicit-content score, since phrasing like this isn't reliably caught by moderation alone).phi_filter.pyβ soft redaction. Low-sensitivity, operationally-necessary contact details (email, phone, IP) are masked with[REDACTED-<TYPE>]but the message still goes through β a ticket needs a reporter's email, a network issue needs an IP. API responses carry aredactionsaudit list (type + count).
π οΈ Tech Stack
Layer | Technology |
API | FastAPI + Uvicorn, SSE streaming |
AI | OpenAI ( |
Orchestration | LangChain ( |
RAG | ChromaDB β chunked, embedded ( |
Safety | OpenAI Moderation API + custom PHI/PII and security-risk pattern checks |
Agent protocol | MCP ( |
Integrations | Jira Service Desk REST, Confluence Cloud REST, Okta REST β each with an offline JSON-backed mock mode |
Config | Pydantic Settings v2 Β· python-dotenv |
UI | Single-page vanilla HTML/JS chat widget β SSE consumer, musing status, blinking cursor |
π License
MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/emran-Automation-Techlead/aria-service-desk-assistant'
If you have feedback or need assistance with the MCP directory API, please join our Discord server