crehq-mcp-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +18 -0
- package/GO-LIVE.md +81 -0
- package/LICENSE +28 -0
- package/NOTICE +28 -0
- package/README.md +177 -0
- package/dist/client.d.ts +56 -0
- package/dist/client.js +157 -0
- package/dist/client.js.map +1 -0
- package/dist/format.d.ts +18 -0
- package/dist/format.js +46 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/tools.d.ts +26 -0
- package/dist/tools.js +335 -0
- package/dist/tools.js.map +1 -0
- package/package.json +56 -0
package/.env.example
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# CREHQ MCP Server configuration
|
|
2
|
+
#
|
|
3
|
+
# Get a FREE sandbox key (1,000 calls/mo, no credit card) at:
|
|
4
|
+
# https://crehq.com/developers/sandbox/
|
|
5
|
+
# or:
|
|
6
|
+
# curl -X POST "https://crehq.com/wp-json/crehq/v1/selfserve/signup" \
|
|
7
|
+
# -H "Content-Type: application/json" \
|
|
8
|
+
# -d '{"email":"you@example.com"}'
|
|
9
|
+
# The key is delivered by email and looks like: crehq_live_xxxxxxxx...
|
|
10
|
+
|
|
11
|
+
# REQUIRED: your CREHQ API key (sandbox, dev, production, or enterprise tier).
|
|
12
|
+
CREHQ_API_KEY=crehq_live_your_key_here
|
|
13
|
+
|
|
14
|
+
# OPTIONAL: override the API base URL (defaults to the value below).
|
|
15
|
+
CREHQ_API_BASE=https://crehq.com/wp-json/crehq/v1
|
|
16
|
+
|
|
17
|
+
# OPTIONAL: per-request timeout in milliseconds (default 30000).
|
|
18
|
+
CREHQ_TIMEOUT_MS=30000
|
package/GO-LIVE.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# GO-LIVE checklist — shipping the CREHQ MCP server
|
|
2
|
+
|
|
3
|
+
This is a runnable proof-of-concept today. To turn it into a product CREHQ can
|
|
4
|
+
distribute, here's what's left, in order.
|
|
5
|
+
|
|
6
|
+
## 1. Keys & access (smallest lift, do first)
|
|
7
|
+
- [ ] Confirm the self-serve sandbox flow (`POST /selfserve/signup`) actually
|
|
8
|
+
emails a `crehq_live_…` key, and verify the key works with `./test.sh`.
|
|
9
|
+
The key table (`xcrehqy_crehq_api_keys`) stores keys **hashed**
|
|
10
|
+
(`key_prefix` + `key_hash`), so the plaintext is only seen once at issue.
|
|
11
|
+
- [ ] Decide the tier gate for MCP users (sandbox to try, paid for production).
|
|
12
|
+
Premium tools (`crehq_whitespace`, `crehq_co_tenancy`, `crehq_site_timeline`)
|
|
13
|
+
are Intel/Enterprise-only — make sure the API returns a clean 403 with an
|
|
14
|
+
upgrade message for lower tiers (the server already surfaces that hint).
|
|
15
|
+
- [ ] Verify the documented endpoints all exist on prod with the exact paths
|
|
16
|
+
this server uses (companies, locations, sites/timeline, intelligence,
|
|
17
|
+
datasets, trends). `./test.sh` covers the read GETs; spot-check
|
|
18
|
+
`/sites/{uid}/timeline`, `/intelligence/*`, `/companies/{id}/occupancy`.
|
|
19
|
+
|
|
20
|
+
## 2. Hosting / distribution model — pick one (or do both)
|
|
21
|
+
|
|
22
|
+
**A. Local stdio (ship now).** Users run `node dist/index.js` from their own
|
|
23
|
+
machine with their key in env. Zero infra for CREHQ. This is the default and
|
|
24
|
+
what Claude Desktop / Cursor / Claude Code expect.
|
|
25
|
+
- [ ] Publish to npm as `crehq-mcp-server` (already configured with a `bin`)
|
|
26
|
+
so users can `npx crehq-mcp-server` — no clone/build step.
|
|
27
|
+
- [ ] Tag a GitHub release; add CI that runs `npm run build` + `npm run typecheck`.
|
|
28
|
+
|
|
29
|
+
**B. Hosted / remote MCP server (scales to non-technical users & web Claude).**
|
|
30
|
+
Wrap the same tool handlers behind a Streamable-HTTP / SSE transport
|
|
31
|
+
(`@modelcontextprotocol/sdk` server with `StreamableHTTPServerTransport`) so
|
|
32
|
+
users add a URL instead of installing anything. This is required for the
|
|
33
|
+
Claude **connector directory** and for Claude.ai web/mobile.
|
|
34
|
+
- [ ] Add an HTTP transport entrypoint alongside the stdio one (share `tools.ts`).
|
|
35
|
+
- [ ] Add OAuth 2.1 (the MCP authorization spec) or a hosted key-exchange so a
|
|
36
|
+
user authorizes once and CREHQ maps the session → their API key, instead
|
|
37
|
+
of pasting a raw key. This is the main net-new work for option B.
|
|
38
|
+
- [ ] Deploy (Cloudflare Workers has first-class remote-MCP support, or any
|
|
39
|
+
Node host: Fly/Render/Lambda). Terminate TLS, set rate limits per session.
|
|
40
|
+
|
|
41
|
+
## 3. Listings / discovery
|
|
42
|
+
- [ ] **Anthropic connector / MCP directory** — submit the hosted server (needs
|
|
43
|
+
option B + OAuth). Highest-intent channel for Claude users.
|
|
44
|
+
- [ ] **`modelcontextprotocol/servers`** GitHub list — PR adding CREHQ under
|
|
45
|
+
community servers (works for the stdio version).
|
|
46
|
+
- [ ] **mcp.so**, **Smithery**, **PulseMCP**, **Glama** — submit listings;
|
|
47
|
+
Smithery can host/build the stdio server for users automatically.
|
|
48
|
+
- [ ] Add an `mcp` badge + "Connect to Claude" snippet to https://crehq.com/developers/.
|
|
49
|
+
|
|
50
|
+
## 4. Productizing & metered pricing (the business case)
|
|
51
|
+
The MCP server is a new, low-friction sales surface: an agent reads the tool
|
|
52
|
+
descriptions and pulls users into CREHQ data mid-conversation. Suggested
|
|
53
|
+
per-call metered tier on top of the existing $99/$1,500/$20k plans:
|
|
54
|
+
- [ ] **Free**: ~100 lookups/mo (maps to the sandbox key) — let agents try it.
|
|
55
|
+
- [ ] **Metered**: ~$20–$50 per 1,000 lookups for standard company/location reads.
|
|
56
|
+
- [ ] **Premium per-call**: higher unit price (or an add-on) for the
|
|
57
|
+
differentiators — `whitespace`, `co-tenancy`, `site-timeline`,
|
|
58
|
+
`occupancy` — since those are the unique, defensible answers.
|
|
59
|
+
- [ ] Meter on the existing `selfserve/usage` endpoint + `request_count` /
|
|
60
|
+
`rate_limit_per_min` columns already on the key table; bill via Stripe.
|
|
61
|
+
- [ ] Track which tools convert (the descriptions are the sales copy) and
|
|
62
|
+
iterate on wording.
|
|
63
|
+
|
|
64
|
+
## 5. Hardening before "production"
|
|
65
|
+
- [ ] Pin the SDK version and add a smoke test in CI that boots the server,
|
|
66
|
+
lists tools, and asserts a known error shape (the repo already has the
|
|
67
|
+
one-off harness used to verify this — promote it to a test).
|
|
68
|
+
- [ ] Add structured logging (stderr only — stdout is the protocol channel).
|
|
69
|
+
- [ ] Add a lightweight client-side cache/backoff for the cached intel
|
|
70
|
+
endpoints (`X-CREHQ-Cache`, 6h TTL) to save quota.
|
|
71
|
+
- [ ] Confirm response shapes match the tool descriptions with a real key, and
|
|
72
|
+
tighten any field docs that drift from production.
|
|
73
|
+
- [ ] Security review: never log the API key; redact it from any error output.
|
|
74
|
+
|
|
75
|
+
## Status of this artifact
|
|
76
|
+
- ✅ Builds & typechecks clean (`npm run build`, `npm run typecheck`).
|
|
77
|
+
- ✅ 24 tools, correct names/descriptions/JSON schemas, Zod-validated inputs.
|
|
78
|
+
- ✅ Verified end-to-end against the **live** API (real 403 + correct hint;
|
|
79
|
+
MCP handshake, tool list, and validation all pass over stdio).
|
|
80
|
+
- ⏳ Real data rows pending a valid `CREHQ_API_KEY` (sandbox key is emailed).
|
|
81
|
+
- ⛔ No production data was created or modified to build this.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CREHQ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
----------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
NOTE: This MIT license covers ONLY the source code of this MCP server (the
|
|
26
|
+
API wrapper). It does NOT grant any rights to the CREHQ data accessed through
|
|
27
|
+
it. The CREHQ location-intelligence data is proprietary and is provided solely
|
|
28
|
+
under CREHQ's API terms and your applicable API tier/contract. See NOTICE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
CREHQ MCP Server
|
|
2
|
+
================
|
|
3
|
+
|
|
4
|
+
This package contains TWO distinct things with TWO distinct licenses:
|
|
5
|
+
|
|
6
|
+
1. THE CODE (this MCP server / API wrapper)
|
|
7
|
+
-----------------------------------------
|
|
8
|
+
Licensed under the MIT License (see LICENSE). You may freely use, copy,
|
|
9
|
+
modify, and redistribute the source code of this server.
|
|
10
|
+
|
|
11
|
+
2. THE DATA (everything the server fetches from the CREHQ API)
|
|
12
|
+
-----------------------------------------------------------
|
|
13
|
+
The CREHQ location-intelligence data accessed through this server —
|
|
14
|
+
franchise/brand records, store-level location data, FDD financials,
|
|
15
|
+
site tenancy history, whitespace, co-tenancy, datasets, trends, and any
|
|
16
|
+
other content returned by https://crehq.com/wp-json/crehq/v1 — is
|
|
17
|
+
PROPRIETARY to CREHQ and is NOT covered by the MIT license above.
|
|
18
|
+
|
|
19
|
+
That data is provided solely under CREHQ's API Terms and the terms of your
|
|
20
|
+
applicable API tier or contract. Your right to retrieve, store, display,
|
|
21
|
+
resell, or redistribute CREHQ data is governed entirely by those terms,
|
|
22
|
+
independent of the open-source license on this code.
|
|
23
|
+
|
|
24
|
+
- API & data license terms: https://crehq.com/license-terms/
|
|
25
|
+
- Enterprise / commercial licensing: enterprise@crehq.com
|
|
26
|
+
|
|
27
|
+
Use of an API key constitutes acceptance of CREHQ's API terms. Removing or
|
|
28
|
+
modifying this NOTICE does not change your obligations regarding CREHQ data.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# CREHQ MCP Server
|
|
2
|
+
|
|
3
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) server that turns
|
|
4
|
+
[CREHQ](https://crehq.com)'s **live location-intelligence REST API** into native
|
|
5
|
+
tools for Claude and other AI agents. Ask an agent *"where should Chipotle open
|
|
6
|
+
next?"*, *"who has ever occupied this address?"*, or *"what are Planet Fitness's
|
|
7
|
+
franchise fees and where are they expanding?"* — and it can actually answer,
|
|
8
|
+
backed by CREHQ's canonical, **multi-source government-verified** database of
|
|
9
|
+
franchise & multi-unit brands, individual storefronts, FDD financials, and
|
|
10
|
+
site-level tenancy history.
|
|
11
|
+
|
|
12
|
+
This is a thin wrapper over CREHQ's existing production API
|
|
13
|
+
(`https://crehq.com/wp-json/crehq/v1`). It does not store data or modify
|
|
14
|
+
anything server-side — it authenticates with your API key and forwards calls.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## What it exposes (24 tools)
|
|
19
|
+
|
|
20
|
+
**Companies / brands**
|
|
21
|
+
- `crehq_companies_list` — list brands, filter by category & expansion status
|
|
22
|
+
- `crehq_companies_search` — resolve a brand name → CREHQ company id (start here)
|
|
23
|
+
- `crehq_company_get` — canonical brand profile, verified unit count, ownership
|
|
24
|
+
- `crehq_company_franchise` — FDD fees, royalties, investment, Item 19 figures
|
|
25
|
+
- `crehq_company_real_estate` — site-selection criteria & target geographies
|
|
26
|
+
- `crehq_company_contacts` — real-estate / development decision-maker contacts
|
|
27
|
+
|
|
28
|
+
**Locations**
|
|
29
|
+
- `crehq_locations_list` — store records by brand / state / category
|
|
30
|
+
- `crehq_location_get` — one location with full attributes & verification trace
|
|
31
|
+
- `crehq_locations_search` — fuzzy multi-field location search
|
|
32
|
+
- `crehq_locations_nearby` — radius search around a lat/lng (trade-area mapping)
|
|
33
|
+
- `crehq_locations_bulk` — bulk pull by id list, brand list, or GeoJSON polygon
|
|
34
|
+
- `crehq_locations_events` — cross-brand open/close/relocate lifecycle stream
|
|
35
|
+
- `crehq_location_history` — full event log for one physical store
|
|
36
|
+
|
|
37
|
+
**History & flagship differentiators**
|
|
38
|
+
- `crehq_company_changes` — date-bounded change feed for one brand
|
|
39
|
+
- `crehq_company_occupancy` — point-in-time roster (footprint on a past date)
|
|
40
|
+
- `crehq_site_timeline` — **every brand that ever occupied an address, over time**
|
|
41
|
+
|
|
42
|
+
**Premium intelligence** (Intel & Enterprise tiers)
|
|
43
|
+
- `crehq_whitespace` — markets where competitors are present but the brand isn't
|
|
44
|
+
- `crehq_co_tenancy` — which brands cluster near this brand's stores
|
|
45
|
+
|
|
46
|
+
**Datasets**
|
|
47
|
+
- `crehq_datasets_list` / `crehq_dataset_get` / `crehq_dataset_download` / `crehq_dataset_categories`
|
|
48
|
+
|
|
49
|
+
**Trends**
|
|
50
|
+
- `crehq_trends_company` — outlet/fee/financial time series for a brand
|
|
51
|
+
- `crehq_trends_geographic` — metro/state concentration & velocity
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Getting an API key
|
|
56
|
+
|
|
57
|
+
1. **Free sandbox** (1,000 calls/mo, 2 req/s, no credit card):
|
|
58
|
+
https://crehq.com/developers/sandbox/ — enter your email and a key is
|
|
59
|
+
emailed to you. Or request one via the API:
|
|
60
|
+
```bash
|
|
61
|
+
curl -X POST "https://crehq.com/wp-json/crehq/v1/selfserve/signup" \
|
|
62
|
+
-H "Content-Type: application/json" \
|
|
63
|
+
-d '{"email":"you@example.com"}'
|
|
64
|
+
```
|
|
65
|
+
The key is delivered by email only and looks like `crehq_live_xxxxxxxx…`.
|
|
66
|
+
2. **Paid tiers** — Developer from **$99/mo**, Production from **$1,500/mo**,
|
|
67
|
+
Enterprise **$20k+/yr** (dedicated key, SLA, premium intel endpoints).
|
|
68
|
+
See https://crehq.com/developers/ and https://crehq.com/apis/.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Install & build
|
|
73
|
+
|
|
74
|
+
Requires Node.js ≥ 18.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git clone <this-repo> crehq-mcp-server
|
|
78
|
+
cd crehq-mcp-server
|
|
79
|
+
npm install
|
|
80
|
+
npm run build # compiles TypeScript → dist/
|
|
81
|
+
cp .env.example .env # then edit .env and set CREHQ_API_KEY
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Verify your key against the live API before wiring up a client:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
export CREHQ_API_KEY=crehq_live_xxxxx
|
|
88
|
+
./test.sh # exercises 6 read endpoints with curl
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Run the server standalone (it speaks MCP over stdio, so it will wait for a
|
|
92
|
+
client on stdin — Ctrl-C to exit):
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
CREHQ_API_KEY=crehq_live_xxxxx node dist/index.js
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Connect to Claude Desktop
|
|
101
|
+
|
|
102
|
+
Add this to your `claude_desktop_config.json`:
|
|
103
|
+
|
|
104
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
105
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"mcpServers": {
|
|
110
|
+
"crehq": {
|
|
111
|
+
"command": "node",
|
|
112
|
+
"args": ["/absolute/path/to/crehq-mcp-server/dist/index.js"],
|
|
113
|
+
"env": {
|
|
114
|
+
"CREHQ_API_KEY": "crehq_live_your_key_here"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Restart Claude Desktop. The CREHQ tools appear under the tools (🔌) menu.
|
|
122
|
+
|
|
123
|
+
### Other MCP clients
|
|
124
|
+
|
|
125
|
+
Any stdio-capable MCP client (Claude Code, Cursor, Cline, Continue, the
|
|
126
|
+
`mcp` CLI, custom `@modelcontextprotocol/sdk` clients, etc.) connects the same
|
|
127
|
+
way — run `node dist/index.js` as the server command with `CREHQ_API_KEY` in
|
|
128
|
+
the environment.
|
|
129
|
+
|
|
130
|
+
**Claude Code:**
|
|
131
|
+
```bash
|
|
132
|
+
claude mcp add crehq --env CREHQ_API_KEY=crehq_live_xxxxx -- node /absolute/path/to/crehq-mcp-server/dist/index.js
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Configuration
|
|
138
|
+
|
|
139
|
+
| Env var | Required | Default | Purpose |
|
|
140
|
+
|--------------------|----------|----------------------------------------------|--------------------------------------|
|
|
141
|
+
| `CREHQ_API_KEY` | yes | — | Your `crehq_live_…` key |
|
|
142
|
+
| `CREHQ_API_BASE` | no | `https://crehq.com/wp-json/crehq/v1` | Override API base (staging/proxy) |
|
|
143
|
+
| `CREHQ_TIMEOUT_MS` | no | `30000` | Per-request timeout in ms |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Error handling
|
|
148
|
+
|
|
149
|
+
Tools never crash the agent's turn — failures come back as a readable message
|
|
150
|
+
with a fix-it hint:
|
|
151
|
+
|
|
152
|
+
- **No key set** → instructs you to set `CREHQ_API_KEY` and links the sandbox.
|
|
153
|
+
- **401 / 403** → "invalid or revoked key / endpoint not in your tier" + upgrade link.
|
|
154
|
+
- **404** → "check the id/slug; resolve it with a search tool first."
|
|
155
|
+
- **429** → respects `Retry-After`; reminds you of the free-tier 2 req/s limit.
|
|
156
|
+
- **5xx / timeout / network** → transient-error guidance to retry with backoff.
|
|
157
|
+
|
|
158
|
+
Pagination, cache, and stream cursors (`X-WP-Total`, `X-CREHQ-Next-Since`,
|
|
159
|
+
`X-CREHQ-Cache`, rate-limit headers) are surfaced in a `response metadata`
|
|
160
|
+
footer on each result so the agent can paginate and poll correctly.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Note on testing
|
|
165
|
+
|
|
166
|
+
The build, typecheck, MCP protocol handshake, tool catalog, argument
|
|
167
|
+
validation, and the full HTTP request/response/error pipeline are verified
|
|
168
|
+
end-to-end against the **live** production API (a real request returns a real
|
|
169
|
+
`403 Invalid or revoked API key` with the correct hint). Fetching real data
|
|
170
|
+
rows requires a valid key — the sandbox key is delivered by email, so set
|
|
171
|
+
`CREHQ_API_KEY` and run `./test.sh` to confirm live data responses.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT. CREHQ data is licensed separately per your API tier/contract.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin HTTP client for the CREHQ REST API (https://crehq.com/wp-json/crehq/v1).
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Inject the API key (Authorization: Bearer + X-API-Key, per the docs).
|
|
6
|
+
* - Build query strings / JSON bodies.
|
|
7
|
+
* - Map HTTP failures (401/403/404/429/4xx/5xx) into clear, agent-readable
|
|
8
|
+
* errors that point the user at the sandbox signup when appropriate.
|
|
9
|
+
* - Surface CREHQ pagination + cache + stream headers to the caller.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_API_BASE = "https://crehq.com/wp-json/crehq/v1";
|
|
12
|
+
export interface CrehqConfig {
|
|
13
|
+
apiKey: string;
|
|
14
|
+
apiBase: string;
|
|
15
|
+
timeoutMs: number;
|
|
16
|
+
}
|
|
17
|
+
export interface CrehqResult {
|
|
18
|
+
/** Parsed JSON body (object or array), or raw text when not JSON. */
|
|
19
|
+
data: unknown;
|
|
20
|
+
/** Selected response headers (pagination/cache/stream cursors). */
|
|
21
|
+
meta: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Error that carries enough context for the tool layer to produce a helpful,
|
|
25
|
+
* non-fatal message back to the LLM instead of crashing the server.
|
|
26
|
+
*/
|
|
27
|
+
export declare class CrehqApiError extends Error {
|
|
28
|
+
readonly status: number;
|
|
29
|
+
readonly hint: string;
|
|
30
|
+
constructor(status: number, message: string, hint?: string);
|
|
31
|
+
}
|
|
32
|
+
export declare function loadConfig(): CrehqConfig;
|
|
33
|
+
type QueryValue = string | number | boolean | undefined | null;
|
|
34
|
+
export interface RequestOptions {
|
|
35
|
+
method?: "GET" | "POST" | "DELETE" | "PUT";
|
|
36
|
+
/** Query string parameters; undefined/null values are dropped. */
|
|
37
|
+
query?: Record<string, QueryValue>;
|
|
38
|
+
/** JSON request body (for POST/PUT). */
|
|
39
|
+
body?: unknown;
|
|
40
|
+
/** Desired representation, sent as the Accept header (e.g. text/csv). */
|
|
41
|
+
accept?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare class CrehqClient {
|
|
44
|
+
private readonly config;
|
|
45
|
+
constructor(config: CrehqConfig);
|
|
46
|
+
/** True when no key is configured — lets tools fail fast with guidance. */
|
|
47
|
+
get hasKey(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Perform a request against a CREHQ endpoint path (e.g. "/companies").
|
|
50
|
+
* Throws CrehqApiError on any non-2xx response or transport failure.
|
|
51
|
+
*/
|
|
52
|
+
request(path: string, opts?: RequestOptions): Promise<CrehqResult>;
|
|
53
|
+
/** Map a non-2xx response into a CrehqApiError with an actionable hint. */
|
|
54
|
+
private toApiError;
|
|
55
|
+
}
|
|
56
|
+
export {};
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin HTTP client for the CREHQ REST API (https://crehq.com/wp-json/crehq/v1).
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Inject the API key (Authorization: Bearer + X-API-Key, per the docs).
|
|
6
|
+
* - Build query strings / JSON bodies.
|
|
7
|
+
* - Map HTTP failures (401/403/404/429/4xx/5xx) into clear, agent-readable
|
|
8
|
+
* errors that point the user at the sandbox signup when appropriate.
|
|
9
|
+
* - Surface CREHQ pagination + cache + stream headers to the caller.
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_API_BASE = "https://crehq.com/wp-json/crehq/v1";
|
|
12
|
+
const SANDBOX_URL = "https://crehq.com/developers/sandbox/";
|
|
13
|
+
/** Headers worth surfacing back to the agent (pagination, cache, event streams). */
|
|
14
|
+
const SURFACED_HEADERS = [
|
|
15
|
+
"x-wp-total",
|
|
16
|
+
"x-wp-totalpages",
|
|
17
|
+
"x-crehq-next-since",
|
|
18
|
+
"x-crehq-cache",
|
|
19
|
+
"x-ratelimit-remaining",
|
|
20
|
+
"x-ratelimit-limit",
|
|
21
|
+
"retry-after",
|
|
22
|
+
"link",
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* Error that carries enough context for the tool layer to produce a helpful,
|
|
26
|
+
* non-fatal message back to the LLM instead of crashing the server.
|
|
27
|
+
*/
|
|
28
|
+
export class CrehqApiError extends Error {
|
|
29
|
+
status;
|
|
30
|
+
hint;
|
|
31
|
+
constructor(status, message, hint = "") {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "CrehqApiError";
|
|
34
|
+
this.status = status;
|
|
35
|
+
this.hint = hint;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function loadConfig() {
|
|
39
|
+
const apiKey = (process.env.CREHQ_API_KEY ?? "").trim();
|
|
40
|
+
const apiBase = (process.env.CREHQ_API_BASE ?? DEFAULT_API_BASE).trim().replace(/\/+$/, "");
|
|
41
|
+
const timeoutMs = Number.parseInt(process.env.CREHQ_TIMEOUT_MS ?? "30000", 10) || 30000;
|
|
42
|
+
return { apiKey, apiBase, timeoutMs };
|
|
43
|
+
}
|
|
44
|
+
export class CrehqClient {
|
|
45
|
+
config;
|
|
46
|
+
constructor(config) {
|
|
47
|
+
this.config = config;
|
|
48
|
+
}
|
|
49
|
+
/** True when no key is configured — lets tools fail fast with guidance. */
|
|
50
|
+
get hasKey() {
|
|
51
|
+
return this.config.apiKey.length > 0;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Perform a request against a CREHQ endpoint path (e.g. "/companies").
|
|
55
|
+
* Throws CrehqApiError on any non-2xx response or transport failure.
|
|
56
|
+
*/
|
|
57
|
+
async request(path, opts = {}) {
|
|
58
|
+
if (!this.hasKey) {
|
|
59
|
+
throw new CrehqApiError(401, "No CREHQ_API_KEY configured.", `Set the CREHQ_API_KEY environment variable. Get a free sandbox key at ${SANDBOX_URL}.`);
|
|
60
|
+
}
|
|
61
|
+
const url = new URL(this.config.apiBase + path);
|
|
62
|
+
if (opts.query) {
|
|
63
|
+
for (const [key, value] of Object.entries(opts.query)) {
|
|
64
|
+
if (value === undefined || value === null || value === "")
|
|
65
|
+
continue;
|
|
66
|
+
url.searchParams.set(key, String(value));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const headers = {
|
|
70
|
+
// The CREHQ docs accept either header; we send both for maximum
|
|
71
|
+
// compatibility across gateway configurations.
|
|
72
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
73
|
+
"X-API-Key": this.config.apiKey,
|
|
74
|
+
Accept: opts.accept ?? "application/json",
|
|
75
|
+
"User-Agent": "crehq-mcp-server/0.1.0",
|
|
76
|
+
};
|
|
77
|
+
const init = { method: opts.method ?? "GET", headers };
|
|
78
|
+
if (opts.body !== undefined) {
|
|
79
|
+
headers["Content-Type"] = "application/json";
|
|
80
|
+
init.body = JSON.stringify(opts.body);
|
|
81
|
+
}
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
84
|
+
init.signal = controller.signal;
|
|
85
|
+
let res;
|
|
86
|
+
try {
|
|
87
|
+
res = await fetch(url, init);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
92
|
+
throw new CrehqApiError(504, `Request to ${path} timed out after ${this.config.timeoutMs}ms.`, "Retry, narrow the query (smaller per_page / tighter filters), or raise CREHQ_TIMEOUT_MS.");
|
|
93
|
+
}
|
|
94
|
+
throw new CrehqApiError(0, `Network error calling CREHQ (${path}): ${err.message}`, "Check connectivity to crehq.com and the CREHQ_API_BASE value.");
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
clearTimeout(timer);
|
|
98
|
+
}
|
|
99
|
+
const meta = {};
|
|
100
|
+
for (const name of SURFACED_HEADERS) {
|
|
101
|
+
const v = res.headers.get(name);
|
|
102
|
+
if (v)
|
|
103
|
+
meta[name] = v;
|
|
104
|
+
}
|
|
105
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
106
|
+
const isJson = contentType.includes("json");
|
|
107
|
+
const rawText = await res.text();
|
|
108
|
+
let parsed = rawText;
|
|
109
|
+
if (isJson && rawText) {
|
|
110
|
+
try {
|
|
111
|
+
parsed = JSON.parse(rawText);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
parsed = rawText;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
throw this.toApiError(res.status, parsed, meta);
|
|
119
|
+
}
|
|
120
|
+
return { data: parsed, meta };
|
|
121
|
+
}
|
|
122
|
+
/** Map a non-2xx response into a CrehqApiError with an actionable hint. */
|
|
123
|
+
toApiError(status, body, meta) {
|
|
124
|
+
const apiMessage = extractMessage(body);
|
|
125
|
+
switch (status) {
|
|
126
|
+
case 401:
|
|
127
|
+
return new CrehqApiError(401, apiMessage ?? "Unauthorized: no API key was accepted.", `Set CREHQ_API_KEY to a valid key. Get a free sandbox key (1,000 calls/mo, no credit card) at ${SANDBOX_URL}.`);
|
|
128
|
+
case 403:
|
|
129
|
+
return new CrehqApiError(403, apiMessage ?? "Forbidden: the API key is invalid, revoked, or not scoped for this endpoint.", `Verify the key is active and that your tier includes this endpoint. Upgrade or request scope at https://crehq.com/api-keys/. Sandbox keys: ${SANDBOX_URL}.`);
|
|
130
|
+
case 404:
|
|
131
|
+
return new CrehqApiError(404, apiMessage ?? "Not found: the requested record or path does not exist.", "Check the id/slug/uid. Use the search tools to resolve an identifier first.");
|
|
132
|
+
case 429: {
|
|
133
|
+
const retry = meta["retry-after"];
|
|
134
|
+
return new CrehqApiError(429, apiMessage ?? "Rate limited.", retry
|
|
135
|
+
? `Slow down and retry after ${retry}s.`
|
|
136
|
+
: "Slow down (free tier is 2 req/s, 1,000 calls/mo) and retry shortly. Higher limits are available on paid tiers.");
|
|
137
|
+
}
|
|
138
|
+
default:
|
|
139
|
+
if (status >= 500) {
|
|
140
|
+
return new CrehqApiError(status, apiMessage ?? `CREHQ server error (${status}).`, "This is a transient server-side issue. Retry with backoff; if it persists, contact CREHQ.");
|
|
141
|
+
}
|
|
142
|
+
return new CrehqApiError(status, apiMessage ?? `Request failed (${status}).`, "Review the parameters against the tool's input schema.");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Pull a human message out of a WP-REST / CREHQ error body. */
|
|
147
|
+
function extractMessage(body) {
|
|
148
|
+
if (body && typeof body === "object" && "message" in body) {
|
|
149
|
+
const m = body.message;
|
|
150
|
+
if (typeof m === "string" && m.length > 0)
|
|
151
|
+
return m;
|
|
152
|
+
}
|
|
153
|
+
if (typeof body === "string" && body.length > 0 && body.length < 500)
|
|
154
|
+
return body;
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers to turn a CrehqResult into the MCP tool-result content blocks the
|
|
3
|
+
* SDK expects, and to render CrehqApiError into a non-fatal, agent-readable
|
|
4
|
+
* message (tools surface errors as content + isError, never by throwing, so a
|
|
5
|
+
* single failing call doesn't kill the agent's turn).
|
|
6
|
+
*/
|
|
7
|
+
import { type CrehqResult } from "./client.js";
|
|
8
|
+
export interface ToolContent {
|
|
9
|
+
content: {
|
|
10
|
+
type: "text";
|
|
11
|
+
text: string;
|
|
12
|
+
}[];
|
|
13
|
+
isError?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** Wrap a successful API result as a pretty-printed JSON text block + meta. */
|
|
16
|
+
export declare function ok(result: CrehqResult): ToolContent;
|
|
17
|
+
/** Wrap a caught error as a non-fatal tool error with an actionable hint. */
|
|
18
|
+
export declare function fail(err: unknown): ToolContent;
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers to turn a CrehqResult into the MCP tool-result content blocks the
|
|
3
|
+
* SDK expects, and to render CrehqApiError into a non-fatal, agent-readable
|
|
4
|
+
* message (tools surface errors as content + isError, never by throwing, so a
|
|
5
|
+
* single failing call doesn't kill the agent's turn).
|
|
6
|
+
*/
|
|
7
|
+
import { CrehqApiError } from "./client.js";
|
|
8
|
+
/** Wrap a successful API result as a pretty-printed JSON text block + meta. */
|
|
9
|
+
export function ok(result) {
|
|
10
|
+
const metaKeys = Object.keys(result.meta);
|
|
11
|
+
const metaNote = metaKeys.length > 0
|
|
12
|
+
? `\n\n--- response metadata ---\n${formatMeta(result.meta)}`
|
|
13
|
+
: "";
|
|
14
|
+
return {
|
|
15
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) + metaNote }],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Wrap a caught error as a non-fatal tool error with an actionable hint. */
|
|
19
|
+
export function fail(err) {
|
|
20
|
+
if (err instanceof CrehqApiError) {
|
|
21
|
+
const lines = [`CREHQ API error (HTTP ${err.status || "n/a"}): ${err.message}`];
|
|
22
|
+
if (err.hint)
|
|
23
|
+
lines.push(`Suggestion: ${err.hint}`);
|
|
24
|
+
return { content: [{ type: "text", text: lines.join("\n") }], isError: true };
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
content: [{ type: "text", text: `Unexpected error: ${err.message}` }],
|
|
28
|
+
isError: true,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function formatMeta(meta) {
|
|
32
|
+
const labels = {
|
|
33
|
+
"x-wp-total": "total_results",
|
|
34
|
+
"x-wp-totalpages": "total_pages",
|
|
35
|
+
"x-crehq-next-since": "next_since_cursor",
|
|
36
|
+
"x-crehq-cache": "cache",
|
|
37
|
+
"x-ratelimit-remaining": "rate_limit_remaining",
|
|
38
|
+
"x-ratelimit-limit": "rate_limit",
|
|
39
|
+
"retry-after": "retry_after_seconds",
|
|
40
|
+
};
|
|
41
|
+
return Object.entries(meta)
|
|
42
|
+
.filter(([k]) => k !== "link")
|
|
43
|
+
.map(([k, v]) => `${labels[k] ?? k}: ${v}`)
|
|
44
|
+
.join("\n");
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAAoB,MAAM,aAAa,CAAC;AAO9D,+EAA+E;AAC/E,MAAM,UAAU,EAAE,CAAC,MAAmB;IACpC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,QAAQ,GACZ,QAAQ,CAAC,MAAM,GAAG,CAAC;QACjB,CAAC,CAAC,kCAAkC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;QAC7D,CAAC,CAAC,EAAE,CAAC;IACT,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,CAAC;KACnF,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,IAAI,CAAC,GAAY;IAC/B,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,CAAC,yBAAyB,GAAG,CAAC,MAAM,IAAI,KAAK,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAChF,IAAI,GAAG,CAAC,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,eAAe,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QACpD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAChF,CAAC;IACD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAsB,GAAa,CAAC,OAAO,EAAE,EAAE,CAAC;QAChF,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,IAA4B;IAC9C,MAAM,MAAM,GAA2B;QACrC,YAAY,EAAE,eAAe;QAC7B,iBAAiB,EAAE,aAAa;QAChC,oBAAoB,EAAE,mBAAmB;QACzC,eAAe,EAAE,OAAO;QACxB,uBAAuB,EAAE,sBAAsB;QAC/C,mBAAmB,EAAE,YAAY;QACjC,aAAa,EAAE,qBAAqB;KACrC,CAAC;IACF,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;SACxB,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC;SAC7B,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;SAC1C,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CREHQ MCP server — exposes CREHQ's live location-intelligence REST API
|
|
4
|
+
* (https://crehq.com/wp-json/crehq/v1) as Model Context Protocol tools so that
|
|
5
|
+
* Claude and other MCP clients can query brands, locations, FDD financials,
|
|
6
|
+
* site-level tenancy history, whitespace, and co-tenancy as native tools.
|
|
7
|
+
*
|
|
8
|
+
* Transport: stdio (the standard for Claude Desktop / local connectors).
|
|
9
|
+
* Auth: CREHQ_API_KEY env var (sandbox/dev/prod/enterprise).
|
|
10
|
+
*
|
|
11
|
+
* This wraps an EXISTING production API; it does not modify any server-side
|
|
12
|
+
* data. See README.md for setup and GO-LIVE.md for shipping.
|
|
13
|
+
*/
|
|
14
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
15
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
16
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
import { CrehqClient, loadConfig } from "./client.js";
|
|
19
|
+
import { TOOLS, toJsonSchema } from "./tools.js";
|
|
20
|
+
async function main() {
|
|
21
|
+
const config = loadConfig();
|
|
22
|
+
const client = new CrehqClient(config);
|
|
23
|
+
// Warn (to stderr — never stdout, which carries the MCP protocol) if no key.
|
|
24
|
+
if (!client.hasKey) {
|
|
25
|
+
process.stderr.write("[crehq-mcp] WARNING: CREHQ_API_KEY is not set. Tools will return an auth hint until a key is configured. " +
|
|
26
|
+
"Get a free sandbox key at https://crehq.com/developers/sandbox/\n");
|
|
27
|
+
}
|
|
28
|
+
const server = new Server({ name: "crehq-mcp-server", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
29
|
+
// Advertise the tool catalog (names + descriptions + JSON schemas).
|
|
30
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
31
|
+
tools: TOOLS.map((t) => ({
|
|
32
|
+
name: t.name,
|
|
33
|
+
description: t.description,
|
|
34
|
+
inputSchema: toJsonSchema(t.schema),
|
|
35
|
+
})),
|
|
36
|
+
}));
|
|
37
|
+
// Dispatch a tool call: validate args with Zod, then run the handler.
|
|
38
|
+
// (Results are cast to the SDK's CallToolResult shape; our ToolContent is a
|
|
39
|
+
// structural subset — content blocks + optional isError.)
|
|
40
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
41
|
+
const tool = TOOLS.find((t) => t.name === request.params.name);
|
|
42
|
+
if (!tool) {
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }],
|
|
45
|
+
isError: true,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const parsed = z.object(tool.schema).safeParse(request.params.arguments ?? {});
|
|
49
|
+
if (!parsed.success) {
|
|
50
|
+
const issues = parsed.error.issues
|
|
51
|
+
.map((i) => `- ${i.path.join(".") || "(root)"}: ${i.message}`)
|
|
52
|
+
.join("\n");
|
|
53
|
+
return {
|
|
54
|
+
content: [{ type: "text", text: `Invalid arguments for ${tool.name}:\n${issues}` }],
|
|
55
|
+
isError: true,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
return (await tool.handler(client, parsed.data));
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
// Defensive net: handlers already convert CrehqApiError to content, but a
|
|
63
|
+
// truly unexpected throw should still not kill the server.
|
|
64
|
+
return {
|
|
65
|
+
content: [{ type: "text", text: `Unhandled error in ${tool.name}: ${err.message}` }],
|
|
66
|
+
isError: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const transport = new StdioServerTransport();
|
|
71
|
+
await server.connect(transport);
|
|
72
|
+
process.stderr.write(`[crehq-mcp] CREHQ MCP server ready over stdio with ${TOOLS.length} tools. Base: ${config.apiBase}\n`);
|
|
73
|
+
}
|
|
74
|
+
main().catch((err) => {
|
|
75
|
+
process.stderr.write(`[crehq-mcp] Fatal: ${err.message}\n`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CREHQ MCP tool registry.
|
|
3
|
+
*
|
|
4
|
+
* Each entry pairs an LLM-facing name + description (the description is a sales
|
|
5
|
+
* surface — it tells Claude what CREHQ uniquely can answer) with a Zod input
|
|
6
|
+
* schema and a handler that maps to one CREHQ REST endpoint.
|
|
7
|
+
*
|
|
8
|
+
* Endpoint contract sourced from https://crehq.com/developers/ and
|
|
9
|
+
* https://crehq.com/apis/ (verified 2026-06-17).
|
|
10
|
+
*/
|
|
11
|
+
import { type ZodRawShape } from "zod";
|
|
12
|
+
import type { CrehqClient } from "./client.js";
|
|
13
|
+
import { type ToolContent } from "./format.js";
|
|
14
|
+
export interface ToolDef {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
schema: ZodRawShape;
|
|
18
|
+
handler: (client: CrehqClient, args: Record<string, unknown>) => Promise<ToolContent>;
|
|
19
|
+
}
|
|
20
|
+
export declare const TOOLS: ToolDef[];
|
|
21
|
+
/** Convert a Zod raw shape to the JSON Schema the MCP SDK advertises to clients. */
|
|
22
|
+
export declare function toJsonSchema(shape: ZodRawShape): {
|
|
23
|
+
type: "object";
|
|
24
|
+
properties: Record<string, unknown>;
|
|
25
|
+
required: string[];
|
|
26
|
+
};
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CREHQ MCP tool registry.
|
|
3
|
+
*
|
|
4
|
+
* Each entry pairs an LLM-facing name + description (the description is a sales
|
|
5
|
+
* surface — it tells Claude what CREHQ uniquely can answer) with a Zod input
|
|
6
|
+
* schema and a handler that maps to one CREHQ REST endpoint.
|
|
7
|
+
*
|
|
8
|
+
* Endpoint contract sourced from https://crehq.com/developers/ and
|
|
9
|
+
* https://crehq.com/apis/ (verified 2026-06-17).
|
|
10
|
+
*/
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { ok, fail } from "./format.js";
|
|
13
|
+
/** Run a request and normalize success/error into MCP content. */
|
|
14
|
+
async function call(fn) {
|
|
15
|
+
try {
|
|
16
|
+
return ok(await fn());
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return fail(err);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// --- shared schema fragments -------------------------------------------------
|
|
23
|
+
const perPage = z
|
|
24
|
+
.number()
|
|
25
|
+
.int()
|
|
26
|
+
.min(1)
|
|
27
|
+
.max(200)
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("Results per page (max 200, default 50).");
|
|
30
|
+
const page = z.number().int().min(1).optional().describe("Page number for cursor/offset pagination (default 1).");
|
|
31
|
+
export const TOOLS = [
|
|
32
|
+
// ========================================================================
|
|
33
|
+
// COMPANIES / BRANDS
|
|
34
|
+
// ========================================================================
|
|
35
|
+
{
|
|
36
|
+
name: "crehq_companies_list",
|
|
37
|
+
description: "List franchise & multi-unit brands (companies) tracked in CREHQ's canonical, multi-source government-verified location database — restaurants, retail, banks, auto dealers, healthcare, hotels, EV charging and more. Filter by category or expansion status to discover brands actively opening or closing units. Each record links to verified store counts, FDD financials, real-estate criteria, and decision-maker contacts. Paginated.",
|
|
38
|
+
schema: {
|
|
39
|
+
category: z.string().optional().describe("Filter by vertical/category slug, e.g. 'restaurant', 'bank', 'auto-dealer', 'ev-charging'."),
|
|
40
|
+
expansion_status: z
|
|
41
|
+
.enum(["expanding", "stable", "contracting"])
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Filter brands by growth trajectory derived from location lifecycle data."),
|
|
44
|
+
per_page: perPage,
|
|
45
|
+
page,
|
|
46
|
+
},
|
|
47
|
+
handler: (c, a) => call(() => c.request("/companies", {
|
|
48
|
+
query: { category: a.category, expansion_status: a.expansion_status, per_page: a.per_page, page: a.page },
|
|
49
|
+
})),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "crehq_companies_search",
|
|
53
|
+
description: "Full-text search CREHQ's brand universe by name to resolve a company to its canonical CREHQ id. Handles messy/alias names (e.g. 'mcdonald-s' → McDonald's Corporation). ALWAYS use this first to get a company_id before calling company detail, franchise/FDD, real-estate, contacts, occupancy, whitespace, or co-tenancy tools.",
|
|
54
|
+
schema: {
|
|
55
|
+
q: z.string().min(1).describe("Search term — brand or company name (partial OK)."),
|
|
56
|
+
per_page: perPage,
|
|
57
|
+
},
|
|
58
|
+
handler: (c, a) => call(() => c.request("/companies/search", { query: { q: a.q, per_page: a.per_page } })),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "crehq_company_get",
|
|
62
|
+
description: "Get a single brand's canonical CREHQ profile: verified current location count, vertical/category, parent/ownership (incl. private-equity backing where known), geographic footprint summary, and verification trace. The authoritative, de-duplicated source-of-truth record for a brand.",
|
|
63
|
+
schema: { id: z.union([z.string(), z.number()]).describe("CREHQ company id (from crehq_companies_search).") },
|
|
64
|
+
handler: (c, a) => call(() => c.request(`/company/${encodeURIComponent(String(a.id))}`)),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "crehq_company_franchise",
|
|
68
|
+
description: "Retrieve FDD (Franchise Disclosure Document)-derived financials for a brand: franchise fees, royalty rates, total initial investment ranges, and Item 19 financial performance representations where disclosed. Sourced and multi-source-verified from state franchise registries — the hard numbers an analyst, investor, or prospective franchisee needs to underwrite a concept.",
|
|
69
|
+
schema: { id: z.union([z.string(), z.number()]).describe("CREHQ company id (from crehq_companies_search).") },
|
|
70
|
+
handler: (c, a) => call(() => c.request(`/company/${encodeURIComponent(String(a.id))}/franchise`)),
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "crehq_company_real_estate",
|
|
74
|
+
description: "Get a brand's site-selection criteria and target real-estate profile: preferred site types, building/lot size, target geographies and trade areas, and expansion markets. Essential for landlords, brokers, and site-selectors who want to know what a tenant is looking for before pitching them space.",
|
|
75
|
+
schema: { id: z.union([z.string(), z.number()]).describe("CREHQ company id (from crehq_companies_search).") },
|
|
76
|
+
handler: (c, a) => call(() => c.request(`/company/${encodeURIComponent(String(a.id))}/real-estate`)),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "crehq_company_contacts",
|
|
80
|
+
description: "Get real-estate decision-maker contacts for a brand (development, site-selection, and franchising roles) compiled from public records and the brand's own disclosures. The shortcut from 'which brand is expanding' to 'who do I email'.",
|
|
81
|
+
schema: { id: z.union([z.string(), z.number()]).describe("CREHQ company id (from crehq_companies_search).") },
|
|
82
|
+
handler: (c, a) => call(() => c.request(`/company/${encodeURIComponent(String(a.id))}/contacts`)),
|
|
83
|
+
},
|
|
84
|
+
// ========================================================================
|
|
85
|
+
// LOCATIONS
|
|
86
|
+
// ========================================================================
|
|
87
|
+
{
|
|
88
|
+
name: "crehq_locations_list",
|
|
89
|
+
description: "List individual store/branch/site records, filterable by brand, US state, and category. Each location carries a stable entity_uid, geocoded address, open/closed status, and a multi-source verification trace. The raw, government-cross-checked footprint behind any brand.",
|
|
90
|
+
schema: {
|
|
91
|
+
brand: z.string().optional().describe("Brand slug or name to filter by (e.g. 'planet-fitness')."),
|
|
92
|
+
state: z.string().optional().describe("US state, 2-letter code or full name (e.g. 'TX')."),
|
|
93
|
+
category: z.string().optional().describe("Vertical/category slug."),
|
|
94
|
+
per_page: perPage,
|
|
95
|
+
page,
|
|
96
|
+
},
|
|
97
|
+
handler: (c, a) => call(() => c.request("/locations", {
|
|
98
|
+
query: { brand: a.brand, state: a.state, category: a.category, per_page: a.per_page, page: a.page },
|
|
99
|
+
})),
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "crehq_location_get",
|
|
103
|
+
description: "Get one location's full record by id: geocoded address, brand, lifecycle status, attributes (e.g. drive-thru, square footage, fuel/EV ports where applicable), and the sources that verify it exists.",
|
|
104
|
+
schema: { id: z.union([z.string(), z.number()]).describe("CREHQ location id.") },
|
|
105
|
+
handler: (c, a) => call(() => c.request(`/locations/${encodeURIComponent(String(a.id))}`)),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "crehq_locations_search",
|
|
109
|
+
description: "Search locations across multiple fields at once — name, brand, street address, city/state/geography. Use when you have a fuzzy description of a physical place rather than an id.",
|
|
110
|
+
schema: {
|
|
111
|
+
name: z.string().optional().describe("Location or brand name fragment."),
|
|
112
|
+
brand: z.string().optional().describe("Brand slug/name."),
|
|
113
|
+
address: z.string().optional().describe("Street address fragment."),
|
|
114
|
+
state: z.string().optional().describe("US state code or name."),
|
|
115
|
+
city: z.string().optional().describe("City name."),
|
|
116
|
+
per_page: perPage,
|
|
117
|
+
},
|
|
118
|
+
handler: (c, a) => call(() => c.request("/locations/search", {
|
|
119
|
+
query: { name: a.name, brand: a.brand, address: a.address, state: a.state, city: a.city, per_page: a.per_page },
|
|
120
|
+
})),
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "crehq_locations_nearby",
|
|
124
|
+
description: "Radius search: find all tracked locations within N miles of a lat/lng point. Powers trade-area analysis, competitor mapping, and 'what's near this address' questions. Returns distance-sorted, government-verified storefronts across every vertical CREHQ covers.",
|
|
125
|
+
schema: {
|
|
126
|
+
lat: z.number().describe("Latitude (decimal degrees)."),
|
|
127
|
+
lng: z.number().describe("Longitude (decimal degrees)."),
|
|
128
|
+
radius_mi: z.number().min(0.1).max(100).optional().describe("Search radius in miles (default 5)."),
|
|
129
|
+
brand: z.string().optional().describe("Optional: restrict to one brand."),
|
|
130
|
+
category: z.string().optional().describe("Optional: restrict to one vertical/category."),
|
|
131
|
+
per_page: perPage,
|
|
132
|
+
},
|
|
133
|
+
handler: (c, a) => call(() => c.request("/locations/nearby", {
|
|
134
|
+
query: { lat: a.lat, lng: a.lng, radius_mi: a.radius_mi, brand: a.brand, category: a.category, per_page: a.per_page },
|
|
135
|
+
})),
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "crehq_locations_bulk",
|
|
139
|
+
description: "Bulk location retrieval for ETL/pipeline use: fetch many locations in one call by a list of ids, a list of brands, or a GeoJSON polygon (e.g. a custom market boundary). Use this instead of looping single-location calls when hydrating a dataset.",
|
|
140
|
+
schema: {
|
|
141
|
+
ids: z.array(z.union([z.string(), z.number()])).optional().describe("Explicit list of location ids/entity_uids."),
|
|
142
|
+
brands: z.array(z.string()).optional().describe("List of brand slugs to pull all locations for."),
|
|
143
|
+
polygon: z
|
|
144
|
+
.unknown()
|
|
145
|
+
.optional()
|
|
146
|
+
.describe("GeoJSON Polygon/MultiPolygon geometry; returns locations inside the boundary."),
|
|
147
|
+
per_page: perPage,
|
|
148
|
+
},
|
|
149
|
+
handler: (c, a) => call(() => c.request("/locations/bulk", {
|
|
150
|
+
method: "POST",
|
|
151
|
+
body: { ids: a.ids, brands: a.brands, polygon: a.polygon, per_page: a.per_page },
|
|
152
|
+
})),
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "crehq_locations_events",
|
|
156
|
+
description: "Pull the cross-brand location LIFECYCLE STREAM — openings, closings, relocations, ownership/brand changes — since a timestamp. The real-time expansion/contraction signal that drives prospecting, market-monitoring, and 'who's moving right now' alerts. Returns a next-since cursor for incremental polling.",
|
|
157
|
+
schema: {
|
|
158
|
+
since: z.string().describe("ISO-8601 timestamp; returns events on/after this time. Use the returned next_since_cursor for the next poll."),
|
|
159
|
+
per_page: perPage,
|
|
160
|
+
},
|
|
161
|
+
handler: (c, a) => call(() => c.request("/locations/events", { query: { since: a.since, per_page: a.per_page } })),
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: "crehq_location_history",
|
|
165
|
+
description: "Full append-only event log for ONE physical store/site (by entity_uid): every open/close/rebrand/attribute change CREHQ has recorded, with dates and sources. Time-series provenance for a single location.",
|
|
166
|
+
schema: {
|
|
167
|
+
entity_uid: z.string().describe("Stable CREHQ entity_uid for the location."),
|
|
168
|
+
limit: z.number().int().min(1).max(1000).optional().describe("Max events to return (default 200, max 1000)."),
|
|
169
|
+
},
|
|
170
|
+
handler: (c, a) => call(() => c.request(`/locations/${encodeURIComponent(a.entity_uid)}/history`, { query: { limit: a.limit } })),
|
|
171
|
+
},
|
|
172
|
+
// ========================================================================
|
|
173
|
+
// CHANGES / OCCUPANCY / SITE TIMELINE (flagship differentiators)
|
|
174
|
+
// ========================================================================
|
|
175
|
+
{
|
|
176
|
+
name: "crehq_company_changes",
|
|
177
|
+
description: "Date-bounded feed of everything that changed for ONE brand's footprint — openings, closings, relocations, attribute edits — between two timestamps and optionally filtered by event type. The brand-scoped version of the lifecycle stream, ideal for monitoring a target account.",
|
|
178
|
+
schema: {
|
|
179
|
+
id: z.union([z.string(), z.number()]).describe("CREHQ company id."),
|
|
180
|
+
since: z.string().optional().describe("ISO-8601 start timestamp."),
|
|
181
|
+
until: z.string().optional().describe("ISO-8601 end timestamp."),
|
|
182
|
+
types: z.string().optional().describe("Comma-separated event types to include (e.g. 'opened,closed,relocated')."),
|
|
183
|
+
limit: z.number().int().min(1).max(5000).optional().describe("Max events (default 500, max 5000)."),
|
|
184
|
+
},
|
|
185
|
+
handler: (c, a) => call(() => c.request(`/companies/${encodeURIComponent(String(a.id))}/changes`, {
|
|
186
|
+
query: { since: a.since, until: a.until, types: a.types, limit: a.limit },
|
|
187
|
+
})),
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "crehq_company_occupancy",
|
|
191
|
+
description: "POINT-IN-TIME roster: reconstruct exactly which locations a brand operated on a given historical date. Answers 'how many units did this chain have on 2022-01-01 and where' — true historical footprint, not just today's count. Powers growth-curve and same-store analysis.",
|
|
192
|
+
schema: {
|
|
193
|
+
id: z.union([z.string(), z.number()]).describe("CREHQ company id."),
|
|
194
|
+
date: z.string().optional().describe("ISO date (YYYY-MM-DD) for the snapshot; omit for current."),
|
|
195
|
+
limit: z.number().int().min(1).max(10000).optional().describe("Max rows (default 1000, max 10000)."),
|
|
196
|
+
offset: z.number().int().min(0).optional().describe("Row offset for pagination."),
|
|
197
|
+
},
|
|
198
|
+
handler: (c, a) => call(() => c.request(`/companies/${encodeURIComponent(String(a.id))}/occupancy`, {
|
|
199
|
+
query: { date: a.date, limit: a.limit, offset: a.offset },
|
|
200
|
+
})),
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: "crehq_site_timeline",
|
|
204
|
+
description: "FLAGSHIP DIFFERENTIATOR — given a physical site (site_uid), return the full chronological tenancy history: every brand that has EVER occupied that address and when. Answers 'this was a Blockbuster, then a Sprint store, now a Chipotle.' Unmatched for backfill/teardown analysis, second-generation space, and landlord due diligence. No other location dataset reconstructs address-level succession like this.",
|
|
205
|
+
schema: { site_uid: z.string().describe("Stable CREHQ site_uid for the physical address.") },
|
|
206
|
+
handler: (c, a) => call(() => c.request(`/sites/${encodeURIComponent(a.site_uid)}/timeline`)),
|
|
207
|
+
},
|
|
208
|
+
// ========================================================================
|
|
209
|
+
// INTELLIGENCE (premium: Intel + Enterprise tiers)
|
|
210
|
+
// ========================================================================
|
|
211
|
+
{
|
|
212
|
+
name: "crehq_whitespace",
|
|
213
|
+
description: "PREMIUM INTELLIGENCE — whitespace analysis: postal codes/markets where a brand's competitors are present and performing but the brand itself is ABSENT. The ranked, data-driven shortlist of where a chain should expand next. Built on CREHQ's full multi-vertical, government-verified footprint. (Intel & Enterprise tiers.)",
|
|
214
|
+
schema: {
|
|
215
|
+
company_id: z.union([z.string(), z.number()]).describe("CREHQ company id to analyze."),
|
|
216
|
+
country: z.string().optional().describe("ISO country code (default 'US')."),
|
|
217
|
+
},
|
|
218
|
+
handler: (c, a) => call(() => c.request("/intelligence/whitespace", { query: { company_id: String(a.company_id), country: a.country ?? "US" } })),
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "crehq_co_tenancy",
|
|
222
|
+
description: "PREMIUM INTELLIGENCE — co-tenancy analysis: which brands most often co-locate within a given radius of this brand's stores (the chains that cluster together: e.g. who anchors near Chipotle). Drives site-selection, anchor-tenant matching, and trade-area benchmarking. (Intel & Enterprise tiers.)",
|
|
223
|
+
schema: {
|
|
224
|
+
company_id: z.union([z.string(), z.number()]).describe("CREHQ company id to analyze."),
|
|
225
|
+
radius_meters: z.number().int().min(10).max(5000).optional().describe("Co-location radius in meters (default 200)."),
|
|
226
|
+
},
|
|
227
|
+
handler: (c, a) => call(() => c.request("/intelligence/co-tenancy", { query: { company_id: String(a.company_id), radius_meters: a.radius_meters ?? 200 } })),
|
|
228
|
+
},
|
|
229
|
+
// ========================================================================
|
|
230
|
+
// DATASETS
|
|
231
|
+
// ========================================================================
|
|
232
|
+
{
|
|
233
|
+
name: "crehq_datasets_list",
|
|
234
|
+
description: "Browse CREHQ's catalog of packaged, ready-to-license datasets (whole-brand footprints, vertical rollups, FDD financials, etc.), filterable by category, country, and freshness. Each entry exposes row counts, schema, and refresh date — the menu of bulk data products.",
|
|
235
|
+
schema: {
|
|
236
|
+
category: z.string().optional().describe("Filter by category slug."),
|
|
237
|
+
country: z.string().optional().describe("ISO country code filter."),
|
|
238
|
+
freshness: z.string().optional().describe("Freshness filter (e.g. '30d', '90d')."),
|
|
239
|
+
per_page: perPage,
|
|
240
|
+
},
|
|
241
|
+
handler: (c, a) => call(() => c.request("/datasets", { query: { category: a.category, country: a.country, freshness: a.freshness, per_page: a.per_page } })),
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: "crehq_dataset_get",
|
|
245
|
+
description: "Get full metadata for one dataset by slug: row count, column schema, coverage, verification methodology, last-refresh date, and licensing notes — everything needed to evaluate it before download.",
|
|
246
|
+
schema: { slug: z.string().describe("Dataset slug (from crehq_datasets_list).") },
|
|
247
|
+
handler: (c, a) => call(() => c.request(`/datasets/${encodeURIComponent(a.slug)}`)),
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: "crehq_dataset_download",
|
|
251
|
+
description: "Download a licensed dataset by slug in your chosen format (CSV, JSON, GeoJSON, or XLSX). Requires a tier/contract that includes the dataset. Returns the raw payload (or a signed link) for direct ingestion.",
|
|
252
|
+
schema: {
|
|
253
|
+
slug: z.string().describe("Dataset slug."),
|
|
254
|
+
format: z.enum(["json", "csv", "geojson", "xlsx"]).optional().describe("Desired format (default json)."),
|
|
255
|
+
},
|
|
256
|
+
handler: (c, a) => {
|
|
257
|
+
const format = a.format ?? "json";
|
|
258
|
+
const acceptMap = {
|
|
259
|
+
json: "application/json",
|
|
260
|
+
csv: "text/csv",
|
|
261
|
+
geojson: "application/geo+json",
|
|
262
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
263
|
+
};
|
|
264
|
+
return call(() => c.request(`/datasets/${encodeURIComponent(a.slug)}/download`, { accept: acceptMap[format] }));
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: "crehq_dataset_categories",
|
|
269
|
+
description: "List all dataset categories with counts — a quick map of how CREHQ's data products are organized across verticals.",
|
|
270
|
+
schema: {},
|
|
271
|
+
handler: (c) => call(() => c.request("/datasets/categories")),
|
|
272
|
+
},
|
|
273
|
+
// ========================================================================
|
|
274
|
+
// TRENDS
|
|
275
|
+
// ========================================================================
|
|
276
|
+
{
|
|
277
|
+
name: "crehq_trends_company",
|
|
278
|
+
description: "Time-series trends for ONE brand: outlet-count history, fee/royalty trends, and FDD financial trajectory over time. The growth/health curve of a concept in a single call.",
|
|
279
|
+
schema: { id: z.union([z.string(), z.number()]).describe("CREHQ company id.") },
|
|
280
|
+
handler: (c, a) => call(() => c.request(`/trends/company/${encodeURIComponent(String(a.id))}`)),
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: "crehq_trends_geographic",
|
|
284
|
+
description: "Geographic trend analysis: metro/state concentration and opening/closing velocity across CREHQ's footprint. Surfaces which markets are heating up or cooling down across brands and verticals.",
|
|
285
|
+
schema: {
|
|
286
|
+
country: z.string().optional().describe("ISO country code (default 'US')."),
|
|
287
|
+
category: z.string().optional().describe("Optional vertical/category filter."),
|
|
288
|
+
state: z.string().optional().describe("Optional US state filter."),
|
|
289
|
+
},
|
|
290
|
+
handler: (c, a) => call(() => c.request("/trends/geographic", { query: { country: a.country, category: a.category, state: a.state } })),
|
|
291
|
+
},
|
|
292
|
+
];
|
|
293
|
+
/** Convert a Zod raw shape to the JSON Schema the MCP SDK advertises to clients. */
|
|
294
|
+
export function toJsonSchema(shape) {
|
|
295
|
+
const properties = {};
|
|
296
|
+
const required = [];
|
|
297
|
+
for (const [key, def] of Object.entries(shape)) {
|
|
298
|
+
properties[key] = zodToJson(def);
|
|
299
|
+
if (!def.isOptional())
|
|
300
|
+
required.push(key);
|
|
301
|
+
}
|
|
302
|
+
return { type: "object", properties, required };
|
|
303
|
+
}
|
|
304
|
+
/** Minimal Zod→JSON-Schema mapping covering the types used in this registry. */
|
|
305
|
+
function zodToJson(def) {
|
|
306
|
+
const description = def.description;
|
|
307
|
+
const base = (obj) => description ? { ...obj, description } : obj;
|
|
308
|
+
const inner = def;
|
|
309
|
+
const typeName = inner._def.typeName;
|
|
310
|
+
switch (typeName) {
|
|
311
|
+
case "ZodOptional":
|
|
312
|
+
case "ZodDefault":
|
|
313
|
+
return { ...zodToJson(inner._def.innerType), ...(description ? { description } : {}) };
|
|
314
|
+
case "ZodString":
|
|
315
|
+
return base({ type: "string" });
|
|
316
|
+
case "ZodNumber":
|
|
317
|
+
return base({ type: "number" });
|
|
318
|
+
case "ZodBoolean":
|
|
319
|
+
return base({ type: "boolean" });
|
|
320
|
+
case "ZodEnum":
|
|
321
|
+
return base({ type: "string", enum: inner._def.values });
|
|
322
|
+
case "ZodArray":
|
|
323
|
+
return base({ type: "array", items: zodToJson(inner._def.type) });
|
|
324
|
+
case "ZodUnion": {
|
|
325
|
+
// Used for id fields that accept string|number.
|
|
326
|
+
const opts = (inner._def.options ?? []).map((o) => zodToJson(o));
|
|
327
|
+
const types = Array.from(new Set(opts.map((o) => o.type).filter(Boolean)));
|
|
328
|
+
return base({ type: types.length === 1 ? types[0] : types });
|
|
329
|
+
}
|
|
330
|
+
case "ZodUnknown":
|
|
331
|
+
default:
|
|
332
|
+
return base({});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "crehq-mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Model Context Protocol server that wraps the CREHQ location-intelligence REST API as native AI-agent tools (franchise brands, store locations, FDD financials, site tenancy history, whitespace & co-tenancy).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"crehq-mcp-server": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"mcp",
|
|
12
|
+
"model-context-protocol",
|
|
13
|
+
"claude",
|
|
14
|
+
"location-data",
|
|
15
|
+
"franchise",
|
|
16
|
+
"fdd",
|
|
17
|
+
"retail",
|
|
18
|
+
"crehq"
|
|
19
|
+
],
|
|
20
|
+
"homepage": "https://crehq.com/developers/",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/groundroof/crehq-mcp-server.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/groundroof/crehq-mcp-server/issues"
|
|
27
|
+
},
|
|
28
|
+
"author": "CREHQ",
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"GO-LIVE.md",
|
|
33
|
+
"LICENSE",
|
|
34
|
+
"NOTICE",
|
|
35
|
+
".env.example"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"lint": "tsc --noEmit",
|
|
44
|
+
"start": "node dist/index.js",
|
|
45
|
+
"dev": "node --loader ts-node/esm src/index.ts",
|
|
46
|
+
"prepublishOnly": "npm run build"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
50
|
+
"zod": "^3.23.8"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^22.10.0",
|
|
54
|
+
"typescript": "^5.7.0"
|
|
55
|
+
}
|
|
56
|
+
}
|