Compare commits

...

12 Commits

Author SHA1 Message Date
2a9c5780c3 fix: gitea mcp issues
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 4s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m41s
Monorepo Pipeline / 🧹 Lint (push) Successful in 5m17s
Monorepo Pipeline / 🏗️ Build (push) Successful in 4m16s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
🏥 Server Maintenance / 🧹 Prune & Clean (push) Failing after 5s
2026-03-12 13:33:10 +01:00
fbfc9feab0 fix(next-config): add serverActions.allowedOrigins to support branch deployments
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 25s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m28s
Monorepo Pipeline / 🧹 Lint (push) Successful in 4m22s
Monorepo Pipeline / 🏗️ Build (push) Successful in 4m3s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
🏥 Server Maintenance / 🧹 Prune & Clean (push) Failing after 4s
2026-03-10 23:29:12 +01:00
8486261555 fix(mcp): refactor all mcp servers to use multi-session sse transport
All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m1s
Monorepo Pipeline / 🏗️ Build (push) Successful in 2m44s
Monorepo Pipeline / 🧹 Lint (push) Successful in 2m55s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-03-10 13:32:16 +01:00
5e1f2669e6 feat(kabelfachmann-mcp): add local Ollama support for KABELFACHMANN_LLM_PROVIDER
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m4s
Monorepo Pipeline / 🏗️ Build (push) Successful in 2m53s
Monorepo Pipeline / 🧹 Lint (push) Successful in 3m2s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
🏥 Server Maintenance / 🧹 Prune & Clean (push) Failing after 4s
2026-03-08 14:01:37 +01:00
541f1c17b7 feat(mcps): add kabelfachmann MCP with Kabelhandbuch integration and remove legacy PM2 orchestration
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m6s
Monorepo Pipeline / 🏗️ Build (push) Successful in 2m52s
Monorepo Pipeline / 🧹 Lint (push) Successful in 3m1s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
🏥 Server Maintenance / 🧹 Prune & Clean (push) Failing after 4s
2026-03-08 01:01:43 +01:00
048fafa3db fix(payload-ai): remove phantom SCSS import and disable dynamic provider injection
All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m3s
Monorepo Pipeline / 🏗️ Build (push) Successful in 3m34s
Monorepo Pipeline / 🧹 Lint (push) Successful in 3m56s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
- Remove non-existent ChatWindow.scss import causing Webpack resolution errors
- Disable dynamic ChatWindowProvider injection (now statically declared in host app)
- Revert build script to tsc (no SCSS copy needed)
2026-03-07 11:46:41 +01:00
77e2c4f9b6 ci: remove unused zod dependencies across mcp servers to satisfy depcheck validation
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m21s
Monorepo Pipeline / 🏗️ Build (push) Successful in 3m48s
Monorepo Pipeline / 🧹 Lint (push) Successful in 3m59s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
🏥 Server Maintenance / 🧹 Prune & Clean (push) Failing after 5s
2026-03-06 17:56:14 +01:00
4eb1aaf640 ci: fix strict TS overloaded parameter matching inside useChat and payload tool bindings
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m14s
Monorepo Pipeline / 🧹 Lint (push) Failing after 2m4s
Monorepo Pipeline / 🏗️ Build (push) Successful in 2m39s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-03-06 16:10:50 +01:00
61f2f83e0c ci: fix unhandled typescript exceptions and strict eslint errors caught by the pipeline
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧹 Lint (push) Failing after 9s
Monorepo Pipeline / 🏗️ Build (push) Failing after 9s
Monorepo Pipeline / 🧪 Test (push) Failing after 9s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-03-06 15:49:45 +01:00
2dc61e4937 chore: finalize containerized MCP setup and fix memory-mcp native module issues
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧹 Lint (push) Failing after 9s
Monorepo Pipeline / 🧪 Test (push) Failing after 9s
Monorepo Pipeline / 🏗️ Build (push) Failing after 8s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-03-06 15:20:52 +01:00
a6ca876823 chore: release v1.9.17
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧹 Lint (push) Failing after 11s
Monorepo Pipeline / 🧪 Test (push) Failing after 9s
Monorepo Pipeline / 🏗️ Build (push) Failing after 9s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
🏥 Server Maintenance / 🧹 Prune & Clean (push) Failing after 4s
2026-03-06 01:32:12 +01:00
f615565323 fix(glitchtip-mcp): remove unused zod dependency blocking CI release 2026-03-06 01:32:11 +01:00
67 changed files with 2684 additions and 1583 deletions

View File

@@ -0,0 +1,5 @@
---
"@mintel/next-config": patch
---
fix: add serverActions.allowedOrigins to support branch deployments

6
.env
View File

@@ -3,6 +3,7 @@ IMAGE_TAG=v1.8.19
PROJECT_NAME=at-mintel PROJECT_NAME=at-mintel
PROJECT_COLOR=#82ed20 PROJECT_COLOR=#82ed20
GITEA_TOKEN=ccce002e30fe16a31a6c9d5a414740af2f72a582 GITEA_TOKEN=ccce002e30fe16a31a6c9d5a414740af2f72a582
GITEA_HOST=https://git.infra.mintel.me
OPENROUTER_API_KEY=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 OPENROUTER_API_KEY=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
ZYTE_API_KEY=1f0f74726f044f55aaafc7ead32cd489 ZYTE_API_KEY=1f0f74726f044f55aaafc7ead32cd489
REPLICATE_API_KEY=r8_W3grtpXMRfi0u3AM9VdkKbuWdZMmhwU2Tn0yt REPLICATE_API_KEY=r8_W3grtpXMRfi0u3AM9VdkKbuWdZMmhwU2Tn0yt
@@ -11,6 +12,11 @@ DATA_FOR_SEO_API_KEY=bWFyY0BtaW50ZWwubWU6MjQ0YjBjZmIzOGY3NTIzZA==
DATA_FOR_SEO_LOGIN=marc@mintel.me DATA_FOR_SEO_LOGIN=marc@mintel.me
DATA_FOR_SEO_PASSWORD=244b0cfb38f7523d DATA_FOR_SEO_PASSWORD=244b0cfb38f7523d
# Kabelfachmann LLM Configuration
KABELFACHMANN_LLM_PROVIDER=openrouter
KABELFACHMANN_OLLAMA_MODEL=qwen3.5
KABELFACHMANN_OLLAMA_HOST=http://host.docker.internal:11434
# Authentication # Authentication
GATEKEEPER_PASSWORD=mintel GATEKEEPER_PASSWORD=mintel
AUTH_COOKIE_NAME=mintel_gatekeeper_session AUTH_COOKIE_NAME=mintel_gatekeeper_session

View File

@@ -1,5 +1,5 @@
# Project # Project
IMAGE_TAG=v1.9.16 IMAGE_TAG=v1.9.17
PROJECT_NAME=sample-website PROJECT_NAME=sample-website
PROJECT_COLOR=#82ed20 PROJECT_COLOR=#82ed20

6
.gitignore vendored
View File

@@ -50,4 +50,8 @@ apps/web/out/estimations/
# Memory MCP # Memory MCP
data/qdrant/ data/qdrant/
packages/memory-mcp/models/ packages/memory-mcp/models/
# Kabelfachmann MCP
packages/kabelfachmann-mcp/data/
packages/kabelfachmann-mcp/models/

View File

@@ -1,6 +1,6 @@
{ {
"name": "sample-website", "name": "sample-website",
"version": "1.9.16", "version": "1.9.17",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@@ -3,14 +3,88 @@ services:
image: qdrant/qdrant:latest image: qdrant/qdrant:latest
container_name: qdrant-mcp container_name: qdrant-mcp
ports: ports:
- "6333:6333" - "6335:6333"
- "6334:6334" - "6336:6334"
volumes: volumes:
- ./data/qdrant:/qdrant/storage - ./data/qdrant:/qdrant/storage
restart: unless-stopped restart: unless-stopped
networks: networks:
- mcp-network - mcp-network
gitea-mcp:
build:
context: ./packages/gitea-mcp
container_name: gitea-mcp
env_file:
- .env
ports:
- "3001:3001"
restart: unless-stopped
networks:
- mcp-network
memory-mcp:
build:
context: ./packages/memory-mcp
container_name: memory-mcp
env_file:
- .env
ports:
- "3002:3002"
depends_on:
- qdrant
restart: unless-stopped
networks:
- mcp-network
umami-mcp:
build:
context: ./packages/umami-mcp
container_name: umami-mcp
env_file:
- .env
ports:
- "3003:3003"
restart: unless-stopped
networks:
- mcp-network
serpbear-mcp:
build:
context: ./packages/serpbear-mcp
container_name: serpbear-mcp
env_file:
- .env
ports:
- "3004:3004"
restart: unless-stopped
networks:
- mcp-network
glitchtip-mcp:
build:
context: ./packages/glitchtip-mcp
container_name: glitchtip-mcp
env_file:
- .env
ports:
- "3005:3005"
restart: unless-stopped
networks:
- mcp-network
klz-payload-mcp:
build:
context: ./packages/klz-payload-mcp
container_name: klz-payload-mcp
env_file:
- .env
ports:
- "3006:3006"
restart: unless-stopped
networks:
- mcp-network
networks: networks:
mcp-network: mcp-network:
driver: bridge driver: bridge

View File

@@ -1,48 +0,0 @@
module.exports = {
apps: [
{
name: 'gitea-mcp',
script: 'node',
args: 'dist/start.js',
cwd: './packages/gitea-mcp',
watch: false,
},
{
name: 'memory-mcp',
script: 'node',
args: 'dist/start.js',
cwd: './packages/memory-mcp',
watch: false,
},
{
name: 'umami-mcp',
script: 'node',
args: 'dist/start.js',
cwd: './packages/umami-mcp',
watch: false,
},
{
name: 'serpbear-mcp',
script: 'node',
args: 'dist/start.js',
cwd: './packages/serpbear-mcp',
watch: false,
},
{
name: 'glitchtip-mcp',
script: 'node',
args: 'dist/start.js',
cwd: './packages/glitchtip-mcp',
watch: false,
},
{
name: 'klz-payload-mcp',
script: 'node',
args: 'dist/start.js',
cwd: './packages/klz-payload-mcp',
watch: false,
},
]
};

69
eslint-errors-2.txt Normal file
View File

@@ -0,0 +1,69 @@

/Users/marcmintel/Projects/at-mintel/packages/gitea-mcp/src/index.ts
 11:0 error Parsing error: Identifier expected

/Users/marcmintel/Projects/at-mintel/packages/glitchtip-mcp/src/index.ts
 124:19 warning 'res' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/klz-payload-mcp/src/index.ts
 39:18 warning 'e' is defined but never used. Allowed unused caught errors must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/memory-mcp/src/qdrant.test.ts
 7:52 warning 'text' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/page-audit/src/report.ts
 7:47 warning 'PageAuditData' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 7:62 warning 'AuditIssue' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/components/FieldGenerators/AiFieldButton.tsx
 11:13 warning 'value' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/components/FieldGenerators/GenerateSlugButton.tsx
 20:21 warning 'replaceState' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 21:13 warning 'value' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/components/FieldGenerators/GenerateThumbnailButton.tsx
 21:13 warning 'value' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/components/OptimizeButton.tsx
 5:10 warning 'Button' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/tools/mcpAdapter.ts
 44:15 warning 'toolSchema' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/tools/memoryDb.ts
 89:31 warning 'query' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/tools/payloadLocal.ts
 3:40 warning 'User' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/types.ts
 1:15 warning 'Plugin' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/pdf-library/src/components/ConceptPDF.tsx
 4:18 warning 'PDFPage' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 5:10 warning 'pdfStyles' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/pdf-library/src/components/EstimationPDF.tsx
 4:18 warning 'PDFPage' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 5:10 warning 'pdfStyles' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 54:11 warning 'getPageNum' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/pdf-library/src/components/InfoPDF.tsx
 5:13 warning 'PDFPage' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 12:5 warning 'pdfStyles' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/pdf-library/src/components/pdf/SharedUI.tsx
 528:5 warning 'bankData' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/pdf-library/src/components/pdf/SimpleLayout.tsx
 4:52 warning 'PDFText' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 5:26 warning 'pdfStyles' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/seo-engine/src/report.ts
 5:3 warning 'TopicCluster' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 6:3 warning 'ContentGap' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 7:3 warning 'CompetitorRanking' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

✖ 28 problems (1 error, 27 warnings)


97
eslint-errors.txt Normal file
View File

@@ -0,0 +1,97 @@

/Users/marcmintel/Projects/at-mintel/packages/gitea-mcp/src/index.ts
 12:5 warning 'Resource' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 14:10 warning 'z' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 427:30 warning 'e' is defined but never used. Allowed unused caught errors must match /^_/u @typescript-eslint/no-unused-vars
 745:50 error Unnecessary escape character: \/ no-useless-escape
 745:60 error Unnecessary escape character: \/ no-useless-escape
 799:54 error Unnecessary escape character: \/ no-useless-escape
 799:64 error Unnecessary escape character: \/ no-useless-escape

/Users/marcmintel/Projects/at-mintel/packages/glitchtip-mcp/src/index.ts
 124:19 warning 'res' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/klz-payload-mcp/src/index.ts
 39:18 warning 'e' is defined but never used. Allowed unused caught errors must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/memory-mcp/src/qdrant.test.ts
 7:52 warning 'text' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/page-audit/src/report.ts
 7:47 warning 'PageAuditData' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 7:62 warning 'AuditIssue' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/chatPlugin.ts
 1:15 warning 'Config' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 10:17 error 'config' is never reassigned. Use 'const' instead prefer-const
 48:37 warning 'req' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/components/ChatWindow/index.tsx
 43:5 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment
 44:63 warning 'setMessages' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/components/FieldGenerators/AiFieldButton.tsx
 11:13 warning 'value' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/components/FieldGenerators/GenerateSlugButton.tsx
 20:21 warning 'replaceState' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 21:13 warning 'value' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/components/FieldGenerators/GenerateThumbnailButton.tsx
 21:13 warning 'value' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/components/OptimizeButton.tsx
 5:10 warning 'Button' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/endpoints/chatEndpoint.ts
 96:13 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment
 100:13 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/tools/mcpAdapter.ts
 44:15 warning 'toolSchema' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 53:13 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/tools/memoryDb.ts
 50:13 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment
 88:13 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment
 89:31 warning 'query' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/tools/payloadLocal.ts
 3:40 warning 'User' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 25:13 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment
 45:13 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment
 61:13 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment
 78:13 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment
 95:13 error Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment

/Users/marcmintel/Projects/at-mintel/packages/payload-ai/src/types.ts
 1:15 warning 'Plugin' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/pdf-library/src/components/ConceptPDF.tsx
 4:18 warning 'PDFPage' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 5:10 warning 'pdfStyles' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/pdf-library/src/components/EstimationPDF.tsx
 4:18 warning 'PDFPage' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 5:10 warning 'pdfStyles' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 54:11 warning 'getPageNum' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/pdf-library/src/components/InfoPDF.tsx
 5:13 warning 'PDFPage' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 12:5 warning 'pdfStyles' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/pdf-library/src/components/pdf/SharedUI.tsx
 528:5 warning 'bankData' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/pdf-library/src/components/pdf/SimpleLayout.tsx
 4:52 warning 'PDFText' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 5:26 warning 'pdfStyles' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

/Users/marcmintel/Projects/at-mintel/packages/seo-engine/src/report.ts
 5:3 warning 'TopicCluster' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 6:3 warning 'ContentGap' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
 7:3 warning 'CompetitorRanking' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars

✖ 49 problems (16 errors, 33 warnings)
 1 error and 0 warnings potentially fixable with the `--fix` option.


View File

@@ -6,12 +6,12 @@
"build": "pnpm -r build", "build": "pnpm -r build",
"dev": "pnpm -r dev", "dev": "pnpm -r dev",
"dev:gatekeeper": "bash -c 'trap \"COMPOSE_PROJECT_NAME=gatekeeper docker-compose -f docker-compose.gatekeeper.yml down\" EXIT INT TERM; docker network create infra 2>/dev/null || true && COMPOSE_PROJECT_NAME=gatekeeper docker-compose -f docker-compose.gatekeeper.yml down && COMPOSE_PROJECT_NAME=gatekeeper docker-compose -f docker-compose.gatekeeper.yml up --build --remove-orphans'", "dev:gatekeeper": "bash -c 'trap \"COMPOSE_PROJECT_NAME=gatekeeper docker-compose -f docker-compose.gatekeeper.yml down\" EXIT INT TERM; docker network create infra 2>/dev/null || true && COMPOSE_PROJECT_NAME=gatekeeper docker-compose -f docker-compose.gatekeeper.yml down && COMPOSE_PROJECT_NAME=gatekeeper docker-compose -f docker-compose.gatekeeper.yml up --build --remove-orphans'",
"dev:mcps:up": "docker-compose -f docker-compose.mcps.yml up -d", "dev:mcps:up": "docker-compose -f docker-compose.mcps.yml up -d --build --remove-orphans",
"dev:mcps:down": "docker-compose -f docker-compose.mcps.yml down && pm2 delete ecosystem.mcps.config.cjs || true", "dev:mcps:down": "docker-compose -f docker-compose.mcps.yml down",
"dev:mcps:watch": "pnpm -r --filter=\"./packages/*-mcp\" exec tsc -w", "dev:mcps:watch": "pnpm -r --filter=\"./packages/*-mcp\" exec tsc -w",
"dev:mcps": "npm run dev:mcps:up && pm2 start ecosystem.mcps.config.cjs --watch && npm run dev:mcps:watch", "dev:mcps": "npm run dev:mcps:up && npm run dev:mcps:watch",
"start:mcps:run": "pm2 start ecosystem.mcps.config.cjs", "start:mcps": "npm run dev:mcps:up",
"start:mcps": "npm run dev:mcps:up && npm run start:mcps:run", "start:mcps:force": "docker-compose -f docker-compose.mcps.yml up -d --build --force-recreate --remove-orphans",
"lint": "pnpm -r --filter='./packages/**' --filter='./apps/**' lint", "lint": "pnpm -r --filter='./packages/**' --filter='./apps/**' lint",
"test": "pnpm -r test", "test": "pnpm -r test",
"changeset": "changeset", "changeset": "changeset",
@@ -56,7 +56,7 @@
"pino-pretty": "^13.1.3", "pino-pretty": "^13.1.3",
"require-in-the-middle": "^8.0.1" "require-in-the-middle": "^8.0.1"
}, },
"version": "1.9.16", "version": "1.9.17",
"pnpm": { "pnpm": {
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [
"@parcel/watcher", "@parcel/watcher",
@@ -75,4 +75,4 @@
"@sentry/nextjs": "10.38.0" "@sentry/nextjs": "10.38.0"
} }
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/cli", "name": "@mintel/cli",
"version": "1.9.16", "version": "1.9.17",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://git.infra.mintel.me/api/packages/mmintel/npm" "registry": "https://git.infra.mintel.me/api/packages/mmintel/npm"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/cloner", "name": "@mintel/cloner",
"version": "1.9.16", "version": "1.9.17",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"module": "dist/index.js", "module": "dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/concept-engine", "name": "@mintel/concept-engine",
"version": "1.9.16", "version": "1.9.17",
"description": "AI-powered web project concept generation and analysis", "description": "AI-powered web project concept generation and analysis",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/content-engine", "name": "@mintel/content-engine",
"version": "1.9.16", "version": "1.9.17",
"private": false, "private": false,
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/eslint-config", "name": "@mintel/eslint-config",
"version": "1.9.16", "version": "1.9.17",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://git.infra.mintel.me/api/packages/mmintel/npm" "registry": "https://git.infra.mintel.me/api/packages/mmintel/npm"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/estimation-engine", "name": "@mintel/estimation-engine",
"version": "1.9.16", "version": "1.9.17",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/gatekeeper", "name": "@mintel/gatekeeper",
"version": "1.9.16", "version": "1.9.17",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",

View File

@@ -15,5 +15,5 @@ COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist COPY --from=builder /app/dist ./dist
# Use node to run the compiled index.js # Use node to run the compiled start.js
ENTRYPOINT ["node", "dist/index.js"] ENTRYPOINT ["node", "dist/start.js"]

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/gitea-mcp", "name": "@mintel/gitea-mcp",
"version": "1.9.16", "version": "1.9.17",
"description": "Native Gitea MCP server for 100% Antigravity compatibility", "description": "Native Gitea MCP server for 100% Antigravity compatibility",
"main": "dist/index.js", "main": "dist/index.js",
"type": "module", "type": "module",
@@ -12,8 +12,7 @@
"@modelcontextprotocol/sdk": "^1.5.0", "@modelcontextprotocol/sdk": "^1.5.0",
"axios": "^1.7.2", "axios": "^1.7.2",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"express": "^5.2.1", "express": "^5.2.1"
"zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^5.0.6", "@types/express": "^5.0.6",

File diff suppressed because it is too large Load Diff

View File

@@ -5,9 +5,9 @@ import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url)); const __dirname = fileURLToPath(new URL('.', import.meta.url));
// Try to load .env.local first (contains credentials usually) // Try to load .env.local first (contains credentials usually)
config({ path: resolve(__dirname, '../../../.env.local') }); config({ quiet: true, path: resolve(__dirname, '../../../.env.local') });
// Fallback to .env (contains defaults) // Fallback to .env (contains defaults)
config({ path: resolve(__dirname, '../../../.env') }); config({ quiet: true, path: resolve(__dirname, '../../../.env') });
// Now boot the compiled MCP index // Now boot the compiled MCP index
import('./index.js').catch(err => { import('./index.js').catch(err => {

View File

@@ -0,0 +1,15 @@
FROM node:20-bookworm-slim AS builder
WORKDIR /app
COPY package.json ./
RUN corepack enable pnpm && pnpm install --ignore-workspace
COPY tsconfig.json ./
COPY src ./src
RUN pnpm build
FROM node:20-bookworm-slim
WORKDIR /app
COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
ENTRYPOINT ["node", "dist/start.js"]

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/glitchtip-mcp", "name": "@mintel/glitchtip-mcp",
"version": "1.9.16", "version": "1.9.17",
"description": "GlitchTip Error Tracking MCP server for Mintel infrastructure", "description": "GlitchTip Error Tracking MCP server for Mintel infrastructure",
"main": "dist/index.js", "main": "dist/index.js",
"type": "module", "type": "module",
@@ -13,13 +13,12 @@
"@modelcontextprotocol/sdk": "^1.5.0", "@modelcontextprotocol/sdk": "^1.5.0",
"axios": "^1.7.2", "axios": "^1.7.2",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"express": "^5.2.1", "express": "^5.2.1"
"zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/node": "^20.14.10", "@types/node": "^20.14.10",
"typescript": "^5.5.3", "tsx": "^4.19.2",
"tsx": "^4.19.2" "typescript": "^5.5.3"
} }
} }

View File

@@ -1,6 +1,7 @@
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from 'express'; import express, { Request, Response } from 'express';
import crypto from 'crypto';
import { import {
CallToolRequestSchema, CallToolRequestSchema,
ListToolsRequestSchema, ListToolsRequestSchema,
@@ -141,17 +142,34 @@ async function run() {
console.error('GlitchTip MCP server is running on stdio'); console.error('GlitchTip MCP server is running on stdio');
} else { } else {
const app = express(); const app = express();
let transport: SSEServerTransport | null = null; const transports = new Map<string, SSEServerTransport>();
app.use((req, _res, next) => {
console.error(`${req.method} ${req.url}`);
next();
});
app.get('/sse', async (req, res) => { app.get('/sse', async (req, res) => {
console.error('New SSE connection established'); const sessionId = crypto.randomUUID();
transport = new SSEServerTransport('/message', res); console.error(`New SSE connection: ${sessionId}`);
const transport = new SSEServerTransport(`/message/${sessionId}`, res);
transports.set(sessionId, transport);
req.on('close', () => {
console.error(`SSE connection closed: ${sessionId}`);
transports.delete(sessionId);
});
await server.connect(transport); await server.connect(transport);
}); });
app.post('/message', async (req, res) => { app.post('/message/:sessionId', async (req: Request, res: Response) => {
const sessionId = req.params.sessionId;
const transport = transports.get(sessionId as string);
if (!transport) { if (!transport) {
res.status(400).send('No active SSE connection'); console.error(`No transport found for session: ${sessionId}`);
res.status(400).send('No active SSE connection for this session');
return; return;
} }
await transport.handlePostMessage(req, res); await transport.handlePostMessage(req, res);

View File

@@ -4,8 +4,8 @@ import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url)); const __dirname = fileURLToPath(new URL('.', import.meta.url));
config({ path: resolve(__dirname, '../../../.env.local') }); config({ quiet: true, path: resolve(__dirname, '../../../.env.local') });
config({ path: resolve(__dirname, '../../../.env') }); config({ quiet: true, path: resolve(__dirname, '../../../.env') });
import('./index.js').catch(err => { import('./index.js').catch(err => {
console.error('Failed to start GlitchTip MCP Server:', err); console.error('Failed to start GlitchTip MCP Server:', err);

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/husky-config", "name": "@mintel/husky-config",
"version": "1.9.16", "version": "1.9.17",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://git.infra.mintel.me/api/packages/mmintel/npm" "registry": "https://git.infra.mintel.me/api/packages/mmintel/npm"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/infra", "name": "@mintel/infra",
"version": "1.9.16", "version": "1.9.17",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://git.infra.mintel.me/api/packages/mmintel/npm" "registry": "https://git.infra.mintel.me/api/packages/mmintel/npm"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/journaling", "name": "@mintel/journaling",
"version": "1.9.16", "version": "1.9.17",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",

View File

@@ -0,0 +1,15 @@
FROM node:20-bookworm-slim AS builder
WORKDIR /app
COPY package.json ./
RUN corepack enable pnpm && pnpm install --ignore-workspace
COPY tsconfig.json ./
COPY src ./src
RUN pnpm build
FROM node:20-bookworm-slim
WORKDIR /app
COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
ENTRYPOINT ["node", "dist/start.js"]

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/klz-payload-mcp", "name": "@mintel/klz-payload-mcp",
"version": "1.9.16", "version": "1.9.17",
"description": "KLZ PayloadCMS MCP server for technical product data and leads", "description": "KLZ PayloadCMS MCP server for technical product data and leads",
"main": "dist/index.js", "main": "dist/index.js",
"type": "module", "type": "module",
@@ -13,13 +13,12 @@
"@modelcontextprotocol/sdk": "^1.27.1", "@modelcontextprotocol/sdk": "^1.27.1",
"axios": "^1.7.2", "axios": "^1.7.2",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"express": "^5.2.1", "express": "^5.2.1"
"zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/node": "^20.14.10", "@types/node": "^20.14.10",
"typescript": "^5.5.3", "tsx": "^4.19.2",
"tsx": "^4.19.2" "typescript": "^5.5.3"
} }
} }

View File

@@ -7,6 +7,7 @@ import {
Tool, Tool,
} from "@modelcontextprotocol/sdk/types.js"; } from "@modelcontextprotocol/sdk/types.js";
import axios from "axios"; import axios from "axios";
import crypto from "crypto";
import https from "https"; import https from "https";
const PAYLOAD_URL = process.env.PAYLOAD_URL || "https://klz-cables.com"; const PAYLOAD_URL = process.env.PAYLOAD_URL || "https://klz-cables.com";
@@ -588,17 +589,34 @@ async function run() {
console.error('KLZ Payload MCP server is running on stdio'); console.error('KLZ Payload MCP server is running on stdio');
} else { } else {
const app = express(); const app = express();
let transport: SSEServerTransport | null = null; const transports = new Map<string, SSEServerTransport>();
app.use((req, _res, next) => {
console.error(`${req.method} ${req.url}`);
next();
});
app.get('/sse', async (req: Request, res: Response) => { app.get('/sse', async (req: Request, res: Response) => {
console.error('New SSE connection established'); const sessionId = crypto.randomUUID();
transport = new SSEServerTransport('/message', res); console.error(`New SSE connection: ${sessionId}`);
const transport = new SSEServerTransport(`/message/${sessionId}`, res);
transports.set(sessionId, transport);
req.on('close', () => {
console.error(`SSE connection closed: ${sessionId}`);
transports.delete(sessionId);
});
await server.connect(transport); await server.connect(transport);
}); });
app.post('/message', async (req: Request, res: Response) => { app.post('/message/:sessionId', async (req: Request, res: Response) => {
const sessionId = req.params.sessionId;
const transport = transports.get(sessionId as string);
if (!transport) { if (!transport) {
res.status(400).send('No active SSE connection'); console.error(`No transport found for session: ${sessionId}`);
res.status(400).send('No active SSE connection for this session');
return; return;
} }
await transport.handlePostMessage(req, res); await transport.handlePostMessage(req, res);

View File

@@ -4,8 +4,8 @@ import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url)); const __dirname = fileURLToPath(new URL('.', import.meta.url));
config({ path: resolve(__dirname, '../../../.env.local') }); config({ quiet: true, path: resolve(__dirname, '../../../.env.local') });
config({ path: resolve(__dirname, '../../../.env') }); config({ quiet: true, path: resolve(__dirname, '../../../.env') });
import('./index.js').catch(err => { import('./index.js').catch(err => {
console.error('Failed to start KLZ Payload MCP Server:', err); console.error('Failed to start KLZ Payload MCP Server:', err);

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/mail", "name": "@mintel/mail",
"version": "1.9.16", "version": "1.9.17",
"private": false, "private": false,
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/meme-generator", "name": "@mintel/meme-generator",
"version": "1.9.16", "version": "1.9.17",
"private": false, "private": false,
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -0,0 +1,18 @@
FROM node:20-bookworm-slim AS builder
WORKDIR /app
COPY package.json ./
RUN corepack enable pnpm && pnpm install --ignore-workspace
RUN for dir in $(find /app/node_modules -type d -name "sharp" | grep "node_modules/sharp$"); do \
echo "module.exports = {};" > "$dir/lib/index.js" || true; \
done
COPY tsconfig.json ./
COPY src ./src
RUN pnpm build
FROM node:20-bookworm-slim
WORKDIR /app
COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
ENTRYPOINT ["node", "dist/start.js"]

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/memory-mcp", "name": "@mintel/memory-mcp",
"version": "1.9.16", "version": "1.9.17",
"description": "Local Qdrant-based Memory MCP server", "description": "Local Qdrant-based Memory MCP server",
"main": "dist/index.js", "main": "dist/index.js",
"type": "module", "type": "module",
@@ -14,6 +14,7 @@
"@modelcontextprotocol/sdk": "^1.5.0", "@modelcontextprotocol/sdk": "^1.5.0",
"@qdrant/js-client-rest": "^1.12.0", "@qdrant/js-client-rest": "^1.12.0",
"@xenova/transformers": "^2.17.2", "@xenova/transformers": "^2.17.2",
"onnxruntime-node": "^1.14.0",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"express": "^5.2.1", "express": "^5.2.1",
"zod": "^3.23.8" "zod": "^3.23.8"
@@ -25,4 +26,4 @@
"typescript": "^5.5.3", "typescript": "^5.5.3",
"vitest": "^2.1.3" "vitest": "^2.1.3"
} }
} }

View File

@@ -1,6 +1,7 @@
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import express from 'express'; import express from 'express';
import crypto from 'crypto';
import { z } from 'zod'; import { z } from 'zod';
import { QdrantMemoryService } from './qdrant.js'; import { QdrantMemoryService } from './qdrant.js';
@@ -77,17 +78,34 @@ async function main() {
} }
} else { } else {
const app = express(); const app = express();
let transport: SSEServerTransport | null = null; const transports = new Map<string, SSEServerTransport>();
app.use((req, _res, next) => {
console.error(`${req.method} ${req.url}`);
next();
});
app.get('/sse', async (req, res) => { app.get('/sse', async (req, res) => {
console.error('New SSE connection established'); const sessionId = crypto.randomUUID();
transport = new SSEServerTransport('/message', res); console.error(`New SSE connection: ${sessionId}`);
const transport = new SSEServerTransport(`/message/${sessionId}`, res);
transports.set(sessionId, transport);
req.on('close', () => {
console.error(`SSE connection closed: ${sessionId}`);
transports.delete(sessionId);
});
await server.connect(transport); await server.connect(transport);
}); });
app.post('/message', async (req, res) => { app.post('/message/:sessionId', async (req, res) => {
const { sessionId } = req.params;
const transport = transports.get(sessionId as string);
if (!transport) { if (!transport) {
res.status(400).send('No active SSE connection'); console.error(`No transport found for session: ${sessionId}`);
res.status(400).send('No active SSE connection for this session');
return; return;
} }
await transport.handlePostMessage(req, res); await transport.handlePostMessage(req, res);

View File

@@ -5,9 +5,9 @@ import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url)); const __dirname = fileURLToPath(new URL('.', import.meta.url));
// Try to load .env.local first (contains credentials usually) // Try to load .env.local first (contains credentials usually)
config({ path: resolve(__dirname, '../../../.env.local') }); config({ quiet: true, path: resolve(__dirname, '../../../.env.local') });
// Fallback to .env (contains defaults) // Fallback to .env (contains defaults)
config({ path: resolve(__dirname, '../../../.env') }); config({ quiet: true, path: resolve(__dirname, '../../../.env') });
// Now boot the compiled MCP index // Now boot the compiled MCP index
import('./index.js').catch(err => { import('./index.js').catch(err => {

View File

@@ -8,6 +8,9 @@ import path from "node:path";
export const baseNextConfig = { export const baseNextConfig = {
output: "standalone", output: "standalone",
turbopack: {}, turbopack: {},
serverActions: {
allowedOrigins: ["*.klz-cables.com", "*.branch.klz-cables.com", "localhost:3000", "*.mintel.me"],
},
images: { images: {
dangerouslyAllowSVG: true, dangerouslyAllowSVG: true,
contentDispositionType: "attachment", contentDispositionType: "attachment",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-config", "name": "@mintel/next-config",
"version": "1.9.16", "version": "1.9.17",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://git.infra.mintel.me/api/packages/mmintel/npm" "registry": "https://git.infra.mintel.me/api/packages/mmintel/npm"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-feedback", "name": "@mintel/next-feedback",
"version": "1.9.16", "version": "1.9.17",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://git.infra.mintel.me/api/packages/mmintel/npm" "registry": "https://git.infra.mintel.me/api/packages/mmintel/npm"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-observability", "name": "@mintel/next-observability",
"version": "1.9.16", "version": "1.9.17",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://git.infra.mintel.me/api/packages/mmintel/npm" "registry": "https://git.infra.mintel.me/api/packages/mmintel/npm"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-utils", "name": "@mintel/next-utils",
"version": "1.9.16", "version": "1.9.17",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://git.infra.mintel.me/api/packages/mmintel/npm" "registry": "https://git.infra.mintel.me/api/packages/mmintel/npm"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/observability", "name": "@mintel/observability",
"version": "1.9.16", "version": "1.9.17",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://git.infra.mintel.me/api/packages/mmintel/npm" "registry": "https://git.infra.mintel.me/api/packages/mmintel/npm"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/page-audit", "name": "@mintel/page-audit",
"version": "1.9.16", "version": "1.9.17",
"description": "AI-powered website IST-analysis using DataForSEO and Gemini", "description": "AI-powered website IST-analysis using DataForSEO and Gemini",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/payload-ai", "name": "@mintel/payload-ai",
"version": "1.9.16", "version": "1.9.17",
"description": "Reusable Payload CMS AI Extensions", "description": "Reusable Payload CMS AI Extensions",
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -11,12 +11,13 @@
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"exports": { "exports": {
".": "./dist/index.js", ".": "./dist/index.js",
"./components/*": "./dist/components/*", "./components/FieldGenerators/*": "./dist/components/FieldGenerators/*.js",
"./actions/*": "./dist/actions/*", "./components/*": "./dist/components/*.js",
"./globals/*": "./dist/globals/*", "./actions/*": "./dist/actions/*.js",
"./endpoints/*": "./dist/endpoints/*", "./globals/*": "./dist/globals/*.js",
"./utils/*": "./dist/utils/*", "./endpoints/*": "./dist/endpoints/*.js",
"./tools/*": "./dist/tools/*" "./utils/*": "./dist/utils/*.js",
"./tools/*": "./dist/tools/*.js"
}, },
"peerDependencies": { "peerDependencies": {
"@payloadcms/next": ">=3.0.0", "@payloadcms/next": ">=3.0.0",

View File

@@ -1,90 +1,98 @@
import type { Config, Plugin } from 'payload' import type { Plugin } from "payload";
import { AIChatPermissionsCollection } from './collections/AIChatPermissions.js' import { AIChatPermissionsCollection } from "./collections/AIChatPermissions.js";
import type { PayloadChatPluginConfig } from './types.js' import type { PayloadChatPluginConfig } from "./types.js";
import { optimizePostEndpoint } from './endpoints/optimizeEndpoint.js' import { optimizePostEndpoint } from "./endpoints/optimizeEndpoint.js";
import { generateSlugEndpoint, generateThumbnailEndpoint, generateSingleFieldEndpoint } from './endpoints/generateEndpoints.js' import {
generateSlugEndpoint,
generateThumbnailEndpoint,
generateSingleFieldEndpoint,
} from "./endpoints/generateEndpoints.js";
export const payloadChatPlugin = export const payloadChatPlugin =
(pluginOptions: PayloadChatPluginConfig): Plugin => (pluginOptions: PayloadChatPluginConfig): Plugin =>
(incomingConfig) => { (incomingConfig) => {
let config = { ...incomingConfig } const config = { ...incomingConfig };
// If disabled, return config untouched // If disabled, return config untouched
if (pluginOptions.enabled === false) { if (pluginOptions.enabled === false) {
return config return config;
} }
// 1. Inject the Permissions Collection into the Schema // 1. Inject the Permissions Collection into the Schema
const existingCollections = config.collections || [] const existingCollections = config.collections || [];
const mcpServers = pluginOptions.mcpServers || [] const mcpServers = pluginOptions.mcpServers || [];
// Dynamically populate the select options for Collections and MCP Servers // Dynamically populate the select options for Collections and MCP Servers
const permissionCollection = { ...AIChatPermissionsCollection } const permissionCollection = { ...AIChatPermissionsCollection };
const collectionField = permissionCollection.fields.find(f => 'name' in f && f.name === 'allowedCollections') as any const collectionField = permissionCollection.fields.find(
if (collectionField) { (f) => "name" in f && f.name === "allowedCollections",
collectionField.options = existingCollections.map(c => ({ ) as any;
label: c.labels?.singular || c.slug, if (collectionField) {
value: c.slug collectionField.options = existingCollections.map((c) => ({
})) label: c.labels?.singular || c.slug,
} value: c.slug,
}));
}
const mcpField = permissionCollection.fields.find(f => 'name' in f && f.name === 'allowedMcpServers') as any const mcpField = permissionCollection.fields.find(
if (mcpField) { (f) => "name" in f && f.name === "allowedMcpServers",
mcpField.options = mcpServers.map(s => ({ ) as any;
label: s.name, if (mcpField) {
value: s.name mcpField.options = mcpServers.map((s) => ({
})) label: s.name,
} value: s.name,
}));
}
config.collections = [...existingCollections, permissionCollection] config.collections = [...existingCollections, permissionCollection];
// 2. Register Custom API Endpoint for the AI Chat // 2. Register Custom API Endpoint for the AI Chat
config.endpoints = [ config.endpoints = [
...(config.endpoints || []), ...(config.endpoints || []),
{ {
path: '/api/mcp-chat', path: "/api/mcp-chat",
method: 'post', method: "post",
handler: async (req) => { handler: async (_req) => {
// Fallback simple handler while developing endpoint logic // Fallback simple handler while developing endpoint logic
return Response.json({ message: "Chat endpoint active" }) return Response.json({ message: "Chat endpoint active" });
}, },
}, },
{ {
path: '/api/mintel-ai/optimize', path: "/api/mintel-ai/optimize",
method: 'post', method: "post",
handler: optimizePostEndpoint, handler: optimizePostEndpoint,
}, },
{ {
path: '/api/mintel-ai/generate-slug', path: "/api/mintel-ai/generate-slug",
method: 'post', method: "post",
handler: generateSlugEndpoint, handler: generateSlugEndpoint,
}, },
{ {
path: '/api/mintel-ai/generate-thumbnail', path: "/api/mintel-ai/generate-thumbnail",
method: 'post', method: "post",
handler: generateThumbnailEndpoint, handler: generateThumbnailEndpoint,
}, },
{ {
path: '/api/mintel-ai/generate-single-field', path: "/api/mintel-ai/generate-single-field",
method: 'post', method: "post",
handler: generateSingleFieldEndpoint, handler: generateSingleFieldEndpoint,
}, },
] ];
// 3. Inject Chat React Component into Admin UI // 3. Inject Chat React Component into Admin UI
if (pluginOptions.renderChatBubble !== false) { if (pluginOptions.renderChatBubble !== false) {
config.admin = { config.admin = {
...(config.admin || {}), ...(config.admin || {}),
components: { components: {
...(config.admin?.components || {}), ...(config.admin?.components || {}),
providers: [ providers: [
...(config.admin?.components?.providers || []), ...(config.admin?.components?.providers || []),
'@mintel/payload-ai/components/ChatWindow#ChatWindowProvider', "@mintel/payload-ai/components/ChatWindow/index#ChatWindowProvider",
], ],
}, },
} };
} }
return config return config;
} };

View File

@@ -1,136 +1,156 @@
'use client' "use client";
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from "react";
import { useChat } from '@ai-sdk/react' import { useChat } from "@ai-sdk/react";
import './ChatWindow.scss'
export const ChatWindowProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { export const ChatWindowProvider: React.FC<{ children: React.ReactNode }> = ({
return ( children,
<> }) => {
{children} return (
<ChatWindow /> <>
</> {children}
) <ChatWindow />
} </>
);
};
const ChatWindow: React.FC = () => { const ChatWindow: React.FC = () => {
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false);
const [pageContext, setPageContext] = useState<any>({ url: '' }) const [pageContext, setPageContext] = useState<any>({ url: "" });
useEffect(() => { useEffect(() => {
if (typeof window !== 'undefined') { if (typeof window !== "undefined") {
const path = window.location.pathname; const path = window.location.pathname;
let collectionSlug = null; let collectionSlug = null;
let id = null; let id = null;
// Payload admin URLs are usually /admin/collections/:slug/:id // Payload admin URLs are usually /admin/collections/:slug/:id
const match = path.match(/\/collections\/([^/]+)(?:\/([^/]+))?/); const match = path.match(/\/collections\/([^/]+)(?:\/([^/]+))?/);
if (match) { if (match) {
collectionSlug = match[1]; collectionSlug = match[1];
if (match[2] && match[2] !== 'create') { if (match[2] && match[2] !== "create") {
id = match[2]; id = match[2];
}
}
setPageContext({
url: window.location.href,
title: document.title,
collectionSlug,
id
})
} }
}, [isOpen]) // Refresh context when chat is opened }
// @ts-ignore - AI hook version mismatch between core and react packages setPageContext({
const { messages, input, handleInputChange, handleSubmit, setMessages } = useChat({ url: window.location.href,
api: '/api/mcp-chat', title: document.title,
initialMessages: [], collectionSlug,
body: { id,
pageContext });
} }
} as any) }, [isOpen]); // Refresh context when chat is opened
// Basic implementation to toggle chat window and submit messages const { messages, input, handleInputChange, handleSubmit } = useChat({
return ( api: "/api/mcp-chat",
<div className="payload-mcp-chat-container"> initialMessages: [],
<button body: {
className="payload-mcp-chat-toggle" pageContext,
onClick={() => setIsOpen(!isOpen)} },
} as any) as any;
// Basic implementation to toggle chat window and submit messages
return (
<div className="payload-mcp-chat-container">
<button
className="payload-mcp-chat-toggle"
onClick={() => setIsOpen(!isOpen)}
style={{
position: "fixed",
bottom: "20px",
right: "20px",
zIndex: 9999,
padding: "12px 24px",
backgroundColor: "#000",
color: "#fff",
borderRadius: "8px",
border: "none",
cursor: "pointer",
fontWeight: "bold",
}}
>
{isOpen ? "Close AI Chat" : "Ask AI"}
</button>
{isOpen && (
<div
className="payload-mcp-chat-window"
style={{
position: "fixed",
bottom: "80px",
right: "20px",
width: "450px",
height: "650px",
backgroundColor: "#fff",
border: "1px solid #eaeaea",
borderRadius: "12px",
zIndex: 9999,
display: "flex",
flexDirection: "column",
boxShadow: "0 10px 40px rgba(0,0,0,0.1)",
}}
>
<div
className="chat-header"
style={{
padding: "16px",
borderBottom: "1px solid #eaeaea",
backgroundColor: "#f9f9f9",
borderTopLeftRadius: "12px",
borderTopRightRadius: "12px",
}}
>
<h3 style={{ margin: 0, fontSize: "16px" }}>Payload MCP Chat</h3>
</div>
<div
className="chat-messages"
style={{ flex: 1, padding: "16px", overflowY: "auto" }}
>
{messages.map((m: any) => (
<div
key={m.id}
style={{ style={{
position: 'fixed', marginBottom: "12px",
bottom: '20px', textAlign: m.role === "user" ? "right" : "left",
right: '20px',
zIndex: 9999,
padding: '12px 24px',
backgroundColor: '#000',
color: '#fff',
borderRadius: '8px',
border: 'none',
cursor: 'pointer',
fontWeight: 'bold'
}} }}
> >
{isOpen ? 'Close AI Chat' : 'Ask AI'}
</button>
{isOpen && (
<div <div
className="payload-mcp-chat-window" style={{
style={{ display: "inline-block",
position: 'fixed', padding: "8px 12px",
bottom: '80px', borderRadius: "8px",
right: '20px', backgroundColor: m.role === "user" ? "#000" : "#f0f0f0",
width: '450px', color: m.role === "user" ? "#fff" : "#000",
height: '650px', maxWidth: "80%",
backgroundColor: '#fff', }}
border: '1px solid #eaeaea',
borderRadius: '12px',
zIndex: 9999,
display: 'flex',
flexDirection: 'column',
boxShadow: '0 10px 40px rgba(0,0,0,0.1)'
}}
> >
<div className="chat-header" style={{ padding: '16px', borderBottom: '1px solid #eaeaea', backgroundColor: '#f9f9f9', borderTopLeftRadius: '12px', borderTopRightRadius: '12px' }}> {m.role === "user" ? "G: " : "AI: "}
<h3 style={{ margin: 0, fontSize: '16px' }}>Payload MCP Chat</h3> {m.content}
</div>
<div className="chat-messages" style={{ flex: 1, padding: '16px', overflowY: 'auto' }}>
{messages.map((m: any) => (
<div key={m.id} style={{
marginBottom: '12px',
textAlign: m.role === 'user' ? 'right' : 'left'
}}>
<div style={{
display: 'inline-block',
padding: '8px 12px',
borderRadius: '8px',
backgroundColor: m.role === 'user' ? '#000' : '#f0f0f0',
color: m.role === 'user' ? '#fff' : '#000',
maxWidth: '80%'
}}>
{m.role === 'user' ? 'G: ' : 'AI: '}
{m.content}
</div>
</div>
))}
</div>
<form onSubmit={handleSubmit} style={{ padding: '16px', borderTop: '1px solid #eaeaea' }}>
<input
value={input}
placeholder="Ask me anything or use /commands..."
onChange={handleInputChange}
style={{
width: '100%',
padding: '12px',
borderRadius: '8px',
border: '1px solid #eaeaea',
boxSizing: 'border-box'
}}
/>
</form>
</div> </div>
)} </div>
))}
</div>
<form
onSubmit={handleSubmit}
style={{ padding: "16px", borderTop: "1px solid #eaeaea" }}
>
<input
value={input}
placeholder="Ask me anything or use /commands..."
onChange={handleInputChange}
style={{
width: "100%",
padding: "12px",
borderRadius: "8px",
border: "1px solid #eaeaea",
boxSizing: "border-box",
}}
/>
</form>
</div> </div>
) )}
} </div>
);
};

View File

@@ -1,115 +1,142 @@
import { streamText } from 'ai' import { streamText } from "ai";
import { createOpenAI } from '@ai-sdk/openai' import { createOpenAI } from "@ai-sdk/openai";
import { generatePayloadLocalTools } from '../tools/payloadLocal.js' import { generatePayloadLocalTools } from "../tools/payloadLocal.js";
import { createMcpTools } from '../tools/mcpAdapter.js' import { createMcpTools } from "../tools/mcpAdapter.js";
import { generateMemoryTools } from '../tools/memoryDb.js' import { generateMemoryTools } from "../tools/memoryDb.js";
import type { PayloadRequest } from 'payload' import type { PayloadRequest } from "payload";
const openrouter = createOpenAI({ const openrouter = createOpenAI({
baseURL: 'https://openrouter.ai/api/v1', baseURL: "https://openrouter.ai/api/v1",
apiKey: process.env.OPENROUTER_API_KEY || 'dummy_key', apiKey: process.env.OPENROUTER_API_KEY || "dummy_key",
}) });
export const handleMcpChat = async (req: PayloadRequest) => { export const handleMcpChat = async (req: PayloadRequest) => {
if (!req.user) { if (!req.user) {
return Response.json({ error: 'Unauthorized. You must be logged in to use AI Chat.' }, { status: 401 }) return Response.json(
{ error: "Unauthorized. You must be logged in to use AI Chat." },
{ status: 401 },
);
}
const { messages, pageContext } = ((await req.json?.()) || {
messages: [],
}) as { messages: any[]; pageContext?: any };
// 1. Check AI Permissions for req.user
// Look up the collection for permissions
const permissionsQuery = await req.payload.find({
collection: "ai-chat-permissions" as any,
where: {
or: [
{ targetUser: { equals: req.user.id } },
{ targetRole: { equals: req.user.role || "admin" } },
],
},
limit: 10,
});
const allowedCollections = new Set<string>();
const allowedMcpServers = new Set<string>();
for (const perm of permissionsQuery.docs) {
if (perm.allowedCollections) {
perm.allowedCollections.forEach((c: string) => allowedCollections.add(c));
} }
if (perm.allowedMcpServers) {
const { messages, pageContext } = (await req.json?.() || { messages: [] }) as { messages: any[], pageContext?: any } perm.allowedMcpServers.forEach((s: string) => allowedMcpServers.add(s));
// 1. Check AI Permissions for req.user
// Look up the collection for permissions
const permissionsQuery = await req.payload.find({
collection: 'ai-chat-permissions' as any,
where: {
or: [
{ targetUser: { equals: req.user.id } },
{ targetRole: { equals: req.user.role || 'admin' } }
]
},
limit: 10
})
const allowedCollections = new Set<string>()
const allowedMcpServers = new Set<string>()
for (const perm of permissionsQuery.docs) {
if (perm.allowedCollections) {
perm.allowedCollections.forEach((c: string) => allowedCollections.add(c))
}
if (perm.allowedMcpServers) {
perm.allowedMcpServers.forEach((s: string) => allowedMcpServers.add(s))
}
} }
}
let accessCollections = Array.from(allowedCollections) let accessCollections = Array.from(allowedCollections);
if (accessCollections.length === 0) { if (accessCollections.length === 0) {
// Fallback or demo config if not configured yet // Fallback or demo config if not configured yet
accessCollections = ['users', 'pages', 'posts', 'products', 'leads', 'media'] accessCollections = [
"users",
"pages",
"posts",
"products",
"leads",
"media",
];
}
let activeTools: Record<string, any> = {};
// 2. Generate Payload Local Tools
if (accessCollections.length > 0) {
const payloadTools = generatePayloadLocalTools(
req.payload,
req,
accessCollections,
);
activeTools = { ...activeTools, ...payloadTools };
}
// 3. Connect External MCPs
if (Array.from(allowedMcpServers).includes("gitea")) {
try {
const { tools: giteaTools } = await createMcpTools({
name: "gitea",
command: "npx",
args: [
"-y",
"@modelcontextprotocol/server-gitea",
"--url",
"https://git.mintel.int",
"--token",
process.env.GITEA_TOKEN || "",
],
});
activeTools = { ...activeTools, ...giteaTools };
} catch (e) {
console.error("Failed to connect to Gitea MCP", e);
} }
}
let activeTools: Record<string, any> = {} // 4. Inject Memory Database Tools
// We provide the user ID so memory is partitioned per user
const memoryTools = generateMemoryTools(req.user.id);
activeTools = { ...activeTools, ...memoryTools };
// 2. Generate Payload Local Tools // 5. Build prompt to ensure it asks before saving
if (accessCollections.length > 0) { const memorySystemPrompt = `
const payloadTools = generatePayloadLocalTools(req.payload, req, accessCollections)
activeTools = { ...activeTools, ...payloadTools }
}
// 3. Connect External MCPs
if (Array.from(allowedMcpServers).includes('gitea')) {
try {
const { tools: giteaTools } = await createMcpTools({
name: 'gitea',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-gitea', '--url', 'https://git.mintel.int', '--token', process.env.GITEA_TOKEN || '']
})
activeTools = { ...activeTools, ...giteaTools }
} catch (e) {
console.error('Failed to connect to Gitea MCP', e)
}
}
// 4. Inject Memory Database Tools
// We provide the user ID so memory is partitioned per user
const memoryTools = generateMemoryTools(req.user.id)
activeTools = { ...activeTools, ...memoryTools }
// 5. Build prompt to ensure it asks before saving
const memorySystemPrompt = `
You have access to a long-term vector memory database (Qdrant). You have access to a long-term vector memory database (Qdrant).
If the user says "speicher das", "merk dir das", "vergiss das nicht" etc., you MUST use the save_memory tool. If the user says "speicher das", "merk dir das", "vergiss das nicht" etc., you MUST use the save_memory tool.
If the user shares important context but doesn't explicitly ask you to remember it, you should ask "Soll ich mir das für die Zukunft merken?" before saving it. Do not ask for trivial things. If the user shares important context but doesn't explicitly ask you to remember it, you should ask "Soll ich mir das für die Zukunft merken?" before saving it. Do not ask for trivial things.
` `;
const contextContextStr = pageContext ? ` const contextContextStr = pageContext
? `
Current User Context: Current User Context:
URL: ${pageContext.url || 'Unknown'} URL: ${pageContext.url || "Unknown"}
Title: ${pageContext.title || 'Unknown'} Title: ${pageContext.title || "Unknown"}
Collection: ${pageContext.collectionSlug || 'None'} Collection: ${pageContext.collectionSlug || "None"}
Document ID: ${pageContext.id || 'None'} Document ID: ${pageContext.id || "None"}
You can use this to understand what the user is currently looking at. You can use this to understand what the user is currently looking at.
` : '' `
: "";
try { try {
const result = streamText({ const result = streamText({
// @ts-ignore - AI SDK type mismatch model: openrouter("google/gemini-3.0-flash"),
model: openrouter('google/gemini-3.0-flash'), messages,
messages, tools: activeTools,
tools: activeTools, // @ts-expect-error - AI SDK type mismatch with maxSteps
// @ts-ignore - AI SDK type mismatch with maxSteps maxSteps: 10,
maxSteps: 10, system: `You are a helpful Payload CMS Agent orchestrating the local Mintel ecosystem.
system: `You are a helpful Payload CMS Agent orchestrating the local Mintel ecosystem.
You only have access to tools explicitly granted by the Admin. You only have access to tools explicitly granted by the Admin.
You can completely control Payload CMS (read, create, update, delete documents). You can completely control Payload CMS (read, create, update, delete documents).
If you need more details to fulfill a request (e.g. creating a blog post), you can ask the user. If you need more details to fulfill a request (e.g. creating a blog post), you can ask the user.
${contextContextStr} ${contextContextStr}
${memorySystemPrompt}` ${memorySystemPrompt}`,
}) });
return result.toTextStreamResponse() return result.toTextStreamResponse();
} catch (error) { } catch (error) {
console.error("AI Error:", error) console.error("AI Error:", error);
return Response.json({ error: 'Failed to process AI request' }, { status: 500 }) return Response.json(
} { error: "Failed to process AI request" },
} { status: 500 },
);
}
};

View File

@@ -4,126 +4,157 @@ import * as path from "node:path";
import * as os from "node:os"; import * as os from "node:os";
async function getOrchestrator() { async function getOrchestrator() {
const OPENROUTER_KEY = const OPENROUTER_KEY =
process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY;
const REPLICATE_KEY = process.env.REPLICATE_API_KEY; const REPLICATE_KEY = process.env.REPLICATE_API_KEY;
if (!OPENROUTER_KEY) { if (!OPENROUTER_KEY) {
throw new Error( throw new Error(
"Missing OPENROUTER_API_KEY in .env (Required for AI generation)", "Missing OPENROUTER_API_KEY in .env (Required for AI generation)",
);
}
const importDynamic = new Function("modulePath", "return import(modulePath)");
const { AiBlogPostOrchestrator } = await importDynamic(
"@mintel/content-engine",
); );
}
return new AiBlogPostOrchestrator({ const importDynamic = new Function("modulePath", "return import(modulePath)");
apiKey: OPENROUTER_KEY, const { AiBlogPostOrchestrator } = await importDynamic(
replicateApiKey: REPLICATE_KEY, "@mintel/content-engine",
model: "google/gemini-3-flash-preview", );
});
return new AiBlogPostOrchestrator({
apiKey: OPENROUTER_KEY,
replicateApiKey: REPLICATE_KEY,
model: "google/gemini-3-flash-preview",
});
} }
export const generateSlugEndpoint = async (req: PayloadRequest) => { export const generateSlugEndpoint = async (req: PayloadRequest) => {
try {
let body: any = {};
try { try {
const { title, draftContent, oldSlug, instructions } = (await req.json?.() || {}) as any; if (req.body) body = (await req.json?.()) || {};
const orchestrator = await getOrchestrator(); } catch {
const newSlug = await orchestrator.generateSlug( /* ignore */
draftContent,
title,
instructions,
);
if (oldSlug && oldSlug !== newSlug) {
await req.payload.create({
collection: "redirects" as any,
data: {
from: oldSlug,
to: newSlug,
},
});
}
return Response.json({ success: true, slug: newSlug });
} catch (e: any) {
return Response.json({ success: false, error: e.message }, { status: 500 });
} }
} const { title, draftContent, oldSlug, instructions } = body;
const orchestrator = await getOrchestrator();
const newSlug = await orchestrator.generateSlug(
draftContent,
title,
instructions,
);
if (oldSlug && oldSlug !== newSlug) {
await req.payload.create({
collection: "redirects" as any,
data: {
from: oldSlug,
to: newSlug,
},
});
}
return Response.json({ success: true, slug: newSlug });
} catch (e: any) {
return Response.json({ success: false, error: e.message }, { status: 500 });
}
};
export const generateThumbnailEndpoint = async (req: PayloadRequest) => { export const generateThumbnailEndpoint = async (req: PayloadRequest) => {
try {
let body: any = {};
try { try {
const { draftContent, title, instructions } = (await req.json?.() || {}) as any; if (req.body) body = (await req.json?.()) || {};
const OPENROUTER_KEY = } catch {
process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; /* ignore */
const REPLICATE_KEY = process.env.REPLICATE_API_KEY;
if (!OPENROUTER_KEY) throw new Error("Missing OPENROUTER_API_KEY in .env");
if (!REPLICATE_KEY) throw new Error("Missing REPLICATE_API_KEY in .env");
const importDynamic = new Function("modulePath", "return import(modulePath)");
const { AiBlogPostOrchestrator } = await importDynamic("@mintel/content-engine");
const { ThumbnailGenerator } = await importDynamic("@mintel/thumbnail-generator");
const orchestrator = new AiBlogPostOrchestrator({
apiKey: OPENROUTER_KEY,
replicateApiKey: REPLICATE_KEY,
model: "google/gemini-3-flash-preview",
});
const tg = new ThumbnailGenerator({ replicateApiKey: REPLICATE_KEY });
const prompt = await orchestrator.generateVisualPrompt(
draftContent || title || "Technology",
instructions,
);
const tmpPath = path.join(os.tmpdir(), `mintel-thumb-${Date.now()}.png`);
await tg.generateImage(prompt, tmpPath);
const fileData = await fs.readFile(tmpPath);
const stat = await fs.stat(tmpPath);
const fileName = path.basename(tmpPath);
const newMedia = await req.payload.create({
collection: "media" as any,
data: {
alt: title ? `Thumbnail for ${title}` : "AI Generated Thumbnail",
},
file: {
data: fileData,
name: fileName,
mimetype: "image/png",
size: stat.size,
},
});
await fs.unlink(tmpPath).catch(() => { });
return Response.json({ success: true, mediaId: newMedia.id });
} catch (e: any) {
return Response.json({ success: false, error: e.message }, { status: 500 });
} }
} const { draftContent, title, instructions } = body;
const OPENROUTER_KEY =
process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY;
const REPLICATE_KEY = process.env.REPLICATE_API_KEY;
if (!OPENROUTER_KEY) throw new Error("Missing OPENROUTER_API_KEY in .env");
if (!REPLICATE_KEY) throw new Error("Missing REPLICATE_API_KEY in .env");
const importDynamic = new Function(
"modulePath",
"return import(modulePath)",
);
const { AiBlogPostOrchestrator } = await importDynamic(
"@mintel/content-engine",
);
const { ThumbnailGenerator } = await importDynamic(
"@mintel/thumbnail-generator",
);
const orchestrator = new AiBlogPostOrchestrator({
apiKey: OPENROUTER_KEY,
replicateApiKey: REPLICATE_KEY,
model: "google/gemini-3-flash-preview",
});
const tg = new ThumbnailGenerator({ replicateApiKey: REPLICATE_KEY });
const prompt = await orchestrator.generateVisualPrompt(
draftContent || title || "Technology",
instructions,
);
const tmpPath = path.join(os.tmpdir(), `mintel-thumb-${Date.now()}.png`);
await tg.generateImage(prompt, tmpPath);
const fileData = await fs.readFile(tmpPath);
const stat = await fs.stat(tmpPath);
const fileName = path.basename(tmpPath);
const newMedia = await req.payload.create({
collection: "media" as any,
data: {
alt: title ? `Thumbnail for ${title}` : "AI Generated Thumbnail",
},
file: {
data: fileData,
name: fileName,
mimetype: "image/png",
size: stat.size,
},
});
await fs.unlink(tmpPath).catch(() => {});
return Response.json({ success: true, mediaId: newMedia.id });
} catch (e: any) {
return Response.json({ success: false, error: e.message }, { status: 500 });
}
};
export const generateSingleFieldEndpoint = async (req: PayloadRequest) => { export const generateSingleFieldEndpoint = async (req: PayloadRequest) => {
try {
let body: any = {};
try { try {
const { documentTitle, documentContent, fieldName, fieldDescription, instructions } = (await req.json?.() || {}) as any; if (req.body) body = (await req.json?.()) || {};
} catch {
/* ignore */
}
const {
documentTitle,
documentContent,
fieldName,
fieldDescription,
instructions,
} = body;
const OPENROUTER_KEY = const OPENROUTER_KEY =
process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY;
if (!OPENROUTER_KEY) throw new Error("Missing OPENROUTER_API_KEY"); if (!OPENROUTER_KEY) throw new Error("Missing OPENROUTER_API_KEY");
const contextDocsData = await req.payload.find({ const contextDocsData = await req.payload.find({
collection: "context-files" as any, collection: "context-files" as any,
limit: 100, limit: 100,
}); });
const projectContext = contextDocsData.docs const projectContext = contextDocsData.docs
.map((doc: any) => `--- ${doc.filename} ---\n${doc.content}`) .map((doc: any) => `--- ${doc.filename} ---\n${doc.content}`)
.join("\n\n"); .join("\n\n");
const prompt = `You are an expert AI assistant perfectly trained for generating exact data values for CMS components. const prompt = `You are an expert AI assistant perfectly trained for generating exact data values for CMS components.
PROJECT STRATEGY & CONTEXT: PROJECT STRATEGY & CONTEXT:
${projectContext} ${projectContext}
@@ -138,21 +169,21 @@ CRITICAL RULES:
3. If the field implies a diagram or flow, output RAW Mermaid.js code. 3. If the field implies a diagram or flow, output RAW Mermaid.js code.
4. If it's standard text, write professional B2B German. No quotes, no conversational filler.`; 4. If it's standard text, write professional B2B German. No quotes, no conversational filler.`;
const res = await fetch("https://openrouter.ai/api/v1/chat/completions", { const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `Bearer ${OPENROUTER_KEY}`, Authorization: `Bearer ${OPENROUTER_KEY}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
model: "google/gemini-3-flash-preview", model: "google/gemini-3-flash-preview",
messages: [{ role: "user", content: prompt }], messages: [{ role: "user", content: prompt }],
}), }),
}); });
const data = await res.json(); const data = await res.json();
const text = data.choices?.[0]?.message?.content?.trim() || ""; const text = data.choices?.[0]?.message?.content?.trim() || "";
return Response.json({ success: true, text }); return Response.json({ success: true, text });
} catch (e: any) { } catch (e: any) {
return Response.json({ success: false, error: e.message }, { status: 500 }); return Response.json({ success: false, error: e.message }, { status: 500 });
} }
} };

View File

@@ -1,75 +1,108 @@
import { PayloadRequest } from 'payload' import { PayloadRequest } from "payload";
import { parseMarkdownToLexical } from "../utils/lexicalParser.js"; import { parseMarkdownToLexical } from "../utils/lexicalParser.js";
export const optimizePostEndpoint = async (req: PayloadRequest) => { export const optimizePostEndpoint = async (req: PayloadRequest) => {
try {
let body: any = {};
try { try {
const { draftContent, instructions } = (await req.json?.() || {}) as { draftContent: string; instructions?: string }; if (req.body) {
// req.json() acts as a method in Next.js/Payload req wrapper
if (!draftContent) { body = (await req.json?.()) || {};
return Response.json({ error: 'Missing draftContent' }, { status: 400 }) }
} } catch (e) {
// Ignore JSON parse error, body remains empty
const globalAiSettings = (await req.payload.findGlobal({ slug: "ai-settings" })) as any;
const customSources =
globalAiSettings?.customSources?.map((s: any) => s.sourceName) || [];
const OPENROUTER_KEY =
process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY;
const REPLICATE_KEY = process.env.REPLICATE_API_KEY;
if (!OPENROUTER_KEY) {
return Response.json({ error: "OPENROUTER_KEY not found in environment." }, { status: 500 })
}
// Dynamically import to avoid bundling it into client components that might accidentally import this file
const importDynamic = new Function("modulePath", "return import(modulePath)");
const { AiBlogPostOrchestrator } = await importDynamic("@mintel/content-engine");
const orchestrator = new AiBlogPostOrchestrator({
apiKey: OPENROUTER_KEY,
replicateApiKey: REPLICATE_KEY,
model: "google/gemini-3-flash-preview",
});
const contextDocsData = await req.payload.find({
collection: "context-files" as any,
limit: 100,
});
const projectContext = contextDocsData.docs.map((doc: any) => doc.content);
const optimizedMarkdown = await orchestrator.optimizeDocument({
content: draftContent,
projectContext,
availableComponents: [],
instructions,
internalLinks: [],
customSources,
});
if (!optimizedMarkdown || typeof optimizedMarkdown !== "string") {
return Response.json({ error: "AI returned invalid markup." }, { status: 500 })
}
const blocks = parseMarkdownToLexical(optimizedMarkdown);
return Response.json({
success: true,
lexicalAST: {
root: {
type: "root",
format: "",
indent: 0,
version: 1,
children: blocks,
direction: "ltr",
},
},
})
} catch (error: any) {
console.error("Failed to optimize post in endpoint:", error);
return Response.json({
success: false,
error: error.message || "An unknown error occurred during optimization.",
}, { status: 500 })
} }
}
const { draftContent, instructions } = body as {
draftContent?: string;
instructions?: string;
};
if (!draftContent) {
return Response.json(
{ success: false, error: "Missing draftContent" },
{ status: 400 },
);
}
const globalAiSettings = (await req.payload.findGlobal({
slug: "ai-settings",
})) as any;
const customSources =
globalAiSettings?.customSources?.map((s: any) => s.sourceName) || [];
const OPENROUTER_KEY =
process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY;
const REPLICATE_KEY = process.env.REPLICATE_API_KEY;
if (!OPENROUTER_KEY) {
return Response.json(
{ error: "OPENROUTER_KEY not found in environment." },
{ status: 500 },
);
}
// Dynamically import to avoid bundling it into client components that might accidentally import this file
const importDynamic = new Function(
"modulePath",
"return import(modulePath)",
);
const { AiBlogPostOrchestrator } = await importDynamic(
"@mintel/content-engine",
);
const orchestrator = new AiBlogPostOrchestrator({
apiKey: OPENROUTER_KEY,
replicateApiKey: REPLICATE_KEY,
model: "google/gemini-3-flash-preview",
});
const contextDocsData = await req.payload.find({
collection: "context-files" as any,
limit: 100,
});
const projectContext = contextDocsData.docs.map((doc: any) => doc.content);
const optimizedMarkdown = await orchestrator.optimizeDocument({
content: draftContent,
projectContext,
availableComponents: [],
instructions,
internalLinks: [],
customSources,
});
if (!optimizedMarkdown || typeof optimizedMarkdown !== "string") {
return Response.json(
{ error: "AI returned invalid markup." },
{ status: 500 },
);
}
const blocks = parseMarkdownToLexical(optimizedMarkdown);
return Response.json({
success: true,
lexicalAST: {
root: {
type: "root",
format: "",
indent: 0,
version: 1,
children: blocks,
direction: "ltr",
},
},
});
} catch (error: any) {
console.error("Failed to optimize post in endpoint:", error);
return Response.json(
{
success: false,
error:
error.message || "An unknown error occurred during optimization.",
},
{ status: 500 },
);
}
};

View File

@@ -1,65 +1,72 @@
import { Client } from '@modelcontextprotocol/sdk/client/index.js' import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js' import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js' import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { tool } from 'ai' import { tool } from "ai";
import { z } from 'zod' import { z } from "zod";
/** /**
* Connects to an external MCP Server and maps its tools to Vercel AI SDK Tools. * Connects to an external MCP Server and maps its tools to Vercel AI SDK Tools.
*/ */
export async function createMcpTools(mcpConfig: { name: string, url?: string, command?: string, args?: string[] }) { export async function createMcpTools(mcpConfig: {
let transport name: string;
url?: string;
command?: string;
args?: string[];
}) {
let transport;
// Support both HTTP/SSE and STDIO transports // Support both HTTP/SSE and STDIO transports
if (mcpConfig.url) { if (mcpConfig.url) {
transport = new SSEClientTransport(new URL(mcpConfig.url)) transport = new SSEClientTransport(new URL(mcpConfig.url));
} else if (mcpConfig.command) { } else if (mcpConfig.command) {
transport = new StdioClientTransport({ transport = new StdioClientTransport({
command: mcpConfig.command, command: mcpConfig.command,
args: mcpConfig.args || [], args: mcpConfig.args || [],
}) });
} else { } else {
throw new Error('Invalid MCP config: Must provide either URL or Command.') throw new Error("Invalid MCP config: Must provide either URL or Command.");
} }
const client = new Client( const client = new Client(
{ name: `payload-ai-client-${mcpConfig.name}`, version: '1.0.0' }, { name: `payload-ai-client-${mcpConfig.name}`, version: "1.0.0" },
{ capabilities: {} } { capabilities: {} },
) );
await client.connect(transport) await client.connect(transport);
// Fetch available tools from the external MCP server // Fetch available tools from the external MCP server
const toolListResult = await client.listTools() const toolListResult = await client.listTools();
const externalTools = toolListResult.tools || [] const externalTools = toolListResult.tools || [];
const aiSdkTools: Record<string, any> = {} const aiSdkTools: Record<string, any> = {};
// Map each external tool to a Vercel AI SDK Tool // Map each external tool to a Vercel AI SDK Tool
for (const extTool of externalTools) { for (const extTool of externalTools) {
// Basic conversion of JSON Schema to Zod for the AI SDK // Basic conversion of JSON Schema to Zod for the AI SDK
// Note: For a production ready adapter, you might need a more robust jsonSchemaToZod converter // Note: For a production ready adapter, you might need a more robust jsonSchemaToZod converter
// or use AI SDK's new experimental generateSchema feature if available. // or use AI SDK's new experimental generateSchema feature if available.
// Here we use a generic `z.any()` as a fallback since AI SDK requires a Zod schema. // Here we use a generic `z.any()` as a fallback since AI SDK requires a Zod schema.
const toolSchema = extTool.inputSchema as Record<string, any> const toolSchema = extTool.inputSchema as Record<string, any>;
// We create a simplified parameter parser. // We create a simplified parameter parser.
// An ideal approach uses `jsonSchemaToZod` library or native AI SDK JSON schema support // An ideal approach uses `jsonSchemaToZod` library or native AI SDK JSON schema support
// (introduced recently in `ai` package). // (introduced recently in `ai` package).
aiSdkTools[`${mcpConfig.name}_${extTool.name}`] = tool({ aiSdkTools[`${mcpConfig.name}_${extTool.name}`] = tool({
description: `[From ${mcpConfig.name}] ${extTool.description || extTool.name}`, description: `[From ${mcpConfig.name}] ${extTool.description || extTool.name}`,
parameters: z.any().describe('JSON matching the original MCP input_schema'), // Simplify for prototype parameters: z
// @ts-ignore - AI strict mode overload bug with implicit zod inferences .any()
execute: async (args: any) => { .describe("JSON matching the original MCP input_schema"), // Simplify for prototype
const result = await client.callTool({ // @ts-expect-error - AI strict mode overload bug with implicit zod inferences
name: extTool.name, execute: async (args: any) => {
arguments: args const result = await client.callTool({
}) name: extTool.name,
return result arguments: args,
} });
}) return result;
} },
});
}
return { tools: aiSdkTools, client } return { tools: aiSdkTools, client };
} }

View File

@@ -1,37 +1,39 @@
import { tool } from 'ai' import { tool } from "ai";
import { z } from 'zod' import { z } from "zod";
import { QdrantClient } from '@qdrant/js-client-rest' import { QdrantClient } from "@qdrant/js-client-rest";
// Qdrant initialization // Qdrant initialization
// This requires the user to have Qdrant running and QDRANT_URL/QDRANT_API_KEY environment variables set // This requires the user to have Qdrant running and QDRANT_URL/QDRANT_API_KEY environment variables set
const qdrantClient = new QdrantClient({ const qdrantClient = new QdrantClient({
url: process.env.QDRANT_URL || 'http://localhost:6333', url: process.env.QDRANT_URL || "http://localhost:6333",
apiKey: process.env.QDRANT_API_KEY, apiKey: process.env.QDRANT_API_KEY,
}) });
const MEMORY_COLLECTION = 'mintel_ai_memory' const MEMORY_COLLECTION = "mintel_ai_memory";
// Ensure collection exists on load // Ensure collection exists on load
async function initQdrant() { async function initQdrant() {
try { try {
const res = await qdrantClient.getCollections() const res = await qdrantClient.getCollections();
const exists = res.collections.find((c: any) => c.name === MEMORY_COLLECTION) const exists = res.collections.find(
if (!exists) { (c: any) => c.name === MEMORY_COLLECTION,
await qdrantClient.createCollection(MEMORY_COLLECTION, { );
vectors: { if (!exists) {
size: 1536, // typical embedding size, adjust based on the embedding model used await qdrantClient.createCollection(MEMORY_COLLECTION, {
distance: 'Cosine', vectors: {
}, size: 1536, // typical embedding size, adjust based on the embedding model used
}) distance: "Cosine",
console.log(`Qdrant collection '${MEMORY_COLLECTION}' created.`) },
} });
} catch (error) { console.log(`Qdrant collection '${MEMORY_COLLECTION}' created.`);
console.error('Failed to initialize Qdrant memory collection:', error)
} }
} catch (error) {
console.error("Failed to initialize Qdrant memory collection:", error);
}
} }
// Call init, but don't block // Call init, but don't block
initQdrant() initQdrant();
/** /**
* Returns memory tools for the AI SDK. * Returns memory tools for the AI SDK.
@@ -40,76 +42,99 @@ initQdrant()
* by a utility function, or we use Qdrant's FastEmbed (if running their specialized container). * by a utility function, or we use Qdrant's FastEmbed (if running their specialized container).
*/ */
export const generateMemoryTools = (userId: string | number) => { export const generateMemoryTools = (userId: string | number) => {
return { return {
save_memory: tool({ save_memory: tool({
description: 'Save an important preference, fact, or instruction about the user to long-term memory. Only use this when explicitly asked or when it is clearly a long-term preference.', description:
parameters: z.object({ "Save an important preference, fact, or instruction about the user to long-term memory. Only use this when explicitly asked or when it is clearly a long-term preference.",
fact: z.string().describe('The fact or instruction to remember.'), parameters: z.object({
category: z.string().optional().describe('An optional category like "preference", "rule", or "project_detail".'), fact: z.string().describe("The fact or instruction to remember."),
}), category: z
// @ts-ignore - AI SDK strict mode bug .string()
execute: async ({ fact, category }: { fact: string; category?: string }) => { .optional()
// In a real scenario, you MUST generate embeddings for the 'fact' string here .describe(
// using OpenAI or another embedding provider before inserting into Qdrant. 'An optional category like "preference", "rule", or "project_detail".',
// const embedding = await generateEmbedding(fact) ),
}),
// @ts-expect-error - AI SDK strict mode bug
execute: async ({
fact,
category,
}: {
fact: string;
category?: string;
}) => {
// In a real scenario, you MUST generate embeddings for the 'fact' string here
// using OpenAI or another embedding provider before inserting into Qdrant.
// const embedding = await generateEmbedding(fact)
try { try {
// Mock embedding payload for demonstration // Mock embedding payload for demonstration
const mockEmbedding = new Array(1536).fill(0).map(() => Math.random()) const mockEmbedding = new Array(1536)
.fill(0)
.map(() => Math.random());
await qdrantClient.upsert(MEMORY_COLLECTION, { await qdrantClient.upsert(MEMORY_COLLECTION, {
wait: true, wait: true,
points: [ points: [
{ {
id: crypto.randomUUID(), id: crypto.randomUUID(),
vector: mockEmbedding, vector: mockEmbedding,
payload: { payload: {
userId: String(userId), // Partition memory by user userId: String(userId), // Partition memory by user
fact, fact,
category, category,
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
}, },
}, },
], ],
}) });
return { success: true, message: `Successfully remembered: "${fact}"` } return {
} catch (error) { success: true,
console.error("Qdrant save error:", error) message: `Successfully remembered: "${fact}"`,
return { success: false, error: 'Failed to save to memory database.' } };
} } catch (error) {
console.error("Qdrant save error:", error);
return {
success: false,
error: "Failed to save to memory database.",
};
}
},
}),
search_memory: tool({
description:
"Search the user's long-term memory for past factual context, preferences, or rules.",
parameters: z.object({
query: z.string().describe("The search string to find in memory."),
}),
// @ts-expect-error - AI SDK strict mode bug
execute: async ({ query }: { query: string }) => {
// Generate embedding for query
const mockQueryEmbedding = new Array(1536)
.fill(0)
.map(() => Math.random());
try {
const results = await qdrantClient.search(MEMORY_COLLECTION, {
vector: mockQueryEmbedding,
limit: 5,
filter: {
must: [
{
key: "userId",
match: { value: String(userId) },
},
],
}, },
}), });
search_memory: tool({ return results.map((r: any) => r.payload?.fact || "");
description: 'Search the user\'s long-term memory for past factual context, preferences, or rules.', } catch (error) {
parameters: z.object({ console.error("Qdrant search error:", error);
query: z.string().describe('The search string to find in memory.'), return [];
}), }
// @ts-ignore - AI SDK strict mode bug },
execute: async ({ query }: { query: string }) => { }),
// Generate embedding for query };
const mockQueryEmbedding = new Array(1536).fill(0).map(() => Math.random()) };
try {
const results = await qdrantClient.search(MEMORY_COLLECTION, {
vector: mockQueryEmbedding,
limit: 5,
filter: {
must: [
{
key: 'userId',
match: { value: String(userId) }
}
]
}
})
return results.map((r: any) => r.payload?.fact || '')
} catch (error) {
console.error("Qdrant search error:", error)
return []
}
}
})
}
}

View File

@@ -1,107 +1,137 @@
import { tool } from 'ai' import { tool } from "ai";
import { z } from 'zod' import { z } from "zod";
import type { Payload, PayloadRequest, User } from 'payload' import type { Payload, PayloadRequest, User } from "payload";
export const generatePayloadLocalTools = ( export const generatePayloadLocalTools = (
payload: Payload, payload: Payload,
req: PayloadRequest, req: PayloadRequest,
allowedCollections: string[] allowedCollections: string[],
) => { ) => {
const tools: Record<string, any> = {} const tools: Record<string, any> = {};
for (const collectionSlug of allowedCollections) { for (const collectionSlug of allowedCollections) {
const slugKey = collectionSlug.replace(/-/g, '_') const slugKey = collectionSlug.replace(/-/g, "_");
// 1. Read (Find) Tool // 1. Read (Find) Tool
tools[`read_${slugKey}`] = tool({ tools[`read_${slugKey}`] = tool({
description: `Read/Find documents from the Payload CMS collection: ${collectionSlug}`, description: `Read/Find documents from the Payload CMS collection: ${collectionSlug}`,
parameters: z.object({ parameters: z.object({
limit: z.number().optional().describe('Number of documents to return, max 100.'), limit: z
page: z.number().optional().describe('Page number for pagination.'), .number()
// Simple string-based query for demo purposes. For a robust implementation, .optional()
// we'd map this to Payload's where query logic using a structured Zod schema. .describe("Number of documents to return, max 100."),
query: z.string().optional().describe('Optional text to search within the collection.'), page: z.number().optional().describe("Page number for pagination."),
}), // Simple string-based query for demo purposes. For a robust implementation,
// @ts-ignore - AI SDK strict mode type inference bug // we'd map this to Payload's where query logic using a structured Zod schema.
execute: async ({ limit = 10, page = 1, query }: { limit?: number; page?: number; query?: string }) => { query: z
const where = query ? { id: { equals: query } } : undefined // Placeholder logic .string()
.optional()
.describe("Optional text to search within the collection."),
}),
// @ts-expect-error - AI SDK strict mode type inference bug
execute: async ({
limit = 10,
page = 1,
query,
}: {
limit?: number;
page?: number;
query?: string;
}) => {
const where = query ? { id: { equals: query } } : undefined; // Placeholder logic
return await payload.find({ return await payload.find({
collection: collectionSlug as any, collection: collectionSlug as any,
limit: Math.min(limit, 100), limit: Math.min(limit, 100),
page, page,
where, where,
req, // Crucial for passing the user context and respecting access control! req, // Crucial for passing the user context and respecting access control!
}) });
}, },
}) });
// 2. Read by ID Tool // 2. Read by ID Tool
tools[`read_${slugKey}_by_id`] = tool({ tools[`read_${slugKey}_by_id`] = tool({
description: `Get a specific document by its ID from the ${collectionSlug} collection.`, description: `Get a specific document by its ID from the ${collectionSlug} collection.`,
parameters: z.object({ parameters: z.object({
id: z.union([z.string(), z.number()]).describe('The ID of the document.'), id: z
}), .union([z.string(), z.number()])
// @ts-ignore - AI SDK strict mode type inference bug .describe("The ID of the document."),
execute: async ({ id }: { id: string | number }) => { }),
return await payload.findByID({ // @ts-expect-error - AI SDK strict mode type inference bug
collection: collectionSlug as any, execute: async ({ id }: { id: string | number }) => {
id, return await payload.findByID({
req, // Enforce access control collection: collectionSlug as any,
}) id,
}, req, // Enforce access control
}) });
},
});
// 3. Create Tool // 3. Create Tool
tools[`create_${slugKey}`] = tool({ tools[`create_${slugKey}`] = tool({
description: `Create a new document in the ${collectionSlug} collection.`, description: `Create a new document in the ${collectionSlug} collection.`,
parameters: z.object({ parameters: z.object({
data: z.record(z.any()).describe('A JSON object containing the data to insert.'), data: z
}), .record(z.any())
// @ts-ignore - AI SDK strict mode type inference bug .describe("A JSON object containing the data to insert."),
execute: async ({ data }: { data: Record<string, any> }) => { }),
return await payload.create({ // @ts-expect-error - AI SDK strict mode type inference bug
collection: collectionSlug as any, execute: async ({ data }: { data: Record<string, any> }) => {
data, return await payload.create({
req, // Enforce access control collection: collectionSlug as any,
}) data,
}, req, // Enforce access control
}) });
},
});
// 4. Update Tool // 4. Update Tool
tools[`update_${slugKey}`] = tool({ tools[`update_${slugKey}`] = tool({
description: `Update an existing document in the ${collectionSlug} collection.`, description: `Update an existing document in the ${collectionSlug} collection.`,
parameters: z.object({ parameters: z.object({
id: z.union([z.string(), z.number()]).describe('The ID of the document to update.'), id: z
data: z.record(z.any()).describe('A JSON object containing the fields to update.'), .union([z.string(), z.number()])
}), .describe("The ID of the document to update."),
// @ts-ignore - AI SDK strict mode type inference bug data: z
execute: async ({ id, data }: { id: string | number; data: Record<string, any> }) => { .record(z.any())
return await payload.update({ .describe("A JSON object containing the fields to update."),
collection: collectionSlug as any, }),
id, // @ts-expect-error - AI SDK strict mode type inference bug
data, execute: async ({
req, // Enforce access control id,
}) data,
}, }: {
}) id: string | number;
data: Record<string, any>;
}) => {
return await payload.update({
collection: collectionSlug as any,
id,
data,
req, // Enforce access control
});
},
});
// 5. Delete Tool // 5. Delete Tool
tools[`delete_${slugKey}`] = tool({ tools[`delete_${slugKey}`] = tool({
description: `Delete a document from the ${collectionSlug} collection by ID.`, description: `Delete a document from the ${collectionSlug} collection by ID.`,
parameters: z.object({ parameters: z.object({
id: z.union([z.string(), z.number()]).describe('The ID of the document to delete.'), id: z
}), .union([z.string(), z.number()])
// @ts-ignore - AI SDK strict mode type inference bug .describe("The ID of the document to delete."),
execute: async ({ id }: { id: string | number }) => { }),
return await payload.delete({ // @ts-expect-error - AI SDK strict mode type inference bug
collection: collectionSlug as any, execute: async ({ id }: { id: string | number }) => {
id, return await payload.delete({
req, // Enforce access control collection: collectionSlug as any,
}) id,
}, req, // Enforce access control
}) });
} },
});
}
return tools return tools;
} };

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/pdf", "name": "@mintel/pdf",
"version": "1.9.16", "version": "1.9.17",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"module": "dist/index.js", "module": "dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/seo-engine", "name": "@mintel/seo-engine",
"version": "1.9.16", "version": "1.9.17",
"description": "AI-powered SEO keyword and topic cluster evaluation engine", "description": "AI-powered SEO keyword and topic cluster evaluation engine",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -0,0 +1,15 @@
FROM node:20-bookworm-slim AS builder
WORKDIR /app
COPY package.json ./
RUN corepack enable pnpm && pnpm install --ignore-workspace
COPY tsconfig.json ./
COPY src ./src
RUN pnpm build
FROM node:20-bookworm-slim
WORKDIR /app
COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
ENTRYPOINT ["node", "dist/start.js"]

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/serpbear-mcp", "name": "@mintel/serpbear-mcp",
"version": "1.9.16", "version": "1.9.17",
"description": "SerpBear SEO Tracking MCP server for Mintel infrastructure", "description": "SerpBear SEO Tracking MCP server for Mintel infrastructure",
"main": "dist/index.js", "main": "dist/index.js",
"type": "module", "type": "module",
@@ -13,13 +13,12 @@
"@modelcontextprotocol/sdk": "^1.5.0", "@modelcontextprotocol/sdk": "^1.5.0",
"axios": "^1.7.2", "axios": "^1.7.2",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"express": "^5.2.1", "express": "^5.2.1"
"zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/node": "^20.14.10", "@types/node": "^20.14.10",
"typescript": "^5.5.3", "tsx": "^4.19.2",
"tsx": "^4.19.2" "typescript": "^5.5.3"
} }
} }

View File

@@ -1,6 +1,7 @@
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from 'express'; import express from 'express';
import crypto from 'crypto';
import { import {
CallToolRequestSchema, CallToolRequestSchema,
ListToolsRequestSchema, ListToolsRequestSchema,
@@ -213,17 +214,34 @@ async function run() {
console.error('SerpBear MCP server is running on stdio'); console.error('SerpBear MCP server is running on stdio');
} else { } else {
const app = express(); const app = express();
let transport: SSEServerTransport | null = null; const transports = new Map<string, SSEServerTransport>();
app.use((req, _res, next) => {
console.error(`${req.method} ${req.url}`);
next();
});
app.get('/sse', async (req, res) => { app.get('/sse', async (req, res) => {
console.error('New SSE connection established'); const sessionId = crypto.randomUUID();
transport = new SSEServerTransport('/message', res); console.error(`New SSE connection: ${sessionId}`);
const transport = new SSEServerTransport(`/message/${sessionId}`, res);
transports.set(sessionId, transport);
req.on('close', () => {
console.error(`SSE connection closed: ${sessionId}`);
transports.delete(sessionId);
});
await server.connect(transport); await server.connect(transport);
}); });
app.post('/message', async (req, res) => { app.post('/message/:sessionId', async (req, res) => {
const { sessionId } = req.params;
const transport = transports.get(sessionId as string);
if (!transport) { if (!transport) {
res.status(400).send('No active SSE connection'); console.error(`No transport found for session: ${sessionId}`);
res.status(400).send('No active SSE connection for this session');
return; return;
} }
await transport.handlePostMessage(req, res); await transport.handlePostMessage(req, res);

View File

@@ -4,8 +4,8 @@ import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url)); const __dirname = fileURLToPath(new URL('.', import.meta.url));
config({ path: resolve(__dirname, '../../../.env.local') }); config({ quiet: true, path: resolve(__dirname, '../../../.env.local') });
config({ path: resolve(__dirname, '../../../.env') }); config({ quiet: true, path: resolve(__dirname, '../../../.env') });
import('./index.js').catch(err => { import('./index.js').catch(err => {
console.error('Failed to start SerpBear MCP Server:', err); console.error('Failed to start SerpBear MCP Server:', err);

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/thumbnail-generator", "name": "@mintel/thumbnail-generator",
"version": "1.9.16", "version": "1.9.17",
"private": false, "private": false,
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/tsconfig", "name": "@mintel/tsconfig",
"version": "1.9.16", "version": "1.9.17",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://git.infra.mintel.me/api/packages/mmintel/npm" "registry": "https://git.infra.mintel.me/api/packages/mmintel/npm"

View File

@@ -0,0 +1,15 @@
FROM node:20-bookworm-slim AS builder
WORKDIR /app
COPY package.json ./
RUN corepack enable pnpm && pnpm install --ignore-workspace
COPY tsconfig.json ./
COPY src ./src
RUN pnpm build
FROM node:20-bookworm-slim
WORKDIR /app
COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
ENTRYPOINT ["node", "dist/start.js"]

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/umami-mcp", "name": "@mintel/umami-mcp",
"version": "1.9.16", "version": "1.9.17",
"description": "Umami Analytics MCP server for Mintel infrastructure", "description": "Umami Analytics MCP server for Mintel infrastructure",
"main": "dist/index.js", "main": "dist/index.js",
"type": "module", "type": "module",
@@ -13,13 +13,12 @@
"@modelcontextprotocol/sdk": "^1.5.0", "@modelcontextprotocol/sdk": "^1.5.0",
"axios": "^1.7.2", "axios": "^1.7.2",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"express": "^5.2.1", "express": "^5.2.1"
"zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/node": "^20.14.10", "@types/node": "^20.14.10",
"typescript": "^5.5.3", "tsx": "^4.19.2",
"tsx": "^4.19.2" "typescript": "^5.5.3"
} }
} }

View File

@@ -1,6 +1,7 @@
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from 'express'; import express from 'express';
import crypto from 'crypto';
import { import {
CallToolRequestSchema, CallToolRequestSchema,
ListToolsRequestSchema, ListToolsRequestSchema,
@@ -251,17 +252,34 @@ async function run() {
console.error('Umami MCP server is running on stdio'); console.error('Umami MCP server is running on stdio');
} else { } else {
const app = express(); const app = express();
let transport: SSEServerTransport | null = null; const transports = new Map<string, SSEServerTransport>();
app.use((req, _res, next) => {
console.error(`${req.method} ${req.url}`);
next();
});
app.get('/sse', async (req, res) => { app.get('/sse', async (req, res) => {
console.error('New SSE connection established'); const sessionId = crypto.randomUUID();
transport = new SSEServerTransport('/message', res); console.error(`New SSE connection: ${sessionId}`);
const transport = new SSEServerTransport(`/message/${sessionId}`, res);
transports.set(sessionId, transport);
req.on('close', () => {
console.error(`SSE connection closed: ${sessionId}`);
transports.delete(sessionId);
});
await server.connect(transport); await server.connect(transport);
}); });
app.post('/message', async (req, res) => { app.post('/message/:sessionId', async (req, res) => {
const { sessionId } = req.params;
const transport = transports.get(sessionId as string);
if (!transport) { if (!transport) {
res.status(400).send('No active SSE connection'); console.error(`No transport found for session: ${sessionId}`);
res.status(400).send('No active SSE connection for this session');
return; return;
} }
await transport.handlePostMessage(req, res); await transport.handlePostMessage(req, res);

View File

@@ -4,8 +4,8 @@ import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url)); const __dirname = fileURLToPath(new URL('.', import.meta.url));
config({ path: resolve(__dirname, '../../../.env.local') }); config({ quiet: true, path: resolve(__dirname, '../../../.env.local') });
config({ path: resolve(__dirname, '../../../.env') }); config({ quiet: true, path: resolve(__dirname, '../../../.env') });
import('./index.js').catch(err => { import('./index.js').catch(err => {
console.error('Failed to start Umami MCP Server:', err); console.error('Failed to start Umami MCP Server:', err);

125
pnpm-lock.yaml generated
View File

@@ -395,9 +395,6 @@ importers:
express: express:
specifier: ^5.2.1 specifier: ^5.2.1
version: 5.2.1 version: 5.2.1
zod:
specifier: ^3.23.8
version: 3.25.76
devDependencies: devDependencies:
'@types/express': '@types/express':
specifier: ^5.0.6 specifier: ^5.0.6
@@ -423,9 +420,6 @@ importers:
express: express:
specifier: ^5.2.1 specifier: ^5.2.1
version: 5.2.1 version: 5.2.1
zod:
specifier: ^3.23.8
version: 3.25.76
devDependencies: devDependencies:
'@types/express': '@types/express':
specifier: ^5.0.6 specifier: ^5.0.6
@@ -483,6 +477,52 @@ importers:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.9.3 version: 5.9.3
packages/kabelfachmann-mcp:
dependencies:
'@modelcontextprotocol/sdk':
specifier: ^1.5.0
version: 1.27.1(zod@3.25.76)
'@qdrant/js-client-rest':
specifier: ^1.12.0
version: 1.17.0(typescript@5.9.3)
'@xenova/transformers':
specifier: ^2.17.2
version: 2.17.2
dotenv:
specifier: ^17.3.1
version: 17.3.1
express:
specifier: ^5.2.1
version: 5.2.1
node-fetch:
specifier: ^3.3.2
version: 3.3.2
onnxruntime-node:
specifier: ^1.14.0
version: 1.14.0
pdf-parse:
specifier: ^1.1.1
version: 1.1.4
zod:
specifier: ^3.23.8
version: 3.25.76
devDependencies:
'@types/express':
specifier: ^5.0.6
version: 5.0.6
'@types/node':
specifier: ^20.14.10
version: 20.19.33
'@types/pdf-parse':
specifier: ^1.1.4
version: 1.1.5
tsx:
specifier: ^4.19.1
version: 4.21.0
typescript:
specifier: ^5.5.3
version: 5.9.3
packages/klz-payload-mcp: packages/klz-payload-mcp:
dependencies: dependencies:
'@modelcontextprotocol/sdk': '@modelcontextprotocol/sdk':
@@ -497,9 +537,6 @@ importers:
express: express:
specifier: ^5.2.1 specifier: ^5.2.1
version: 5.2.1 version: 5.2.1
zod:
specifier: ^3.23.8
version: 3.25.76
devDependencies: devDependencies:
'@types/express': '@types/express':
specifier: ^5.0.6 specifier: ^5.0.6
@@ -584,6 +621,9 @@ importers:
express: express:
specifier: ^5.2.1 specifier: ^5.2.1
version: 5.2.1 version: 5.2.1
onnxruntime-node:
specifier: ^1.14.0
version: 1.14.0
zod: zod:
specifier: ^3.23.8 specifier: ^3.23.8
version: 3.25.76 version: 3.25.76
@@ -919,9 +959,6 @@ importers:
express: express:
specifier: ^5.2.1 specifier: ^5.2.1
version: 5.2.1 version: 5.2.1
zod:
specifier: ^3.23.8
version: 3.25.76
devDependencies: devDependencies:
'@types/express': '@types/express':
specifier: ^5.0.6 specifier: ^5.0.6
@@ -974,9 +1011,6 @@ importers:
express: express:
specifier: ^5.2.1 specifier: ^5.2.1
version: 5.2.1 version: 5.2.1
zod:
specifier: ^3.23.8
version: 3.25.76
devDependencies: devDependencies:
'@types/express': '@types/express':
specifier: ^5.0.6 specifier: ^5.0.6
@@ -3469,6 +3503,9 @@ packages:
'@types/parse-json@4.0.2': '@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
'@types/pdf-parse@1.1.5':
resolution: {integrity: sha512-kBfrSXsloMnUJOKi25s3+hRmkycHfLK6A09eRGqF/N8BkQoPUmaCr+q8Cli5FnfohEz/rsv82zAiPz/LXtOGhA==}
'@types/pg-pool@2.0.7': '@types/pg-pool@2.0.7':
resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==} resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==}
@@ -4602,6 +4639,10 @@ packages:
resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==}
engines: {node: '>=12'} engines: {node: '>=12'}
data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
data-uri-to-buffer@6.0.2: data-uri-to-buffer@6.0.2:
resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
@@ -5190,6 +5231,10 @@ packages:
picomatch: picomatch:
optional: true optional: true
fetch-blob@3.2.0:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
fflate@0.8.2: fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
@@ -5300,6 +5345,10 @@ packages:
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
engines: {node: '>= 12.20'} engines: {node: '>= 12.20'}
formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
forwarded-parse@2.1.2: forwarded-parse@2.1.2:
resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
@@ -5422,6 +5471,7 @@ packages:
git-raw-commits@4.0.0: git-raw-commits@4.0.0:
resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==}
engines: {node: '>=16'} engines: {node: '>=16'}
deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead.
hasBin: true hasBin: true
git-sha1@0.1.2: git-sha1@0.1.2:
@@ -6521,6 +6571,9 @@ packages:
engines: {node: '>=10.5.0'} engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead deprecated: Use your platform's native DOMException instead
node-ensure@0.0.0:
resolution: {integrity: sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==}
node-fetch@2.7.0: node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
engines: {node: 4.x || >=6.0.0} engines: {node: 4.x || >=6.0.0}
@@ -6530,6 +6583,10 @@ packages:
encoding: encoding:
optional: true optional: true
node-fetch@3.3.2:
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
node-releases@2.0.27: node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
@@ -6791,6 +6848,10 @@ packages:
peerDependencies: peerDependencies:
graphql: ^16.8.1 graphql: ^16.8.1
pdf-parse@1.1.4:
resolution: {integrity: sha512-XRIRcLgk6ZnUbsHsYXExMw+krrPE81hJ6FQPLdBNhhBefqIQKXu/WeTgNBGSwPrfU0v+UCEwn7AoAUOsVKHFvQ==}
engines: {node: '>=6.8.1'}
peberminta@0.9.0: peberminta@0.9.0:
resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==}
@@ -8331,6 +8392,10 @@ packages:
wcwidth@1.0.1: wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
web-streams-polyfill@3.3.3:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
web-streams-polyfill@4.0.0-beta.3: web-streams-polyfill@4.0.0-beta.3:
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
@@ -11322,6 +11387,10 @@ snapshots:
'@types/parse-json@4.0.2': {} '@types/parse-json@4.0.2': {}
'@types/pdf-parse@1.1.5':
dependencies:
'@types/node': 20.19.33
'@types/pg-pool@2.0.7': '@types/pg-pool@2.0.7':
dependencies: dependencies:
'@types/pg': 8.15.6 '@types/pg': 8.15.6
@@ -12540,6 +12609,8 @@ snapshots:
dargs@8.1.0: {} dargs@8.1.0: {}
data-uri-to-buffer@4.0.1: {}
data-uri-to-buffer@6.0.2: {} data-uri-to-buffer@6.0.2: {}
data-urls@5.0.0: data-urls@5.0.0:
@@ -13322,6 +13393,11 @@ snapshots:
optionalDependencies: optionalDependencies:
picomatch: 4.0.3 picomatch: 4.0.3
fetch-blob@3.2.0:
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 3.3.3
fflate@0.8.2: {} fflate@0.8.2: {}
figlet@1.10.0: figlet@1.10.0:
@@ -13452,6 +13528,10 @@ snapshots:
node-domexception: 1.0.0 node-domexception: 1.0.0
web-streams-polyfill: 4.0.0-beta.3 web-streams-polyfill: 4.0.0-beta.3
formdata-polyfill@4.0.10:
dependencies:
fetch-blob: 3.2.0
forwarded-parse@2.1.2: {} forwarded-parse@2.1.2: {}
forwarded@0.2.0: {} forwarded@0.2.0: {}
@@ -14699,10 +14779,18 @@ snapshots:
node-domexception@1.0.0: {} node-domexception@1.0.0: {}
node-ensure@0.0.0: {}
node-fetch@2.7.0: node-fetch@2.7.0:
dependencies: dependencies:
whatwg-url: 5.0.0 whatwg-url: 5.0.0
node-fetch@3.3.2:
dependencies:
data-uri-to-buffer: 4.0.1
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
node-releases@2.0.27: {} node-releases@2.0.27: {}
normalize-path@3.0.0: {} normalize-path@3.0.0: {}
@@ -14794,7 +14882,6 @@ snapshots:
onnxruntime-node@1.14.0: onnxruntime-node@1.14.0:
dependencies: dependencies:
onnxruntime-common: 1.14.0 onnxruntime-common: 1.14.0
optional: true
onnxruntime-web@1.14.0: onnxruntime-web@1.14.0:
dependencies: dependencies:
@@ -15022,6 +15109,10 @@ snapshots:
- typescript - typescript
- utf-8-validate - utf-8-validate
pdf-parse@1.1.4:
dependencies:
node-ensure: 0.0.0
peberminta@0.9.0: {} peberminta@0.9.0: {}
peek-readable@5.4.2: {} peek-readable@5.4.2: {}
@@ -17005,6 +17096,8 @@ snapshots:
dependencies: dependencies:
defaults: 1.0.4 defaults: 1.0.4
web-streams-polyfill@3.3.3: {}
web-streams-polyfill@4.0.0-beta.3: {} web-streams-polyfill@4.0.0-beta.3: {}
webidl-conversions@3.0.1: {} webidl-conversions@3.0.1: {}