From a088df436e6b0d822091a6b0aff4742816429dfd Mon Sep 17 00:00:00 2001 From: Jakob Wennberg <149234542+jakobwennberg@users.noreply.github.com> Date: Sun, 22 Mar 2026 21:50:13 +0100 Subject: [PATCH] fix: document service hangs in API-key auth contexts (#85) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: use caller's supabase client in ensureDocumentsBucket The bucket check was creating its own cookie-dependent service client via createServiceClient(), which calls `await cookies()`. This hangs in API-key auth contexts (MCP server) where no cookie store exists. Now uses the caller's supabase client instead — both uploadDocument() and createNewVersion() already receive a service-role client. Removes the unused createServiceClient import. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: use cookieless service client in ensureDocumentsBucket ensureDocumentsBucket() needs a service-role client for storage admin operations (getBucket/createBucket). Previously it used createServiceClient() which calls `await cookies()` — this hangs in API-key auth contexts (e.g. MCP server) where no cookie store exists. Now uses createServiceClientNoCookies() internally, which provides a service-role client without cookie dependency. This is correct for all callers: both web API routes (which pass user-level clients) and the MCP server (which passes a cookieless service client) — bucket admin always requires service-role regardless of the caller's auth context. Also cleans up stale test mock that referenced the removed import. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .../documents/__tests__/document-service.test.ts | 5 ++--- lib/core/documents/document-service.ts | 12 ++++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/core/documents/__tests__/document-service.test.ts b/lib/core/documents/__tests__/document-service.test.ts index ff7ffa54..db29c588 100644 --- a/lib/core/documents/__tests__/document-service.test.ts +++ b/lib/core/documents/__tests__/document-service.test.ts @@ -43,9 +43,8 @@ function makeClient(storageOverrides: Record = {}) { } } -// Keep createServiceClient mock since ensureDocumentsBucket still uses it -vi.mock('@/lib/supabase/server', () => ({ - createServiceClient: vi.fn(async () => makeClient()), +vi.mock('@/lib/auth/api-keys', () => ({ + createServiceClientNoCookies: vi.fn(() => makeClient()), })) import { uploadDocument, createNewVersion, verifyIntegrity, _resetBucketVerified } from '../document-service' diff --git a/lib/core/documents/document-service.ts b/lib/core/documents/document-service.ts index 362a4165..afebcf56 100644 --- a/lib/core/documents/document-service.ts +++ b/lib/core/documents/document-service.ts @@ -1,5 +1,5 @@ import type { SupabaseClient } from '@supabase/supabase-js' -import { createServiceClient } from '@/lib/supabase/server' +import { createServiceClientNoCookies } from '@/lib/auth/api-keys' import { eventBus } from '@/lib/events' import type { DocumentAttachment, DocumentUploadSource } from '@/types' @@ -21,15 +21,19 @@ export function _resetBucketVerified() { /** * Ensure the 'documents' storage bucket exists, creating it if missing. * Runs once per process lifetime (same pattern as ensureInitialized). + * + * Uses a cookieless service-role client for bucket admin operations + * (getBucket/createBucket require service-role). This avoids the cookie + * dependency that hangs in API-key auth contexts (e.g. MCP server). */ async function ensureDocumentsBucket(): Promise { if (bucketVerified) return - const supabase = await createServiceClient() - const { data: bucket } = await supabase.storage.getBucket('documents') + const serviceClient = createServiceClientNoCookies() + const { data: bucket } = await serviceClient.storage.getBucket('documents') if (!bucket) { - await supabase.storage.createBucket('documents', { + await serviceClient.storage.createBucket('documents', { public: false, fileSizeLimit: 52428800, // 50 MB })