We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Thinh-nguyen-03/virtual-assistant-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
import { createHash } from 'crypto';
export class MemoryIdempotencyStore {
store = new Map();
async get(key, fingerprint) {
const recordKey = `${key}:${fingerprint}`;
const record = this.store.get(recordKey);
if (!record) {
return null;
}
if (new Date(record.expires_at) < new Date()) {
this.store.delete(recordKey);
return null;
}
return record;
}
async set(key, fingerprint, outcome, ttlMs) {
const recordKey = `${key}:${fingerprint}`;
const now = new Date();
const expiresAt = new Date(now.getTime() + ttlMs);
const record = {
key,
fingerprint,
outcome,
created_at: now.toISOString(),
expires_at: expiresAt.toISOString()
};
this.store.set(recordKey, record);
}
async delete(key) {
for (const [recordKey] of this.store) {
if (recordKey.startsWith(`${key}:`)) {
this.store.delete(recordKey);
}
}
}
}
export class RedisIdempotencyStore {
redis;
constructor(redis) {
this.redis = redis;
}
async get(key, fingerprint) {
const recordKey = `idempotency:${key}:${fingerprint}`;
const record = await this.redis.get(recordKey);
if (!record) {
return null;
}
return JSON.parse(record);
}
async set(key, fingerprint, outcome, ttlMs) {
const recordKey = `idempotency:${key}:${fingerprint}`;
const now = new Date();
const expiresAt = new Date(now.getTime() + ttlMs);
const record = {
key,
fingerprint,
outcome,
created_at: now.toISOString(),
expires_at: expiresAt.toISOString()
};
await this.redis.setex(recordKey, Math.ceil(ttlMs / 1000), JSON.stringify(record));
}
async delete(key) {
const pattern = `idempotency:${key}:*`;
const keys = await this.redis.keys(pattern);
if (keys.length > 0) {
await this.redis.del(...keys);
}
}
}
export function generateFingerprint(request) {
const normalized = JSON.stringify(request, Object.keys(request).sort());
return createHash('sha256').update(normalized).digest('hex');
}
export class IdempotencyManager {
store;
constructor(store) {
this.store = store;
}
async checkIdempotency(key, request, ttlMs = 24 * 60 * 60 * 1000) {
const fingerprint = generateFingerprint(request);
const record = await this.store.get(key, fingerprint);
if (record) {
return {
isIdempotent: true,
cachedResult: record.outcome
};
}
return { isIdempotent: false };
}
async storeResult(key, request, result, ttlMs = 24 * 60 * 60 * 1000) {
const fingerprint = generateFingerprint(request);
await this.store.set(key, fingerprint, result, ttlMs);
}
async clearKey(key) {
await this.store.delete(key);
}
}
export function createIdempotencyStore(redis) {
if (redis) {
return new RedisIdempotencyStore(redis);
}
return new MemoryIdempotencyStore();
}
//# sourceMappingURL=idempotency.js.map