@defai.digital/mcp-server 13.0.3
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/LICENSE +214 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/bootstrap.d.ts +89 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +161 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/rate-limiter.d.ts +136 -0
- package/dist/middleware/rate-limiter.d.ts.map +1 -0
- package/dist/middleware/rate-limiter.js +262 -0
- package/dist/middleware/rate-limiter.js.map +1 -0
- package/dist/prompts/agent-guide.d.ts +16 -0
- package/dist/prompts/agent-guide.d.ts.map +1 -0
- package/dist/prompts/agent-guide.js +391 -0
- package/dist/prompts/agent-guide.js.map +1 -0
- package/dist/prompts/explain-workflow.d.ts +15 -0
- package/dist/prompts/explain-workflow.d.ts.map +1 -0
- package/dist/prompts/explain-workflow.js +157 -0
- package/dist/prompts/explain-workflow.js.map +1 -0
- package/dist/prompts/index.d.ts +39 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +83 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/review-changes.d.ts +15 -0
- package/dist/prompts/review-changes.d.ts.map +1 -0
- package/dist/prompts/review-changes.js +102 -0
- package/dist/prompts/review-changes.js.map +1 -0
- package/dist/prompts/troubleshoot-session.d.ts +15 -0
- package/dist/prompts/troubleshoot-session.d.ts.map +1 -0
- package/dist/prompts/troubleshoot-session.js +156 -0
- package/dist/prompts/troubleshoot-session.js.map +1 -0
- package/dist/registry-accessor.d.ts +83 -0
- package/dist/registry-accessor.d.ts.map +1 -0
- package/dist/registry-accessor.js +153 -0
- package/dist/registry-accessor.js.map +1 -0
- package/dist/resources/agents.d.ts +40 -0
- package/dist/resources/agents.d.ts.map +1 -0
- package/dist/resources/agents.js +123 -0
- package/dist/resources/agents.js.map +1 -0
- package/dist/resources/config.d.ts +57 -0
- package/dist/resources/config.d.ts.map +1 -0
- package/dist/resources/config.js +222 -0
- package/dist/resources/config.js.map +1 -0
- package/dist/resources/index.d.ts +38 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +132 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/policies.d.ts +40 -0
- package/dist/resources/policies.d.ts.map +1 -0
- package/dist/resources/policies.js +122 -0
- package/dist/resources/policies.js.map +1 -0
- package/dist/resources/sessions.d.ts +30 -0
- package/dist/resources/sessions.d.ts.map +1 -0
- package/dist/resources/sessions.js +64 -0
- package/dist/resources/sessions.js.map +1 -0
- package/dist/resources/workflows.d.ts +40 -0
- package/dist/resources/workflows.d.ts.map +1 -0
- package/dist/resources/workflows.js +143 -0
- package/dist/resources/workflows.js.map +1 -0
- package/dist/schema-registry.d.ts +23 -0
- package/dist/schema-registry.d.ts.map +1 -0
- package/dist/schema-registry.js +225 -0
- package/dist/schema-registry.js.map +1 -0
- package/dist/server.d.ts +63 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +393 -0
- package/dist/server.js.map +1 -0
- package/dist/session-accessor.d.ts +23 -0
- package/dist/session-accessor.d.ts.map +1 -0
- package/dist/session-accessor.js +39 -0
- package/dist/session-accessor.js.map +1 -0
- package/dist/shared-registry.d.ts +23 -0
- package/dist/shared-registry.d.ts.map +1 -0
- package/dist/shared-registry.js +235 -0
- package/dist/shared-registry.js.map +1 -0
- package/dist/stdio.d.ts +6 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +152 -0
- package/dist/stdio.js.map +1 -0
- package/dist/tool-namespacing.d.ts +28 -0
- package/dist/tool-namespacing.d.ts.map +1 -0
- package/dist/tool-namespacing.js +80 -0
- package/dist/tool-namespacing.js.map +1 -0
- package/dist/tools/ability.d.ts +55 -0
- package/dist/tools/ability.d.ts.map +1 -0
- package/dist/tools/ability.js +560 -0
- package/dist/tools/ability.js.map +1 -0
- package/dist/tools/agent.d.ts +73 -0
- package/dist/tools/agent.d.ts.map +1 -0
- package/dist/tools/agent.js +895 -0
- package/dist/tools/agent.js.map +1 -0
- package/dist/tools/config.d.ts +36 -0
- package/dist/tools/config.d.ts.map +1 -0
- package/dist/tools/config.js +265 -0
- package/dist/tools/config.js.map +1 -0
- package/dist/tools/design.d.ts +42 -0
- package/dist/tools/design.d.ts.map +1 -0
- package/dist/tools/design.js +736 -0
- package/dist/tools/design.js.map +1 -0
- package/dist/tools/discuss.d.ts +40 -0
- package/dist/tools/discuss.d.ts.map +1 -0
- package/dist/tools/discuss.js +331 -0
- package/dist/tools/discuss.js.map +1 -0
- package/dist/tools/file-system.d.ts +63 -0
- package/dist/tools/file-system.d.ts.map +1 -0
- package/dist/tools/file-system.js +513 -0
- package/dist/tools/file-system.js.map +1 -0
- package/dist/tools/guard.d.ts +29 -0
- package/dist/tools/guard.d.ts.map +1 -0
- package/dist/tools/guard.js +311 -0
- package/dist/tools/guard.js.map +1 -0
- package/dist/tools/index.d.ts +35 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +178 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/memory.d.ts +101 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +704 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/orchestration.d.ts +58 -0
- package/dist/tools/orchestration.d.ts.map +1 -0
- package/dist/tools/orchestration.js +714 -0
- package/dist/tools/orchestration.js.map +1 -0
- package/dist/tools/review.d.ts +40 -0
- package/dist/tools/review.d.ts.map +1 -0
- package/dist/tools/review.js +319 -0
- package/dist/tools/review.js.map +1 -0
- package/dist/tools/scaffold.d.ts +27 -0
- package/dist/tools/scaffold.d.ts.map +1 -0
- package/dist/tools/scaffold.js +495 -0
- package/dist/tools/scaffold.js.map +1 -0
- package/dist/tools/session.d.ts +75 -0
- package/dist/tools/session.d.ts.map +1 -0
- package/dist/tools/session.js +749 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/telemetry.d.ts +58 -0
- package/dist/tools/telemetry.d.ts.map +1 -0
- package/dist/tools/telemetry.js +638 -0
- package/dist/tools/telemetry.js.map +1 -0
- package/dist/tools/trace.d.ts +29 -0
- package/dist/tools/trace.d.ts.map +1 -0
- package/dist/tools/trace.js +191 -0
- package/dist/tools/trace.js.map +1 -0
- package/dist/tools/workflow.d.ts +26 -0
- package/dist/tools/workflow.d.ts.map +1 -0
- package/dist/tools/workflow.js +269 -0
- package/dist/tools/workflow.js.map +1 -0
- package/dist/trace-wrapper.d.ts +79 -0
- package/dist/trace-wrapper.d.ts.map +1 -0
- package/dist/trace-wrapper.js +151 -0
- package/dist/trace-wrapper.js.map +1 -0
- package/dist/types.d.ts +185 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/artifact-store.d.ts +49 -0
- package/dist/utils/artifact-store.d.ts.map +1 -0
- package/dist/utils/artifact-store.js +102 -0
- package/dist/utils/artifact-store.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +10 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/response.d.ts +139 -0
- package/dist/utils/response.d.ts.map +1 -0
- package/dist/utils/response.js +293 -0
- package/dist/utils/response.js.map +1 -0
- package/dist/validation.d.ts +223 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +372 -0
- package/dist/validation.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Rate Limiter Middleware
|
|
3
|
+
*
|
|
4
|
+
* Implements rate limiting for MCP tools using a sliding window algorithm
|
|
5
|
+
* with token bucket for burst handling.
|
|
6
|
+
*
|
|
7
|
+
* Invariants enforced:
|
|
8
|
+
* - INV-MCP-006: Rate limits enforced per tool
|
|
9
|
+
* - INV-MCP-007: RATE_LIMITED errors include retryAfter
|
|
10
|
+
* - INV-RL-001: Rate limits are enforced per tool
|
|
11
|
+
* - INV-RL-002: Exceeded limits return RATE_LIMITED error with retryAfter
|
|
12
|
+
* - INV-RL-003: Limits are configurable without code changes
|
|
13
|
+
* - INV-RL-004: Read tools have higher limits than mutation tools
|
|
14
|
+
*/
|
|
15
|
+
import type { RateLimitConfig, RateLimitResult, RateLimitError, McpRateLimitToolCategory as ToolCategory } from '@defai.digital/contracts';
|
|
16
|
+
/**
|
|
17
|
+
* Internal state for tracking rate limits per tool
|
|
18
|
+
*/
|
|
19
|
+
interface RateLimitBucket {
|
|
20
|
+
/** Tokens available (for burst) */
|
|
21
|
+
tokens: number;
|
|
22
|
+
/** Last refill timestamp */
|
|
23
|
+
lastRefill: number;
|
|
24
|
+
/** Request count in current minute window */
|
|
25
|
+
minuteCount: number;
|
|
26
|
+
/** Minute window start timestamp */
|
|
27
|
+
minuteStart: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Rate limiter configuration
|
|
31
|
+
*/
|
|
32
|
+
export interface RateLimiterConfig {
|
|
33
|
+
/** Whether rate limiting is enabled globally */
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
/** Custom limits per tool (overrides defaults) */
|
|
36
|
+
customLimits: Map<string, RateLimitConfig>;
|
|
37
|
+
/** Custom limits per category (overrides defaults) */
|
|
38
|
+
categoryLimits: Map<ToolCategory, RateLimitConfig>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Rate Limiter for MCP Tools
|
|
42
|
+
*
|
|
43
|
+
* Uses a combination of:
|
|
44
|
+
* 1. Token bucket for burst handling
|
|
45
|
+
* 2. Sliding window for requests-per-minute tracking
|
|
46
|
+
*/
|
|
47
|
+
export declare class RateLimiter {
|
|
48
|
+
private readonly buckets;
|
|
49
|
+
private config;
|
|
50
|
+
constructor(config?: Partial<RateLimiterConfig>);
|
|
51
|
+
/**
|
|
52
|
+
* Gets the effective rate limit for a tool
|
|
53
|
+
* Priority: customLimits > categoryLimits > DEFAULT_RATE_LIMITS
|
|
54
|
+
*/
|
|
55
|
+
getLimit(toolName: string): RateLimitConfig;
|
|
56
|
+
/**
|
|
57
|
+
* Checks if a tool call is allowed under rate limits
|
|
58
|
+
* INV-RL-001: Rate limits are enforced per tool
|
|
59
|
+
*/
|
|
60
|
+
check(toolName: string): RateLimitResult;
|
|
61
|
+
/**
|
|
62
|
+
* Consumes a rate limit token for a tool
|
|
63
|
+
* Call this after check() returns allowed: true and before executing the tool
|
|
64
|
+
*/
|
|
65
|
+
consume(toolName: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Checks and consumes in one operation
|
|
68
|
+
* Returns the check result, and if allowed, also consumes the token
|
|
69
|
+
*/
|
|
70
|
+
checkAndConsume(toolName: string): RateLimitResult;
|
|
71
|
+
/**
|
|
72
|
+
* Creates a rate limit error for rejected requests
|
|
73
|
+
* INV-MCP-007: RATE_LIMITED errors include retryAfter
|
|
74
|
+
*/
|
|
75
|
+
createError(toolName: string, result: RateLimitResult): RateLimitError;
|
|
76
|
+
/**
|
|
77
|
+
* Updates rate limiter configuration
|
|
78
|
+
* INV-RL-003: Limits are configurable without code changes
|
|
79
|
+
*/
|
|
80
|
+
configure(config: Partial<RateLimiterConfig>): void;
|
|
81
|
+
/**
|
|
82
|
+
* Sets a custom limit for a specific tool
|
|
83
|
+
*/
|
|
84
|
+
setToolLimit(toolName: string, limit: RateLimitConfig): void;
|
|
85
|
+
/**
|
|
86
|
+
* Sets a custom limit for a category
|
|
87
|
+
*/
|
|
88
|
+
setCategoryLimit(category: ToolCategory, limit: RateLimitConfig): void;
|
|
89
|
+
/**
|
|
90
|
+
* Removes custom limit for a tool (reverts to category/default)
|
|
91
|
+
*/
|
|
92
|
+
removeToolLimit(toolName: string): void;
|
|
93
|
+
/**
|
|
94
|
+
* Gets current state for a tool (for debugging/monitoring)
|
|
95
|
+
*/
|
|
96
|
+
getState(toolName: string): {
|
|
97
|
+
bucket: RateLimitBucket | undefined;
|
|
98
|
+
limit: RateLimitConfig;
|
|
99
|
+
category: ToolCategory;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Resets rate limit state for a tool
|
|
103
|
+
*/
|
|
104
|
+
reset(toolName: string): void;
|
|
105
|
+
/**
|
|
106
|
+
* Resets all rate limit state
|
|
107
|
+
*/
|
|
108
|
+
resetAll(): void;
|
|
109
|
+
/**
|
|
110
|
+
* Checks if rate limiting is enabled
|
|
111
|
+
*/
|
|
112
|
+
isEnabled(): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Gets or creates a bucket for a tool
|
|
115
|
+
*/
|
|
116
|
+
private getOrCreateBucket;
|
|
117
|
+
/**
|
|
118
|
+
* Refills tokens based on elapsed time
|
|
119
|
+
* Uses token bucket algorithm: tokens refill at rate of rpm/60 per second
|
|
120
|
+
*/
|
|
121
|
+
private refillTokens;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Gets the global rate limiter instance (singleton)
|
|
125
|
+
*/
|
|
126
|
+
export declare function getRateLimiter(): RateLimiter;
|
|
127
|
+
/**
|
|
128
|
+
* Sets the global rate limiter instance (for testing)
|
|
129
|
+
*/
|
|
130
|
+
export declare function setRateLimiter(limiter: RateLimiter): void;
|
|
131
|
+
/**
|
|
132
|
+
* Resets the global rate limiter (for testing)
|
|
133
|
+
*/
|
|
134
|
+
export declare function resetRateLimiter(): void;
|
|
135
|
+
export {};
|
|
136
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,cAAc,EACd,wBAAwB,IAAI,YAAY,EACzC,MAAM,0BAA0B,CAAC;AAOlC;;GAEG;AACH,UAAU,eAAe;IACvB,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IAEf,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IAEnB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IAEpB,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;IAEjB,kDAAkD;IAClD,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAE3C,sDAAsD;IACtD,cAAc,EAAE,GAAG,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;CACpD;AAED;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAC9D,OAAO,CAAC,MAAM,CAAoB;gBAEtB,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC;IAQ/C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe;IAkB3C;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe;IAuExC;;;OAGG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQ/B;;;OAGG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe;IAQlD;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,cAAc;IAQtE;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAYnD;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI;IAI5D;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI;IAItE;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIvC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG;QAC1B,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC;QACpC,KAAK,EAAE,eAAe,CAAC;QACvB,QAAQ,EAAE,YAAY,CAAC;KACxB;IAQD;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI7B;;OAEG;IACH,QAAQ,IAAI,IAAI;IAIhB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAoBzB;;;OAGG;IACH,OAAO,CAAC,YAAY;CAYrB;AAOD;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAK5C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAEzD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Rate Limiter Middleware
|
|
3
|
+
*
|
|
4
|
+
* Implements rate limiting for MCP tools using a sliding window algorithm
|
|
5
|
+
* with token bucket for burst handling.
|
|
6
|
+
*
|
|
7
|
+
* Invariants enforced:
|
|
8
|
+
* - INV-MCP-006: Rate limits enforced per tool
|
|
9
|
+
* - INV-MCP-007: RATE_LIMITED errors include retryAfter
|
|
10
|
+
* - INV-RL-001: Rate limits are enforced per tool
|
|
11
|
+
* - INV-RL-002: Exceeded limits return RATE_LIMITED error with retryAfter
|
|
12
|
+
* - INV-RL-003: Limits are configurable without code changes
|
|
13
|
+
* - INV-RL-004: Read tools have higher limits than mutation tools
|
|
14
|
+
*/
|
|
15
|
+
import { getToolCategory, getDefaultRateLimit, createRateLimitError, } from '@defai.digital/contracts';
|
|
16
|
+
/**
|
|
17
|
+
* Rate Limiter for MCP Tools
|
|
18
|
+
*
|
|
19
|
+
* Uses a combination of:
|
|
20
|
+
* 1. Token bucket for burst handling
|
|
21
|
+
* 2. Sliding window for requests-per-minute tracking
|
|
22
|
+
*/
|
|
23
|
+
export class RateLimiter {
|
|
24
|
+
buckets = new Map();
|
|
25
|
+
config;
|
|
26
|
+
constructor(config) {
|
|
27
|
+
this.config = {
|
|
28
|
+
enabled: config?.enabled ?? true,
|
|
29
|
+
customLimits: config?.customLimits ?? new Map(),
|
|
30
|
+
categoryLimits: config?.categoryLimits ?? new Map(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Gets the effective rate limit for a tool
|
|
35
|
+
* Priority: customLimits > categoryLimits > DEFAULT_RATE_LIMITS
|
|
36
|
+
*/
|
|
37
|
+
getLimit(toolName) {
|
|
38
|
+
// Check custom tool-specific limit
|
|
39
|
+
const customLimit = this.config.customLimits.get(toolName);
|
|
40
|
+
if (customLimit !== undefined) {
|
|
41
|
+
return customLimit;
|
|
42
|
+
}
|
|
43
|
+
// Check category limit
|
|
44
|
+
const category = getToolCategory(toolName);
|
|
45
|
+
const categoryLimit = this.config.categoryLimits.get(category);
|
|
46
|
+
if (categoryLimit !== undefined) {
|
|
47
|
+
return categoryLimit;
|
|
48
|
+
}
|
|
49
|
+
// Fall back to default
|
|
50
|
+
return getDefaultRateLimit(toolName);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Checks if a tool call is allowed under rate limits
|
|
54
|
+
* INV-RL-001: Rate limits are enforced per tool
|
|
55
|
+
*/
|
|
56
|
+
check(toolName) {
|
|
57
|
+
// If rate limiting is disabled, always allow
|
|
58
|
+
if (!this.config.enabled) {
|
|
59
|
+
const limit = this.getLimit(toolName);
|
|
60
|
+
return {
|
|
61
|
+
allowed: true,
|
|
62
|
+
remaining: limit.rpm,
|
|
63
|
+
resetIn: 0,
|
|
64
|
+
limit,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const limit = this.getLimit(toolName);
|
|
68
|
+
// If tool-specific rate limiting is disabled, allow
|
|
69
|
+
if (!limit.enabled) {
|
|
70
|
+
return {
|
|
71
|
+
allowed: true,
|
|
72
|
+
remaining: limit.rpm,
|
|
73
|
+
resetIn: 0,
|
|
74
|
+
limit,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
const bucket = this.getOrCreateBucket(toolName, limit, now);
|
|
79
|
+
// Refill tokens based on time elapsed
|
|
80
|
+
this.refillTokens(bucket, limit, now);
|
|
81
|
+
// Check minute window
|
|
82
|
+
const minuteElapsed = now - bucket.minuteStart;
|
|
83
|
+
if (minuteElapsed >= 60000) {
|
|
84
|
+
// Reset minute window
|
|
85
|
+
bucket.minuteCount = 0;
|
|
86
|
+
bucket.minuteStart = now;
|
|
87
|
+
}
|
|
88
|
+
// Calculate remaining and reset time
|
|
89
|
+
const remaining = Math.max(0, limit.rpm - bucket.minuteCount);
|
|
90
|
+
const resetIn = Math.ceil((60000 - (now - bucket.minuteStart)) / 1000);
|
|
91
|
+
// Check if request is allowed
|
|
92
|
+
// Must have both: tokens available AND not exceeded RPM
|
|
93
|
+
if (bucket.tokens < 1 || bucket.minuteCount >= limit.rpm) {
|
|
94
|
+
// INV-RL-002: Return retryAfter
|
|
95
|
+
// Guard against division by zero when rpm is 0 (rate limiting disabled at limit level)
|
|
96
|
+
const tokenRefillTime = limit.rpm > 0
|
|
97
|
+
? Math.ceil((1 / (limit.rpm / 60)) * 1000) / 1000 // Time for 1 token
|
|
98
|
+
: 60; // Default to 60 seconds if rpm is 0
|
|
99
|
+
const retryAfter = bucket.tokens < 1
|
|
100
|
+
? tokenRefillTime
|
|
101
|
+
: resetIn;
|
|
102
|
+
return {
|
|
103
|
+
allowed: false,
|
|
104
|
+
remaining,
|
|
105
|
+
resetIn,
|
|
106
|
+
retryAfter: Math.ceil(retryAfter),
|
|
107
|
+
limit,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
allowed: true,
|
|
112
|
+
remaining: remaining - 1, // Account for this request
|
|
113
|
+
resetIn,
|
|
114
|
+
limit,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Consumes a rate limit token for a tool
|
|
119
|
+
* Call this after check() returns allowed: true and before executing the tool
|
|
120
|
+
*/
|
|
121
|
+
consume(toolName) {
|
|
122
|
+
const bucket = this.buckets.get(toolName);
|
|
123
|
+
if (bucket !== undefined) {
|
|
124
|
+
bucket.tokens = Math.max(0, bucket.tokens - 1);
|
|
125
|
+
bucket.minuteCount++;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Checks and consumes in one operation
|
|
130
|
+
* Returns the check result, and if allowed, also consumes the token
|
|
131
|
+
*/
|
|
132
|
+
checkAndConsume(toolName) {
|
|
133
|
+
const result = this.check(toolName);
|
|
134
|
+
if (result.allowed) {
|
|
135
|
+
this.consume(toolName);
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Creates a rate limit error for rejected requests
|
|
141
|
+
* INV-MCP-007: RATE_LIMITED errors include retryAfter
|
|
142
|
+
*/
|
|
143
|
+
createError(toolName, result) {
|
|
144
|
+
return createRateLimitError(toolName, result.retryAfter ?? 60, result.limit.rpm);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Updates rate limiter configuration
|
|
148
|
+
* INV-RL-003: Limits are configurable without code changes
|
|
149
|
+
*/
|
|
150
|
+
configure(config) {
|
|
151
|
+
if (config.enabled !== undefined) {
|
|
152
|
+
this.config.enabled = config.enabled;
|
|
153
|
+
}
|
|
154
|
+
if (config.customLimits !== undefined) {
|
|
155
|
+
this.config.customLimits = config.customLimits;
|
|
156
|
+
}
|
|
157
|
+
if (config.categoryLimits !== undefined) {
|
|
158
|
+
this.config.categoryLimits = config.categoryLimits;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Sets a custom limit for a specific tool
|
|
163
|
+
*/
|
|
164
|
+
setToolLimit(toolName, limit) {
|
|
165
|
+
this.config.customLimits.set(toolName, limit);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Sets a custom limit for a category
|
|
169
|
+
*/
|
|
170
|
+
setCategoryLimit(category, limit) {
|
|
171
|
+
this.config.categoryLimits.set(category, limit);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Removes custom limit for a tool (reverts to category/default)
|
|
175
|
+
*/
|
|
176
|
+
removeToolLimit(toolName) {
|
|
177
|
+
this.config.customLimits.delete(toolName);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Gets current state for a tool (for debugging/monitoring)
|
|
181
|
+
*/
|
|
182
|
+
getState(toolName) {
|
|
183
|
+
return {
|
|
184
|
+
bucket: this.buckets.get(toolName),
|
|
185
|
+
limit: this.getLimit(toolName),
|
|
186
|
+
category: getToolCategory(toolName),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Resets rate limit state for a tool
|
|
191
|
+
*/
|
|
192
|
+
reset(toolName) {
|
|
193
|
+
this.buckets.delete(toolName);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Resets all rate limit state
|
|
197
|
+
*/
|
|
198
|
+
resetAll() {
|
|
199
|
+
this.buckets.clear();
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Checks if rate limiting is enabled
|
|
203
|
+
*/
|
|
204
|
+
isEnabled() {
|
|
205
|
+
return this.config.enabled;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Gets or creates a bucket for a tool
|
|
209
|
+
*/
|
|
210
|
+
getOrCreateBucket(toolName, limit, now) {
|
|
211
|
+
let bucket = this.buckets.get(toolName);
|
|
212
|
+
if (bucket === undefined) {
|
|
213
|
+
bucket = {
|
|
214
|
+
tokens: limit.burst,
|
|
215
|
+
lastRefill: now,
|
|
216
|
+
minuteCount: 0,
|
|
217
|
+
minuteStart: now,
|
|
218
|
+
};
|
|
219
|
+
this.buckets.set(toolName, bucket);
|
|
220
|
+
}
|
|
221
|
+
return bucket;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Refills tokens based on elapsed time
|
|
225
|
+
* Uses token bucket algorithm: tokens refill at rate of rpm/60 per second
|
|
226
|
+
*/
|
|
227
|
+
refillTokens(bucket, limit, now) {
|
|
228
|
+
const elapsed = now - bucket.lastRefill;
|
|
229
|
+
const refillRate = limit.rpm / 60000; // Tokens per millisecond
|
|
230
|
+
const tokensToAdd = elapsed * refillRate;
|
|
231
|
+
bucket.tokens = Math.min(limit.burst, bucket.tokens + tokensToAdd);
|
|
232
|
+
bucket.lastRefill = now;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Global rate limiter instance
|
|
237
|
+
*/
|
|
238
|
+
let globalRateLimiter = null;
|
|
239
|
+
/**
|
|
240
|
+
* Gets the global rate limiter instance (singleton)
|
|
241
|
+
*/
|
|
242
|
+
export function getRateLimiter() {
|
|
243
|
+
if (globalRateLimiter === null) {
|
|
244
|
+
globalRateLimiter = new RateLimiter();
|
|
245
|
+
}
|
|
246
|
+
return globalRateLimiter;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Sets the global rate limiter instance (for testing)
|
|
250
|
+
*/
|
|
251
|
+
export function setRateLimiter(limiter) {
|
|
252
|
+
globalRateLimiter = limiter;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Resets the global rate limiter (for testing)
|
|
256
|
+
*/
|
|
257
|
+
export function resetRateLimiter() {
|
|
258
|
+
if (globalRateLimiter !== null) {
|
|
259
|
+
globalRateLimiter.resetAll();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Guide MCP Prompt
|
|
3
|
+
*
|
|
4
|
+
* Provides comprehensive documentation for using the AutomatosX agent system.
|
|
5
|
+
* Teaches Claude Code (and other consumers) how to effectively use agents.
|
|
6
|
+
*/
|
|
7
|
+
import type { MCPPrompt, PromptHandler } from '../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Agent guide prompt definition
|
|
10
|
+
*/
|
|
11
|
+
export declare const agentGuidePrompt: MCPPrompt;
|
|
12
|
+
/**
|
|
13
|
+
* Handler for agent-guide prompt
|
|
14
|
+
*/
|
|
15
|
+
export declare const handleAgentGuide: PromptHandler;
|
|
16
|
+
//# sourceMappingURL=agent-guide.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guide.d.ts","sourceRoot":"","sources":["../../src/prompts/agent-guide.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAoB,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAU9B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,aAwX9B,CAAC"}
|