@aiwerk/mcp-bridge 2.6.7 → 2.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/bin/mcp-bridge.js +147 -3
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +1 -0
- package/dist/src/mcp-router.d.ts +2 -0
- package/dist/src/mcp-router.js +9 -0
- package/dist/src/rate-limiter.d.ts +29 -0
- package/dist/src/rate-limiter.js +178 -0
- package/dist/src/types.d.ts +4 -0
- package/package.json +1 -1
- package/servers/apify/recipe.json +15 -0
- package/servers/atlassian/recipe.json +17 -2
- package/servers/chrome-devtools/recipe.json +15 -0
- package/servers/firecrawl/recipe.json +79 -0
- package/servers/github/recipe.json +15 -0
- package/servers/google-maps/recipe.json +17 -2
- package/servers/hetzner/recipe.json +15 -0
- package/servers/hostinger/recipe.json +19 -3
- package/servers/imap-email/recipe.json +19 -2
- package/servers/linear/recipe.json +15 -0
- package/servers/miro/recipe.json +17 -2
- package/servers/notion/recipe.json +17 -2
- package/servers/stripe/recipe.json +17 -2
- package/servers/tavily/recipe.json +15 -0
- package/servers/todoist/recipe.json +17 -2
- package/servers/wise/recipe.json +15 -0
package/dist/bin/mcp-bridge.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync, existsSync } from "fs";
|
|
3
|
-
import { join, dirname, resolve } from "path";
|
|
2
|
+
import { readFileSync, existsSync, writeFileSync } from "fs";
|
|
3
|
+
import { join, dirname, resolve, extname } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
|
-
import { platform } from "os";
|
|
5
|
+
import { platform, homedir } from "os";
|
|
6
6
|
import { execFileSync } from "child_process";
|
|
7
7
|
import { loadConfig, initConfigDir } from "../src/config.js";
|
|
8
8
|
import { StandaloneServer } from "../src/standalone-server.js";
|
|
@@ -10,6 +10,7 @@ import { PACKAGE_VERSION } from "../src/protocol.js";
|
|
|
10
10
|
import { checkForUpdate, runUpdate } from "../src/update-checker.js";
|
|
11
11
|
import { FileTokenStore } from "../src/token-store.js";
|
|
12
12
|
import { performAuthCodeLogin, performDeviceCodeLogin } from "../src/cli-auth.js";
|
|
13
|
+
import { RateLimiter } from "../src/rate-limiter.js";
|
|
13
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
15
|
const __dirname = dirname(__filename);
|
|
15
16
|
// After tsc, this file lives at dist/bin/mcp-bridge.js.
|
|
@@ -96,6 +97,22 @@ function parseArgs(argv) {
|
|
|
96
97
|
case "--offline":
|
|
97
98
|
args.offline = true;
|
|
98
99
|
break;
|
|
100
|
+
case "--daily":
|
|
101
|
+
i++;
|
|
102
|
+
args.daily = parseInt(argv[i], 10);
|
|
103
|
+
if (isNaN(args.daily)) {
|
|
104
|
+
process.stderr.write("Error: --daily requires a number\n");
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
case "--monthly":
|
|
109
|
+
i++;
|
|
110
|
+
args.monthly = parseInt(argv[i], 10);
|
|
111
|
+
if (isNaN(args.monthly)) {
|
|
112
|
+
process.stderr.write("Error: --monthly requires a number\n");
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
99
116
|
case "init":
|
|
100
117
|
args.command = "init";
|
|
101
118
|
break;
|
|
@@ -114,6 +131,12 @@ function parseArgs(argv) {
|
|
|
114
131
|
case "update":
|
|
115
132
|
args.command = "update";
|
|
116
133
|
break;
|
|
134
|
+
case "usage":
|
|
135
|
+
args.command = "usage";
|
|
136
|
+
break;
|
|
137
|
+
case "limit":
|
|
138
|
+
args.command = "limit";
|
|
139
|
+
break;
|
|
117
140
|
case "auth":
|
|
118
141
|
args.command = "auth";
|
|
119
142
|
// Consume subcommand
|
|
@@ -152,6 +175,9 @@ Usage:
|
|
|
152
175
|
mcp-bridge catalog [--offline] List available servers
|
|
153
176
|
mcp-bridge servers List configured servers
|
|
154
177
|
mcp-bridge search <query> Search catalog by keyword
|
|
178
|
+
mcp-bridge usage Show current per-server call usage
|
|
179
|
+
mcp-bridge limit <server> [--daily N] [--monthly N]
|
|
180
|
+
Set per-server rate limits (0 = unlimited)
|
|
155
181
|
mcp-bridge update [--check] Check for / install updates
|
|
156
182
|
mcp-bridge auth login <server> Authenticate with an OAuth2 server
|
|
157
183
|
mcp-bridge auth logout <server> Remove stored token for a server
|
|
@@ -235,6 +261,118 @@ function cmdSearch(query, logger) {
|
|
|
235
261
|
});
|
|
236
262
|
process.stdout.write("\n");
|
|
237
263
|
}
|
|
264
|
+
function resolveConfigPath(configPath) {
|
|
265
|
+
if (!configPath) {
|
|
266
|
+
return join(homedir(), ".mcp-bridge", "config.json");
|
|
267
|
+
}
|
|
268
|
+
if (configPath.endsWith("/") || configPath.endsWith("\\") || !extname(configPath)) {
|
|
269
|
+
return join(configPath, "config.json");
|
|
270
|
+
}
|
|
271
|
+
return configPath;
|
|
272
|
+
}
|
|
273
|
+
function cmdUsage(configPath, logger) {
|
|
274
|
+
try {
|
|
275
|
+
const limiter = new RateLimiter();
|
|
276
|
+
const usage = limiter.getAllUsage();
|
|
277
|
+
let configServers = {};
|
|
278
|
+
try {
|
|
279
|
+
const config = loadConfig({ configPath, logger });
|
|
280
|
+
configServers = config.servers ?? {};
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// Show usage files even when config is missing/unreadable.
|
|
284
|
+
}
|
|
285
|
+
const names = new Set([...Object.keys(usage), ...Object.keys(configServers)]);
|
|
286
|
+
if (names.size === 0) {
|
|
287
|
+
process.stdout.write("No usage data found.\n");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
process.stdout.write("\nRate limit usage (note: cached calls are not counted):\n\n");
|
|
291
|
+
process.stdout.write(" Server Daily Monthly Limits\n");
|
|
292
|
+
process.stdout.write(" " + "─".repeat(78) + "\n");
|
|
293
|
+
for (const server of [...names].sort((a, b) => a.localeCompare(b))) {
|
|
294
|
+
const counts = usage[server] ?? { daily: 0, monthly: 0 };
|
|
295
|
+
const limit = configServers[server]?.rateLimit;
|
|
296
|
+
const dailyLimit = typeof limit?.maxCallsPerDay === "number" && limit.maxCallsPerDay > 0
|
|
297
|
+
? limit.maxCallsPerDay
|
|
298
|
+
: "-";
|
|
299
|
+
const monthlyLimit = typeof limit?.maxCallsPerMonth === "number" && limit.maxCallsPerMonth > 0
|
|
300
|
+
? limit.maxCallsPerMonth
|
|
301
|
+
: "-";
|
|
302
|
+
process.stdout.write(` ${server.padEnd(16)}${`${counts.daily}`.padEnd(13)}${`${counts.monthly}`.padEnd(13)}daily=${dailyLimit} monthly=${monthlyLimit}\n`);
|
|
303
|
+
}
|
|
304
|
+
process.stdout.write("\n");
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function cmdLimit(args, logger) {
|
|
312
|
+
const server = args.positional[0];
|
|
313
|
+
if (!server) {
|
|
314
|
+
process.stderr.write("Usage: mcp-bridge limit <server> --daily <n> --monthly <n>\n");
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
const hasDaily = typeof args.daily === "number";
|
|
318
|
+
const hasMonthly = typeof args.monthly === "number";
|
|
319
|
+
if (!hasDaily && !hasMonthly) {
|
|
320
|
+
process.stderr.write("Error: provide at least one limit flag (--daily or --monthly)\n");
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
if (hasDaily && args.daily < 0) {
|
|
324
|
+
process.stderr.write("Error: --daily must be >= 0\n");
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
if (hasMonthly && args.monthly < 0) {
|
|
328
|
+
process.stderr.write("Error: --monthly must be >= 0\n");
|
|
329
|
+
process.exit(1);
|
|
330
|
+
}
|
|
331
|
+
const resolvedPath = resolveConfigPath(args.configPath);
|
|
332
|
+
if (!existsSync(resolvedPath)) {
|
|
333
|
+
logger.error(`Config file not found: ${resolvedPath}`);
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
const raw = JSON.parse(readFileSync(resolvedPath, "utf-8"));
|
|
338
|
+
if (!raw.servers || typeof raw.servers !== "object" || !raw.servers[server]) {
|
|
339
|
+
logger.error(`Server "${server}" not found in config`);
|
|
340
|
+
process.exit(1);
|
|
341
|
+
}
|
|
342
|
+
if (!raw.servers[server].rateLimit || typeof raw.servers[server].rateLimit !== "object") {
|
|
343
|
+
raw.servers[server].rateLimit = {};
|
|
344
|
+
}
|
|
345
|
+
if (hasDaily) {
|
|
346
|
+
if (args.daily === 0) {
|
|
347
|
+
delete raw.servers[server].rateLimit.maxCallsPerDay;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
raw.servers[server].rateLimit.maxCallsPerDay = args.daily;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (hasMonthly) {
|
|
354
|
+
if (args.monthly === 0) {
|
|
355
|
+
delete raw.servers[server].rateLimit.maxCallsPerMonth;
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
raw.servers[server].rateLimit.maxCallsPerMonth = args.monthly;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (raw.servers[server].rateLimit.maxCallsPerDay === undefined &&
|
|
362
|
+
raw.servers[server].rateLimit.maxCallsPerMonth === undefined) {
|
|
363
|
+
delete raw.servers[server].rateLimit;
|
|
364
|
+
}
|
|
365
|
+
writeFileSync(resolvedPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
|
|
366
|
+
const effectiveDaily = raw.servers[server].rateLimit?.maxCallsPerDay ?? "unlimited";
|
|
367
|
+
const effectiveMonthly = raw.servers[server].rateLimit?.maxCallsPerMonth ?? "unlimited";
|
|
368
|
+
process.stdout.write(`Updated limits for ${server}: daily=${effectiveDaily}, monthly=${effectiveMonthly}\n` +
|
|
369
|
+
`Check usage with: mcp-bridge usage\n`);
|
|
370
|
+
}
|
|
371
|
+
catch (err) {
|
|
372
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
238
376
|
function cmdInstall(serverName, logger) {
|
|
239
377
|
const scriptDir = join(PACKAGE_ROOT, "scripts");
|
|
240
378
|
try {
|
|
@@ -460,6 +598,12 @@ async function main() {
|
|
|
460
598
|
}
|
|
461
599
|
cmdSearch(args.positional[0], logger);
|
|
462
600
|
break;
|
|
601
|
+
case "usage":
|
|
602
|
+
cmdUsage(args.configPath, logger);
|
|
603
|
+
break;
|
|
604
|
+
case "limit":
|
|
605
|
+
cmdLimit(args, logger);
|
|
606
|
+
break;
|
|
463
607
|
case "install":
|
|
464
608
|
if (args.positional.length === 0) {
|
|
465
609
|
process.stderr.write("Usage: mcp-bridge install <server>\n");
|
package/dist/src/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export { McpRouter } from "./mcp-router.js";
|
|
|
12
12
|
export type { RouterToolHint, RouterServerStatus, RouterDispatchResponse, RouterTransportRefs } from "./mcp-router.js";
|
|
13
13
|
export { ResultCache, createResultCacheKey, stableStringify } from "./result-cache.js";
|
|
14
14
|
export type { ResultCacheConfig, ResultCacheStats } from "./result-cache.js";
|
|
15
|
+
export { RateLimiter } from "./rate-limiter.js";
|
|
16
|
+
export type { RateLimitConfig, RateLimitResult } from "./rate-limiter.js";
|
|
15
17
|
export { ToolResolver } from "./tool-resolution.js";
|
|
16
18
|
export type { ToolResolutionResult, ToolResolutionCandidate } from "./tool-resolution.js";
|
|
17
19
|
export { convertJsonSchemaToTypeBox, createToolParameters, setTypeBoxLoader, setSchemaLogger } from "./schema-convert.js";
|
package/dist/src/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export { performAuthCodeLogin, generateCodeVerifier, computeCodeChallenge } from
|
|
|
13
13
|
export { McpRouter } from "./mcp-router.js";
|
|
14
14
|
// Result cache
|
|
15
15
|
export { ResultCache, createResultCacheKey, stableStringify } from "./result-cache.js";
|
|
16
|
+
export { RateLimiter } from "./rate-limiter.js";
|
|
16
17
|
export { ToolResolver } from "./tool-resolution.js";
|
|
17
18
|
// Schema conversion
|
|
18
19
|
export { convertJsonSchemaToTypeBox, createToolParameters, setTypeBoxLoader, setSchemaLogger } from "./schema-convert.js";
|
package/dist/src/mcp-router.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export type RouterDispatchResponse = {
|
|
|
39
39
|
tool: string;
|
|
40
40
|
result: any;
|
|
41
41
|
retries?: number;
|
|
42
|
+
warning?: string;
|
|
42
43
|
} | {
|
|
43
44
|
server: string;
|
|
44
45
|
action: "schema";
|
|
@@ -110,6 +111,7 @@ export declare class McpRouter {
|
|
|
110
111
|
private readonly states;
|
|
111
112
|
private readonly toolResolver;
|
|
112
113
|
private readonly tokenManager;
|
|
114
|
+
private readonly rateLimiter;
|
|
113
115
|
private readonly requestIdState;
|
|
114
116
|
private intentRouter;
|
|
115
117
|
private promotion;
|
package/dist/src/mcp-router.js
CHANGED
|
@@ -12,6 +12,7 @@ import { ResultCache, createResultCacheKey } from "./result-cache.js";
|
|
|
12
12
|
import { ToolResolver } from "./tool-resolution.js";
|
|
13
13
|
import { OAuth2TokenManager } from "./oauth2-token-manager.js";
|
|
14
14
|
import { FileTokenStore } from "./token-store.js";
|
|
15
|
+
import { RateLimiter } from "./rate-limiter.js";
|
|
15
16
|
const DEFAULT_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
16
17
|
const DEFAULT_CONNECT_ERROR_COOLDOWN_MS = 10 * 1000;
|
|
17
18
|
const DEFAULT_MAX_CONCURRENT = 5;
|
|
@@ -30,6 +31,7 @@ export class McpRouter {
|
|
|
30
31
|
states = new Map();
|
|
31
32
|
toolResolver;
|
|
32
33
|
tokenManager;
|
|
34
|
+
rateLimiter;
|
|
33
35
|
requestIdState = { value: 0 };
|
|
34
36
|
intentRouter = null;
|
|
35
37
|
promotion = null;
|
|
@@ -55,6 +57,7 @@ export class McpRouter {
|
|
|
55
57
|
this.maxBatchSize = clientConfig.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
|
|
56
58
|
this.toolResolver = new ToolResolver(Object.keys(servers));
|
|
57
59
|
this.tokenManager = new OAuth2TokenManager(logger, new FileTokenStore());
|
|
60
|
+
this.rateLimiter = new RateLimiter();
|
|
58
61
|
if (clientConfig.adaptivePromotion?.enabled) {
|
|
59
62
|
this.promotion = new AdaptivePromotion(clientConfig.adaptivePromotion, logger);
|
|
60
63
|
}
|
|
@@ -249,6 +252,11 @@ export class McpRouter {
|
|
|
249
252
|
return { server, action: "call", tool, result: cachedResult };
|
|
250
253
|
}
|
|
251
254
|
}
|
|
255
|
+
// Rate limit check BEFORE markUsed — rejected calls should not keep connection alive
|
|
256
|
+
const rateLimitResult = this.rateLimiter.checkAndIncrement(server, serverConfig.rateLimit);
|
|
257
|
+
if (!rateLimitResult.allowed) {
|
|
258
|
+
return this.error("mcp_error", rateLimitResult.error || "Rate limit reached");
|
|
259
|
+
}
|
|
252
260
|
this.markUsed(server);
|
|
253
261
|
const callOutcome = await this.callToolWithRetry(server, tool, params ?? {}, state.transport);
|
|
254
262
|
const response = callOutcome.response;
|
|
@@ -270,6 +278,7 @@ export class McpRouter {
|
|
|
270
278
|
action: "call",
|
|
271
279
|
tool,
|
|
272
280
|
result,
|
|
281
|
+
...(rateLimitResult.warning ? { warning: rateLimitResult.warning } : {}),
|
|
273
282
|
...(callOutcome.retries > 0 ? { retries: callOutcome.retries } : {})
|
|
274
283
|
};
|
|
275
284
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface RateLimitConfig {
|
|
2
|
+
maxCallsPerDay?: number;
|
|
3
|
+
maxCallsPerMonth?: number;
|
|
4
|
+
}
|
|
5
|
+
export interface RateLimitResult {
|
|
6
|
+
allowed: boolean;
|
|
7
|
+
warning?: string;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class RateLimiter {
|
|
11
|
+
private readonly usageDir;
|
|
12
|
+
constructor(usageDir?: string);
|
|
13
|
+
checkLimit(serverId: string, config?: RateLimitConfig): RateLimitResult;
|
|
14
|
+
checkAndIncrement(serverId: string, config?: RateLimitConfig): RateLimitResult;
|
|
15
|
+
getUsage(serverId: string): {
|
|
16
|
+
daily: number;
|
|
17
|
+
monthly: number;
|
|
18
|
+
};
|
|
19
|
+
getAllUsage(): Record<string, {
|
|
20
|
+
daily: number;
|
|
21
|
+
monthly: number;
|
|
22
|
+
dailyLimit?: number;
|
|
23
|
+
monthlyLimit?: number;
|
|
24
|
+
}>;
|
|
25
|
+
reset(serverId: string): void;
|
|
26
|
+
private loadUsage;
|
|
27
|
+
private saveUsage;
|
|
28
|
+
private serverFilePath;
|
|
29
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
function utcDate(now) {
|
|
5
|
+
return now.toISOString().slice(0, 10);
|
|
6
|
+
}
|
|
7
|
+
function utcMonth(now) {
|
|
8
|
+
return now.toISOString().slice(0, 7);
|
|
9
|
+
}
|
|
10
|
+
function isPositiveLimit(value) {
|
|
11
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
12
|
+
}
|
|
13
|
+
function warningThreshold(limit) {
|
|
14
|
+
return Math.max(1, Math.ceil(limit * 0.8));
|
|
15
|
+
}
|
|
16
|
+
function nextSuggestedLimit(limit) {
|
|
17
|
+
return Math.ceil(limit * 1.5);
|
|
18
|
+
}
|
|
19
|
+
export class RateLimiter {
|
|
20
|
+
usageDir;
|
|
21
|
+
constructor(usageDir) {
|
|
22
|
+
this.usageDir = usageDir ?? join(homedir(), ".mcp-bridge", "usage");
|
|
23
|
+
}
|
|
24
|
+
checkLimit(serverId, config) {
|
|
25
|
+
const dailyLimit = isPositiveLimit(config?.maxCallsPerDay) ? config.maxCallsPerDay : undefined;
|
|
26
|
+
const monthlyLimit = isPositiveLimit(config?.maxCallsPerMonth) ? config.maxCallsPerMonth : undefined;
|
|
27
|
+
if (!dailyLimit && !monthlyLimit) {
|
|
28
|
+
return { allowed: true };
|
|
29
|
+
}
|
|
30
|
+
const { usage, changed } = this.loadUsage(serverId);
|
|
31
|
+
if (changed) {
|
|
32
|
+
this.saveUsage(serverId, usage);
|
|
33
|
+
}
|
|
34
|
+
if (dailyLimit && usage.daily.count >= dailyLimit) {
|
|
35
|
+
return {
|
|
36
|
+
allowed: false,
|
|
37
|
+
error: `❌ Rate limit reached for ${serverId}: ${usage.daily.count}/${dailyLimit} daily calls used. Resets at midnight UTC. To adjust: mcp-bridge limit ${serverId} --daily ${nextSuggestedLimit(dailyLimit)}. To check usage: mcp-bridge usage. To disable limit: mcp-bridge limit ${serverId} --daily 0`
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (monthlyLimit && usage.monthly.count >= monthlyLimit) {
|
|
41
|
+
return {
|
|
42
|
+
allowed: false,
|
|
43
|
+
error: `❌ Rate limit reached for ${serverId}: ${usage.monthly.count}/${monthlyLimit} monthly calls used. Resets on the 1st of each month at midnight UTC. To adjust: mcp-bridge limit ${serverId} --monthly ${nextSuggestedLimit(monthlyLimit)}. To check usage: mcp-bridge usage. To disable limit: mcp-bridge limit ${serverId} --monthly 0`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { allowed: true };
|
|
47
|
+
}
|
|
48
|
+
checkAndIncrement(serverId, config) {
|
|
49
|
+
const dailyLimit = isPositiveLimit(config?.maxCallsPerDay) ? config.maxCallsPerDay : undefined;
|
|
50
|
+
const monthlyLimit = isPositiveLimit(config?.maxCallsPerMonth) ? config.maxCallsPerMonth : undefined;
|
|
51
|
+
if (!dailyLimit && !monthlyLimit) {
|
|
52
|
+
return { allowed: true };
|
|
53
|
+
}
|
|
54
|
+
// Single loadUsage call — check + increment in one pass (avoids double file read)
|
|
55
|
+
const { usage, changed } = this.loadUsage(serverId);
|
|
56
|
+
if (changed) {
|
|
57
|
+
this.saveUsage(serverId, usage);
|
|
58
|
+
}
|
|
59
|
+
if (dailyLimit && usage.daily.count >= dailyLimit) {
|
|
60
|
+
return {
|
|
61
|
+
allowed: false,
|
|
62
|
+
error: `❌ Rate limit reached for ${serverId}: ${usage.daily.count}/${dailyLimit} daily calls used. Resets at midnight UTC. To adjust: mcp-bridge limit ${serverId} --daily ${nextSuggestedLimit(dailyLimit)}. To check usage: mcp-bridge usage. To disable limit: mcp-bridge limit ${serverId} --daily 0`
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (monthlyLimit && usage.monthly.count >= monthlyLimit) {
|
|
66
|
+
return {
|
|
67
|
+
allowed: false,
|
|
68
|
+
error: `❌ Rate limit reached for ${serverId}: ${usage.monthly.count}/${monthlyLimit} monthly calls used. Resets on the 1st of each month at midnight UTC. To adjust: mcp-bridge limit ${serverId} --monthly ${nextSuggestedLimit(monthlyLimit)}. To check usage: mcp-bridge usage. To disable limit: mcp-bridge limit ${serverId} --monthly 0`
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
usage.daily.count += 1;
|
|
72
|
+
usage.monthly.count += 1;
|
|
73
|
+
this.saveUsage(serverId, usage);
|
|
74
|
+
if (dailyLimit && usage.daily.count >= warningThreshold(dailyLimit) && usage.daily.count < dailyLimit) {
|
|
75
|
+
return {
|
|
76
|
+
allowed: true,
|
|
77
|
+
warning: `⚠️ ${serverId}: 80% of daily limit used (${usage.daily.count}/${dailyLimit}). Adjust with: mcp-bridge limit ${serverId} --daily <number>`
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (monthlyLimit && usage.monthly.count >= warningThreshold(monthlyLimit) && usage.monthly.count < monthlyLimit) {
|
|
81
|
+
return {
|
|
82
|
+
allowed: true,
|
|
83
|
+
warning: `⚠️ ${serverId}: 80% of monthly limit used (${usage.monthly.count}/${monthlyLimit}). Adjust with: mcp-bridge limit ${serverId} --monthly <number>`
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return { allowed: true };
|
|
87
|
+
}
|
|
88
|
+
getUsage(serverId) {
|
|
89
|
+
const { usage, changed } = this.loadUsage(serverId);
|
|
90
|
+
if (changed) {
|
|
91
|
+
this.saveUsage(serverId, usage);
|
|
92
|
+
}
|
|
93
|
+
return { daily: usage.daily.count, monthly: usage.monthly.count };
|
|
94
|
+
}
|
|
95
|
+
getAllUsage() {
|
|
96
|
+
const all = {};
|
|
97
|
+
if (!existsSync(this.usageDir)) {
|
|
98
|
+
return all;
|
|
99
|
+
}
|
|
100
|
+
for (const fileName of readdirSync(this.usageDir)) {
|
|
101
|
+
if (!fileName.endsWith(".json"))
|
|
102
|
+
continue;
|
|
103
|
+
const encodedId = fileName.slice(0, -5);
|
|
104
|
+
let serverId;
|
|
105
|
+
try {
|
|
106
|
+
serverId = decodeURIComponent(encodedId);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
serverId = encodedId;
|
|
110
|
+
}
|
|
111
|
+
const { usage, changed } = this.loadUsage(serverId);
|
|
112
|
+
if (changed) {
|
|
113
|
+
this.saveUsage(serverId, usage);
|
|
114
|
+
}
|
|
115
|
+
all[serverId] = {
|
|
116
|
+
daily: usage.daily.count,
|
|
117
|
+
monthly: usage.monthly.count
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return all;
|
|
121
|
+
}
|
|
122
|
+
reset(serverId) {
|
|
123
|
+
const now = new Date();
|
|
124
|
+
this.saveUsage(serverId, {
|
|
125
|
+
daily: { date: utcDate(now), count: 0 },
|
|
126
|
+
monthly: { month: utcMonth(now), count: 0 }
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
loadUsage(serverId) {
|
|
130
|
+
const now = new Date();
|
|
131
|
+
const expectedDate = utcDate(now);
|
|
132
|
+
const expectedMonth = utcMonth(now);
|
|
133
|
+
const filePath = this.serverFilePath(serverId);
|
|
134
|
+
const fallback = {
|
|
135
|
+
daily: { date: expectedDate, count: 0 },
|
|
136
|
+
monthly: { month: expectedMonth, count: 0 }
|
|
137
|
+
};
|
|
138
|
+
if (!existsSync(filePath)) {
|
|
139
|
+
return { usage: fallback, changed: false };
|
|
140
|
+
}
|
|
141
|
+
let parsed;
|
|
142
|
+
try {
|
|
143
|
+
parsed = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return { usage: fallback, changed: true };
|
|
147
|
+
}
|
|
148
|
+
const dailyRaw = typeof parsed === "object" && parsed !== null ? parsed.daily : undefined;
|
|
149
|
+
const monthlyRaw = typeof parsed === "object" && parsed !== null ? parsed.monthly : undefined;
|
|
150
|
+
let changed = false;
|
|
151
|
+
const dailyDate = typeof dailyRaw?.date === "string" ? dailyRaw.date : expectedDate;
|
|
152
|
+
const dailyCount = typeof dailyRaw?.count === "number" && dailyRaw.count >= 0 ? dailyRaw.count : 0;
|
|
153
|
+
const monthlyMonth = typeof monthlyRaw?.month === "string" ? monthlyRaw.month : expectedMonth;
|
|
154
|
+
const monthlyCount = typeof monthlyRaw?.count === "number" && monthlyRaw.count >= 0 ? monthlyRaw.count : 0;
|
|
155
|
+
const usage = {
|
|
156
|
+
daily: { date: dailyDate, count: dailyCount },
|
|
157
|
+
monthly: { month: monthlyMonth, count: monthlyCount }
|
|
158
|
+
};
|
|
159
|
+
if (usage.daily.date !== expectedDate) {
|
|
160
|
+
usage.daily.date = expectedDate;
|
|
161
|
+
usage.daily.count = 0;
|
|
162
|
+
changed = true;
|
|
163
|
+
}
|
|
164
|
+
if (usage.monthly.month !== expectedMonth) {
|
|
165
|
+
usage.monthly.month = expectedMonth;
|
|
166
|
+
usage.monthly.count = 0;
|
|
167
|
+
changed = true;
|
|
168
|
+
}
|
|
169
|
+
return { usage, changed };
|
|
170
|
+
}
|
|
171
|
+
saveUsage(serverId, usage) {
|
|
172
|
+
mkdirSync(this.usageDir, { recursive: true });
|
|
173
|
+
writeFileSync(this.serverFilePath(serverId), JSON.stringify(usage, null, 2) + "\n", "utf-8");
|
|
174
|
+
}
|
|
175
|
+
serverFilePath(serverId) {
|
|
176
|
+
return join(this.usageDir, `${encodeURIComponent(serverId)}.json`);
|
|
177
|
+
}
|
|
178
|
+
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -59,6 +59,10 @@ export interface McpServerConfig {
|
|
|
59
59
|
};
|
|
60
60
|
maxResultChars?: number;
|
|
61
61
|
retry?: RetryConfig;
|
|
62
|
+
rateLimit?: {
|
|
63
|
+
maxCallsPerDay?: number;
|
|
64
|
+
maxCallsPerMonth?: number;
|
|
65
|
+
};
|
|
62
66
|
}
|
|
63
67
|
export interface McpClientConfig {
|
|
64
68
|
servers: Record<string, McpServerConfig>;
|
package/package.json
CHANGED
|
@@ -50,5 +50,20 @@
|
|
|
50
50
|
"resources": false,
|
|
51
51
|
"prompts": false,
|
|
52
52
|
"sampling": false
|
|
53
|
+
},
|
|
54
|
+
"signature": {
|
|
55
|
+
"algorithm": "ed25519",
|
|
56
|
+
"publisherId": "aiwerk",
|
|
57
|
+
"value": "2eHbBy6v0MMBTdnIusU9oNReuNjg1CKKA7iknkS7FxQNTuisjZFAW2x5DFjUgN9FqjWmlwwhSLnk/qhwfV42Cw==",
|
|
58
|
+
"signedFields": [
|
|
59
|
+
"id",
|
|
60
|
+
"name",
|
|
61
|
+
"description",
|
|
62
|
+
"transports",
|
|
63
|
+
"auth",
|
|
64
|
+
"install",
|
|
65
|
+
"metadata"
|
|
66
|
+
],
|
|
67
|
+
"signedAt": "2026-03-20T14:24:25.196Z"
|
|
53
68
|
}
|
|
54
69
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"type": "stdio",
|
|
10
10
|
"command": "uvx",
|
|
11
11
|
"args": [
|
|
12
|
-
"mcp-atlassian"
|
|
12
|
+
"mcp-atlassian@2.1.0"
|
|
13
13
|
],
|
|
14
14
|
"env": {
|
|
15
15
|
"CONFLUENCE_URL": "${CONFLUENCE_URL}",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"install": {
|
|
39
39
|
"method": "uvx",
|
|
40
40
|
"package": "mcp-atlassian",
|
|
41
|
-
"version": "
|
|
41
|
+
"version": "2.1.0"
|
|
42
42
|
},
|
|
43
43
|
"metadata": {
|
|
44
44
|
"homepage": "https://github.com/sooperset/mcp-atlassian",
|
|
@@ -68,5 +68,20 @@
|
|
|
68
68
|
"resources": false,
|
|
69
69
|
"prompts": false,
|
|
70
70
|
"sampling": false
|
|
71
|
+
},
|
|
72
|
+
"signature": {
|
|
73
|
+
"algorithm": "ed25519",
|
|
74
|
+
"publisherId": "aiwerk",
|
|
75
|
+
"value": "ysHKVAb4IwPRJF3XtEEtumZC2xjt32ouGu5Dt3VrMl7DIp6PWb56JzewqLe4zJ14lVlksIgmDVrFCrVnVJ1QDw==",
|
|
76
|
+
"signedFields": [
|
|
77
|
+
"id",
|
|
78
|
+
"name",
|
|
79
|
+
"description",
|
|
80
|
+
"transports",
|
|
81
|
+
"auth",
|
|
82
|
+
"install",
|
|
83
|
+
"metadata"
|
|
84
|
+
],
|
|
85
|
+
"signedAt": "2026-03-20T14:24:29.672Z"
|
|
71
86
|
}
|
|
72
87
|
}
|
|
@@ -55,5 +55,20 @@
|
|
|
55
55
|
"resources": false,
|
|
56
56
|
"prompts": false,
|
|
57
57
|
"sampling": false
|
|
58
|
+
},
|
|
59
|
+
"signature": {
|
|
60
|
+
"algorithm": "ed25519",
|
|
61
|
+
"publisherId": "aiwerk",
|
|
62
|
+
"value": "fo/w/oYgd8T4ecaOnSUq3vE5nl82RVB31Mj+2v5W/j6qM+wK17xstVMdiZuiqH+a6YDQXXe30S9enRjLK0y/CA==",
|
|
63
|
+
"signedFields": [
|
|
64
|
+
"id",
|
|
65
|
+
"name",
|
|
66
|
+
"description",
|
|
67
|
+
"transports",
|
|
68
|
+
"auth",
|
|
69
|
+
"install",
|
|
70
|
+
"metadata"
|
|
71
|
+
],
|
|
72
|
+
"signedAt": "2026-03-20T14:24:32.876Z"
|
|
58
73
|
}
|
|
59
74
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 2,
|
|
3
|
+
"id": "firecrawl",
|
|
4
|
+
"name": "Firecrawl",
|
|
5
|
+
"description": "Web scraping, crawling, search, and structured data extraction — turn any website into clean markdown or structured data for AI agents",
|
|
6
|
+
"repository": "https://github.com/firecrawl/firecrawl-mcp-server",
|
|
7
|
+
"transports": [
|
|
8
|
+
{
|
|
9
|
+
"type": "stdio",
|
|
10
|
+
"command": "npx",
|
|
11
|
+
"args": [
|
|
12
|
+
"-y",
|
|
13
|
+
"firecrawl-mcp@3.12.1"
|
|
14
|
+
],
|
|
15
|
+
"env": {
|
|
16
|
+
"FIRECRAWL_API_KEY": "${FIRECRAWL_API_KEY}"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"auth": {
|
|
21
|
+
"required": true,
|
|
22
|
+
"type": "api-key",
|
|
23
|
+
"envVars": [
|
|
24
|
+
"FIRECRAWL_API_KEY"
|
|
25
|
+
],
|
|
26
|
+
"credentialsUrl": "https://www.firecrawl.dev/app/api-keys",
|
|
27
|
+
"instructions": "Sign up at firecrawl.dev and copy your API key from the dashboard."
|
|
28
|
+
},
|
|
29
|
+
"install": {
|
|
30
|
+
"method": "npx",
|
|
31
|
+
"package": "firecrawl-mcp",
|
|
32
|
+
"version": "3.12.1"
|
|
33
|
+
},
|
|
34
|
+
"metadata": {
|
|
35
|
+
"homepage": "https://www.firecrawl.dev/",
|
|
36
|
+
"author": "Firecrawl (Mendable)",
|
|
37
|
+
"tags": [
|
|
38
|
+
"scraping",
|
|
39
|
+
"crawling",
|
|
40
|
+
"web",
|
|
41
|
+
"extraction",
|
|
42
|
+
"search",
|
|
43
|
+
"markdown"
|
|
44
|
+
],
|
|
45
|
+
"category": "data",
|
|
46
|
+
"pricing": "byok",
|
|
47
|
+
"maturity": "stable",
|
|
48
|
+
"subcategory": "web-scraping",
|
|
49
|
+
"origin": "official",
|
|
50
|
+
"countries": [
|
|
51
|
+
"global"
|
|
52
|
+
],
|
|
53
|
+
"audience": "developer",
|
|
54
|
+
"selfHosted": true,
|
|
55
|
+
"sideEffects": "read-only",
|
|
56
|
+
"authSummary": "api-key"
|
|
57
|
+
},
|
|
58
|
+
"capabilities": {
|
|
59
|
+
"tools": true,
|
|
60
|
+
"resources": false,
|
|
61
|
+
"prompts": false,
|
|
62
|
+
"sampling": false
|
|
63
|
+
},
|
|
64
|
+
"signature": {
|
|
65
|
+
"algorithm": "ed25519",
|
|
66
|
+
"publisherId": "aiwerk",
|
|
67
|
+
"value": "s2daVrlfMhaE5+Tk/Y0CnXtG/S6qIVzP0/iHobQ4edC2RmfZFOfiaBUfOL+p6745VSoiiO8nMLDhuiRrUGQrDQ==",
|
|
68
|
+
"signedFields": [
|
|
69
|
+
"id",
|
|
70
|
+
"name",
|
|
71
|
+
"description",
|
|
72
|
+
"transports",
|
|
73
|
+
"auth",
|
|
74
|
+
"install",
|
|
75
|
+
"metadata"
|
|
76
|
+
],
|
|
77
|
+
"signedAt": "2026-03-20T14:24:35.540Z"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -63,5 +63,20 @@
|
|
|
63
63
|
"resources": false,
|
|
64
64
|
"prompts": false,
|
|
65
65
|
"sampling": false
|
|
66
|
+
},
|
|
67
|
+
"signature": {
|
|
68
|
+
"algorithm": "ed25519",
|
|
69
|
+
"publisherId": "aiwerk",
|
|
70
|
+
"value": "PWJf2aV7iYDLZA9IXJilKO3tO7noz5yqw3U4Nghe/bIyflshLG3tAq0UqSMbN4L/J+c4CdAHiqAGLCm/eLTXBg==",
|
|
71
|
+
"signedFields": [
|
|
72
|
+
"id",
|
|
73
|
+
"name",
|
|
74
|
+
"description",
|
|
75
|
+
"transports",
|
|
76
|
+
"auth",
|
|
77
|
+
"install",
|
|
78
|
+
"metadata"
|
|
79
|
+
],
|
|
80
|
+
"signedAt": "2026-03-20T14:24:38.175Z"
|
|
66
81
|
}
|
|
67
82
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"command": "npx",
|
|
11
11
|
"args": [
|
|
12
12
|
"-y",
|
|
13
|
-
"@modelcontextprotocol/server-google-maps"
|
|
13
|
+
"@modelcontextprotocol/server-google-maps@0.6.2"
|
|
14
14
|
],
|
|
15
15
|
"env": {
|
|
16
16
|
"GOOGLE_MAPS_API_KEY": "${GOOGLE_MAPS_API_KEY}"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"install": {
|
|
30
30
|
"method": "npx",
|
|
31
31
|
"package": "@modelcontextprotocol/server-google-maps",
|
|
32
|
-
"version": "
|
|
32
|
+
"version": "0.6.2"
|
|
33
33
|
},
|
|
34
34
|
"metadata": {
|
|
35
35
|
"homepage": "https://developers.google.com/maps",
|
|
@@ -59,5 +59,20 @@
|
|
|
59
59
|
"resources": false,
|
|
60
60
|
"prompts": false,
|
|
61
61
|
"sampling": false
|
|
62
|
+
},
|
|
63
|
+
"signature": {
|
|
64
|
+
"algorithm": "ed25519",
|
|
65
|
+
"publisherId": "aiwerk",
|
|
66
|
+
"value": "nc+tSwZmwP+MHs9z8VtyL5ZNf/iAsItoHdlygRezoZ5TjR2H++3nOlMmvp5JKC+Fq61P3iYFDRXTM0/VO6a3DA==",
|
|
67
|
+
"signedFields": [
|
|
68
|
+
"id",
|
|
69
|
+
"name",
|
|
70
|
+
"description",
|
|
71
|
+
"transports",
|
|
72
|
+
"auth",
|
|
73
|
+
"install",
|
|
74
|
+
"metadata"
|
|
75
|
+
],
|
|
76
|
+
"signedAt": "2026-03-20T14:24:43.129Z"
|
|
62
77
|
}
|
|
63
78
|
}
|
|
@@ -59,5 +59,20 @@
|
|
|
59
59
|
"resources": false,
|
|
60
60
|
"prompts": false,
|
|
61
61
|
"sampling": false
|
|
62
|
+
},
|
|
63
|
+
"signature": {
|
|
64
|
+
"algorithm": "ed25519",
|
|
65
|
+
"publisherId": "aiwerk",
|
|
66
|
+
"value": "WX6LE6XzBdkVW/uDv7MsUBT6GBE0JYNkfzNFoQqL59KFtivMXq5LmNvxL2JmyF0PlhulmQl0NGf09cSp8QagAw==",
|
|
67
|
+
"signedFields": [
|
|
68
|
+
"id",
|
|
69
|
+
"name",
|
|
70
|
+
"description",
|
|
71
|
+
"transports",
|
|
72
|
+
"auth",
|
|
73
|
+
"install",
|
|
74
|
+
"metadata"
|
|
75
|
+
],
|
|
76
|
+
"signedAt": "2026-03-20T14:24:45.822Z"
|
|
62
77
|
}
|
|
63
78
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"command": "npx",
|
|
10
10
|
"args": [
|
|
11
11
|
"-y",
|
|
12
|
-
"
|
|
12
|
+
"hostinger-api-mcp@0.1.28"
|
|
13
13
|
],
|
|
14
14
|
"env": {
|
|
15
15
|
"HOSTINGER_API_TOKEN": "${HOSTINGER_API_TOKEN}"
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
},
|
|
28
28
|
"install": {
|
|
29
29
|
"method": "npx",
|
|
30
|
-
"package": "
|
|
31
|
-
"version": "
|
|
30
|
+
"package": "hostinger-api-mcp",
|
|
31
|
+
"version": "0.1.28"
|
|
32
32
|
},
|
|
33
33
|
"metadata": {
|
|
34
34
|
"homepage": "https://www.hostinger.com/",
|
|
@@ -58,5 +58,21 @@
|
|
|
58
58
|
"resources": false,
|
|
59
59
|
"prompts": false,
|
|
60
60
|
"sampling": false
|
|
61
|
+
},
|
|
62
|
+
"repository": "https://github.com/hostinger/api-mcp-server",
|
|
63
|
+
"signature": {
|
|
64
|
+
"algorithm": "ed25519",
|
|
65
|
+
"publisherId": "aiwerk",
|
|
66
|
+
"value": "Btxn49bVCENtq5hgKDhSvYjU8KtbcxFKIAjclBfxl2W87YDFH68q2vkXzkqd1B9QHE74P0djp8KWt5hR0m7lBA==",
|
|
67
|
+
"signedFields": [
|
|
68
|
+
"id",
|
|
69
|
+
"name",
|
|
70
|
+
"description",
|
|
71
|
+
"transports",
|
|
72
|
+
"auth",
|
|
73
|
+
"install",
|
|
74
|
+
"metadata"
|
|
75
|
+
],
|
|
76
|
+
"signedAt": "2026-03-20T14:25:53.457Z"
|
|
61
77
|
}
|
|
62
78
|
}
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"command": "npx",
|
|
35
35
|
"args": [
|
|
36
36
|
"-y",
|
|
37
|
-
"@aiwerk/mcp-server-imap"
|
|
37
|
+
"@aiwerk/mcp-server-imap@1.1.8"
|
|
38
38
|
],
|
|
39
39
|
"env": {
|
|
40
40
|
"IMAP_HOST": "${IMAP_HOST}",
|
|
@@ -65,7 +65,8 @@
|
|
|
65
65
|
"install": {
|
|
66
66
|
"method": "npx",
|
|
67
67
|
"package": "@aiwerk/mcp-server-imap",
|
|
68
|
-
"version": "
|
|
68
|
+
"version": "1.1.8",
|
|
69
|
+
"toolsHash": "sha256-6ae071b777345740e7edaf319c71b57e65c657c145b68b96e6f6bcd6c0c1730a"
|
|
69
70
|
},
|
|
70
71
|
"capabilities": {
|
|
71
72
|
"toolCount": 10,
|
|
@@ -82,5 +83,21 @@
|
|
|
82
83
|
"email_attachment"
|
|
83
84
|
],
|
|
84
85
|
"sideEffects": "external-write"
|
|
86
|
+
},
|
|
87
|
+
"repository": "https://github.com/AIWerk/mcp-server-imap",
|
|
88
|
+
"signature": {
|
|
89
|
+
"algorithm": "ed25519",
|
|
90
|
+
"publisherId": "aiwerk",
|
|
91
|
+
"value": "IJQAle97X6wb3x+b6tPne74fwOUhif34GLkd4QII9ziwPyYI7r4Tv6eiV8gBc06d+H2mxMcqn/LE7qxMrxpDDg==",
|
|
92
|
+
"signedFields": [
|
|
93
|
+
"id",
|
|
94
|
+
"name",
|
|
95
|
+
"description",
|
|
96
|
+
"transports",
|
|
97
|
+
"auth",
|
|
98
|
+
"install",
|
|
99
|
+
"metadata"
|
|
100
|
+
],
|
|
101
|
+
"signedAt": "2026-03-20T15:15:39.408Z"
|
|
85
102
|
}
|
|
86
103
|
}
|
|
@@ -59,5 +59,20 @@
|
|
|
59
59
|
"resources": false,
|
|
60
60
|
"prompts": false,
|
|
61
61
|
"sampling": false
|
|
62
|
+
},
|
|
63
|
+
"signature": {
|
|
64
|
+
"algorithm": "ed25519",
|
|
65
|
+
"publisherId": "aiwerk",
|
|
66
|
+
"value": "Q3yXbOmJTN7V2+fUmXbxjtxv4ScPMGsQRbffDYYzsAjQGYllz8DwmuvZeHpLSGtP1Fl6g2X0iVnzlxfTzl3FCg==",
|
|
67
|
+
"signedFields": [
|
|
68
|
+
"id",
|
|
69
|
+
"name",
|
|
70
|
+
"description",
|
|
71
|
+
"transports",
|
|
72
|
+
"auth",
|
|
73
|
+
"install",
|
|
74
|
+
"metadata"
|
|
75
|
+
],
|
|
76
|
+
"signedAt": "2026-03-20T14:24:58.098Z"
|
|
62
77
|
}
|
|
63
78
|
}
|
package/servers/miro/recipe.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"command": "npx",
|
|
11
11
|
"args": [
|
|
12
12
|
"-y",
|
|
13
|
-
"@llmindset/mcp-miro",
|
|
13
|
+
"@llmindset/mcp-miro@0.1.1",
|
|
14
14
|
"--token",
|
|
15
15
|
"${MIRO_API_TOKEN}"
|
|
16
16
|
],
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"install": {
|
|
32
32
|
"method": "npx",
|
|
33
33
|
"package": "@llmindset/mcp-miro",
|
|
34
|
-
"version": "
|
|
34
|
+
"version": "0.1.1"
|
|
35
35
|
},
|
|
36
36
|
"metadata": {
|
|
37
37
|
"homepage": "https://miro.com/",
|
|
@@ -61,5 +61,20 @@
|
|
|
61
61
|
"resources": false,
|
|
62
62
|
"prompts": false,
|
|
63
63
|
"sampling": false
|
|
64
|
+
},
|
|
65
|
+
"signature": {
|
|
66
|
+
"algorithm": "ed25519",
|
|
67
|
+
"publisherId": "aiwerk",
|
|
68
|
+
"value": "ekptOzlrjh4EmGVDQMBPpCJNchPEsRIEqFOSkMH2lM4cbMtpqVy9jWvErRTZwkLIFi4nQx2T3SGapmpBDjxhDA==",
|
|
69
|
+
"signedFields": [
|
|
70
|
+
"id",
|
|
71
|
+
"name",
|
|
72
|
+
"description",
|
|
73
|
+
"transports",
|
|
74
|
+
"auth",
|
|
75
|
+
"install",
|
|
76
|
+
"metadata"
|
|
77
|
+
],
|
|
78
|
+
"signedAt": "2026-03-20T14:25:02.500Z"
|
|
64
79
|
}
|
|
65
80
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"command": "npx",
|
|
11
11
|
"args": [
|
|
12
12
|
"-y",
|
|
13
|
-
"@notionhq/notion-mcp-server"
|
|
13
|
+
"@notionhq/notion-mcp-server@2.2.1"
|
|
14
14
|
],
|
|
15
15
|
"env": {
|
|
16
16
|
"NOTION_TOKEN": "${NOTION_API_KEY}"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"install": {
|
|
30
30
|
"method": "npx",
|
|
31
31
|
"package": "@notionhq/notion-mcp-server",
|
|
32
|
-
"version": "
|
|
32
|
+
"version": "2.2.1"
|
|
33
33
|
},
|
|
34
34
|
"metadata": {
|
|
35
35
|
"homepage": "https://www.notion.so/",
|
|
@@ -59,5 +59,20 @@
|
|
|
59
59
|
"resources": false,
|
|
60
60
|
"prompts": false,
|
|
61
61
|
"sampling": false
|
|
62
|
+
},
|
|
63
|
+
"signature": {
|
|
64
|
+
"algorithm": "ed25519",
|
|
65
|
+
"publisherId": "aiwerk",
|
|
66
|
+
"value": "d1UG7xnQ3ZR5qWqjMYrgDf7mju2etp6otv7RJgXsBMWw/Ia4ARwLXCDg7NTBpWL3QPlJGD+231bZx6yOhIY5AQ==",
|
|
67
|
+
"signedFields": [
|
|
68
|
+
"id",
|
|
69
|
+
"name",
|
|
70
|
+
"description",
|
|
71
|
+
"transports",
|
|
72
|
+
"auth",
|
|
73
|
+
"install",
|
|
74
|
+
"metadata"
|
|
75
|
+
],
|
|
76
|
+
"signedAt": "2026-03-20T14:25:07.431Z"
|
|
62
77
|
}
|
|
63
78
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"command": "npx",
|
|
11
11
|
"args": [
|
|
12
12
|
"-y",
|
|
13
|
-
"@stripe/mcp",
|
|
13
|
+
"@stripe/mcp@0.3.1",
|
|
14
14
|
"--tools=all",
|
|
15
15
|
"--api-key=${STRIPE_API_KEY}"
|
|
16
16
|
],
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"install": {
|
|
32
32
|
"method": "npx",
|
|
33
33
|
"package": "@stripe/mcp",
|
|
34
|
-
"version": "
|
|
34
|
+
"version": "0.3.1"
|
|
35
35
|
},
|
|
36
36
|
"metadata": {
|
|
37
37
|
"homepage": "https://stripe.com/",
|
|
@@ -61,5 +61,20 @@
|
|
|
61
61
|
"resources": false,
|
|
62
62
|
"prompts": false,
|
|
63
63
|
"sampling": false
|
|
64
|
+
},
|
|
65
|
+
"signature": {
|
|
66
|
+
"algorithm": "ed25519",
|
|
67
|
+
"publisherId": "aiwerk",
|
|
68
|
+
"value": "K+265McBYk3Y8RHHEXDSUILier1i2G5MVCcXFRGjTGJjvj/rjKSPAb9yB0BkCNhEbo3Lf6+wwpfrcdNBUyXSAg==",
|
|
69
|
+
"signedFields": [
|
|
70
|
+
"id",
|
|
71
|
+
"name",
|
|
72
|
+
"description",
|
|
73
|
+
"transports",
|
|
74
|
+
"auth",
|
|
75
|
+
"install",
|
|
76
|
+
"metadata"
|
|
77
|
+
],
|
|
78
|
+
"signedAt": "2026-03-20T14:25:11.866Z"
|
|
64
79
|
}
|
|
65
80
|
}
|
|
@@ -59,5 +59,20 @@
|
|
|
59
59
|
"resources": false,
|
|
60
60
|
"prompts": false,
|
|
61
61
|
"sampling": false
|
|
62
|
+
},
|
|
63
|
+
"signature": {
|
|
64
|
+
"algorithm": "ed25519",
|
|
65
|
+
"publisherId": "aiwerk",
|
|
66
|
+
"value": "a4nc94mlhdcxxQ38NKJ3ez++KBp5KyoPsS05uyzCfoR1YaIpejFRKYJ/5D3YlgI/BKQrOo6uzcBfWXMtxHGuBA==",
|
|
67
|
+
"signedFields": [
|
|
68
|
+
"id",
|
|
69
|
+
"name",
|
|
70
|
+
"description",
|
|
71
|
+
"transports",
|
|
72
|
+
"auth",
|
|
73
|
+
"install",
|
|
74
|
+
"metadata"
|
|
75
|
+
],
|
|
76
|
+
"signedAt": "2026-03-20T14:25:14.930Z"
|
|
62
77
|
}
|
|
63
78
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"command": "npx",
|
|
11
11
|
"args": [
|
|
12
12
|
"-y",
|
|
13
|
-
"@doist/todoist-ai"
|
|
13
|
+
"@doist/todoist-ai@8.4.0"
|
|
14
14
|
],
|
|
15
15
|
"env": {
|
|
16
16
|
"TODOIST_API_KEY": "${TODOIST_API_TOKEN}"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"install": {
|
|
30
30
|
"method": "npx",
|
|
31
31
|
"package": "@doist/todoist-ai",
|
|
32
|
-
"version": "
|
|
32
|
+
"version": "8.4.0"
|
|
33
33
|
},
|
|
34
34
|
"metadata": {
|
|
35
35
|
"homepage": "https://todoist.com/",
|
|
@@ -59,5 +59,20 @@
|
|
|
59
59
|
"resources": false,
|
|
60
60
|
"prompts": false,
|
|
61
61
|
"sampling": false
|
|
62
|
+
},
|
|
63
|
+
"signature": {
|
|
64
|
+
"algorithm": "ed25519",
|
|
65
|
+
"publisherId": "aiwerk",
|
|
66
|
+
"value": "soScAYYYd+k/xETeH6kSytJGW5dLW7qkPUL8ulYP0VvOwmCXlvntW0uS4TvtoJC3+FvjE7DhPUhHmW7CUfDbAw==",
|
|
67
|
+
"signedFields": [
|
|
68
|
+
"id",
|
|
69
|
+
"name",
|
|
70
|
+
"description",
|
|
71
|
+
"transports",
|
|
72
|
+
"auth",
|
|
73
|
+
"install",
|
|
74
|
+
"metadata"
|
|
75
|
+
],
|
|
76
|
+
"signedAt": "2026-03-20T14:25:19.646Z"
|
|
62
77
|
}
|
|
63
78
|
}
|
package/servers/wise/recipe.json
CHANGED
|
@@ -59,5 +59,20 @@
|
|
|
59
59
|
"resources": false,
|
|
60
60
|
"prompts": false,
|
|
61
61
|
"sampling": false
|
|
62
|
+
},
|
|
63
|
+
"signature": {
|
|
64
|
+
"algorithm": "ed25519",
|
|
65
|
+
"publisherId": "aiwerk",
|
|
66
|
+
"value": "GNdSmM2mKmejTTHTt8RwfVl4QzM/Y9f582OAuE1EUvPFS53BBze5wZQInkuRNlc3GtwsFJrR61lLZfP5Dn/QCQ==",
|
|
67
|
+
"signedFields": [
|
|
68
|
+
"id",
|
|
69
|
+
"name",
|
|
70
|
+
"description",
|
|
71
|
+
"transports",
|
|
72
|
+
"auth",
|
|
73
|
+
"install",
|
|
74
|
+
"metadata"
|
|
75
|
+
],
|
|
76
|
+
"signedAt": "2026-03-20T14:25:22.915Z"
|
|
62
77
|
}
|
|
63
78
|
}
|