| import type Keyv from 'keyv'; |
| import { fromPairs } from 'lodash'; |
| import { standardCache, keyvRedisClient } from '~/cache'; |
| import { ParsedServerConfig } from '~/mcp/types'; |
| import { BaseRegistryCache } from './BaseRegistryCache'; |
|
|
| |
| |
| |
| |
| |
| |
| |
| export class ServerConfigsCacheRedis extends BaseRegistryCache { |
| protected readonly cache: Keyv; |
| private readonly owner: string; |
| private readonly leaderOnly: boolean; |
|
|
| constructor(owner: string, leaderOnly: boolean) { |
| super(); |
| this.owner = owner; |
| this.leaderOnly = leaderOnly; |
| this.cache = standardCache(`${this.PREFIX}::Servers::${owner}`); |
| } |
|
|
| public async add(serverName: string, config: ParsedServerConfig): Promise<void> { |
| if (this.leaderOnly) await this.leaderCheck(`add ${this.owner} MCP servers`); |
| const exists = await this.cache.has(serverName); |
| if (exists) |
| throw new Error( |
| `Server "${serverName}" already exists in cache. Use update() to modify existing configs.`, |
| ); |
| const success = await this.cache.set(serverName, config); |
| this.successCheck(`add ${this.owner} server "${serverName}"`, success); |
| } |
|
|
| public async update(serverName: string, config: ParsedServerConfig): Promise<void> { |
| if (this.leaderOnly) await this.leaderCheck(`update ${this.owner} MCP servers`); |
| const exists = await this.cache.has(serverName); |
| if (!exists) |
| throw new Error( |
| `Server "${serverName}" does not exist in cache. Use add() to create new configs.`, |
| ); |
| const success = await this.cache.set(serverName, config); |
| this.successCheck(`update ${this.owner} server "${serverName}"`, success); |
| } |
|
|
| public async remove(serverName: string): Promise<void> { |
| if (this.leaderOnly) await this.leaderCheck(`remove ${this.owner} MCP servers`); |
| const success = await this.cache.delete(serverName); |
| this.successCheck(`remove ${this.owner} server "${serverName}"`, success); |
| } |
|
|
| public async get(serverName: string): Promise<ParsedServerConfig | undefined> { |
| return this.cache.get(serverName); |
| } |
|
|
| public async getAll(): Promise<Record<string, ParsedServerConfig>> { |
| |
| |
| const pattern = `*${this.cache.namespace}:*`; |
| const entries: Array<[string, ParsedServerConfig]> = []; |
|
|
| |
| if (keyvRedisClient && 'scanIterator' in keyvRedisClient) { |
| for await (const key of keyvRedisClient.scanIterator({ MATCH: pattern })) { |
| |
| |
| const lastColonIndex = key.lastIndexOf(':'); |
| const keyName = key.substring(lastColonIndex + 1); |
| const value = await this.cache.get(keyName); |
| if (value) { |
| entries.push([keyName, value as ParsedServerConfig]); |
| } |
| } |
| } else { |
| throw new Error('Redis client with scanIterator not available.'); |
| } |
|
|
| return fromPairs(entries); |
| } |
| } |
|
|