@revealui/mcp 0.1.7 → 0.1.9

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.
@@ -159,7 +159,7 @@ export declare class MCPHypervisor {
159
159
  /**
160
160
  * Start an MCP server with tenant-specific credentials.
161
161
  * Each tenant gets its own isolated server process with credentials
162
- * resolved from the database (BYOK keys or platform defaults).
162
+ * resolved from the database (stored keys or platform defaults).
163
163
  *
164
164
  * Server instances are keyed by `${tenantId}:${serverName}`.
165
165
  */
@@ -371,7 +371,7 @@ export class MCPHypervisor {
371
371
  /**
372
372
  * Start an MCP server with tenant-specific credentials.
373
373
  * Each tenant gets its own isolated server process with credentials
374
- * resolved from the database (BYOK keys or platform defaults).
374
+ * resolved from the database (stored keys or platform defaults).
375
375
  *
376
376
  * Server instances are keyed by `${tenantId}:${serverName}`.
377
377
  */
package/dist/index.d.ts CHANGED
@@ -4,10 +4,10 @@
4
4
  * Model Context Protocol integrations for RevealUI.
5
5
  *
6
6
  * Provides:
7
- * - MCP hypervisor: process management, tool discovery, health checks (production-ready)
8
- * - MCP adapter framework: base class with retry, idempotency, error handling (production-ready)
9
- * - Database adapter: PGlite / PostgreSQL with CRDT support (production-ready)
10
- * - MCP contracts: Zod schemas for request/response/tool bridging (production-ready)
7
+ * - MCP hypervisor: process management, tool discovery, health checks (pre-wired)
8
+ * - MCP adapter framework: base class with retry, idempotency, error handling (pre-wired)
9
+ * - Database adapter: PGlite / PostgreSQL with CRDT support (pre-wired)
10
+ * - MCP contracts: Zod schemas for request/response/tool bridging (pre-wired)
11
11
  *
12
12
  * Server adapter status:
13
13
  * - NeonAdapter, StripeAdapter, VercelAdapter: scaffolded (adapter config + error mapping only,
@@ -27,7 +27,8 @@ export { getMcpConfig, type McpConfig as McpEnvConfig, type McpMetricsMode, } fr
27
27
  export { agentDefinitionToAgentCard, agentDefinitionToMcpTools, contractsToolDefinitionToMcpTool, type MCPAdapterConfig, MCPAdapterConfigSchema, type MCPRequest, type MCPRequestOptions, MCPRequestOptionsSchema, MCPRequestSchema, type MCPResponse, type MCPResponseMetadata, MCPResponseMetadataSchema, MCPResponseSchema, mcpToolToContractsToolDefinition, type ToolOutputSchemaName, ToolOutputSchemas, validateToolOutput, } from './contracts.js';
28
28
  export { type MCPCredentialResolver, MCPHypervisor, type MCPServerConfig, type MCPTenantContext, type MCPTool, type NamespacedTool, } from './hypervisor.js';
29
29
  export { executePipeline, type PipelineResult, type PipelineStep, type PipelineStepResult, } from './pipeline.js';
30
- export { DEFAULT_TIER_LIMITS, McpRateLimiter, type RateLimitConfig, type RateLimitResult, } from './rate-limiter.js';
30
+ export { InMemoryRateLimitStore, PGliteRateLimitStore, type RateLimitStore, type WindowEntry, } from './rate-limit-store.js';
31
+ export { DEFAULT_TIER_LIMITS, McpRateLimiter, type McpRateLimiterOptions, type RateLimitConfig, type RateLimitResult, } from './rate-limiter.js';
31
32
  export { createMCPAdapter, disposeAllAdapters, generateIdempotencyKey, generateUniqueIdempotencyKey, type IdempotencyStore, MCPAdapter, type MCPConfig, NeonAdapter, StripeAdapter, VercelAdapter, } from './servers/adapter.js';
32
33
  export { launchNeonMcp } from './servers/neon.js';
33
34
  export { launchNextDevtoolsMcp } from './servers/next-devtools.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAUxD;AAGD,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,iBAAiB,EACjB,KAAK,aAAa,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,YAAY,EACZ,KAAK,SAAS,IAAI,YAAY,EAC9B,KAAK,cAAc,GACpB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,iBAAiB,EACjB,gCAAgC,EAChC,KAAK,oBAAoB,EACzB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,KAAK,qBAAqB,EAC1B,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,OAAO,EACZ,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,4BAA4B,EAC5B,KAAK,gBAAgB,EACrB,UAAU,EACV,KAAK,SAAS,EACd,WAAW,EACX,aAAa,EACb,aAAa,GACd,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AAElF,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,YAAY,GACb,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAUxD;AAGD,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,iBAAiB,EACjB,KAAK,aAAa,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,YAAY,EACZ,KAAK,SAAS,IAAI,YAAY,EAC9B,KAAK,cAAc,GACpB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,iBAAiB,EACjB,gCAAgC,EAChC,KAAK,oBAAoB,EACzB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,KAAK,qBAAqB,EAC1B,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,OAAO,EACZ,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,4BAA4B,EAC5B,KAAK,gBAAgB,EACrB,UAAU,EACV,KAAK,SAAS,EACd,WAAW,EACX,aAAa,EACb,aAAa,GACd,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AAElF,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,YAAY,GACb,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -4,10 +4,10 @@
4
4
  * Model Context Protocol integrations for RevealUI.
5
5
  *
6
6
  * Provides:
7
- * - MCP hypervisor: process management, tool discovery, health checks (production-ready)
8
- * - MCP adapter framework: base class with retry, idempotency, error handling (production-ready)
9
- * - Database adapter: PGlite / PostgreSQL with CRDT support (production-ready)
10
- * - MCP contracts: Zod schemas for request/response/tool bridging (production-ready)
7
+ * - MCP hypervisor: process management, tool discovery, health checks (pre-wired)
8
+ * - MCP adapter framework: base class with retry, idempotency, error handling (pre-wired)
9
+ * - Database adapter: PGlite / PostgreSQL with CRDT support (pre-wired)
10
+ * - MCP contracts: Zod schemas for request/response/tool bridging (pre-wired)
11
11
  *
12
12
  * Server adapter status:
13
13
  * - NeonAdapter, StripeAdapter, VercelAdapter: scaffolded (adapter config + error mapping only,
@@ -44,6 +44,8 @@ export { agentDefinitionToAgentCard, agentDefinitionToMcpTools, contractsToolDef
44
44
  export { MCPHypervisor, } from './hypervisor.js';
45
45
  // Tool pipeline (composition / chaining)
46
46
  export { executePipeline, } from './pipeline.js';
47
+ // Rate limit stores (pluggable backends)
48
+ export { InMemoryRateLimitStore, PGliteRateLimitStore, } from './rate-limit-store.js';
47
49
  // Rate limiting (per-tier)
48
50
  export { DEFAULT_TIER_LIMITS, McpRateLimiter, } from './rate-limiter.js';
49
51
  // Adapter framework
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAE7D;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,iBAAiB,EAAE,CAAC;IAC1B,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CACT,+EAA+E;YAC7E,iDAAiD,CACpD,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mBAAmB;AACnB,OAAO,EAGL,aAAa,EACb,eAAe,EACf,iBAAiB,GAGlB,MAAM,kBAAkB,CAAC;AAC1B,2DAA2D;AAC3D,OAAO,EACL,iBAAiB,EAEjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,gBAAgB;AAChB,OAAO,EACL,YAAY,GAGb,MAAM,mBAAmB,CAAC;AAC3B,0CAA0C;AAC1C,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAEhC,sBAAsB,EAGtB,uBAAuB,EACvB,gBAAgB,EAGhB,yBAAyB,EACzB,iBAAiB,EACjB,gCAAgC,EAEhC,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,0DAA0D;AAC1D,OAAO,EAEL,aAAa,GAKd,MAAM,iBAAiB,CAAC;AACzB,yCAAyC;AACzC,OAAO,EACL,eAAe,GAIhB,MAAM,eAAe,CAAC;AACvB,2BAA2B;AAC3B,OAAO,EACL,mBAAmB,EACnB,cAAc,GAGf,MAAM,mBAAmB,CAAC;AAC3B,oBAAoB;AACpB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,4BAA4B,EAE5B,UAAU,EAEV,WAAW,EACX,aAAa,EACb,aAAa,GACd,MAAM,sBAAsB,CAAC;AAC9B,mBAAmB;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,gCAAgC;AAChC,OAAO,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AAClF,8CAA8C;AAC9C,OAAO,EAIL,YAAY,GACb,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAE7D;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,iBAAiB,EAAE,CAAC;IAC1B,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CACT,+EAA+E;YAC7E,iDAAiD,CACpD,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mBAAmB;AACnB,OAAO,EAGL,aAAa,EACb,eAAe,EACf,iBAAiB,GAGlB,MAAM,kBAAkB,CAAC;AAC1B,2DAA2D;AAC3D,OAAO,EACL,iBAAiB,EAEjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,gBAAgB;AAChB,OAAO,EACL,YAAY,GAGb,MAAM,mBAAmB,CAAC;AAC3B,0CAA0C;AAC1C,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAEhC,sBAAsB,EAGtB,uBAAuB,EACvB,gBAAgB,EAGhB,yBAAyB,EACzB,iBAAiB,EACjB,gCAAgC,EAEhC,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,0DAA0D;AAC1D,OAAO,EAEL,aAAa,GAKd,MAAM,iBAAiB,CAAC;AACzB,yCAAyC;AACzC,OAAO,EACL,eAAe,GAIhB,MAAM,eAAe,CAAC;AACvB,yCAAyC;AACzC,OAAO,EACL,sBAAsB,EACtB,oBAAoB,GAGrB,MAAM,uBAAuB,CAAC;AAC/B,2BAA2B;AAC3B,OAAO,EACL,mBAAmB,EACnB,cAAc,GAIf,MAAM,mBAAmB,CAAC;AAC3B,oBAAoB;AACpB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,4BAA4B,EAE5B,UAAU,EAEV,WAAW,EACX,aAAa,EACb,aAAa,GACd,MAAM,sBAAsB,CAAC;AAC9B,mBAAmB;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,gCAAgC;AAChC,OAAO,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AAClF,8CAA8C;AAC9C,OAAO,EAIL,YAAY,GACb,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Rate Limit Store — Pluggable backends for rate limiter state.
3
+ *
4
+ * - InMemoryRateLimitStore: Map-backed, zero-dep, single-instance (default)
5
+ * - PGliteRateLimitStore: PostgreSQL-backed via PGlite, supports per-instance
6
+ * persistence and can be extended with ElectricSQL for distributed sync.
7
+ */
8
+ export interface WindowEntry {
9
+ count: number;
10
+ windowStart: number;
11
+ }
12
+ /**
13
+ * Pluggable storage backend for rate limiter window entries.
14
+ * Implementations must handle TTL-based expiry internally.
15
+ */
16
+ export interface RateLimitStore {
17
+ /** Get the current window entry for a key, or null if expired/missing. */
18
+ get(key: string): Promise<WindowEntry | null>;
19
+ /** Set or overwrite the window entry for a key. */
20
+ set(key: string, entry: WindowEntry): Promise<void>;
21
+ /** Increment the count for a key. Returns the new count. */
22
+ increment(key: string): Promise<number>;
23
+ /**
24
+ * Atomically increment count only if below limit.
25
+ * Returns the new count and whether the increment happened.
26
+ * This prevents race conditions where concurrent requests all pass
27
+ * a non-atomic read-check-increment sequence.
28
+ */
29
+ incrementIfBelow(key: string, limit: number): Promise<{
30
+ count: number;
31
+ incremented: boolean;
32
+ }>;
33
+ /** Remove expired entries older than the given cutoff timestamp. */
34
+ cleanup(cutoffMs: number): Promise<number>;
35
+ /** Clear all entries. */
36
+ clear(): Promise<void>;
37
+ /** Release any resources (timers, connections). */
38
+ close(): Promise<void>;
39
+ }
40
+ export declare class InMemoryRateLimitStore implements RateLimitStore {
41
+ private windows;
42
+ get(key: string): Promise<WindowEntry | null>;
43
+ set(key: string, entry: WindowEntry): Promise<void>;
44
+ increment(key: string): Promise<number>;
45
+ incrementIfBelow(key: string, limit: number): Promise<{
46
+ count: number;
47
+ incremented: boolean;
48
+ }>;
49
+ cleanup(cutoffMs: number): Promise<number>;
50
+ clear(): Promise<void>;
51
+ close(): Promise<void>;
52
+ }
53
+ /** Minimal PGlite interface — avoids importing the full @electric-sql/pglite package. */
54
+ interface PGliteInstance {
55
+ exec(query: string): Promise<unknown>;
56
+ query<T = Record<string, unknown>>(query: string, params?: unknown[]): Promise<{
57
+ rows: T[];
58
+ }>;
59
+ close(): Promise<void>;
60
+ }
61
+ interface PGliteRateLimitStoreOptions {
62
+ /** PGlite instance (caller owns lifecycle unless closeOnDestroy is true). */
63
+ db: PGliteInstance;
64
+ /** Close the PGlite instance when close() is called (default: false). */
65
+ closeOnDestroy?: boolean;
66
+ }
67
+ export declare class PGliteRateLimitStore implements RateLimitStore {
68
+ private db;
69
+ private ready;
70
+ private closeOnDestroy;
71
+ constructor(options: PGliteRateLimitStoreOptions);
72
+ private init;
73
+ get(key: string): Promise<WindowEntry | null>;
74
+ set(key: string, entry: WindowEntry): Promise<void>;
75
+ increment(key: string): Promise<number>;
76
+ incrementIfBelow(key: string, limit: number): Promise<{
77
+ count: number;
78
+ incremented: boolean;
79
+ }>;
80
+ cleanup(cutoffMs: number): Promise<number>;
81
+ clear(): Promise<void>;
82
+ close(): Promise<void>;
83
+ }
84
+ export {};
85
+ //# sourceMappingURL=rate-limit-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit-store.d.ts","sourceRoot":"","sources":["../src/rate-limit-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Rate Limit Store — Pluggable backends for rate limiter state.
3
+ *
4
+ * - InMemoryRateLimitStore: Map-backed, zero-dep, single-instance (default)
5
+ * - PGliteRateLimitStore: PostgreSQL-backed via PGlite, supports per-instance
6
+ * persistence and can be extended with ElectricSQL for distributed sync.
7
+ */
8
+ // =============================================================================
9
+ // In-memory store (Map-backed, default)
10
+ // =============================================================================
11
+ export class InMemoryRateLimitStore {
12
+ windows = new Map();
13
+ async get(key) {
14
+ return this.windows.get(key) ?? null;
15
+ }
16
+ async set(key, entry) {
17
+ this.windows.set(key, entry);
18
+ }
19
+ async increment(key) {
20
+ const entry = this.windows.get(key);
21
+ if (!entry)
22
+ return 0;
23
+ entry.count++;
24
+ return entry.count;
25
+ }
26
+ async incrementIfBelow(key, limit) {
27
+ const entry = this.windows.get(key);
28
+ if (!entry)
29
+ return { count: 0, incremented: false };
30
+ if (entry.count >= limit)
31
+ return { count: entry.count, incremented: false };
32
+ entry.count++;
33
+ return { count: entry.count, incremented: true };
34
+ }
35
+ async cleanup(cutoffMs) {
36
+ let removed = 0;
37
+ for (const [key, entry] of this.windows) {
38
+ if (entry.windowStart < cutoffMs) {
39
+ this.windows.delete(key);
40
+ removed++;
41
+ }
42
+ }
43
+ return removed;
44
+ }
45
+ async clear() {
46
+ this.windows.clear();
47
+ }
48
+ async close() {
49
+ this.windows.clear();
50
+ }
51
+ }
52
+ const CREATE_RATE_LIMIT_TABLE_SQL = `
53
+ CREATE TABLE IF NOT EXISTS _rate_limit_windows (
54
+ key TEXT PRIMARY KEY,
55
+ count INTEGER NOT NULL DEFAULT 0,
56
+ window_start BIGINT NOT NULL
57
+ );
58
+ `;
59
+ export class PGliteRateLimitStore {
60
+ db;
61
+ ready;
62
+ closeOnDestroy;
63
+ constructor(options) {
64
+ this.db = options.db;
65
+ this.closeOnDestroy = options.closeOnDestroy ?? false;
66
+ this.ready = this.init();
67
+ }
68
+ async init() {
69
+ await this.db.exec(CREATE_RATE_LIMIT_TABLE_SQL);
70
+ }
71
+ async get(key) {
72
+ await this.ready;
73
+ const result = await this.db.query('SELECT count, window_start FROM _rate_limit_windows WHERE key = $1', [key]);
74
+ const row = result.rows[0];
75
+ if (!row)
76
+ return null;
77
+ return { count: row.count, windowStart: Number(row.window_start) };
78
+ }
79
+ async set(key, entry) {
80
+ await this.ready;
81
+ await this.db.query(`INSERT INTO _rate_limit_windows (key, count, window_start)
82
+ VALUES ($1, $2, $3)
83
+ ON CONFLICT (key) DO UPDATE
84
+ SET count = EXCLUDED.count, window_start = EXCLUDED.window_start`, [key, entry.count, entry.windowStart]);
85
+ }
86
+ async increment(key) {
87
+ await this.ready;
88
+ const result = await this.db.query(`UPDATE _rate_limit_windows SET count = count + 1 WHERE key = $1 RETURNING count`, [key]);
89
+ const row = result.rows[0];
90
+ return row?.count ?? 0;
91
+ }
92
+ async incrementIfBelow(key, limit) {
93
+ await this.ready;
94
+ const result = await this.db.query(`UPDATE _rate_limit_windows SET count = count + 1
95
+ WHERE key = $1 AND count < $2
96
+ RETURNING count`, [key, limit]);
97
+ const row = result.rows[0];
98
+ if (row)
99
+ return { count: row.count, incremented: true };
100
+ // No rows updated — either key missing or limit reached
101
+ const current = await this.get(key);
102
+ return { count: current?.count ?? 0, incremented: false };
103
+ }
104
+ async cleanup(cutoffMs) {
105
+ await this.ready;
106
+ const result = await this.db.query(`WITH deleted AS (DELETE FROM _rate_limit_windows WHERE window_start < $1 RETURNING 1)
107
+ SELECT count(*)::text AS count FROM deleted`, [cutoffMs]);
108
+ return Number.parseInt(result.rows[0]?.count ?? '0', 10);
109
+ }
110
+ async clear() {
111
+ await this.ready;
112
+ await this.db.exec('DELETE FROM _rate_limit_windows');
113
+ }
114
+ async close() {
115
+ if (this.closeOnDestroy) {
116
+ await this.db.close();
117
+ }
118
+ }
119
+ }
120
+ //# sourceMappingURL=rate-limit-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit-store.js","sourceRoot":"","sources":["../src/rate-limit-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2CH,gFAAgF;AAChF,wCAAwC;AACxC,gFAAgF;AAEhF,MAAM,OAAO,sBAAsB;IACzB,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAEjD,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAkB;QACvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAW;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,CAAC;QACrB,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,GAAW,EACX,KAAa;QAEb,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;QACpD,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;QAC5E,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,QAAgB;QAC5B,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,GAAG,QAAQ,EAAE,CAAC;gBACjC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACzB,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;CACF;AAaD,MAAM,2BAA2B,GAAG;;;;;;CAMnC,CAAC;AASF,MAAM,OAAO,oBAAoB;IACvB,EAAE,CAAiB;IACnB,KAAK,CAAgB;IACrB,cAAc,CAAU;IAEhC,YAAY,OAAoC;QAC9C,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC;QACtD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,MAAM,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,IAAI,CAAC,KAAK,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAChC,oEAAoE,EACpE,CAAC,GAAG,CAAC,CACN,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAkB;QACvC,MAAM,IAAI,CAAC,KAAK,CAAC;QACjB,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CACjB;;;wEAGkE,EAClE,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,CACtC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAW;QACzB,MAAM,IAAI,CAAC,KAAK,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAChC,iFAAiF,EACjF,CAAC,GAAG,CAAC,CACN,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,OAAO,GAAG,EAAE,KAAK,IAAI,CAAC,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,GAAW,EACX,KAAa;QAEb,MAAM,IAAI,CAAC,KAAK,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAChC;;uBAEiB,EACjB,CAAC,GAAG,EAAE,KAAK,CAAC,CACb,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,GAAG;YAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QACxD,wDAAwD;QACxD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,QAAgB;QAC5B,MAAM,IAAI,CAAC,KAAK,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAChC;mDAC6C,EAC7C,CAAC,QAAQ,CAAC,CACX,CAAC;QACF,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,KAAK,CAAC;QACjB,MAAM,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;CACF"}
@@ -1,20 +1,31 @@
1
1
  /**
2
2
  * MCP Rate Limiter
3
3
  *
4
- * In-memory fixed-window rate limiter scoped by tenant + tier.
5
- * For distributed deployments, share state via PGlite or ElectricSQL.
4
+ * Fixed-window rate limiter scoped by tenant + tier.
5
+ * Supports pluggable storage backends via RateLimitStore:
6
+ * - InMemoryRateLimitStore (default, Map-backed)
7
+ * - PGliteRateLimitStore (SQL-backed, persistent)
6
8
  *
7
9
  * @example
8
10
  * ```typescript
11
+ * // In-memory (default)
9
12
  * const limiter = new McpRateLimiter();
10
13
  *
11
- * const result = limiter.check('tenant-123', 'pro');
14
+ * // PGlite-backed
15
+ * import { PGlite } from '@electric-sql/pglite';
16
+ * import { PGliteRateLimitStore } from './rate-limit-store.js';
17
+ * const db = new PGlite();
18
+ * const limiter = new McpRateLimiter({
19
+ * store: new PGliteRateLimitStore({ db }),
20
+ * });
21
+ *
22
+ * const result = await limiter.check('tenant-123', 'pro');
12
23
  * if (!result.allowed) {
13
24
  * throw new Error(`Rate limited. Retry after ${result.resetMs}ms`);
14
25
  * }
15
- * // result.remaining === requests left in window
16
26
  * ```
17
27
  */
28
+ import { type RateLimitStore } from './rate-limit-store.js';
18
29
  export interface RateLimitConfig {
19
30
  /** Max requests per window */
20
31
  maxRequests: number;
@@ -27,27 +38,33 @@ export interface RateLimitResult {
27
38
  limit: number;
28
39
  resetMs: number;
29
40
  }
41
+ export interface McpRateLimiterOptions {
42
+ /** Tier-based rate limit configuration. */
43
+ limits?: Record<string, RateLimitConfig>;
44
+ /** Storage backend (default: InMemoryRateLimitStore). */
45
+ store?: RateLimitStore;
46
+ }
30
47
  /** Default rate limits per tier. */
31
48
  export declare const DEFAULT_TIER_LIMITS: Record<string, RateLimitConfig>;
32
49
  /**
33
- * In-memory fixed-window rate limiter scoped by tenant + tier.
34
- * For distributed deployments, share state via PGlite or ElectricSQL.
50
+ * Fixed-window rate limiter scoped by tenant + tier.
51
+ * Accepts a pluggable RateLimitStore for different storage backends.
35
52
  */
36
53
  export declare class McpRateLimiter {
37
- private windows;
54
+ private store;
38
55
  private limits;
39
56
  private cleanupInterval;
40
- constructor(limits?: Record<string, RateLimitConfig>);
57
+ constructor(options?: McpRateLimiterOptions);
41
58
  /**
42
59
  * Check if a request is allowed and consume one token if so.
43
60
  * Returns quota information including remaining requests and reset time.
44
61
  */
45
- check(tenantId: string, tier: string): RateLimitResult;
62
+ check(tenantId: string, tier: string): Promise<RateLimitResult>;
46
63
  /** Override limits for a specific tier. */
47
64
  setTierLimit(tier: string, config: RateLimitConfig): void;
48
65
  /** Clean up expired window entries. */
49
66
  private cleanup;
50
67
  /** Dispose the rate limiter, clearing the cleanup timer and all windows. */
51
- dispose(): void;
68
+ dispose(): Promise<void>;
52
69
  }
53
70
  //# sourceMappingURL=rate-limiter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,oCAAoC;AACpC,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAK/D,CAAC;AAeF;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,eAAe,CAA+C;gBAE1D,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAuB;IAOzE;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe;IA4BtD,2CAA2C;IAC3C,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAIzD,uCAAuC;IACvC,OAAO,CAAC,OAAO;IAWf,4EAA4E;IAC5E,OAAO,IAAI,IAAI;CAOhB"}
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAMpF,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACzC,yDAAyD;IACzD,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAMD,oCAAoC;AACpC,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAK/D,CAAC;AAMF;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,eAAe,CAA+C;gBAE1D,OAAO,GAAE,qBAA0B;IAU/C;;;OAGG;IACG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA8BrE,2CAA2C;IAC3C,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAIzD,uCAAuC;YACzB,OAAO;IAOrB,4EAA4E;IACtE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAO/B"}
@@ -1,20 +1,31 @@
1
1
  /**
2
2
  * MCP Rate Limiter
3
3
  *
4
- * In-memory fixed-window rate limiter scoped by tenant + tier.
5
- * For distributed deployments, share state via PGlite or ElectricSQL.
4
+ * Fixed-window rate limiter scoped by tenant + tier.
5
+ * Supports pluggable storage backends via RateLimitStore:
6
+ * - InMemoryRateLimitStore (default, Map-backed)
7
+ * - PGliteRateLimitStore (SQL-backed, persistent)
6
8
  *
7
9
  * @example
8
10
  * ```typescript
11
+ * // In-memory (default)
9
12
  * const limiter = new McpRateLimiter();
10
13
  *
11
- * const result = limiter.check('tenant-123', 'pro');
14
+ * // PGlite-backed
15
+ * import { PGlite } from '@electric-sql/pglite';
16
+ * import { PGliteRateLimitStore } from './rate-limit-store.js';
17
+ * const db = new PGlite();
18
+ * const limiter = new McpRateLimiter({
19
+ * store: new PGliteRateLimitStore({ db }),
20
+ * });
21
+ *
22
+ * const result = await limiter.check('tenant-123', 'pro');
12
23
  * if (!result.allowed) {
13
24
  * throw new Error(`Rate limited. Retry after ${result.resetMs}ms`);
14
25
  * }
15
- * // result.remaining === requests left in window
16
26
  * ```
17
27
  */
28
+ import { InMemoryRateLimitStore } from './rate-limit-store.js';
18
29
  // =============================================================================
19
30
  // Default tier limits
20
31
  // =============================================================================
@@ -29,17 +40,20 @@ export const DEFAULT_TIER_LIMITS = {
29
40
  // Rate limiter
30
41
  // =============================================================================
31
42
  /**
32
- * In-memory fixed-window rate limiter scoped by tenant + tier.
33
- * For distributed deployments, share state via PGlite or ElectricSQL.
43
+ * Fixed-window rate limiter scoped by tenant + tier.
44
+ * Accepts a pluggable RateLimitStore for different storage backends.
34
45
  */
35
46
  export class McpRateLimiter {
36
- windows = new Map();
47
+ store;
37
48
  limits;
38
49
  cleanupInterval = null;
39
- constructor(limits = DEFAULT_TIER_LIMITS) {
40
- this.limits = limits;
50
+ constructor(options = {}) {
51
+ this.limits = options.limits ?? DEFAULT_TIER_LIMITS;
52
+ this.store = options.store ?? new InMemoryRateLimitStore();
41
53
  // Clean up expired windows every 60s
42
- this.cleanupInterval = setInterval(() => this.cleanup(), 60_000);
54
+ this.cleanupInterval = setInterval(() => {
55
+ void this.cleanup();
56
+ }, 60_000);
43
57
  if (this.cleanupInterval.unref)
44
58
  this.cleanupInterval.unref();
45
59
  }
@@ -47,24 +61,26 @@ export class McpRateLimiter {
47
61
  * Check if a request is allowed and consume one token if so.
48
62
  * Returns quota information including remaining requests and reset time.
49
63
  */
50
- check(tenantId, tier) {
64
+ async check(tenantId, tier) {
51
65
  const config = this.limits[tier] ?? this.limits.free ?? { maxRequests: 60, windowMs: 60_000 };
52
66
  const key = `${tenantId}:${tier}`;
53
67
  const now = Date.now();
54
- let entry = this.windows.get(key);
68
+ let entry = await this.store.get(key);
55
69
  // New window or expired window
56
70
  if (!entry || now - entry.windowStart >= config.windowMs) {
57
71
  entry = { count: 0, windowStart: now };
58
- this.windows.set(key, entry);
72
+ await this.store.set(key, entry);
59
73
  }
60
74
  const resetMs = config.windowMs - (now - entry.windowStart);
61
- if (entry.count >= config.maxRequests) {
75
+ // Atomic check-and-increment: prevents concurrent requests from
76
+ // all passing a stale count check before any increment lands.
77
+ const result = await this.store.incrementIfBelow(key, config.maxRequests);
78
+ if (!result.incremented) {
62
79
  return { allowed: false, remaining: 0, limit: config.maxRequests, resetMs };
63
80
  }
64
- entry.count++;
65
81
  return {
66
82
  allowed: true,
67
- remaining: Math.max(0, config.maxRequests - entry.count),
83
+ remaining: Math.max(0, config.maxRequests - result.count),
68
84
  limit: config.maxRequests,
69
85
  resetMs,
70
86
  };
@@ -74,23 +90,19 @@ export class McpRateLimiter {
74
90
  this.limits[tier] = config;
75
91
  }
76
92
  /** Clean up expired window entries. */
77
- cleanup() {
93
+ async cleanup() {
78
94
  const now = Date.now();
79
- for (const [key, entry] of this.windows) {
80
- const tier = key.split(':')[1] ?? 'free';
81
- const config = this.limits[tier] ?? { windowMs: 60_000 };
82
- if (now - entry.windowStart >= config.windowMs * 2) {
83
- this.windows.delete(key);
84
- }
85
- }
95
+ // Use the longest window duration × 2 as the expiry cutoff
96
+ const maxWindowMs = Math.max(...Object.values(this.limits).map((c) => c.windowMs));
97
+ await this.store.cleanup(now - maxWindowMs * 2);
86
98
  }
87
99
  /** Dispose the rate limiter, clearing the cleanup timer and all windows. */
88
- dispose() {
100
+ async dispose() {
89
101
  if (this.cleanupInterval) {
90
102
  clearInterval(this.cleanupInterval);
91
103
  this.cleanupInterval = null;
92
104
  }
93
- this.windows.clear();
105
+ await this.store.close();
94
106
  }
95
107
  }
96
108
  //# sourceMappingURL=rate-limiter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Email provider for MCP servers.
3
+ *
4
+ * Provider priority:
5
+ * 1. Gmail REST API (GOOGLE_SERVICE_ACCOUNT_EMAIL + GOOGLE_PRIVATE_KEY)
6
+ * 2. SMTP (SMTP_HOST + SMTP_USER + SMTP_PASS)
7
+ * 3. Resend API (RESEND_API_KEY) — deprecated, kept as fallback
8
+ *
9
+ * Gmail uses a Google Workspace service account with domain-wide delegation
10
+ * to send as EMAIL_FROM via the Gmail REST API. Edge-compatible (fetch + jose).
11
+ */
12
+ export interface EmailPayload {
13
+ from: string;
14
+ to: string | string[];
15
+ subject: string;
16
+ html?: string;
17
+ text?: string;
18
+ reply_to?: string;
19
+ tags?: Array<{
20
+ name: string;
21
+ value: string;
22
+ }>;
23
+ }
24
+ export interface EmailResult {
25
+ provider: 'gmail' | 'resend';
26
+ id?: string;
27
+ data?: unknown;
28
+ }
29
+ /**
30
+ * Send a single email using the best available provider.
31
+ * Gmail REST API is preferred; falls back to Resend if Gmail is not configured.
32
+ */
33
+ export declare function sendEmail(payload: EmailPayload, overrides?: Record<string, string>): Promise<EmailResult>;
34
+ /**
35
+ * Send a batch of emails. Uses Gmail for each individually (no batch API),
36
+ * or Resend batch API if Gmail is not configured.
37
+ */
38
+ export declare function sendEmailBatch(payloads: EmailPayload[], overrides?: Record<string, string>): Promise<EmailResult>;
39
+ //# sourceMappingURL=_email-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_email-provider.d.ts","sourceRoot":"","sources":["../../src/servers/_email-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAyKD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,YAAY,EACrB,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACrC,OAAO,CAAC,WAAW,CAAC,CAgBtB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,YAAY,EAAE,EACxB,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACrC,OAAO,CAAC,WAAW,CAAC,CAkBtB"}