mcp-apitools 1.0.5 → 1.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/README.md +4 -79
- package/index.js +185 -1
- package/package.json +19 -3
package/README.md
CHANGED
|
@@ -1,83 +1,8 @@
|
|
|
1
|
-
# mcp-apitools
|
|
2
1
|
|
|
3
|
-
MCP server with 8 API & web development utilities for Claude, Cursor, and other MCP-compatible AI assistants.
|
|
4
2
|
|
|
5
|
-
##
|
|
3
|
+
## Support
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
npx mcp-apitools
|
|
9
|
-
```
|
|
5
|
+
If this tool saves you time, consider supporting development:
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Add to `claude_desktop_config.json`:
|
|
14
|
-
|
|
15
|
-
```json
|
|
16
|
-
{
|
|
17
|
-
"mcpServers": {
|
|
18
|
-
"apitools": {
|
|
19
|
-
"command": "npx",
|
|
20
|
-
"args": ["-y", "mcp-apitools"]
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### Cursor
|
|
27
|
-
|
|
28
|
-
Add to MCP settings:
|
|
29
|
-
|
|
30
|
-
```json
|
|
31
|
-
{
|
|
32
|
-
"mcpServers": {
|
|
33
|
-
"apitools": {
|
|
34
|
-
"command": "npx",
|
|
35
|
-
"args": ["-y", "mcp-apitools"]
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Tools (8)
|
|
42
|
-
|
|
43
|
-
| Tool | Description |
|
|
44
|
-
|------|-------------|
|
|
45
|
-
| `http_status` | Look up any HTTP status code — phrase, category, description |
|
|
46
|
-
| `mime_lookup` | Get MIME type for file extension or find extensions for a MIME type |
|
|
47
|
-
| `jwt_create` | Create unsigned JWTs for testing with custom claims |
|
|
48
|
-
| `mock_data` | Generate fake people with names, emails, phones, addresses |
|
|
49
|
-
| `cors_headers` | Generate CORS response headers for your API |
|
|
50
|
-
| `cookie_parse` | Parse Cookie or Set-Cookie headers into structured data |
|
|
51
|
-
| `basic_auth` | Generate Basic Authorization headers |
|
|
52
|
-
| `query_string` | Parse or build URL query strings |
|
|
53
|
-
|
|
54
|
-
## Examples
|
|
55
|
-
|
|
56
|
-
**Look up HTTP 429:**
|
|
57
|
-
> "What does HTTP 429 mean?" → `Too Many Requests — User has sent too many requests (rate limiting).`
|
|
58
|
-
|
|
59
|
-
**Generate test data:**
|
|
60
|
-
> "Generate 3 fake users for testing" → Returns 3 people with realistic names, emails, addresses
|
|
61
|
-
|
|
62
|
-
**Create a test JWT:**
|
|
63
|
-
> "Make a JWT with sub=123 and role=admin" → Returns unsigned JWT token
|
|
64
|
-
|
|
65
|
-
**Parse cookies:**
|
|
66
|
-
> "Parse this cookie header: session=abc123; theme=dark" → `{"session": "abc123", "theme": "dark"}`
|
|
67
|
-
|
|
68
|
-
## See Also
|
|
69
|
-
|
|
70
|
-
- [mcp-devutils](https://www.npmjs.com/package/mcp-devutils) — UUID, hash, base64, timestamps, JWT decode, and more
|
|
71
|
-
- [mcp-texttools](https://www.npmjs.com/package/mcp-texttools) — 10 text transformation tools: case convert, slugify, word count, lorem ipsum, regex replace, markdown strip
|
|
72
|
-
- [mcp-mathtools](https://www.npmjs.com/package/mcp-mathtools) — 12 math & statistics tools: arithmetic, statistics, unit conversion, financial calculations, matrices
|
|
73
|
-
- [mcp-datetime](https://www.npmjs.com/package/mcp-datetime) — 10 date & time tools: timezone conversion, date math, cron explanation, business days
|
|
74
|
-
- [mcp-quick-calc](https://www.npmjs.com/package/mcp-quick-calc) — 5 calculator tools: currency conversion, percentages, compound interest, unit conversion, loan payments
|
|
75
|
-
- **[mcp-all-tools](https://www.npmjs.com/package/mcp-all-tools)** — All 54+ tools in a single MCP server
|
|
76
|
-
|
|
77
|
-
## License
|
|
78
|
-
|
|
79
|
-
MIT
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
☕ If this saves you time, [buy me a coffee](https://buymeacoffee.com/gl89tu25lp)
|
|
7
|
+
- [Buy me a coffee](https://buymeacoffee.com/gl89tu25lp)
|
|
8
|
+
- [Tip via Stripe ($3)](https://buy.stripe.com/dRm8wP8R295Z9VyeN59Zm00)
|
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
|
|
|
6
6
|
import crypto from "crypto";
|
|
7
7
|
|
|
8
8
|
const server = new Server(
|
|
9
|
-
{ name: "mcp-apitools", version: "1.
|
|
9
|
+
{ name: "mcp-apitools", version: "1.1.0" },
|
|
10
10
|
{ capabilities: { tools: {} } }
|
|
11
11
|
);
|
|
12
12
|
|
|
@@ -180,6 +180,79 @@ const TOOLS = [
|
|
|
180
180
|
},
|
|
181
181
|
required: ["input"]
|
|
182
182
|
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: "url_parse",
|
|
186
|
+
description: "Parse a URL into its components — protocol, host, port, pathname, search params, hash, origin",
|
|
187
|
+
inputSchema: {
|
|
188
|
+
type: "object",
|
|
189
|
+
properties: {
|
|
190
|
+
url: { type: "string", description: "URL to parse (e.g. 'https://api.example.com:8080/v1/users?page=2#section')" }
|
|
191
|
+
},
|
|
192
|
+
required: ["url"]
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: "bearer_token",
|
|
197
|
+
description: "Generate a Bearer Authorization header from a token string",
|
|
198
|
+
inputSchema: {
|
|
199
|
+
type: "object",
|
|
200
|
+
properties: {
|
|
201
|
+
token: { type: "string", description: "Bearer token value" }
|
|
202
|
+
},
|
|
203
|
+
required: ["token"]
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: "request_id",
|
|
208
|
+
description: "Generate unique request/correlation IDs for distributed tracing (UUID v4, prefixed, or nanoid-style)",
|
|
209
|
+
inputSchema: {
|
|
210
|
+
type: "object",
|
|
211
|
+
properties: {
|
|
212
|
+
format: { type: "string", enum: ["uuid", "prefixed", "short"], description: "ID format: uuid (default), prefixed (req_xxx), or short (12-char alphanumeric)" },
|
|
213
|
+
prefix: { type: "string", description: "Custom prefix for 'prefixed' format (default: 'req')" },
|
|
214
|
+
count: { type: "number", description: "Number of IDs to generate (1-20, default 1)" }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: "api_error",
|
|
220
|
+
description: "Generate a standard API error response body (RFC 7807 Problem Details format)",
|
|
221
|
+
inputSchema: {
|
|
222
|
+
type: "object",
|
|
223
|
+
properties: {
|
|
224
|
+
status: { type: "number", description: "HTTP status code (e.g. 404)" },
|
|
225
|
+
title: { type: "string", description: "Short error title" },
|
|
226
|
+
detail: { type: "string", description: "Detailed error description" },
|
|
227
|
+
instance: { type: "string", description: "URI reference identifying the specific occurrence" }
|
|
228
|
+
},
|
|
229
|
+
required: ["status"]
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "rate_limit_headers",
|
|
234
|
+
description: "Generate standard rate limit response headers (X-RateLimit-* and Retry-After)",
|
|
235
|
+
inputSchema: {
|
|
236
|
+
type: "object",
|
|
237
|
+
properties: {
|
|
238
|
+
limit: { type: "number", description: "Max requests per window (default 100)" },
|
|
239
|
+
remaining: { type: "number", description: "Remaining requests in window (default 99)" },
|
|
240
|
+
windowSeconds: { type: "number", description: "Window duration in seconds (default 3600)" },
|
|
241
|
+
retryAfter: { type: "number", description: "Seconds until rate limit resets (omit if not rate limited)" }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: "form_encode",
|
|
247
|
+
description: "Encode a JSON object as application/x-www-form-urlencoded, or decode form data back to JSON",
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: "object",
|
|
250
|
+
properties: {
|
|
251
|
+
input: { type: "string", description: "JSON object to encode, or URL-encoded form string to decode" },
|
|
252
|
+
action: { type: "string", enum: ["encode", "decode"], description: "Action: encode (default) or decode" }
|
|
253
|
+
},
|
|
254
|
+
required: ["input"]
|
|
255
|
+
}
|
|
183
256
|
}
|
|
184
257
|
];
|
|
185
258
|
|
|
@@ -298,6 +371,117 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
298
371
|
return { content: [{ type: "text", text: `Parsed query parameters:\n${JSON.stringify(result, null, 2)}` }] };
|
|
299
372
|
}
|
|
300
373
|
|
|
374
|
+
case "url_parse": {
|
|
375
|
+
try {
|
|
376
|
+
const u = new URL(args.url);
|
|
377
|
+
const params = {};
|
|
378
|
+
for (const [k, v] of u.searchParams) {
|
|
379
|
+
if (params[k]) params[k] = Array.isArray(params[k]) ? [...params[k], v] : [params[k], v];
|
|
380
|
+
else params[k] = v;
|
|
381
|
+
}
|
|
382
|
+
const result = {
|
|
383
|
+
href: u.href,
|
|
384
|
+
origin: u.origin,
|
|
385
|
+
protocol: u.protocol,
|
|
386
|
+
host: u.host,
|
|
387
|
+
hostname: u.hostname,
|
|
388
|
+
port: u.port || "(default)",
|
|
389
|
+
pathname: u.pathname,
|
|
390
|
+
search: u.search,
|
|
391
|
+
searchParams: params,
|
|
392
|
+
hash: u.hash,
|
|
393
|
+
username: u.username || undefined,
|
|
394
|
+
password: u.password || undefined,
|
|
395
|
+
};
|
|
396
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
397
|
+
} catch {
|
|
398
|
+
return { content: [{ type: "text", text: `Invalid URL: "${args.url}". Provide a full URL (e.g. https://example.com/path?key=val).` }], isError: true };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
case "bearer_token": {
|
|
403
|
+
return { content: [{ type: "text", text: `Authorization: Bearer ${args.token}\n\n⚠️ Bearer tokens should be transmitted over HTTPS only.` }] };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
case "request_id": {
|
|
407
|
+
const fmt = args.format || "uuid";
|
|
408
|
+
const count = Math.max(1, Math.min(20, args.count || 1));
|
|
409
|
+
const ids = [];
|
|
410
|
+
for (let i = 0; i < count; i++) {
|
|
411
|
+
if (fmt === "uuid") {
|
|
412
|
+
ids.push(crypto.randomUUID());
|
|
413
|
+
} else if (fmt === "prefixed") {
|
|
414
|
+
const prefix = args.prefix || "req";
|
|
415
|
+
ids.push(`${prefix}_${crypto.randomUUID().replace(/-/g, "").slice(0, 20)}`);
|
|
416
|
+
} else {
|
|
417
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
418
|
+
let id = "";
|
|
419
|
+
for (let j = 0; j < 12; j++) id += chars[Math.floor(Math.random() * chars.length)];
|
|
420
|
+
ids.push(id);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const header = count === 1
|
|
424
|
+
? `X-Request-ID: ${ids[0]}`
|
|
425
|
+
: ids.map((id, i) => `${i + 1}. ${id}`).join("\n");
|
|
426
|
+
return { content: [{ type: "text", text: header }] };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
case "api_error": {
|
|
430
|
+
const status = args.status;
|
|
431
|
+
const info = HTTP_STATUSES[status];
|
|
432
|
+
const title = args.title || (info ? info[0] : "Error");
|
|
433
|
+
const detail = args.detail || (info ? info[1] : "An error occurred.");
|
|
434
|
+
const body = {
|
|
435
|
+
type: `about:blank`,
|
|
436
|
+
title,
|
|
437
|
+
status,
|
|
438
|
+
detail,
|
|
439
|
+
};
|
|
440
|
+
if (args.instance) body.instance = args.instance;
|
|
441
|
+
return { content: [{ type: "text", text: `Content-Type: application/problem+json\n\n${JSON.stringify(body, null, 2)}\n\nSee RFC 7807 for Problem Details specification.` }] };
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
case "rate_limit_headers": {
|
|
445
|
+
const limit = args.limit || 100;
|
|
446
|
+
const remaining = args.remaining ?? 99;
|
|
447
|
+
const windowSec = args.windowSeconds || 3600;
|
|
448
|
+
const resetTime = Math.floor(Date.now() / 1000) + windowSec;
|
|
449
|
+
const headers = [
|
|
450
|
+
`X-RateLimit-Limit: ${limit}`,
|
|
451
|
+
`X-RateLimit-Remaining: ${remaining}`,
|
|
452
|
+
`X-RateLimit-Reset: ${resetTime}`,
|
|
453
|
+
];
|
|
454
|
+
if (args.retryAfter != null) {
|
|
455
|
+
headers.push(`Retry-After: ${args.retryAfter}`);
|
|
456
|
+
}
|
|
457
|
+
return { content: [{ type: "text", text: `Rate Limit Headers:\n\n${headers.join("\n")}\n\nWindow: ${windowSec}s | Limit: ${limit} req/window | Remaining: ${remaining}` }] };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
case "form_encode": {
|
|
461
|
+
const action = args.action || "encode";
|
|
462
|
+
if (action === "encode") {
|
|
463
|
+
try {
|
|
464
|
+
const obj = JSON.parse(args.input);
|
|
465
|
+
const params = new URLSearchParams();
|
|
466
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
467
|
+
if (Array.isArray(v)) v.forEach(item => params.append(k, String(item)));
|
|
468
|
+
else params.append(k, String(v));
|
|
469
|
+
}
|
|
470
|
+
return { content: [{ type: "text", text: `Content-Type: application/x-www-form-urlencoded\n\n${params.toString()}` }] };
|
|
471
|
+
} catch {
|
|
472
|
+
return { content: [{ type: "text", text: `Invalid JSON input. Provide a JSON object to encode.` }], isError: true };
|
|
473
|
+
}
|
|
474
|
+
} else {
|
|
475
|
+
const params = new URLSearchParams(args.input);
|
|
476
|
+
const result = {};
|
|
477
|
+
for (const [k, v] of params) {
|
|
478
|
+
if (result[k]) result[k] = Array.isArray(result[k]) ? [...result[k], v] : [result[k], v];
|
|
479
|
+
else result[k] = v;
|
|
480
|
+
}
|
|
481
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
301
485
|
default:
|
|
302
486
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
303
487
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-apitools",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "MCP server with API & web development utilities — HTTP status codes, MIME types, JWT creation, mock data
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MCP server with 14 API & web development utilities — HTTP status codes, MIME types, JWT creation, mock data, CORS headers, cookie parsing, URL parser, Bearer auth, request IDs, RFC 7807 errors, rate limit headers, form encoding",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
@@ -24,7 +24,15 @@
|
|
|
24
24
|
"claude",
|
|
25
25
|
"cursor",
|
|
26
26
|
"developer-tools",
|
|
27
|
-
"rest-api"
|
|
27
|
+
"rest-api",
|
|
28
|
+
"url-parser",
|
|
29
|
+
"bearer-auth",
|
|
30
|
+
"request-id",
|
|
31
|
+
"rate-limit",
|
|
32
|
+
"rfc-7807",
|
|
33
|
+
"form-encode",
|
|
34
|
+
"distributed-tracing",
|
|
35
|
+
"ai-tools"
|
|
28
36
|
],
|
|
29
37
|
"author": "Hong Teoh",
|
|
30
38
|
"license": "MIT",
|
|
@@ -34,5 +42,13 @@
|
|
|
34
42
|
},
|
|
35
43
|
"dependencies": {
|
|
36
44
|
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/hlteoh37/mcp-apitools.git"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/hlteoh37/mcp-apitools#readme",
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/hlteoh37/mcp-apitools/issues"
|
|
37
53
|
}
|
|
38
54
|
}
|