@sweetoburrito/backstage-plugin-ai-assistant-backend 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants/prompts.cjs.js +12 -6
- package/dist/constants/prompts.cjs.js.map +1 -1
- package/dist/database/user-settings-store.cjs.js +42 -0
- package/dist/database/user-settings-store.cjs.js.map +1 -0
- package/dist/plugin.cjs.js +14 -6
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/services/chat.cjs.js +23 -3
- package/dist/services/chat.cjs.js.map +1 -1
- package/dist/services/mcp/helpers.cjs.js +31 -0
- package/dist/services/mcp/helpers.cjs.js.map +1 -0
- package/dist/services/mcp/index.cjs.js +150 -0
- package/dist/services/mcp/index.cjs.js.map +1 -0
- package/dist/services/router/chat.cjs.js +17 -5
- package/dist/services/router/chat.cjs.js.map +1 -1
- package/dist/services/router/index.cjs.js +2 -2
- package/dist/services/router/index.cjs.js.map +1 -1
- package/dist/services/router/settings/index.cjs.js +51 -0
- package/dist/services/router/settings/index.cjs.js.map +1 -0
- package/dist/services/router/{mcp.cjs.js → settings/mcp.cjs.js} +3 -3
- package/dist/services/router/settings/mcp.cjs.js.map +1 -0
- package/dist/services/user-settings.cjs.js +34 -0
- package/dist/services/user-settings.cjs.js.map +1 -0
- package/dist/{services/tools → tools}/searchKnowledge.cjs.js +12 -2
- package/dist/tools/searchKnowledge.cjs.js.map +1 -0
- package/migrations/20251124_user_settings.js +131 -0
- package/package.json +3 -3
- package/dist/database/mcp-store.cjs.js +0 -46
- package/dist/database/mcp-store.cjs.js.map +0 -1
- package/dist/services/mcp.cjs.js +0 -119
- package/dist/services/mcp.cjs.js.map +0 -1
- package/dist/services/router/mcp.cjs.js.map +0 -1
- package/dist/services/tools/searchKnowledge.cjs.js.map +0 -1
|
@@ -22,21 +22,28 @@ async function createChatRouter(options) {
|
|
|
22
22
|
})
|
|
23
23
|
),
|
|
24
24
|
modelId: z__default.default.string(),
|
|
25
|
-
conversationId: z__default.default.
|
|
26
|
-
stream: z__default.default.boolean().optional()
|
|
25
|
+
conversationId: z__default.default.uuid().optional().default(uuid.v4),
|
|
26
|
+
stream: z__default.default.boolean().optional(),
|
|
27
|
+
tools: z__default.default.array(
|
|
28
|
+
z__default.default.object({
|
|
29
|
+
name: z__default.default.string(),
|
|
30
|
+
provider: z__default.default.string()
|
|
31
|
+
})
|
|
32
|
+
).optional()
|
|
27
33
|
});
|
|
28
34
|
router.post(
|
|
29
35
|
"/message",
|
|
30
36
|
validation.validation(messageSchema, "body"),
|
|
31
37
|
async (req, res) => {
|
|
32
|
-
const { messages, conversationId, modelId, stream } = req.body;
|
|
38
|
+
const { messages, conversationId, modelId, stream, tools } = req.body;
|
|
33
39
|
const userCredentials = await httpAuth.credentials(req);
|
|
34
40
|
const responseMessages = await chat.prompt({
|
|
35
41
|
modelId,
|
|
36
42
|
messages,
|
|
37
43
|
conversationId,
|
|
38
44
|
stream,
|
|
39
|
-
userCredentials
|
|
45
|
+
userCredentials,
|
|
46
|
+
tools
|
|
40
47
|
});
|
|
41
48
|
res.json({
|
|
42
49
|
messages: responseMessages,
|
|
@@ -45,7 +52,7 @@ async function createChatRouter(options) {
|
|
|
45
52
|
}
|
|
46
53
|
);
|
|
47
54
|
const chatSchema = z__default.default.object({
|
|
48
|
-
id: z__default.default.
|
|
55
|
+
id: z__default.default.uuid()
|
|
49
56
|
});
|
|
50
57
|
router.get("/conversations", async (req, res) => {
|
|
51
58
|
const credentials = await httpAuth.credentials(req);
|
|
@@ -55,6 +62,11 @@ async function createChatRouter(options) {
|
|
|
55
62
|
});
|
|
56
63
|
res.json({ conversations });
|
|
57
64
|
});
|
|
65
|
+
router.get("/tools", async (req, res) => {
|
|
66
|
+
const credentials = await httpAuth.credentials(req);
|
|
67
|
+
const tools = await chat.getAvailableTools({ credentials });
|
|
68
|
+
res.json({ tools });
|
|
69
|
+
});
|
|
58
70
|
router.get("/:id", validation.validation(chatSchema, "params"), async (req, res) => {
|
|
59
71
|
const { id } = req.params;
|
|
60
72
|
const credentials = await httpAuth.credentials(req);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.cjs.js","sources":["../../../src/services/router/chat.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { ChatService } from '../chat';\nimport z from 'zod';\nimport { validation } from './middleware/validation';\nimport { v4 as uuid } from 'uuid';\nimport {\n DatabaseService,\n HttpAuthService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\n\nexport type ChatRouterOptions = {\n chat: ChatService;\n database: DatabaseService;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n};\n\nexport async function createChatRouter(\n options: ChatRouterOptions,\n): Promise<express.Router> {\n const { chat, httpAuth, userInfo } = options;\n\n const router = Router();\n\n const messageSchema = z.object({\n messages: z.array(\n z.object({\n id: z.string().uuid().optional().default(uuid),\n role: z.string(),\n content: z.string(),\n }),\n ),\n modelId: z.string(),\n conversationId: z.
|
|
1
|
+
{"version":3,"file":"chat.cjs.js","sources":["../../../src/services/router/chat.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { ChatService } from '../chat';\nimport z from 'zod';\nimport { validation } from './middleware/validation';\nimport { v4 as uuid } from 'uuid';\nimport {\n DatabaseService,\n HttpAuthService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\n\nexport type ChatRouterOptions = {\n chat: ChatService;\n database: DatabaseService;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n};\n\nexport async function createChatRouter(\n options: ChatRouterOptions,\n): Promise<express.Router> {\n const { chat, httpAuth, userInfo } = options;\n\n const router = Router();\n\n const messageSchema = z.object({\n messages: z.array(\n z.object({\n id: z.string().uuid().optional().default(uuid),\n role: z.string(),\n content: z.string(),\n }),\n ),\n modelId: z.string(),\n conversationId: z.uuid().optional().default(uuid),\n stream: z.boolean().optional(),\n tools: z\n .array(\n z.object({\n name: z.string(),\n provider: z.string(),\n }),\n )\n .optional(),\n });\n\n router.post(\n '/message',\n validation(messageSchema, 'body'),\n async (req, res) => {\n const { messages, conversationId, modelId, stream, tools } = req.body;\n\n const userCredentials = await httpAuth.credentials(req);\n\n const responseMessages = await chat.prompt({\n modelId,\n messages,\n conversationId,\n stream,\n userCredentials,\n tools,\n });\n\n res.json({\n messages: responseMessages,\n conversationId,\n });\n },\n );\n\n const chatSchema = z.object({\n id: z.uuid(),\n });\n\n router.get('/conversations', async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n\n const conversations = await chat.getConversations({\n userEntityRef,\n });\n\n res.json({ conversations });\n });\n\n router.get('/tools', async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n\n const tools = await chat.getAvailableTools({ credentials });\n\n res.json({ tools });\n });\n\n router.get('/:id', validation(chatSchema, 'params'), async (req, res) => {\n const { id } = req.params;\n\n const credentials = await httpAuth.credentials(req);\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n\n const conversation = await chat.getConversation({\n conversationId: id,\n userEntityRef,\n });\n\n res.json({ conversation });\n });\n\n router.post(\n '/message/:messageId/score',\n validation(\n z.object({\n messageId: z.string().uuid(),\n }),\n 'params',\n ),\n validation(\n z.object({\n score: z.number(),\n }),\n 'body',\n ),\n async (req, res) => {\n const { messageId } = req.params;\n const { score } = req.body;\n\n await chat.scoreMessage(messageId, score);\n\n res.status(204).end();\n },\n );\n\n return router;\n}\n"],"names":["Router","z","uuid","validation"],"mappings":"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"}
|
|
@@ -5,8 +5,8 @@ var Router = require('express-promise-router');
|
|
|
5
5
|
var chat = require('./chat.cjs.js');
|
|
6
6
|
var models = require('./models.cjs.js');
|
|
7
7
|
var rootHttpRouter = require('@backstage/backend-defaults/rootHttpRouter');
|
|
8
|
-
var mcp = require('./mcp.cjs.js');
|
|
9
8
|
var summary = require('./summary.cjs.js');
|
|
9
|
+
var index = require('./settings/index.cjs.js');
|
|
10
10
|
|
|
11
11
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
12
12
|
|
|
@@ -18,8 +18,8 @@ async function createRouter(options) {
|
|
|
18
18
|
router.use(express__default.default.json());
|
|
19
19
|
router.use("/chat", await chat.createChatRouter(options));
|
|
20
20
|
router.use("/models", await models.createModelRouter(options));
|
|
21
|
-
router.use("/mcp", await mcp.createMcpRouter(options));
|
|
22
21
|
router.use("/summary", await summary.createSummaryRouter(options));
|
|
22
|
+
router.use("/settings", await index.createSettingsRouter(options));
|
|
23
23
|
const middleware = rootHttpRouter.MiddlewareFactory.create(options);
|
|
24
24
|
router.use(middleware.error());
|
|
25
25
|
return router;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../../src/services/router/index.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { createChatRouter, ChatRouterOptions } from './chat';\nimport { createModelRouter } from './models';\nimport {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport {
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../src/services/router/index.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { createChatRouter, ChatRouterOptions } from './chat';\nimport { createModelRouter, ModelRouterOptions } from './models';\nimport {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { SummaryRouterOptions, createSummaryRouter } from './summary';\nimport { createSettingsRouter, SettingsRouterOptions } from './settings';\n\nexport type RouterOptions = ChatRouterOptions &\n SummaryRouterOptions &\n ModelRouterOptions &\n SettingsRouterOptions & {\n config: RootConfigService;\n logger: LoggerService;\n };\n\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const router = Router();\n router.use(express.json());\n\n router.use('/chat', await createChatRouter(options));\n router.use('/models', await createModelRouter(options));\n router.use('/summary', await createSummaryRouter(options));\n router.use('/settings', await createSettingsRouter(options));\n\n const middleware = MiddlewareFactory.create(options);\n\n router.use(middleware.error());\n\n return router;\n}\n"],"names":["Router","express","createChatRouter","createModelRouter","createSummaryRouter","createSettingsRouter","MiddlewareFactory"],"mappings":";;;;;;;;;;;;;;;AAoBA,eAAsB,aACpB,OAAA,EACyB;AACzB,EAAA,MAAM,SAASA,uBAAA,EAAO;AACtB,EAAA,MAAA,CAAO,GAAA,CAAIC,wBAAA,CAAQ,IAAA,EAAM,CAAA;AAEzB,EAAA,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,MAAMC,qBAAA,CAAiB,OAAO,CAAC,CAAA;AACnD,EAAA,MAAA,CAAO,GAAA,CAAI,SAAA,EAAW,MAAMC,wBAAA,CAAkB,OAAO,CAAC,CAAA;AACtD,EAAA,MAAA,CAAO,GAAA,CAAI,UAAA,EAAY,MAAMC,2BAAA,CAAoB,OAAO,CAAC,CAAA;AACzD,EAAA,MAAA,CAAO,GAAA,CAAI,WAAA,EAAa,MAAMC,0BAAA,CAAqB,OAAO,CAAC,CAAA;AAE3D,EAAA,MAAM,UAAA,GAAaC,gCAAA,CAAkB,MAAA,CAAO,OAAO,CAAA;AAEnD,EAAA,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,KAAA,EAAO,CAAA;AAE7B,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var express = require('express');
|
|
4
|
+
var Router = require('express-promise-router');
|
|
5
|
+
var rootHttpRouter = require('@backstage/backend-defaults/rootHttpRouter');
|
|
6
|
+
var mcp = require('./mcp.cjs.js');
|
|
7
|
+
var z = require('zod');
|
|
8
|
+
var validation = require('../middleware/validation.cjs.js');
|
|
9
|
+
|
|
10
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
var express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
|
13
|
+
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
14
|
+
var z__default = /*#__PURE__*/_interopDefaultCompat(z);
|
|
15
|
+
|
|
16
|
+
async function createSettingsRouter(options) {
|
|
17
|
+
const { userSettings, httpAuth } = options;
|
|
18
|
+
const router = Router__default.default();
|
|
19
|
+
router.use(express__default.default.json());
|
|
20
|
+
router.use("/mcp", await mcp.createMcpRouter(options));
|
|
21
|
+
const settingsSchema = z__default.default.object({
|
|
22
|
+
type: z__default.default.string()
|
|
23
|
+
});
|
|
24
|
+
router.get("/", validation.validation(settingsSchema, "query"), async (req, res) => {
|
|
25
|
+
const credentials = await httpAuth.credentials(req);
|
|
26
|
+
const { type } = req.query;
|
|
27
|
+
const settings = await userSettings.getSettingsForType(
|
|
28
|
+
credentials,
|
|
29
|
+
type
|
|
30
|
+
);
|
|
31
|
+
res.json({
|
|
32
|
+
settings
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
const setSettingsSchema = z__default.default.object({
|
|
36
|
+
type: z__default.default.string(),
|
|
37
|
+
settings: z__default.default.any()
|
|
38
|
+
});
|
|
39
|
+
router.patch("/", validation.validation(setSettingsSchema, "body"), async (req, res) => {
|
|
40
|
+
const credentials = await httpAuth.credentials(req);
|
|
41
|
+
const { type, settings } = req.body;
|
|
42
|
+
await userSettings.setSettingsForType(credentials, type, settings);
|
|
43
|
+
res.status(204).send();
|
|
44
|
+
});
|
|
45
|
+
const middleware = rootHttpRouter.MiddlewareFactory.create(options);
|
|
46
|
+
router.use(middleware.error());
|
|
47
|
+
return router;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
exports.createSettingsRouter = createSettingsRouter;
|
|
51
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../../src/services/router/settings/index.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { createMcpRouter, McpRouterOptions } from './mcp';\nimport { UserSettingsService } from '../../user-settings';\nimport z from 'zod';\nimport { validation } from '../middleware/validation';\n\nexport type SettingsRouterOptions = McpRouterOptions & {\n config: RootConfigService;\n logger: LoggerService;\n userSettings: UserSettingsService;\n};\n\nexport async function createSettingsRouter(\n options: SettingsRouterOptions,\n): Promise<express.Router> {\n const { userSettings, httpAuth } = options;\n const router = Router();\n router.use(express.json());\n\n router.use('/mcp', await createMcpRouter(options));\n\n const settingsSchema = z.object({\n type: z.string(),\n });\n\n router.get('/', validation(settingsSchema, 'query'), async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n\n const { type } = req.query;\n\n const settings = await userSettings.getSettingsForType(\n credentials,\n type as string,\n );\n\n res.json({\n settings,\n });\n });\n\n const setSettingsSchema = z.object({\n type: z.string(),\n settings: z.any(),\n });\n\n router.patch('/', validation(setSettingsSchema, 'body'), async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { type, settings } = req.body;\n\n await userSettings.setSettingsForType(credentials, type, settings);\n res.status(204).send();\n });\n\n const middleware = MiddlewareFactory.create(options);\n\n router.use(middleware.error());\n\n return router;\n}\n"],"names":["Router","express","createMcpRouter","z","validation","MiddlewareFactory"],"mappings":";;;;;;;;;;;;;;;AAkBA,eAAsB,qBACpB,OAAA,EACyB;AACzB,EAAA,MAAM,EAAE,YAAA,EAAc,QAAA,EAAS,GAAI,OAAA;AACnC,EAAA,MAAM,SAASA,uBAAA,EAAO;AACtB,EAAA,MAAA,CAAO,GAAA,CAAIC,wBAAA,CAAQ,IAAA,EAAM,CAAA;AAEzB,EAAA,MAAA,CAAO,GAAA,CAAI,MAAA,EAAQ,MAAMC,mBAAA,CAAgB,OAAO,CAAC,CAAA;AAEjD,EAAA,MAAM,cAAA,GAAiBC,mBAAE,MAAA,CAAO;AAAA,IAC9B,IAAA,EAAMA,mBAAE,MAAA;AAAO,GAChB,CAAA;AAED,EAAA,MAAA,CAAO,GAAA,CAAI,KAAKC,qBAAA,CAAW,cAAA,EAAgB,OAAO,CAAA,EAAG,OAAO,KAAK,GAAA,KAAQ;AACvE,IAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,WAAA,CAAY,GAAG,CAAA;AAElD,IAAA,MAAM,EAAE,IAAA,EAAK,GAAI,GAAA,CAAI,KAAA;AAErB,IAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa,kBAAA;AAAA,MAClC,WAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,GAAA,CAAI,IAAA,CAAK;AAAA,MACP;AAAA,KACD,CAAA;AAAA,EACH,CAAC,CAAA;AAED,EAAA,MAAM,iBAAA,GAAoBD,mBAAE,MAAA,CAAO;AAAA,IACjC,IAAA,EAAMA,mBAAE,MAAA,EAAO;AAAA,IACf,QAAA,EAAUA,mBAAE,GAAA;AAAI,GACjB,CAAA;AAED,EAAA,MAAA,CAAO,KAAA,CAAM,KAAKC,qBAAA,CAAW,iBAAA,EAAmB,MAAM,CAAA,EAAG,OAAO,KAAK,GAAA,KAAQ;AAC3E,IAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,WAAA,CAAY,GAAG,CAAA;AAClD,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,GAAA,CAAI,IAAA;AAE/B,IAAA,MAAM,YAAA,CAAa,kBAAA,CAAmB,WAAA,EAAa,IAAA,EAAM,QAAQ,CAAA;AACjE,IAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,EAAK;AAAA,EACvB,CAAC,CAAA;AAED,EAAA,MAAM,UAAA,GAAaC,gCAAA,CAAkB,MAAA,CAAO,OAAO,CAAA;AAEnD,EAAA,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,KAAA,EAAO,CAAA;AAE7B,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var Router = require('express-promise-router');
|
|
4
4
|
var z = require('zod');
|
|
5
|
-
var validation = require('
|
|
5
|
+
var validation = require('../middleware/validation.cjs.js');
|
|
6
6
|
|
|
7
7
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
8
8
|
|
|
@@ -27,7 +27,7 @@ async function createMcpRouter(options) {
|
|
|
27
27
|
const credentials = await httpAuth.credentials(req);
|
|
28
28
|
const { name, options: mcpOptions } = req.body;
|
|
29
29
|
try {
|
|
30
|
-
await mcp.
|
|
30
|
+
await mcp.setUserMcpServerConfig(credentials, {
|
|
31
31
|
name,
|
|
32
32
|
options: mcpOptions
|
|
33
33
|
});
|
|
@@ -47,7 +47,7 @@ async function createMcpRouter(options) {
|
|
|
47
47
|
const credentials = await httpAuth.credentials(req);
|
|
48
48
|
const { name, options: mcpOptions } = req.body;
|
|
49
49
|
try {
|
|
50
|
-
await mcp.
|
|
50
|
+
await mcp.setUserMcpServerConfig(credentials, {
|
|
51
51
|
name,
|
|
52
52
|
options: mcpOptions
|
|
53
53
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.cjs.js","sources":["../../../../src/services/router/settings/mcp.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { McpService } from '../../mcp';\nimport { HttpAuthService } from '@backstage/backend-plugin-api';\nimport z from 'zod';\nimport { validation } from '../middleware/validation';\n\nexport type McpRouterOptions = {\n mcp: McpService;\n httpAuth: HttpAuthService;\n};\n\nexport async function createMcpRouter(\n options: McpRouterOptions,\n): Promise<express.Router> {\n const { mcp, httpAuth } = options;\n const router = Router();\n\n router.get('/config', async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n\n const names = await mcp.getUserMcpServerConfigNames(credentials);\n\n res.json({\n names,\n });\n });\n\n const configSchema = z.object({\n name: z.string(),\n options: z.record(z.string(), z.any()),\n });\n\n router.post('/config', validation(configSchema, 'body'), async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { name, options: mcpOptions } = req.body;\n try {\n await mcp.setUserMcpServerConfig(credentials, {\n name,\n options: mcpOptions,\n });\n\n res.status(201).send();\n } catch (error) {\n if (error instanceof Error && error.name === 'McpConfigurationError') {\n res.status(400).json({ error: error.message });\n return;\n }\n throw error;\n }\n });\n\n router.patch(\n '/config',\n validation(configSchema, 'body'),\n async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { name, options: mcpOptions } = req.body;\n\n try {\n await mcp.setUserMcpServerConfig(credentials, {\n name,\n options: mcpOptions,\n });\n res.status(204).send();\n } catch (error) {\n if (error instanceof Error && error.name === 'McpConfigurationError') {\n res.status(400).json({ error: error.message });\n return;\n }\n throw error;\n }\n },\n );\n\n const deleteConfigSchema = z.object({\n name: z.string(),\n });\n\n router.delete(\n '/config',\n validation(deleteConfigSchema, 'body'),\n async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { name } = req.body;\n\n await mcp.deleteUserMcpServerConfig(credentials, name);\n\n res.status(204).send();\n },\n );\n\n return router;\n}\n"],"names":["Router","z","validation"],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var userSettingsStore = require('../database/user-settings-store.cjs.js');
|
|
4
|
+
|
|
5
|
+
const createUserSettingsService = async ({
|
|
6
|
+
userInfo,
|
|
7
|
+
database
|
|
8
|
+
}) => {
|
|
9
|
+
const userSettingsStore$1 = await userSettingsStore.UserSettingsStore.fromConfig({ database });
|
|
10
|
+
const getSettingsForType = async (credentials, type) => {
|
|
11
|
+
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
12
|
+
const settings = await userSettingsStore$1.getUserSettingsByType(
|
|
13
|
+
userEntityRef,
|
|
14
|
+
type
|
|
15
|
+
);
|
|
16
|
+
return settings ?? {};
|
|
17
|
+
};
|
|
18
|
+
const setSettingsForType = async (credentials, type, settings) => {
|
|
19
|
+
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
20
|
+
await userSettingsStore$1.setUserSettings(userEntityRef, type, settings);
|
|
21
|
+
};
|
|
22
|
+
const deleteSettingsForType = async (credentials, type) => {
|
|
23
|
+
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
24
|
+
await userSettingsStore$1.deleteUserSettings(userEntityRef, type);
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
getSettingsForType,
|
|
28
|
+
setSettingsForType,
|
|
29
|
+
deleteSettingsForType
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
exports.createUserSettingsService = createUserSettingsService;
|
|
34
|
+
//# sourceMappingURL=user-settings.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-settings.cjs.js","sources":["../../src/services/user-settings.ts"],"sourcesContent":["import {\n BackstageCredentials,\n DatabaseService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { UserSettingsStore } from '../database/user-settings-store';\n\ntype CreateUserSettingsServiceOptions = {\n userInfo: UserInfoService;\n database: DatabaseService;\n};\n\nexport type UserSettingsService = {\n getSettingsForType: (\n credentials: BackstageCredentials,\n type: string,\n ) => Promise<Record<string, unknown>>;\n setSettingsForType: (\n credentials: BackstageCredentials,\n type: string,\n settings: Record<string, unknown>,\n ) => Promise<void>;\n deleteSettingsForType: (\n credentials: BackstageCredentials,\n type: string,\n ) => Promise<void>;\n};\n\nexport const createUserSettingsService = async ({\n userInfo,\n database,\n}: CreateUserSettingsServiceOptions): Promise<UserSettingsService> => {\n const userSettingsStore = await UserSettingsStore.fromConfig({ database });\n\n const getSettingsForType: UserSettingsService['getSettingsForType'] = async (\n credentials,\n type,\n ) => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n const settings = await userSettingsStore.getUserSettingsByType(\n userEntityRef,\n type,\n );\n\n return settings ?? {};\n };\n\n const setSettingsForType: UserSettingsService['setSettingsForType'] = async (\n credentials,\n type,\n settings,\n ) => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n await userSettingsStore.setUserSettings(userEntityRef, type, settings);\n };\n\n const deleteSettingsForType: UserSettingsService['deleteSettingsForType'] =\n async (credentials, type) => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n await userSettingsStore.deleteUserSettings(userEntityRef, type);\n };\n\n return {\n getSettingsForType,\n setSettingsForType,\n deleteSettingsForType,\n };\n};\n"],"names":["userSettingsStore","UserSettingsStore"],"mappings":";;;;AA4BO,MAAM,4BAA4B,OAAO;AAAA,EAC9C,QAAA;AAAA,EACA;AACF,CAAA,KAAsE;AACpE,EAAA,MAAMA,sBAAoB,MAAMC,mCAAA,CAAkB,UAAA,CAAW,EAAE,UAAU,CAAA;AAEzE,EAAA,MAAM,kBAAA,GAAgE,OACpE,WAAA,EACA,IAAA,KACG;AACH,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAChE,IAAA,MAAM,QAAA,GAAW,MAAMD,mBAAA,CAAkB,qBAAA;AAAA,MACvC,aAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,OAAO,YAAY,EAAC;AAAA,EACtB,CAAA;AAEA,EAAA,MAAM,kBAAA,GAAgE,OACpE,WAAA,EACA,IAAA,EACA,QAAA,KACG;AACH,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAChE,IAAA,MAAMA,mBAAA,CAAkB,eAAA,CAAgB,aAAA,EAAe,IAAA,EAAM,QAAQ,CAAA;AAAA,EACvE,CAAA;AAEA,EAAA,MAAM,qBAAA,GACJ,OAAO,WAAA,EAAa,IAAA,KAAS;AAC3B,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAChE,IAAA,MAAMA,mBAAA,CAAkB,kBAAA,CAAmB,aAAA,EAAe,IAAI,CAAA;AAAA,EAChE,CAAA;AAEF,EAAA,OAAO;AAAA,IACL,kBAAA;AAAA,IACA,kBAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
|
|
@@ -19,6 +19,7 @@ Use this tool when users ask about:
|
|
|
19
19
|
- General questions about the company or internal information
|
|
20
20
|
|
|
21
21
|
Do NOT use for general knowledge that doesn't require company-specific information.`,
|
|
22
|
+
provider: "core",
|
|
22
23
|
schema: z__default.default.object({
|
|
23
24
|
query: z__default.default.string().describe("The query to search for."),
|
|
24
25
|
filter: z__default.default.object({
|
|
@@ -34,9 +35,18 @@ Do NOT use for general knowledge that doesn't require company-specific informati
|
|
|
34
35
|
amount
|
|
35
36
|
);
|
|
36
37
|
if (results.length === 0) {
|
|
37
|
-
return
|
|
38
|
+
return {
|
|
39
|
+
content: "No relevant information found in the knowledge base."
|
|
40
|
+
};
|
|
38
41
|
}
|
|
39
|
-
|
|
42
|
+
const content = results.map((r) => r.content).join("\n---\n");
|
|
43
|
+
const urls = results.map((r) => r.metadata.url);
|
|
44
|
+
return {
|
|
45
|
+
content,
|
|
46
|
+
metadata: {
|
|
47
|
+
urls
|
|
48
|
+
}
|
|
49
|
+
};
|
|
40
50
|
}
|
|
41
51
|
}
|
|
42
52
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchKnowledge.cjs.js","sources":["../../src/tools/searchKnowledge.ts"],"sourcesContent":["import {\n createAssistantTool,\n VectorStore,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { Tool } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport z from 'zod';\n\ntype CreateSearchKnowledgeToolOptions = {\n vectorStore: VectorStore;\n};\n\nexport const createSearchKnowledgeTool = ({\n vectorStore,\n}: CreateSearchKnowledgeToolOptions): Tool => {\n const knowledgeTool = createAssistantTool({\n tool: {\n name: 'search-knowledge-base',\n description: `Search the internal knowledge base containing company specific information.\n\nUse this tool when users ask about:\n- General questions about the company or internal information\n\nDo NOT use for general knowledge that doesn't require company-specific information.`,\n provider: 'core',\n schema: z.object({\n query: z.string().describe('The query to search for.'),\n filter: z\n .object({\n source: z.string().optional().describe('Source to filter by.'),\n id: z.string().optional().describe('ID to filter by.'),\n })\n .optional()\n .describe('Filters to apply to the search.'),\n amount: z\n .number()\n .min(1)\n .optional()\n .describe('The number of results to return.'),\n }),\n func: async ({ query, filter, amount }) => {\n const results = await vectorStore.similaritySearch(\n query,\n filter,\n amount,\n );\n\n if (results.length === 0) {\n return {\n content: 'No relevant information found in the knowledge base.',\n };\n }\n\n const content = results.map(r => r.content).join('\\n---\\n');\n\n const urls = results.map(r => r.metadata.url);\n\n return {\n content,\n metadata: {\n urls,\n },\n };\n },\n },\n });\n\n return knowledgeTool;\n};\n"],"names":["createAssistantTool","z"],"mappings":";;;;;;;;;AAWO,MAAM,4BAA4B,CAAC;AAAA,EACxC;AACF,CAAA,KAA8C;AAC5C,EAAA,MAAM,gBAAgBA,kDAAA,CAAoB;AAAA,IACxC,IAAA,EAAM;AAAA,MACJ,IAAA,EAAM,uBAAA;AAAA,MACN,WAAA,EAAa,CAAA;;AAAA;AAAA;;AAAA,mFAAA,CAAA;AAAA,MAMb,QAAA,EAAU,MAAA;AAAA,MACV,MAAA,EAAQC,mBAAE,MAAA,CAAO;AAAA,QACf,KAAA,EAAOA,kBAAA,CAAE,MAAA,EAAO,CAAE,SAAS,0BAA0B,CAAA;AAAA,QACrD,MAAA,EAAQA,mBACL,MAAA,CAAO;AAAA,UACN,QAAQA,kBAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,sBAAsB,CAAA;AAAA,UAC7D,IAAIA,kBAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,kBAAkB;AAAA,SACtD,CAAA,CACA,QAAA,EAAS,CACT,SAAS,iCAAiC,CAAA;AAAA,QAC7C,MAAA,EAAQA,kBAAA,CACL,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA,EAAS,CACT,QAAA,CAAS,kCAAkC;AAAA,OAC/C,CAAA;AAAA,MACD,MAAM,OAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAO,KAAM;AACzC,QAAA,MAAM,OAAA,GAAU,MAAM,WAAA,CAAY,gBAAA;AAAA,UAChC,KAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACF;AAEA,QAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,UAAA,OAAO;AAAA,YACL,OAAA,EAAS;AAAA,WACX;AAAA,QACF;AAEA,QAAA,MAAM,OAAA,GAAU,QAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,OAAO,CAAA,CAAE,KAAK,SAAS,CAAA;AAE1D,QAAA,MAAM,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,GAAG,CAAA;AAE5C,QAAA,OAAO;AAAA,UACL,OAAA;AAAA,UACA,QAAA,EAAU;AAAA,YACR;AAAA;AACF,SACF;AAAA,MACF;AAAA;AACF,GACD,CAAA;AAED,EAAA,OAAO,aAAA;AACT;;;;"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
const mcpConfigTable = 'user_mcp_config';
|
|
2
|
+
const newConfigTable = 'user_ai_assistant_settings';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {import('knex').knex} knex
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
exports.down = async knex => {
|
|
10
|
+
// Recreate old table
|
|
11
|
+
await knex.schema.createTable(mcpConfigTable, table => {
|
|
12
|
+
table
|
|
13
|
+
.uuid('id')
|
|
14
|
+
.primary()
|
|
15
|
+
.notNullable()
|
|
16
|
+
.defaultTo(knex.raw('gen_random_uuid()'));
|
|
17
|
+
table
|
|
18
|
+
.text('userRef')
|
|
19
|
+
.notNullable()
|
|
20
|
+
.comment('Reference to the user who sent the message');
|
|
21
|
+
table
|
|
22
|
+
.text('name')
|
|
23
|
+
.notNullable()
|
|
24
|
+
.comment('Name of the MCP server configuration');
|
|
25
|
+
table
|
|
26
|
+
.text('encryptedOptions')
|
|
27
|
+
.notNullable()
|
|
28
|
+
.comment('Encrypted MCP server configuration');
|
|
29
|
+
table.timestamps(true, true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const mcpSettings = await knex(newConfigTable)
|
|
33
|
+
.where('type', 'mcp_server_config')
|
|
34
|
+
.select('*');
|
|
35
|
+
|
|
36
|
+
for (const setting of mcpSettings) {
|
|
37
|
+
const servers = setting.data;
|
|
38
|
+
const insertRecords = Object.entries(servers).map(
|
|
39
|
+
([name, encryptedOptions]) => ({
|
|
40
|
+
userRef: setting.userRef,
|
|
41
|
+
name,
|
|
42
|
+
encryptedOptions,
|
|
43
|
+
created_at: setting.created_at,
|
|
44
|
+
updated_at: setting.updated_at,
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (insertRecords.length > 0) {
|
|
49
|
+
await knex.batchInsert(mcpConfigTable, insertRecords);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await knex.schema.dropTable(newConfigTable);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
*
|
|
58
|
+
* @param {import('knex').knex} knex
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
exports.up = async knex => {
|
|
62
|
+
await knex.schema.createTable(newConfigTable, table => {
|
|
63
|
+
table
|
|
64
|
+
.uuid('id')
|
|
65
|
+
.primary()
|
|
66
|
+
.notNullable()
|
|
67
|
+
.defaultTo(knex.raw('gen_random_uuid()'));
|
|
68
|
+
|
|
69
|
+
table.text('userRef').notNullable().comment('Reference to the user');
|
|
70
|
+
|
|
71
|
+
table
|
|
72
|
+
.text('type')
|
|
73
|
+
.notNullable()
|
|
74
|
+
.comment('AI Assistant setting type. i.e "MCP, tools etc"');
|
|
75
|
+
|
|
76
|
+
table
|
|
77
|
+
.jsonb('data')
|
|
78
|
+
.notNullable()
|
|
79
|
+
.comment('AI assistant settings for the user');
|
|
80
|
+
|
|
81
|
+
table.unique(['userRef', 'type']);
|
|
82
|
+
table.timestamps(true, true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Check if old table exists
|
|
86
|
+
const hasMcpConfigTable = await knex.schema.hasTable(mcpConfigTable);
|
|
87
|
+
|
|
88
|
+
if (!hasMcpConfigTable) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const mcpConfigs = await knex(mcpConfigTable).select('*');
|
|
93
|
+
|
|
94
|
+
if (mcpConfigs.length <= 0) {
|
|
95
|
+
await knex.schema.dropTable(mcpConfigTable);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const configsByUser = mcpConfigs.reduce((acc, config) => {
|
|
100
|
+
if (!acc[config.userRef]) {
|
|
101
|
+
acc[config.userRef] = {
|
|
102
|
+
userRef: config.userRef,
|
|
103
|
+
servers: {},
|
|
104
|
+
created_at: config.created_at,
|
|
105
|
+
updated_at: config.updated_at,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// Use name as key, encryptedOptions as value
|
|
109
|
+
acc[config.userRef].servers[config.name] = config.encryptedOptions;
|
|
110
|
+
|
|
111
|
+
// Keep the most recent timestamps
|
|
112
|
+
if (config.updated_at > acc[config.userRef].updated_at) {
|
|
113
|
+
acc[config.userRef].updated_at = config.updated_at;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return acc;
|
|
117
|
+
}, {});
|
|
118
|
+
|
|
119
|
+
await knex.batchInsert(
|
|
120
|
+
newConfigTable,
|
|
121
|
+
Object.values(configsByUser).map(userConfig => ({
|
|
122
|
+
userRef: userConfig.userRef,
|
|
123
|
+
type: 'mcp_server_config',
|
|
124
|
+
data: userConfig.servers,
|
|
125
|
+
created_at: userConfig.created_at,
|
|
126
|
+
updated_at: userConfig.updated_at,
|
|
127
|
+
})),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
await knex.schema.dropTable(mcpConfigTable);
|
|
131
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sweetoburrito/backstage-plugin-ai-assistant-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"@langchain/langgraph": "^0.4.9",
|
|
44
44
|
"@langchain/mcp-adapters": "^1.0.0",
|
|
45
45
|
"@langchain/textsplitters": "^0.1.0",
|
|
46
|
-
"@sweetoburrito/backstage-plugin-ai-assistant-common": "^0.
|
|
47
|
-
"@sweetoburrito/backstage-plugin-ai-assistant-node": "^0.
|
|
46
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-common": "^0.7.0",
|
|
47
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-node": "^0.8.0",
|
|
48
48
|
"express": "^4.17.1",
|
|
49
49
|
"express-promise-router": "^4.1.0",
|
|
50
50
|
"knex": "^3.1.0",
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const MCP_TABLE_NAME = "user_mcp_config";
|
|
4
|
-
class McpStore {
|
|
5
|
-
/**
|
|
6
|
-
* Creates an instance of ChatStore.
|
|
7
|
-
* @param client - The Knex client to interact with the PostgreSQL database.
|
|
8
|
-
*/
|
|
9
|
-
constructor(client) {
|
|
10
|
-
this.client = client;
|
|
11
|
-
}
|
|
12
|
-
static async fromConfig({ database }) {
|
|
13
|
-
const client = await database.getClient();
|
|
14
|
-
return new McpStore(client);
|
|
15
|
-
}
|
|
16
|
-
mcpTable() {
|
|
17
|
-
return this.client(MCP_TABLE_NAME);
|
|
18
|
-
}
|
|
19
|
-
async getUserUserMcpConfigNames(userRef) {
|
|
20
|
-
const rows = await this.mcpTable().where({ userRef }).select("name");
|
|
21
|
-
return rows.map((row) => row.name);
|
|
22
|
-
}
|
|
23
|
-
async getUserMcpConfigs(userRef) {
|
|
24
|
-
const rows = await this.mcpTable().where({ userRef }).select();
|
|
25
|
-
return rows.map((row) => ({
|
|
26
|
-
name: row.name,
|
|
27
|
-
encryptedOptions: row.encryptedOptions
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
async createUserMcpConfig(userRef, name, encryptedOptions) {
|
|
31
|
-
await this.mcpTable().insert({
|
|
32
|
-
userRef,
|
|
33
|
-
name,
|
|
34
|
-
encryptedOptions
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
async updateUserMcpConfig(userRef, name, encryptedOptions) {
|
|
38
|
-
await this.mcpTable().where({ userRef, name }).update({ encryptedOptions });
|
|
39
|
-
}
|
|
40
|
-
async deleteUserMcpConfig(userRef, name) {
|
|
41
|
-
await this.mcpTable().where({ userRef, name }).delete();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
exports.McpStore = McpStore;
|
|
46
|
-
//# sourceMappingURL=mcp-store.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-store.cjs.js","sources":["../../src/database/mcp-store.ts"],"sourcesContent":["import { DatabaseService } from '@backstage/backend-plugin-api';\n\nimport { Knex } from 'knex';\n\nconst MCP_TABLE_NAME = 'user_mcp_config';\n\nexport type McpStoreOptions = {\n database: DatabaseService;\n};\n\nexport class McpStore {\n /**\n * Creates an instance of ChatStore.\n * @param client - The Knex client to interact with the PostgreSQL database.\n */\n constructor(private readonly client: Knex) {}\n\n static async fromConfig({ database }: McpStoreOptions) {\n const client = await database.getClient();\n return new McpStore(client);\n }\n\n mcpTable() {\n return this.client(MCP_TABLE_NAME);\n }\n\n async getUserUserMcpConfigNames(userRef: string): Promise<string[]> {\n const rows = await this.mcpTable().where({ userRef }).select('name');\n return rows.map(row => row.name);\n }\n\n async getUserMcpConfigs(\n userRef: string,\n ): Promise<Array<{ name: string; encryptedOptions: string }>> {\n const rows = await this.mcpTable().where({ userRef }).select();\n return rows.map(row => ({\n name: row.name,\n encryptedOptions: row.encryptedOptions,\n }));\n }\n\n async createUserMcpConfig(\n userRef: string,\n name: string,\n encryptedOptions: string,\n ): Promise<void> {\n await this.mcpTable().insert({\n userRef,\n name,\n encryptedOptions,\n });\n }\n\n async updateUserMcpConfig(\n userRef: string,\n name: string,\n encryptedOptions: string,\n ): Promise<void> {\n await this.mcpTable().where({ userRef, name }).update({ encryptedOptions });\n }\n\n async deleteUserMcpConfig(userRef: string, name: string): Promise<void> {\n await this.mcpTable().where({ userRef, name }).delete();\n }\n}\n"],"names":[],"mappings":";;AAIA,MAAM,cAAA,GAAiB,iBAAA;AAMhB,MAAM,QAAA,CAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpB,YAA6B,MAAA,EAAc;AAAd,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAe;AAAA,EAE5C,aAAa,UAAA,CAAW,EAAE,QAAA,EAAS,EAAoB;AACrD,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,SAAA,EAAU;AACxC,IAAA,OAAO,IAAI,SAAS,MAAM,CAAA;AAAA,EAC5B;AAAA,EAEA,QAAA,GAAW;AACT,IAAA,OAAO,IAAA,CAAK,OAAO,cAAc,CAAA;AAAA,EACnC;AAAA,EAEA,MAAM,0BAA0B,OAAA,EAAoC;AAClE,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,QAAA,EAAS,CAAE,KAAA,CAAM,EAAE,OAAA,EAAS,CAAA,CAAE,MAAA,CAAO,MAAM,CAAA;AACnE,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,GAAA,KAAO,GAAA,CAAI,IAAI,CAAA;AAAA,EACjC;AAAA,EAEA,MAAM,kBACJ,OAAA,EAC4D;AAC5D,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,QAAA,EAAS,CAAE,MAAM,EAAE,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO;AAC7D,IAAA,OAAO,IAAA,CAAK,IAAI,CAAA,GAAA,MAAQ;AAAA,MACtB,MAAM,GAAA,CAAI,IAAA;AAAA,MACV,kBAAkB,GAAA,CAAI;AAAA,KACxB,CAAE,CAAA;AAAA,EACJ;AAAA,EAEA,MAAM,mBAAA,CACJ,OAAA,EACA,IAAA,EACA,gBAAA,EACe;AACf,IAAA,MAAM,IAAA,CAAK,QAAA,EAAS,CAAE,MAAA,CAAO;AAAA,MAC3B,OAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,mBAAA,CACJ,OAAA,EACA,IAAA,EACA,gBAAA,EACe;AACf,IAAA,MAAM,IAAA,CAAK,QAAA,EAAS,CAAE,KAAA,CAAM,EAAE,OAAA,EAAS,IAAA,EAAM,CAAA,CAAE,MAAA,CAAO,EAAE,gBAAA,EAAkB,CAAA;AAAA,EAC5E;AAAA,EAEA,MAAM,mBAAA,CAAoB,OAAA,EAAiB,IAAA,EAA6B;AACtE,IAAA,MAAM,IAAA,CAAK,UAAS,CAAE,KAAA,CAAM,EAAE,OAAA,EAAS,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO;AAAA,EACxD;AACF;;;;"}
|
package/dist/services/mcp.cjs.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var mcpAdapters = require('@langchain/mcp-adapters');
|
|
4
|
-
var backstagePluginAiAssistantNode = require('@sweetoburrito/backstage-plugin-ai-assistant-node');
|
|
5
|
-
var mcpStore = require('../database/mcp-store.cjs.js');
|
|
6
|
-
|
|
7
|
-
const createMcpService = async ({
|
|
8
|
-
config,
|
|
9
|
-
userInfo,
|
|
10
|
-
database
|
|
11
|
-
}) => {
|
|
12
|
-
const serversConfig = config.getOptionalConfigArray(
|
|
13
|
-
"aiAssistant.mcp.servers"
|
|
14
|
-
);
|
|
15
|
-
const encryptionKey = config.getString("aiAssistant.mcp.encryptionKey");
|
|
16
|
-
const preConfiguredMcpServers = serversConfig ? serversConfig.reduce((acc, server) => {
|
|
17
|
-
const serverName = server.getString("name");
|
|
18
|
-
const options = server.get("options");
|
|
19
|
-
acc[serverName] = options;
|
|
20
|
-
return acc;
|
|
21
|
-
}, {}) : null;
|
|
22
|
-
const preConfiguredMcpClient = preConfiguredMcpServers ? new mcpAdapters.MultiServerMCPClient({
|
|
23
|
-
prefixToolNameWithServerName: true,
|
|
24
|
-
useStandardContentBlocks: true,
|
|
25
|
-
mcpServers: preConfiguredMcpServers
|
|
26
|
-
}) : null;
|
|
27
|
-
const mcpStore$1 = await mcpStore.McpStore.fromConfig({ database });
|
|
28
|
-
const getUserMcpServerConfigNames = async (credentials) => {
|
|
29
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
30
|
-
return mcpStore$1.getUserUserMcpConfigNames(userEntityRef);
|
|
31
|
-
};
|
|
32
|
-
const getUserMcpServerConfig = async (credentials) => {
|
|
33
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
34
|
-
const encryptedMcpConfig = await mcpStore$1.getUserMcpConfigs(userEntityRef);
|
|
35
|
-
const mcpConfig = encryptedMcpConfig.map((c) => ({
|
|
36
|
-
name: c.name,
|
|
37
|
-
options: JSON.parse(backstagePluginAiAssistantNode.decrypt(c.encryptedOptions, encryptionKey))
|
|
38
|
-
}));
|
|
39
|
-
return mcpConfig;
|
|
40
|
-
};
|
|
41
|
-
const getTools = async (credentials) => {
|
|
42
|
-
const userMcpConfig = await getUserMcpServerConfig(credentials);
|
|
43
|
-
const userMcpServers = userMcpConfig.length ? userMcpConfig.reduce((acc, server) => {
|
|
44
|
-
const { name, options } = server;
|
|
45
|
-
acc[name] = options;
|
|
46
|
-
return acc;
|
|
47
|
-
}, {}) : null;
|
|
48
|
-
const userConfigClient = userMcpServers ? new mcpAdapters.MultiServerMCPClient({
|
|
49
|
-
prefixToolNameWithServerName: true,
|
|
50
|
-
useStandardContentBlocks: true,
|
|
51
|
-
mcpServers: userMcpServers
|
|
52
|
-
}) : null;
|
|
53
|
-
const userMcpTools = userConfigClient ? await userConfigClient.getTools() : [];
|
|
54
|
-
const preConfiguredMcpTools = preConfiguredMcpClient ? await preConfiguredMcpClient.getTools() : [];
|
|
55
|
-
const mcpTools = [...userMcpTools, ...preConfiguredMcpTools];
|
|
56
|
-
const tools = mcpTools.map((mcpTool) => {
|
|
57
|
-
const { name, description, schema } = mcpTool;
|
|
58
|
-
return backstagePluginAiAssistantNode.createAssistantTool({
|
|
59
|
-
tool: {
|
|
60
|
-
name,
|
|
61
|
-
description,
|
|
62
|
-
schema,
|
|
63
|
-
func: async (params) => {
|
|
64
|
-
const result = await mcpTool.invoke(params);
|
|
65
|
-
return JSON.stringify(result);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
return tools;
|
|
71
|
-
};
|
|
72
|
-
const validateMcpServerConfig = async (mcpConfig) => {
|
|
73
|
-
try {
|
|
74
|
-
const userMcpServers = mcpConfig.reduce((acc, server) => {
|
|
75
|
-
const { name, options } = server;
|
|
76
|
-
acc[name] = options;
|
|
77
|
-
return acc;
|
|
78
|
-
}, {});
|
|
79
|
-
const userConfigClient = new mcpAdapters.MultiServerMCPClient({
|
|
80
|
-
prefixToolNameWithServerName: true,
|
|
81
|
-
useStandardContentBlocks: true,
|
|
82
|
-
mcpServers: userMcpServers
|
|
83
|
-
});
|
|
84
|
-
await userConfigClient.getTools();
|
|
85
|
-
} catch (e) {
|
|
86
|
-
const error = new Error("Invalid MCP server configuration");
|
|
87
|
-
error.name = "McpConfigurationError";
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
const createUserMcpServerConfig = async (credentials, mcpConfig) => {
|
|
92
|
-
await validateMcpServerConfig([mcpConfig]);
|
|
93
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
94
|
-
const { name, options } = mcpConfig;
|
|
95
|
-
const encryptedOptions = backstagePluginAiAssistantNode.encrypt(JSON.stringify(options), encryptionKey);
|
|
96
|
-
await mcpStore$1.createUserMcpConfig(userEntityRef, name, encryptedOptions);
|
|
97
|
-
};
|
|
98
|
-
const updateUserMcpServerConfig = async (credentials, mcpConfig) => {
|
|
99
|
-
await validateMcpServerConfig([mcpConfig]);
|
|
100
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
101
|
-
const { name, options } = mcpConfig;
|
|
102
|
-
const encryptedOptions = backstagePluginAiAssistantNode.encrypt(JSON.stringify(options), encryptionKey);
|
|
103
|
-
await mcpStore$1.updateUserMcpConfig(userEntityRef, name, encryptedOptions);
|
|
104
|
-
};
|
|
105
|
-
const deleteUserMcpServerConfig = async (credentials, name) => {
|
|
106
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
107
|
-
await mcpStore$1.deleteUserMcpConfig(userEntityRef, name);
|
|
108
|
-
};
|
|
109
|
-
return {
|
|
110
|
-
getTools,
|
|
111
|
-
createUserMcpServerConfig,
|
|
112
|
-
updateUserMcpServerConfig,
|
|
113
|
-
getUserMcpServerConfigNames,
|
|
114
|
-
deleteUserMcpServerConfig
|
|
115
|
-
};
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
exports.createMcpService = createMcpService;
|
|
119
|
-
//# sourceMappingURL=mcp.cjs.js.map
|