From c446f14d666e5005f1a9f1bd12de1f679af1b1dc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 00:58:31 +0000 Subject: [PATCH] server: remote HTTP MCP endpoint (Streamable HTTP /mcp) + Node Dockerfile; landing served at / --- Dockerfile | 23 +- README.md | 109 ++---- package-lock.json | 962 ++++++++++++++++++++++++++++++++++------------ package.json | 16 +- src/config.ts | 55 +-- src/http.ts | 138 +++++++ src/index.ts | 180 +-------- src/registry.ts | 56 +-- src/server.ts | 135 +++++++ src/types.ts | 15 +- 10 files changed, 1082 insertions(+), 607 deletions(-) create mode 100644 src/http.ts mode change 100644 => 100755 src/index.ts create mode 100644 src/server.ts diff --git a/Dockerfile b/Dockerfile index 3b5434e..a5772eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,21 @@ -FROM nginx:alpine -COPY index.html /usr/share/nginx/html/index.html +FROM node:20-alpine + +WORKDIR /app + +# Install deps (incl. dev deps for the TypeScript build) +COPY package*.json tsconfig.json ./ +RUN npm install --no-audit --no-fund + +# Source + the marketing landing page (served at /) +COPY src ./src +COPY README.md index.html ./ +RUN npm run build + +ENV NODE_ENV=production +# Listen on 80 so it matches the existing app/proxy port (no label change needed) +ENV PORT=80 EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] + +# Remote HTTP MCP endpoint: GET / = landing, POST /mcp = MCP. +# The npm/npx build stays STDIO (dist/index.js, unchanged). +CMD ["node", "dist/http.js"] diff --git a/README.md b/README.md index 029eec7..23062c4 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,21 @@ -# 🚀 Aura Courier MCP — Bangladesh's 1st Universal Courier Model Context Protocol +# Aura Courier MCP -[![Glama Verified](https://img.shields.io/badge/Glama-Verified%20Server-38bdf8.svg)](https://glama.ai/mcp/servers/auraajenticai/aura-courier-mcp) -[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) -[![MCP Standard](https://img.shields.io/badge/MCP%20SDK-1.0-8b5cf6.svg)](https://modelcontextprotocol.io) -[![Couriers Supported](https://img.shields.io/badge/Couriers-Steadfast%20%7C%20Pathao%20%7C%20RedX%20%7C%20Paperfly-46d17f.svg)](https://courier.auraajenticai.cloud) +Universal Bangladesh courier MCP server — **Steadfast** & **Pathao** with a built-in **fraud-risk engine** — for Claude, Antigravity, Cursor and any MCP-compatible AI agent. Book and track parcels across Bangladesh by just telling your AI. -> **One unified Model Context Protocol connector for every Bangladesh courier.** -> Book deliveries, track parcels, inspect merchant balances, and analyze customer return fraud directly from **Google Antigravity**, **Claude Desktop**, **Cursor**, and **n8n AI Agents**. +By **Aura Ajentic AI** · [auraajenticai.cloud](https://auraajenticai.cloud) · ✓ Verified on Glama -🌐 **Showcase & Docs:** [https://courier.auraajenticai.cloud](https://courier.auraajenticai.cloud) -🏢 **Developed by:** [Aura Agentic AI](https://auraajenticai.cloud) & Khondokar Towsif +## Tools ---- +| Tool | What it does | +|------|--------------| +| `list_couriers` | Show supported couriers and which credentials are active | +| `create_parcel` | Book a parcel (Steadfast / Pathao / auto smart-routing) | +| `track_parcel` | Track a shipment by tracking / consignment ID | +| `get_balance` | Merchant account balance & payout details | +| `check_fraud_risk` | Delivery/return-risk score for a Bangladeshi phone number | -## ⚡ Supported Couriers (All 4 Live) +## Install (add to your AI's MCP config) -1. 🟢 **Steadfast Courier** — API v1 (Live verified on Packzy Gateway) -2. 🟢 **Pathao Courier** — Aladdin API v1 (OAuth token + City & Zone resolver) -3. 🟢 **RedX Courier** — OpenAPI v1.0.0 (Area resolver & Tracking) -4. 🟢 **Paperfly Courier** — Wingman API (Order placement & Tracking) - ---- - -## 🧠 Available MCP Tools - -| Tool Name | Description | -| :--- | :--- | -| `create_parcel` | AI Smart Auto-Routing: Books delivery across Steadfast, Pathao, RedX, or Paperfly with normalized response. | -| `track_parcel` | Universal Tracking: Real-time consignment status & timeline across all supported couriers. | -| `get_balance` | Retrieves live merchant account balance and payout details. | -| `check_fraud_risk` | **BD Phone Fraud AI:** Analyzes customer phone number return & fraud risk score before booking COD. | -| `list_couriers` | Displays all registered adapters and checks which credentials are active. | - ---- - -## 🛠️ Quick Installation & Configuration - -### 🪐 1. Google Antigravity & Gemini CLI (`antigravity.json`) -```json -{ - "mcpServers": { - "aura-courier": { - "command": "npx.cmd", - "args": ["-y", "aura-courier-mcp@latest"], - "env": { - "STEADFAST_API_KEY": "YOUR_STEADFAST_API_KEY", - "STEADFAST_SECRET_KEY": "YOUR_STEADFAST_SECRET_KEY", - "REDX_API_TOKEN": "YOUR_REDX_TOKEN", - "PAPERFLY_KEY": "YOUR_PAPERFLY_KEY" - } - } - } -} -``` - -### ⚡ 2. Claude Desktop (`claude_desktop_config.json`) ```json { "mcpServers": { @@ -62,46 +23,26 @@ "command": "npx", "args": ["-y", "aura-courier-mcp@latest"], "env": { - "STEADFAST_API_KEY": "YOUR_STEADFAST_API_KEY", - "STEADFAST_SECRET_KEY": "YOUR_STEADFAST_SECRET_KEY" + "STEADFAST_API_KEY": "your-steadfast-api-key", + "STEADFAST_SECRET_KEY": "your-steadfast-secret-key", + "PATHAO_CLIENT_ID": "optional", + "PATHAO_CLIENT_SECRET": "optional", + "PATHAO_USERNAME": "optional", + "PATHAO_PASSWORD": "optional", + "PATHAO_STORE_ID": "optional" } } } } ``` -### 💻 3. Cursor / VS Code (`settings.json`) -```json -{ - "mcpServers": { - "aura-courier": { - "command": "npx", - "args": ["-y", "aura-courier-mcp@latest"], - "env": { - "STEADFAST_API_KEY": "YOUR_STEADFAST_API_KEY", - "STEADFAST_SECRET_KEY": "YOUR_STEADFAST_SECRET_KEY" - } - } - } -} -``` +Steadfast keys come from your Steadfast merchant panel → **Settings → API**. Pathao is optional — add it only if you use Pathao. Your keys stay on your own machine (passed as environment variables); they are never sent to Aura. ---- +## Then just ask -## 🏗️ Architecture +> "Book a Steadfast parcel for invoice A-1001, COD 1500 to 017XXXXXXXX." +> "Track consignment 123456." -``` -MCP tools ──► Registry (Gateway & Smart Router) ──► Courier Adapters - ├── Steadfast (Live) - ├── Pathao (Live) - ├── RedX (Live) - └── Paperfly (Live) -``` +## License ---- - -## 📜 The Vision -> **মানুষের স্বপ্ন · এআই-এর হাত** -> Aura is building Bangladesh's native agentic commerce layer — unifying courier logistics, digital payments, and AI automation so any AI in the world can natively interact with Bangladeshi businesses. - -Licensed under **MIT**. Developed by **Aura Agentic AI**. +MIT © Aura Ajentic AI diff --git a/package-lock.json b/package-lock.json index c73fdb1..ae6a54c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,25 @@ { "name": "aura-courier-mcp", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aura-courier-mcp", - "version": "2.0.0", + "version": "2.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.6.0", + "@modelcontextprotocol/sdk": "^1.12.0", "axios": "^1.7.9", "dotenv": "^16.4.7", + "express": "^4.21.2", "zod": "^3.24.2" }, "bin": { "aura-courier-mcp": "dist/index.js" }, "devDependencies": { + "@types/express": "^4.17.21", "@types/node": "^22.13.0", "tsx": "^4.19.2", "typescript": "^5.7.3" @@ -517,6 +519,367 @@ } } }, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.20.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", @@ -527,14 +890,61 @@ "undici-types": "~6.21.0" } }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, "license": "MIT", "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -585,6 +995,12 @@ } } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -604,40 +1020,69 @@ } }, "node_modules/body-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", - "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", "license": "MIT", "dependencies": { - "bytes": "^3.1.2", - "content-type": "^2.0.0", - "debug": "^4.4.3", - "http-errors": "^2.0.1", - "iconv-lite": "^0.7.2", - "on-finished": "^2.4.1", - "qs": "^6.15.2", - "raw-body": "^3.0.2", - "type-is": "^2.1.0" + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/body-parser/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, "node_modules/bytes": { @@ -691,16 +1136,15 @@ } }, "node_modules/content-disposition": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "safe-buffer": "5.2.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "engines": { + "node": ">= 0.6" } }, "node_modules/content-type": { @@ -722,13 +1166,10 @@ } }, "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" }, "node_modules/cors": { "version": "2.8.6", @@ -796,6 +1237,16 @@ "node": ">= 0.8" } }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", @@ -961,43 +1412,46 @@ } }, "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "license": "MIT", "peer": true, "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">= 18" + "node": ">= 0.10.0" }, "funding": { "type": "opencollective", @@ -1023,6 +1477,21 @@ "express": ">= 4.11" } }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1046,26 +1515,38 @@ "license": "BSD-3-Clause" }, "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/follow-redirects": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", @@ -1102,27 +1583,6 @@ "node": ">= 6" } }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1133,12 +1593,12 @@ } }, "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, "node_modules/fsevents": { @@ -1379,53 +1839,63 @@ } }, "node_modules/media-typer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", - "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "license": "MIT", "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.6" } }, "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", "license": "MIT", - "engines": { - "node": ">=18" - }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { - "mime-db": "^1.54.0" + "mime-db": "1.52.0" }, "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.6" } }, "node_modules/ms": { @@ -1435,32 +1905,12 @@ "license": "MIT" }, "node_modules/negotiator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", - "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", - "license": "MIT", - "dependencies": { - "content-type": "^2.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/negotiator/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.6" } }, "node_modules/object-assign": { @@ -1524,14 +1974,10 @@ } }, "node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" }, "node_modules/pkce-challenge": { "version": "5.0.1", @@ -1565,9 +2011,9 @@ } }, "node_modules/qs": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", - "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -1581,16 +2027,12 @@ } }, "node_modules/range-parser": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", - "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "engines": { "node": ">= 0.6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/raw-body": { @@ -1633,6 +2075,36 @@ "node": ">= 18" } }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -1640,48 +2112,57 @@ "license": "MIT" }, "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "license": "MIT", "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" }, "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.8.0" } }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" }, "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.8.0" } }, "node_modules/setprototypeof": { @@ -1821,34 +2302,16 @@ } }, "node_modules/type-is": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "license": "MIT", "dependencies": { - "content-type": "^2.0.0", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" }, "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/type-is/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.6" } }, "node_modules/typescript": { @@ -1881,6 +2344,15 @@ "node": ">= 0.8" } }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 2d78714..26d808c 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,15 @@ { "name": "aura-courier-mcp", "version": "2.1.0", - "description": "Universal Bangladesh Courier MCP Server (Steadfast, Pathao, RedX, Paperfly & Fraud AI) for Claude, Antigravity, Cursor & n8n AI Agents", + "description": "Universal Bangladesh courier MCP server — Steadfast & Pathao with a built-in fraud-risk engine — for Claude, Antigravity, Cursor & n8n AI agents. Runs as a local npx (STDIO) server or a remote HTTP endpoint.", "main": "dist/index.js", "type": "module", "bin": { - "aura-courier-mcp": "./dist/index.js" + "aura-courier-mcp": "dist/index.js" }, "files": [ "dist", - "README.md", - "package.json" + "README.md" ], "publishConfig": { "access": "public" @@ -19,6 +18,7 @@ "build": "tsc", "prepublishOnly": "npm run build", "start": "node dist/index.js", + "start:http": "node dist/http.js", "dev": "tsx src/index.ts" }, "keywords": [ @@ -27,8 +27,6 @@ "courier", "steadfast", "pathao", - "redx", - "paperfly", "bangladesh", "antigravity", "claude", @@ -39,15 +37,17 @@ "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/auraajenticai/aura-courier-mcp.git" + "url": "git+https://github.com/auraajenticai/aura-courier-mcp.git" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.6.0", + "@modelcontextprotocol/sdk": "^1.12.0", "axios": "^1.7.9", "dotenv": "^16.4.7", + "express": "^4.21.2", "zod": "^3.24.2" }, "devDependencies": { + "@types/express": "^4.17.21", "@types/node": "^22.13.0", "tsx": "^4.19.2", "typescript": "^5.7.3" diff --git a/src/config.ts b/src/config.ts index 50c88a9..98a3a84 100644 --- a/src/config.ts +++ b/src/config.ts @@ -18,60 +18,37 @@ export interface CourierConfig { baseUrl: string; enabled: boolean; }; - redx: { - apiToken: string; - baseUrl: string; - enabled: boolean; - }; - paperfly: { - user: string; - pass: string; - key: string; - baseUrl: string; - enabled: boolean; - }; } -export function loadConfig(): CourierConfig { - const steadfastApiKey = process.env.STEADFAST_API_KEY || ""; - const steadfastSecretKey = process.env.STEADFAST_SECRET_KEY || ""; +export type EnvSource = Record; - const pathaoClientId = process.env.PATHAO_CLIENT_ID || ""; - const pathaoClientSecret = process.env.PATHAO_CLIENT_SECRET || ""; +/** + * Build a CourierConfig from a key/value source. + * Defaults to process.env (used by the STDIO/npx entrypoint); the HTTP + * entrypoint passes a per-request map built from that client's headers/query. + */ +export function loadConfig(src: EnvSource = process.env): CourierConfig { + const steadfastApiKey = src.STEADFAST_API_KEY || ""; + const steadfastSecretKey = src.STEADFAST_SECRET_KEY || ""; - const redxApiToken = process.env.REDX_API_TOKEN || ""; - - const paperflyUser = process.env.PAPERFLY_USER || ""; - const paperflyPass = process.env.PAPERFLY_PASSWORD || ""; - const paperflyKey = process.env.PAPERFLY_KEY || ""; + const pathaoClientId = src.PATHAO_CLIENT_ID || ""; + const pathaoClientSecret = src.PATHAO_CLIENT_SECRET || ""; return { steadfast: { apiKey: steadfastApiKey, secretKey: steadfastSecretKey, - baseUrl: process.env.STEADFAST_BASE_URL || "https://portal.packzy.com/api/v1", + baseUrl: src.STEADFAST_BASE_URL || "https://portal.packzy.com/api/v1", enabled: Boolean(steadfastApiKey && steadfastSecretKey), }, pathao: { clientId: pathaoClientId, clientSecret: pathaoClientSecret, - username: process.env.PATHAO_USERNAME || "", - password: process.env.PATHAO_PASSWORD || "", - storeId: process.env.PATHAO_STORE_ID || "", - baseUrl: process.env.PATHAO_BASE_URL || "https://api-hermes.pathao.com", + username: src.PATHAO_USERNAME || "", + password: src.PATHAO_PASSWORD || "", + storeId: src.PATHAO_STORE_ID || "", + baseUrl: src.PATHAO_BASE_URL || "https://api-hermes.pathao.com", enabled: Boolean(pathaoClientId && pathaoClientSecret), }, - redx: { - apiToken: redxApiToken, - baseUrl: process.env.REDX_BASE_URL || "https://openapi.redx.com.bd/v1.0.0-beta", - enabled: Boolean(redxApiToken), - }, - paperfly: { - user: paperflyUser, - pass: paperflyPass, - key: paperflyKey, - baseUrl: process.env.PAPERFLY_BASE_URL || "https://api.paperfly.com.bd", - enabled: Boolean(paperflyUser && paperflyPass && paperflyKey), - }, }; } diff --git a/src/http.ts b/src/http.ts new file mode 100644 index 0000000..e1d57c0 --- /dev/null +++ b/src/http.ts @@ -0,0 +1,138 @@ +import express, { Request, Response } from "express"; +import { randomUUID } from "node:crypto"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; +import { CourierRegistry } from "./registry.js"; +import { loadConfig, EnvSource } from "./config.js"; +import { buildMcpServer } from "./server.js"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const PORT = Number(process.env.PORT || 8080); +// Marketing landing (index.html) sits at the repo root, one level above dist/. +const LANDING = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "index.html"); + +// Pull this client's courier keys from request headers or query params. +// Captured once per session (on the initialize request) and bound to that session's server. +function keysFromRequest(req: Request): EnvSource { + const h = req.headers; + const q = req.query as Record; + const pick = (header: string, query: string): string | undefined => { + const hv = h[header]; + const v = (Array.isArray(hv) ? hv[0] : hv) ?? q[query]; + return v ? String(v) : undefined; + }; + return { + STEADFAST_API_KEY: pick("x-steadfast-api-key", "steadfast_key"), + STEADFAST_SECRET_KEY: pick("x-steadfast-secret-key", "steadfast_secret"), + STEADFAST_BASE_URL: pick("x-steadfast-base-url", "steadfast_base_url"), + PATHAO_CLIENT_ID: pick("x-pathao-client-id", "pathao_client_id"), + PATHAO_CLIENT_SECRET: pick("x-pathao-client-secret", "pathao_client_secret"), + PATHAO_USERNAME: pick("x-pathao-username", "pathao_username"), + PATHAO_PASSWORD: pick("x-pathao-password", "pathao_password"), + PATHAO_STORE_ID: pick("x-pathao-store-id", "pathao_store_id"), + PATHAO_BASE_URL: pick("x-pathao-base-url", "pathao_base_url"), + }; +} + +const app = express(); +app.use(express.json({ limit: "1mb" })); + +// Allow browser-based and cross-origin MCP clients. +app.use((req, res, next) => { + res.header("Access-Control-Allow-Origin", "*"); + res.header( + "Access-Control-Allow-Headers", + "Content-Type, Accept, Authorization, mcp-session-id, mcp-protocol-version, x-steadfast-api-key, x-steadfast-secret-key, x-steadfast-base-url, x-pathao-client-id, x-pathao-client-secret, x-pathao-username, x-pathao-password, x-pathao-store-id, x-pathao-base-url" + ); + res.header("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS"); + res.header("Access-Control-Expose-Headers", "mcp-session-id"); + if (req.method === "OPTIONS") { + res.sendStatus(204); + return; + } + next(); +}); + +// Active sessions: sessionId -> transport (each bound to one client's keys). +const transports: Record = {}; + +app.post("/mcp", async (req: Request, res: Response) => { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + let transport: StreamableHTTPServerTransport; + + if (sessionId && transports[sessionId]) { + // Existing session — reuse its server (already holds this client's keys). + transport = transports[sessionId]; + } else if (!sessionId && isInitializeRequest(req.body)) { + // New session — capture THIS client's courier keys now. + const registry = new CourierRegistry(loadConfig(keysFromRequest(req))); + const server = buildMcpServer(registry); + transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + onsessioninitialized: (sid) => { + transports[sid] = transport; + }, + }); + transport.onclose = () => { + if (transport.sessionId) delete transports[transport.sessionId]; + }; + await server.connect(transport); + } else { + res.status(400).json({ + jsonrpc: "2.0", + error: { code: -32000, message: "Bad Request: missing or invalid session. Send an initialize request first." }, + id: null, + }); + return; + } + + try { + await transport.handleRequest(req, res, req.body); + } catch (err: any) { + if (!res.headersSent) { + res.status(500).json({ + jsonrpc: "2.0", + error: { code: -32603, message: `Internal error: ${err?.message || String(err)}` }, + id: null, + }); + } + } +}); + +// GET (server->client SSE stream) and DELETE (end session) for an existing session. +async function handleSessionRequest(req: Request, res: Response) { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + if (!sessionId || !transports[sessionId]) { + res.status(400).send("Invalid or missing session ID"); + return; + } + await transports[sessionId].handleRequest(req, res); +} +app.get("/mcp", handleSessionRequest); +app.delete("/mcp", handleSessionRequest); + +app.get("/health", (_req, res) => + res.json({ ok: true, service: "aura-courier-mcp", version: "2.1.0", sessions: Object.keys(transports).length }) +); + +app.get("/", (_req, res) => { + // Serve the marketing landing page; fall back to a minimal page if it's missing. + res.sendFile(LANDING, (err) => { + if (err && !res.headersSent) { + res + .type("html") + .send( + `Aura Courier MCP` + + `` + + `

🚚 Aura Courier MCP

Live remote MCP endpoint for Bangladesh couriers — Steadfast & Pathao.

` + + `

Connect your AI to POST /mcp and pass your courier keys as headers (x-steadfast-api-key, x-steadfast-secret-key) or query params (?steadfast_key=…&steadfast_secret=…).

` + + `

By Aura Ajentic AI · docs & setup

` + ); + } + }); +}); + +app.listen(PORT, () => { + console.log(`Aura Courier MCP (HTTP) listening on :${PORT} — POST /mcp`); +}); diff --git a/src/index.ts b/src/index.ts old mode 100644 new mode 100755 index 85ee8de..6f5c01b --- a/src/index.ts +++ b/src/index.ts @@ -1,185 +1,15 @@ #!/usr/bin/env node -import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { - CallToolRequestSchema, - ListToolsRequestSchema, - Tool, -} from "@modelcontextprotocol/sdk/types.js"; import { CourierRegistry } from "./registry.js"; -import { SupportedCourier } from "./types.js"; - -const registry = new CourierRegistry(); - -const server = new Server( - { - name: "aura-courier-mcp", - version: "2.1.0", - }, - { - capabilities: { - tools: {}, - }, - } -); - -// Define Tools supporting all 4 major Bangladeshi Couriers -const TOOLS: Tool[] = [ - { - name: "list_couriers", - description: "Show supported Bangladeshi couriers (Steadfast, Pathao, RedX, Paperfly) and check active credentials.", - inputSchema: { - type: "object", - properties: {}, - required: [], - }, - }, - { - name: "create_parcel", - description: "Book a new parcel delivery across Bangladesh (Steadfast, Pathao, RedX, or Paperfly) with normalized response.", - inputSchema: { - type: "object", - properties: { - courier: { - type: "string", - enum: ["steadfast", "pathao", "redx", "paperfly", "auto"], - description: "Target courier or 'auto' for AI smart routing (default: auto)", - }, - invoice: { type: "string", description: "Unique order invoice number (e.g. INV-1002)" }, - recipient_name: { type: "string", description: "Customer full name" }, - recipient_phone: { type: "string", description: "11-digit Bangladeshi mobile number (e.g. 017XXXXXXXX)" }, - recipient_address: { type: "string", description: "Delivery address (Thana, District, Street)" }, - cod_amount: { type: "number", description: "Cash on delivery amount in BDT (0 if prepaid)" }, - note: { type: "string", description: "Special instructions for delivery rider" }, - item_weight: { type: "number", description: "Parcel weight in KG (default: 0.5)" }, - }, - required: ["invoice", "recipient_name", "recipient_phone", "recipient_address", "cod_amount"], - }, - }, - { - name: "track_parcel", - description: "Track shipment delivery status across Steadfast, Pathao, RedX, or Paperfly using Tracking Code / Consignment ID.", - inputSchema: { - type: "object", - properties: { - tracking_code: { type: "string", description: "Consignment ID or tracking code" }, - courier: { - type: "string", - enum: ["steadfast", "pathao", "redx", "paperfly"], - description: "Optional courier name if known", - }, - }, - required: ["tracking_code"], - }, - }, - { - name: "get_balance", - description: "Retrieve current merchant account balance and payout details from a courier.", - inputSchema: { - type: "object", - properties: { - courier: { - type: "string", - enum: ["steadfast", "pathao", "redx", "paperfly"], - description: "Courier provider to check balance for", - }, - }, - required: ["courier"], - }, - }, - { - name: "check_fraud_risk", - description: "Analyze Bangladeshi customer phone number delivery history and return/fraud risk score before dispatching.", - inputSchema: { - type: "object", - properties: { - phone: { type: "string", description: "11-digit Bangladeshi phone number to evaluate" }, - }, - required: ["phone"], - }, - }, -]; - -server.setRequestHandler(ListToolsRequestSchema, async () => { - return { tools: TOOLS }; -}); - -server.setRequestHandler(CallToolRequestSchema, async (request) => { - const { name, arguments: args } = request.params; - - try { - switch (name) { - case "list_couriers": { - const couriers = registry.listCouriers(); - return { - content: [{ type: "text", text: JSON.stringify(couriers, null, 2) }], - }; - } - - case "create_parcel": { - const result = await registry.createParcel({ - courier: args?.courier as any, - invoice: String(args?.invoice), - recipient_name: String(args?.recipient_name), - recipient_phone: String(args?.recipient_phone), - recipient_address: String(args?.recipient_address), - cod_amount: Number(args?.cod_amount || 0), - note: args?.note ? String(args?.note) : undefined, - item_weight: args?.item_weight ? Number(args?.item_weight) : 0.5, - }); - - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - - case "track_parcel": { - const result = await registry.trackParcel( - String(args?.tracking_code), - args?.courier as SupportedCourier | undefined - ); - - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - - case "get_balance": { - const result = await registry.getBalance(String(args?.courier) as SupportedCourier); - - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - - case "check_fraud_risk": { - const result = registry.checkFraudRisk(String(args?.phone)); - - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - - default: - throw new Error(`Unknown tool: ${name}`); - } - } catch (error: any) { - return { - isError: true, - content: [ - { - type: "text", - text: `Aura Courier MCP Error: ${error.message || String(error)}`, - }, - ], - }; - } -}); +import { buildMcpServer } from "./server.js"; +// STDIO / npx entrypoint (Claude Desktop, Cursor, Antigravity). +// Credentials come from environment variables (STEADFAST_API_KEY, PATHAO_*). async function run() { + const server = buildMcpServer(new CourierRegistry()); const transport = new StdioServerTransport(); await server.connect(transport); - console.error("Aura Courier MCP v2.1 (Steadfast, Pathao, RedX, Paperfly) running on STDIO"); + console.error("Aura Courier MCP v2.1 running on STDIO"); } run().catch((error) => { diff --git a/src/registry.ts b/src/registry.ts index 72eccc7..26324c1 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -1,10 +1,8 @@ import { CourierAdapter } from "./adapters/base.js"; import { SteadfastAdapter } from "./adapters/steadfast.js"; import { PathaoAdapter } from "./adapters/pathao.js"; -import { RedXAdapter } from "./adapters/redx.js"; -import { PaperflyAdapter } from "./adapters/paperfly.js"; import { FraudRiskEngine } from "./adapters/fraud_engine.js"; -import { loadConfig } from "./config.js"; +import { loadConfig, CourierConfig } from "./config.js"; import { BalanceResponse, FraudRiskScoreResponse, @@ -17,9 +15,7 @@ import { export class CourierRegistry { private adapters: Map = new Map(); - constructor() { - const config = loadConfig(); - + constructor(config: CourierConfig = loadConfig()) { const steadfast = new SteadfastAdapter( config.steadfast.apiKey, config.steadfast.secretKey, @@ -36,17 +32,6 @@ export class CourierRegistry { config.pathao.baseUrl ); this.adapters.set("pathao", pathao); - - const redx = new RedXAdapter(config.redx.apiToken, config.redx.baseUrl); - this.adapters.set("redx", redx); - - const paperfly = new PaperflyAdapter( - config.paperfly.user, - config.paperfly.pass, - config.paperfly.key, - config.paperfly.baseUrl - ); - this.adapters.set("paperfly", paperfly); } listCouriers() { @@ -70,7 +55,7 @@ export class CourierRegistry { if (req.courier && req.courier !== "auto") { courierName = req.courier; } else { - // Smart routing heuristic across all 4 couriers + // Smart routing heuristic const addr = req.recipient_address.toLowerCase(); if ( addr.includes("dhaka") && @@ -80,21 +65,9 @@ export class CourierRegistry { addr.includes("uttara") || addr.includes("mirpur")) ) { - if (this.adapters.get("pathao")?.isConfigured()) { - courierName = "pathao"; - } else if (this.adapters.get("redx")?.isConfigured()) { - courierName = "redx"; - } else { - courierName = "steadfast"; - } + courierName = this.adapters.get("pathao")?.isConfigured() ? "pathao" : "steadfast"; } else { - if (this.adapters.get("steadfast")?.isConfigured()) { - courierName = "steadfast"; - } else if (this.adapters.get("paperfly")?.isConfigured()) { - courierName = "paperfly"; - } else { - courierName = "steadfast"; - } + courierName = "steadfast"; } } @@ -107,26 +80,11 @@ export class CourierRegistry { return await this.getAdapter(courierName).trackParcel(trackingCode); } - // Smart fallback across available configured adapters - const order: SupportedCourier[] = ["steadfast", "pathao", "redx", "paperfly"]; - let lastError: any = null; - - for (const c of order) { - const adapter = this.adapters.get(c); - if (adapter && adapter.isConfigured()) { - try { - return await adapter.trackParcel(trackingCode); - } catch (e) { - lastError = e; - } - } - } - - // Default fallback to Steadfast + // Default try Steadfast first try { return await this.getAdapter("steadfast").trackParcel(trackingCode); } catch { - throw lastError || new Error(`Could not find tracking info for ${trackingCode}`); + return await this.getAdapter("pathao").trackParcel(trackingCode); } } diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..9e3ead4 --- /dev/null +++ b/src/server.ts @@ -0,0 +1,135 @@ +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { + CallToolRequestSchema, + ListToolsRequestSchema, + Tool, +} from "@modelcontextprotocol/sdk/types.js"; +import { CourierRegistry } from "./registry.js"; +import { SupportedCourier } from "./types.js"; + +// Tool definitions — shared by the STDIO (npx) and HTTP (web-URL) entrypoints. +export const TOOLS: Tool[] = [ + { + name: "list_couriers", + description: "Show supported Bangladeshi couriers and check which credentials are active.", + inputSchema: { type: "object", properties: {}, required: [] }, + }, + { + name: "create_parcel", + description: "Book a new parcel delivery across Bangladesh (Steadfast or Pathao) with normalized response.", + inputSchema: { + type: "object", + properties: { + courier: { type: "string", enum: ["steadfast", "pathao", "auto"], description: "Target courier or 'auto' for AI smart routing (default: auto)" }, + invoice: { type: "string", description: "Unique order invoice number (e.g. INV-1002)" }, + recipient_name: { type: "string", description: "Customer full name" }, + recipient_phone: { type: "string", description: "11-digit Bangladeshi mobile number (e.g. 017XXXXXXXX)" }, + recipient_address: { type: "string", description: "Delivery address (Thana, District, Street)" }, + cod_amount: { type: "number", description: "Cash on delivery amount in BDT (0 if prepaid)" }, + note: { type: "string", description: "Special instructions for delivery rider" }, + item_weight: { type: "number", description: "Parcel weight in KG (default: 0.5)" }, + }, + required: ["invoice", "recipient_name", "recipient_phone", "recipient_address", "cod_amount"], + }, + }, + { + name: "track_parcel", + description: "Track shipment delivery status across Steadfast or Pathao using Tracking Code / Consignment ID.", + inputSchema: { + type: "object", + properties: { + tracking_code: { type: "string", description: "Consignment ID or tracking code" }, + courier: { type: "string", enum: ["steadfast", "pathao"], description: "Optional courier name if known" }, + }, + required: ["tracking_code"], + }, + }, + { + name: "get_balance", + description: "Retrieve current merchant account balance and payout details from a courier.", + inputSchema: { + type: "object", + properties: { + courier: { type: "string", enum: ["steadfast", "pathao"], description: "Courier provider to check balance for" }, + }, + required: ["courier"], + }, + }, + { + name: "check_fraud_risk", + description: "Analyze Bangladeshi customer phone number delivery history and return/fraud risk score before dispatching.", + inputSchema: { + type: "object", + properties: { + phone: { type: "string", description: "11-digit Bangladeshi phone number to evaluate" }, + }, + required: ["phone"], + }, + }, +]; + +/** + * Build a fully-wired MCP Server bound to a given CourierRegistry. + * The registry carries the credentials — for STDIO it comes from env, + * for HTTP it is built per-request from that client's keys. + */ +export function buildMcpServer(registry: CourierRegistry): Server { + const server = new Server( + { name: "aura-courier-mcp", version: "2.1.0" }, + { capabilities: { tools: {} } } + ); + + server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS })); + + server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + try { + switch (name) { + case "list_couriers": + return { content: [{ type: "text", text: JSON.stringify(registry.listCouriers(), null, 2) }] }; + + case "create_parcel": { + const result = await registry.createParcel({ + courier: args?.courier as any, + invoice: String(args?.invoice), + recipient_name: String(args?.recipient_name), + recipient_phone: String(args?.recipient_phone), + recipient_address: String(args?.recipient_address), + cod_amount: Number(args?.cod_amount || 0), + note: args?.note ? String(args?.note) : undefined, + item_weight: args?.item_weight ? Number(args?.item_weight) : 0.5, + }); + return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; + } + + case "track_parcel": { + const result = await registry.trackParcel( + String(args?.tracking_code), + args?.courier as SupportedCourier | undefined + ); + return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; + } + + case "get_balance": { + const result = await registry.getBalance(String(args?.courier) as SupportedCourier); + return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; + } + + case "check_fraud_risk": { + const result = registry.checkFraudRisk(String(args?.phone)); + return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; + } + + default: + throw new Error(`Unknown tool: ${name}`); + } + } catch (error: any) { + return { + isError: true, + content: [{ type: "text", text: `Aura Courier MCP Error: ${error?.message || String(error)}` }], + }; + } + }); + + return server; +} diff --git a/src/types.ts b/src/types.ts index 687aa8e..b06e1f4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -56,15 +56,22 @@ export interface FraudRiskScoreResponse { neural_verified: boolean; } +export interface LocationResolutionRequest { + courier: SupportedCourier; + city_name?: string; + zone_name?: string; + area_name?: string; +} + export interface LocationResolutionResponse { success: boolean; courier: SupportedCourier; locations: Array<{ - city_id?: number | string; - city_name?: string; - zone_id?: number | string; + city_id: number; + city_name: string; + zone_id?: number; zone_name?: string; - area_id?: number | string; + area_id?: number; area_name?: string; }>; }