@mafdet/mcp 0.1.0 → 0.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAgBpE,wBAAgB,WAAW,CAAC,MAAM,kCAAe,GAAG,SAAS,CAmE5D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAgBpE,wBAAgB,WAAW,CAAC,MAAM,kCAAe,GAAG,SAAS,CAmE5D"}
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ import { realpathSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
2
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
6
  import { z } from "zod";
@@ -9,7 +11,7 @@ import { getBalance, getSubscription, getUsageSummary, listModels, searchDocsToo
9
11
  // it); everything diagnostic goes to stderr via logError. The five tools are
10
12
  // read-only; account tools need MAFDET_API_KEY, the two public tools do not.
11
13
  export function buildServer(config = loadConfig()) {
12
- const server = new McpServer({ name: "mafdet-mcp", version: "0.1.0" });
14
+ const server = new McpServer({ name: "mafdet-mcp", version: "0.1.2" });
13
15
  server.registerTool("list_models", {
14
16
  title: "List Mafdet AI models",
15
17
  description: "List the models available on Mafdet AI with provider, tier, and per-1M-token pricing. No API key required.",
@@ -55,8 +57,22 @@ async function main() {
55
57
  process.on("SIGINT", shutdown);
56
58
  process.on("SIGTERM", shutdown);
57
59
  }
58
- // Only run when invoked as the binary, not when imported by tests.
59
- if (process.argv[1] && import.meta.url === `file://${process.argv[1]}`) {
60
+ // Only run when invoked as the binary, not when imported by tests. npm/npx run
61
+ // the bin through a symlink (`node_modules/.bin/mafdet-mcp`), so process.argv[1]
62
+ // is the symlink path while import.meta.url is the resolved file — compare their
63
+ // real paths, or the server would silently never start when launched via npx.
64
+ function isInvokedAsBinary() {
65
+ const entry = process.argv[1];
66
+ if (!entry)
67
+ return false;
68
+ try {
69
+ return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ }
75
+ if (isInvokedAsBinary()) {
60
76
  main().catch((error) => {
61
77
  logError(error instanceof Error ? error.message : String(error));
62
78
  process.exit(1);
package/dist/tools.d.ts CHANGED
@@ -7,6 +7,7 @@ export interface ToolResult {
7
7
  }>;
8
8
  isError?: boolean;
9
9
  }
10
+ export declare function money(microString: string | undefined): string;
10
11
  export declare function listModels(config: McpConfig, args?: {
11
12
  provider?: string;
12
13
  tier?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAW7C,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAkCD,wBAAsB,UAAU,CAC9B,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9C,OAAO,CAAC,UAAU,CAAC,CAmCrB;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAiBvE;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3B,OAAO,CAAC,UAAU,CAAC,CAmBrB;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CA+B5E;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACtC,OAAO,CAAC,UAAU,CAAC,CAkBrB"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAW7C,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA6BD,wBAAgB,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAkB7D;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9C,OAAO,CAAC,UAAU,CAAC,CAmCrB;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAiBvE;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3B,OAAO,CAAC,UAAU,CAAC,CAmBrB;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CA+B5E;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACtC,OAAO,CAAC,UAAU,CAAC,CAkBrB"}
package/dist/tools.js CHANGED
@@ -19,12 +19,23 @@ function requireKey(config) {
19
19
  }
20
20
  return config.apiKey;
21
21
  }
22
- // micro-USD (integer string) -> "$0.28" style; keeps the raw value alongside.
23
- function usd(microString) {
24
- const micro = Number(microString ?? "0");
25
- if (!Number.isFinite(micro))
26
- return "$0.00";
27
- return `$${(micro / 1_000_000).toFixed(micro % 10_000 === 0 ? 2 : 4)}`;
22
+ // Formats integer micro-USD exactly. BigInt avoids precision loss for balances
23
+ // above Number.MAX_SAFE_INTEGER, and the raw amount remains visible to clients.
24
+ export function money(microString) {
25
+ const raw = microString ?? "0";
26
+ if (!/^-?\d+$/.test(raw)) {
27
+ throw new MafdetClientError("bad_response", "Mafdet returned an invalid money amount");
28
+ }
29
+ const micro = BigInt(raw);
30
+ const negative = micro < 0n;
31
+ const absolute = negative ? -micro : micro;
32
+ const whole = absolute / 1000000n;
33
+ const fraction = (absolute % 1000000n)
34
+ .toString()
35
+ .padStart(6, "0")
36
+ .replace(/0+$/, "")
37
+ .padEnd(2, "0");
38
+ return `${negative ? "-" : ""}$${whole}.${fraction} (${raw} micro-USD)`;
28
39
  }
29
40
  export async function listModels(config, args = {}) {
30
41
  try {
@@ -53,7 +64,7 @@ export async function listModels(config, args = {}) {
53
64
  }
54
65
  const lines = models.map((m) => `- ${m.model_id} (${m.provider}, ${m.tier})` +
55
66
  `${m.trial ? " [trial]" : ""}${m.deprecated ? " [deprecated]" : ""}` +
56
- ` — in ${usd(m.input_per_1m_micro_usd)}/1M, out ${usd(m.output_per_1m_micro_usd)}/1M, ctx ${m.context_length}`);
67
+ ` — in ${money(m.input_per_1m_micro_usd)}/1M, out ${money(m.output_per_1m_micro_usd)}/1M, ctx ${m.context_length}`);
57
68
  return ok(`Mafdet AI models (${models.length}):\n${lines.join("\n")}`);
58
69
  }
59
70
  catch (error) {
@@ -70,9 +81,9 @@ export async function getBalance(config) {
70
81
  path: "/keyinfo/wallet",
71
82
  authKey: key,
72
83
  }));
73
- const text = `Available: ${usd(w.available_micro_usd)} ` +
74
- `(balance ${usd(w.balance_micro_usd)} + bonus ${usd(w.bonus_micro_usd)} ` +
75
- `− frozen ${usd(w.frozen_micro_usd)}), anchored in ${String(w.currency_anchor).toUpperCase()}.`;
84
+ const text = `Available: ${money(w.available_micro_usd)} ` +
85
+ `(balance ${money(w.balance_micro_usd)} + bonus ${money(w.bonus_micro_usd)} ` +
86
+ `− frozen ${money(w.frozen_micro_usd)}), anchored in ${String(w.currency_anchor).toUpperCase()}.`;
76
87
  return ok(text);
77
88
  }
78
89
  catch (error) {
@@ -93,7 +104,7 @@ export async function getUsageSummary(config, args = {}) {
93
104
  }));
94
105
  const text = `Last ${u.days} day(s): ${u.count} request(s), ` +
95
106
  `${u.input_tokens} in / ${u.output_tokens} out (${u.total_tokens} total tokens), ` +
96
- `charge ${usd(String(u.charge_micro_usd))}.`;
107
+ `charge ${money(String(u.charge_micro_usd))}.`;
97
108
  return ok(text);
98
109
  }
99
110
  catch (error) {
@@ -114,7 +125,7 @@ export async function getSubscription(config) {
114
125
  ? `Plan: ${s.plan_name} (${s.status ?? "unknown"})`
115
126
  : "No active subscription (default entitlement).";
116
127
  const pg = s.playground
117
- ? ` Playground credit: ${usd(s.playground.remaining_micro_usd)} left of ${usd(s.playground.allowance_micro_usd)}.`
128
+ ? ` Playground credit: ${money(s.playground.remaining_micro_usd)} left of ${money(s.playground.allowance_micro_usd)}; used ${money(s.playground.used_micro_usd)}.`
118
129
  : "";
119
130
  const period = s.current_period_end
120
131
  ? ` Period ends ${s.current_period_end}.`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mafdet/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
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",