Skip to main content
Glama
README.md
# ailistmybusiness

MCP-callable directory for AI-driven SMB discovery. Country-agnostic, zero-PII catalog of realtors, insurance agents, and medical practitioners (Wave 1).

> **Working title.** Public brand pending domain registration. Folder name and `package.json` will be renamed once the domain is locked.

## What this is

When a user asks ChatGPT, Claude, or Gemini "find me a realtor in Dallas" / "evening walk-in clinic in Toronto" / "bilingual insurance broker", the AI calls this MCP server. It returns ranked business listings with UTM-tagged booking URLs. The user books directly with the SMB. **We never see customer data — we are a business catalog, not a lead processor.**

## Phase 1 status

- [x] MCP server scaffold (Node 20 + TypeScript)
- [x] 5 tools: `search_businesses`, `get_business_profile`, `get_booking_options`, `search_by_query`, `get_categories`
- [x] 30 mock SMBs across 3 verticals × 2 countries (Dallas + Toronto)
- [x] OpenStreetMap Nominatim geocoding (free, no PII)
- [x] UTM-tagged booking URLs for SMB attribution
- [x] Vitest test suite for all 5 tools
- [x] Smithery + Glama + Railway manifests
- [ ] Supabase wiring (Phase 2)
- [ ] Stripe billing for paid tiers (Phase 2)
- [ ] Coinbase x402 metering for API tier (Phase 3)

## Quick start

```bash
npm install
npm test                    # run unit tests
npm run test:mcp            # smoke test all tools end-to-end
npm run dev                 # start MCP server on stdio
npm run http                # start HTTP server on :3000 (preview endpoints + Railway entrypoint)
```

Then visit `http://localhost:3000/preview/search?category=realtor&location=Dallas` to see the ranking output.

## Architecture

```
src/
  server.ts              # MCP server (stdio transport, Smithery entrypoint)
  http.ts                # Express server (Railway entrypoint, /health, /preview/*)
  types.ts               # BusinessProfile, SearchHit, BookingOptions, etc.
  tools/                 # one file per MCP tool
    searchBusinesses.ts
    getBusinessProfile.ts
    getBookingOptions.ts
    searchByQuery.ts
    getCategories.ts
  lib/
    db.ts                # data access — switches on DATA_SOURCE env (mock | supabase)
    ranking.ts           # 6-factor weighted relevance score
    utm.ts               # UTM URL builder for booking links
    geo.ts               # OpenStreetMap Nominatim geocoder + Haversine distance
data/
  mockBusinesses.json    # 30 sample SMBs (realtors, insurance, medical × Dallas, Toronto)
  categories.json        # vertical taxonomy
scripts/
  seed.ts                # Supabase seeder (Phase 2 stub)
  test-mcp.ts            # smoke test runner
tests/
  tools.test.ts          # Vitest tests for all tools
```

## Zero-PII rule

This catalog stores **business data only**:
- Business name, address, hours, services
- Public credentials and license numbers
- Aggregate review counts and ratings (sourced from public APIs in Phase 2)
- UTM-tagged booking URLs

It explicitly does **not** store:
- Customer / patient names, phones, emails, or any other identifiers
- Insurance policy details, medical history, or anything covered by HIPAA / PIPEDA / GDPR
- Individual booking records or appointment data

Booking flow: agent gets the SMB's booking URL → user clicks → user books on the SMB's own system. We see impressions; SMB sees conversions via UTM tags on their own analytics.

## MCP tool contracts

### `search_businesses`
```ts
{
  category: string,         // "realtor" | "insurance_agent" | "medical_practitioner" | etc.
  location: string,         // "Dallas, TX" — geocoded server-side
  countryCode?: "US" | "CA" | "GB" | "AU" | ...,
  language?: string,        // ISO-639-1, e.g. "en", "fr", "es"
  subcategory?: string,
  maxResults?: number,      // default 10, max 25
  minRating?: number
}
→ SearchHit[]
```

### `get_business_profile`
```ts
{ id: string, agentName?: string }
→ BusinessProfile  // bookingUrl is UTM-tagged
```

### `get_booking_options`
```ts
{ id: string, agentName?: string }
→ { bookingUrl, acceptedMethods, hours, timezone, fallbackContact }
```

### `search_by_query`
```ts
{ query: string, location?: string, countryCode?: string, maxResults?: number }
→ SearchHit[]
```

Phase 1 implementation is keyword/substring-based. Phase 2 swaps in pgvector or OpenAI embeddings for true semantic search.

### `get_categories`
```ts
{ countryCode?: string }
→ CategoryEntry[]
```

## Ranking logic

Weighted score (0–100) per business:
- **Tier** (20%) — healthcare 100 / pro 85 / standard 65 / free 40
- **Distance** (30%) — closer to query origin scores higher
- **Rating** (20%) — public review rating × volume
- **Vertical / subcategory match** (20%)
- **Verified listing** (5%)
- **Language match** (5%)

See `src/lib/ranking.ts`.

## Hand-off to Claude Code

Once you clone this folder into your local dev directory:

```bash
# 1. Install
npm install

# 2. Initialize git
git init
git add .
git commit -m "Initial scaffold: MCP server + 5 tools + mock data"
git branch -M main
git remote add origin git@github.com:YOUR_GH_USERNAME/ailistmybusiness.git
git push -u origin main

# 3. Validate locally
npm run typecheck
npm test
npm run test:mcp

# 4. Submit to Smithery (when ready)
#    https://smithery.ai/new — point to your GitHub repo

# 5. Deploy HTTP entrypoint to Railway (when ready)
#    https://railway.app/new — uses railway.json
```

## Phase 2 plug-in points

When you're ready to wire real services:

| Service | What to do | File to edit |
|---|---|---|
| **Supabase** | Create tables `businesses` + `categories`, set `SUPABASE_*` env vars, set `DATA_SOURCE=supabase`, run `npm run seed` | `src/lib/db.ts` |
| **Stripe** | Add billing routes, wire `POST /webhooks/stripe`, gate paid-tier features in ranking | `src/http.ts`, new `src/billing/` |
| **Coinbase x402** | Wrap MCP tool handlers in metered facilitator | `src/server.ts`, new `src/lib/x402.ts` |
| **AEO syndication** | Push profiles to Google Business + schema.org markup on landing pages | new `src/syndication/` |
| **Real reviews** | Pull from Google Places / OSM for Phase 2 listings | new `src/lib/reviews.ts` |

## License

[MIT](LICENSE) © 2026 SokoTech.

TDQS

A4/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: get_booking_options returns booking URL and hours; get_business_profile returns full structured profile; get_categories lists business verticals; search_businesses performs structured search; search_by_query handles natural-language queries. No overlaps.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case: get_* for retrieval operations, search_* for searching. Predictable and clear.

Tool Count5/5

5 tools is well-scoped for a business listing and search server. It covers the core functionalities of searching (two modes), retrieving profiles, booking options, and category discovery without being bloated.

Completeness4/5

The tool surface covers the main use cases: search by category/location, natural-language search, profile retrieval, booking info, and category listing. Minor gaps include lack of review details or contact info beyond what's in the profile, but overall it's complete for a read-only search and listing service.

Maintenance

ActivityMaintained
ResponsivenessSyncing