@mafdet/mcp 1.6.0 → 1.7.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/dist/client.js +48 -30
- package/dist/index.js +0 -0
- package/package.json +8 -8
package/dist/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { redact } from "./logger.js";
|
|
1
|
+
import { redact, logError } from "./logger.js";
|
|
2
2
|
// MCP-002B: the single HTTP client for every Mafdet call. It enforces a total
|
|
3
3
|
// timeout, a response-body size cap, and JSON validation; attaches the Bearer
|
|
4
4
|
// key ONLY on account requests; and maps every failure to a stable, actionable
|
|
@@ -16,37 +16,54 @@ export class MafdetClientError extends Error {
|
|
|
16
16
|
export async function getJson(options) {
|
|
17
17
|
const url = buildUrl(options.baseUrl, options.path, options.query);
|
|
18
18
|
const controller = new AbortController();
|
|
19
|
-
|
|
20
|
-
let
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (!response.ok)
|
|
33
|
-
throw mapStatus(response.status);
|
|
34
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
35
|
-
if (!contentType.includes("application/json")) {
|
|
36
|
-
throw new MafdetClientError("bad_response", "Mafdet AI returned an unexpected (non-JSON) response");
|
|
37
|
-
}
|
|
38
|
-
const text = await readBounded(response);
|
|
19
|
+
let reader;
|
|
20
|
+
let timer;
|
|
21
|
+
const deadline = new Promise((_resolve, reject) => {
|
|
22
|
+
timer = setTimeout(() => {
|
|
23
|
+
reject(new MafdetClientError("network", "could not reach Mafdet AI (timed out)"));
|
|
24
|
+
void reader
|
|
25
|
+
?.cancel()
|
|
26
|
+
.catch(() => logError("response cleanup failed after timeout"));
|
|
27
|
+
controller.abort();
|
|
28
|
+
}, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
29
|
+
});
|
|
30
|
+
const request = async () => {
|
|
31
|
+
let response;
|
|
39
32
|
try {
|
|
40
|
-
|
|
33
|
+
const headers = { Accept: "application/json" };
|
|
34
|
+
if (options.authKey) {
|
|
35
|
+
headers.Authorization = `Bearer ${options.authKey}`;
|
|
36
|
+
}
|
|
37
|
+
response = await fetch(url, {
|
|
38
|
+
method: "GET",
|
|
39
|
+
headers,
|
|
40
|
+
signal: controller.signal,
|
|
41
|
+
redirect: "error",
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok)
|
|
44
|
+
throw mapStatus(response.status);
|
|
45
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
46
|
+
if (!contentType.includes("application/json")) {
|
|
47
|
+
throw new MafdetClientError("bad_response", "Mafdet AI returned an unexpected (non-JSON) response");
|
|
48
|
+
}
|
|
49
|
+
const text = await readBounded(response, (value) => {
|
|
50
|
+
reader = value;
|
|
51
|
+
});
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(text);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new MafdetClientError("bad_response", "Mafdet AI returned malformed JSON");
|
|
57
|
+
}
|
|
41
58
|
}
|
|
42
|
-
catch {
|
|
43
|
-
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error instanceof MafdetClientError)
|
|
61
|
+
throw error;
|
|
62
|
+
throw new MafdetClientError("network", `could not reach Mafdet AI (${describeNetworkError(error)})`);
|
|
44
63
|
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
throw error;
|
|
49
|
-
throw new MafdetClientError("network", `could not reach Mafdet AI (${describeNetworkError(error)})`);
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
return await Promise.race([request(), deadline]);
|
|
50
67
|
}
|
|
51
68
|
finally {
|
|
52
69
|
clearTimeout(timer);
|
|
@@ -76,11 +93,12 @@ function mapStatus(status) {
|
|
|
76
93
|
return new MafdetClientError("unavailable", `Mafdet AI is temporarily unavailable (HTTP ${status})`);
|
|
77
94
|
}
|
|
78
95
|
// Read the response body but never buffer more than the cap.
|
|
79
|
-
async function readBounded(response) {
|
|
96
|
+
async function readBounded(response, onReader) {
|
|
80
97
|
const reader = response.body?.getReader();
|
|
81
98
|
if (!reader) {
|
|
82
99
|
return "";
|
|
83
100
|
}
|
|
101
|
+
onReader(reader);
|
|
84
102
|
const decoder = new TextDecoder();
|
|
85
103
|
let total = 0;
|
|
86
104
|
let out = "";
|
package/dist/index.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mafdet/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Mafdet AI MCP server — read-only account tools (models, balance, usage, subscription, docs) for Claude Code, Cursor, Codex, OpenCode, and Claude Desktop.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,12 +33,6 @@
|
|
|
33
33
|
"dist",
|
|
34
34
|
"README.md"
|
|
35
35
|
],
|
|
36
|
-
"scripts": {
|
|
37
|
-
"build": "tsc -p tsconfig.json",
|
|
38
|
-
"lint": "prettier --check \"src/**/*.ts\" \"test/**/*.mjs\" package.json tsconfig.json",
|
|
39
|
-
"test": "pnpm run build && node --test \"test/**/*.test.mjs\"",
|
|
40
|
-
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
41
|
-
},
|
|
42
36
|
"dependencies": {
|
|
43
37
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
44
38
|
"zod": "^4.6.7"
|
|
@@ -49,5 +43,11 @@
|
|
|
49
43
|
},
|
|
50
44
|
"engines": {
|
|
51
45
|
"node": ">=20"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.json",
|
|
49
|
+
"lint": "prettier --check \"src/**/*.ts\" \"test/**/*.mjs\" package.json tsconfig.json",
|
|
50
|
+
"test": "pnpm run build && node --test \"test/**/*.test.mjs\"",
|
|
51
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}
|