@mafdet/mcp 1.5.1 → 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/README.md CHANGED
@@ -17,7 +17,7 @@ OpenAI API works by pointing its base URL and key at Mafdet AI.
17
17
  - Base URL: `https://api.mafdet.ai/v1`
18
18
  - Endpoints: `/chat/completions`, `/embeddings`, `/audio/speech`
19
19
  - Providers: Google (Gemini), Kimi (Moonshot), Qwen, DeepSeek
20
- - 30 models as of August 2026. The catalogue changes, so the authoritative list
20
+ - The catalogue changes; the authoritative model list
21
21
  with current prices is [`models.json`](https://docs.mafdet.ai/models.json)
22
22
  - Docs: <https://docs.mafdet.ai>
23
23
 
@@ -25,7 +25,7 @@ OpenAI API works by pointing its base URL and key at Mafdet AI.
25
25
 
26
26
  | Tool | Needs a key | What it does |
27
27
  | --- | --- | --- |
28
- | `list_models` | no | List Mafdet AI models with provider, tier, per-1M-token pricing, and a `[preview]` / `[beta]` marker for models the provider has not declared generally available |
28
+ | `list_models` | no | List Mafdet AI models with provider, tier, pricing per dimension (tokens, images, or seconds), and a `[preview]` / `[beta]` marker for models the provider has not declared generally available |
29
29
  | `search_docs` | no | Search the Mafdet AI documentation (titles, URLs, summaries) |
30
30
  | `get_balance` | yes | Your organization's wallet: balance, bonus, frozen, available |
31
31
  | `get_usage_summary` | yes | Requests, tokens, and charge over the last N days (1–90) |
@@ -100,7 +100,7 @@ Omit `MAFDET_API_KEY` to use only the public tools (`list_models`,
100
100
 
101
101
  ## Notes
102
102
 
103
- - Pin a major version (`@mafdet/mcp@0`) in automated setups to avoid surprise
103
+ - Pin a major version (`@mafdet/mcp@1`) in automated setups to avoid surprise
104
104
  upgrades.
105
105
  - The server writes only JSON-RPC to stdout; diagnostics go to stderr and never
106
106
  include your key.
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
- const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
20
- let response;
21
- try {
22
- const headers = { Accept: "application/json" };
23
- if (options.authKey) {
24
- headers.Authorization = `Bearer ${options.authKey}`;
25
- }
26
- response = await fetch(url, {
27
- method: "GET",
28
- headers,
29
- signal: controller.signal,
30
- redirect: "error",
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
- return JSON.parse(text);
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
- throw new MafdetClientError("bad_response", "Mafdet AI returned malformed JSON");
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
- catch (error) {
47
- if (error instanceof MafdetClientError)
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mafdet/mcp",
3
- "version": "1.5.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",