Skip to main content
Glama
README.md
# MiniCab MCP

MiniCab MCP is a TypeScript Node.js backend for a small cab dispatch system exposed through the Model Context Protocol. It uses MySQL 8, raw SQL via `mysql2`, Zod validation, Pino logging, and the official `@modelcontextprotocol/sdk`.

## Features

- MySQL schema with only the assignment tables: cities, drivers, vehicles, fare rules, bookings, and ledger.
- Fare rules support global defaults, time-of-day surge rows, and city-specific partial overrides without duplicating every vehicle type for every city.
- Hardcoded city route table for known pickup/drop points, as requested.
- MCP stdio server for Claude Desktop.
- Function-based repositories and services, with no app-owned classes or heavy framework abstractions.
- Transactional booking with `SELECT ... FOR UPDATE` to prevent two users booking the same cab.
- Server-side fare recalculation during booking.
- Ledger entry creation when a ride is completed.
- Jest tests for pure fare, route, and time helpers.

## Requirements

- Node.js 18 or newer
- MySQL 8.0 or newer
- npm

## Setup Without Docker

1. Install dependencies:

```bash
npm install
```

2. Create the database and seed it:

```bash
mysql -u root -p < schema.sql
mysql -u root -p < seed.sql
```

3. Copy the environment file:

```bash
cp .env.example .env
```

4. Update `.env` if your MySQL credentials differ.

5. Build the project:

```bash
npm run build
```

6. Start the MCP server:

```bash
npm start
```

The server uses stdio transport, so when Claude Desktop runs it, stdout is reserved for MCP protocol messages and logs go to stderr.


## Claude Desktop Configuration

Build first with `npm run build`, then copy `claude_desktop_config.example.json` into your Claude Desktop config location and replace the placeholder path with your absolute project path.

Example:

```json
{
  "mcpServers": {
    "minicab": {
      "command": "node",
      "args": [
        "C:\\absolute\\path\\to\\minicab-mcp\\dist\\src\\index.js"
      ],
      "env": {
        "DB_HOST": "localhost",
        "DB_PORT": "3306",
        "DB_USER": "root",
        "DB_PASSWORD": "root",
        "DB_NAME": "minicab"
      }
    }
  }
}
```

Restart Claude Desktop after saving the config.

## MCP Tools

### `search_cabs`

Searches available active vehicles in a city for a pickup/drop route and optional vehicle type.

Input:

```json
{
  "city": "Hyderabad",
  "pickup": "Gachibowli",
  "drop": "RGIA Airport",
  "vehicle_type": "sedan"
}
```

### `get_fare_estimate`

Returns fare estimates for all vehicle types without creating a booking.

Input:

```json
{
  "pickup": "Gachibowli",
  "drop": "RGIA Airport",
  "time": "18:00"
}
```

### `book_ride`

Creates a confirmed booking for a specific cab. The server recalculates the fare and does not trust client-provided fare amounts.

Input:

```json
{
  "cab_id": 1,
  "pickup": "Gachibowli",
  "drop": "RGIA Airport",
  "rider_name": "Mohd Tabrez"
}
```

### `cancel_ride`

Cancels a confirmed booking and releases the vehicle.

Input:

```json
{
  "booking_id": 1
}
```

## Tested Example Prompts

Prompt: "Get me a sedan from Gachibowli to the airport around 6pm in Hyderabad."

Expected tool call:

```json
{
  "tool": "search_cabs",
  "arguments": {
    "city": "Hyderabad",
    "pickup": "Gachibowli",
    "drop": "RGIA Airport",
    "vehicle_type": "sedan"
  }
}
```

Prompt: "How much would an SUV from Connaught Place to IGI Airport cost at 8:30am in Delhi?"

Expected tool call:

```json
{
  "tool": "get_fare_estimate",
  "arguments": {
    "pickup": "Connaught Place",
    "drop": "IGI Airport",
    "time": "08:30"
  }
}
```

Prompt: "Book cab 1 for Mohd Tabrez from Gachibowli to RGIA Airport at 6pm."

Expected tool call:

```json
{
  "tool": "book_ride",
  "arguments": {
    "cab_id": 1,
    "pickup": "Gachibowli",
    "drop": "RGIA Airport",
    "rider_name": "Mohd Tabrez"
  }
}
```

Prompt: "Cancel booking 1."

Expected tool call:

```json
{
  "tool": "cancel_ride",
  "arguments": {
    "booking_id": 1
  }
}
```

## Tests

```bash
npm test
```

The included tests cover pure logic that does not need MySQL. A production submission should add integration tests with a disposable MySQL container for transaction and lock behavior.