feat(mcps): add klz-payload-mcp on port 3006 for customer data
This commit is contained in:
@@ -35,6 +35,14 @@ module.exports = {
|
||||
cwd: './packages/glitchtip-mcp',
|
||||
watch: false,
|
||||
},
|
||||
{
|
||||
name: 'klz-payload-mcp',
|
||||
script: 'node',
|
||||
args: 'dist/start.js',
|
||||
cwd: './packages/klz-payload-mcp',
|
||||
watch: false,
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
|
||||
25
packages/klz-payload-mcp/package.json
Normal file
25
packages/klz-payload-mcp/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@mintel/klz-payload-mcp",
|
||||
"version": "1.9.10",
|
||||
"description": "KLZ PayloadCMS MCP server for technical product data and leads",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/start.js",
|
||||
"dev": "tsx watch src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.27.1",
|
||||
"axios": "^1.7.2",
|
||||
"dotenv": "^17.3.1",
|
||||
"express": "^5.2.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/node": "^20.14.10",
|
||||
"typescript": "^5.5.3",
|
||||
"tsx": "^4.19.2"
|
||||
}
|
||||
}
|
||||
137
packages/klz-payload-mcp/src/index.ts
Normal file
137
packages/klz-payload-mcp/src/index.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
||||
import express, { Request, Response } from 'express';
|
||||
import {
|
||||
CallToolRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
Tool,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import axios from "axios";
|
||||
import https from "https";
|
||||
|
||||
const PAYLOAD_URL = process.env.PAYLOAD_URL || "https://klz.infra.mintel.me";
|
||||
const PAYLOAD_API_KEY = process.env.PAYLOAD_API_KEY;
|
||||
|
||||
if (!PAYLOAD_API_KEY) {
|
||||
console.error("Warning: PAYLOAD_API_KEY is not set. API calls will fail.");
|
||||
}
|
||||
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false, // For internal infra
|
||||
});
|
||||
|
||||
const payloadClient = axios.create({
|
||||
baseURL: `${PAYLOAD_URL}/api`,
|
||||
headers: { Authorization: `users API-Key ${PAYLOAD_API_KEY}` },
|
||||
httpsAgent
|
||||
});
|
||||
|
||||
const SEARCH_PRODUCTS_TOOL: Tool = {
|
||||
name: "payload_search_products",
|
||||
description: "Search for technical product specifications (cables, cross-sections) in KLZ Payload CMS",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: { type: "string", description: "Search query or part number" },
|
||||
limit: { type: "number", description: "Maximum number of results" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const LIST_LEADS_TOOL: Tool = {
|
||||
name: "payload_list_leads",
|
||||
description: "List recent lead inquiries and contact requests",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: { type: "number", description: "Maximum number of leads" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const server = new Server(
|
||||
{ name: "klz-payload-mcp", version: "1.0.0" },
|
||||
{ capabilities: { tools: {} } }
|
||||
);
|
||||
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: [
|
||||
SEARCH_PRODUCTS_TOOL,
|
||||
LIST_LEADS_TOOL,
|
||||
],
|
||||
}));
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
if (request.params.name === "payload_search_products") {
|
||||
const { query, limit = 10 } = request.params.arguments as any;
|
||||
try {
|
||||
const res = await payloadClient.get('/products', {
|
||||
params: {
|
||||
where: query ? {
|
||||
or: [
|
||||
{ title: { contains: query } },
|
||||
{ slug: { contains: query } },
|
||||
{ description: { contains: query } }
|
||||
]
|
||||
} : {},
|
||||
limit
|
||||
}
|
||||
});
|
||||
return { content: [{ type: "text", text: JSON.stringify(res.data.docs, null, 2) }] };
|
||||
} catch (e: any) {
|
||||
return { isError: true, content: [{ type: "text", text: `Error: ${e.message}` }] };
|
||||
}
|
||||
}
|
||||
|
||||
if (request.params.name === "payload_list_leads") {
|
||||
const { limit = 10 } = request.params.arguments as any;
|
||||
try {
|
||||
const res = await payloadClient.get('/leads', {
|
||||
params: { limit, sort: '-createdAt' }
|
||||
});
|
||||
return { content: [{ type: "text", text: JSON.stringify(res.data.docs, null, 2) }] };
|
||||
} catch (e: any) {
|
||||
return { isError: true, content: [{ type: "text", text: `Error: ${e.message}` }] };
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Unknown tool: ${request.params.name}`);
|
||||
});
|
||||
|
||||
async function run() {
|
||||
const isStdio = process.argv.includes('--stdio');
|
||||
|
||||
if (isStdio) {
|
||||
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error('KLZ Payload MCP server is running on stdio');
|
||||
} else {
|
||||
const app = express();
|
||||
let transport: SSEServerTransport | null = null;
|
||||
|
||||
app.get('/sse', async (req: Request, res: Response) => {
|
||||
console.error('New SSE connection established');
|
||||
transport = new SSEServerTransport('/message', res);
|
||||
await server.connect(transport);
|
||||
});
|
||||
|
||||
app.post('/message', async (req: Request, res: Response) => {
|
||||
if (!transport) {
|
||||
res.status(400).send('No active SSE connection');
|
||||
return;
|
||||
}
|
||||
await transport.handlePostMessage(req, res);
|
||||
});
|
||||
|
||||
const PORT = process.env.KLZ_PAYLOAD_MCP_PORT || 3006;
|
||||
app.listen(PORT, () => {
|
||||
console.error(`KLZ Payload MCP server running on http://localhost:${PORT}/sse`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
run().catch((err) => {
|
||||
console.error("Fatal error:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
13
packages/klz-payload-mcp/src/start.ts
Normal file
13
packages/klz-payload-mcp/src/start.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { config } from 'dotenv';
|
||||
import { resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
config({ path: resolve(__dirname, '../../../.env.local') });
|
||||
config({ path: resolve(__dirname, '../../../.env') });
|
||||
|
||||
import('./index.js').catch(err => {
|
||||
console.error('Failed to start KLZ Payload MCP Server:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
16
packages/klz-payload-mcp/tsconfig.json
Normal file
16
packages/klz-payload-mcp/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
31
pnpm-lock.yaml
generated
31
pnpm-lock.yaml
generated
@@ -483,6 +483,37 @@ importers:
|
||||
specifier: ^5.0.0
|
||||
version: 5.9.3
|
||||
|
||||
packages/klz-payload-mcp:
|
||||
dependencies:
|
||||
'@modelcontextprotocol/sdk':
|
||||
specifier: ^1.27.1
|
||||
version: 1.27.1(zod@3.25.76)
|
||||
axios:
|
||||
specifier: ^1.7.2
|
||||
version: 1.13.5
|
||||
dotenv:
|
||||
specifier: ^17.3.1
|
||||
version: 17.3.1
|
||||
express:
|
||||
specifier: ^5.2.1
|
||||
version: 5.2.1
|
||||
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
|
||||
tsx:
|
||||
specifier: ^4.19.2
|
||||
version: 4.21.0
|
||||
typescript:
|
||||
specifier: ^5.5.3
|
||||
version: 5.9.3
|
||||
|
||||
packages/mail:
|
||||
dependencies:
|
||||
'@react-email/components':
|
||||
|
||||
Reference in New Issue
Block a user