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.
Files changed (3) hide show
  1. package/README.md +4 -79
  2. package/index.js +185 -1
  3. 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
- ## Quick Start
3
+ ## Support
6
4
 
7
- ```bash
8
- npx mcp-apitools
9
- ```
5
+ If this tool saves you time, consider supporting development:
10
6
 
11
- ### Claude Desktop
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.0.0" },
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.5",
4
- "description": "MCP server with API & web development utilities — HTTP status codes, MIME types, JWT creation, mock data generation, CORS headers, cookie parsing",
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
  }