Skip to main content
Glama

πŸ€– 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.

Python FastAPI LangChain ChromaDB MCP License


✨ 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


Related MCP server: confluence-mcp

πŸ—οΈ 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.py

Open http://localhost:7862

Going 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.py

Point 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 own flagged default β€” 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 a redactions audit list (type + count).


πŸ› οΈ Tech Stack

Layer

Technology

API

FastAPI + Uvicorn, SSE streaming

AI

OpenAI (gpt-4o-mini by default, temperature=0.2 everywhere)

Orchestration

LangChain (langchain-openai + langchain-core) for the intake-probing/reword loop

RAG

ChromaDB β€” chunked, embedded (text-embedding-3-small), confidence-scored retrieval

Safety

OpenAI Moderation API + custom PHI/PII and security-risk pattern checks

Agent protocol

MCP (mcp Python SDK, stdio transport)

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

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