Skip to main content
Glama
Ray-Khosravi

full-ai-customer-service-handling

by Ray-Khosravi

Full AI Customer Service Handling

An AI receptionist for service businesses that actually books the job. Website chat → safety check → live technician calendars → SMS to the technician → customer confirms → appointment lands in Outlook. Fully asynchronous, fully audited, zero customer sign-in.

Azure Functions Microsoft Foundry MCP Python Tests Last commit License


Why this exists

Every plumber, electrician and HVAC company loses jobs the same way: the phone rings while the technician is under a sink, the website form goes to an inbox nobody checks, and the customer books the next company that answers. This project turns a website chat widget into a 24/7 receptionist that dispatches real technicians — reading their real calendars, texting them, and confirming with the customer — while the business owner does nothing but keep their Outlook calendar up to date.

Built for Nexroza Plumbing (Toronto); designed so any field-service business can fork it.

Related MCP server: Microsoft MCP

What a customer sees (real transcript from the end-to-end test)

Customer: My gas water heater is leaking in Scarborough. I need someone today after 4 PM. Assistant: Thanks — a few safety questions first. 1) Do you smell gas or hear hissing near the water heater? If yes, leave the building, don't operate switches, call your gas utility emergency line or 911 from outside… 2) Any active flooding or water near electrical outlets? … Customer: No gas smell, no flooding — just dripping from the bottom of the tank. Tomorrow after 4 PM works. Customer: My name is Sam Tester, phone 416‑555‑0177, 25 Test Road, Scarborough, M1B 2K3. Yes, text me updates. Assistant: Request submitted. A technician (Michael) is being contacted by SMS to confirm an appointment on 2026‑09‑14 between 4:00 PM and 8:00 PM. Tracking reference: NX‑QWL6A99EPNOR. Confirmation will follow once he replies.

(Michael texts back "I can do 5:30 PM". Later…)

Customer: Any update on NX‑QWL6A99EPNOR? My phone ends in 0000. Assistant: Sorry — I couldn't find a request with that reference + phone. Customer: Sorry, my number is 416‑555‑0177. Assistant: Michael proposed Monday September 14 at 5:30 PM. Do you confirm? Customer: Yes, 5:30 works. Assistant: Done — your booking is confirmed. Technician: Michael · Monday September 14 at 5:30 PM.

Behind that last line an Outlook event was created in Michael's calendar and the request moved to confirmed — and only then did the assistant say "confirmed".

How it works

flowchart LR
    W[🌐 Website chat widget<br/>no login] -->|HTTPS| B[Website backend<br/>session · rate limit]
    B -->|Entra identity| A[🤖 Foundry Agent<br/>gpt‑5‑mini · safety check · intake]
    A -->|MCP + system key| F[⚡ Azure Functions MCP server<br/>8 tools]
    F --> T[(Table Storage<br/>requests · audit · idempotency)]
    F -->|calendarView / create event| O[📅 Outlook calendars<br/>owner token, silent refresh]
    F -->|queue sms‑outbound| S[📱 SMS provider<br/>Twilio · ACS · mock]
    S -->|signed webhook| F
    F -->|queue sms‑inbound| T
    E[⏱ expiry timer<br/>timeout → next technician] --> T
sequenceDiagram
    autonumber
    participant C as Customer
    participant AG as Foundry Agent
    participant MCP as MCP server
    participant Q as Queues
    participant TECH as Technician (SMS)
    participant CAL as Outlook
    C->>AG: "Water heater leaking, after 4 PM"
    AG->>C: Safety check, collect name / phone / address
    AG->>MCP: create_service_request → NX‑ref
    AG->>MCP: find_matching_technicians (skill · area · schedule · live calendar)
    AG->>MCP: send_technician_request (returns immediately)
    MCP->>Q: sms‑outbound
    Q->>TECH: "Nexroza job ABCD: Water heaters in M1B, Mon 4–8 PM. Reply YES / a time / NO"
    AG->>C: "Submitted — reference NX‑…, confirmation follows"
    TECH-->>MCP: "I can do 5:30 PM" (signed webhook → sms‑inbound)
    C->>AG: "Status of NX‑…? phone ends 0177"
    AG->>MCP: get_service_request_status → proposed 5:30 PM
    C->>AG: "Yes"
    AG->>MCP: confirm_booking (ETag lock → re‑check calendar → create event)
    MCP->>CAL: event created
    AG->>C: "Confirmed: Michael, Mon 5:30 PM"
stateDiagram-v2
    [*] --> new
    new --> matching
    matching --> awaiting_technician: SMS queued
    awaiting_technician --> technician_proposed_time: reply "YES" / "5:30 PM"
    awaiting_technician --> technician_declined: reply "NO"
    awaiting_technician --> expired: no reply in 30 min
    technician_declined --> awaiting_technician: next technician
    expired --> awaiting_technician: next technician
    expired --> failed: nobody left
    technician_proposed_time --> awaiting_customer_confirmation: shown to customer
    awaiting_customer_confirmation --> confirmed: customer accepts → Outlook event
    awaiting_customer_confirmation --> awaiting_technician: customer declines → ask again
    confirmed --> [*]

The MCP tools

Tool

What it does

check_availability

weekly schedule ∧ live Outlook status per technician for a date/window

get_technician_work_status

WORKING / OFF / SICK / VACATION / ON_CALL from calendar events

create_service_request

validates input, returns a high‑entropy tracking reference (NX‑…)

find_matching_technicians

skill · active · service area (postal FSA) · schedule · calendar · pending load · on‑call fallback

send_technician_request

queues the SMS and returns at once — the agent never waits

get_service_request_status

reference + callback phone → customer‑safe status and any proposed time

confirm_booking

ETag‑locked, calendar re‑checked, Outlook event created, then — and only then — confirmed

list_technician_calendars

technicians, skills, calendar presence

What makes it production‑grade

  • Asynchronous by design — no HTTP request or agent run ever waits for a human. Storage queues, signed webhooks, 5‑minute expiry sweeps, automatic fallback to the next technician, poison‑queue handling.

  • Never invents availability — the agent must call a tool before any availability claim; a tool failure is reported honestly ("live availability couldn't be checked"), never guessed.

  • Says "confirmed" only after Outlook says so — the booking is created with an ETag lock (4 concurrent confirmations → exactly one event) and re‑checks the calendar first.

  • Security first — MCP endpoint behind a system key held in a Foundry connection (never in the browser), customers never sign in to Microsoft, tracking references are ~72‑bit random and stored hashed, second factor = callback phone, internal IDs never leave the server, provider webhooks validated (X‑Twilio‑Signature / HMAC), phone numbers and message IDs redacted in logs, no PII in alerts or SMS bodies, prompt‑injection hardened (plus Azure prompt shields).

  • Owner authorizes once — a PKCE public‑client flow (no client secret) stores a refresh token in a private blob; the server refreshes it silently. A 30‑minute heartbeat and Azure Monitor alerts tell you before the customer does.

  • Observable — status history per request, Application Insights markers (SERVICE_REQUEST_FAILED, SMS_DELIVERY_FAILURE, POISON_MESSAGE, BOOKING_FAILED, …), 4 alert rules for ≈ US$2/month.

  • Cheap to run — Flex Consumption Functions + Table Storage + queues ≈ a few dollars a month; the model is pay‑per‑token.

Quick start

git clone https://github.com/Ray-Khosravi/full-ai-customer-service-handling
cd full-ai-customer-service-handling
cp deploy.env.example deploy.env        # your resource names (git-ignored)

# 1) infrastructure + function app (Azure Developer CLI)
cd src/FunctionsMcpTool && azd up

# 2) one-time Outlook authorization by the business owner (opens a private browser window)
python ../../tools/authorize_outlook.py

# 3) Foundry: store the MCP key in a project connection and publish the agent
python ../../foundry/create_connection.py
az rest --method post --resource https://ai.azure.com \
  --url "https://<foundry-account>.services.ai.azure.com/api/projects/<project>/agents/Plumbing-Service-Coordinator/versions?api-version=2025-11-15-preview" \
  --headers Content-Type=application/json --body @../../foundry/agent-version.json

# 4) talk to it
python ../../web/test_client/server.py     # http://localhost:8787

Full runbook — calendar naming rules, database schema, SMS providers (Twilio / Azure Communication Services / mock), monitoring, troubleshooting, costs: docs/OPERATIONS.md. Website integration contract: web/INTEGRATION.md.

Testing

Layer

Command

Covers

Unit (43)

pytest src/FunctionsMcpTool/tests -q

validation, state machine, matching, OFF/SICK/VACATION exclusion, ON_CALL fallback, Toronto DST, duplicate webhooks, decline / timeout / fallback, confirmation, Graph failure, concurrency, secure lookup, injection, provider signatures

Deployed workflow

python foundry/test_mcp_workflow.py

MCP → queues → signed webhook → status → Outlook event

Full conversation

python foundry/test_e2e_conversation.py

the transcript above, through the real agent

Live SMS (hybrid)

python foundry/test_twilio_hybrid.py

one real Twilio SMS + real delivery callback; simulated carrier step; 403 on bad signature; idempotent duplicates

Repository layout

src/FunctionsMcpTool/   Azure Functions app — function_app.py (triggers) + nexroza/ (workflow, store, graph, sms, matching, replies)
foundry/                agent instructions & version, connection script, operator scripts, end-to-end tests
tools/                  one-time Outlook owner authorization
web/                    website integration contract + standalone chat widget/test backend
infra/                  Bicep for azd (Function App, storage, identity, monitoring & alerts)
docs/                   operations runbook, hero animation

Roadmap

  • Customer‑side SMS OTP as the second factor (after a real SMS number is live)

  • Customer self‑service cancel / reschedule tool

  • Key Vault references for provider secrets

  • RAG over pricing, warranty and FAQ policies

  • Multi‑tenant onboarding (one deployment, many businesses)


Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A simple MCP server that enables meeting room booking through an AI assistant, supporting room availability checks and booking operations with React Agent pattern for tool calling.
    2
    -
  • F
    license
    C
    quality
    D
    maintenance
    A powerful MCP server that enables AI assistants to interact with Microsoft Graph API for managing Outlook emails, Calendar events, OneDrive files, and Contacts through natural language commands.
    35
    56
    -
  • F
    license
    B
    quality
    B
    maintenance
    MCP server providing AI assistants with full access to Microsoft Outlook email and calendar via the Microsoft Graph API, featuring 26 tools for mail, calendar, contacts, and scheduling with delegated authentication.
    29
    -