Skip to main content
Glama
uploadManager.ts3.42 kB
import { randomUUID } from "node:crypto"; import { promises as fs } from "node:fs"; import os from "node:os"; import path from "node:path"; interface UploadMeta { id: string; sessionId: string; filePath: string; name: string; mimeType?: string; size?: number; } const uploads = new Map<string, Map<string, UploadMeta>>(); const uploadsRoot = path.join(os.tmpdir(), "mcp-playwright", "uploads"); let uploadEndpointUrl: string | undefined; export function setUploadEndpointUrl(url: string | undefined) { uploadEndpointUrl = url; } export function getUploadEndpointUrl(): string | undefined { return uploadEndpointUrl; } export function registerUploadedFile({ sessionId, filePath, name, mimeType, size, }: { sessionId: string; filePath: string; name: string; mimeType?: string; size?: number; }) { const id = randomUUID(); let store = uploads.get(sessionId); if (!store) { store = new Map(); uploads.set(sessionId, store); } const meta: UploadMeta = { id, sessionId, filePath, name, mimeType, size, }; store.set(id, meta); return { uri: `mcp-uploads://${sessionId}/${id}`, id, ...meta, }; } export async function resolveUploadResource({ resourceUri, sessionId, }: { resourceUri: string; sessionId: string; }): Promise<UploadMeta | null> { const parsed = parseUploadResourceUri(resourceUri); if (!parsed) return null; if (parsed.sessionId !== sessionId) return null; const store = uploads.get(sessionId); if (!store) return null; return store.get(parsed.id) ?? null; } export function parseUploadResourceUri(uri: string): { sessionId: string; id: string } | null { if (!uri.startsWith("mcp-uploads://") && !uri.startsWith("mcp+upload://")) return null; const withoutScheme = uri.replace("mcp-uploads://", "").replace("mcp+upload://", ""); const parts = withoutScheme.split("/").filter(Boolean); if (parts.length !== 2) return null; const [sessionId, id] = parts; return { sessionId, id }; } export async function cleanupUpload(sessionId: string, uploadId: string) { const store = uploads.get(sessionId); if (!store) return; const meta = store.get(uploadId); if (!meta) return; store.delete(uploadId); try { await fs.unlink(meta.filePath); } catch { // ignore } await removeDirIfEmpty(path.dirname(meta.filePath)); await removeDirIfEmpty(path.join(uploadsRoot, sessionId)); await removeDirIfEmpty(uploadsRoot); } export async function clearUploadsForSession(sessionId: string) { const store = uploads.get(sessionId); if (!store) return; for (const [uploadId, meta] of store.entries()) { try { await fs.unlink(meta.filePath); } catch { // ignore } await removeDirIfEmpty(path.dirname(meta.filePath)); await removeDirIfEmpty(path.join(uploadsRoot, sessionId)); await removeDirIfEmpty(uploadsRoot); store.delete(uploadId); } uploads.delete(sessionId); } export async function ensureUploadsDir(sessionId: string) { const sessionDir = path.join(uploadsRoot, sessionId); await fs.mkdir(sessionDir, { recursive: true }); return sessionDir; } async function removeDirIfEmpty(dir: string) { try { const entries = await fs.readdir(dir); if (entries.length === 0) { await fs.rmdir(dir); console.log(`Removed empty resource directory: ${dir}`); } } catch { // ignore } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aakashH242/mcp-playwright'

If you have feedback or need assistance with the MCP directory API, please join our Discord server