strapi-content-embeddings 0.1.3 → 0.1.5

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.
Files changed (36) hide show
  1. package/README.md +187 -0
  2. package/dist/_chunks/{App-Swmo_WMf.js → App-Rq72tIgS.js} +37 -55
  3. package/dist/_chunks/App-Rq72tIgS.js.map +1 -0
  4. package/dist/_chunks/{App-BlCKKuQN.mjs → App-j180lztd.mjs} +37 -55
  5. package/dist/_chunks/App-j180lztd.mjs.map +1 -0
  6. package/dist/_chunks/en-B4KWt_jN.js +1 -0
  7. package/dist/_chunks/en-B4KWt_jN.js.map +1 -0
  8. package/dist/_chunks/en-Byx4XI2L.mjs +1 -0
  9. package/dist/_chunks/en-Byx4XI2L.mjs.map +1 -0
  10. package/dist/_chunks/{index-CXVoFiJp.mjs → index-B3j0IFUi.mjs} +70 -27
  11. package/dist/_chunks/index-B3j0IFUi.mjs.map +1 -0
  12. package/dist/_chunks/{index-BpKkUIJY.js → index-jf6vikTZ.js} +70 -27
  13. package/dist/_chunks/index-jf6vikTZ.js.map +1 -0
  14. package/dist/admin/index.js +2 -1
  15. package/dist/admin/index.js.map +1 -0
  16. package/dist/admin/index.mjs +2 -1
  17. package/dist/admin/index.mjs.map +1 -0
  18. package/dist/admin/src/components/custom/MarkdownEditor.d.ts +1 -1
  19. package/dist/server/index.js +850 -57
  20. package/dist/server/index.js.map +1 -0
  21. package/dist/server/index.mjs +850 -57
  22. package/dist/server/index.mjs.map +1 -0
  23. package/dist/server/src/config/index.d.ts +9 -0
  24. package/dist/server/src/controllers/controller.d.ts +14 -0
  25. package/dist/server/src/controllers/index.d.ts +2 -0
  26. package/dist/server/src/index.d.ts +38 -2
  27. package/dist/server/src/mcp/tools/create-embedding.d.ts +6 -0
  28. package/dist/server/src/mcp/tools/index.d.ts +4 -0
  29. package/dist/server/src/plugin-manager.d.ts +16 -0
  30. package/dist/server/src/routes/content-api.d.ts +10 -0
  31. package/dist/server/src/routes/index.d.ts +10 -0
  32. package/dist/server/src/services/embeddings.d.ts +43 -2
  33. package/dist/server/src/services/index.d.ts +23 -2
  34. package/dist/server/src/services/sync.d.ts +48 -0
  35. package/dist/server/src/utils/chunking.d.ts +44 -0
  36. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../../server/src/config/index.ts","../../server/src/plugin-manager.ts","../../server/src/mcp/schemas/index.ts","../../server/src/mcp/tools/semantic-search.ts","../../server/src/mcp/tools/rag-query.ts","../../server/src/mcp/tools/list-embeddings.ts","../../server/src/mcp/tools/get-embedding.ts","../../server/src/mcp/tools/create-embedding.ts","../../server/src/mcp/tools/index.ts","../../server/src/mcp/server.ts","../../server/src/bootstrap.ts","../../server/src/destroy.ts","../../server/src/register.ts","../../server/src/content-types/embedding/index.ts","../../server/src/content-types/index.ts","../../server/src/controllers/controller.ts","../../server/src/controllers/mcp.ts","../../server/src/controllers/index.ts","../../server/src/middlewares/index.ts","../../server/src/policies/index.ts","../../server/src/routes/content-api.ts","../../server/src/routes/admin.ts","../../server/src/routes/index.ts","../../server/src/utils/chunking.ts","../../server/src/services/embeddings.ts","../../server/src/services/sync.ts","../../server/src/services/index.ts","../../server/src/index.ts"],"sourcesContent":["// Available OpenAI embedding models and their dimensions\nexport const EMBEDDING_MODELS = {\n \"text-embedding-3-small\": { dimensions: 1536 },\n \"text-embedding-3-large\": { dimensions: 3072 },\n \"text-embedding-ada-002\": { dimensions: 1536 },\n} as const;\n\nexport type EmbeddingModelName = keyof typeof EMBEDDING_MODELS;\n\nexport interface PluginConfigSchema {\n openAIApiKey?: string;\n neonConnectionString?: string;\n embeddingModel?: EmbeddingModelName;\n /** Maximum characters per chunk (default: 4000, roughly ~1000 tokens) */\n chunkSize?: number;\n /** Number of characters to overlap between chunks (default: 200) */\n chunkOverlap?: number;\n /** Automatically chunk content that exceeds chunkSize (default: false) */\n autoChunk?: boolean;\n}\n\nexport default {\n default: {\n openAIApiKey: \"\",\n neonConnectionString: \"\",\n embeddingModel: \"text-embedding-3-small\" as EmbeddingModelName,\n chunkSize: 4000,\n chunkOverlap: 200,\n autoChunk: false,\n },\n validator(config: PluginConfigSchema) {\n if (!config.openAIApiKey) {\n console.warn(\n \"strapi-content-embeddings: openAIApiKey is not configured. Plugin features will be disabled.\"\n );\n }\n if (!config.neonConnectionString) {\n console.warn(\n \"strapi-content-embeddings: neonConnectionString is not configured. Plugin features will be disabled.\"\n );\n }\n if (config.embeddingModel && !EMBEDDING_MODELS[config.embeddingModel]) {\n console.warn(\n `strapi-content-embeddings: Invalid embeddingModel \"${config.embeddingModel}\". ` +\n `Valid options: ${Object.keys(EMBEDDING_MODELS).join(\", \")}. ` +\n `Defaulting to \"text-embedding-3-small\".`\n );\n }\n if (config.chunkSize && (config.chunkSize < 100 || config.chunkSize > 8000)) {\n console.warn(\n `strapi-content-embeddings: chunkSize ${config.chunkSize} is outside recommended range (100-8000). ` +\n `Using default value of 4000.`\n );\n }\n },\n};\n","import { OpenAIEmbeddings, ChatOpenAI } from \"@langchain/openai\";\nimport { PGVectorStore } from \"@langchain/community/vectorstores/pgvector\";\nimport { Document } from \"@langchain/core/documents\";\nimport { StringOutputParser } from \"@langchain/core/output_parsers\";\nimport { ChatPromptTemplate } from \"@langchain/core/prompts\";\nimport {\n RunnableSequence,\n RunnablePassthrough,\n} from \"@langchain/core/runnables\";\nimport { Pool, PoolConfig } from \"pg\";\nimport {\n EMBEDDING_MODELS,\n type EmbeddingModelName,\n} from \"./config\";\n\ninterface PluginConfig {\n openAIApiKey: string;\n neonConnectionString: string;\n embeddingModel?: EmbeddingModelName;\n}\n\ninterface EmbeddingDocument {\n id: string;\n title: string;\n content: string;\n collectionType?: string;\n fieldName?: string;\n}\n\ninterface CreateEmbeddingResult {\n embeddingId: string;\n embedding: number[];\n}\n\ninterface QueryResponse {\n text: string;\n sourceDocuments: Document[];\n}\n\nclass PluginManager {\n private embeddings: OpenAIEmbeddings | null = null;\n private chat: ChatOpenAI | null = null;\n private pool: Pool | null = null;\n private embeddingModel: EmbeddingModelName = \"text-embedding-3-small\";\n private dimensions: number = 1536;\n private vectorStoreConfig: {\n pool: Pool;\n tableName: string;\n columns: {\n idColumnName: string;\n vectorColumnName: string;\n contentColumnName: string;\n metadataColumnName: string;\n };\n distanceStrategy: \"cosine\" | \"innerProduct\" | \"euclidean\";\n } | null = null;\n\n async initializePool(connectionString: string): Promise<Pool> {\n console.log(\"Initializing Neon DB Pool\");\n\n if (this.pool) return this.pool;\n\n try {\n const poolConfig: PoolConfig = {\n connectionString,\n ssl: { rejectUnauthorized: false },\n max: 10,\n };\n\n this.pool = new Pool(poolConfig);\n\n // Test the connection\n const client = await this.pool.connect();\n await client.query(\"SELECT 1\");\n client.release();\n\n // Initialize the vector store table if it doesn't exist\n await this.initializeVectorTable();\n\n console.log(\"Neon DB Pool initialized successfully\");\n return this.pool;\n } catch (error) {\n console.error(`Failed to initialize Neon DB Pool: ${error}`);\n throw new Error(`Failed to initialize Neon DB Pool: ${error}`);\n }\n }\n\n private async initializeVectorTable(): Promise<void> {\n if (!this.pool) throw new Error(\"Pool not initialized\");\n\n const client = await this.pool.connect();\n try {\n // Enable the pgvector extension\n await client.query(\"CREATE EXTENSION IF NOT EXISTS vector\");\n\n // Create the documents table if it doesn't exist\n // Note: If you change embedding models with different dimensions,\n // you may need to drop and recreate this table\n await client.query(`\n CREATE TABLE IF NOT EXISTS embeddings_documents (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n content TEXT,\n metadata JSONB,\n embedding vector(${this.dimensions})\n )\n `);\n\n // Drop any IVFFlat indexes that may have been created (they cause issues with small datasets)\n await client.query(`\n DROP INDEX IF EXISTS embeddings_documents_embedding_idx\n `);\n\n // Create HNSW index for similarity search (works better with any dataset size)\n await client.query(`\n CREATE INDEX IF NOT EXISTS embeddings_documents_embedding_hnsw_idx\n ON embeddings_documents\n USING hnsw (embedding vector_cosine_ops)\n `);\n\n // Create GIN index on metadata for faster lookups\n await client.query(`\n CREATE INDEX IF NOT EXISTS embeddings_documents_metadata_idx\n ON embeddings_documents\n USING gin (metadata)\n `);\n\n console.log(`Vector table initialized (dimensions: ${this.dimensions})`);\n } catch (error) {\n // Index creation might fail if not enough rows, that's okay\n console.log(\"Note: Index creation may require more data\");\n } finally {\n client.release();\n }\n }\n\n async initializeEmbeddings(openAIApiKey: string): Promise<OpenAIEmbeddings> {\n console.log(`Initializing OpenAI Embeddings (model: ${this.embeddingModel})`);\n\n if (this.embeddings) return this.embeddings;\n\n try {\n this.embeddings = new OpenAIEmbeddings({\n openAIApiKey,\n modelName: this.embeddingModel,\n dimensions: this.dimensions,\n });\n\n return this.embeddings;\n } catch (error) {\n console.error(`Failed to initialize Embeddings: ${error}`);\n throw new Error(`Failed to initialize Embeddings: ${error}`);\n }\n }\n\n async initializeChat(openAIApiKey: string): Promise<ChatOpenAI> {\n console.log(\"Initializing Chat Model\");\n\n if (this.chat) return this.chat;\n\n try {\n this.chat = new ChatOpenAI({\n modelName: \"gpt-4o-mini\",\n temperature: 0.7,\n openAIApiKey,\n });\n\n return this.chat;\n } catch (error) {\n console.error(`Failed to initialize Chat: ${error}`);\n throw new Error(`Failed to initialize Chat: ${error}`);\n }\n }\n\n async initialize(config: PluginConfig): Promise<void> {\n // Set embedding model and dimensions from config\n const model = config.embeddingModel || \"text-embedding-3-small\";\n if (EMBEDDING_MODELS[model]) {\n this.embeddingModel = model;\n this.dimensions = EMBEDDING_MODELS[model].dimensions;\n } else {\n console.warn(`Invalid embedding model \"${model}\", using default`);\n this.embeddingModel = \"text-embedding-3-small\";\n this.dimensions = EMBEDDING_MODELS[\"text-embedding-3-small\"].dimensions;\n }\n\n console.log(`Using embedding model: ${this.embeddingModel} (${this.dimensions} dimensions)`);\n\n await this.initializePool(config.neonConnectionString);\n await this.initializeEmbeddings(config.openAIApiKey);\n await this.initializeChat(config.openAIApiKey);\n\n if (this.pool) {\n this.vectorStoreConfig = {\n pool: this.pool,\n tableName: \"embeddings_documents\",\n columns: {\n idColumnName: \"id\",\n vectorColumnName: \"embedding\",\n contentColumnName: \"content\",\n metadataColumnName: \"metadata\",\n },\n distanceStrategy: \"cosine\",\n };\n }\n\n console.log(\"Plugin Manager Initialization Complete\");\n }\n\n async createEmbedding(docData: EmbeddingDocument): Promise<CreateEmbeddingResult> {\n if (!this.embeddings || !this.vectorStoreConfig) {\n throw new Error(\"Plugin manager not initialized\");\n }\n\n try {\n // Generate the embedding vector\n const embeddingVector = await this.embeddings.embedQuery(docData.content);\n\n const doc = new Document({\n pageContent: docData.content,\n metadata: {\n id: docData.id,\n title: docData.title,\n collectionType: docData.collectionType || \"standalone\",\n fieldName: docData.fieldName || \"content\",\n },\n });\n\n await PGVectorStore.fromDocuments(\n [doc],\n this.embeddings,\n this.vectorStoreConfig\n );\n\n // Get the ID of the inserted document\n const result = await this.pool!.query(\n `SELECT id FROM embeddings_documents\n WHERE metadata->>'id' = $1\n ORDER BY id DESC LIMIT 1`,\n [docData.id]\n );\n\n return {\n embeddingId: result.rows[0]?.id || \"\",\n embedding: embeddingVector,\n };\n } catch (error) {\n console.error(`Failed to create embedding: ${error}`);\n throw new Error(`Failed to create embedding: ${error}`);\n }\n }\n\n async deleteEmbedding(strapiId: string): Promise<void> {\n if (!this.pool) {\n throw new Error(\"Plugin manager not initialized\");\n }\n\n try {\n await this.pool.query(\n `DELETE FROM embeddings_documents WHERE metadata->>'id' = $1`,\n [strapiId]\n );\n } catch (error) {\n console.error(`Failed to delete embedding: ${error}`);\n throw new Error(`Failed to delete embedding: ${error}`);\n }\n }\n\n async queryEmbedding(query: string): Promise<QueryResponse> {\n if (!this.embeddings || !this.chat || !this.vectorStoreConfig) {\n throw new Error(\"Plugin manager not initialized\");\n }\n\n try {\n const vectorStore = await PGVectorStore.initialize(\n this.embeddings,\n this.vectorStoreConfig\n );\n\n // Use similaritySearchWithScore to get relevance scores\n // Retrieve more documents initially, then filter by score\n const resultsWithScores = await vectorStore.similaritySearchWithScore(query, 6);\n\n // Filter by similarity threshold (cosine similarity: 0 = identical, 2 = opposite)\n // Keep only documents with score < 0.5 (more similar)\n const SIMILARITY_THRESHOLD = 0.5;\n const relevantResults = resultsWithScores.filter(([_, score]) => score < SIMILARITY_THRESHOLD);\n\n // Take top 3 most relevant documents for context\n const topResults = relevantResults.slice(0, 3);\n const sourceDocuments = topResults.map(([doc]) => doc);\n\n // Only show the single best matching source to the user\n const bestMatchForDisplay = topResults.length > 0 ? [topResults[0][0]] : [];\n\n // Format documents for context - include title from metadata\n const formatDocs = (docs: Document[]): string => {\n return docs.map((doc) => {\n const title = doc.metadata?.title ? `Title: ${doc.metadata.title}\\n` : '';\n return `${title}${doc.pageContent}`;\n }).join(\"\\n\\n\");\n };\n\n // Create RAG prompt\n const ragPrompt = ChatPromptTemplate.fromMessages([\n [\n \"system\",\n `You are a helpful assistant that answers questions based on the provided context.\nIf you cannot find the answer in the context, say so. Be concise and accurate.\n\nContext:\n{context}`,\n ],\n [\"human\", \"{question}\"],\n ]);\n\n // Build LCEL chain - use all relevant docs for context\n const ragChain = RunnableSequence.from([\n {\n context: async () => formatDocs(sourceDocuments),\n question: new RunnablePassthrough(),\n },\n ragPrompt,\n this.chat,\n new StringOutputParser(),\n ]);\n\n const text = await ragChain.invoke(query);\n\n return {\n text,\n sourceDocuments: bestMatchForDisplay, // Only return best match to display\n };\n } catch (error) {\n console.error(`Failed to query embeddings: ${error}`);\n throw new Error(`Failed to query embeddings: ${error}`);\n }\n }\n\n async similaritySearch(\n query: string,\n k: number = 4\n ): Promise<Document[]> {\n if (!this.embeddings || !this.vectorStoreConfig) {\n throw new Error(\"Plugin manager not initialized\");\n }\n\n try {\n const vectorStore = await PGVectorStore.initialize(\n this.embeddings,\n this.vectorStoreConfig\n );\n\n return await vectorStore.similaritySearch(query, k);\n } catch (error) {\n console.error(`Failed to perform similarity search: ${error}`);\n throw new Error(`Failed to perform similarity search: ${error}`);\n }\n }\n\n isInitialized(): boolean {\n return !!(this.embeddings && this.chat && this.pool);\n }\n\n /**\n * Get all embeddings from Neon DB\n * Returns the metadata (including Strapi documentId) for each embedding\n */\n async getAllNeonEmbeddings(): Promise<Array<{\n id: string;\n strapiId: string;\n title: string;\n content: string;\n collectionType: string;\n fieldName: string;\n }>> {\n if (!this.pool) {\n throw new Error(\"Plugin manager not initialized\");\n }\n\n try {\n const result = await this.pool.query(`\n SELECT\n id,\n content,\n metadata->>'id' as strapi_id,\n metadata->>'title' as title,\n metadata->>'collectionType' as collection_type,\n metadata->>'fieldName' as field_name\n FROM embeddings_documents\n ORDER BY id\n `);\n\n return result.rows.map((row) => ({\n id: row.id,\n strapiId: row.strapi_id,\n title: row.title || '',\n content: row.content || '',\n collectionType: row.collection_type || 'standalone',\n fieldName: row.field_name || 'content',\n }));\n } catch (error) {\n console.error(`Failed to get Neon embeddings: ${error}`);\n throw new Error(`Failed to get Neon embeddings: ${error}`);\n }\n }\n\n /**\n * Delete an embedding from Neon by its Neon UUID (not Strapi ID)\n */\n async deleteNeonEmbeddingById(neonId: string): Promise<void> {\n if (!this.pool) {\n throw new Error(\"Plugin manager not initialized\");\n }\n\n try {\n await this.pool.query(\n `DELETE FROM embeddings_documents WHERE id = $1`,\n [neonId]\n );\n } catch (error) {\n console.error(`Failed to delete Neon embedding: ${error}`);\n throw new Error(`Failed to delete Neon embedding: ${error}`);\n }\n }\n\n async destroy(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n this.pool = null;\n }\n this.embeddings = null;\n this.chat = null;\n this.vectorStoreConfig = null;\n }\n}\n\nexport const pluginManager = new PluginManager();\nexport type { PluginConfig, EmbeddingDocument, QueryResponse, CreateEmbeddingResult };\n","/**\n * Zod Schemas for MCP Tool Input Validation\n */\n\nimport { z } from 'zod';\n\n// Semantic Search Schema\nexport const SemanticSearchSchema = z.object({\n query: z.string().min(1, 'Query is required'),\n limit: z.number().min(1).max(20).optional().default(5),\n});\n\n// RAG Query Schema\nexport const RagQuerySchema = z.object({\n query: z.string().min(1, 'Query is required'),\n includeSourceDocuments: z.boolean().optional().default(true),\n});\n\n// List Embeddings Schema\nexport const ListEmbeddingsSchema = z.object({\n page: z.number().min(1).optional().default(1),\n pageSize: z.number().min(1).max(50).optional().default(25),\n search: z.string().optional(),\n});\n\n// Get Embedding Schema\nexport const GetEmbeddingSchema = z.object({\n documentId: z.string().min(1, 'Document ID is required'),\n includeContent: z.boolean().optional().default(true),\n});\n\n// Create Embedding Schema\nexport const CreateEmbeddingSchema = z.object({\n title: z.string().min(1, 'Title is required'),\n content: z.string().min(1, 'Content is required'),\n metadata: z.record(z.any()).optional(),\n});\n\n// Schema registry\nexport const ToolSchemas: Record<string, z.ZodSchema> = {\n semantic_search: SemanticSearchSchema,\n rag_query: RagQuerySchema,\n list_embeddings: ListEmbeddingsSchema,\n get_embedding: GetEmbeddingSchema,\n create_embedding: CreateEmbeddingSchema,\n};\n\n/**\n * Validate tool input against its schema\n */\nexport function validateToolInput<T = unknown>(\n toolName: string,\n input: unknown\n): T {\n const schema = ToolSchemas[toolName];\n\n if (!schema) {\n throw new Error(`No schema defined for tool: ${toolName}`);\n }\n\n const result = schema.safeParse(input);\n\n if (!result.success) {\n const errors = result.error.errors\n .map((e) => `${e.path.join('.')}: ${e.message}`)\n .join(', ');\n throw new Error(`Validation failed for ${toolName}: ${errors}`);\n }\n\n return result.data as T;\n}\n","/**\n * Semantic Search Tool\n *\n * Performs vector similarity search to find relevant content.\n */\n\nimport type { Core } from '@strapi/strapi';\n\nexport const semanticSearchTool = {\n name: 'semantic_search',\n description:\n 'TRIGGER: Use when user types \"/rag\" or asks to search embeddings/content. Search for semantically similar content using vector embeddings. Returns the most relevant documents matching your query based on meaning, not just keywords.',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'The search query text to find similar content',\n },\n limit: {\n type: 'number',\n description: 'Maximum number of results to return (default: 5, max: 20)',\n default: 5,\n },\n },\n required: ['query'],\n },\n};\n\nexport async function handleSemanticSearch(\n strapi: Core.Strapi,\n args: { query: string; limit?: number }\n) {\n const { query, limit = 5 } = args;\n const maxLimit = Math.min(limit, 20);\n\n try {\n // Get the plugin manager for vector operations\n const pluginManager = (strapi as any).contentEmbeddingsManager;\n\n if (!pluginManager) {\n throw new Error('Content embeddings plugin not initialized');\n }\n\n // Perform similarity search\n const results = await pluginManager.similaritySearch(query, maxLimit);\n\n // Format results\n const formattedResults = results.map((doc: any, index: number) => ({\n rank: index + 1,\n content: doc.pageContent,\n metadata: doc.metadata,\n score: doc.score || null,\n }));\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n query,\n resultCount: formattedResults.length,\n results: formattedResults,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (error) {\n throw new Error(\n `Semantic search failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n","/**\n * RAG Query Tool\n *\n * Performs Retrieval-Augmented Generation to answer questions using embedded content.\n */\n\nimport type { Core } from '@strapi/strapi';\n\nexport const ragQueryTool = {\n name: 'rag_query',\n description:\n 'TRIGGER: Use when user types \"/rag\" followed by a question. Ask a question and get an AI-generated answer based on your embedded content. Uses RAG (Retrieval-Augmented Generation) to find relevant documents and generate a contextual response. This is the PRIMARY tool for /rag queries.',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'The question or query to answer using embedded content',\n },\n includeSourceDocuments: {\n type: 'boolean',\n description: 'Include the source documents used to generate the answer (default: true)',\n default: true,\n },\n },\n required: ['query'],\n },\n};\n\nexport async function handleRagQuery(\n strapi: Core.Strapi,\n args: { query: string; includeSourceDocuments?: boolean }\n) {\n const { query, includeSourceDocuments = true } = args;\n\n try {\n // Get the embeddings service\n const embeddingsService = strapi\n .plugin('strapi-content-embeddings')\n .service('embeddings');\n\n // Perform RAG query\n const result = await embeddingsService.queryEmbeddings(query);\n\n // Format response\n const response: any = {\n query,\n answer: result.text,\n };\n\n if (includeSourceDocuments && result.sourceDocuments) {\n response.sourceDocuments = result.sourceDocuments.map((doc: any, index: number) => ({\n rank: index + 1,\n content: doc.pageContent?.substring(0, 500) + (doc.pageContent?.length > 500 ? '...' : ''),\n metadata: doc.metadata,\n }));\n response.sourceCount = result.sourceDocuments.length;\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(response, null, 2),\n },\n ],\n };\n } catch (error) {\n throw new Error(\n `RAG query failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n","/**\n * List Embeddings Tool\n *\n * Lists all embeddings stored in the database.\n */\n\nimport type { Core } from '@strapi/strapi';\n\nexport const listEmbeddingsTool = {\n name: 'list_embeddings',\n description:\n 'List all embeddings stored in the database. Returns metadata without the full content to avoid context overflow.',\n inputSchema: {\n type: 'object',\n properties: {\n page: {\n type: 'number',\n description: 'Page number (starts at 1)',\n default: 1,\n },\n pageSize: {\n type: 'number',\n description: 'Number of items per page (max: 50)',\n default: 25,\n },\n search: {\n type: 'string',\n description: 'Search filter for title',\n },\n },\n required: [],\n },\n};\n\nexport async function handleListEmbeddings(\n strapi: Core.Strapi,\n args: { page?: number; pageSize?: number; search?: string }\n) {\n const { page = 1, pageSize = 25, search } = args;\n const limit = Math.min(pageSize, 50);\n\n try {\n // Get the embeddings service\n const embeddingsService = strapi\n .plugin('strapi-content-embeddings')\n .service('embeddings');\n\n // Build filters\n const filters: any = {};\n if (search) {\n filters.title = { $containsi: search };\n }\n\n // Fetch embeddings\n const result = await embeddingsService.getEmbeddings({\n page,\n pageSize: limit,\n filters,\n });\n\n // Format response - exclude large fields\n const embeddings = (result.results || []).map((emb: any) => ({\n id: emb.id,\n documentId: emb.documentId,\n title: emb.title,\n collectionType: emb.collectionType,\n fieldName: emb.fieldName,\n metadata: emb.metadata,\n contentPreview: emb.content?.substring(0, 200) + (emb.content?.length > 200 ? '...' : ''),\n createdAt: emb.createdAt,\n updatedAt: emb.updatedAt,\n }));\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n embeddings,\n pagination: result.pagination || {\n page,\n pageSize: limit,\n total: embeddings.length,\n },\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (error) {\n throw new Error(\n `Failed to list embeddings: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n","/**\n * Get Embedding Tool\n *\n * Retrieves a single embedding by ID.\n */\n\nimport type { Core } from '@strapi/strapi';\n\nexport const getEmbeddingTool = {\n name: 'get_embedding',\n description:\n 'Get a specific embedding by its document ID. Returns the full content and metadata.',\n inputSchema: {\n type: 'object',\n properties: {\n documentId: {\n type: 'string',\n description: 'The document ID of the embedding to retrieve',\n },\n includeContent: {\n type: 'boolean',\n description: 'Include the full content text (default: true)',\n default: true,\n },\n },\n required: ['documentId'],\n },\n};\n\nexport async function handleGetEmbedding(\n strapi: Core.Strapi,\n args: { documentId: string; includeContent?: boolean }\n) {\n const { documentId, includeContent = true } = args;\n\n try {\n // Get the embeddings service\n const embeddingsService = strapi\n .plugin('strapi-content-embeddings')\n .service('embeddings');\n\n // Fetch the embedding\n const embedding = await embeddingsService.getEmbedding(documentId);\n\n if (!embedding) {\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n error: true,\n message: `Embedding not found with documentId: ${documentId}`,\n }),\n },\n ],\n };\n }\n\n // Format response\n const result: any = {\n id: embedding.id,\n documentId: embedding.documentId,\n title: embedding.title,\n collectionType: embedding.collectionType,\n fieldName: embedding.fieldName,\n metadata: embedding.metadata,\n embeddingId: embedding.embeddingId,\n createdAt: embedding.createdAt,\n updatedAt: embedding.updatedAt,\n };\n\n if (includeContent) {\n result.content = embedding.content;\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (error) {\n throw new Error(\n `Failed to get embedding: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n","/**\n * Create Embedding Tool\n *\n * Creates a new embedding from text content.\n * Supports automatic chunking for large content.\n */\n\nimport type { Core } from '@strapi/strapi';\n\nexport const createEmbeddingTool = {\n name: 'create_embedding',\n description:\n 'Create a new embedding from text content. The content will be vectorized and stored for semantic search. For large content (over 4000 characters), enable autoChunk to automatically split into multiple embeddings.',\n inputSchema: {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description: 'A descriptive title for the embedding',\n },\n content: {\n type: 'string',\n description: 'The text content to embed (will be vectorized)',\n },\n metadata: {\n type: 'object',\n description: 'Optional metadata to associate with the embedding (tags, source, etc.)',\n },\n autoChunk: {\n type: 'boolean',\n description:\n 'Automatically split large content into chunks (default: false). When enabled, content over 4000 characters will be split into multiple embeddings with overlap for context preservation.',\n },\n },\n required: ['title', 'content'],\n },\n};\n\nexport async function handleCreateEmbedding(\n strapi: Core.Strapi,\n args: {\n title: string;\n content: string;\n metadata?: Record<string, any>;\n autoChunk?: boolean;\n }\n) {\n const { title, content, metadata, autoChunk } = args;\n\n try {\n // Get the embeddings service\n const embeddingsService = strapi\n .plugin('strapi-content-embeddings')\n .service('embeddings');\n\n // Check if we should use chunked embedding\n if (autoChunk) {\n const result = await embeddingsService.createChunkedEmbedding({\n data: {\n title,\n content,\n metadata: metadata || {},\n collectionType: 'standalone',\n fieldName: 'content',\n },\n });\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n success: true,\n message: result.wasChunked\n ? `Content chunked into ${result.totalChunks} embeddings`\n : 'Embedding created successfully (no chunking needed)',\n wasChunked: result.wasChunked,\n totalChunks: result.totalChunks,\n primaryEmbedding: {\n id: result.entity.id,\n documentId: result.entity.documentId,\n title: result.entity.title,\n embeddingId: result.entity.embeddingId,\n },\n chunks: result.chunks.map((chunk: any) => ({\n documentId: chunk.documentId,\n title: chunk.title,\n contentLength: chunk.content?.length || 0,\n })),\n contentLength: content.length,\n estimatedTokens: Math.ceil(content.length / 4),\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n // Create single embedding (original behavior)\n const embedding = await embeddingsService.createEmbedding({\n data: {\n title,\n content,\n metadata: metadata || {},\n collectionType: 'standalone',\n fieldName: 'content',\n },\n });\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n success: true,\n message: 'Embedding created successfully',\n embedding: {\n id: embedding.id,\n documentId: embedding.documentId,\n title: embedding.title,\n embeddingId: embedding.embeddingId,\n contentLength: content.length,\n metadata: embedding.metadata,\n createdAt: embedding.createdAt,\n },\n hint:\n content.length > 4000\n ? 'Content is large. Consider using autoChunk: true for better search results.'\n : undefined,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (error) {\n throw new Error(\n `Failed to create embedding: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n","/**\n * MCP Tools for Content Embeddings\n *\n * Exposes vector search, RAG queries, and embedding management tools.\n */\n\nimport type { Core } from '@strapi/strapi';\nimport { validateToolInput } from '../schemas';\n\n// Import tool handlers\nimport { semanticSearchTool, handleSemanticSearch } from './semantic-search';\nimport { ragQueryTool, handleRagQuery } from './rag-query';\nimport { listEmbeddingsTool, handleListEmbeddings } from './list-embeddings';\nimport { getEmbeddingTool, handleGetEmbedding } from './get-embedding';\nimport { createEmbeddingTool, handleCreateEmbedding } from './create-embedding';\n\n// Export all tool definitions\nexport const tools = [\n semanticSearchTool,\n ragQueryTool,\n listEmbeddingsTool,\n getEmbeddingTool,\n createEmbeddingTool,\n];\n\n// Tool handler registry\nconst toolHandlers: Record<\n string,\n (strapi: Core.Strapi, args: unknown) => Promise<any>\n> = {\n semantic_search: handleSemanticSearch,\n rag_query: handleRagQuery,\n list_embeddings: handleListEmbeddings,\n get_embedding: handleGetEmbedding,\n create_embedding: handleCreateEmbedding,\n};\n\n/**\n * Handle MCP tool calls\n */\nexport async function handleToolCall(\n strapi: Core.Strapi,\n request: { params: { name: string; arguments?: unknown } }\n) {\n const { name, arguments: args } = request.params;\n\n const handler = toolHandlers[name];\n if (!handler) {\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n error: true,\n message: `Unknown tool: ${name}`,\n availableTools: Object.keys(toolHandlers),\n }),\n },\n ],\n };\n }\n\n try {\n // Validate input using Zod schemas\n const validatedArgs = validateToolInput(name, args || {});\n const result = await handler(strapi, validatedArgs);\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n strapi.log.error(`[strapi-content-embeddings] Tool ${name} error:`, { error: errorMessage });\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n error: true,\n tool: name,\n message: errorMessage,\n }, null, 2),\n },\n ],\n };\n }\n}\n","/**\n * MCP Server Factory for Content Embeddings\n *\n * Creates an MCP server that exposes vector search and RAG tools.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type { Core } from '@strapi/strapi';\nimport { tools, handleToolCall } from './tools';\n\nexport function createMcpServer(strapi: Core.Strapi): Server {\n const server = new Server(\n {\n name: 'strapi-content-embeddings-mcp',\n version: '1.0.0',\n },\n {\n capabilities: {\n tools: {},\n },\n }\n );\n\n // Handle tool listing\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools };\n });\n\n // Handle tool execution\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n return handleToolCall(strapi, request);\n });\n\n return server;\n}\n","import type { Core } from \"@strapi/strapi\";\nimport { pluginManager } from \"./plugin-manager\";\nimport { createMcpServer } from \"./mcp/server\";\n\nconst PLUGIN_ID = \"strapi-content-embeddings\";\nconst OAUTH_PLUGIN_ID = \"strapi-oauth-mcp-manager\";\n\n/**\n * Fallback auth middleware for when OAuth manager plugin is not installed.\n * Requires Bearer token (Strapi API token) for MCP endpoints.\n */\nfunction createFallbackAuthMiddleware(strapi: Core.Strapi) {\n const mcpPath = `/api/${PLUGIN_ID}/mcp`;\n\n return async (ctx: any, next: () => Promise<void>) => {\n // Only apply to this plugin's MCP endpoint\n if (!ctx.path.startsWith(mcpPath)) {\n return next();\n }\n\n const authHeader = ctx.request.headers.authorization;\n\n if (!authHeader?.startsWith(\"Bearer \")) {\n ctx.status = 401;\n ctx.body = {\n error: \"Unauthorized\",\n message: \"Bearer token required. Provide a Strapi API token.\",\n };\n return;\n }\n\n // Extract token and set it for the controller\n const token = authHeader.slice(7);\n ctx.state.strapiToken = token;\n ctx.state.authMethod = \"api-token\";\n\n return next();\n };\n}\n\nconst bootstrap = async ({ strapi }: { strapi: Core.Strapi }) => {\n // Register RBAC actions for the plugin\n const actions = [\n {\n section: \"plugins\",\n displayName: \"Read\",\n uid: \"read\",\n pluginName: PLUGIN_ID,\n },\n {\n section: \"plugins\",\n displayName: \"Update\",\n uid: \"update\",\n pluginName: PLUGIN_ID,\n },\n {\n section: \"plugins\",\n displayName: \"Create\",\n uid: \"create\",\n pluginName: PLUGIN_ID,\n },\n {\n section: \"plugins\",\n displayName: \"Delete\",\n uid: \"delete\",\n pluginName: PLUGIN_ID,\n },\n {\n section: \"plugins\",\n displayName: \"Chat\",\n uid: \"chat\",\n pluginName: PLUGIN_ID,\n },\n ];\n\n await strapi.admin.services.permission.actionProvider.registerMany(actions);\n\n // Initialize the plugin manager with configuration\n const pluginConfig = strapi.config.get(`plugin::${PLUGIN_ID}`) as {\n openAIApiKey?: string;\n neonConnectionString?: string;\n embeddingModel?: string;\n };\n\n if (pluginConfig?.openAIApiKey && pluginConfig?.neonConnectionString) {\n try {\n await pluginManager.initialize({\n openAIApiKey: pluginConfig.openAIApiKey,\n neonConnectionString: pluginConfig.neonConnectionString,\n embeddingModel: pluginConfig.embeddingModel as any,\n });\n\n // Store plugin manager on strapi for MCP tools to access\n (strapi as any).contentEmbeddingsManager = pluginManager;\n\n strapi.log.info(`[${PLUGIN_ID}] Plugin initialized successfully`);\n } catch (error) {\n strapi.log.error(`[${PLUGIN_ID}] Failed to initialize:`, error);\n }\n } else {\n strapi.log.warn(\n `[${PLUGIN_ID}] Missing configuration. Set openAIApiKey and neonConnectionString in plugin config.`\n );\n }\n\n // Initialize MCP server\n const plugin = strapi.plugin(PLUGIN_ID) as any;\n plugin.createMcpServer = () => createMcpServer(strapi);\n plugin.sessions = new Map();\n\n // Check if OAuth manager is installed\n const oauthPlugin = strapi.plugin(OAUTH_PLUGIN_ID);\n\n if (oauthPlugin) {\n strapi.log.info(`[${PLUGIN_ID}] OAuth manager detected - OAuth + API token auth enabled`);\n } else {\n // No OAuth manager - use fallback auth\n const fallbackMiddleware = createFallbackAuthMiddleware(strapi);\n strapi.server.use(fallbackMiddleware);\n strapi.log.info(`[${PLUGIN_ID}] Using API token authentication (OAuth manager not installed)`);\n }\n\n strapi.log.info(`[${PLUGIN_ID}] MCP endpoint available at: /api/${PLUGIN_ID}/mcp`);\n};\n\nexport default bootstrap;\n","import type { Core } from \"@strapi/strapi\";\nimport { pluginManager } from \"./plugin-manager\";\n\nconst destroy = async ({ strapi }: { strapi: Core.Strapi }) => {\n // Clean up the plugin manager (close DB connections)\n await pluginManager.destroy();\n console.log(\"Content Embeddings plugin destroyed\");\n};\n\nexport default destroy;\n","import type { Core } from \"@strapi/strapi\";\n\nconst PLUGIN_ID = \"strapi-content-embeddings\";\n\nconst register = ({ strapi }: { strapi: Core.Strapi }) => {\n // Add embedding relation to all content types\n Object.values(strapi.contentTypes).forEach((contentType: any) => {\n // Skip internal content types and the embedding content type itself\n if (\n contentType.uid.startsWith(\"admin::\") ||\n contentType.uid.startsWith(\"strapi::\") ||\n contentType.uid === `plugin::${PLUGIN_ID}.embedding`\n ) {\n return;\n }\n\n // Add morphOne relation to content type\n contentType.attributes.embedding = {\n type: \"relation\",\n relation: \"morphOne\",\n target: `plugin::${PLUGIN_ID}.embedding`,\n morphBy: \"related\",\n private: false,\n configurable: false,\n };\n });\n};\n\nexport default register;\n","import schema from './schema.json';\n\nexport default {\n schema,\n};","import embedding from './embedding';\n\nexport default {\n embedding,\n}","import type { Core } from \"@strapi/strapi\";\n\nconst PLUGIN_ID = \"strapi-content-embeddings\";\n\nconst controller = ({ strapi }: { strapi: Core.Strapi }) => ({\n async createEmbedding(ctx: any) {\n try {\n const result = await strapi\n .plugin(PLUGIN_ID)\n .service(\"embeddings\")\n .createEmbedding(ctx.request.body);\n\n ctx.body = result;\n } catch (error: any) {\n ctx.throw(500, error.message || \"Failed to create embedding\");\n }\n },\n\n async deleteEmbedding(ctx: any) {\n try {\n const { id } = ctx.params;\n const result = await strapi\n .plugin(PLUGIN_ID)\n .service(\"embeddings\")\n .deleteEmbedding(id);\n\n ctx.body = result;\n } catch (error: any) {\n ctx.throw(500, error.message || \"Failed to delete embedding\");\n }\n },\n\n async updateEmbedding(ctx: any) {\n try {\n const { id } = ctx.params;\n const result = await strapi\n .plugin(PLUGIN_ID)\n .service(\"embeddings\")\n .updateEmbedding(id, ctx.request.body);\n\n ctx.body = result;\n } catch (error: any) {\n ctx.throw(500, error.message || \"Failed to update embedding\");\n }\n },\n\n async getEmbeddings(ctx: any) {\n try {\n const { page, pageSize, filters } = ctx.query;\n const result = await strapi\n .plugin(PLUGIN_ID)\n .service(\"embeddings\")\n .getEmbeddings({\n page: page ? parseInt(page, 10) : 1,\n pageSize: pageSize ? parseInt(pageSize, 10) : 10,\n filters,\n });\n\n ctx.body = result;\n } catch (error: any) {\n ctx.throw(500, error.message || \"Failed to get embeddings\");\n }\n },\n\n async getEmbedding(ctx: any) {\n try {\n const { id } = ctx.params;\n const result = await strapi\n .plugin(PLUGIN_ID)\n .service(\"embeddings\")\n .getEmbedding(id);\n\n if (!result) {\n ctx.throw(404, \"Embedding not found\");\n }\n\n ctx.body = result;\n } catch (error: any) {\n if (error.status === 404) {\n ctx.throw(404, error.message);\n }\n ctx.throw(500, error.message || \"Failed to get embedding\");\n }\n },\n\n async queryEmbeddings(ctx: any) {\n try {\n const { query } = ctx.query;\n const result = await strapi\n .plugin(PLUGIN_ID)\n .service(\"embeddings\")\n .queryEmbeddings(query);\n\n ctx.body = result;\n } catch (error: any) {\n ctx.throw(500, error.message || \"Failed to query embeddings\");\n }\n },\n\n /**\n * Sync embeddings from Neon DB to Strapi DB\n * GET /api/strapi-content-embeddings/sync\n *\n * Query params:\n * - removeOrphans: boolean (default: false) - Remove Strapi entries that don't exist in Neon\n * - dryRun: boolean (default: false) - Preview changes without applying them\n */\n async syncFromNeon(ctx: any) {\n try {\n const { removeOrphans, dryRun } = ctx.query;\n\n const result = await strapi\n .plugin(PLUGIN_ID)\n .service(\"sync\")\n .syncFromNeon({\n removeOrphans: removeOrphans === \"true\",\n dryRun: dryRun === \"true\",\n });\n\n ctx.body = result;\n } catch (error: any) {\n ctx.throw(500, error.message || \"Failed to sync embeddings\");\n }\n },\n\n /**\n * Get sync status - compare Neon and Strapi without making changes\n * GET /api/strapi-content-embeddings/sync/status\n */\n async getSyncStatus(ctx: any) {\n try {\n const result = await strapi\n .plugin(PLUGIN_ID)\n .service(\"sync\")\n .getSyncStatus();\n\n ctx.body = result;\n } catch (error: any) {\n ctx.throw(500, error.message || \"Failed to get sync status\");\n }\n },\n});\n\nexport default controller;\n","/**\n * MCP Controller for Content Embeddings\n *\n * Handles MCP (Model Context Protocol) requests with session management.\n */\n\nimport type { Core } from '@strapi/strapi';\nimport { randomUUID } from 'node:crypto';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\n\nconst PLUGIN_ID = 'strapi-content-embeddings';\n\n// Session timeout: 4 hours\nconst SESSION_TIMEOUT_MS = 4 * 60 * 60 * 1000;\n\ninterface McpSession {\n server: any;\n transport: StreamableHTTPServerTransport;\n createdAt: number;\n strapiToken?: string;\n}\n\ninterface ContentEmbeddingsPlugin {\n createMcpServer: () => any;\n sessions: Map<string, McpSession>;\n}\n\n/**\n * Check if a session has expired\n */\nfunction isSessionExpired(session: { createdAt: number }): boolean {\n return Date.now() - session.createdAt > SESSION_TIMEOUT_MS;\n}\n\n/**\n * Clean up expired sessions\n */\nfunction cleanupExpiredSessions(plugin: ContentEmbeddingsPlugin, strapi: Core.Strapi): void {\n let cleaned = 0;\n for (const [sessionId, session] of plugin.sessions.entries()) {\n if (isSessionExpired(session)) {\n try {\n session.server.close();\n } catch {\n // Ignore close errors\n }\n plugin.sessions.delete(sessionId);\n cleaned++;\n }\n }\n if (cleaned > 0) {\n strapi.log.debug(`[${PLUGIN_ID}] Cleaned up ${cleaned} expired MCP sessions`);\n }\n}\n\n/**\n * MCP Controller\n */\nconst mcpController = ({ strapi }: { strapi: Core.Strapi }) => ({\n /**\n * Handle MCP requests (POST, GET, DELETE)\n */\n async handle(ctx: any) {\n const plugin = strapi.plugin(PLUGIN_ID) as unknown as ContentEmbeddingsPlugin;\n\n if (!plugin.createMcpServer) {\n ctx.status = 503;\n ctx.body = {\n error: 'MCP not initialized',\n message: 'The MCP server is not available. Check plugin configuration.',\n };\n return;\n }\n\n // Periodically clean up expired sessions\n if (Math.random() < 0.01) {\n cleanupExpiredSessions(plugin, strapi);\n }\n\n try {\n // Get session ID from header\n const requestedSessionId = ctx.request.headers['mcp-session-id'];\n let session = requestedSessionId ? plugin.sessions.get(requestedSessionId) : null;\n\n // Check if session exists and is not expired\n if (session && isSessionExpired(session)) {\n strapi.log.debug(`[${PLUGIN_ID}] Session expired, removing: ${requestedSessionId}`);\n try {\n session.server.close();\n } catch {\n // Ignore close errors\n }\n plugin.sessions.delete(requestedSessionId);\n session = null;\n }\n\n // If client sent a session ID but session doesn't exist, return error to force re-init\n if (requestedSessionId && !session) {\n ctx.status = 400;\n ctx.body = {\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Session expired or invalid. Please reinitialize the connection.',\n },\n id: null,\n };\n return;\n }\n\n // Create new session if none exists\n if (!session) {\n const sessionId = randomUUID();\n const server = plugin.createMcpServer();\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => sessionId,\n });\n\n await server.connect(transport);\n\n session = {\n server,\n transport,\n createdAt: Date.now(),\n strapiToken: ctx.state.strapiToken,\n };\n plugin.sessions.set(sessionId, session);\n\n strapi.log.debug(\n `[${PLUGIN_ID}] New MCP session created: ${sessionId} (auth: ${ctx.state.authMethod || 'unknown'})`\n );\n }\n\n // Handle the request - wrap in try/catch to handle transport errors\n try {\n await session.transport.handleRequest(ctx.req, ctx.res, ctx.request.body);\n } catch (transportError) {\n strapi.log.warn(`[${PLUGIN_ID}] Transport error, cleaning up session: ${requestedSessionId}`, {\n error: transportError instanceof Error ? transportError.message : String(transportError),\n });\n\n try {\n session.server.close();\n } catch {\n // Ignore close errors\n }\n plugin.sessions.delete(requestedSessionId!);\n\n if (!ctx.res.headersSent) {\n ctx.status = 400;\n ctx.body = {\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Session transport error. Please reinitialize the connection.',\n },\n id: null,\n };\n }\n return;\n }\n\n // Prevent Koa from handling response\n ctx.respond = false;\n } catch (error) {\n strapi.log.error(`[${PLUGIN_ID}] Error handling MCP request`, {\n error: error instanceof Error ? error.message : String(error),\n method: ctx.method,\n path: ctx.path,\n });\n\n if (!ctx.res.headersSent) {\n ctx.status = 500;\n ctx.body = {\n error: 'MCP request failed',\n message: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n },\n});\n\nexport default mcpController;\n","import controller from './controller';\nimport mcp from './mcp';\n\nexport default {\n controller,\n mcp,\n};\n","export default {};\n","export default {};\n","export default [\n {\n method: 'GET',\n path: '/embeddings-query',\n handler: 'controller.queryEmbeddings',\n },\n // Sync routes - for cron jobs or manual triggering\n // Use API token for authentication\n {\n method: 'GET',\n path: '/sync',\n handler: 'controller.syncFromNeon',\n config: {\n description: 'Sync embeddings from Neon DB to Strapi. Query params: removeOrphans=true, dryRun=true',\n },\n },\n {\n method: 'POST',\n path: '/sync',\n handler: 'controller.syncFromNeon',\n config: {\n description: 'Sync embeddings from Neon DB to Strapi. Query params: removeOrphans=true, dryRun=true',\n },\n },\n {\n method: 'GET',\n path: '/sync/status',\n handler: 'controller.getSyncStatus',\n config: {\n description: 'Get sync status between Neon and Strapi without making changes',\n },\n },\n // MCP routes - auth handled by middleware\n {\n method: 'POST',\n path: '/mcp',\n handler: 'mcp.handle',\n config: {\n auth: false,\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/mcp',\n handler: 'mcp.handle',\n config: {\n auth: false,\n policies: [],\n },\n },\n {\n method: 'DELETE',\n path: '/mcp',\n handler: 'mcp.handle',\n config: {\n auth: false,\n policies: [],\n },\n },\n]","export default [\n{\n method: 'POST',\n path: '/embeddings/create-embedding',\n handler: 'controller.createEmbedding',\n config: {\n policies: [\n {\n name: 'admin::hasPermissions',\n config: { actions: ['plugin::strapi-content-embeddings.create'] }\n },\n ]\n },\n},\n{\n method: 'DELETE',\n path: '/embeddings/delete-embedding/:id',\n handler: 'controller.deleteEmbedding',\n config: {\n policies: [\n {\n name: 'admin::hasPermissions',\n config: { actions: ['plugin::strapi-content-embeddings.delete'] }\n },\n ]\n },\n},\n{\n method: 'PUT',\n path: '/embeddings/update-embedding/:id',\n handler: 'controller.updateEmbedding',\n config: {\n policies: [\n {\n name: 'admin::hasPermissions',\n config: { actions: ['plugin::strapi-content-embeddings.update'] }\n },\n ]\n },\n},\n{\n method: 'GET',\n path: '/embeddings/embeddings-query',\n handler: 'controller.queryEmbeddings',\n config: {\n policies: [\n {\n name: 'admin::hasPermissions',\n config: { actions: ['plugin::strapi-content-embeddings.chat'] }\n },\n ]\n },\n},\n{\n method: 'GET',\n path: '/embeddings/find/:id',\n handler: 'controller.getEmbedding',\n config: {\n policies: [\n {\n name: 'admin::hasPermissions',\n config: { actions: ['plugin::strapi-content-embeddings.read'] }\n },\n ]\n },\n},\n{\n method: 'GET',\n path: '/embeddings/find',\n handler: 'controller.getEmbeddings',\n config: {\n policies: [\n {\n name: 'admin::hasPermissions',\n config: { actions: ['plugin::strapi-content-embeddings.read'] }\n },\n ]\n },\n},]","\"use strict\";\n\nimport contentApi from './content-api';\nimport admin from './admin';\n\nexport default {\n \"content-api\": {\n type: \"content-api\",\n routes: [...contentApi],\n },\n admin: {\n type: \"admin\",\n routes: [...admin],\n },\n}; \n","/**\n * Text chunking utilities for splitting large content into embeddable chunks\n */\n\nexport interface ChunkOptions {\n /** Maximum characters per chunk (default: 4000, roughly ~1000 tokens) */\n chunkSize?: number;\n /** Number of characters to overlap between chunks (default: 200) */\n chunkOverlap?: number;\n /** Separator to use when splitting (default: splits on paragraphs, sentences, then words) */\n separators?: string[];\n}\n\nexport interface TextChunk {\n /** The chunk text content */\n text: string;\n /** Zero-based chunk index */\n chunkIndex: number;\n /** Total number of chunks */\n totalChunks: number;\n /** Character offset in original text */\n startOffset: number;\n /** Character end offset in original text */\n endOffset: number;\n}\n\nconst DEFAULT_SEPARATORS = [\n \"\\n\\n\", // Paragraphs\n \"\\n\", // Lines\n \". \", // Sentences\n \"! \", // Exclamations\n \"? \", // Questions\n \"; \", // Semicolons\n \", \", // Commas\n \" \", // Words\n \"\", // Characters (last resort)\n];\n\n/**\n * Estimate token count from character count\n * OpenAI models average ~4 characters per token for English text\n */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\n/**\n * Check if content exceeds the recommended chunk size\n */\nexport function needsChunking(content: string, maxChars: number = 4000): boolean {\n return content.length > maxChars;\n}\n\n/**\n * Split text by a separator, keeping the separator at the end of each piece\n */\nfunction splitWithSeparator(text: string, separator: string): string[] {\n if (separator === \"\") {\n return text.split(\"\");\n }\n\n const parts = text.split(separator);\n const result: string[] = [];\n\n for (let i = 0; i < parts.length; i++) {\n if (i < parts.length - 1) {\n result.push(parts[i] + separator);\n } else if (parts[i]) {\n result.push(parts[i]);\n }\n }\n\n return result;\n}\n\n/**\n * Recursively split text into chunks that fit within the size limit\n */\nfunction splitText(\n text: string,\n chunkSize: number,\n separators: string[]\n): string[] {\n if (text.length <= chunkSize) {\n return [text];\n }\n\n // Find the best separator to use\n let bestSeparator = separators[separators.length - 1]; // Default to last (smallest)\n\n for (const sep of separators) {\n if (text.includes(sep)) {\n bestSeparator = sep;\n break;\n }\n }\n\n const splits = splitWithSeparator(text, bestSeparator);\n const chunks: string[] = [];\n let currentChunk = \"\";\n\n for (const split of splits) {\n if ((currentChunk + split).length <= chunkSize) {\n currentChunk += split;\n } else {\n if (currentChunk) {\n chunks.push(currentChunk);\n }\n\n // If single split is too large, recursively split it\n if (split.length > chunkSize) {\n const remainingSeparators = separators.slice(separators.indexOf(bestSeparator) + 1);\n if (remainingSeparators.length > 0) {\n chunks.push(...splitText(split, chunkSize, remainingSeparators));\n } else {\n // Force split at chunkSize if no separators left\n for (let i = 0; i < split.length; i += chunkSize) {\n chunks.push(split.slice(i, i + chunkSize));\n }\n }\n currentChunk = \"\";\n } else {\n currentChunk = split;\n }\n }\n }\n\n if (currentChunk) {\n chunks.push(currentChunk);\n }\n\n return chunks;\n}\n\n/**\n * Add overlap between chunks for better context preservation\n */\nfunction addOverlap(chunks: string[], overlap: number): string[] {\n if (overlap <= 0 || chunks.length <= 1) {\n return chunks;\n }\n\n const result: string[] = [];\n\n for (let i = 0; i < chunks.length; i++) {\n let chunk = chunks[i];\n\n // Add overlap from previous chunk\n if (i > 0) {\n const prevChunk = chunks[i - 1];\n const overlapText = prevChunk.slice(-overlap);\n chunk = overlapText + chunk;\n }\n\n result.push(chunk);\n }\n\n return result;\n}\n\n/**\n * Split content into chunks suitable for embedding\n *\n * @param content - The text content to split\n * @param options - Chunking options\n * @returns Array of TextChunk objects\n */\nexport function chunkContent(\n content: string,\n options: ChunkOptions = {}\n): TextChunk[] {\n const {\n chunkSize = 4000,\n chunkOverlap = 200,\n separators = DEFAULT_SEPARATORS,\n } = options;\n\n // Clean the content\n const cleanContent = content.trim();\n\n if (!cleanContent) {\n return [];\n }\n\n // If content fits in one chunk, return as single chunk\n if (cleanContent.length <= chunkSize) {\n return [{\n text: cleanContent,\n chunkIndex: 0,\n totalChunks: 1,\n startOffset: 0,\n endOffset: cleanContent.length,\n }];\n }\n\n // Split into initial chunks\n const rawChunks = splitText(cleanContent, chunkSize - chunkOverlap, separators);\n\n // Add overlap\n const chunksWithOverlap = addOverlap(rawChunks, chunkOverlap);\n\n // Calculate offsets and build result\n const result: TextChunk[] = [];\n let currentOffset = 0;\n\n for (let i = 0; i < chunksWithOverlap.length; i++) {\n const text = chunksWithOverlap[i].trim();\n\n if (text) {\n result.push({\n text,\n chunkIndex: i,\n totalChunks: chunksWithOverlap.length,\n startOffset: currentOffset,\n endOffset: currentOffset + rawChunks[i].length,\n });\n }\n\n currentOffset += rawChunks[i].length;\n }\n\n // Update totalChunks after filtering empty chunks\n const totalChunks = result.length;\n result.forEach((chunk, idx) => {\n chunk.chunkIndex = idx;\n chunk.totalChunks = totalChunks;\n });\n\n return result;\n}\n\n/**\n * Format chunk title with index information\n */\nexport function formatChunkTitle(\n baseTitle: string,\n chunkIndex: number,\n totalChunks: number\n): string {\n if (totalChunks === 1) {\n return baseTitle;\n }\n return `${baseTitle} [Part ${chunkIndex + 1}/${totalChunks}]`;\n}\n","import type { Core } from \"@strapi/strapi\";\nimport { pluginManager } from \"../plugin-manager\";\nimport {\n chunkContent,\n formatChunkTitle,\n needsChunking,\n estimateTokens,\n} from \"../utils/chunking\";\nimport type { PluginConfigSchema } from \"../config\";\n\nconst PLUGIN_ID = \"strapi-content-embeddings\";\nconst CONTENT_TYPE_UID = `plugin::${PLUGIN_ID}.embedding` as const;\n\nexport interface CreateEmbeddingData {\n data: {\n title: string;\n content: string;\n collectionType?: string;\n fieldName?: string;\n metadata?: Record<string, any>;\n related?: {\n __type: string;\n id: number;\n };\n /** Enable chunking for large content (overrides config.autoChunk) */\n autoChunk?: boolean;\n };\n}\n\nexport interface UpdateEmbeddingData {\n data: {\n title?: string;\n content?: string;\n metadata?: Record<string, any>;\n /** Enable chunking for large content on update (overrides config.autoChunk) */\n autoChunk?: boolean;\n };\n}\n\nexport interface ChunkedEmbeddingResult {\n /** The parent/first embedding entity */\n entity: any;\n /** All chunk entities created */\n chunks: any[];\n /** Total number of chunks created */\n totalChunks: number;\n /** Whether content was chunked */\n wasChunked: boolean;\n}\n\nconst embeddings = ({ strapi }: { strapi: Core.Strapi }) => ({\n /**\n * Get plugin config with defaults\n */\n getConfig(): PluginConfigSchema {\n const config = strapi.config.get(\"plugin::strapi-content-embeddings\") as PluginConfigSchema || {};\n return {\n chunkSize: config.chunkSize || 4000,\n chunkOverlap: config.chunkOverlap || 200,\n autoChunk: config.autoChunk || false,\n ...config,\n };\n },\n\n /**\n * Create a single embedding (no chunking)\n */\n async createEmbedding(data: CreateEmbeddingData) {\n const { title, content, collectionType, fieldName, metadata, related, autoChunk } = data.data;\n const config = this.getConfig();\n\n // Check if chunking should be applied\n const shouldChunk = autoChunk ?? config.autoChunk;\n const chunkSize = config.chunkSize || 4000;\n\n if (shouldChunk && needsChunking(content, chunkSize)) {\n // Delegate to chunked embedding creation\n const result = await this.createChunkedEmbedding(data);\n // Return the first chunk as the primary entity for backwards compatibility\n return result.entity;\n }\n\n // Build entity data - only include related if it has valid __type and id\n const entityData: Record<string, any> = {\n title,\n content,\n collectionType: collectionType || \"standalone\",\n fieldName: fieldName || \"content\",\n metadata: metadata || null,\n };\n\n // Only add related if both __type and id are provided\n if (related && related.__type && related.id) {\n entityData.related = related;\n }\n\n // First create the entity in Strapi DB\n const entity = await strapi.documents(CONTENT_TYPE_UID).create({\n data: entityData,\n });\n\n if (!pluginManager.isInitialized()) {\n console.warn(\"Plugin manager not initialized, skipping vector embedding\");\n return entity;\n }\n\n try {\n // Create embedding in vector store and get the vector\n const result = await pluginManager.createEmbedding({\n id: entity.documentId,\n title,\n content,\n collectionType: collectionType || \"standalone\",\n fieldName: fieldName || \"content\",\n });\n\n // Update entity with embedding ID and vector\n const updatedEntity = await strapi.documents(CONTENT_TYPE_UID).update({\n documentId: entity.documentId,\n data: {\n embeddingId: result.embeddingId,\n embedding: result.embedding,\n } as any,\n });\n\n return updatedEntity;\n } catch (error) {\n console.error(\"Failed to create embedding in vector store:\", error);\n // Return the entity even if embedding failed\n return entity;\n }\n },\n\n /**\n * Create embeddings with automatic chunking for large content\n * Creates multiple embedding entities, one per chunk\n */\n async createChunkedEmbedding(data: CreateEmbeddingData): Promise<ChunkedEmbeddingResult> {\n const { title, content, collectionType, fieldName, metadata, related } = data.data;\n const config = this.getConfig();\n\n const chunkSize = config.chunkSize || 4000;\n const chunkOverlap = config.chunkOverlap || 200;\n\n // Split content into chunks\n const chunks = chunkContent(content, { chunkSize, chunkOverlap });\n\n if (chunks.length === 0) {\n throw new Error(\"Content is empty or could not be chunked\");\n }\n\n // If only one chunk, create normally\n if (chunks.length === 1) {\n const entity = await this.createEmbedding({\n data: {\n ...data.data,\n autoChunk: false, // Prevent recursive chunking\n },\n });\n return {\n entity,\n chunks: [entity],\n totalChunks: 1,\n wasChunked: false,\n };\n }\n\n console.log(`Chunking content into ${chunks.length} parts (chunkSize: ${chunkSize}, overlap: ${chunkOverlap})`);\n\n const createdChunks: any[] = [];\n let parentDocumentId: string | null = null;\n\n for (const chunk of chunks) {\n const chunkTitle = formatChunkTitle(title, chunk.chunkIndex, chunk.totalChunks);\n\n // Build chunk metadata\n const chunkMetadata = {\n ...metadata,\n isChunk: true,\n chunkIndex: chunk.chunkIndex,\n totalChunks: chunk.totalChunks,\n startOffset: chunk.startOffset,\n endOffset: chunk.endOffset,\n originalTitle: title,\n parentDocumentId: parentDocumentId,\n estimatedTokens: estimateTokens(chunk.text),\n };\n\n // Build entity data\n const entityData: Record<string, any> = {\n title: chunkTitle,\n content: chunk.text,\n collectionType: collectionType || \"standalone\",\n fieldName: fieldName || \"content\",\n metadata: chunkMetadata,\n };\n\n // Only add related to first chunk\n if (chunk.chunkIndex === 0 && related && related.__type && related.id) {\n entityData.related = related;\n }\n\n // Create entity in Strapi DB\n const entity = await strapi.documents(CONTENT_TYPE_UID).create({\n data: entityData,\n });\n\n // Store first chunk's documentId as parent reference\n if (chunk.chunkIndex === 0) {\n parentDocumentId = entity.documentId;\n } else {\n // Update metadata with parent reference\n await strapi.documents(CONTENT_TYPE_UID).update({\n documentId: entity.documentId,\n data: {\n metadata: {\n ...chunkMetadata,\n parentDocumentId,\n },\n } as any,\n });\n }\n\n // Create vector embedding if plugin is initialized\n if (pluginManager.isInitialized()) {\n try {\n const result = await pluginManager.createEmbedding({\n id: entity.documentId,\n title: chunkTitle,\n content: chunk.text,\n collectionType: collectionType || \"standalone\",\n fieldName: fieldName || \"content\",\n });\n\n // Update entity with embedding ID and vector\n const updatedEntity = await strapi.documents(CONTENT_TYPE_UID).update({\n documentId: entity.documentId,\n data: {\n embeddingId: result.embeddingId,\n embedding: result.embedding,\n } as any,\n });\n\n createdChunks.push(updatedEntity);\n } catch (error) {\n console.error(`Failed to create embedding for chunk ${chunk.chunkIndex}:`, error);\n createdChunks.push(entity);\n }\n } else {\n createdChunks.push(entity);\n }\n }\n\n return {\n entity: createdChunks[0],\n chunks: createdChunks,\n totalChunks: createdChunks.length,\n wasChunked: true,\n };\n },\n\n async deleteEmbedding(id: number | string) {\n const currentEntry = await strapi.documents(CONTENT_TYPE_UID).findOne({\n documentId: String(id),\n });\n\n if (!currentEntry) {\n throw new Error(`Embedding with id ${id} not found`);\n }\n\n // Delete from vector store if plugin is initialized\n if (pluginManager.isInitialized()) {\n try {\n await pluginManager.deleteEmbedding(String(id));\n } catch (error) {\n console.error(\"Failed to delete from vector store:\", error);\n }\n }\n\n // Delete from Strapi DB\n const deletedEntry = await strapi.documents(CONTENT_TYPE_UID).delete({\n documentId: String(id),\n });\n\n return deletedEntry;\n },\n\n /**\n * Find all chunks related to a parent document\n * Returns chunks including the parent itself\n */\n async findRelatedChunks(documentId: string): Promise<any[]> {\n const entry = await strapi.documents(CONTENT_TYPE_UID).findOne({\n documentId,\n });\n\n if (!entry) {\n return [];\n }\n\n const metadata = entry.metadata as Record<string, any> | null;\n\n // Determine the parent document ID\n // If this entry has a parentDocumentId, use that; otherwise this is the parent\n const parentId = metadata?.parentDocumentId || documentId;\n const isChunked = metadata?.isChunk === true;\n\n // If not a chunked document, return just this entry\n if (!isChunked && !metadata?.parentDocumentId) {\n // Check if this document has children (is a parent)\n const children = await strapi.documents(CONTENT_TYPE_UID).findMany({\n filters: {\n metadata: {\n $containsi: `\"parentDocumentId\":\"${documentId}\"`,\n },\n },\n });\n\n if (children.length === 0) {\n return [entry];\n }\n // This is a parent with children\n return [entry, ...children];\n }\n\n // Find all chunks with the same parentDocumentId (including the parent)\n const allChunks = await strapi.documents(CONTENT_TYPE_UID).findMany({\n filters: {\n $or: [\n { documentId: parentId },\n {\n metadata: {\n $containsi: `\"parentDocumentId\":\"${parentId}\"`,\n },\n },\n ],\n },\n });\n\n // Sort by chunk index\n return allChunks.sort((a, b) => {\n const aIndex = (a.metadata as any)?.chunkIndex ?? 0;\n const bIndex = (b.metadata as any)?.chunkIndex ?? 0;\n return aIndex - bIndex;\n });\n },\n\n /**\n * Delete all chunks related to a parent document\n */\n async deleteRelatedChunks(documentId: string): Promise<number> {\n const chunks = await this.findRelatedChunks(documentId);\n\n for (const chunk of chunks) {\n // Delete from vector store\n if (pluginManager.isInitialized()) {\n try {\n await pluginManager.deleteEmbedding(chunk.documentId);\n } catch (error) {\n console.error(`Failed to delete chunk ${chunk.documentId} from vector store:`, error);\n }\n }\n\n // Delete from Strapi DB\n await strapi.documents(CONTENT_TYPE_UID).delete({\n documentId: chunk.documentId,\n });\n }\n\n return chunks.length;\n },\n\n /**\n * Update embeddings with automatic chunking support\n * Handles re-chunking when content changes and exceeds chunk size\n */\n async updateChunkedEmbedding(\n id: string,\n data: UpdateEmbeddingData\n ): Promise<ChunkedEmbeddingResult> {\n const { title, content, metadata, autoChunk } = data.data;\n const config = this.getConfig();\n\n // Find the current entry\n const currentEntry = await strapi.documents(CONTENT_TYPE_UID).findOne({\n documentId: id,\n });\n\n if (!currentEntry) {\n throw new Error(`Embedding with id ${id} not found`);\n }\n\n const currentMetadata = currentEntry.metadata as Record<string, any> | null;\n const parentDocumentId = currentMetadata?.parentDocumentId || id;\n\n // Determine new content\n const newContent = content ?? currentEntry.content;\n const newTitle = title ?? currentMetadata?.originalTitle ?? currentEntry.title;\n\n // Check if new content needs chunking\n const shouldChunk = autoChunk ?? config.autoChunk;\n const chunkSize = config.chunkSize || 4000;\n const contentNeedsChunking = shouldChunk && needsChunking(newContent, chunkSize);\n\n // Find all related chunks to get original relationship info\n const existingChunks = await this.findRelatedChunks(id);\n\n // Extract the original related info from the first chunk (index 0)\n let originalRelated: { __type: string; id: number } | undefined;\n const firstChunk = existingChunks.find(\n (c) => (c.metadata as any)?.chunkIndex === 0 || c.documentId === parentDocumentId\n );\n if (firstChunk?.related) {\n originalRelated = firstChunk.related;\n }\n\n // Delete all existing chunks\n const deletedCount = await this.deleteRelatedChunks(id);\n console.log(`Deleted ${deletedCount} existing chunk(s) for update`);\n\n // Preserve non-chunk metadata\n const preservedMetadata = { ...metadata };\n // Remove chunk-specific fields that will be regenerated\n delete preservedMetadata?.isChunk;\n delete preservedMetadata?.chunkIndex;\n delete preservedMetadata?.totalChunks;\n delete preservedMetadata?.startOffset;\n delete preservedMetadata?.endOffset;\n delete preservedMetadata?.originalTitle;\n delete preservedMetadata?.parentDocumentId;\n delete preservedMetadata?.estimatedTokens;\n\n // Create new embedding(s)\n if (contentNeedsChunking) {\n // Create chunked embeddings\n return await this.createChunkedEmbedding({\n data: {\n title: newTitle.replace(/\\s*\\[Part \\d+\\/\\d+\\]$/, ''), // Remove old part suffix\n content: newContent,\n collectionType: currentEntry.collectionType || \"standalone\",\n fieldName: currentEntry.fieldName || \"content\",\n metadata: preservedMetadata,\n related: originalRelated,\n autoChunk: true,\n },\n });\n } else {\n // Create single embedding\n const entity = await this.createEmbedding({\n data: {\n title: newTitle.replace(/\\s*\\[Part \\d+\\/\\d+\\]$/, ''), // Remove old part suffix\n content: newContent,\n collectionType: currentEntry.collectionType || \"standalone\",\n fieldName: currentEntry.fieldName || \"content\",\n metadata: preservedMetadata,\n related: originalRelated,\n autoChunk: false,\n },\n });\n\n return {\n entity,\n chunks: [entity],\n totalChunks: 1,\n wasChunked: false,\n };\n }\n },\n\n async updateEmbedding(id: string, data: UpdateEmbeddingData) {\n const { title, content, metadata, autoChunk } = data.data;\n const config = this.getConfig();\n\n const currentEntry = await strapi.documents(CONTENT_TYPE_UID).findOne({\n documentId: id,\n });\n\n if (!currentEntry) {\n throw new Error(`Embedding with id ${id} not found`);\n }\n\n const currentMetadata = currentEntry.metadata as Record<string, any> | null;\n const isCurrentlyChunked = currentMetadata?.isChunk === true;\n const hasRelatedChunks = currentMetadata?.parentDocumentId || isCurrentlyChunked;\n\n // Determine if we need chunked update\n const shouldChunk = autoChunk ?? config.autoChunk;\n const chunkSize = config.chunkSize || 4000;\n const newContent = content ?? currentEntry.content;\n const contentNeedsChunking = shouldChunk && needsChunking(newContent, chunkSize);\n const contentChanged = content !== undefined && content !== currentEntry.content;\n\n // Delegate to chunked update if:\n // 1. The entry is currently part of a chunk group, OR\n // 2. The new content needs chunking\n if (hasRelatedChunks || contentNeedsChunking) {\n const result = await this.updateChunkedEmbedding(id, data);\n return result.entity;\n }\n\n // Simple update for non-chunked content\n const updateData: Record<string, any> = {};\n if (title !== undefined) updateData.title = title;\n if (content !== undefined) updateData.content = content;\n if (metadata !== undefined) updateData.metadata = metadata;\n\n // Update entity in Strapi DB\n let updatedEntity = await strapi.documents(CONTENT_TYPE_UID).update({\n documentId: id,\n data: updateData,\n });\n\n // If content changed and plugin is initialized, update the vector\n if (contentChanged && pluginManager.isInitialized()) {\n try {\n // Delete old embedding from vector store\n await pluginManager.deleteEmbedding(id);\n\n // Create new embedding with updated content\n const result = await pluginManager.createEmbedding({\n id,\n title: title || currentEntry.title,\n content: content,\n collectionType: currentEntry.collectionType || \"standalone\",\n fieldName: currentEntry.fieldName || \"content\",\n });\n\n // Update entity with new embedding data\n updatedEntity = await strapi.documents(CONTENT_TYPE_UID).update({\n documentId: id,\n data: {\n embeddingId: result.embeddingId,\n embedding: result.embedding,\n } as any,\n });\n } catch (error) {\n console.error(\"Failed to update embedding in vector store:\", error);\n }\n }\n\n return updatedEntity;\n },\n\n async queryEmbeddings(query: string) {\n if (!query || query.trim() === \"\") {\n return { error: \"Please provide a query\" };\n }\n\n if (!pluginManager.isInitialized()) {\n return { error: \"Plugin not initialized. Check your configuration.\" };\n }\n\n try {\n const response = await pluginManager.queryEmbedding(query);\n return response;\n } catch (error) {\n console.error(\"Query failed:\", error);\n return { error: \"Failed to query embeddings\" };\n }\n },\n\n async getEmbedding(id: number | string) {\n return await strapi.documents(CONTENT_TYPE_UID).findOne({\n documentId: String(id),\n });\n },\n\n async getEmbeddings(params?: {\n page?: number;\n pageSize?: number;\n filters?: any;\n }) {\n const page = params?.page || 1;\n const pageSize = params?.pageSize || 10;\n const start = (page - 1) * pageSize;\n\n const [data, totalCount] = await Promise.all([\n strapi.documents(CONTENT_TYPE_UID).findMany({\n limit: pageSize,\n start,\n filters: params?.filters,\n }),\n strapi.documents(CONTENT_TYPE_UID).count({\n filters: params?.filters,\n }),\n ]);\n\n return {\n data,\n count: data.length,\n totalCount,\n };\n },\n});\n\nexport default embeddings;\n","import type { Core } from \"@strapi/strapi\";\nimport { pluginManager } from \"../plugin-manager\";\n\nconst PLUGIN_ID = \"strapi-content-embeddings\";\nconst CONTENT_TYPE_UID = `plugin::${PLUGIN_ID}.embedding` as const;\n\nexport interface SyncResult {\n success: boolean;\n timestamp: string;\n neonCount: number;\n strapiCount: number;\n actions: {\n created: number;\n updated: number;\n orphansRemoved: number;\n };\n details: {\n created: string[];\n updated: string[];\n orphansRemoved: string[];\n };\n errors: string[];\n}\n\ninterface NeonEmbedding {\n id: string;\n strapiId: string;\n title: string;\n content: string;\n collectionType: string;\n fieldName: string;\n}\n\ninterface StrapiEmbedding {\n documentId: string;\n title: string;\n content: string;\n embeddingId: string | null;\n collectionType: string;\n fieldName: string;\n}\n\nconst sync = ({ strapi }: { strapi: Core.Strapi }) => ({\n /**\n * Sync embeddings from Neon DB to Strapi DB\n *\n * This performs the following operations:\n * 1. Fetches all embeddings from Neon DB (source of truth)\n * 2. Fetches all embeddings from Strapi DB\n * 3. Creates missing entries in Strapi that exist in Neon\n * 4. Updates Strapi entries where content differs from Neon\n * 5. Optionally removes orphaned Strapi entries (no matching Neon record)\n */\n async syncFromNeon(options?: {\n removeOrphans?: boolean;\n dryRun?: boolean;\n }): Promise<SyncResult> {\n const { removeOrphans = false, dryRun = false } = options || {};\n\n const result: SyncResult = {\n success: false,\n timestamp: new Date().toISOString(),\n neonCount: 0,\n strapiCount: 0,\n actions: {\n created: 0,\n updated: 0,\n orphansRemoved: 0,\n },\n details: {\n created: [],\n updated: [],\n orphansRemoved: [],\n },\n errors: [],\n };\n\n // Check if plugin is initialized\n if (!pluginManager.isInitialized()) {\n result.errors.push(\n \"Plugin manager not initialized. Check your Neon and OpenAI configuration.\"\n );\n return result;\n }\n\n try {\n // Step 1: Get all embeddings from Neon DB\n const neonEmbeddings = await pluginManager.getAllNeonEmbeddings();\n result.neonCount = neonEmbeddings.length;\n\n // Step 2: Get all embeddings from Strapi DB\n const strapiEmbeddings = (await strapi\n .documents(CONTENT_TYPE_UID)\n .findMany({\n limit: 10000, // High limit to get all\n })) as unknown as StrapiEmbedding[];\n result.strapiCount = strapiEmbeddings.length;\n\n // Create lookup maps\n const neonBystrapiId = new Map<string, NeonEmbedding>();\n for (const neon of neonEmbeddings) {\n if (neon.strapiId) {\n neonBystrapiId.set(neon.strapiId, neon);\n }\n }\n\n const strapiByDocumentId = new Map<string, StrapiEmbedding>();\n for (const strapi of strapiEmbeddings) {\n strapiByDocumentId.set(strapi.documentId, strapi);\n }\n\n // Step 3: Find Neon embeddings that don't exist in Strapi\n for (const neon of neonEmbeddings) {\n if (!neon.strapiId) {\n // Neon record has no Strapi reference - skip or log\n result.errors.push(\n `Neon embedding ${neon.id} has no strapiId in metadata`\n );\n continue;\n }\n\n const existingStrapi = strapiByDocumentId.get(neon.strapiId);\n\n if (!existingStrapi) {\n // Create new Strapi entry\n if (!dryRun) {\n try {\n await strapi.documents(CONTENT_TYPE_UID).create({\n data: {\n documentId: neon.strapiId,\n title: neon.title,\n content: neon.content,\n embeddingId: neon.id,\n collectionType: neon.collectionType,\n fieldName: neon.fieldName,\n } as any,\n });\n result.actions.created++;\n result.details.created.push(\n `${neon.strapiId} (${neon.title || \"untitled\"})`\n );\n } catch (error) {\n result.errors.push(\n `Failed to create Strapi entry for ${neon.strapiId}: ${error}`\n );\n }\n } else {\n result.actions.created++;\n result.details.created.push(\n `[DRY RUN] ${neon.strapiId} (${neon.title || \"untitled\"})`\n );\n }\n } else {\n // Check if content needs updating\n const contentChanged = existingStrapi.content !== neon.content;\n const titleChanged = existingStrapi.title !== neon.title;\n const embeddingIdMissing = !existingStrapi.embeddingId;\n\n if (contentChanged || titleChanged || embeddingIdMissing) {\n if (!dryRun) {\n try {\n await strapi.documents(CONTENT_TYPE_UID).update({\n documentId: neon.strapiId,\n data: {\n title: neon.title,\n content: neon.content,\n embeddingId: neon.id,\n } as any,\n });\n result.actions.updated++;\n result.details.updated.push(\n `${neon.strapiId} (${neon.title || \"untitled\"})`\n );\n } catch (error) {\n result.errors.push(\n `Failed to update Strapi entry ${neon.strapiId}: ${error}`\n );\n }\n } else {\n result.actions.updated++;\n result.details.updated.push(\n `[DRY RUN] ${neon.strapiId} (${neon.title || \"untitled\"})`\n );\n }\n }\n }\n }\n\n // Step 4: Handle orphaned Strapi entries (exist in Strapi but not in Neon)\n if (removeOrphans) {\n for (const strapiEmbed of strapiEmbeddings) {\n const hasNeonRecord = neonBystrapiId.has(strapiEmbed.documentId);\n\n if (!hasNeonRecord) {\n if (!dryRun) {\n try {\n await strapi.documents(CONTENT_TYPE_UID).delete({\n documentId: strapiEmbed.documentId,\n });\n result.actions.orphansRemoved++;\n result.details.orphansRemoved.push(\n `${strapiEmbed.documentId} (${strapiEmbed.title || \"untitled\"})`\n );\n } catch (error) {\n result.errors.push(\n `Failed to remove orphan ${strapiEmbed.documentId}: ${error}`\n );\n }\n } else {\n result.actions.orphansRemoved++;\n result.details.orphansRemoved.push(\n `[DRY RUN] ${strapiEmbed.documentId} (${strapiEmbed.title || \"untitled\"})`\n );\n }\n }\n }\n }\n\n result.success = result.errors.length === 0;\n return result;\n } catch (error) {\n result.errors.push(`Sync failed: ${error}`);\n return result;\n }\n },\n\n /**\n * Get sync status - compare Neon and Strapi without making changes\n */\n async getSyncStatus(): Promise<{\n neonCount: number;\n strapiCount: number;\n inSync: boolean;\n missingInStrapi: number;\n missingInNeon: number;\n contentDifferences: number;\n }> {\n if (!pluginManager.isInitialized()) {\n throw new Error(\"Plugin manager not initialized\");\n }\n\n const neonEmbeddings = await pluginManager.getAllNeonEmbeddings();\n const strapiEmbeddings = (await strapi\n .documents(CONTENT_TYPE_UID)\n .findMany({\n limit: 10000,\n })) as unknown as StrapiEmbedding[];\n\n const neonBystrapiId = new Map<string, NeonEmbedding>();\n for (const neon of neonEmbeddings) {\n if (neon.strapiId) {\n neonBystrapiId.set(neon.strapiId, neon);\n }\n }\n\n const strapiByDocumentId = new Map<string, StrapiEmbedding>();\n for (const s of strapiEmbeddings) {\n strapiByDocumentId.set(s.documentId, s);\n }\n\n let missingInStrapi = 0;\n let contentDifferences = 0;\n\n for (const neon of neonEmbeddings) {\n if (!neon.strapiId) continue;\n const strapiRecord = strapiByDocumentId.get(neon.strapiId);\n if (!strapiRecord) {\n missingInStrapi++;\n } else if (strapiRecord.content !== neon.content) {\n contentDifferences++;\n }\n }\n\n let missingInNeon = 0;\n for (const s of strapiEmbeddings) {\n if (!neonBystrapiId.has(s.documentId)) {\n missingInNeon++;\n }\n }\n\n return {\n neonCount: neonEmbeddings.length,\n strapiCount: strapiEmbeddings.length,\n inSync:\n missingInStrapi === 0 &&\n missingInNeon === 0 &&\n contentDifferences === 0,\n missingInStrapi,\n missingInNeon,\n contentDifferences,\n };\n },\n});\n\nexport default sync;\n","import embeddings from \"./embeddings\";\nimport sync from \"./sync\";\n\nexport default {\n embeddings,\n sync,\n};\n","/**\n * Application methods\n */\nimport bootstrap from './bootstrap';\nimport destroy from './destroy';\nimport register from './register';\n\n/**\n * Plugin server methods\n */\nimport config from './config';\nimport contentTypes from './content-types';\nimport controllers from './controllers';\nimport middlewares from './middlewares';\nimport policies from './policies';\nimport routes from './routes';\nimport services from './services';\n\nexport default {\n register,\n bootstrap,\n destroy,\n config,\n controllers,\n routes,\n services,\n contentTypes,\n policies,\n middlewares,\n};\n"],"names":["config","schema","pluginManager","index","embeddings","embedding","PLUGIN_ID","mcp","options","CONTENT_TYPE_UID","strapi"],"mappings":"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"}
@@ -14,12 +14,21 @@ export interface PluginConfigSchema {
14
14
  openAIApiKey?: string;
15
15
  neonConnectionString?: string;
16
16
  embeddingModel?: EmbeddingModelName;
17
+ /** Maximum characters per chunk (default: 4000, roughly ~1000 tokens) */
18
+ chunkSize?: number;
19
+ /** Number of characters to overlap between chunks (default: 200) */
20
+ chunkOverlap?: number;
21
+ /** Automatically chunk content that exceeds chunkSize (default: false) */
22
+ autoChunk?: boolean;
17
23
  }
18
24
  declare const _default: {
19
25
  default: {
20
26
  openAIApiKey: string;
21
27
  neonConnectionString: string;
22
28
  embeddingModel: "text-embedding-3-small" | "text-embedding-3-large" | "text-embedding-ada-002";
29
+ chunkSize: number;
30
+ chunkOverlap: number;
31
+ autoChunk: boolean;
23
32
  };
24
33
  validator(config: PluginConfigSchema): void;
25
34
  };
@@ -8,5 +8,19 @@ declare const controller: ({ strapi }: {
8
8
  getEmbeddings(ctx: any): Promise<void>;
9
9
  getEmbedding(ctx: any): Promise<void>;
10
10
  queryEmbeddings(ctx: any): Promise<void>;
11
+ /**
12
+ * Sync embeddings from Neon DB to Strapi DB
13
+ * GET /api/strapi-content-embeddings/sync
14
+ *
15
+ * Query params:
16
+ * - removeOrphans: boolean (default: false) - Remove Strapi entries that don't exist in Neon
17
+ * - dryRun: boolean (default: false) - Preview changes without applying them
18
+ */
19
+ syncFromNeon(ctx: any): Promise<void>;
20
+ /**
21
+ * Get sync status - compare Neon and Strapi without making changes
22
+ * GET /api/strapi-content-embeddings/sync/status
23
+ */
24
+ getSyncStatus(ctx: any): Promise<void>;
11
25
  };
12
26
  export default controller;
@@ -8,6 +8,8 @@ declare const _default: {
8
8
  getEmbeddings(ctx: any): Promise<void>;
9
9
  getEmbedding(ctx: any): Promise<void>;
10
10
  queryEmbeddings(ctx: any): Promise<void>;
11
+ syncFromNeon(ctx: any): Promise<void>;
12
+ getSyncStatus(ctx: any): Promise<void>;
11
13
  };
12
14
  mcp: ({ strapi }: {
13
15
  strapi: import("@strapi/types/dist/core").Strapi;
@@ -13,6 +13,9 @@ declare const _default: {
13
13
  openAIApiKey: string;
14
14
  neonConnectionString: string;
15
15
  embeddingModel: "text-embedding-3-small" | "text-embedding-3-large" | "text-embedding-ada-002";
16
+ chunkSize: number;
17
+ chunkOverlap: number;
18
+ autoChunk: boolean;
16
19
  };
17
20
  validator(config: import("./config").PluginConfigSchema): void;
18
21
  };
@@ -26,6 +29,8 @@ declare const _default: {
26
29
  getEmbeddings(ctx: any): Promise<void>;
27
30
  getEmbedding(ctx: any): Promise<void>;
28
31
  queryEmbeddings(ctx: any): Promise<void>;
32
+ syncFromNeon(ctx: any): Promise<void>;
33
+ getSyncStatus(ctx: any): Promise<void>;
29
34
  };
30
35
  mcp: ({ strapi }: {
31
36
  strapi: import("@strapi/types/dist/core").Strapi;
@@ -41,6 +46,15 @@ declare const _default: {
41
46
  path: string;
42
47
  handler: string;
43
48
  config?: undefined;
49
+ } | {
50
+ method: string;
51
+ path: string;
52
+ handler: string;
53
+ config: {
54
+ description: string;
55
+ auth?: undefined;
56
+ policies?: undefined;
57
+ };
44
58
  } | {
45
59
  method: string;
46
60
  path: string;
@@ -48,6 +62,7 @@ declare const _default: {
48
62
  config: {
49
63
  auth: boolean;
50
64
  policies: any[];
65
+ description?: undefined;
51
66
  };
52
67
  })[];
53
68
  };
@@ -72,12 +87,17 @@ declare const _default: {
72
87
  embeddings: ({ strapi }: {
73
88
  strapi: import("@strapi/types/dist/core").Strapi;
74
89
  }) => {
75
- createEmbedding(data: import("./services/embeddings").CreateEmbeddingData): Promise<import("@strapi/types/dist/modules/documents").AnyDocument>;
90
+ getConfig(): import("./config").PluginConfigSchema;
91
+ createEmbedding(data: import("./services/embeddings").CreateEmbeddingData): Promise<any>;
92
+ createChunkedEmbedding(data: import("./services/embeddings").CreateEmbeddingData): Promise<import("./services/embeddings").ChunkedEmbeddingResult>;
76
93
  deleteEmbedding(id: string | number): Promise<{
77
94
  documentId: string;
78
95
  entries: import("@strapi/types/dist/modules/documents").AnyDocument[];
79
96
  }>;
80
- updateEmbedding(id: string, data: import("./services/embeddings").UpdateEmbeddingData): Promise<import("@strapi/types/dist/modules/documents").AnyDocument>;
97
+ findRelatedChunks(documentId: string): Promise<any[]>;
98
+ deleteRelatedChunks(documentId: string): Promise<number>;
99
+ updateChunkedEmbedding(id: string, data: import("./services/embeddings").UpdateEmbeddingData): Promise<import("./services/embeddings").ChunkedEmbeddingResult>;
100
+ updateEmbedding(id: string, data: import("./services/embeddings").UpdateEmbeddingData): Promise<any>;
81
101
  queryEmbeddings(query: string): Promise<import("./plugin-manager").QueryResponse | {
82
102
  error: string;
83
103
  }>;
@@ -92,6 +112,22 @@ declare const _default: {
92
112
  totalCount: number;
93
113
  }>;
94
114
  };
115
+ sync: ({ strapi }: {
116
+ strapi: import("@strapi/types/dist/core").Strapi;
117
+ }) => {
118
+ syncFromNeon(options?: {
119
+ removeOrphans?: boolean;
120
+ dryRun?: boolean;
121
+ }): Promise<import("./services/sync").SyncResult>;
122
+ getSyncStatus(): Promise<{
123
+ neonCount: number;
124
+ strapiCount: number;
125
+ inSync: boolean;
126
+ missingInStrapi: number;
127
+ missingInNeon: number;
128
+ contentDifferences: number;
129
+ }>;
130
+ };
95
131
  };
96
132
  contentTypes: {
97
133
  embedding: {
@@ -2,6 +2,7 @@
2
2
  * Create Embedding Tool
3
3
  *
4
4
  * Creates a new embedding from text content.
5
+ * Supports automatic chunking for large content.
5
6
  */
6
7
  import type { Core } from '@strapi/strapi';
7
8
  export declare const createEmbeddingTool: {
@@ -22,6 +23,10 @@ export declare const createEmbeddingTool: {
22
23
  type: string;
23
24
  description: string;
24
25
  };
26
+ autoChunk: {
27
+ type: string;
28
+ description: string;
29
+ };
25
30
  };
26
31
  required: string[];
27
32
  };
@@ -30,6 +35,7 @@ export declare function handleCreateEmbedding(strapi: Core.Strapi, args: {
30
35
  title: string;
31
36
  content: string;
32
37
  metadata?: Record<string, any>;
38
+ autoChunk?: boolean;
33
39
  }): Promise<{
34
40
  content: {
35
41
  type: string;
@@ -99,6 +99,10 @@ export declare const tools: ({
99
99
  type: string;
100
100
  description: string;
101
101
  };
102
+ autoChunk: {
103
+ type: string;
104
+ description: string;
105
+ };
102
106
  };
103
107
  required: string[];
104
108
  };
@@ -39,6 +39,22 @@ declare class PluginManager {
39
39
  queryEmbedding(query: string): Promise<QueryResponse>;
40
40
  similaritySearch(query: string, k?: number): Promise<Document[]>;
41
41
  isInitialized(): boolean;
42
+ /**
43
+ * Get all embeddings from Neon DB
44
+ * Returns the metadata (including Strapi documentId) for each embedding
45
+ */
46
+ getAllNeonEmbeddings(): Promise<Array<{
47
+ id: string;
48
+ strapiId: string;
49
+ title: string;
50
+ content: string;
51
+ collectionType: string;
52
+ fieldName: string;
53
+ }>>;
54
+ /**
55
+ * Delete an embedding from Neon by its Neon UUID (not Strapi ID)
56
+ */
57
+ deleteNeonEmbeddingById(neonId: string): Promise<void>;
42
58
  destroy(): Promise<void>;
43
59
  }
44
60
  export declare const pluginManager: PluginManager;
@@ -3,6 +3,15 @@ declare const _default: ({
3
3
  path: string;
4
4
  handler: string;
5
5
  config?: undefined;
6
+ } | {
7
+ method: string;
8
+ path: string;
9
+ handler: string;
10
+ config: {
11
+ description: string;
12
+ auth?: undefined;
13
+ policies?: undefined;
14
+ };
6
15
  } | {
7
16
  method: string;
8
17
  path: string;
@@ -10,6 +19,7 @@ declare const _default: ({
10
19
  config: {
11
20
  auth: boolean;
12
21
  policies: any[];
22
+ description?: undefined;
13
23
  };
14
24
  })[];
15
25
  export default _default;
@@ -6,6 +6,15 @@ declare const _default: {
6
6
  path: string;
7
7
  handler: string;
8
8
  config?: undefined;
9
+ } | {
10
+ method: string;
11
+ path: string;
12
+ handler: string;
13
+ config: {
14
+ description: string;
15
+ auth?: undefined;
16
+ policies?: undefined;
17
+ };
9
18
  } | {
10
19
  method: string;
11
20
  path: string;
@@ -13,6 +22,7 @@ declare const _default: {
13
22
  config: {
14
23
  auth: boolean;
15
24
  policies: any[];
25
+ description?: undefined;
16
26
  };
17
27
  })[];
18
28
  };