@frontmcp/guard 0.0.1

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.
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Guard Manager
3
+ *
4
+ * Central coordinator for rate limiting, concurrency control, IP filtering,
5
+ * and timeout within a scope. SDK-agnostic — built on StorageAdapter.
6
+ */
7
+ import type { NamespacedStorage } from '@frontmcp/utils';
8
+ import type { GuardConfig } from './types';
9
+ import type { RateLimitConfig, RateLimitResult } from '../rate-limit/types';
10
+ import type { ConcurrencyConfig, SemaphoreTicket } from '../concurrency/types';
11
+ import type { PartitionKeyContext } from '../partition-key/types';
12
+ import type { IpFilterResult } from '../ip-filter/types';
13
+ export declare class GuardManager {
14
+ private readonly storage;
15
+ private readonly rateLimiter;
16
+ private readonly semaphore;
17
+ private readonly ipFilter?;
18
+ readonly config: GuardConfig;
19
+ constructor(storage: NamespacedStorage, config: GuardConfig);
20
+ /**
21
+ * Check if a client IP is allowed by the IP filter.
22
+ * Returns undefined if no IP filter is configured.
23
+ */
24
+ checkIpFilter(clientIp: string | undefined): IpFilterResult | undefined;
25
+ /**
26
+ * Check if a client IP is on the allow list (bypasses rate limiting).
27
+ */
28
+ isIpAllowListed(clientIp: string | undefined): boolean;
29
+ /**
30
+ * Check per-entity rate limit.
31
+ * Merges entity config with app-level defaults (entity takes precedence).
32
+ */
33
+ checkRateLimit(entityName: string, entityConfig: RateLimitConfig | undefined, context: PartitionKeyContext | undefined): Promise<RateLimitResult>;
34
+ /**
35
+ * Check global rate limit.
36
+ */
37
+ checkGlobalRateLimit(context: PartitionKeyContext | undefined): Promise<RateLimitResult>;
38
+ /**
39
+ * Acquire a concurrency slot for an entity.
40
+ */
41
+ acquireSemaphore(entityName: string, entityConfig: ConcurrencyConfig | undefined, context: PartitionKeyContext | undefined): Promise<SemaphoreTicket | null>;
42
+ /**
43
+ * Acquire a global concurrency slot.
44
+ */
45
+ acquireGlobalSemaphore(context: PartitionKeyContext | undefined): Promise<SemaphoreTicket | null>;
46
+ destroy(): Promise<void>;
47
+ }
@@ -0,0 +1,3 @@
1
+ export type { GuardConfig, GuardLogger, CreateGuardManagerArgs } from './types';
2
+ export { GuardManager } from './guard.manager';
3
+ export { createGuardManager } from './guard.factory';
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Guard Manager Types
3
+ */
4
+ import type { StorageConfig } from '@frontmcp/utils';
5
+ import type { RateLimitConfig } from '../rate-limit/types';
6
+ import type { ConcurrencyConfig } from '../concurrency/types';
7
+ import type { TimeoutConfig } from '../timeout/types';
8
+ import type { IpFilterConfig } from '../ip-filter/types';
9
+ /**
10
+ * Full guard configuration — SDK-agnostic.
11
+ */
12
+ export interface GuardConfig {
13
+ /** Whether the guard system is enabled. */
14
+ enabled: boolean;
15
+ /** Storage backend. */
16
+ storage?: StorageConfig;
17
+ /** Key prefix for all storage keys. @default 'mcp:guard:' */
18
+ keyPrefix?: string;
19
+ /** Global rate limit applied to ALL requests. */
20
+ global?: RateLimitConfig;
21
+ /** Global concurrency limit. */
22
+ globalConcurrency?: ConcurrencyConfig;
23
+ /** Default rate limit for entities without explicit config. */
24
+ defaultRateLimit?: RateLimitConfig;
25
+ /** Default concurrency for entities without explicit config. */
26
+ defaultConcurrency?: ConcurrencyConfig;
27
+ /** Default timeout for entity execution. */
28
+ defaultTimeout?: TimeoutConfig;
29
+ /** IP filtering configuration. */
30
+ ipFilter?: IpFilterConfig;
31
+ }
32
+ /**
33
+ * Minimal logger interface — any logger that has info/warn methods.
34
+ */
35
+ export interface GuardLogger {
36
+ info(msg: string, ...args: unknown[]): void;
37
+ warn(msg: string, ...args: unknown[]): void;
38
+ }
39
+ /**
40
+ * Arguments for createGuardManager factory.
41
+ */
42
+ export interface CreateGuardManagerArgs {
43
+ config: GuardConfig;
44
+ logger?: GuardLogger;
45
+ }
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@frontmcp/guard",
3
+ "version": "0.0.1",
4
+ "description": "Rate limiting, concurrency control, timeout, IP filtering, and traffic guard utilities for FrontMCP",
5
+ "author": "AgentFront <info@agentfront.dev>",
6
+ "license": "Apache-2.0",
7
+ "keywords": [
8
+ "rate-limiting",
9
+ "concurrency",
10
+ "semaphore",
11
+ "timeout",
12
+ "ip-filter",
13
+ "guard",
14
+ "throttle",
15
+ "distributed",
16
+ "redis",
17
+ "typescript"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/agentfront/frontmcp.git",
22
+ "directory": "libs/guard"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/agentfront/frontmcp/issues"
26
+ },
27
+ "homepage": "https://github.com/agentfront/frontmcp/blob/main/libs/guard/README.md",
28
+ "type": "commonjs",
29
+ "main": "./index.js",
30
+ "module": "./esm/index.mjs",
31
+ "types": "./index.d.ts",
32
+ "sideEffects": false,
33
+ "exports": {
34
+ "./package.json": "./package.json",
35
+ ".": {
36
+ "require": {
37
+ "types": "./index.d.ts",
38
+ "default": "./index.js"
39
+ },
40
+ "import": {
41
+ "types": "./index.d.ts",
42
+ "default": "./esm/index.mjs"
43
+ }
44
+ },
45
+ "./esm": null
46
+ },
47
+ "engines": {
48
+ "node": ">=22.0.0"
49
+ },
50
+ "dependencies": {
51
+ "@frontmcp/utils": "1.0.0"
52
+ },
53
+ "peerDependencies": {
54
+ "zod": "^4.0.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "^24.0.0",
58
+ "typescript": "^5.0.0",
59
+ "zod": "^4.0.0"
60
+ }
61
+ }
@@ -0,0 +1,2 @@
1
+ export type { PartitionKeyStrategy, CustomPartitionKeyFn, PartitionKeyContext, PartitionKey } from './types';
2
+ export { resolvePartitionKey, buildStorageKey } from './partition-key.resolver';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Partition Key Resolver
3
+ *
4
+ * Resolves a partition key string from a PartitionKey config and context.
5
+ */
6
+ import type { PartitionKey, PartitionKeyContext } from './types';
7
+ /**
8
+ * Resolve a partition key string from the given strategy and context.
9
+ */
10
+ export declare function resolvePartitionKey(partitionBy: PartitionKey | undefined, context: PartitionKeyContext | undefined): string;
11
+ /**
12
+ * Build a full storage key combining entity name, partition key, and optional suffix.
13
+ */
14
+ export declare function buildStorageKey(entityName: string, partitionKey: string, suffix?: string): string;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Partition Key Types
3
+ */
4
+ /**
5
+ * Built-in partition key strategies.
6
+ * - 'ip': partition by client IP
7
+ * - 'session': partition by session ID
8
+ * - 'userId': partition by authenticated user ID
9
+ * - 'global': no partition — shared limit across all callers
10
+ */
11
+ export type PartitionKeyStrategy = 'ip' | 'session' | 'userId' | 'global';
12
+ /**
13
+ * Custom partition key resolver function.
14
+ */
15
+ export type CustomPartitionKeyFn = (ctx: PartitionKeyContext) => string;
16
+ /**
17
+ * Context provided to custom partition key resolvers.
18
+ */
19
+ export interface PartitionKeyContext {
20
+ sessionId: string;
21
+ clientIp?: string;
22
+ userId?: string;
23
+ }
24
+ /**
25
+ * Partition key configuration — either a built-in strategy or a custom function.
26
+ */
27
+ export type PartitionKey = PartitionKeyStrategy | CustomPartitionKeyFn;
@@ -0,0 +1,2 @@
1
+ export type { RateLimitConfig, RateLimitResult } from './types';
2
+ export { SlidingWindowRateLimiter } from './rate-limiter';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Sliding Window Rate Limiter
3
+ *
4
+ * Implements the sliding window counter algorithm for rate limiting.
5
+ * Uses two adjacent fixed-window counters with weighted interpolation
6
+ * to approximate a true sliding window with O(1) storage per check.
7
+ *
8
+ * Built entirely on StorageAdapter interface (incr, mget, expire) —
9
+ * works with Memory, Redis, Vercel KV, Upstash backends.
10
+ */
11
+ import type { StorageAdapter } from '@frontmcp/utils';
12
+ import type { RateLimitResult } from './types';
13
+ export declare class SlidingWindowRateLimiter {
14
+ private readonly storage;
15
+ constructor(storage: StorageAdapter);
16
+ /**
17
+ * Check whether a request is allowed under the rate limit.
18
+ * If allowed, the counter is atomically incremented.
19
+ */
20
+ check(key: string, maxRequests: number, windowMs: number): Promise<RateLimitResult>;
21
+ /**
22
+ * Reset the rate limit counters for a key.
23
+ */
24
+ reset(key: string, windowMs: number): Promise<void>;
25
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Rate Limiting Types
3
+ */
4
+ import type { PartitionKey } from '../partition-key/types';
5
+ /**
6
+ * Rate limiting configuration.
7
+ * Uses sliding window counter algorithm.
8
+ */
9
+ export interface RateLimitConfig {
10
+ /** Maximum number of requests allowed within the window. */
11
+ maxRequests: number;
12
+ /** Time window in milliseconds. @default 60_000 */
13
+ windowMs?: number;
14
+ /** Partition key strategy. @default 'global' */
15
+ partitionBy?: PartitionKey;
16
+ }
17
+ /**
18
+ * Result from a rate limiter check.
19
+ */
20
+ export interface RateLimitResult {
21
+ allowed: boolean;
22
+ remaining: number;
23
+ resetMs: number;
24
+ retryAfterMs?: number;
25
+ }
@@ -0,0 +1 @@
1
+ export { partitionKeySchema, rateLimitConfigSchema, concurrencyConfigSchema, timeoutConfigSchema, ipFilterConfigSchema, guardConfigSchema, } from './schemas';
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Zod validation schemas for guard configuration.
3
+ */
4
+ import { z } from 'zod';
5
+ export declare const partitionKeySchema: z.ZodUnion<readonly [z.ZodEnum<{
6
+ ip: "ip";
7
+ session: "session";
8
+ userId: "userId";
9
+ global: "global";
10
+ }>, z.ZodCustom<(ctx: {
11
+ sessionId: string;
12
+ clientIp?: string;
13
+ userId?: string;
14
+ }) => string, (ctx: {
15
+ sessionId: string;
16
+ clientIp?: string;
17
+ userId?: string;
18
+ }) => string>]>;
19
+ export declare const rateLimitConfigSchema: z.ZodObject<{
20
+ maxRequests: z.ZodNumber;
21
+ windowMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
22
+ partitionBy: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
23
+ ip: "ip";
24
+ session: "session";
25
+ userId: "userId";
26
+ global: "global";
27
+ }>, z.ZodCustom<(ctx: {
28
+ sessionId: string;
29
+ clientIp?: string;
30
+ userId?: string;
31
+ }) => string, (ctx: {
32
+ sessionId: string;
33
+ clientIp?: string;
34
+ userId?: string;
35
+ }) => string>]>>>;
36
+ }, z.core.$strip>;
37
+ export declare const concurrencyConfigSchema: z.ZodObject<{
38
+ maxConcurrent: z.ZodNumber;
39
+ queueTimeoutMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
40
+ partitionBy: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
41
+ ip: "ip";
42
+ session: "session";
43
+ userId: "userId";
44
+ global: "global";
45
+ }>, z.ZodCustom<(ctx: {
46
+ sessionId: string;
47
+ clientIp?: string;
48
+ userId?: string;
49
+ }) => string, (ctx: {
50
+ sessionId: string;
51
+ clientIp?: string;
52
+ userId?: string;
53
+ }) => string>]>>>;
54
+ }, z.core.$strip>;
55
+ export declare const timeoutConfigSchema: z.ZodObject<{
56
+ executeMs: z.ZodNumber;
57
+ }, z.core.$strip>;
58
+ export declare const ipFilterConfigSchema: z.ZodObject<{
59
+ allowList: z.ZodOptional<z.ZodArray<z.ZodString>>;
60
+ denyList: z.ZodOptional<z.ZodArray<z.ZodString>>;
61
+ defaultAction: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
62
+ allow: "allow";
63
+ deny: "deny";
64
+ }>>>;
65
+ trustProxy: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
66
+ trustedProxyDepth: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
67
+ }, z.core.$strip>;
68
+ export declare const guardConfigSchema: z.ZodObject<{
69
+ enabled: z.ZodBoolean;
70
+ storage: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
71
+ keyPrefix: z.ZodDefault<z.ZodOptional<z.ZodString>>;
72
+ global: z.ZodOptional<z.ZodObject<{
73
+ maxRequests: z.ZodNumber;
74
+ windowMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
75
+ partitionBy: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
76
+ ip: "ip";
77
+ session: "session";
78
+ userId: "userId";
79
+ global: "global";
80
+ }>, z.ZodCustom<(ctx: {
81
+ sessionId: string;
82
+ clientIp?: string;
83
+ userId?: string;
84
+ }) => string, (ctx: {
85
+ sessionId: string;
86
+ clientIp?: string;
87
+ userId?: string;
88
+ }) => string>]>>>;
89
+ }, z.core.$strip>>;
90
+ globalConcurrency: z.ZodOptional<z.ZodObject<{
91
+ maxConcurrent: z.ZodNumber;
92
+ queueTimeoutMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
93
+ partitionBy: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
94
+ ip: "ip";
95
+ session: "session";
96
+ userId: "userId";
97
+ global: "global";
98
+ }>, z.ZodCustom<(ctx: {
99
+ sessionId: string;
100
+ clientIp?: string;
101
+ userId?: string;
102
+ }) => string, (ctx: {
103
+ sessionId: string;
104
+ clientIp?: string;
105
+ userId?: string;
106
+ }) => string>]>>>;
107
+ }, z.core.$strip>>;
108
+ defaultRateLimit: z.ZodOptional<z.ZodObject<{
109
+ maxRequests: z.ZodNumber;
110
+ windowMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
111
+ partitionBy: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
112
+ ip: "ip";
113
+ session: "session";
114
+ userId: "userId";
115
+ global: "global";
116
+ }>, z.ZodCustom<(ctx: {
117
+ sessionId: string;
118
+ clientIp?: string;
119
+ userId?: string;
120
+ }) => string, (ctx: {
121
+ sessionId: string;
122
+ clientIp?: string;
123
+ userId?: string;
124
+ }) => string>]>>>;
125
+ }, z.core.$strip>>;
126
+ defaultConcurrency: z.ZodOptional<z.ZodObject<{
127
+ maxConcurrent: z.ZodNumber;
128
+ queueTimeoutMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
129
+ partitionBy: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
130
+ ip: "ip";
131
+ session: "session";
132
+ userId: "userId";
133
+ global: "global";
134
+ }>, z.ZodCustom<(ctx: {
135
+ sessionId: string;
136
+ clientIp?: string;
137
+ userId?: string;
138
+ }) => string, (ctx: {
139
+ sessionId: string;
140
+ clientIp?: string;
141
+ userId?: string;
142
+ }) => string>]>>>;
143
+ }, z.core.$strip>>;
144
+ defaultTimeout: z.ZodOptional<z.ZodObject<{
145
+ executeMs: z.ZodNumber;
146
+ }, z.core.$strip>>;
147
+ ipFilter: z.ZodOptional<z.ZodObject<{
148
+ allowList: z.ZodOptional<z.ZodArray<z.ZodString>>;
149
+ denyList: z.ZodOptional<z.ZodArray<z.ZodString>>;
150
+ defaultAction: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
151
+ allow: "allow";
152
+ deny: "deny";
153
+ }>>>;
154
+ trustProxy: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
155
+ trustedProxyDepth: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
156
+ }, z.core.$strip>>;
157
+ }, z.core.$strip>;
@@ -0,0 +1,2 @@
1
+ export type { TimeoutConfig } from './types';
2
+ export { withTimeout } from './timeout';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Timeout Utility
3
+ *
4
+ * Wraps an async function with a deadline using AbortController + Promise.race.
5
+ */
6
+ /**
7
+ * Execute a function with a timeout. Throws ExecutionTimeoutError if exceeded.
8
+ */
9
+ export declare function withTimeout<T>(fn: () => Promise<T>, timeoutMs: number, entityName: string): Promise<T>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Timeout Types
3
+ */
4
+ /**
5
+ * Timeout configuration.
6
+ */
7
+ export interface TimeoutConfig {
8
+ /** Maximum execution time in milliseconds. */
9
+ executeMs: number;
10
+ }