Skip to main content
Glama
Quotations MCP Server

Quotations MCP Server

by cbunny-2005

Quotations MCP Server

Standalone MCP server exposing the quotations-app backend as tools. It's a thin HTTP client over the quotations REST API — no database of its own — so it stays decoupled and can point at local dev or the live Render backend.

MCP agent ──MCP──► server.py ──HTTP──► quotations-app backend ──► MongoDB

Other services in this repo

This is one of three independently-deployable pieces that happen to share a git repo — no shared state, no shared process:

Path

What it is

server.py (here)

This MCP server — read-only, queries the quotations backend

rfq_service/

Background worker: Gmail → classify/extract/match (pgvector catalog matching) → quotation → Oscar task. See rfq_service/README.md

demo/

Temporary FastAPI + Streamlit UI for demoing the rfq_service pipeline in a browser — not part of production, delete after use. See demo/README.md

oscar_ai_bot_legacy/

The old on-demand "Oscar AI" DM bot + its price lists, relocated OUT of the Oscar backend on 2026-09-26. Currently not runnable as-is — see below.

Related MCP server: finance-mcp

oscar_ai_bot_legacy/ — the "Oscar AI" DM bot (moved 2026-09-26)

What it was: a second, focused RFQ path inside the Oscar backend (AlumnxAILabs_epa), separate from the automatic background poller (rfq_service/ above). A team member could DM a special "Oscar AI" account inside the Oscar chat app and ask "any new RFQ emails?" — the bot would check the mailbox on demand and reply in-chat with quotation details, instead of waiting for the background worker's own polling cycle.

Why it's here now: all RFQ-processing code was consolidated into this repo. This folder holds every file that on-demand path depended on:

File

Role

oscar_ai_bot.py

The focused LangChain agent — ONE tool (check_rfq_emails), a narrow system prompt, nothing else bound

tools/rfq_tools.py

The check_rfq_emails tool itself

services/email_processing_service.py

Pipeline: dedup → classify → extract → match → quote → create task

services/rfq_classifier_service.py

"Is this an RFQ?" (keyword prefilter + GPT-4o-mini)

services/rfq_extractor_service.py

Pulls products/customer/company out of the email

services/rfq_matcher_service.py

Matches extracted items against the price list (rapidfuzz + LLM pick)

services/price_lookup_service.py

Loads PRICE_LIST_excel/*.xlsx into an in-memory index at startup

services/quotation_client.py

Calls the Quotation App's POST /api/quotations/generate

services/gmail_service.py

Single-account Gmail OAuth client (shared RFQ inbox)

scripts/create_oscar_bot.py

One-time script: provisions the "Oscar AI" user account

scripts/remove_oscar_bot_membership.py

Removes that account from all teams (it's server-side, not a real team member)

scripts/gmail_oauth_bootstrap.py

Mints the Gmail refresh token these services need

PRICE_LIST_excel/*.xlsx

The actual company price lists price_lookup_service.py reads

⚠️ NOT currently runnable standalone. email_processing_service.py imports Oscar-core services (item_service, comment_service, notification_service) that only exist inside the Oscar backend's own Python path — those aren't in this repo. To bring the DM bot back to life here, one of two things has to happen first:

  1. Rewire it to call Oscar's /internal/* API (the same pattern rfq_service/oscar_client.py in this repo already uses for the background worker) instead of importing item_service/comment_service directly — i.e. make email_processing_service.py's task-creation step an HTTP call, not a local import.

  2. Or run it co-located with a copy of the Oscar backend's models/services package on the Python path (not recommended — reintroduces the tight coupling this move was meant to remove).

Automatic assignment behavior (unchanged from when it lived in Oscar): every quotation this pipeline creates is auto-assigned to the team lead of whichever team the asking user belongs to (resolve_team_lead in email_processing_service.py) — not to the asker, and not to a fixed person. A task carrying the quotation link is created and assigned the same way. If the asker has no team, it falls back to RFQ_TEAM_LEAD_USER_ID, then the owner of RFQ_ORG_TEAM_ID.

Config this path needs (same names as rfq_service/'s own env vars — see that folder's .env.example): GMAIL_* (OAuth creds for the shared inbox), OPENAI_API_KEY, QUOTATION_API_URL / QUOTATION_FRONTEND_URL, RFQ_TEAM_LEAD_USER_ID, RFQ_ORG_TEAM_ID, RFQ_TASK_DUE_MINUTES / RFQ_TASK_DUE_HOURS, OSCAR_AI_BOT_ID (the provisioned bot account's user id in Oscar's own database).

As of this move, the "Oscar AI" DM feature is OFF inside the live Oscar app — the code that triggered it (main.py's DM-interception hook) has been removed there. Reviving it means finishing option 1 or 2 above, then re-adding an equivalent trigger wherever DMs are handled going forward.

Tools

Purpose is conversational intelligence — let an agent answer natural-language questions about quotations (counts, values, who-has-what, lookups) and take actions.

Conversational / query (read):

Tool

Answers

quotation_stats()

"how many are pending?", "total pipeline value?", "who has the most?" — counts by status + value by assignee

search_quotations(company?, status?, assignee?, number?, min_total?, max_total?)

"show quotations assigned to Vijender over ₹10k" — filtered list + total value

find_by_number(quotation_number)

resolves a human number like QT-2026-000005 to the full quotation

list_quotations()

all quotations (compact)

get_quotation(quotation_id)

one full quotation by internal id

health()

backend reachability + count

READ-ONLY by design. This MCP only queries the customer's data and answers questions — it never writes (no create/reassign). Creating or modifying quotations is the customer app's job. The backend exposes only list + get, so filtering/aggregation is done here; prices/totals come only from the server-computed data — never invented.

Completion / status

A quotation is "completed" only if the customer's data stores that state. Today their backend hard-codes status:"draft" and never changes it, so quotation_stats / search_quotations(status=...) will only ever report draft. The moment their app writes a real status (e.g. completed/sent), these read tools surface it automatically — no change here.

Install

pip install -r requirements.txt   # mcp[cli], httpx

Run

# stdio (dev / desktop MCP clients)
python server.py

# HTTP endpoint (for the MCP agent to connect to)
MCP_TRANSPORT=http MCP_PORT=8200 python server.py
# → MCP endpoint at http://<host>:8200/mcp

# inspect/try tools
mcp dev server.py

Env

Var

Default

Purpose

QUOTATIONS_API_URL

https://quotations-app.onrender.com

backend base URL

QUOTATIONS_TIMEOUT

30

HTTP timeout (bump for Render cold start ~50s)

MCP_TRANSPORT

stdio

stdio or http

MCP_HOST / MCP_PORT

127.0.0.1 / 8200

bind for http transport

Notes

  • Deployed separately from the quotations backend and from Oscar.

  • The Oscar agent will later connect to this server (as an MCP client) via a new endpoint — this repo/folder does not depend on Oscar.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      MCP server exposing Accelo quote tools (list, get, create, update) with per-user OAuth authentication for natural language interaction.
      1
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      A read-only MCP server for stock quotes, Coinbase prices, and Polymarket markets, supporting tools like get_stock_quote, get_coinbase_spot_price, and search_polymarket_markets.
      66 npm
      1
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Read-only MCP server for inspecting a neuro auto-posting service's publication pipeline. Allows operators to query channels, publications, errors, and configuration via natural language.
      MIT