@frontmcp/plugins 0.6.0 → 0.6.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.
- package/package.json +14 -3
- package/src/cache/cache.plugin.js +33 -1
- package/src/cache/cache.plugin.js.map +1 -1
- package/src/cache/cache.types.d.ts +26 -3
- package/src/cache/cache.types.js.map +1 -1
- package/src/cache/providers/cache-memory.provider.d.ts +3 -3
- package/src/cache/providers/cache-memory.provider.js +2 -2
- package/src/cache/providers/cache-memory.provider.js.map +1 -1
- package/src/cache/providers/cache-redis.provider.d.ts +3 -3
- package/src/cache/providers/cache-redis.provider.js +1 -1
- package/src/cache/providers/cache-redis.provider.js.map +1 -1
- package/src/cache/providers/cache-vercel-kv.provider.d.ts +24 -0
- package/src/cache/providers/cache-vercel-kv.provider.js +85 -0
- package/src/cache/providers/cache-vercel-kv.provider.js.map +1 -0
- package/src/codecall/tools/invoke.schema.d.ts +6 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugins",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "FrontMCP plugins to extend the SDK",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"homepage": "https://docs.agentfront.dev",
|
|
@@ -33,11 +33,22 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"ioredis": "^5.8.0",
|
|
36
|
-
"@frontmcp/sdk": "0.6.
|
|
36
|
+
"@frontmcp/sdk": "0.6.1",
|
|
37
37
|
"enclave-vm": "^1.0.3",
|
|
38
38
|
"vectoriadb": "^2.0.1",
|
|
39
39
|
"zod": "^4.0.0",
|
|
40
|
-
"@modelcontextprotocol/sdk": "1.
|
|
40
|
+
"@modelcontextprotocol/sdk": "1.25.1"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"reflect-metadata": "^0.2.2"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@vercel/kv": "^2.0.0 || ^3.0.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"@vercel/kv": {
|
|
50
|
+
"optional": true
|
|
51
|
+
}
|
|
41
52
|
},
|
|
42
53
|
"type": "commonjs"
|
|
43
54
|
}
|
|
@@ -5,12 +5,44 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const sdk_1 = require("@frontmcp/sdk");
|
|
6
6
|
const cache_redis_provider_1 = tslib_1.__importDefault(require("./providers/cache-redis.provider"));
|
|
7
7
|
const cache_memory_provider_1 = tslib_1.__importDefault(require("./providers/cache-memory.provider"));
|
|
8
|
+
const cache_vercel_kv_provider_1 = tslib_1.__importDefault(require("./providers/cache-vercel-kv.provider"));
|
|
8
9
|
const cache_symbol_1 = require("./cache.symbol");
|
|
9
10
|
let CachePlugin = class CachePlugin extends sdk_1.DynamicPlugin {
|
|
10
11
|
static { CachePlugin_1 = this; }
|
|
11
12
|
static dynamicProviders = (options) => {
|
|
12
13
|
const providers = [];
|
|
13
14
|
switch (options.type) {
|
|
15
|
+
case 'global-store':
|
|
16
|
+
// Use inject/useFactory to access FrontMcpConfig at runtime
|
|
17
|
+
providers.push({
|
|
18
|
+
name: 'cache:global-store',
|
|
19
|
+
provide: cache_symbol_1.CacheStoreToken,
|
|
20
|
+
inject: () => [sdk_1.FrontMcpConfig],
|
|
21
|
+
useFactory: (config) => {
|
|
22
|
+
const storeConfig = (0, sdk_1.getGlobalStoreConfig)('CachePlugin', config);
|
|
23
|
+
const globalOptions = options;
|
|
24
|
+
if ((0, sdk_1.isVercelKvProvider)(storeConfig)) {
|
|
25
|
+
return new cache_vercel_kv_provider_1.default({
|
|
26
|
+
url: storeConfig.url,
|
|
27
|
+
token: storeConfig.token,
|
|
28
|
+
keyPrefix: storeConfig.keyPrefix,
|
|
29
|
+
defaultTTL: globalOptions.defaultTTL,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
// Redis provider (including legacy format without provider field)
|
|
33
|
+
return new cache_redis_provider_1.default({
|
|
34
|
+
type: 'redis',
|
|
35
|
+
config: {
|
|
36
|
+
host: storeConfig.host ?? 'localhost',
|
|
37
|
+
port: storeConfig.port ?? 6379,
|
|
38
|
+
password: storeConfig.password,
|
|
39
|
+
db: storeConfig.db,
|
|
40
|
+
},
|
|
41
|
+
defaultTTL: globalOptions.defaultTTL,
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
break;
|
|
14
46
|
case 'redis':
|
|
15
47
|
case 'redis-client':
|
|
16
48
|
providers.push({
|
|
@@ -126,7 +158,7 @@ function hashObject(obj) {
|
|
|
126
158
|
acc += hashObject(val);
|
|
127
159
|
}
|
|
128
160
|
else {
|
|
129
|
-
acc += val;
|
|
161
|
+
acc += String(val);
|
|
130
162
|
}
|
|
131
163
|
acc += ';';
|
|
132
164
|
return acc;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.plugin.js","sourceRoot":"","sources":["../../../src/cache/cache.plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { DynamicPlugin, FlowCtxOf, Plugin, ProviderType, ToolHook } from '@frontmcp/sdk';\nimport CacheRedisProvider from './providers/cache-redis.provider';\nimport CacheMemoryProvider from './providers/cache-memory.provider';\nimport { CachePluginOptions } from './cache.types';\nimport { CacheStoreToken } from './cache.symbol';\n\n@Plugin({\n name: 'cache',\n description: 'Cache plugin for caching tool results',\n providers: [\n /* add providers that always loaded with the plugin or default providers */\n {\n // this is a default provider for cache, will be overridden if dynamicProviders based on config\n name: 'cache:memory',\n provide: CacheStoreToken,\n useValue: new CacheMemoryProvider(60 * 60 * 24),\n },\n ],\n})\nexport default class CachePlugin extends DynamicPlugin<CachePluginOptions> {\n static override dynamicProviders = (options: CachePluginOptions) => {\n const providers: ProviderType[] = [];\n switch (options.type) {\n case 'redis':\n case 'redis-client':\n providers.push({\n name: 'cache:redis',\n provide: CacheStoreToken,\n useValue: new CacheRedisProvider(options),\n });\n break;\n case 'memory':\n providers.push({\n name: 'cache:memory',\n provide: CacheStoreToken,\n useValue: new CacheMemoryProvider(options.defaultTTL),\n });\n break;\n }\n return providers;\n };\n\n static defaultOptions: CachePluginOptions = {\n type: 'memory',\n };\n options: CachePluginOptions;\n\n constructor(options: CachePluginOptions = CachePlugin.defaultOptions) {\n super();\n this.options = {\n defaultTTL: 60 * 60 * 24,\n ...options,\n };\n }\n\n @ToolHook.Will('execute', { priority: 1000 })\n async willReadCache(flowCtx: FlowCtxOf<'tools:call-tool'>) {\n const { tool, toolContext } = flowCtx.state;\n if (!tool || !toolContext) return;\n\n const { cache } = toolContext.metadata;\n if (!cache || typeof toolContext.input === 'undefined') {\n // no cache or no input, skip\n return;\n }\n const cacheStore = this.get(CacheStoreToken);\n const hash = hashObject({ tool: tool.fullName, input: toolContext.input });\n const cached = await cacheStore.getValue(hash);\n\n if (cached !== undefined && cached !== null) {\n if (cache === true || (cache.ttl && cache.slideWindow)) {\n const ttl = cache === true ? this.options.defaultTTL : cache.ttl ?? this.options.defaultTTL;\n await cacheStore.setValue(hash, cached, ttl);\n }\n\n /**\n * double check if cache still valid based on tool output schema\n */\n if (!tool.safeParseOutput(cached).success) {\n await cacheStore.delete(hash);\n return;\n }\n /**\n * cache hit, set output to the main flow context\n */\n flowCtx.state.rawOutput = cached;\n\n /**\n * call respond to bypass tool execution\n */\n toolContext.respond(cached);\n }\n }\n\n @ToolHook.Did('execute', { priority: 1000 })\n async willWriteCache(flowCtx: FlowCtxOf<'tools:call-tool'>) {\n const { tool, toolContext } = flowCtx.state;\n if (!tool || !toolContext) return;\n const { cache } = toolContext.metadata;\n if (!cache || typeof toolContext.input === 'undefined') {\n return;\n }\n const cacheStore = this.get(CacheStoreToken);\n const ttl = cache === true ? this.options.defaultTTL : cache.ttl ?? this.options.defaultTTL;\n\n const hash = hashObject({ tool: tool.fullName, input: toolContext.input });\n await cacheStore.setValue(hash, toolContext.output, ttl);\n }\n}\n\nfunction hashObject(obj: any) {\n const keys = Object.keys(obj).sort();\n return keys.reduce((acc, key) => {\n acc += key + ':';\n const val = obj[key];\n if (typeof val === 'object' && val !== null) {\n acc += hashObject(val);\n } else {\n acc += val;\n }\n acc += ';';\n return acc;\n }, '');\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cache.plugin.js","sourceRoot":"","sources":["../../../src/cache/cache.plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DynamicPlugin,\n FlowCtxOf,\n Plugin,\n ProviderType,\n ToolHook,\n FrontMcpConfig,\n FrontMcpConfigType,\n getGlobalStoreConfig,\n isVercelKvProvider,\n} from '@frontmcp/sdk';\nimport CacheRedisProvider from './providers/cache-redis.provider';\nimport CacheMemoryProvider from './providers/cache-memory.provider';\nimport CacheVercelKvProvider from './providers/cache-vercel-kv.provider';\nimport { CachePluginOptions, GlobalStoreCachePluginOptions } from './cache.types';\nimport { CacheStoreToken } from './cache.symbol';\n\n@Plugin({\n name: 'cache',\n description: 'Cache plugin for caching tool results',\n providers: [\n /* add providers that always loaded with the plugin or default providers */\n {\n // this is a default provider for cache, will be overridden if dynamicProviders based on config\n name: 'cache:memory',\n provide: CacheStoreToken,\n useValue: new CacheMemoryProvider(60 * 60 * 24),\n },\n ],\n})\nexport default class CachePlugin extends DynamicPlugin<CachePluginOptions> {\n static override dynamicProviders = (options: CachePluginOptions) => {\n const providers: ProviderType[] = [];\n switch (options.type) {\n case 'global-store':\n // Use inject/useFactory to access FrontMcpConfig at runtime\n providers.push({\n name: 'cache:global-store',\n provide: CacheStoreToken,\n inject: () => [FrontMcpConfig] as const,\n useFactory: (config: FrontMcpConfigType) => {\n const storeConfig = getGlobalStoreConfig('CachePlugin', config);\n const globalOptions = options as GlobalStoreCachePluginOptions;\n\n if (isVercelKvProvider(storeConfig)) {\n return new CacheVercelKvProvider({\n url: storeConfig.url,\n token: storeConfig.token,\n keyPrefix: storeConfig.keyPrefix,\n defaultTTL: globalOptions.defaultTTL,\n });\n }\n\n // Redis provider (including legacy format without provider field)\n return new CacheRedisProvider({\n type: 'redis',\n config: {\n host: storeConfig.host ?? 'localhost',\n port: storeConfig.port ?? 6379,\n password: storeConfig.password,\n db: storeConfig.db,\n },\n defaultTTL: globalOptions.defaultTTL,\n });\n },\n });\n break;\n case 'redis':\n case 'redis-client':\n providers.push({\n name: 'cache:redis',\n provide: CacheStoreToken,\n useValue: new CacheRedisProvider(options),\n });\n break;\n case 'memory':\n providers.push({\n name: 'cache:memory',\n provide: CacheStoreToken,\n useValue: new CacheMemoryProvider(options.defaultTTL),\n });\n break;\n }\n return providers;\n };\n\n static defaultOptions: CachePluginOptions = {\n type: 'memory',\n };\n options: CachePluginOptions;\n\n constructor(options: CachePluginOptions = CachePlugin.defaultOptions) {\n super();\n this.options = {\n defaultTTL: 60 * 60 * 24,\n ...options,\n };\n }\n\n @ToolHook.Will('execute', { priority: 1000 })\n async willReadCache(flowCtx: FlowCtxOf<'tools:call-tool'>) {\n const { tool, toolContext } = flowCtx.state;\n if (!tool || !toolContext) return;\n\n const { cache } = toolContext.metadata;\n if (!cache || typeof toolContext.input === 'undefined') {\n // no cache or no input, skip\n return;\n }\n const cacheStore = this.get(CacheStoreToken);\n const hash = hashObject({ tool: tool.fullName, input: toolContext.input });\n const cached = await cacheStore.getValue(hash);\n\n if (cached !== undefined && cached !== null) {\n if (cache === true || (cache.ttl && cache.slideWindow)) {\n const ttl = cache === true ? this.options.defaultTTL : cache.ttl ?? this.options.defaultTTL;\n await cacheStore.setValue(hash, cached, ttl);\n }\n\n /**\n * double check if cache still valid based on tool output schema\n */\n if (!tool.safeParseOutput(cached).success) {\n await cacheStore.delete(hash);\n return;\n }\n /**\n * cache hit, set output to the main flow context\n */\n flowCtx.state.rawOutput = cached;\n\n /**\n * call respond to bypass tool execution\n */\n toolContext.respond(cached);\n }\n }\n\n @ToolHook.Did('execute', { priority: 1000 })\n async willWriteCache(flowCtx: FlowCtxOf<'tools:call-tool'>) {\n const { tool, toolContext } = flowCtx.state;\n if (!tool || !toolContext) return;\n const { cache } = toolContext.metadata;\n if (!cache || typeof toolContext.input === 'undefined') {\n return;\n }\n const cacheStore = this.get(CacheStoreToken);\n const ttl = cache === true ? this.options.defaultTTL : cache.ttl ?? this.options.defaultTTL;\n\n const hash = hashObject({ tool: tool.fullName, input: toolContext.input });\n await cacheStore.setValue(hash, toolContext.output, ttl);\n }\n}\n\nfunction hashObject(obj: Record<string, unknown>): string {\n const keys = Object.keys(obj).sort();\n return keys.reduce((acc, key) => {\n acc += key + ':';\n const val = obj[key];\n if (typeof val === 'object' && val !== null) {\n acc += hashObject(val as Record<string, unknown>);\n } else {\n acc += String(val);\n }\n acc += ';';\n return acc;\n }, '');\n}\n"]}
|
|
@@ -34,11 +34,34 @@ export interface RedisCachePluginOptions extends BaseCachePluginOptions {
|
|
|
34
34
|
export type MemoryCachePluginOptions = BaseCachePluginOptions & {
|
|
35
35
|
type: 'memory';
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Use global store configuration from @FrontMcp decorator.
|
|
39
|
+
* Requires `redis` to be configured in the main FrontMcp options.
|
|
40
|
+
* Supports both Redis and Vercel KV global configurations.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // In main.ts - configure global store
|
|
45
|
+
* @FrontMcp({
|
|
46
|
+
* redis: { host: 'localhost', port: 6379 },
|
|
47
|
+
* apps: [MyApp]
|
|
48
|
+
* })
|
|
49
|
+
*
|
|
50
|
+
* // In app - use global store
|
|
51
|
+
* @App({
|
|
52
|
+
* plugins: [CachePlugin.init({ type: 'global-store' })]
|
|
53
|
+
* })
|
|
54
|
+
* class MyApp {}
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export interface GlobalStoreCachePluginOptions extends BaseCachePluginOptions {
|
|
58
|
+
type: 'global-store';
|
|
59
|
+
}
|
|
37
60
|
export type RedisCacheOptions = RedisClientCachePluginOptions | RedisCachePluginOptions;
|
|
38
|
-
export type CachePluginOptions = MemoryCachePluginOptions | RedisCacheOptions;
|
|
61
|
+
export type CachePluginOptions = MemoryCachePluginOptions | RedisCacheOptions | GlobalStoreCachePluginOptions;
|
|
39
62
|
export interface CacheStoreInterface {
|
|
40
|
-
setValue(key: string, value:
|
|
41
|
-
getValue<T =
|
|
63
|
+
setValue(key: string, value: unknown, ttlSeconds?: number): Promise<void>;
|
|
64
|
+
getValue<T = unknown>(key: string, defaultValue?: T): Promise<T | undefined>;
|
|
42
65
|
delete(key: string): Promise<void>;
|
|
43
66
|
exists(key: string): Promise<boolean>;
|
|
44
67
|
close(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.types.js","sourceRoot":"","sources":["../../../src/cache/cache.types.ts"],"names":[],"mappings":"","sourcesContent":["import { Redis as RedisClient } from 'ioredis';\n\ndeclare global {\n interface ExtendFrontMcpToolMetadata {\n cache?: CachePluginToolOptions | true;\n }\n}\n\nexport interface CachePluginToolOptions {\n /**\n * Time to live in seconds. Default is 1 day.\n */\n ttl?: number; // default 1 day\n\n /**\n * If true, the cache value will be updated with the new value after the TTL.\n * Default is false.\n */\n slideWindow?: boolean;\n}\n\nexport interface BaseCachePluginOptions {\n defaultTTL?: number; // default 1 day\n}\n\nexport interface RedisClientCachePluginOptions extends BaseCachePluginOptions {\n type: 'redis-client';\n client: RedisClient;\n}\n\nexport interface RedisCachePluginOptions extends BaseCachePluginOptions {\n type: 'redis';\n config: {\n host: string;\n port: number;\n password?: string;\n db?: number;\n };\n}\n\nexport type MemoryCachePluginOptions = BaseCachePluginOptions & {\n type: 'memory';\n};\n\nexport type RedisCacheOptions = RedisClientCachePluginOptions | RedisCachePluginOptions;\n\nexport type CachePluginOptions = MemoryCachePluginOptions | RedisCacheOptions;\n\nexport interface CacheStoreInterface {\n setValue(key: string, value:
|
|
1
|
+
{"version":3,"file":"cache.types.js","sourceRoot":"","sources":["../../../src/cache/cache.types.ts"],"names":[],"mappings":"","sourcesContent":["import { Redis as RedisClient } from 'ioredis';\n\ndeclare global {\n interface ExtendFrontMcpToolMetadata {\n cache?: CachePluginToolOptions | true;\n }\n}\n\nexport interface CachePluginToolOptions {\n /**\n * Time to live in seconds. Default is 1 day.\n */\n ttl?: number; // default 1 day\n\n /**\n * If true, the cache value will be updated with the new value after the TTL.\n * Default is false.\n */\n slideWindow?: boolean;\n}\n\nexport interface BaseCachePluginOptions {\n defaultTTL?: number; // default 1 day\n}\n\nexport interface RedisClientCachePluginOptions extends BaseCachePluginOptions {\n type: 'redis-client';\n client: RedisClient;\n}\n\nexport interface RedisCachePluginOptions extends BaseCachePluginOptions {\n type: 'redis';\n config: {\n host: string;\n port: number;\n password?: string;\n db?: number;\n };\n}\n\nexport type MemoryCachePluginOptions = BaseCachePluginOptions & {\n type: 'memory';\n};\n\n/**\n * Use global store configuration from @FrontMcp decorator.\n * Requires `redis` to be configured in the main FrontMcp options.\n * Supports both Redis and Vercel KV global configurations.\n *\n * @example\n * ```typescript\n * // In main.ts - configure global store\n * @FrontMcp({\n * redis: { host: 'localhost', port: 6379 },\n * apps: [MyApp]\n * })\n *\n * // In app - use global store\n * @App({\n * plugins: [CachePlugin.init({ type: 'global-store' })]\n * })\n * class MyApp {}\n * ```\n */\nexport interface GlobalStoreCachePluginOptions extends BaseCachePluginOptions {\n type: 'global-store';\n}\n\nexport type RedisCacheOptions = RedisClientCachePluginOptions | RedisCachePluginOptions;\n\nexport type CachePluginOptions = MemoryCachePluginOptions | RedisCacheOptions | GlobalStoreCachePluginOptions;\n\nexport interface CacheStoreInterface {\n setValue(key: string, value: unknown, ttlSeconds?: number): Promise<void>;\n\n getValue<T = unknown>(key: string, defaultValue?: T): Promise<T | undefined>;\n\n delete(key: string): Promise<void>;\n\n exists(key: string): Promise<boolean>;\n\n close(): Promise<void>;\n}\n"]}
|
|
@@ -3,10 +3,10 @@ export default class CacheMemoryProvider implements CacheStoreInterface {
|
|
|
3
3
|
private readonly memory;
|
|
4
4
|
private sweeper?;
|
|
5
5
|
constructor(sweepIntervalTTL?: number);
|
|
6
|
-
/** Set
|
|
7
|
-
setValue(key: string, value:
|
|
6
|
+
/** Set a value (auto-stringifies objects) */
|
|
7
|
+
setValue(key: string, value: unknown, ttlSeconds?: number): Promise<void>;
|
|
8
8
|
/** Get a value and automatically parse JSON if possible */
|
|
9
|
-
getValue<T =
|
|
9
|
+
getValue<T = unknown>(key: string, defaultValue?: T): Promise<T | undefined>;
|
|
10
10
|
/** Delete a key */
|
|
11
11
|
delete(key: string): Promise<void>;
|
|
12
12
|
/** Check if a key exists (and not expired) */
|
|
@@ -8,10 +8,10 @@ let CacheMemoryProvider = class CacheMemoryProvider {
|
|
|
8
8
|
sweeper;
|
|
9
9
|
constructor(sweepIntervalTTL = 60) {
|
|
10
10
|
this.sweeper = setInterval(() => this.sweep(), sweepIntervalTTL * 1000);
|
|
11
|
-
// don
|
|
11
|
+
// don't keep the process alive just for the sweeper (Node >=14)
|
|
12
12
|
this.sweeper.unref?.();
|
|
13
13
|
}
|
|
14
|
-
/** Set
|
|
14
|
+
/** Set a value (auto-stringifies objects) */
|
|
15
15
|
async setValue(key, value, ttlSeconds) {
|
|
16
16
|
const strValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
17
17
|
// clear any previous timeout on this key
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-memory.provider.js","sourceRoot":"","sources":["../../../../src/cache/providers/cache-memory.provider.ts"],"names":[],"mappings":";;;AAAA,uCAAwD;AAWxD,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,qCAAqC;AAO1D,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IACrB,MAAM,GAAG,IAAI,GAAG,EAAiB,CAAC;IAC3C,OAAO,CAAkB;IAEjC,YAAY,gBAAgB,GAAG,EAAE;QAC/B,IAAI,CAAC,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,gBAAgB,GAAG,IAAI,CAAC,CAAC;QACxE,gEAAgE;QAC/D,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"cache-memory.provider.js","sourceRoot":"","sources":["../../../../src/cache/providers/cache-memory.provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { Provider, ProviderScope } from '@frontmcp/sdk';\nimport { CacheStoreInterface } from '../cache.types';\n\ntype Entry = {\n value: string;\n /** epoch millis when this entry expires (undefined = no TTL) */\n expiresAt?: number;\n /** per-key timeout when TTL is short enough to schedule */\n timeout?: NodeJS.Timeout;\n};\n\nconst MAX_TIMEOUT_MS = 2 ** 31 - 1; // ~24.8 days (Node setTimeout limit)\n\n@Provider({\n name: 'provider:cache:memory',\n description: 'Memory-based cache provider',\n scope: ProviderScope.GLOBAL,\n})\nexport default class CacheMemoryProvider implements CacheStoreInterface {\n private readonly memory = new Map<string, Entry>();\n private sweeper?: NodeJS.Timeout;\n\n constructor(sweepIntervalTTL = 60) {\n this.sweeper = setInterval(() => this.sweep(), sweepIntervalTTL * 1000);\n // don't keep the process alive just for the sweeper (Node >=14)\n (this.sweeper as { unref?: () => void }).unref?.();\n }\n\n /** Set a value (auto-stringifies objects) */\n async setValue(key: string, value: unknown, ttlSeconds?: number): Promise<void> {\n const strValue = typeof value === 'string' ? value : JSON.stringify(value);\n\n // clear any previous timeout on this key\n const existing = this.memory.get(key);\n if (existing?.timeout) clearTimeout(existing.timeout);\n\n const entry: Entry = { value: strValue };\n\n if (ttlSeconds && ttlSeconds > 0) {\n const ttlMs = ttlSeconds * 1000;\n entry.expiresAt = Date.now() + ttlMs;\n\n // Only schedule a timer if within Node's setTimeout limit; otherwise rely on sweeper/lazy purge\n if (ttlMs <= MAX_TIMEOUT_MS) {\n entry.timeout = setTimeout(() => {\n // final check guards against clock drift or updates\n const e = this.memory.get(key);\n if (e && e.expiresAt && e.expiresAt <= Date.now()) {\n this.memory.delete(key);\n }\n }, ttlMs);\n (entry.timeout as { unref?: () => void }).unref?.();\n }\n }\n\n this.memory.set(key, entry);\n }\n\n /** Get a value and automatically parse JSON if possible */\n async getValue<T = unknown>(key: string, defaultValue?: T): Promise<T | undefined> {\n const entry = this.memory.get(key);\n if (!entry) return defaultValue;\n\n if (this.isExpired(entry)) {\n await this.delete(key);\n return defaultValue;\n }\n\n const raw = entry.value;\n try {\n return JSON.parse(raw) as T;\n } catch {\n // fallback for plain string values\n return raw as unknown as T;\n }\n }\n\n /** Delete a key */\n async delete(key: string): Promise<void> {\n const entry = this.memory.get(key);\n if (entry?.timeout) clearTimeout(entry.timeout);\n this.memory.delete(key);\n }\n\n /** Check if a key exists (and not expired) */\n async exists(key: string): Promise<boolean> {\n const entry = this.memory.get(key);\n if (!entry) return false;\n if (this.isExpired(entry)) {\n await this.delete(key);\n return false;\n }\n return true;\n }\n\n /** Gracefully close the provider */\n async close(): Promise<void> {\n if (this.sweeper) clearInterval(this.sweeper);\n for (const [, entry] of this.memory) {\n if (entry.timeout) clearTimeout(entry.timeout);\n }\n this.memory.clear();\n }\n\n // ---- internals ----\n\n private isExpired(entry: Entry): boolean {\n return entry.expiresAt !== undefined && entry.expiresAt <= Date.now();\n }\n\n /** Periodically remove expired keys to keep memory tidy */\n private sweep(): void {\n const now = Date.now();\n for (const [key, entry] of this.memory) {\n if (entry.expiresAt !== undefined && entry.expiresAt <= now) {\n if (entry.timeout) clearTimeout(entry.timeout);\n this.memory.delete(key);\n }\n }\n }\n}\n"]}
|
|
@@ -2,10 +2,10 @@ import { CacheStoreInterface, RedisCacheOptions } from '../cache.types';
|
|
|
2
2
|
export default class CacheRedisProvider implements CacheStoreInterface {
|
|
3
3
|
private readonly client;
|
|
4
4
|
constructor(options: RedisCacheOptions);
|
|
5
|
-
/** Set
|
|
6
|
-
setValue(key: string, value:
|
|
5
|
+
/** Set a value (auto-stringifies objects) */
|
|
6
|
+
setValue(key: string, value: unknown, ttlSeconds?: number): Promise<void>;
|
|
7
7
|
/** Get a value and automatically parse JSON if possible */
|
|
8
|
-
getValue<T =
|
|
8
|
+
getValue<T = unknown>(key: string, defaultValue?: T): Promise<T | undefined>;
|
|
9
9
|
/** Delete a key */
|
|
10
10
|
delete(key: string): Promise<void>;
|
|
11
11
|
/** Check if a key exists */
|
|
@@ -21,7 +21,7 @@ let CacheRedisProvider = class CacheRedisProvider {
|
|
|
21
21
|
this.client.on('connect', () => console.log('[Redis] Connected'));
|
|
22
22
|
this.client.on('error', (err) => console.error('[Redis] Error:', err));
|
|
23
23
|
}
|
|
24
|
-
/** Set
|
|
24
|
+
/** Set a value (auto-stringifies objects) */
|
|
25
25
|
async setValue(key, value, ttlSeconds) {
|
|
26
26
|
const strValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
27
27
|
if (ttlSeconds && ttlSeconds > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-redis.provider.js","sourceRoot":"","sources":["../../../../src/cache/providers/cache-redis.provider.ts"],"names":[],"mappings":";;;AAAA,8DAAsD;AACtD,uCAAwD;AAQzC,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IACpB,MAAM,CAAc;IAErC,YAAY,OAA0B;QACpC,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC7B,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,iBAAK,CAAC;YACtB,WAAW,EAAE,KAAK;YAClB,oBAAoB,EAAE,CAAC;YACvB,GAAG,OAAO,CAAC,MAAM;SAClB,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,CAAC;IACzE,CAAC;IAED
|
|
1
|
+
{"version":3,"file":"cache-redis.provider.js","sourceRoot":"","sources":["../../../../src/cache/providers/cache-redis.provider.ts"],"names":[],"mappings":";;;AAAA,8DAAsD;AACtD,uCAAwD;AAQzC,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IACpB,MAAM,CAAc;IAErC,YAAY,OAA0B;QACpC,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC7B,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,iBAAK,CAAC;YACtB,WAAW,EAAE,KAAK;YAClB,oBAAoB,EAAE,CAAC;YACvB,GAAG,OAAO,CAAC,MAAM;SAClB,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,6CAA6C;IAC7C,KAAK,CAAC,QAAQ,CAAC,GAAW,EAAE,KAAc,EAAE,UAAmB;QAC7D,MAAM,QAAQ,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC3E,IAAI,UAAU,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,KAAK,CAAC,QAAQ,CAAc,GAAW,EAAE,YAAgB;QACvD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,YAAY,CAAC;QAEtC,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;YACnC,OAAO,GAAmB,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,4BAA4B;IAC5B,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC;IAED,4CAA4C;IAC5C,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;CACF,CAAA;AA3DoB,kBAAkB;IALtC,IAAA,cAAQ,EAAC;QACR,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,4BAA4B;QACzC,KAAK,EAAE,mBAAa,CAAC,MAAM;KAC5B,CAAC;;GACmB,kBAAkB,CA2DtC;kBA3DoB,kBAAkB","sourcesContent":["import Redis, { Redis as RedisClient } from 'ioredis';\nimport { Provider, ProviderScope } from '@frontmcp/sdk';\nimport { CacheStoreInterface, RedisCacheOptions } from '../cache.types';\n\n@Provider({\n name: 'provider:cache:redis',\n description: 'Redis-based cache provider',\n scope: ProviderScope.GLOBAL,\n})\nexport default class CacheRedisProvider implements CacheStoreInterface {\n private readonly client: RedisClient;\n\n constructor(options: RedisCacheOptions) {\n if (options.type !== 'redis' && options.type !== 'redis-client') {\n throw new Error('Invalid cache provider type');\n }\n\n if (options.type === 'redis-client') {\n this.client = options.client;\n return;\n }\n this.client = new Redis({\n lazyConnect: false,\n maxRetriesPerRequest: 3,\n ...options.config,\n });\n\n this.client.on('connect', () => console.log('[Redis] Connected'));\n this.client.on('error', (err) => console.error('[Redis] Error:', err));\n }\n\n /** Set a value (auto-stringifies objects) */\n async setValue(key: string, value: unknown, ttlSeconds?: number): Promise<void> {\n const strValue = typeof value === 'string' ? value : JSON.stringify(value);\n if (ttlSeconds && ttlSeconds > 0) {\n await this.client.set(key, strValue, 'EX', ttlSeconds);\n } else {\n await this.client.set(key, strValue);\n }\n }\n\n /** Get a value and automatically parse JSON if possible */\n async getValue<T = unknown>(key: string, defaultValue?: T): Promise<T | undefined> {\n const raw = await this.client.get(key);\n if (raw === null) return defaultValue;\n\n try {\n return JSON.parse(raw) as T;\n } catch {\n // fallback for plain string values\n return raw as unknown as T;\n }\n }\n\n /** Delete a key */\n async delete(key: string): Promise<void> {\n await this.client.del(key);\n }\n\n /** Check if a key exists */\n async exists(key: string): Promise<boolean> {\n return (await this.client.exists(key)) === 1;\n }\n\n /** Gracefully close the Redis connection */\n async close(): Promise<void> {\n await this.client.quit();\n }\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CacheStoreInterface } from '../cache.types';
|
|
2
|
+
export interface CacheVercelKvProviderOptions {
|
|
3
|
+
url?: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
keyPrefix?: string;
|
|
6
|
+
defaultTTL?: number;
|
|
7
|
+
}
|
|
8
|
+
export default class CacheVercelKvProvider implements CacheStoreInterface {
|
|
9
|
+
private kv;
|
|
10
|
+
private readonly keyPrefix;
|
|
11
|
+
private readonly defaultTTL;
|
|
12
|
+
constructor(options?: CacheVercelKvProviderOptions);
|
|
13
|
+
private prefixKey;
|
|
14
|
+
/** Set a value (auto-stringifies objects) */
|
|
15
|
+
setValue(key: string, value: unknown, ttlSeconds?: number): Promise<void>;
|
|
16
|
+
/** Get a value and automatically parse JSON if possible */
|
|
17
|
+
getValue<T = unknown>(key: string, defaultValue?: T): Promise<T | undefined>;
|
|
18
|
+
/** Delete a key */
|
|
19
|
+
delete(key: string): Promise<void>;
|
|
20
|
+
/** Check if a key exists */
|
|
21
|
+
exists(key: string): Promise<boolean>;
|
|
22
|
+
/** Gracefully close the provider (no-op for Vercel KV - stateless REST API) */
|
|
23
|
+
close(): Promise<void>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const sdk_1 = require("@frontmcp/sdk");
|
|
5
|
+
let CacheVercelKvProvider = class CacheVercelKvProvider {
|
|
6
|
+
kv;
|
|
7
|
+
keyPrefix;
|
|
8
|
+
defaultTTL;
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
// Lazy import @vercel/kv to avoid bundling when not used
|
|
11
|
+
const vercelKv = require('@vercel/kv');
|
|
12
|
+
// Validate partial configuration - both url and token must be provided together, or neither
|
|
13
|
+
const hasUrl = options.url !== undefined;
|
|
14
|
+
const hasToken = options.token !== undefined;
|
|
15
|
+
if (hasUrl !== hasToken) {
|
|
16
|
+
throw new Error(`CacheVercelKvProvider: Both 'url' and 'token' must be provided together, or neither. ` +
|
|
17
|
+
`Received: url=${hasUrl ? 'provided' : 'missing'}, token=${hasToken ? 'provided' : 'missing'}`);
|
|
18
|
+
}
|
|
19
|
+
// Use the kv instance with custom config if url/token provided
|
|
20
|
+
if (options.url && options.token) {
|
|
21
|
+
this.kv = vercelKv.createClient({
|
|
22
|
+
url: options.url,
|
|
23
|
+
token: options.token,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// Use default kv instance (reads from KV_REST_API_URL and KV_REST_API_TOKEN env vars)
|
|
28
|
+
this.kv = vercelKv.kv;
|
|
29
|
+
}
|
|
30
|
+
this.keyPrefix = options.keyPrefix ?? 'cache:';
|
|
31
|
+
this.defaultTTL = options.defaultTTL ?? 60 * 60 * 24; // 1 day default
|
|
32
|
+
}
|
|
33
|
+
prefixKey(key) {
|
|
34
|
+
return `${this.keyPrefix}${key}`;
|
|
35
|
+
}
|
|
36
|
+
/** Set a value (auto-stringifies objects) */
|
|
37
|
+
async setValue(key, value, ttlSeconds) {
|
|
38
|
+
const strValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
39
|
+
const ttl = ttlSeconds ?? this.defaultTTL;
|
|
40
|
+
if (ttl > 0) {
|
|
41
|
+
await this.kv.set(this.prefixKey(key), strValue, { ex: ttl });
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
await this.kv.set(this.prefixKey(key), strValue);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Get a value and automatically parse JSON if possible */
|
|
48
|
+
async getValue(key, defaultValue) {
|
|
49
|
+
const raw = await this.kv.get(this.prefixKey(key));
|
|
50
|
+
if (raw === null || raw === undefined)
|
|
51
|
+
return defaultValue;
|
|
52
|
+
// Vercel KV auto-parses JSON, but we handle string fallback
|
|
53
|
+
if (typeof raw === 'string') {
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(raw);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return raw;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return raw;
|
|
62
|
+
}
|
|
63
|
+
/** Delete a key */
|
|
64
|
+
async delete(key) {
|
|
65
|
+
await this.kv.del(this.prefixKey(key));
|
|
66
|
+
}
|
|
67
|
+
/** Check if a key exists */
|
|
68
|
+
async exists(key) {
|
|
69
|
+
return (await this.kv.exists(this.prefixKey(key))) === 1;
|
|
70
|
+
}
|
|
71
|
+
/** Gracefully close the provider (no-op for Vercel KV - stateless REST API) */
|
|
72
|
+
async close() {
|
|
73
|
+
// No-op: Vercel KV uses stateless REST API, no connection to close
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
CacheVercelKvProvider = tslib_1.__decorate([
|
|
77
|
+
(0, sdk_1.Provider)({
|
|
78
|
+
name: 'provider:cache:vercel-kv',
|
|
79
|
+
description: 'Vercel KV-based cache provider',
|
|
80
|
+
scope: sdk_1.ProviderScope.GLOBAL,
|
|
81
|
+
}),
|
|
82
|
+
tslib_1.__metadata("design:paramtypes", [Object])
|
|
83
|
+
], CacheVercelKvProvider);
|
|
84
|
+
exports.default = CacheVercelKvProvider;
|
|
85
|
+
//# sourceMappingURL=cache-vercel-kv.provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-vercel-kv.provider.js","sourceRoot":"","sources":["../../../../src/cache/providers/cache-vercel-kv.provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { Provider, ProviderScope } from '@frontmcp/sdk';\nimport { CacheStoreInterface } from '../cache.types';\n\nexport interface CacheVercelKvProviderOptions {\n url?: string;\n token?: string;\n keyPrefix?: string;\n defaultTTL?: number;\n}\n\n/** Minimal interface for Vercel KV client operations used by the cache provider */\ninterface VercelKvClient {\n set(key: string, value: string, options?: { ex?: number }): Promise<void>;\n get(key: string): Promise<unknown>;\n del(key: string): Promise<void>;\n exists(key: string): Promise<number>;\n}\n\n@Provider({\n name: 'provider:cache:vercel-kv',\n description: 'Vercel KV-based cache provider',\n scope: ProviderScope.GLOBAL,\n})\nexport default class CacheVercelKvProvider implements CacheStoreInterface {\n private kv: VercelKvClient;\n private readonly keyPrefix: string;\n private readonly defaultTTL: number;\n\n constructor(options: CacheVercelKvProviderOptions = {}) {\n // Lazy import @vercel/kv to avoid bundling when not used\n const vercelKv = require('@vercel/kv');\n\n // Validate partial configuration - both url and token must be provided together, or neither\n const hasUrl = options.url !== undefined;\n const hasToken = options.token !== undefined;\n if (hasUrl !== hasToken) {\n throw new Error(\n `CacheVercelKvProvider: Both 'url' and 'token' must be provided together, or neither. ` +\n `Received: url=${hasUrl ? 'provided' : 'missing'}, token=${hasToken ? 'provided' : 'missing'}`,\n );\n }\n\n // Use the kv instance with custom config if url/token provided\n if (options.url && options.token) {\n this.kv = vercelKv.createClient({\n url: options.url,\n token: options.token,\n });\n } else {\n // Use default kv instance (reads from KV_REST_API_URL and KV_REST_API_TOKEN env vars)\n this.kv = vercelKv.kv;\n }\n\n this.keyPrefix = options.keyPrefix ?? 'cache:';\n this.defaultTTL = options.defaultTTL ?? 60 * 60 * 24; // 1 day default\n }\n\n private prefixKey(key: string): string {\n return `${this.keyPrefix}${key}`;\n }\n\n /** Set a value (auto-stringifies objects) */\n async setValue(key: string, value: unknown, ttlSeconds?: number): Promise<void> {\n const strValue = typeof value === 'string' ? value : JSON.stringify(value);\n const ttl = ttlSeconds ?? this.defaultTTL;\n\n if (ttl > 0) {\n await this.kv.set(this.prefixKey(key), strValue, { ex: ttl });\n } else {\n await this.kv.set(this.prefixKey(key), strValue);\n }\n }\n\n /** Get a value and automatically parse JSON if possible */\n async getValue<T = unknown>(key: string, defaultValue?: T): Promise<T | undefined> {\n const raw = await this.kv.get(this.prefixKey(key));\n if (raw === null || raw === undefined) return defaultValue;\n\n // Vercel KV auto-parses JSON, but we handle string fallback\n if (typeof raw === 'string') {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return raw as unknown as T;\n }\n }\n\n return raw as T;\n }\n\n /** Delete a key */\n async delete(key: string): Promise<void> {\n await this.kv.del(this.prefixKey(key));\n }\n\n /** Check if a key exists */\n async exists(key: string): Promise<boolean> {\n return (await this.kv.exists(this.prefixKey(key))) === 1;\n }\n\n /** Gracefully close the provider (no-op for Vercel KV - stateless REST API) */\n async close(): Promise<void> {\n // No-op: Vercel KV uses stateless REST API, no connection to close\n }\n}\n"]}
|
|
@@ -7,9 +7,10 @@ export declare const invokeToolInputSchema: z.ZodObject<{
|
|
|
7
7
|
export type InvokeToolInput = z.infer<typeof invokeToolInputSchema>;
|
|
8
8
|
export declare const invokeToolOutputSchema: z.ZodObject<{
|
|
9
9
|
_meta: z.ZodOptional<z.ZodObject<{
|
|
10
|
+
progressToken: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
10
11
|
"io.modelcontextprotocol/related-task": z.ZodOptional<z.ZodObject<{
|
|
11
12
|
taskId: z.ZodString;
|
|
12
|
-
}, z.core.$
|
|
13
|
+
}, z.core.$strip>>;
|
|
13
14
|
}, z.core.$loose>>;
|
|
14
15
|
content: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
15
16
|
type: z.ZodLiteral<"text">;
|
|
@@ -66,6 +67,10 @@ export declare const invokeToolOutputSchema: z.ZodObject<{
|
|
|
66
67
|
src: z.ZodString;
|
|
67
68
|
mimeType: z.ZodOptional<z.ZodString>;
|
|
68
69
|
sizes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
70
|
+
theme: z.ZodOptional<z.ZodEnum<{
|
|
71
|
+
light: "light";
|
|
72
|
+
dark: "dark";
|
|
73
|
+
}>>;
|
|
69
74
|
}, z.core.$strip>>>;
|
|
70
75
|
name: z.ZodString;
|
|
71
76
|
title: z.ZodOptional<z.ZodString>;
|