@matchuplabs/nycfhv-mcp 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -1,18 +1,166 @@
1
1
  # @matchuplabs/nycfhv-mcp
2
2
 
3
- MCP server that exposes the [NYC FHV Intelligence API](https://fhv.matchup.dev) — TLC for-hire-vehicle license verification and renewal-window lookups — as Claude / OpenAI agent tools.
3
+ MCP server that exposes the [NYC FHV Intelligence API](https://fhv.matchup.dev) — real-time TLC for-hire-vehicle license verification and renewal-window lookups — as Claude, Cursor, Windsurf, and VS Code agent tools.
4
+
5
+ Data source: NYC TLC's [For-Hire Vehicles - Active dataset](https://data.cityofnewyork.us/Transportation/For-Hire-Vehicles-FHV-Active/8wbx-tsch), refreshed daily 4–7 PM ET. Public record under NY State FOIL.
6
+
7
+ ## Why this exists
8
+
9
+ Apps and AI agents that touch NYC for-hire transportation today can't verify a TLC license without scraping a manual portal. This MCP wraps a commercial-grade verification API so an agent can confirm "is this vehicle currently TLC-active?" as a single tool call before authorizing a livery rate, dispatching a Medicaid NEMT trip, or pre-binding insurance.
10
+
11
+ The API also correctly handles wheelchair-accessible (WAV) status — Socrata returns the WAV column as `"WAV"` / `"PILOT"` / absent (not `"YES"` / `"NO"`), and most wrappers silently misclassify the ~8,400-vehicle WAV cohort. This one doesn't.
4
12
 
5
13
  ## Tools
6
14
 
7
15
  | Tool | What it does | Cost |
8
16
  |---|---|---|
9
- | `verify_tlc_vehicle` | Verify an FHV by TLC license, DMV plate, or VIN. Returns active status, expiration, base, WAV flag. | 1 credit |
10
- | `list_upcoming_renewals` | Vehicles whose license expires in the next N days. Filters: base, WAV, max model year. | 5 credits / page |
17
+ | `verify_tlc_vehicle` | Verify an FHV by TLC license, DMV plate, or VIN. | 1 credit |
18
+ | `list_upcoming_renewals` | Vehicles whose license expires in the next N days. | 5 credits / page |
19
+ | `subscribe_renewal_webhook` | Daily push notifications for renewal-window deltas. | 0 to create, 1 / event |
20
+
21
+ ### `verify_tlc_vehicle`
22
+
23
+ Verify whether a NYC TLC for-hire vehicle is currently licensed. Read-only.
24
+
25
+ **Inputs**
26
+
27
+ | Param | Type | Required | Description |
28
+ |---|---|---|---|
29
+ | `identifier` | string | yes | TLC license (`C05015` or `5545596`), DMV plate (`T438350C`), or 17-char VIN. |
30
+ | `type` | `"auto" \| "license" \| "plate" \| "vin"` | no | Override auto-detection. Default `"auto"`. |
31
+
32
+ **Auto-detection rules**
33
+
34
+ - 17 alphanumeric chars → VIN
35
+ - One letter + five digits (e.g., `C05015`) → TLC license number
36
+ - Otherwise → DMV plate
37
+
38
+ **Example response**
39
+
40
+ ```json
41
+ {
42
+ "data": {
43
+ "active": true,
44
+ "vehicle_license_number": "5545596",
45
+ "name": "OPERATOR NAME",
46
+ "license_type": "FHV",
47
+ "expiration_date": "2026-08-14",
48
+ "days_until_expiration": 95,
49
+ "permit_license_number": "B03404",
50
+ "dmv_license_plate_number": "T438350C",
51
+ "vehicle_vin_number": "1FADP3K20JL215555",
52
+ "wheelchair_accessible": false,
53
+ "vehicle_year": 2018,
54
+ "base_number": "B03404",
55
+ "base_name": "UBER USA, LLC",
56
+ "base_type": "Black Car"
57
+ },
58
+ "meta": {
59
+ "source": "nyc-open-data/tlc-fhv-active/8wbx-tsch",
60
+ "updated": "2026-05-11T22:14:00Z",
61
+ "credits_remaining": 4998
62
+ }
63
+ }
64
+ ```
65
+
66
+ **Error shape** (any tool)
67
+
68
+ ```json
69
+ {
70
+ "error": {
71
+ "code": "not_found",
72
+ "message": "No active TLC record matches that identifier.",
73
+ "suggested_next_step": "Confirm the identifier with the operator or try a different type."
74
+ }
75
+ }
76
+ ```
77
+
78
+ ### `list_upcoming_renewals`
11
79
 
12
- ## Quickstart
80
+ List vehicles whose license expires within the next N days. Read-only. Paginated.
81
+
82
+ **Inputs**
83
+
84
+ | Param | Type | Required | Description |
85
+ |---|---|---|---|
86
+ | `days` | integer (1–180) | yes | Renewal window from today, inclusive. |
87
+ | `base_number` | string | no | TLC base filter (e.g., `B03404` = Uber). |
88
+ | `wheelchair_accessible` | boolean | no | `true` = WAV only, `false` = non-WAV only, omit for both. |
89
+ | `vehicle_year_max` | integer | no | Max model year — fleet-aging targeting. |
90
+ | `page` | integer | no | 1-indexed. Default 1. |
91
+ | `page_size` | integer (10–100) | no | Default 50. |
92
+
93
+ **Example response**
94
+
95
+ ```json
96
+ {
97
+ "data": [
98
+ {
99
+ "active": true,
100
+ "vehicle_license_number": "5545596",
101
+ "name": "OPERATOR NAME",
102
+ "expiration_date": "2026-06-01",
103
+ "days_until_expiration": 21,
104
+ "wheelchair_accessible": true,
105
+ "vehicle_year": 2017,
106
+ "base_number": "B03404",
107
+ "base_name": "UBER USA, LLC"
108
+ }
109
+ ],
110
+ "meta": {
111
+ "source": "nyc-open-data/tlc-fhv-active/8wbx-tsch",
112
+ "updated": "2026-05-11T22:14:00Z",
113
+ "page": 1,
114
+ "page_size": 50,
115
+ "total_count": 412,
116
+ "credits_remaining": 4993
117
+ }
118
+ }
119
+ ```
120
+
121
+ ### `subscribe_renewal_webhook`
122
+
123
+ Subscribe a webhook URL to daily renewal-window deltas. Each day after the TLC dataset refresh, the API diffs newly-entered renewal-window vehicles against yesterday's snapshot and POSTs the delta. Events are HMAC-SHA256-signed via the `signing_secret` returned at creation.
124
+
125
+ **Inputs**
126
+
127
+ | Param | Type | Required | Description |
128
+ |---|---|---|---|
129
+ | `webhook_url` | string (HTTPS) | yes | Endpoint to receive daily POST events. |
130
+ | `days` | integer (1–180) | no | Renewal window. Default 30. |
131
+ | `base_number` | string | no | Filter to one TLC base. |
132
+ | `wheelchair_accessible` | boolean | no | WAV-only or non-WAV-only filter. |
133
+ | `vehicle_year_max` | integer | no | Max model year filter. |
134
+ | `description` | string (≤200) | no | Human-readable label. |
135
+
136
+ **Example response**
137
+
138
+ ```json
139
+ {
140
+ "data": {
141
+ "id": "sub_01HXYZ...",
142
+ "status": "active",
143
+ "signing_secret": "whsec_...",
144
+ "webhook_url": "https://example.com/fhv-webhook",
145
+ "filter": { "days": 30, "base_number": "B03404" },
146
+ "created_at": "2026-05-11T22:14:00Z"
147
+ },
148
+ "meta": {
149
+ "source": "matchuplabs/fhv-intelligence/subscriptions"
150
+ },
151
+ "important": "Store the signing_secret securely — it is only returned once. Use it to verify the X-FHV-Signature header on incoming webhook events."
152
+ }
153
+ ```
154
+
155
+ ## Install
13
156
 
14
157
  1. Get an API key at [matchuplabs.com](https://matchuplabs.com) (product: `fhv-intelligence`).
15
- 2. Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
158
+ 2. Add the config block below to your MCP host.
159
+ 3. Restart the host. The three tools appear in the tool picker.
160
+
161
+ ### Claude Desktop
162
+
163
+ Config path: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows).
16
164
 
17
165
  ```json
18
166
  {
@@ -28,20 +176,75 @@ MCP server that exposes the [NYC FHV Intelligence API](https://fhv.matchup.dev)
28
176
  }
29
177
  ```
30
178
 
31
- 3. Restart Claude Desktop. The two tools appear in the tool picker.
179
+ ### Cursor
32
180
 
33
- ## Environment
181
+ Config path: `~/.cursor/mcp.json` (global) or `<project>/.cursor/mcp.json` (per-project).
34
182
 
35
- | Variable | Required | Default |
36
- |---|---|---|
37
- | `FHV_API_KEY` | Yes | — |
38
- | `FHV_BASE_URL` | No | `https://fhv.matchup.dev` |
183
+ ```json
184
+ {
185
+ "mcpServers": {
186
+ "nycfhv": {
187
+ "command": "npx",
188
+ "args": ["-y", "@matchuplabs/nycfhv-mcp"],
189
+ "env": {
190
+ "FHV_API_KEY": "mv_live_your_key_here"
191
+ }
192
+ }
193
+ }
194
+ }
195
+ ```
39
196
 
40
- ## Why this exists
197
+ ### VS Code (with Copilot MCP)
41
198
 
42
- Apps and AI agents that touch NYC for-hire transportation today can't verify a TLC license without scraping a manual portal. This MCP wraps a commercial-grade verification API so an agent can confirm "is this vehicle currently TLC-active?" as a single tool call before authorizing a livery rate, dispatching a Medicaid NEMT trip, or pre-binding insurance.
199
+ Config path: `<project>/.vscode/mcp.json`.
200
+
201
+ ```json
202
+ {
203
+ "servers": {
204
+ "nycfhv": {
205
+ "type": "stdio",
206
+ "command": "npx",
207
+ "args": ["-y", "@matchuplabs/nycfhv-mcp"],
208
+ "env": {
209
+ "FHV_API_KEY": "mv_live_your_key_here"
210
+ }
211
+ }
212
+ }
213
+ }
214
+ ```
43
215
 
44
- Data source: NYC TLC's daily-refreshed [For-Hire Vehicles - Active dataset](https://data.cityofnewyork.us/Transportation/For-Hire-Vehicles-FHV-Active/8wbx-tsch). Public record under NY State FOIL.
216
+ ### Windsurf
217
+
218
+ Config path: `~/.codeium/windsurf/mcp_config.json`.
219
+
220
+ ```json
221
+ {
222
+ "mcpServers": {
223
+ "nycfhv": {
224
+ "command": "npx",
225
+ "args": ["-y", "@matchuplabs/nycfhv-mcp"],
226
+ "env": {
227
+ "FHV_API_KEY": "mv_live_your_key_here"
228
+ }
229
+ }
230
+ }
231
+ }
232
+ ```
233
+
234
+ ### Claude Code (CLI)
235
+
236
+ ```bash
237
+ claude mcp add nycfhv -- npx -y @matchuplabs/nycfhv-mcp
238
+ ```
239
+
240
+ Then set `FHV_API_KEY` in your shell or via `claude mcp` env config.
241
+
242
+ ## Environment
243
+
244
+ | Variable | Required | Default |
245
+ |---|---|---|
246
+ | `FHV_API_KEY` | yes | — |
247
+ | `FHV_BASE_URL` | no | `https://fhv.matchup.dev` |
45
248
 
46
249
  ## Development
47
250
 
@@ -52,6 +255,12 @@ npm run build
52
255
  npm test
53
256
  ```
54
257
 
258
+ ## Links
259
+
260
+ - [API docs](https://fhv.matchup.dev)
261
+ - [Source (api-ventures monorepo)](https://github.com/MATCHUP-LABS/api-ventures/tree/main/products/nyc-fhv-intelligence-api/mcp-server)
262
+ - [Report issues](https://github.com/MATCHUP-LABS/api-ventures/issues)
263
+
55
264
  ## License
56
265
 
57
266
  MIT. © Matchup Labs.
@@ -52,4 +52,25 @@ export declare class FhvClient {
52
52
  page?: number;
53
53
  page_size?: number;
54
54
  }): Promise<FhvResult<Vehicle[]>>;
55
+ /** POST /api/v1/renewals/subscriptions */
56
+ createSubscription(params: {
57
+ webhook_url: string;
58
+ filter: {
59
+ days?: number;
60
+ base_number?: string;
61
+ wheelchair_accessible?: boolean;
62
+ vehicle_year_max?: number;
63
+ };
64
+ description?: string;
65
+ }): Promise<FhvResult<Subscription>>;
66
+ }
67
+ export interface Subscription {
68
+ id: string;
69
+ webhook_url: string;
70
+ filter: Record<string, unknown>;
71
+ description?: string;
72
+ signing_secret: string;
73
+ status: "active" | "paused" | "failed";
74
+ created_at: string;
75
+ last_delivery_at: string | null;
55
76
  }
@@ -53,22 +53,28 @@ export class FhvClient {
53
53
  return lastResponse;
54
54
  throw lastError ?? new Error("retryFetch exhausted retries");
55
55
  }
56
- async request(path, params = {}) {
56
+ async request(path, params = {}, options) {
57
+ const method = options?.method ?? "GET";
57
58
  const url = new URL(path, this.baseUrl);
58
- for (const [k, v] of Object.entries(params)) {
59
- if (v !== undefined)
60
- url.searchParams.set(k, String(v));
59
+ if (method === "GET") {
60
+ for (const [k, v] of Object.entries(params)) {
61
+ if (v !== undefined)
62
+ url.searchParams.set(k, String(v));
63
+ }
64
+ }
65
+ const headers = {
66
+ Authorization: `Bearer ${this.apiKey}`,
67
+ Accept: "application/json",
68
+ "User-Agent": "nycfhv-mcp/0.1.0",
69
+ };
70
+ const init = { method, headers };
71
+ if (options?.jsonBody !== undefined) {
72
+ headers["Content-Type"] = "application/json";
73
+ init.body = JSON.stringify(options.jsonBody);
61
74
  }
62
75
  let response;
63
76
  try {
64
- response = await this.retryFetch(url.toString(), {
65
- method: "GET",
66
- headers: {
67
- Authorization: `Bearer ${this.apiKey}`,
68
- Accept: "application/json",
69
- "User-Agent": "nycfhv-mcp/0.1.0",
70
- },
71
- });
77
+ response = await this.retryFetch(url.toString(), init);
72
78
  }
73
79
  catch (err) {
74
80
  return mcpError("upstream_timeout", `Request to fhv.matchup.dev failed: ${err instanceof Error ? err.message : String(err)}`, "Check network connectivity and retry.");
@@ -94,4 +100,11 @@ export class FhvClient {
94
100
  listUpcomingRenewals(params) {
95
101
  return this.request(`/api/v1/renewals/upcoming`, params);
96
102
  }
103
+ /** POST /api/v1/renewals/subscriptions */
104
+ createSubscription(params) {
105
+ return this.request(`/api/v1/renewals/subscriptions`, {}, {
106
+ method: "POST",
107
+ jsonBody: params,
108
+ });
109
+ }
97
110
  }
@@ -2,10 +2,11 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
2
  import { ListToolsRequestSchema, CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
3
3
  import { TOOL_DEFINITION as verifyDef, TOOL_NAME as verifyName, verifyTlcVehicle, } from "./tools/verify-tlc-vehicle.js";
4
4
  import { TOOL_DEFINITION as renewalsDef, TOOL_NAME as renewalsName, listUpcomingRenewals, } from "./tools/list-upcoming-renewals.js";
5
+ import { TOOL_DEFINITION as subscribeDef, TOOL_NAME as subscribeName, subscribeRenewalWebhook, } from "./tools/subscribe-renewal-webhook.js";
5
6
  export function createServer() {
6
- const server = new Server({ name: "nycfhv", version: "0.1.0" }, { capabilities: { tools: {} } });
7
+ const server = new Server({ name: "nycfhv", version: "0.1.1" }, { capabilities: { tools: {} } });
7
8
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
8
- tools: [verifyDef, renewalsDef],
9
+ tools: [verifyDef, renewalsDef, subscribeDef],
9
10
  }));
10
11
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
11
12
  const { name, arguments: args } = request.params;
@@ -19,6 +20,10 @@ export function createServer() {
19
20
  const text = await listUpcomingRenewals((args ?? {}));
20
21
  return { content: [{ type: "text", text }] };
21
22
  }
23
+ case subscribeName: {
24
+ const text = await subscribeRenewalWebhook((args ?? {}));
25
+ return { content: [{ type: "text", text }] };
26
+ }
22
27
  default:
23
28
  return {
24
29
  content: [
@@ -2,6 +2,13 @@ export declare const TOOL_NAME = "list_upcoming_renewals";
2
2
  export declare const TOOL_DEFINITION: {
3
3
  name: string;
4
4
  description: string;
5
+ annotations: {
6
+ title: string;
7
+ readOnlyHint: boolean;
8
+ destructiveHint: boolean;
9
+ idempotentHint: boolean;
10
+ openWorldHint: boolean;
11
+ };
5
12
  inputSchema: {
6
13
  type: "object";
7
14
  properties: {
@@ -3,6 +3,13 @@ export const TOOL_NAME = "list_upcoming_renewals";
3
3
  export const TOOL_DEFINITION = {
4
4
  name: TOOL_NAME,
5
5
  description: "List NYC TLC for-hire vehicles whose license expires within the next N days. Optional filters: TLC base (e.g., 'B03404' for Uber), wheelchair-accessible (WAV) only, and maximum vehicle model year (for fleet-aging targeting). Paginated, 50 results per page by default. Costs 5 credits per page. Use this for renewal-window lead lists (license expediters, insurance brokers, training schools, EV/hybrid dealer prospecting).",
6
+ annotations: {
7
+ title: "List Upcoming TLC Renewals",
8
+ readOnlyHint: true,
9
+ destructiveHint: false,
10
+ idempotentHint: true,
11
+ openWorldHint: true,
12
+ },
6
13
  inputSchema: {
7
14
  type: "object",
8
15
  properties: {
@@ -0,0 +1,53 @@
1
+ export declare const TOOL_NAME = "subscribe_renewal_webhook";
2
+ export declare const TOOL_DEFINITION: {
3
+ name: string;
4
+ description: string;
5
+ annotations: {
6
+ title: string;
7
+ readOnlyHint: boolean;
8
+ destructiveHint: boolean;
9
+ idempotentHint: boolean;
10
+ openWorldHint: boolean;
11
+ };
12
+ inputSchema: {
13
+ type: "object";
14
+ properties: {
15
+ webhook_url: {
16
+ type: string;
17
+ description: string;
18
+ };
19
+ days: {
20
+ type: string;
21
+ description: string;
22
+ minimum: number;
23
+ maximum: number;
24
+ };
25
+ base_number: {
26
+ type: string;
27
+ description: string;
28
+ };
29
+ wheelchair_accessible: {
30
+ type: string;
31
+ description: string;
32
+ };
33
+ vehicle_year_max: {
34
+ type: string;
35
+ description: string;
36
+ };
37
+ description: {
38
+ type: string;
39
+ description: string;
40
+ };
41
+ };
42
+ required: string[];
43
+ };
44
+ };
45
+ export interface SubscribeRenewalWebhookArgs {
46
+ webhook_url: string;
47
+ days?: number;
48
+ base_number?: string;
49
+ wheelchair_accessible?: boolean;
50
+ vehicle_year_max?: number;
51
+ description?: string;
52
+ }
53
+ export declare function subscribeRenewalWebhook(args: SubscribeRenewalWebhookArgs): Promise<string>;
@@ -0,0 +1,84 @@
1
+ import { FhvClient } from "../lib/fhv-client.js";
2
+ export const TOOL_NAME = "subscribe_renewal_webhook";
3
+ export const TOOL_DEFINITION = {
4
+ name: TOOL_NAME,
5
+ description: "Subscribe a webhook URL to daily FHV renewal-window notifications. Each day after the TLC dataset refresh (~4-7 PM ET), the API diffs newly-entered renewal-window vehicles against yesterday's snapshot and POSTs the delta to your webhook. Payload is signed with HMAC-SHA256 via the signing_secret returned at creation. Costs 0 credits to create; 1 credit per delivered event.",
6
+ annotations: {
7
+ title: "Subscribe to Renewal Webhook",
8
+ readOnlyHint: false,
9
+ destructiveHint: false,
10
+ idempotentHint: false,
11
+ openWorldHint: true,
12
+ },
13
+ inputSchema: {
14
+ type: "object",
15
+ properties: {
16
+ webhook_url: {
17
+ type: "string",
18
+ description: "HTTPS endpoint that will receive daily POST events with renewal-window vehicle deltas.",
19
+ },
20
+ days: {
21
+ type: "integer",
22
+ description: "Renewal window from today, 1-180 days. Vehicles entering this window trigger an event. Default 30.",
23
+ minimum: 1,
24
+ maximum: 180,
25
+ },
26
+ base_number: {
27
+ type: "string",
28
+ description: "Optional TLC base filter (e.g., 'B03404' = Uber). Only vehicles from this base trigger events.",
29
+ },
30
+ wheelchair_accessible: {
31
+ type: "boolean",
32
+ description: "If set, only WAV (true) or non-WAV (false) vehicles trigger events.",
33
+ },
34
+ vehicle_year_max: {
35
+ type: "integer",
36
+ description: "Optional max model year filter for fleet-aging targeting.",
37
+ },
38
+ description: {
39
+ type: "string",
40
+ description: "Optional human-readable label for this subscription (max 200 chars).",
41
+ },
42
+ },
43
+ required: ["webhook_url"],
44
+ },
45
+ };
46
+ export async function subscribeRenewalWebhook(args) {
47
+ if (!args.webhook_url || typeof args.webhook_url !== "string") {
48
+ return JSON.stringify({
49
+ error: {
50
+ code: "invalid_input",
51
+ message: "`webhook_url` is required and must be an HTTPS URL.",
52
+ suggested_next_step: "Provide a valid HTTPS webhook endpoint.",
53
+ },
54
+ });
55
+ }
56
+ const client = new FhvClient();
57
+ const result = await client.createSubscription({
58
+ webhook_url: args.webhook_url,
59
+ filter: {
60
+ days: args.days,
61
+ base_number: args.base_number,
62
+ wheelchair_accessible: args.wheelchair_accessible,
63
+ vehicle_year_max: args.vehicle_year_max,
64
+ },
65
+ description: args.description,
66
+ });
67
+ if ("error" in result) {
68
+ return JSON.stringify(result);
69
+ }
70
+ return JSON.stringify({
71
+ data: {
72
+ id: result.data.id,
73
+ status: result.data.status,
74
+ signing_secret: result.data.signing_secret,
75
+ webhook_url: result.data.webhook_url,
76
+ filter: result.data.filter,
77
+ created_at: result.data.created_at,
78
+ },
79
+ meta: {
80
+ source: result.meta.source,
81
+ },
82
+ important: "Store the signing_secret securely — it is only returned once. Use it to verify the X-FHV-Signature header on incoming webhook events.",
83
+ });
84
+ }
@@ -2,6 +2,13 @@ export declare const TOOL_NAME = "verify_tlc_vehicle";
2
2
  export declare const TOOL_DEFINITION: {
3
3
  name: string;
4
4
  description: string;
5
+ annotations: {
6
+ title: string;
7
+ readOnlyHint: boolean;
8
+ destructiveHint: boolean;
9
+ idempotentHint: boolean;
10
+ openWorldHint: boolean;
11
+ };
5
12
  inputSchema: {
6
13
  type: "object";
7
14
  properties: {
@@ -3,6 +3,13 @@ export const TOOL_NAME = "verify_tlc_vehicle";
3
3
  export const TOOL_DEFINITION = {
4
4
  name: TOOL_NAME,
5
5
  description: "Verify whether a NYC TLC for-hire vehicle is currently licensed. Accepts a TLC license number (e.g. 'C05015' or '5545596'), DMV plate (e.g. 'T438350C'), or 17-character VIN. Returns active status, expiration date, base affiliation, wheelchair-accessible (WAV) flag, and days_until_expiration. Identifier type is auto-detected by default. Costs 1 credit per call. Use this before authorizing a livery rate, dispatch, or pre-bind insurance check on a NYC for-hire vehicle.",
6
+ annotations: {
7
+ title: "Verify TLC Vehicle",
8
+ readOnlyHint: true,
9
+ destructiveHint: false,
10
+ idempotentHint: true,
11
+ openWorldHint: true,
12
+ },
6
13
  inputSchema: {
7
14
  type: "object",
8
15
  properties: {
package/llms-full.txt ADDED
@@ -0,0 +1,264 @@
1
+ # @matchuplabs/nycfhv-mcp — Full Reference for LLMs
2
+
3
+ This is a single-file dump of everything an LLM needs to plan tool use against the NYC FHV Intelligence MCP server. Includes per-tool input schemas, response shapes, error semantics, and the cost model.
4
+
5
+ Package: `@matchuplabs/nycfhv-mcp` (npm)
6
+ Server name: `io.github.MatchupLabs/nycfhv`
7
+ Transport: stdio
8
+ Source: https://github.com/MATCHUP-LABS/api-ventures/tree/main/products/nyc-fhv-intelligence-api/mcp-server
9
+
10
+ ## Environment variables
11
+
12
+ - `FHV_API_KEY` (required, secret) — get one at matchuplabs.com, product slug `fhv-intelligence`.
13
+ - `FHV_BASE_URL` (optional) — defaults to `https://fhv.matchup.dev`.
14
+
15
+ ## Cost model
16
+
17
+ | Operation | Credits |
18
+ |---|---|
19
+ | `verify_tlc_vehicle` call | 1 |
20
+ | `list_upcoming_renewals` page | 5 |
21
+ | `subscribe_renewal_webhook` create | 0 |
22
+ | Webhook event delivery | 1 |
23
+ | Edge-cached repeat call within TTL | 0 |
24
+
25
+ Credits are charged only on successful responses. The remaining balance is in `meta.credits_remaining` on every response.
26
+
27
+ ## Identifier auto-detection (verify_tlc_vehicle)
28
+
29
+ - 17 alphanumeric characters → VIN
30
+ - One letter followed by five digits (e.g., `C05015`) → TLC license number (legacy format)
31
+ - 7-digit numeric (e.g., `5545596`) → TLC license number (current format)
32
+ - Otherwise → DMV plate
33
+
34
+ Override via `type: "license" | "plate" | "vin"`.
35
+
36
+ ## Tools
37
+
38
+ ### Tool 1: verify_tlc_vehicle
39
+
40
+ Annotations: `readOnlyHint: true`, `destructiveHint: false`, `idempotentHint: true`, `openWorldHint: true`.
41
+
42
+ Description: Verify whether a NYC TLC for-hire vehicle is currently licensed. Use before authorizing a livery rate, dispatching a Medicaid NEMT trip, or pre-binding insurance.
43
+
44
+ Input schema:
45
+
46
+ ```json
47
+ {
48
+ "type": "object",
49
+ "properties": {
50
+ "identifier": {
51
+ "type": "string",
52
+ "description": "TLC license number (legacy 'C05015' or 7-digit '5545596'), DMV plate, or 17-char VIN."
53
+ },
54
+ "type": {
55
+ "type": "string",
56
+ "enum": ["auto", "license", "plate", "vin"],
57
+ "description": "Optional override for identifier auto-detection. Default 'auto'."
58
+ }
59
+ },
60
+ "required": ["identifier"]
61
+ }
62
+ ```
63
+
64
+ Success response shape:
65
+
66
+ ```json
67
+ {
68
+ "data": {
69
+ "active": "boolean",
70
+ "vehicle_license_number": "string",
71
+ "name": "string (operator name)",
72
+ "license_type": "string",
73
+ "expiration_date": "string (YYYY-MM-DD)",
74
+ "days_until_expiration": "number (can be negative if expired)",
75
+ "permit_license_number": "string | null",
76
+ "dmv_license_plate_number": "string | null",
77
+ "vehicle_vin_number": "string | null",
78
+ "wheelchair_accessible": "boolean",
79
+ "vehicle_year": "number | null",
80
+ "base_number": "string | null (TLC base, e.g. 'B03404')",
81
+ "base_name": "string | null (e.g. 'UBER USA, LLC')",
82
+ "base_type": "string | null (e.g. 'Black Car', 'Livery', 'Luxury Limousine')"
83
+ },
84
+ "meta": {
85
+ "source": "nyc-open-data/tlc-fhv-active/8wbx-tsch",
86
+ "updated": "ISO 8601 timestamp",
87
+ "credits_remaining": "number"
88
+ }
89
+ }
90
+ ```
91
+
92
+ ### Tool 2: list_upcoming_renewals
93
+
94
+ Annotations: `readOnlyHint: true`, `destructiveHint: false`, `idempotentHint: true`, `openWorldHint: true`.
95
+
96
+ Description: List vehicles whose license expires within the next N days. Paginated. Use for renewal-window lead lists (license expediters, insurance brokers, training schools, EV/hybrid dealer prospecting).
97
+
98
+ Input schema:
99
+
100
+ ```json
101
+ {
102
+ "type": "object",
103
+ "properties": {
104
+ "days": {
105
+ "type": "integer",
106
+ "minimum": 1,
107
+ "maximum": 180,
108
+ "description": "Renewal window from today, inclusive. Most use cases pick 14, 30, 60, or 90."
109
+ },
110
+ "base_number": {
111
+ "type": "string",
112
+ "description": "Optional TLC base filter (case-insensitive). Example: 'B03404' = Uber, 'B00477' = Inta-Boro Acres."
113
+ },
114
+ "wheelchair_accessible": {
115
+ "type": "boolean",
116
+ "description": "true = WAV / WAV-pilot only (~8% of fleet). false = non-WAV only. Omit for both."
117
+ },
118
+ "vehicle_year_max": {
119
+ "type": "integer",
120
+ "description": "Optional max model year. Targets older fleet for replacement-financing or pre-sales lead-gen."
121
+ },
122
+ "page": {
123
+ "type": "integer",
124
+ "minimum": 1,
125
+ "description": "1-indexed page number. Default 1."
126
+ },
127
+ "page_size": {
128
+ "type": "integer",
129
+ "minimum": 10,
130
+ "maximum": 100,
131
+ "description": "Results per page. Default 50."
132
+ }
133
+ },
134
+ "required": ["days"]
135
+ }
136
+ ```
137
+
138
+ Success response shape:
139
+
140
+ ```json
141
+ {
142
+ "data": "Array<Vehicle> (same shape as verify_tlc_vehicle.data, one per vehicle)",
143
+ "meta": {
144
+ "source": "nyc-open-data/tlc-fhv-active/8wbx-tsch",
145
+ "updated": "ISO 8601 timestamp",
146
+ "page": "number",
147
+ "page_size": "number",
148
+ "total_count": "number (total matching the filter, not just this page)",
149
+ "credits_remaining": "number"
150
+ }
151
+ }
152
+ ```
153
+
154
+ ### Tool 3: subscribe_renewal_webhook
155
+
156
+ Annotations: `readOnlyHint: false`, `destructiveHint: false`, `idempotentHint: false`, `openWorldHint: true`.
157
+
158
+ Description: Subscribe a webhook URL to daily FHV renewal-window notifications. After each daily TLC dataset refresh (~4–7 PM ET), the API diffs newly-entered renewal-window vehicles against yesterday's snapshot and POSTs the delta to the webhook. Events are HMAC-SHA256 signed via the `signing_secret` returned at creation.
159
+
160
+ Input schema:
161
+
162
+ ```json
163
+ {
164
+ "type": "object",
165
+ "properties": {
166
+ "webhook_url": {
167
+ "type": "string",
168
+ "description": "HTTPS endpoint that will receive daily POST events."
169
+ },
170
+ "days": {
171
+ "type": "integer",
172
+ "minimum": 1,
173
+ "maximum": 180,
174
+ "description": "Renewal window from today. Default 30."
175
+ },
176
+ "base_number": {
177
+ "type": "string",
178
+ "description": "Optional TLC base filter."
179
+ },
180
+ "wheelchair_accessible": {
181
+ "type": "boolean",
182
+ "description": "WAV-only (true) or non-WAV-only (false) filter."
183
+ },
184
+ "vehicle_year_max": {
185
+ "type": "integer",
186
+ "description": "Max model year filter."
187
+ },
188
+ "description": {
189
+ "type": "string",
190
+ "description": "Optional human-readable label, max 200 chars."
191
+ }
192
+ },
193
+ "required": ["webhook_url"]
194
+ }
195
+ ```
196
+
197
+ Success response shape:
198
+
199
+ ```json
200
+ {
201
+ "data": {
202
+ "id": "string (subscription id)",
203
+ "status": "active | paused | failed",
204
+ "signing_secret": "string (ONLY RETURNED ONCE — store securely)",
205
+ "webhook_url": "string",
206
+ "filter": "object (echo of input filter params)",
207
+ "created_at": "ISO 8601 timestamp"
208
+ },
209
+ "meta": {
210
+ "source": "matchuplabs/fhv-intelligence/subscriptions"
211
+ },
212
+ "important": "Store the signing_secret securely — it is only returned once. Use it to verify the X-FHV-Signature header on incoming webhook events."
213
+ }
214
+ ```
215
+
216
+ Webhook event payload (delivered daily to the registered URL):
217
+
218
+ ```json
219
+ {
220
+ "event": "renewal_window.delta",
221
+ "subscription_id": "sub_...",
222
+ "delivered_at": "ISO 8601",
223
+ "data": {
224
+ "added": "Array<Vehicle>",
225
+ "removed": "Array<Vehicle>"
226
+ },
227
+ "meta": {
228
+ "source": "nyc-open-data/tlc-fhv-active/8wbx-tsch",
229
+ "snapshot_date": "YYYY-MM-DD"
230
+ }
231
+ }
232
+ ```
233
+
234
+ Signature header: `X-FHV-Signature: sha256=<hex digest of body using signing_secret>`.
235
+
236
+ ## Error semantics (all tools)
237
+
238
+ Every error follows this shape:
239
+
240
+ ```json
241
+ {
242
+ "error": {
243
+ "code": "string (machine-readable: 'invalid_input' | 'not_found' | 'auth_failed' | 'rate_limited' | 'credits_exhausted' | 'upstream_unavailable' | 'internal_error')",
244
+ "message": "string (human-readable)",
245
+ "suggested_next_step": "string (actionable guidance for the agent)"
246
+ }
247
+ }
248
+ ```
249
+
250
+ Common codes:
251
+
252
+ - `invalid_input` — bad/missing param. Re-check input schema.
253
+ - `not_found` — verify_tlc_vehicle: no matching active record. Vehicle may be inactive, mistyped, or never licensed.
254
+ - `auth_failed` — bad/missing FHV_API_KEY.
255
+ - `rate_limited` — too many requests for the current tier. Back off.
256
+ - `credits_exhausted` — out of credits. Upgrade tier or wait for monthly reset.
257
+ - `upstream_unavailable` — NYC Socrata is down or refreshing. Retry in 5–10 min.
258
+
259
+ ## Use-case primers
260
+
261
+ - **NEMT Medicaid trip dispatch**: before assigning a trip, call `verify_tlc_vehicle` with the assigned vehicle's plate or TLC number. Confirm `active: true` and (for WAV-required trips) `wheelchair_accessible: true`. If false, route the trip to a different vehicle.
262
+ - **Livery insurance pre-bind**: call `verify_tlc_vehicle`; deny binding if `active: false` or `days_until_expiration < 7`.
263
+ - **Renewal-window lead-gen**: call `list_upcoming_renewals` with `days: 30`, optionally filtered by base or `vehicle_year_max` (for replacement-financing pitches). Paginate.
264
+ - **Fleet monitoring**: call `subscribe_renewal_webhook` once with your base filter; receive a daily delta of vehicles entering the renewal window without polling.
package/llms.txt ADDED
@@ -0,0 +1,25 @@
1
+ # @matchuplabs/nycfhv-mcp
2
+
3
+ > MCP server for the NYC FHV Intelligence API. Verifies NYC TLC for-hire vehicle licenses (by TLC number, DMV plate, or VIN) and lists renewal-window leads. Backed by fhv.matchup.dev, daily-refreshed from NYC TLC's official For-Hire Vehicles - Active dataset (8wbx-tsch). Public-record data under NY State FOIL.
4
+
5
+ The MCP exposes 3 tools:
6
+
7
+ - `verify_tlc_vehicle` — read-only, 1 credit. Verify whether a vehicle is currently TLC-active by license, plate, or VIN. Returns active status, expiration, base affiliation, and wheelchair-accessible (WAV) flag.
8
+ - `list_upcoming_renewals` — read-only, 5 credits/page. Paginated list of vehicles whose license expires in the next N days. Filterable by base, WAV status, and vehicle model year.
9
+ - `subscribe_renewal_webhook` — write, 0 credits to create + 1 credit per delivered event. Subscribes a webhook URL to daily renewal-window deltas, HMAC-SHA256 signed.
10
+
11
+ Use cases: NEMT fleet pre-dispatch verification, livery dispatch authorization, insurance pre-bind, fleet-aging targeting for dealer/financing lead-gen, license-expediter prospecting.
12
+
13
+ ## Install
14
+
15
+ - [README](https://github.com/MATCHUP-LABS/api-ventures/blob/main/products/nyc-fhv-intelligence-api/mcp-server/README.md): full install configs for Claude Desktop, Cursor, VS Code, Windsurf, Claude Code CLI.
16
+ - [Full schemas](https://github.com/MATCHUP-LABS/api-ventures/blob/main/products/nyc-fhv-intelligence-api/mcp-server/llms-full.txt): single-file dump of every tool definition + response shape.
17
+
18
+ ## Get an API key
19
+
20
+ - [matchuplabs.com](https://matchuplabs.com) — product slug `fhv-intelligence`. Free tier available.
21
+
22
+ ## Optional
23
+
24
+ - [API docs](https://fhv.matchup.dev): underlying HTTP API the MCP wraps.
25
+ - [Source](https://github.com/MATCHUP-LABS/api-ventures/tree/main/products/nyc-fhv-intelligence-api/mcp-server): TypeScript, MIT license.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matchuplabs/nycfhv-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "MCP server for the NYC FHV Intelligence API. Exposes TLC for-hire vehicle license verification and renewal-window lookups as agent tools backed by fhv.matchup.dev.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,10 +11,16 @@
11
11
  "license": "MIT",
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "https://github.com/MATCHUP-LABS/nycfhv.git",
15
- "directory": "mcp-server"
14
+ "url": "https://github.com/MATCHUP-LABS/api-ventures.git",
15
+ "directory": "products/nyc-fhv-intelligence-api/mcp-server"
16
16
  },
17
17
  "homepage": "https://fhv.matchup.dev",
18
+ "bugs": {
19
+ "url": "https://github.com/MATCHUP-LABS/api-ventures/issues"
20
+ },
21
+ "engines": {
22
+ "node": ">=18.17"
23
+ },
18
24
  "keywords": [
19
25
  "mcp",
20
26
  "model-context-protocol",
@@ -24,12 +30,21 @@
24
30
  "fhv",
25
31
  "license-verification",
26
32
  "rideshare",
27
- "ai-agent"
33
+ "ai-agent",
34
+ "nemt",
35
+ "fleet-management",
36
+ "compliance",
37
+ "wheelchair-accessible",
38
+ "wav",
39
+ "government-data",
40
+ "transportation"
28
41
  ],
29
42
  "files": [
30
43
  "dist",
31
44
  "server.json",
32
- "README.md"
45
+ "README.md",
46
+ "llms.txt",
47
+ "llms-full.txt"
33
48
  ],
34
49
  "scripts": {
35
50
  "build": "tsc",
package/server.json CHANGED
@@ -3,17 +3,18 @@
3
3
  "name": "io.github.MatchupLabs/nycfhv",
4
4
  "title": "NYC FHV Intelligence API",
5
5
  "description": "Real-time NYC TLC for-hire vehicle license verification and renewal-window lead lookups via MCP.",
6
- "version": "0.1.0",
6
+ "version": "0.1.1",
7
7
  "repository": {
8
- "url": "https://github.com/MATCHUP-LABS/nycfhv",
9
- "source": "github"
8
+ "url": "https://github.com/MATCHUP-LABS/api-ventures",
9
+ "source": "github",
10
+ "subfolder": "products/nyc-fhv-intelligence-api/mcp-server"
10
11
  },
11
12
  "packages": [
12
13
  {
13
14
  "registryType": "npm",
14
15
  "registryBaseUrl": "https://registry.npmjs.org",
15
16
  "identifier": "@matchuplabs/nycfhv-mcp",
16
- "version": "0.1.0",
17
+ "version": "0.1.1",
17
18
  "transport": {
18
19
  "type": "stdio"
19
20
  },