parseapi-mcp 0.2.1 → 0.3.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/README.md +26 -4
- package/dist/{chunk-FQJPDQPX.js → chunk-2LHHK5F6.js} +122 -75
- package/dist/http.js +1 -1
- package/dist/registry.js +1 -1
- package/dist/stdio.js +1 -1
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# parseapi-mcp
|
|
2
2
|
|
|
3
|
-
Official parseAPI MCP server.
|
|
3
|
+
Official parseAPI MCP server. Look up places, addresses, company numbers, email, phone, weather, currency, timezones, dates and more from your AI agent.
|
|
4
4
|
|
|
5
5
|
## Hosted
|
|
6
6
|
|
|
@@ -47,7 +47,26 @@ CI and headless setups skip the browser with a key from [parseapi.com](https://p
|
|
|
47
47
|
|
|
48
48
|
## Tools
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
51 local tools and 50 hosted tools cover all 53 SDK operations. `ip_self` is local only. `timezone` accepts either a timezone ID or coordinates. `date` parses the supplied date, or returns today in UTC when omitted. Every tool returns the JSON the API serves.
|
|
51
|
+
|
|
52
|
+
Tools follow the lookup names: `country_states`, `city_search`, `postal_nearby`, `address`, `address_search`, `company`, `email`, `vat`, `iban`, `npi`, `vin`, `tariff`, `asn`, `mac`, `currency_rate`, and the rest. All search tools take `query`.
|
|
53
|
+
|
|
54
|
+
Example tool arguments:
|
|
55
|
+
|
|
56
|
+
| Tool | Arguments |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `asn` | `{"asn":"AS13335"}` |
|
|
59
|
+
| `mac` | `{"mac":"00:1B:63:84:45:E6"}` |
|
|
60
|
+
| `country_states` | `{"code":"US"}` |
|
|
61
|
+
| `address_search` | `{"query":"1600 Pennsylvania","country":"US","city":"Washington","state":"DC"}` |
|
|
62
|
+
| `company` | `{"number":"552100554","country":"FR"}` |
|
|
63
|
+
| `timezone` | `{"timezone":"America/New_York","at":"2026-09-05T09:00:00","to":"Asia/Tokyo"}` |
|
|
64
|
+
| `timezone` | `{"lat":40.71,"lon":-74.01}` |
|
|
65
|
+
| `date` | `{"date":"03/04/2026","format":"dmy"}` |
|
|
66
|
+
|
|
67
|
+
Address lookup returns standardized components and registration status for the US and France. Its `deep` object is currently empty. Company lookup returns validity, registration status and business details when available. `address_search` also accepts `postal` and `ip` to narrow or rank matches. French search needs `country: "FR"` and either `postal` or `city`.
|
|
68
|
+
|
|
69
|
+
Ordinary lookups retry up to twice after a transient failure. Metered lookups and address deep checks default to no retries. Cancelling a tool call cancels the pending SDK request.
|
|
51
70
|
|
|
52
71
|
Errors come back as JSON with a machine-readable `code`. Branch on `code`, never on message text. A miss is `not_found`. No key is `invalid_api_key`.
|
|
53
72
|
|
|
@@ -55,9 +74,12 @@ Errors come back as JSON with a machine-readable `code`. Branch on `code`, never
|
|
|
55
74
|
|
|
56
75
|
```bash
|
|
57
76
|
npm install
|
|
58
|
-
npm run
|
|
59
|
-
npm
|
|
77
|
+
npm run typecheck
|
|
78
|
+
npm test # builds and tests with mocked fetch and in-memory MCP
|
|
79
|
+
npm run smoke # stdio + http, includes live API authentication checks
|
|
60
80
|
npm run serve # http on :8080
|
|
61
81
|
```
|
|
62
82
|
|
|
83
|
+
Offline tests pin the public tool names and argument schemas in `test/public-api.json`, exercise every operation and query option, and verify errors, retries and cancellation. Review baseline changes as public API changes. Publishing runs typecheck and offline tests first.
|
|
84
|
+
|
|
63
85
|
MIT licensed.
|
|
@@ -34,8 +34,8 @@ function noKeyResult(transport) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
// src/registry.ts
|
|
37
|
-
var VERSION = "0.
|
|
38
|
-
var deep = z.boolean().optional().describe("Include the nested deep object
|
|
37
|
+
var VERSION = "0.3.0";
|
|
38
|
+
var deep = z.boolean().optional().describe("Include the nested deep object when available. Pricing depends on the operation.");
|
|
39
39
|
var lat = z.number().min(-90).max(90).describe("Latitude in decimal degrees");
|
|
40
40
|
var lon = z.number().min(-180).max(180).describe("Longitude in decimal degrees");
|
|
41
41
|
var iso2 = (what) => z.string().describe(`ISO 3166-1 alpha-2 ${what}, e.g. US`);
|
|
@@ -46,24 +46,24 @@ function buildServer(key, transport) {
|
|
|
46
46
|
name: "parseapi",
|
|
47
47
|
version: VERSION,
|
|
48
48
|
title: "parseAPI",
|
|
49
|
-
description: "Lookups for agents: IP and place data, email, VAT, IBAN, NPI, phone
|
|
49
|
+
description: "Lookups for agents: IP and place data, addresses, company numbers, email, VAT, IBAN, NPI, phone, domains, weather, currency, timezones, dates and holidays. Real reference data instead of guessing.",
|
|
50
50
|
websiteUrl: "https://parseapi.com"
|
|
51
51
|
},
|
|
52
52
|
{ capabilities: { tools: {} } }
|
|
53
53
|
);
|
|
54
54
|
const parse = key ? parseAPI(key) : null;
|
|
55
|
-
function tool(name, description, shape, fn) {
|
|
55
|
+
function tool(name, description, shape, fn, refine) {
|
|
56
56
|
server.registerTool(
|
|
57
57
|
name,
|
|
58
58
|
{
|
|
59
59
|
description,
|
|
60
|
-
inputSchema: z.object(shape),
|
|
60
|
+
inputSchema: refine ? refine(z.object(shape)) : z.object(shape),
|
|
61
61
|
annotations: { readOnlyHint: true }
|
|
62
62
|
},
|
|
63
|
-
async (args) => {
|
|
63
|
+
async (args, context) => {
|
|
64
64
|
if (!parse) return noKeyResult(transport);
|
|
65
65
|
try {
|
|
66
|
-
return ok(await fn(parse, args));
|
|
66
|
+
return ok(await fn(parse, args, { signal: context.mcpReq.signal }));
|
|
67
67
|
} catch (err) {
|
|
68
68
|
return toErrorResult(err);
|
|
69
69
|
}
|
|
@@ -74,27 +74,27 @@ function buildServer(key, transport) {
|
|
|
74
74
|
"ip",
|
|
75
75
|
"Look up an IPv4 or IPv6 address: country, region, ASN, timezone. Deep adds datacenter, relay, tor and vpn flags.",
|
|
76
76
|
{ ip: z.string().describe("IPv4 or IPv6 address, e.g. 8.8.8.8"), deep },
|
|
77
|
-
(c, a) => c.ip(a.ip, { deep: a.deep })
|
|
77
|
+
(c, a, request) => c.ip(a.ip, { ...request, deep: a.deep })
|
|
78
78
|
);
|
|
79
79
|
if (transport === "stdio") {
|
|
80
80
|
tool(
|
|
81
81
|
"ip_self",
|
|
82
82
|
"Look up the public IP of the machine running this MCP server.",
|
|
83
83
|
{ deep },
|
|
84
|
-
(c, a) => c.ip.self({ deep: a.deep })
|
|
84
|
+
(c, a, request) => c.ip.self({ ...request, deep: a.deep })
|
|
85
85
|
);
|
|
86
86
|
}
|
|
87
87
|
tool(
|
|
88
88
|
"continent",
|
|
89
89
|
"Look up a continent by code: name, area, population.",
|
|
90
90
|
{ code: z.string().describe("Continent code: AF, AN, AS, EU, NA, OC, SA") },
|
|
91
|
-
(c, a) => c.continent(a.code)
|
|
91
|
+
(c, a, request) => c.continent(a.code, request)
|
|
92
92
|
);
|
|
93
93
|
tool(
|
|
94
94
|
"continent_countries",
|
|
95
95
|
"List every country on a continent.",
|
|
96
96
|
{ code: z.string().describe("Continent code: AF, AN, AS, EU, NA, OC, SA") },
|
|
97
|
-
(c, a) => c.continent.countries(a.code)
|
|
97
|
+
(c, a, request) => c.continent.countries(a.code, request)
|
|
98
98
|
);
|
|
99
99
|
tool(
|
|
100
100
|
"bloc",
|
|
@@ -102,10 +102,7 @@ function buildServer(key, transport) {
|
|
|
102
102
|
{
|
|
103
103
|
code: z.string().describe("Bloc code: EU, EEA, EFTA, SCHENGEN, EUROZONE, SEPA, NATO, OECD, G7, ASEAN, GCC, MERCOSUR")
|
|
104
104
|
},
|
|
105
|
-
(c, a) =>
|
|
106
|
-
const client = c;
|
|
107
|
-
return client.bloc(a.code);
|
|
108
|
-
}
|
|
105
|
+
(c, a, request) => c.bloc(a.code, request)
|
|
109
106
|
);
|
|
110
107
|
tool(
|
|
111
108
|
"bloc_countries",
|
|
@@ -113,22 +110,19 @@ function buildServer(key, transport) {
|
|
|
113
110
|
{
|
|
114
111
|
code: z.string().describe("Bloc code: EU, EEA, EFTA, SCHENGEN, EUROZONE, SEPA, NATO, OECD, G7, ASEAN, GCC, MERCOSUR")
|
|
115
112
|
},
|
|
116
|
-
(c, a) =>
|
|
117
|
-
const client = c;
|
|
118
|
-
return client.bloc.countries(a.code);
|
|
119
|
-
}
|
|
113
|
+
(c, a, request) => c.bloc.countries(a.code, request)
|
|
120
114
|
);
|
|
121
115
|
tool(
|
|
122
116
|
"country",
|
|
123
117
|
"Look up a country: names, capital, currency, languages, calling code, timezones.",
|
|
124
118
|
{ code: iso2("country code") },
|
|
125
|
-
(c, a) => c.country(a.code)
|
|
119
|
+
(c, a, request) => c.country(a.code, request)
|
|
126
120
|
);
|
|
127
121
|
tool(
|
|
128
122
|
"country_states",
|
|
129
123
|
"List the states, provinces or regions of a country.",
|
|
130
124
|
{ code: iso2("country code") },
|
|
131
|
-
(c, a) => c.country.states(a.code)
|
|
125
|
+
(c, a, request) => c.country.states(a.code, request)
|
|
132
126
|
);
|
|
133
127
|
tool(
|
|
134
128
|
"state",
|
|
@@ -137,7 +131,7 @@ function buildServer(key, transport) {
|
|
|
137
131
|
code: z.string().describe("State code or name, e.g. colorado, NC"),
|
|
138
132
|
country: countryOpt
|
|
139
133
|
},
|
|
140
|
-
(c, a) => c.state(a.code, { country: a.country })
|
|
134
|
+
(c, a, request) => c.state(a.code, { ...request, country: a.country })
|
|
141
135
|
);
|
|
142
136
|
tool(
|
|
143
137
|
"state_districts",
|
|
@@ -146,7 +140,7 @@ function buildServer(key, transport) {
|
|
|
146
140
|
code: z.string().describe("State code or name, e.g. NC, colorado"),
|
|
147
141
|
country: countryOpt
|
|
148
142
|
},
|
|
149
|
-
(c, a) => c.state.districts(a.code, { country: a.country })
|
|
143
|
+
(c, a, request) => c.state.districts(a.code, { ...request, country: a.country })
|
|
150
144
|
);
|
|
151
145
|
tool(
|
|
152
146
|
"district",
|
|
@@ -156,7 +150,7 @@ function buildServer(key, transport) {
|
|
|
156
150
|
country: countryOpt,
|
|
157
151
|
state: z.string().optional().describe("ADM1 code or name to disambiguate, e.g. NC or louisiana")
|
|
158
152
|
},
|
|
159
|
-
(c, a) => c.district(a.code, { country: a.country, state: a.state })
|
|
153
|
+
(c, a, request) => c.district(a.code, { ...request, country: a.country, state: a.state })
|
|
160
154
|
);
|
|
161
155
|
tool(
|
|
162
156
|
"city",
|
|
@@ -166,30 +160,30 @@ function buildServer(key, transport) {
|
|
|
166
160
|
country: countryOpt,
|
|
167
161
|
state: z.string().optional().describe("State code to disambiguate, e.g. NC")
|
|
168
162
|
},
|
|
169
|
-
(c, a) => c.city(a.name, { country: a.country, state: a.state })
|
|
163
|
+
(c, a, request) => c.city(a.name, { ...request, country: a.country, state: a.state })
|
|
170
164
|
);
|
|
171
165
|
tool(
|
|
172
166
|
"city_id",
|
|
173
167
|
"Refetch a city by its stable parse id from an earlier response.",
|
|
174
168
|
{ id: z.string().describe("Stable city id, e.g. city_mb8mbqrkz8zb") },
|
|
175
|
-
(c, a) => c.city.id(a.id)
|
|
169
|
+
(c, a, request) => c.city.id(a.id, request)
|
|
176
170
|
);
|
|
177
171
|
tool(
|
|
178
172
|
"city_search",
|
|
179
173
|
"Search cities by name prefix. Use when the exact name is unknown.",
|
|
180
174
|
{
|
|
181
|
-
|
|
175
|
+
query: z.string().describe("Name prefix, e.g. char"),
|
|
182
176
|
country: countryOpt,
|
|
183
177
|
state: z.string().optional().describe("State code filter"),
|
|
184
178
|
limit: z.number().int().min(1).max(50).optional().describe("Max results")
|
|
185
179
|
},
|
|
186
|
-
(c, a) => c.city.search(a.
|
|
180
|
+
(c, a, request) => c.city.search(a.query, { ...request, country: a.country, state: a.state, limit: a.limit })
|
|
187
181
|
);
|
|
188
182
|
tool(
|
|
189
183
|
"city_nearest",
|
|
190
184
|
"Find the nearest city to coordinates.",
|
|
191
185
|
{ lat, lon },
|
|
192
|
-
(c, a) => c.city.nearest(a.lat, a.lon)
|
|
186
|
+
(c, a, request) => c.city.nearest(a.lat, a.lon, request)
|
|
193
187
|
);
|
|
194
188
|
tool(
|
|
195
189
|
"city_nearby",
|
|
@@ -202,7 +196,8 @@ function buildServer(key, transport) {
|
|
|
202
196
|
unit: z.enum(["km", "mi"]).optional().describe("Radius unit, default km"),
|
|
203
197
|
limit: z.number().int().min(1).max(50).optional().describe("Max results, default 10")
|
|
204
198
|
},
|
|
205
|
-
(c, a) => c.city.nearby(a.name, {
|
|
199
|
+
(c, a, request) => c.city.nearby(a.name, {
|
|
200
|
+
...request,
|
|
206
201
|
country: a.country,
|
|
207
202
|
state: a.state,
|
|
208
203
|
radius: a.radius,
|
|
@@ -217,7 +212,7 @@ function buildServer(key, transport) {
|
|
|
217
212
|
code: z.string().describe("Postal or ZIP code, e.g. SW1A 1AA, 28202"),
|
|
218
213
|
country: countryOpt
|
|
219
214
|
},
|
|
220
|
-
(c, a) => c.postal(a.code, { country: a.country })
|
|
215
|
+
(c, a, request) => c.postal(a.code, { ...request, country: a.country })
|
|
221
216
|
);
|
|
222
217
|
tool(
|
|
223
218
|
"postal_nearby",
|
|
@@ -228,7 +223,7 @@ function buildServer(key, transport) {
|
|
|
228
223
|
radius: z.number().positive().optional().describe("Search radius"),
|
|
229
224
|
unit: z.enum(["km", "mi"]).optional().describe("Radius unit, default km")
|
|
230
225
|
},
|
|
231
|
-
(c, a) => c.postal.nearby(a.code, { country: a.country, radius: a.radius, unit: a.unit })
|
|
226
|
+
(c, a, request) => c.postal.nearby(a.code, { ...request, country: a.country, radius: a.radius, unit: a.unit })
|
|
232
227
|
);
|
|
233
228
|
tool(
|
|
234
229
|
"postal_distance",
|
|
@@ -238,36 +233,69 @@ function buildServer(key, transport) {
|
|
|
238
233
|
to: z.string().describe("Second postal code"),
|
|
239
234
|
country: countryOpt
|
|
240
235
|
},
|
|
241
|
-
(c, a) => c.postal.distance(a.from, a.to, { country: a.country })
|
|
236
|
+
(c, a, request) => c.postal.distance(a.from, a.to, { ...request, country: a.country })
|
|
237
|
+
);
|
|
238
|
+
tool(
|
|
239
|
+
"address",
|
|
240
|
+
"Look up a US or French street address: house number, street, unit, city, state, postal code and registration status. Registration does not establish deliverability or verify an apartment. Deep is currently an empty object.",
|
|
241
|
+
{
|
|
242
|
+
address: z.string().describe("Street address, e.g. 1600 Pennsylvania Ave NW, Washington, DC 20500"),
|
|
243
|
+
country: z.string().optional().describe("Country: US or FR. Use FR or a written France suffix for France. Defaults to US."),
|
|
244
|
+
deep
|
|
245
|
+
},
|
|
246
|
+
(c, a, request) => c.address(a.address, { ...request, country: a.country, deep: a.deep })
|
|
247
|
+
);
|
|
248
|
+
tool(
|
|
249
|
+
"address_search",
|
|
250
|
+
"Search US or French street addresses from a partial address. Postal code, city, state and user IP can narrow or rank matches. French search requires country FR and either postal or city.",
|
|
251
|
+
{
|
|
252
|
+
query: z.string().describe("Partial street address, e.g. 1600 Pennsylvania"),
|
|
253
|
+
country: z.string().optional().describe("Country: US or FR. Defaults to US."),
|
|
254
|
+
postal: z.string().optional().describe("Postal code filter"),
|
|
255
|
+
city: z.string().optional().describe("City filter"),
|
|
256
|
+
state: z.string().optional().describe("State code filter"),
|
|
257
|
+
ip: z.string().optional().describe("User IP address for ranking nearby matches")
|
|
258
|
+
},
|
|
259
|
+
(c, a, request) => c.address.search(a.query, { ...request, country: a.country, postal: a.postal, city: a.city, state: a.state, ip: a.ip })
|
|
260
|
+
);
|
|
261
|
+
tool(
|
|
262
|
+
"company",
|
|
263
|
+
"Look up a company registration number: validity, registration status, name, activity and address when available. Pass country when more than one scheme can match. Deep adds country, postal and city data.",
|
|
264
|
+
{
|
|
265
|
+
number: z.string().describe("Company registration number, e.g. 552100554 with country FR"),
|
|
266
|
+
country: countryOpt,
|
|
267
|
+
deep
|
|
268
|
+
},
|
|
269
|
+
(c, a, request) => c.company(a.number, { ...request, country: a.country, deep: a.deep })
|
|
242
270
|
);
|
|
243
271
|
tool(
|
|
244
272
|
"point",
|
|
245
273
|
"Reverse geocode coordinates to country, state, district and nearest city. Deep adds richer admin data.",
|
|
246
274
|
{ lat, lon, deep },
|
|
247
|
-
(c, a) => c.point(a.lat, a.lon, { deep: a.deep })
|
|
275
|
+
(c, a, request) => c.point(a.lat, a.lon, { ...request, deep: a.deep })
|
|
248
276
|
);
|
|
249
277
|
tool(
|
|
250
278
|
"elevation",
|
|
251
279
|
"Elevation in meters at coordinates.",
|
|
252
280
|
{ lat, lon },
|
|
253
|
-
(c, a) => c.elevation(a.lat, a.lon)
|
|
281
|
+
(c, a, request) => c.elevation(a.lat, a.lon, request)
|
|
254
282
|
);
|
|
255
283
|
tool(
|
|
256
284
|
"weather",
|
|
257
|
-
"Current weather observation at coordinates
|
|
285
|
+
"Current weather observation at coordinates. Every measurement ships metric and imperial side by side. Deep adds minute-by-minute rain, hourly and daily rows worldwide, alerts, and air quality. With deep, date returns a past day as deep.history.",
|
|
258
286
|
{
|
|
259
287
|
lat,
|
|
260
288
|
lon,
|
|
261
289
|
deep,
|
|
262
290
|
date: z.string().optional().describe("A past UTC day, YYYY-MM-DD. Requires deep. Returns that day as deep.history")
|
|
263
291
|
},
|
|
264
|
-
(c, a) => c.weather(a.lat, a.lon, { deep: a.deep, date: a.date })
|
|
292
|
+
(c, a, request) => c.weather(a.lat, a.lon, { ...request, deep: a.deep, date: a.date })
|
|
265
293
|
);
|
|
266
294
|
tool(
|
|
267
295
|
"email",
|
|
268
296
|
"Validate an email address: syntax, domain, MX, disposable, role, and a typo suggestion when the host looks misspelled. Deep runs a live mailbox verification.",
|
|
269
297
|
{ email: z.string().describe("Email address to validate"), deep },
|
|
270
|
-
(c, a) => c.email(a.email, { deep: a.deep })
|
|
298
|
+
(c, a, request) => c.email(a.email, { ...request, deep: a.deep })
|
|
271
299
|
);
|
|
272
300
|
tool(
|
|
273
301
|
"vat",
|
|
@@ -278,25 +306,25 @@ function buildServer(key, transport) {
|
|
|
278
306
|
from: z.string().optional().describe("Your own VAT number. Returns a consultation identifier for your audit file"),
|
|
279
307
|
deep
|
|
280
308
|
},
|
|
281
|
-
(c, a) => c.vat(a.number, { country: a.country, from: a.from, deep: a.deep })
|
|
309
|
+
(c, a, request) => c.vat(a.number, { ...request, country: a.country, from: a.from, deep: a.deep })
|
|
282
310
|
);
|
|
283
311
|
tool(
|
|
284
312
|
"iban",
|
|
285
|
-
"Parse an IBAN: checksum and structure. Returns the normalized number, the print form for display, country, checksum digits,
|
|
313
|
+
"Parse an IBAN: checksum and structure. Returns the normalized number, the print form for display, country, checksum digits, bank, branch, and account identifiers, plus bank_name and bic when available. Junk answers valid false, never a 404. Pass country when the value has no prefix.",
|
|
286
314
|
{
|
|
287
315
|
iban: z.string().describe("IBAN, with or without spaces, with or without the country prefix"),
|
|
288
316
|
country: iso2("country code when the number has no prefix").optional()
|
|
289
317
|
},
|
|
290
|
-
(c, a) => c.iban(a.iban, { country: a.country })
|
|
318
|
+
(c, a, request) => c.iban(a.iban, { ...request, country: a.country })
|
|
291
319
|
);
|
|
292
320
|
tool(
|
|
293
321
|
"npi",
|
|
294
|
-
"Look up
|
|
322
|
+
"Look up a US healthcare provider by NPI: name, specialty, practice address, deactivation date, and exclusion status. Deep adds Medicare enrollment on paid plans.",
|
|
295
323
|
{
|
|
296
324
|
npi: z.string().describe("10-digit NPI number"),
|
|
297
325
|
deep
|
|
298
326
|
},
|
|
299
|
-
(c, a) => c.npi(a.npi, { deep: a.deep })
|
|
327
|
+
(c, a, request) => c.npi(a.npi, { ...request, deep: a.deep })
|
|
300
328
|
);
|
|
301
329
|
tool(
|
|
302
330
|
"phone",
|
|
@@ -306,7 +334,7 @@ function buildServer(key, transport) {
|
|
|
306
334
|
country: iso2("country code for national-format numbers").optional(),
|
|
307
335
|
deep
|
|
308
336
|
},
|
|
309
|
-
(c, a) => c.phone(a.number, { country: a.country, deep: a.deep })
|
|
337
|
+
(c, a, request) => c.phone(a.number, { ...request, country: a.country, deep: a.deep })
|
|
310
338
|
);
|
|
311
339
|
tool(
|
|
312
340
|
"carrier",
|
|
@@ -315,7 +343,7 @@ function buildServer(key, transport) {
|
|
|
315
343
|
number: z.string().describe("Phone number, e.g. +14155552671"),
|
|
316
344
|
country: iso2("country code for national-format numbers").optional()
|
|
317
345
|
},
|
|
318
|
-
(c, a) => c.carrier(a.number, { country: a.country })
|
|
346
|
+
(c, a, request) => c.carrier(a.number, { ...request, country: a.country })
|
|
319
347
|
);
|
|
320
348
|
tool(
|
|
321
349
|
"caller",
|
|
@@ -324,7 +352,7 @@ function buildServer(key, transport) {
|
|
|
324
352
|
number: z.string().describe("Phone number, e.g. +18004633339"),
|
|
325
353
|
country: iso2("country code for national-format numbers").optional()
|
|
326
354
|
},
|
|
327
|
-
(c, a) => c.caller(a.number, { country: a.country })
|
|
355
|
+
(c, a, request) => c.caller(a.number, { ...request, country: a.country })
|
|
328
356
|
);
|
|
329
357
|
tool(
|
|
330
358
|
"hlr",
|
|
@@ -333,31 +361,43 @@ function buildServer(key, transport) {
|
|
|
333
361
|
number: z.string().describe("Phone number, e.g. +447712345678"),
|
|
334
362
|
country: iso2("country code for national-format numbers").optional()
|
|
335
363
|
},
|
|
336
|
-
(c, a) => c.hlr(a.number, { country: a.country })
|
|
364
|
+
(c, a, request) => c.hlr(a.number, { ...request, country: a.country })
|
|
337
365
|
);
|
|
338
366
|
tool(
|
|
339
367
|
"domain",
|
|
340
368
|
"Look up a domain: registration, DNS, mail setup. Deep adds richer checks.",
|
|
341
369
|
{ domain: z.string().describe("Domain name, e.g. example.com"), deep },
|
|
342
|
-
(c, a) => c.domain(a.domain, { deep: a.deep })
|
|
370
|
+
(c, a, request) => c.domain(a.domain, { ...request, deep: a.deep })
|
|
371
|
+
);
|
|
372
|
+
tool(
|
|
373
|
+
"asn",
|
|
374
|
+
"Network name and country for an autonomous system number.",
|
|
375
|
+
{ asn: z.string().describe("Autonomous system number, such as AS13335 or 13335") },
|
|
376
|
+
(c, a, request) => c.asn(a.asn, request)
|
|
377
|
+
);
|
|
378
|
+
tool(
|
|
379
|
+
"mac",
|
|
380
|
+
"Normalize a 48-bit MAC address and return its registered assignment holder, local flag, and multicast flag. Vendor is null for local or multicast addresses.",
|
|
381
|
+
{ mac: z.string().describe("MAC address") },
|
|
382
|
+
(c, a, request) => c.mac(a.mac, request)
|
|
343
383
|
);
|
|
344
384
|
tool(
|
|
345
385
|
"mx",
|
|
346
386
|
"MX records for a domain.",
|
|
347
387
|
{ domain: z.string().describe("Domain name") },
|
|
348
|
-
(c, a) => c.mx(a.domain)
|
|
388
|
+
(c, a, request) => c.mx(a.domain, request)
|
|
349
389
|
);
|
|
350
390
|
tool(
|
|
351
391
|
"useragent",
|
|
352
392
|
"Parse a User-Agent string: browser, OS, device, bot detection.",
|
|
353
393
|
{ ua: z.string().describe("The User-Agent string to parse"), deep },
|
|
354
|
-
(c, a) => c.useragent(a.ua, { deep: a.deep })
|
|
394
|
+
(c, a, request) => c.useragent(a.ua, { ...request, deep: a.deep })
|
|
355
395
|
);
|
|
356
396
|
tool(
|
|
357
397
|
"vin",
|
|
358
398
|
"Decode a 17-character VIN: year, make, model, trim, body, engine, drive, transmission, manufacturer, and assembly plant. Junk or a failed check digit answers valid false, never a 404. Deep adds open recall campaigns on paid plans.",
|
|
359
399
|
{ vin: z.string().describe("The VIN as you have it. Spaces and punctuation fold out"), deep },
|
|
360
|
-
(c, a) => c.vin(a.vin, { deep: a.deep })
|
|
400
|
+
(c, a, request) => c.vin(a.vin, { ...request, deep: a.deep })
|
|
361
401
|
);
|
|
362
402
|
tool(
|
|
363
403
|
"tariff",
|
|
@@ -367,23 +407,23 @@ function buildServer(key, transport) {
|
|
|
367
407
|
origin: iso2("country of origin for duty resolution, only read with deep").optional(),
|
|
368
408
|
deep
|
|
369
409
|
},
|
|
370
|
-
(c, a) => c.tariff(a.code, { deep: a.deep, origin: a.origin })
|
|
410
|
+
(c, a, request) => c.tariff(a.code, { ...request, deep: a.deep, origin: a.origin })
|
|
371
411
|
);
|
|
372
412
|
tool(
|
|
373
413
|
"tariff_search",
|
|
374
414
|
"Search US tariff schedule descriptions by product. Returns up to 20 lines, best match first, each with hts, description, and the general duty rate.",
|
|
375
|
-
{
|
|
376
|
-
(c, a) => c.tariff.search(a.
|
|
415
|
+
{ query: z.string().describe("Product words, e.g. sunglasses, laptop, coffee") },
|
|
416
|
+
(c, a, request) => c.tariff.search(a.query, request)
|
|
377
417
|
);
|
|
378
418
|
tool(
|
|
379
419
|
"currency",
|
|
380
420
|
"Look up a currency: name, symbol, decimal places, countries using it.",
|
|
381
421
|
{ code: z.string().describe("ISO 4217 code, e.g. USD") },
|
|
382
|
-
(c, a) => c.currency(a.code)
|
|
422
|
+
(c, a, request) => c.currency(a.code, request)
|
|
383
423
|
);
|
|
384
424
|
tool(
|
|
385
425
|
"currency_rate",
|
|
386
|
-
"
|
|
426
|
+
"Reference exchange rate between two currencies. Pass date for a past business day, amount to convert.",
|
|
387
427
|
{
|
|
388
428
|
base: z.string().describe("Base currency ISO 4217 code, e.g. USD"),
|
|
389
429
|
quote: z.string().describe("Quote currency ISO 4217 code, e.g. EUR"),
|
|
@@ -392,25 +432,25 @@ function buildServer(key, transport) {
|
|
|
392
432
|
),
|
|
393
433
|
amount: z.number().optional().describe("Appends amount and converted, rounded to the quote currency minor-unit digits")
|
|
394
434
|
},
|
|
395
|
-
(c, a) => c.currency.rate(a.base, a.quote, { date: a.date, amount: a.amount })
|
|
435
|
+
(c, a, request) => c.currency.rate(a.base, a.quote, { ...request, date: a.date, amount: a.amount })
|
|
396
436
|
);
|
|
397
437
|
tool(
|
|
398
438
|
"language",
|
|
399
439
|
"Look up a language by BCP 47 or ISO 639-3 code: names, script, direction.",
|
|
400
440
|
{ code: z.string().describe("Language code, e.g. en, ja, gsw") },
|
|
401
|
-
(c, a) => c.language(a.code)
|
|
441
|
+
(c, a, request) => c.language(a.code, request)
|
|
402
442
|
);
|
|
403
443
|
tool(
|
|
404
444
|
"name",
|
|
405
445
|
"Parse a person name: prefix, first, middle, last, suffix, gender, salutation. Junk input returns valid false. Gender comes from dictionary data and is null when the data does not decide.",
|
|
406
446
|
{ name: z.string().describe("The name to parse, e.g. Smith, John or BILLY OSHALL") },
|
|
407
|
-
(c, a) => c.name(a.name)
|
|
447
|
+
(c, a, request) => c.name(a.name, request)
|
|
408
448
|
);
|
|
409
449
|
tool(
|
|
410
450
|
"timezone",
|
|
411
451
|
"Look up a timezone from an IANA id or from lat and lon. Offset, DST, local time. Pass at for a specific instant. Pass to with another IANA id to convert a time between zones: the response appends at (wall time in the from zone) and to.at (the converted time). Open ocean answers the nautical Etc/GMT zone.",
|
|
412
452
|
{
|
|
413
|
-
timezone: z.string().optional().describe("IANA timezone id, e.g. America/New_York"),
|
|
453
|
+
timezone: z.string().min(1).optional().describe("IANA timezone id, e.g. America/New_York"),
|
|
414
454
|
lat: lat.optional(),
|
|
415
455
|
lon: lon.optional(),
|
|
416
456
|
at: z.string().optional().describe(
|
|
@@ -418,29 +458,36 @@ function buildServer(key, transport) {
|
|
|
418
458
|
),
|
|
419
459
|
to: z.string().optional().describe("Convert: the other IANA zone, e.g. Asia/Tokyo. Requires timezone, not lat/lon")
|
|
420
460
|
},
|
|
421
|
-
(c, a) => {
|
|
461
|
+
(c, a, request) => {
|
|
422
462
|
if (a.lat != null && a.lon != null) {
|
|
423
|
-
return c.timezone(a.lat, a.lon, { at: a.at });
|
|
463
|
+
return c.timezone.at(a.lat, a.lon, { ...request, at: a.at });
|
|
424
464
|
}
|
|
425
465
|
if (!a.timezone) {
|
|
426
466
|
return Promise.reject(new Error("Pass timezone or lat and lon"));
|
|
427
467
|
}
|
|
428
|
-
return c.timezone(a.timezone, { at: a.at, to: a.to });
|
|
429
|
-
}
|
|
468
|
+
return c.timezone(a.timezone, { ...request, at: a.at, to: a.to });
|
|
469
|
+
},
|
|
470
|
+
(schema) => schema.refine(
|
|
471
|
+
(a) => a.timezone !== void 0 ? a.lat === void 0 && a.lon === void 0 : a.lat !== void 0 && a.lon !== void 0 && a.to === void 0,
|
|
472
|
+
{ message: "Pass either timezone (with optional to), or both lat and lon." }
|
|
473
|
+
)
|
|
430
474
|
);
|
|
431
475
|
tool(
|
|
432
476
|
"date",
|
|
433
477
|
"Parse a date in any common format (2026-03-29, March 29, 2026, 3/29/2026, 20260829) into calendar facts: ISO date, weekday, ISO week, day of year, quarter, leap year, unix time. Ambiguous numeric dates answer valid false unless format asserts a reading, never a guess. Pass to with another date for the signed days between. Omit date for today (UTC), so to alone answers days from today.",
|
|
434
478
|
{
|
|
435
|
-
date: z.string().optional().describe("The date as you have it, any common format. Omit for today (UTC)"),
|
|
479
|
+
date: z.string().min(1).optional().describe("The date as you have it, any common format. Omit for today (UTC)"),
|
|
436
480
|
format: z.enum(["mdy", "dmy"]).optional().describe("Breaks the month-first / day-first tie on numeric dates like 03/04/2026"),
|
|
437
481
|
to: z.string().optional().describe("Another date. Appends to (normalized ISO) and days (signed days between)")
|
|
438
482
|
},
|
|
439
|
-
(c, a) => {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
483
|
+
(c, a, request) => {
|
|
484
|
+
if (a.date == null) return c.date.today({ ...request, to: a.to });
|
|
485
|
+
return c.date(a.date, { ...request, format: a.format, to: a.to });
|
|
486
|
+
},
|
|
487
|
+
(schema) => schema.refine(
|
|
488
|
+
(a) => a.format === void 0 || a.date !== void 0,
|
|
489
|
+
{ message: "Pass date when specifying format; omit both for today." }
|
|
490
|
+
)
|
|
444
491
|
);
|
|
445
492
|
tool(
|
|
446
493
|
"holiday",
|
|
@@ -449,28 +496,28 @@ function buildServer(key, transport) {
|
|
|
449
496
|
country: iso2("country code"),
|
|
450
497
|
year: z.number().int().optional().describe("Year, default current")
|
|
451
498
|
},
|
|
452
|
-
(c, a) => c.holiday(a.country, { year: a.year })
|
|
499
|
+
(c, a, request) => c.holiday(a.country, { ...request, year: a.year })
|
|
453
500
|
);
|
|
454
501
|
tool(
|
|
455
502
|
"holiday_date",
|
|
456
503
|
"Whether a specific date is a holiday or observance in a country. holiday is null when it is not.",
|
|
457
504
|
{ country: iso2("country code"), date: z.string().describe("Date as YYYY-MM-DD") },
|
|
458
|
-
(c, a) => c.holiday.date(a.country, a.date)
|
|
505
|
+
(c, a, request) => c.holiday.date(a.country, a.date, request)
|
|
459
506
|
);
|
|
460
507
|
tool(
|
|
461
508
|
"emoji",
|
|
462
509
|
"Look up an emoji by name or character: unicode, hex, skin tones.",
|
|
463
510
|
{ emoji: z.string().describe("Emoji name or the character itself, e.g. rocket") },
|
|
464
|
-
(c, a) => c.emoji(a.emoji)
|
|
511
|
+
(c, a, request) => c.emoji(a.emoji, request)
|
|
465
512
|
);
|
|
466
513
|
tool(
|
|
467
514
|
"emoji_search",
|
|
468
515
|
"Search emoji by keyword.",
|
|
469
516
|
{
|
|
470
|
-
|
|
517
|
+
query: z.string().describe("Search keyword, e.g. fire"),
|
|
471
518
|
limit: z.number().int().min(1).max(50).optional().describe("Max results")
|
|
472
519
|
},
|
|
473
|
-
(c, a) => c.emoji.search(a.
|
|
520
|
+
(c, a, request) => c.emoji.search(a.query, { ...request, limit: a.limit })
|
|
474
521
|
);
|
|
475
522
|
return server;
|
|
476
523
|
}
|
package/dist/http.js
CHANGED
package/dist/registry.js
CHANGED
package/dist/stdio.js
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "parseapi-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"mcpName": "com.parseapi/mcp",
|
|
5
|
-
"description": "Official parseAPI MCP server.
|
|
5
|
+
"description": "Official parseAPI MCP server. Look up places, addresses, company numbers, email, phone, weather, dates and more.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"parseapi",
|
|
8
8
|
"mcp",
|
|
@@ -40,12 +40,14 @@
|
|
|
40
40
|
"build": "tsup",
|
|
41
41
|
"typecheck": "tsc --noEmit",
|
|
42
42
|
"smoke": "npm run build && node smoke/smoke.mjs",
|
|
43
|
-
"serve": "npm run build && node dist/http.js"
|
|
43
|
+
"serve": "npm run build && node dist/http.js",
|
|
44
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
45
|
+
"prepublishOnly": "npm run typecheck && npm test"
|
|
44
46
|
},
|
|
45
47
|
"dependencies": {
|
|
46
48
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
47
49
|
"@modelcontextprotocol/node": "^2.0.0",
|
|
48
|
-
"@parseapi/sdk": "^0.
|
|
50
|
+
"@parseapi/sdk": "^0.3.0",
|
|
49
51
|
"zod": "^4.0.0"
|
|
50
52
|
},
|
|
51
53
|
"devDependencies": {
|