Skip to main content
Glama
AraksyaG

SmartPark Central MCP Server

by AraksyaG

SmartPark Central — MCP Server for Confirmed Reservations (Stage 3)

An intelligent assistant for a parking facility, built with LangChain and LangGraph on a Retrieval-Augmented Generation (RAG) architecture. The bot answers questions about the garage, looks up live prices / opening hours / availability, interactively collects reservation details, and protects personal data with a guardrails layer.

This repository is Stage 3 of the EPAM final project. It builds on the Stage 1 RAG chatbot and the Stage 2 human-in-the-loop approval flow by adding an MCP (Model Context Protocol) server that records every approved reservation to a text file in the format Name | Car Number | Reservation Period | Approval Time. The server is secured (shared-token auth, input sanitisation, path confinement, atomic locked writes). When the administrator approves a request, the confirmed booking is written automatically — via the MCP server or an equivalent direct function call.


Features

Requirement (brief)

Where it lives

RAG chatbot architecture

graph.py (LangGraph agent), vector_store.py, tools.py

Vector database for information

Milvus (vector_store.py; Milvus Lite locally)

Static/dynamic data split (optional bonus)

Static → Milvus (knowledge_base.py); dynamic (prices, hours, availability) → SQLite/SQLAlchemy (dynamic_db.py)

Provide information to users

retriever tool + 3 dynamic-data tools (tools.py)

Collect user inputs for reservations

reservation.py (validated slot-filling) + submit_reservation tool

Guardrails against sensitive-data exposure

guardrails.py (Microsoft Presidio + regex fallback)

Evaluation (Recall@K, Precision, latency)

evaluation/evaluate.py

Second agent for the administrator

admin_agent.py (compose request + interpret reply)

Escalate reservation to a human

tools.py::submit_reservationreservation_store + channels

Send request / receive decision

channels.py (memory/email/REST) + admin_service.py (FastAPI)

Maintain agent-to-agent communication

reservation lifecycle in reservation_store.py, status tool

MCP server to write data to file

mcp_server.py (FastMCP) + recorder.py (secure core)

Record on approval (Name | Car | Period | Time)

admin_service.py approval hook → recorder/mcp_client

Secure & resistant to unauthorised access

token auth, input sanitisation, path confinement, file locking


Related MCP server: MCP Filesystem Server

MCP server (Stage 3)

MCP (Model Context Protocol) is an open standard for exposing tools to AI apps. Our server (mcp_server.py, built with the official SDK's FastMCP) exposes one tool, record_reservation, that appends a confirmed booking to a text file. The write logic (recorder.py) is shared by the MCP path and a direct function-call fallback.

poetry run parking-mcp-server         # run the MCP server (stdio transport)

Flow: administrator approves a request (Stage 2 API) → the approval hook records it → a line is appended to data/confirmed_reservations.txt:

Anna Smith | AB12CD | 2026-07-15 09:00 to 18:00 | 2026-07-11T10:00:00+00:00

Security measures

  • Token auth — the recorder rejects writes without the correct MCP_AUTH_TOKEN.

  • Input sanitisation|, \n, \r are stripped so a value can't forge extra columns/rows (format injection).

  • Path confinement — the target file must resolve inside the project data/ dir, blocking path traversal (../../etc/...).

  • Atomic, locked appends — a thread lock plus an OS advisory file lock prevent corrupted/interleaved lines under concurrent approvals.

Set USE_MCP=true to record through the MCP server (client↔server over stdio); USE_MCP=false (default) uses the equivalent direct call. The full stdio round-trip is covered by an e2e test (RUN_MCP_E2E=1 pytest).


Human-in-the-loop flow (Stage 2)

 user ──▶ chatbot (agent 1) ──collect details──▶ submit_reservation
                                                     │
                                       save PENDING record (reservation_store)
                                                     │
                                       notify admin (channel: memory/email/REST)
                                                     ▼
                          administrator ──▶ Admin REST API (/pending, /decision)
                                                     │
                                 AdminAgent (agent 2) interprets the reply
                                                     ▼
                                    reservation → APPROVED / REFUSED
                                                     │
 user ──ask status──▶ chatbot ──check_reservation_status──▶ reports outcome

Running the admin API

poetry run parking-admin-api          # serves http://localhost:8001

Example (the admin approves a request):

TOKEN=change-me-admin-token           # set ADMIN_API_TOKEN in .env for real use
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8001/pending
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"reply":"approve, looks fine","admin":"robert"}' \
     http://localhost:8001/reservations/SP-ABC123/decision

The reply field can be free text ("yes, that's fine" / "no, we're full") — the AdminAgent interprets it into an approved / refused decision.


Architecture

                 ┌──────────────────────────── LangGraph agent ───────────────────────────┐
  user  ──▶  query_or_respond ──(tool needed?)──▶  tools  ──▶  generate ──▶  answer  ──▶ user
                    │  (LLM decides)                  │            │ (grounded + PII-redacted)
                    │                                 │
                    └── no tool: answer directly      ├─ search_parking_info ─▶ Milvus (static KB)
                       (e.g. next reservation Q)      ├─ get_parking_prices   ─┐
                                                      ├─ get_working_hours     ├▶ SQLite (dynamic)
                                                      ├─ check_availability    ─┘
                                                      └─ submit_reservation    ─▶ validated request
  • Static knowledge (general info, location, rules, booking process, FAQ) is embedded with text-embedding-3-small and stored in Milvus.

  • Dynamic data (prices, hours, live availability) lives in SQLite and is queried exactly — never embedded, never hallucinated.

  • The LLM (Azure OpenAI gpt-4o-mini) chooses which tool to call, then writes a grounded answer. Every retrieved chunk and every final answer passes through the guardrails so personal data cannot leak.


Project structure

Stage1_RAG_Chatbot/
├── src/parking_chatbot/
│   ├── config.py          # settings from .env (pydantic-settings)
│   ├── llm.py             # Azure chat + embedding model factories
│   ├── knowledge_base.py  # load + chunk static markdown docs
│   ├── vector_store.py    # Milvus build/load/retriever
│   ├── dynamic_db.py      # SQLite schema, seeding, queries
│   ├── guardrails.py      # PII detection + redaction (Presidio / regex)
│   ├── reservation.py     # validated reservation model + slot-filling
│   ├── tools.py           # LangChain tools bound to the agent
│   ├── graph.py           # LangGraph agent (query_or_respond→tools→generate)
│   ├── chatbot.py         # high-level Chatbot facade
│   └── cli.py             # terminal chat REPL
├── data/
│   ├── static/            # knowledge base (markdown) → vector DB
│   └── dynamic/           # SQLite DB is created here
├── evaluation/            # eval dataset + Recall@K / Precision / MRR / latency
├── scripts/ingest.py      # build vector store + seed SQL DB
├── tests/                 # pytest suite (offline, LLM mocked)
├── docker-compose.yml     # optional full Milvus server
└── .github/workflows/ci.yml

Setup

Prerequisites: Python 3.11/3.12, and — for anything that calls the model — an Azure OpenAI resource reachable from the EPAM VPN.

# 1. install dependencies (Poetry)
poetry install

# 2. configure credentials
cp .env.example .env
#   then edit .env and fill in AZURE_OPENAI_API_KEY / AZURE_OPENAI_ENDPOINT

# 3. build the knowledge base + dynamic DB  (needs VPN for embeddings)
poetry run parking-ingest

MILVUS_URI defaults to a local file (./milvus_lite.db) — Milvus Lite, so no server is needed. To use a full Milvus server instead:

docker compose up -d          # starts Milvus on localhost:19530
# set MILVUS_URI=http://localhost:19530 in .env, then re-run parking-ingest

Usage

poetry run parking-chat

Example conversation:

You: what are your opening hours?
Assistant: SmartPark Central is open 24 hours a day, 7 days a week.

You: how much is daily parking for a normal car?
Assistant: Standard car parking is 24.00 USD per day.

You: I want to book a space
Assistant: Sure! What is your first name?
... (collects first name, last name, car number, period, confirms, submits)

You: what's the manager's phone number?
Assistant: I'm sorry, I can't share staff contact details. Please call our support
           hotline on +1-555-0100.

Evaluation

poetry run python evaluation/evaluate.py --k 3

Produces evaluation/results/report.md and metrics.json with Recall@K, Precision@K, MRR and latency (mean / p95). See the report for the per-question breakdown.


Testing

poetry run pytest            # 27 tests, fully offline (LLM + Milvus mocked)
poetry run pytest --cov=parking_chatbot

The suite never contacts Azure or a Milvus server, so it runs in CI without a VPN.


Guardrails / data protection

Personal data (staff phones, existing customer records, licence plates) may exist in the knowledge base. The guardrails layer redacts it twice: once when retrieved context is serialised for the model, and again on the final answer. Two backends:

  • Presidio (default): pretrained spaCy NLP model detects PERSON etc., plus pattern recognisers for email / phone / credit card and a custom licence-plate recogniser.

  • Regex fallback (USE_PRESIDIO=false): no model download; covers email, phone, credit card and plates. Used in CI.


Tech choices in one line each

  • LangChain + LangGraph — required by the brief; LangGraph gives an explicit, inspectable state machine with built-in conversation memory.

  • Milvus — a production vector DB explicitly recommended by the brief; Milvus Lite makes local dev/testing zero-setup.

  • SQLite/SQLAlchemy for dynamic data — exact, cheap-to-update structured answers where embeddings would be the wrong tool.

  • Azure OpenAI gpt-4o-mini + text-embedding-3-small — the models available through EPAM, matching the course material.

  • Presidio — the open-source standard for PII detection using pretrained NLP.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    A secure Model Context Protocol server that provides controlled filesystem access within predefined directories, enabling AI models to perform file and directory operations with strict path validation.
    16
    5 npm
    7
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Provides secure filesystem access for AI models through the Model Context Protocol with strict path validation, file operations, directory management, and system command execution within predefined directories.
    16
    5 npm
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    A Model Context Protocol server that provides secure, sandboxed file system operations including reading, writing, and managing files within a configurable base directory. It features robust path traversal protection to ensure all file interactions remain confined to a safe, restricted environment.
    -