{"id":31817647,"url":"https://github.com/hiborn4/travel-agent","last_synced_at":"2025-10-29T13:04:14.130Z","repository":{"id":313458472,"uuid":"1034306443","full_name":"HiBorn4/Travel-Agent","owner":"HiBorn4","description":"AI-powered Travel Assistant Bot built with Model Context Protocol (MCP), LangGraph, and Chainlit/Streamlit frontends. Handles travel requests, employee ID validation, trip planning, and booking confirmation with Azure OpenAI integration.","archived":false,"fork":false,"pushed_at":"2025-09-20T09:20:17.000Z","size":433,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-20T11:30:05.137Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HiBorn4.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.rst","dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-08T07:18:38.000Z","updated_at":"2025-09-20T09:20:55.000Z","dependencies_parsed_at":"2025-09-06T08:24:06.528Z","dependency_job_id":"474dec4c-f4f0-43ec-922c-aa92293370fa","html_url":"https://github.com/HiBorn4/Travel-Agent","commit_stats":null,"previous_names":["hiborn4/travel_bot"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/HiBorn4/Travel-Agent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HiBorn4%2FTravel-Agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HiBorn4%2FTravel-Agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HiBorn4%2FTravel-Agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HiBorn4%2FTravel-Agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HiBorn4","download_url":"https://codeload.github.com/HiBorn4/Travel-Agent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HiBorn4%2FTravel-Agent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279006841,"owners_count":26084204,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-11T10:21:57.831Z","updated_at":"2025-10-11T10:21:58.765Z","avatar_url":"https://github.com/HiBorn4.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Travel Bot\n\n---\n\nhttps://github.com/user-attachments/assets/7fbf9fe4-6f82-4595-a516-2d4a1ce1397d\n\n# Table of contents\n\n1. [Project structure](#project-structure)\n2. [High-level architecture](#high-level-architecture)\n3. [The MCP approach — deep dive](#the-mcp-approach---deep-dive)\n4. [The LangGraph approach — deep dive](#the-langgraph-approach---deep-dive)\n5. [Frontend options: Streamlit \u0026 Chainlit](#frontend-options-streamlit--chainlit)\n6. [Setup \u0026 installation (local dev)](#setup--installation-local-dev)\n7. [Environment variables](#environment-variables)\n8. [How to run (quick start)](#how-to-run-quick-start)\n9. [API surface \u0026 sample calls](#api-surface--sample-calls)\n10. [Prompts \u0026 prompt engineering notes](#prompts--prompt-engineering-notes)\n11. [Troubleshooting \u0026 common errors](#troubleshooting--common-errors)\n12. [Testing \u0026 QA suggestions](#testing--qa-suggestions)\n13. [Design tradeoffs \u0026 recommendation](#design-tradeoffs--recommendation)\n14. [Docs images (placeholders) — where to place them](#docs-images-placeholders---where-to-place-them)\n15. [Contributing \u0026 License](#contributing--license)\n\n---\n\n# Project structure\n\nCommand used to print structure: `python3 structure.py` — current output:\n\n```\n├── app.py\n├── chainlit.md\n├── client.py\n├── langgraph\n│   ├── app copy.py\n│   └── main copy.py\n├── main.py\n├── pyproject.toml\n├── requirements.txt\n├── src\n└── structure.py\n```\n\nImportant files and folders:\n\n* `main.py` — **MCP server** using `FastMCP` (tools: `new_travel_request`, `travel_data_collected`, `out_of_domain_tool`).\n* `client.py` — **FastAPI wrapper / MCP client** that spawns the MCP server stdio subprocess using `mcp-python-client` / `ClientSession` and exposes `/chat` endpoint consumed by frontends.\n* `app.py` — **Streamlit** frontend (older) or **Chainlit** frontend (migrated version) depending on which `app.py` you run (you may have multiple copies).\n* `langgraph/main copy.py`, `langgraph/app copy.py` — **LangGraph** implementation (custom orchestrator + Chainlit frontend).\n* `requirements.txt` / `pyproject.toml` — dependency list.\n* `chainlit.md` — notes for Chainlit usage or configuration.\n\n\u003e Tip: Keep `main.py` and `client.py` as the canonical MCP implementation and keep `langgraph/` as an alternate approach (experimental). Keep `app.py` for Chainlit UI (rename other copies like `app_streamlit.py` to avoid confusion).\n\n---\n\n# High-level architecture\n\n## Two parallel approaches\n\n1. **MCP approach (recommended)**\n\n   * `main.py` runs as an MCP server: it **registers callable tools** (Python functions) with `FastMCP`.\n   * A client (in `client.py`) uses `mcp-python-client` / `ClientSession` or `stdio_client` to connect to the server process over STDIO (or run `mcp.run(transport=\"http\")` for HTTP/SSE).\n   * A FastAPI wrapper (`client.py` as server) exposes `/chat` and selects which tool to invoke (router prompt) and calls that tool via MCP.\n   * Frontend (Chainlit) calls `POST /chat` and displays the assistant response.\n\n2. **LangGraph approach (alternate)**\n\n   * `langgraph/main copy.py` defines a `StateGraph` workflow: `select_tool` → `call_tool` nodes.\n   * The graph calls the LLM for tool selection and then calls local functions directly.\n   * `langgraph/app copy.py` (Chainlit) calls a FastAPI endpoint `/agent` that executes the graph and returns the result.\n\nBoth approaches use Azure OpenAI (via `langchain_openai.AzureChatOpenAI`) in this repo. Both store simple in-memory `data` (employee\\_id and `travel_data`) for session-less demo purposes. For production you should persist per-user session to DB.\n\n---\n\n# The MCP approach — deep dive\n\n## What MCP is (short)\n\nModel Context Protocol (MCP) provides a standardized way to:\n\n* Expose \"tools\" (functions) a server can run,\n* Allow LLMs and clients to discover available tools (`list_tools`),\n* Call tools in a structured manner (`call_tool`),\n* Use multiple transports (stdio, HTTP/SSE, WebSocket), and\n* Enable orchestration across systems.\n\n## How it's used here\n\n* `main.py` registers functions with `@mcp.tool()`:\n\n  * `new_travel_request(user_query: str) -\u003e str`: captures employee ID (expects 8-digit).\n  * `travel_data_collected(messages: list[dict]) -\u003e dict/str`: collects travel details and returns JSON or plain `response`.\n  * `out_of_domain_tool(messages: list[dict]) -\u003e str`: polite redirect for non-travel input.\n\n* `client.py` runs a small FastAPI app that:\n\n  * Spawns `main.py` subprocess (stdio) using `StdioServerParameters` + `stdio_client`.\n  * Keeps a `ClientSession` to call `list_tools()` and `call_tool(...)`.\n  * Provides `/chat` endpoint. It does:\n\n    1. Build a compact *selector prompt* (available tools + conversation).\n    2. Ask an LLM which tool to call (or pick heuristics / call travel tool directly for 8-digit ID).\n    3. Call chosen tool via MCP and return a cleaned assistant response.\n\n## Advantages of MCP here\n\n* Tools are discoverable: any client can ask the server what tools it provides.\n* Decouples LLM orchestration and tool implementation.\n* Interchangeable transports.\n* Easier to integrate multiple servers (HR server, payments server, travel server).\n\n---\n\n# The LangGraph approach — deep dive\n\n## What LangGraph does\n\nLangGraph models conversation logic with nodes and edges. You define a state type (`AgentState`) and create nodes (functions) that manipulate state and call LLM. It's a custom finite-state graph + LLM orchestration.\n\n## How it's used here\n\n* `main copy.py` uses `StateGraph`:\n\n  * `select_tool(state)` — LLM returns one tool name (string).\n  * `call_selected_tool(state)` — calls the selected function from `TOOL_REGISTRY`.\n* `app copy.py` uses Chainlit to call FastAPI `/agent` which drives the graph.\n\n## Strengths \u0026 differences\n\n* Very deterministic flow — you control node transitions and function calling order.\n* Easier to reason about complex workflows with branching and retries.\n* Tighter coupling between graph and tools (good for domain-specific controlled flows).\n\n## Tradeoffs vs MCP\n\n* Not easily discoverable by external clients — custom architecture.\n* Harder to plug new services dynamically compared to MCP.\n* Good when workflow must be tightly controlled and deterministic.\n\n---\n\n# Frontends: Streamlit vs Chainlit\n\n## Streamlit\n\n* Quick to spin up, familiar for dashboards.\n* Chat UI is possible but requires custom styling for good UX (we implemented a polished `app.py` with CSS).\n* Not optimized for streaming assistant typing or chat-centric features.\n\n## Chainlit (recommended for chat)\n\n* Built for chatbots: delivers avatars, typing indicators, markdown rendering, steps, streaming messages, and developer tools.\n* Simplifies frontend code: you send assistant messages and Chainlit handles rendering.\n* Use Chainlit when you want a production chat-like UX fast.\n\n---\n\n# Setup \u0026 installation (local dev)\n\n1. Create \u0026 activate Python environment:\n\n```bash\npython -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\n```\n\n(If you use conda: `conda create -n api_agent python=3.10 \u0026\u0026 conda activate api_agent` and then `pip install -r requirements.txt`.)\n\n2. Install tools used by repo:\n\n* `fastmcp` (for server)\n* `mcp-python-client` (client)\n* `fastapi`, `uvicorn`\n* `langchain-openai` (Azure client)\n* `chainlit` (frontend)\n* `loguru`, `python-dotenv`, etc.\n\nExample `requirements.txt` snippet:\n\n```\nfastmcp\nmcp-python-client\nfastapi\nuvicorn\nlangchain-openai\nchainlit\nloguru\npython-dotenv\nrequests\n```\n\n3. Put your Azure OpenAI keys in a `.env` file (see next section).\n\n---\n\n# Environment variables\n\nCreate `.env` at project root:\n\n```\nAZURE_OPENAI_API_KEY=your_api_key_here\nAZURE_OPENAI_ENDPOINT=https://\u003cyour-resource-name\u003e.openai.azure.com\nAZURE_OPENAI_API_VERSION=2024-02-15-preview\nAZURE_OPENAI_DEPLOYMENT=gpt-4o-mini\n```\n\nIf your code uses different env names (e.g., `AZURE_OPENAI_DEPLOYMENT`), make sure they match.\n\n---\n\n# How to run (quick start)\n\n### Option A — run the MCP server (stdio) and the FastAPI client wrapper (recommended dev flow)\n\n1. Start FastAPI wrapper (it spawns the MCP server as a stdio subprocess):\n\n```bash\nuvicorn client:app --reload\n```\n\n* This will:\n\n  * Start a FastAPI server at `http://127.0.0.1:8000`.\n  * Inside startup, spawn `python main.py` as a subprocess and create an MCP `ClientSession` over stdio.\n  * Use `/chat` endpoint for frontends.\n\n2. Start Chainlit frontend:\n\n```bash\nchainlit run app.py -w\n```\n\n* Interact in the Chainlit UI (it will call `POST http://127.0.0.1:8000/chat`).\n\n### Option B — run main.py directly (HTTP transport)\n\nIf you modify `main.py` to run HTTP transport:\n\n```python\n# in main.py\nmcp.run(transport=\"http\", host=\"127.0.0.1\", port=8888)\n```\n\nThen call tools over HTTP/SSE (client needs to support it). This avoids stdio subprocess and is suitable for production.\n\n---\n\n# API surface \u0026 sample calls\n\n### Endpoint: `POST /chat`\n\n* Body: plain text (user message).\n* Header: `X-History`: optional conversation history (string).\n* Returns: assistant reply (plain text). On errors it returns a short error message.\n\n**Sample curl**:\n\n```bash\ncurl -X POST \"http://127.0.0.1:8000/chat\" \\\n  -H \"Content-Type: text/plain\" \\\n  -d \"I want to book a trip from Mumbai to Pune on 2025-09-15\"\n```\n\n### Expected behavior:\n\n* If the message is 8-digit number → `new_travel_request` will capture employee id.\n* If travel details → `travel_data_collected` will be called and return a confirmation message.\n* If offtopic → `out_of_domain_tool` politely declines.\n\n---\n\n# Prompts \u0026 prompt-engineering notes\n\n### General guidelines used in this project:\n\n* Keep selector prompts **concise** and **rule-driven**: give the LLM enough context to choose a single tool.\n* Prefer **structured JSON output** from LLM when you want machine-readability (tools), but **unwrap** to human-readable text for UI.\n* Use **local heuristics** before calling the LLM to reduce cost (e.g., exact 8-digit detect).\n* Use **robust parsing**: always `json.loads` safely and accept double-encoded JSON strings.\n* Keep **explicit constraints** (date format, example outputs) but avoid long enumerations that can be ignored by the model.\n\n### Example selector prompt (compact)\n\n```\nYou are a tool selector. Tools: new_travel_request, travel_data_collected, out_of_domain_tool.\nConversation (last messages): ...\nUser now says: \"\u003cinput\u003e\"\nRules:\n- If input is exactly 8 digits → \"new_travel_request\".\n- If input contains travel details and employee_id exists → \"travel_data_collected\".\n- If unrelated → \"out_of_domain_tool\".\nReturn JSON only: {\"tool\":\"\u003ctool-name\u003e\", \"arguments\":{...}} OR plain answer if no tool is needed.\n```\n\n---\n\n# Troubleshooting \u0026 common errors\n\n### `ImportError: cannot import name 'serve_stdio' from 'mcp.server'`\n\n* Fix: Use `FastMCP.run()` directly; `serve_stdio` was removed/deprecated.\n\n### `ImportError: cannot import name 'Client' from 'mcp'`\n\n* Fix: Use `mcp-python-client` package (e.g., `from mcp_python_client import MCPClient`) or low-level `ClientSession` with `stdio_client`.\n\n### Backend returning escaped JSON in UI\n\n* Fix backend to unwrap `{\"response\":...}` into plain text, or sanitize in frontend (Chainlit) by parsing and extracting `response` (the recommended approach is to fix backend so the UI always gets plain text for simple replies).\n\n### LLM parsing errors (invalid JSON)\n\n* Solution:\n\n  * Add explicit example outputs in prompts that produce JSON (show exactly expected structure).\n  * Use `try: json.loads()` with fallback.\n  * Accept double-encoded JSON strings and parse twice.\n\n---\n\n# Testing \u0026 QA suggestions\n\n* **Unit tests for prompt parsing**: feed model-like strings and test `safe_json_load` logic.\n* **Integration tests**:\n\n  * Start server in CI (or as subprocess) and run example flows via HTTP client.\n  * Mock LLM responses in tests to validate state transitions.\n* **Manual test cases**:\n\n  * 8-digit ID only → should capture employee id.\n  * Purpose only after id → should call travel collection tool.\n  * Full details in single message → should parse and respond with summary + `confirm`.\n  * `confirm` → should result in booking confirmation message (if implemented).\n* **Load testing**: test client `list_tools` calls frequency and cache them to avoid repeated LLM calls.\n\n---\n\n# Design tradeoffs \u0026 recommendation\n\n* If you want **modularity, future integrations, multi-service orchestration**, use **MCP** (recommended).\n* If you need a **single, deterministic flow** (complex business rules), **LangGraph** is good.\n* For clean frontend UX use **Chainlit** (chat-focused); use Streamlit for dashboards.\n\n**Recommendation**: Use MCP as the core pattern + Chainlit as UI. Keep LangGraph as a specialized experiment for flows that require deterministic branching.\n\n---\n\n# Docs images (placeholders) — where to place them\n\nCreate `/docs` or `/images` and add three images (PNG/SVG). Reference them in README with these names and captions.\n\n1. `docs/architecture.png` — **Architecture Diagram (MCP vs LangGraph)**\n\n   * Shows: Client (Chainlit/Streamlit) → FastAPI wrapper → MCP ClientSession → MCP Server (`main.py`) with tools; and separate LangGraph flow.\n   * Caption: \"High-level architecture showing the MCP pipeline and LangGraph alternative.\"\n   \n   ![Architecture diagram](public/archi.png)\n   \n\n2. `docs/ui_preview.png` — **UI screenshot (Chainlit)**\n\n   * Shows: conversation with avatars, sample messages, final booking summary formatted in markdown.\n   * Caption: \"Chainlit chat UI with avatars and a formatted booking summary.\"\n\n\n\n   \n   ![UI preview](public/intro.png)\n   *Figure 2 — Chainlit chat UI screenshot.*\n   \n\n\u003e Tip: for each image, include a small alt text for accessibility, and keep them in `docs/` to avoid clutter.\n\n---\n\n# Contributing \u0026 License\n\n* Contributions welcome. Please:\n\n  * Open an issue describing changes.\n  * Add PRs against `main` with tests and documentation.\n  * Keep changes backward compatible.\n\n* Suggested `LICENSE`: MIT (change as desired).\n\n---\n\n# Example conversation (end-to-end)\n\n1. **User**: `I want to book a trip`\n   → Selector decides: `new_travel_request` or asks LLM to choose. Server responds: `Please provide your 8-digit Employee ID.`\n\n2. **User**: `23456789`\n   → `new_travel_request` captures ID and returns: `Got your ID. What's your travel purpose and destination?`\n\n3. **User**: `R\u0026D Project, Mumbai to Pune, 15th sept 2025 9am to 20th sept 2025 10pm, Round trip, Train, Sleeper, Self Booked`\n   → `travel_data_collected` returns parsed travel details plus ask for `cost_center` / `project_wbs`.\n\n4. **User**: `607402 and ADRG.25IT.DG.GE.A01`\n   → `travel_data_collected` returns a formatted summary and asks `reply 'confirm' to book or 'no' to edit`.\n\n5. **User**: `confirm`\n   → Server returns: `✅ Your trip is booked!` (and you persist to DB in production).\n\n---\n\n# Final notes \u0026 next steps\n\n* Add persistent session storage (per-user) — current in-memory `data` is demo-only.\n* Add authentication to the FastAPI endpoints for security.\n* Add unit tests for prompt parsing and JSON extraction.\n* Add monitoring and logging around LLM calls to watch cost \u0026 failures.\n* Consider running MCP server over HTTP/SSE in production for simpler scaling (no subprocess).\n\n---\n\nIf you want, I can:\n\n* Generate the three diagrams for you (architecture, sequence, UI preview) as PNGs and place them in `docs/`.\n* Create a `README.md` file and save it into the repo for you.\n* Add example Postman collection or OpenAPI docs for `/chat`.\n\nTell me which of those you’d like me to do next and I’ll produce them immediately.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhiborn4%2Ftravel-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhiborn4%2Ftravel-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhiborn4%2Ftravel-agent/lists"}