@exulu/backend 1.30.5 → 1.31.1

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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
- ## [1.30.5](https://github.com/Qventu/exulu-backend/compare/v1.30.4...v1.30.5) (2025-11-04)
1
+ ## [1.31.1](https://github.com/Qventu/exulu-backend/compare/v1.31.0...v1.31.1) (2025-11-10)
2
2
 
3
3
 
4
4
  ### Bug Fixes
5
5
 
6
- * bug if queue enum is empty ([85ef7bc](https://github.com/Qventu/exulu-backend/commit/85ef7bc8c8ff03e1890cb12cb82d6537ec65588f))
6
+ * redis auth url ([bbf51d6](https://github.com/Qventu/exulu-backend/commit/bbf51d63be8dbbc88e8989633207db4b147e3da6))
package/dist/index.cjs CHANGED
@@ -60,7 +60,8 @@ var import_redis = require("redis");
60
60
  var redisServer = {
61
61
  host: `${process.env.REDIS_HOST}`,
62
62
  port: process.env.REDIS_PORT,
63
- password: process.env.REDIS_PASSWORD || void 0
63
+ password: process.env.REDIS_PASSWORD || void 0,
64
+ username: process.env.REDIS_USER || void 0
64
65
  };
65
66
 
66
67
  // src/redis/client.ts
@@ -71,7 +72,12 @@ async function redisClient() {
71
72
  }
72
73
  if (!client["exulu"]) {
73
74
  try {
74
- const url = `redis://${redisServer.host}:${redisServer.port}`;
75
+ let url = "";
76
+ if (redisServer.username) {
77
+ url = `redis://${redisServer.username}:${redisServer.password}@${redisServer.host}:${redisServer.port}`;
78
+ } else {
79
+ url = `redis://${redisServer.host}:${redisServer.port}`;
80
+ }
75
81
  client["exulu"] = (0, import_redis.createClient)({
76
82
  url
77
83
  });
@@ -437,8 +443,6 @@ var sanitizeName = (name) => {
437
443
 
438
444
  // src/registry/classes.ts
439
445
  var import_crypto_js2 = __toESM(require("crypto-js"), 1);
440
- var import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
441
- var import_mcp_stdio = require("ai/mcp-stdio");
442
446
 
443
447
  // src/registry/utils/graphql.ts
444
448
  var import_schema = require("@graphql-tools/schema");
@@ -1141,6 +1145,10 @@ var statisticsSchema = {
1141
1145
  {
1142
1146
  name: "role",
1143
1147
  type: "uuid"
1148
+ },
1149
+ {
1150
+ name: "project",
1151
+ type: "uuid"
1144
1152
  }
1145
1153
  ]
1146
1154
  };
@@ -4040,7 +4048,6 @@ type StatisticsResult {
4040
4048
  }
4041
4049
  `;
4042
4050
  const fullSDL = typeDefs + mutationDefs + modelDefs + genericTypes;
4043
- console.log("[EXULU] Full SDL:", fullSDL);
4044
4051
  const schema = (0, import_schema.makeExecutableSchema)({
4045
4052
  typeDefs: fullSDL,
4046
4053
  resolvers
@@ -4633,7 +4640,6 @@ var createUppyRoutes = async (app, config) => {
4633
4640
  };
4634
4641
 
4635
4642
  // src/registry/classes.ts
4636
- var import_streamableHttp2 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
4637
4643
  var s3Client2;
4638
4644
  function sanitizeToolName(name) {
4639
4645
  if (typeof name !== "string") return "";
@@ -4875,7 +4881,6 @@ var ExuluAgent2 = class {
4875
4881
  return typeof model === "string" ? model : model?.modelId || "";
4876
4882
  }
4877
4883
  // Exports the agent as a tool that can be used by another agent
4878
- // todo test this
4879
4884
  tool = async (instance, agents) => {
4880
4885
  const agentInstance = await loadAgent(instance);
4881
4886
  if (!agentInstance) {
@@ -5794,6 +5799,7 @@ var updateStatistic = async (statistic) => {
5794
5799
  const existing = await db3.from("tracking").where({
5795
5800
  ...statistic.user ? { user: statistic.user } : {},
5796
5801
  ...statistic.role ? { role: statistic.role } : {},
5802
+ ...statistic.project ? { project: statistic.project } : {},
5797
5803
  name: statistic.name,
5798
5804
  label: statistic.label,
5799
5805
  type: statistic.type,
@@ -5807,16 +5813,14 @@ var updateStatistic = async (statistic) => {
5807
5813
  total: statistic.count ?? 1,
5808
5814
  createdAt: currentDate,
5809
5815
  ...statistic.user ? { user: statistic.user } : {},
5810
- ...statistic.role ? { role: statistic.role } : {}
5816
+ ...statistic.role ? { role: statistic.role } : {},
5817
+ ...statistic.project ? { project: statistic.project } : {}
5811
5818
  });
5812
5819
  } else {
5813
5820
  await db3.from("tracking").update({
5814
5821
  total: db3.raw("total + ?", [statistic.count ?? 1])
5815
5822
  }).where({
5816
- name: statistic.name,
5817
- label: statistic.label,
5818
- type: statistic.type,
5819
- createdAt: currentDate
5823
+ id: existing.id
5820
5824
  });
5821
5825
  }
5822
5826
  };
@@ -5906,6 +5910,9 @@ var CLAUDE_MESSAGES = {
5906
5910
  \x1B[0m`,
5907
5911
  not_enabled: `
5908
5912
  \x1B[41m -- The agent you selected does not have a valid API key set for it. --
5913
+ \x1B[0m`,
5914
+ missing_project: `
5915
+ \x1B[41m -- Project not found or you do not have access to it. --
5909
5916
  \x1B[0m`
5910
5917
  };
5911
5918
 
@@ -6208,7 +6215,6 @@ Mood: friendly and intelligent.
6208
6215
  console.log("[EXULU] agent tools", agentInstance.tools?.map((x) => x.name + " (" + x.id + ")"));
6209
6216
  const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
6210
6217
  let enabledTools = await getEnabledTools(agentInstance, tools, disabledTools, agents, user);
6211
- console.log("[EXULU] enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
6212
6218
  const variableName = agentInstance.providerapikey;
6213
6219
  const variable = await db3.from("variables").where({ name: variableName }).first();
6214
6220
  if (!variable) {
@@ -6356,11 +6362,12 @@ Mood: friendly and intelligent.
6356
6362
  }
6357
6363
  });
6358
6364
  });
6359
- app.use("/gateway/anthropic/:id", import_express3.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
6365
+ app.use("/gateway/anthropic/:agent/:project", import_express3.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
6360
6366
  try {
6361
6367
  if (!req.body.tools) {
6362
6368
  req.body.tools = [];
6363
6369
  }
6370
+ const { db: db3 } = await postgresClient();
6364
6371
  const authenticationResult = await requestValidators.authenticate(req);
6365
6372
  if (!authenticationResult.user?.id) {
6366
6373
  console.log("[EXULU] failed authentication result", authenticationResult);
@@ -6368,20 +6375,35 @@ Mood: friendly and intelligent.
6368
6375
  return;
6369
6376
  }
6370
6377
  const user = authenticationResult.user;
6371
- const { db: db3 } = await postgresClient();
6372
- let query = db3("agents");
6373
- query.select("*");
6374
- query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
6375
- query.where({ id: req.params.id });
6376
- const agent = await query.first();
6378
+ let agentQuery = db3("agents");
6379
+ agentQuery.select("*");
6380
+ agentQuery = applyAccessControl(agentsSchema2(), authenticationResult.user, agentQuery);
6381
+ agentQuery.where({ id: req.params.agent });
6382
+ const agent = await agentQuery.first();
6377
6383
  if (!agent) {
6378
6384
  const arrayBuffer = createCustomAnthropicStreamingMessage(`
6379
- \x1B[41m -- Agent ${req.params.id} not found or you do not have access to it. --
6385
+ \x1B[41m -- Agent ${req.params.agent} not found or you do not have access to it. --
6380
6386
  \x1B[0m`);
6381
6387
  res.setHeader("Content-Type", "application/json");
6382
6388
  res.end(Buffer.from(arrayBuffer));
6383
6389
  return;
6384
6390
  }
6391
+ let project = null;
6392
+ if (!req.body.project || req.body.project === "DEFAULT") {
6393
+ project = null;
6394
+ } else {
6395
+ let projectQuery = db3("projects");
6396
+ projectQuery.select("*");
6397
+ projectQuery = applyAccessControl(projectsSchema2(), authenticationResult.user, projectQuery);
6398
+ projectQuery.where({ id: req.params.project });
6399
+ project = await projectQuery.first();
6400
+ if (!project) {
6401
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_project);
6402
+ res.setHeader("Content-Type", "application/json");
6403
+ res.end(Buffer.from(arrayBuffer));
6404
+ return;
6405
+ }
6406
+ }
6385
6407
  console.log("[EXULU] anthropic proxy called for agent:", agent?.name);
6386
6408
  if (!process.env.NEXTAUTH_SECRET) {
6387
6409
  const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
@@ -6425,10 +6447,47 @@ Mood: friendly and intelligent.
6425
6447
  apiKey: anthropicApiKey
6426
6448
  });
6427
6449
  const tokens = {};
6428
- for await (const event of client2.messages.stream(req.body)) {
6429
- console.log("[EXULU] Event", event);
6430
- if (event.message?.usage) {
6431
- }
6450
+ const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
6451
+ let enabledTools = await getEnabledTools(agent, tools, disabledTools, agents, user);
6452
+ let system = req.body.system;
6453
+ if (Array.isArray(req.body.system)) {
6454
+ system = [
6455
+ ...req.body.system,
6456
+ ...agent ? [
6457
+ {
6458
+ type: "text",
6459
+ text: `
6460
+ You are an agent named: ${agent?.name}
6461
+ Here are some additional instructions for you: ${agent?.instructions}`
6462
+ }
6463
+ ] : [],
6464
+ ...project ? [
6465
+ {
6466
+ type: "text",
6467
+ text: `Additional information:
6468
+
6469
+ The project you are working on is: ${project?.name}
6470
+ Here is some additional information about the project: ${project?.description}`
6471
+ }
6472
+ ] : []
6473
+ ];
6474
+ } else {
6475
+ system = `${req.body.system}
6476
+
6477
+
6478
+ ${agent ? `You are an agent named: ${agent?.name}
6479
+ Here are some additional instructions for you: ${agent?.instructions}` : ""}
6480
+
6481
+ ${project?.id ? `Additional information:
6482
+
6483
+ The project you are working on is: ${project?.name}
6484
+ The project description is: ${project?.description}` : ""}
6485
+ `;
6486
+ }
6487
+ for await (const event of client2.messages.stream({
6488
+ ...req.body,
6489
+ system
6490
+ })) {
6432
6491
  if (event.message?.id) {
6433
6492
  tokens[event.message.id] = {
6434
6493
  input_tokens: event.message.usage.input_tokens,
@@ -6437,11 +6496,45 @@ Mood: friendly and intelligent.
6437
6496
  output_tokens: event.message.usage.output_tokens
6438
6497
  };
6439
6498
  }
6440
- const msg = `event: ${event.type}
6499
+ if (event.message?.type === "tool_use" && event.message?.name?.includes("exulu_")) {
6500
+ const toolName = event.message?.name;
6501
+ console.log("[EXULU] Using tool", toolName);
6502
+ const inputs = event.message?.input;
6503
+ const id = event.message?.id;
6504
+ const tool2 = enabledTools.find((tool3) => tool3.id === toolName.replace("exulu_", ""));
6505
+ if (!tool2 || !tool2.tool.execute) {
6506
+ console.error("[EXULU] Tool not found or not enabled.", toolName);
6507
+ continue;
6508
+ }
6509
+ const toolResult = await tool2.tool.execute(inputs, {
6510
+ toolCallId: id,
6511
+ messages: [{
6512
+ ...event.message,
6513
+ role: "tool"
6514
+ }]
6515
+ });
6516
+ console.log("[EXULU] Tool result", toolResult);
6517
+ const toolResultMessage = {
6518
+ role: "user",
6519
+ content: [
6520
+ {
6521
+ type: "tool_result",
6522
+ tool_use_id: id,
6523
+ content: toolResult
6524
+ }
6525
+ ]
6526
+ };
6527
+ res.write(`event: tool_result
6528
+ data: ${JSON.stringify(toolResultMessage)}
6529
+
6530
+ `);
6531
+ } else {
6532
+ const msg = `event: ${event.type}
6441
6533
  data: ${JSON.stringify(event)}
6442
6534
 
6443
6535
  `;
6444
- res.write(msg);
6536
+ res.write(msg);
6537
+ }
6445
6538
  }
6446
6539
  let totalInputTokens = 0;
6447
6540
  let totalOutputTokens = 0;
@@ -6461,7 +6554,8 @@ data: ${JSON.stringify(event)}
6461
6554
  trigger: statistics.trigger,
6462
6555
  count: 1,
6463
6556
  user: user.id,
6464
- role: user?.role?.id
6557
+ role: user?.role?.id,
6558
+ ...project ? { project: project.id } : {}
6465
6559
  }),
6466
6560
  ...totalInputTokens ? [
6467
6561
  updateStatistic({
@@ -6471,7 +6565,8 @@ data: ${JSON.stringify(event)}
6471
6565
  trigger: statistics.trigger,
6472
6566
  count: totalInputTokens,
6473
6567
  user: user.id,
6474
- role: user?.role?.id
6568
+ role: user?.role?.id,
6569
+ ...project ? { project: project.id } : {}
6475
6570
  })
6476
6571
  ] : [],
6477
6572
  ...totalOutputTokens ? [
@@ -6480,7 +6575,10 @@ data: ${JSON.stringify(event)}
6480
6575
  label: statistics.label,
6481
6576
  type: STATISTICS_TYPE_ENUM.AGENT_RUN,
6482
6577
  trigger: statistics.trigger,
6483
- count: totalOutputTokens
6578
+ count: totalInputTokens,
6579
+ user: user.id,
6580
+ role: user?.role?.id,
6581
+ ...project ? { project: project.id } : {}
6484
6582
  })
6485
6583
  ] : []
6486
6584
  ]);
@@ -7199,97 +7297,159 @@ function getAverage(arr) {
7199
7297
  // src/mcp/index.ts
7200
7298
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
7201
7299
  var import_node_crypto4 = require("crypto");
7202
- var import_streamableHttp3 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
7300
+ var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
7203
7301
  var import_types = require("@modelcontextprotocol/sdk/types.js");
7204
- var import_zod2 = require("zod");
7205
7302
  var import_express4 = require("express");
7206
7303
  var import_api3 = require("@opentelemetry/api");
7304
+ var import_crypto_js5 = __toESM(require("crypto-js"), 1);
7305
+ var import_zod2 = require("zod");
7207
7306
  var SESSION_ID_HEADER = "mcp-session-id";
7208
7307
  var ExuluMCP = class {
7209
- server;
7308
+ server = {};
7210
7309
  transports = {};
7211
- express;
7212
7310
  constructor() {
7213
7311
  }
7214
- create = async ({ express: express3, contexts, agents, config, tools, tracer }) => {
7215
- this.express = express3;
7216
- if (!this.server) {
7312
+ configure = async ({ user, agentInstance, allTools, allAgents, allContexts, config, tracer }) => {
7313
+ let server = this.server[agentInstance.id];
7314
+ if (!server) {
7217
7315
  console.log("[EXULU] Creating MCP server.");
7218
- this.server = new import_mcp.McpServer({
7219
- name: "exulu-mcp-server",
7220
- version: "1.0.0"
7316
+ server = {
7317
+ mcp: new import_mcp.McpServer({
7318
+ name: "exulu-mcp-server-" + agentInstance.name + "(" + agentInstance.id + ")",
7319
+ version: "1.0.0"
7320
+ }),
7321
+ tools: {}
7322
+ };
7323
+ this.server[agentInstance.id] = server;
7324
+ }
7325
+ const disabledTools = [];
7326
+ let enabledTools = await getEnabledTools(agentInstance, allTools, disabledTools, allAgents, user);
7327
+ const backend = allAgents.find((a) => a.id === agentInstance.backend);
7328
+ if (!backend) {
7329
+ throw new Error("Agent backend not found for agent " + agentInstance.name + " (" + agentInstance.id + ").");
7330
+ }
7331
+ const agentTool = await backend.tool(agentInstance.id, allAgents);
7332
+ if (agentTool) {
7333
+ enabledTools = [
7334
+ ...enabledTools,
7335
+ agentTool
7336
+ ];
7337
+ }
7338
+ const variableName = agentInstance.providerapikey;
7339
+ const { db: db3 } = await postgresClient();
7340
+ const variable = await db3.from("variables").where({ name: variableName }).first();
7341
+ if (!variable) {
7342
+ throw new Error("Provider API key variable not found.");
7343
+ }
7344
+ let providerapikey = variable.value;
7345
+ if (!variable.encrypted) {
7346
+ throw new Error("Provider API key variable not encrypted, for security reasons you are only allowed to use encrypted variables for provider API keys.");
7347
+ }
7348
+ if (variable.encrypted) {
7349
+ const bytes = import_crypto_js5.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
7350
+ providerapikey = bytes.toString(import_crypto_js5.default.enc.Utf8);
7351
+ }
7352
+ console.log("[EXULU] Enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
7353
+ for (const tool2 of enabledTools || []) {
7354
+ if (server.tools[tool2.id]) {
7355
+ continue;
7356
+ }
7357
+ server.mcp.registerTool(sanitizeToolName(tool2.name + "_agent_" + tool2.id), {
7358
+ title: tool2.name + " agent",
7359
+ description: tool2.description,
7360
+ inputSchema: {
7361
+ inputs: tool2.inputSchema || import_zod2.z.object({})
7362
+ }
7363
+ }, async ({ inputs }, args) => {
7364
+ console.log("[EXULU] MCP tool name", tool2.name);
7365
+ console.log("[EXULU] MCP tool inputs", inputs);
7366
+ console.log("[EXULU] MCP tool args", args);
7367
+ const configValues = agentInstance.tools;
7368
+ const tools = convertToolsArrayToObject(
7369
+ [tool2],
7370
+ allTools,
7371
+ configValues,
7372
+ providerapikey,
7373
+ allContexts,
7374
+ user,
7375
+ config
7376
+ );
7377
+ const convertedTool = tools[sanitizeToolName(tool2.name)];
7378
+ if (!convertedTool?.execute) {
7379
+ console.error("[EXULU] Tool not found in converted tools array.", tools);
7380
+ throw new Error("Tool not found in converted tools array.");
7381
+ }
7382
+ const iterator = await convertedTool.execute(inputs, {
7383
+ toolCallId: tool2.id + "_" + (0, import_node_crypto4.randomUUID)(),
7384
+ messages: []
7385
+ });
7386
+ let result;
7387
+ for await (const value of iterator) {
7388
+ result = value;
7389
+ }
7390
+ console.log("[EXULU] MCP tool result", result);
7391
+ return {
7392
+ content: [{ type: "text", text: JSON.stringify(result) }],
7393
+ structuredContent: result
7394
+ };
7221
7395
  });
7396
+ server.tools[tool2.id] = tool2.name;
7222
7397
  }
7223
- this.server.registerTool(
7224
- "getAgents",
7225
- {
7226
- title: "Get agents",
7227
- description: "Retrieves a list of all available agents"
7228
- },
7229
- async () => ({
7230
- content: agents ? agents.map((agent) => {
7231
- return {
7232
- type: "text",
7233
- text: `${agent.name} - ${agent.description}`
7234
- };
7235
- }) : [{
7236
- type: "text",
7237
- text: "No agents found."
7238
- }]
7239
- })
7240
- );
7241
- this.server.registerResource(
7242
- "greeting",
7243
- new import_mcp.ResourceTemplate("greeting://{name}", { list: void 0 }),
7244
- {
7245
- title: "Greeting Resource",
7246
- // Display name for UI
7247
- description: "Dynamic greeting generator"
7248
- },
7249
- async (uri, { name }) => ({
7250
- contents: [{
7251
- uri: uri.href,
7252
- text: `Hello, ${name}!`
7253
- }]
7254
- })
7255
- );
7256
- this.server.registerPrompt(
7257
- "review-code",
7258
- {
7259
- title: "Code Review",
7260
- description: "Review code for best practices and potential issues",
7261
- argsSchema: { code: import_zod2.z.string() }
7262
- },
7263
- ({ code }) => ({
7264
- messages: [{
7265
- role: "user",
7266
- content: {
7267
- type: "text",
7268
- text: `Please review this code:
7269
-
7270
- ${code}`
7271
- }
7272
- }]
7273
- })
7274
- );
7398
+ return server.mcp;
7275
7399
  };
7276
- connect = async () => {
7277
- if (!this.express) {
7400
+ create = async ({ express: express3, allTools, allAgents, allContexts, config }) => {
7401
+ if (!express3) {
7278
7402
  throw new Error("Express not initialized.");
7279
7403
  }
7280
7404
  if (!this.server) {
7281
7405
  throw new Error("MCP server not initialized.");
7282
7406
  }
7283
- this.express.post("/mcp", async (req, res) => {
7284
- if (!this.server) {
7285
- throw new Error("MCP server not initialized.");
7407
+ express3.post("/mcp/:agent", async (req, res) => {
7408
+ console.log("[EXULU] MCP request received.", req.params.agent);
7409
+ if (!req.params.agent) {
7410
+ res.status(400).json({
7411
+ error: "Bad Request: No agent ID provided"
7412
+ });
7413
+ return;
7414
+ }
7415
+ const agentInstance = await loadAgent(req.params.agent);
7416
+ if (!agentInstance) {
7417
+ console.error("[EXULU] Agent not found.", req.params.agent);
7418
+ res.status(404).json({
7419
+ error: "Agent not found"
7420
+ });
7421
+ return;
7422
+ }
7423
+ const authenticationResult = await requestValidators.authenticate(req);
7424
+ if (!authenticationResult.user?.id) {
7425
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
7426
+ return;
7427
+ }
7428
+ const user = authenticationResult.user;
7429
+ const hasAccessToAgent = await checkRecordAccess(agentInstance, "read", user);
7430
+ if (!hasAccessToAgent) {
7431
+ res.status(401).json({
7432
+ message: "You don't have access to this agent."
7433
+ });
7434
+ return;
7435
+ }
7436
+ const server = await this.configure({
7437
+ agentInstance,
7438
+ user,
7439
+ allTools,
7440
+ allAgents,
7441
+ allContexts,
7442
+ config
7443
+ });
7444
+ if (!server) {
7445
+ throw new Error("MCP server for agent " + req.params.agent + " not initialized.");
7286
7446
  }
7287
7447
  const sessionId = req.headers[SESSION_ID_HEADER];
7288
7448
  let transport;
7289
7449
  if (sessionId && this.transports[sessionId]) {
7290
7450
  transport = this.transports[sessionId];
7291
7451
  } else if (!sessionId && (0, import_types.isInitializeRequest)(req.body)) {
7292
- transport = new import_streamableHttp3.StreamableHTTPServerTransport({
7452
+ transport = new import_streamableHttp.StreamableHTTPServerTransport({
7293
7453
  sessionIdGenerator: () => (0, import_node_crypto4.randomUUID)(),
7294
7454
  onsessioninitialized: (sessionId2) => {
7295
7455
  this.transports[sessionId2] = transport;
@@ -7304,13 +7464,14 @@ ${code}`
7304
7464
  delete this.transports[transport.sessionId];
7305
7465
  }
7306
7466
  };
7307
- await this.server.connect(transport);
7467
+ await server.connect(transport);
7308
7468
  } else {
7309
7469
  res.status(400).json({
7310
7470
  error: "Bad Request: No valid session ID provided"
7311
7471
  });
7312
7472
  return;
7313
7473
  }
7474
+ req.headers["exulu-agent-id"] = req.params.agent;
7314
7475
  await transport.handleRequest(req, res, req.body);
7315
7476
  });
7316
7477
  const handleSessionRequest = async (req, res) => {
@@ -7323,9 +7484,10 @@ ${code}`
7323
7484
  const transport = this.transports[sessionId];
7324
7485
  await transport.handleRequest(req, res);
7325
7486
  };
7326
- this.express.get("/mcp", handleSessionRequest);
7327
- this.express.delete("/mcp", handleSessionRequest);
7328
- return this.express;
7487
+ express3.get("/mcp/:agent", handleSessionRequest);
7488
+ express3.delete("/mcp/:agent", handleSessionRequest);
7489
+ console.log("[EXULU] MCP server created.");
7490
+ return express3;
7329
7491
  };
7330
7492
  };
7331
7493
 
@@ -8760,13 +8922,11 @@ var ExuluApp = class {
8760
8922
  const mcp = new ExuluMCP();
8761
8923
  await mcp.create({
8762
8924
  express: app,
8763
- contexts: this._contexts,
8764
- agents: this._agents,
8765
- config: this._config,
8766
- tools: this._tools,
8767
- tracer
8925
+ allTools: this._tools,
8926
+ allAgents: this._agents,
8927
+ allContexts: Object.values(this._contexts ?? {}),
8928
+ config: this._config
8768
8929
  });
8769
- await mcp.connect();
8770
8930
  }
8771
8931
  return app;
8772
8932
  }
@@ -10203,7 +10363,7 @@ var create = ({
10203
10363
  };
10204
10364
 
10205
10365
  // src/index.ts
10206
- var import_crypto_js5 = __toESM(require("crypto-js"), 1);
10366
+ var import_crypto_js6 = __toESM(require("crypto-js"), 1);
10207
10367
  var ExuluJobs = {
10208
10368
  redis: redisClient,
10209
10369
  jobs: {
@@ -10240,8 +10400,8 @@ var ExuluVariables = {
10240
10400
  throw new Error(`Variable ${name} not found.`);
10241
10401
  }
10242
10402
  if (variable.encrypted) {
10243
- const bytes = import_crypto_js5.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
10244
- variable.value = bytes.toString(import_crypto_js5.default.enc.Utf8);
10403
+ const bytes = import_crypto_js6.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
10404
+ variable.value = bytes.toString(import_crypto_js6.default.enc.Utf8);
10245
10405
  }
10246
10406
  return variable.value;
10247
10407
  }
package/dist/index.js CHANGED
@@ -8,7 +8,8 @@ import { createClient } from "redis";
8
8
  var redisServer = {
9
9
  host: `${process.env.REDIS_HOST}`,
10
10
  port: process.env.REDIS_PORT,
11
- password: process.env.REDIS_PASSWORD || void 0
11
+ password: process.env.REDIS_PASSWORD || void 0,
12
+ username: process.env.REDIS_USER || void 0
12
13
  };
13
14
 
14
15
  // src/redis/client.ts
@@ -19,7 +20,12 @@ async function redisClient() {
19
20
  }
20
21
  if (!client["exulu"]) {
21
22
  try {
22
- const url = `redis://${redisServer.host}:${redisServer.port}`;
23
+ let url = "";
24
+ if (redisServer.username) {
25
+ url = `redis://${redisServer.username}:${redisServer.password}@${redisServer.host}:${redisServer.port}`;
26
+ } else {
27
+ url = `redis://${redisServer.host}:${redisServer.port}`;
28
+ }
23
29
  client["exulu"] = createClient({
24
30
  url
25
31
  });
@@ -385,8 +391,6 @@ var sanitizeName = (name) => {
385
391
 
386
392
  // src/registry/classes.ts
387
393
  import CryptoJS2 from "crypto-js";
388
- import "@modelcontextprotocol/sdk/client/streamableHttp.js";
389
- import "ai/mcp-stdio";
390
394
 
391
395
  // src/registry/utils/graphql.ts
392
396
  import { makeExecutableSchema } from "@graphql-tools/schema";
@@ -1089,6 +1093,10 @@ var statisticsSchema = {
1089
1093
  {
1090
1094
  name: "role",
1091
1095
  type: "uuid"
1096
+ },
1097
+ {
1098
+ name: "project",
1099
+ type: "uuid"
1092
1100
  }
1093
1101
  ]
1094
1102
  };
@@ -3988,7 +3996,6 @@ type StatisticsResult {
3988
3996
  }
3989
3997
  `;
3990
3998
  const fullSDL = typeDefs + mutationDefs + modelDefs + genericTypes;
3991
- console.log("[EXULU] Full SDL:", fullSDL);
3992
3999
  const schema = makeExecutableSchema({
3993
4000
  typeDefs: fullSDL,
3994
4001
  resolvers
@@ -4600,7 +4607,6 @@ var createUppyRoutes = async (app, config) => {
4600
4607
  };
4601
4608
 
4602
4609
  // src/registry/classes.ts
4603
- import "@modelcontextprotocol/sdk/server/streamableHttp.js";
4604
4610
  var s3Client2;
4605
4611
  function sanitizeToolName(name) {
4606
4612
  if (typeof name !== "string") return "";
@@ -4842,7 +4848,6 @@ var ExuluAgent2 = class {
4842
4848
  return typeof model === "string" ? model : model?.modelId || "";
4843
4849
  }
4844
4850
  // Exports the agent as a tool that can be used by another agent
4845
- // todo test this
4846
4851
  tool = async (instance, agents) => {
4847
4852
  const agentInstance = await loadAgent(instance);
4848
4853
  if (!agentInstance) {
@@ -5761,6 +5766,7 @@ var updateStatistic = async (statistic) => {
5761
5766
  const existing = await db3.from("tracking").where({
5762
5767
  ...statistic.user ? { user: statistic.user } : {},
5763
5768
  ...statistic.role ? { role: statistic.role } : {},
5769
+ ...statistic.project ? { project: statistic.project } : {},
5764
5770
  name: statistic.name,
5765
5771
  label: statistic.label,
5766
5772
  type: statistic.type,
@@ -5774,16 +5780,14 @@ var updateStatistic = async (statistic) => {
5774
5780
  total: statistic.count ?? 1,
5775
5781
  createdAt: currentDate,
5776
5782
  ...statistic.user ? { user: statistic.user } : {},
5777
- ...statistic.role ? { role: statistic.role } : {}
5783
+ ...statistic.role ? { role: statistic.role } : {},
5784
+ ...statistic.project ? { project: statistic.project } : {}
5778
5785
  });
5779
5786
  } else {
5780
5787
  await db3.from("tracking").update({
5781
5788
  total: db3.raw("total + ?", [statistic.count ?? 1])
5782
5789
  }).where({
5783
- name: statistic.name,
5784
- label: statistic.label,
5785
- type: statistic.type,
5786
- createdAt: currentDate
5790
+ id: existing.id
5787
5791
  });
5788
5792
  }
5789
5793
  };
@@ -5873,6 +5877,9 @@ var CLAUDE_MESSAGES = {
5873
5877
  \x1B[0m`,
5874
5878
  not_enabled: `
5875
5879
  \x1B[41m -- The agent you selected does not have a valid API key set for it. --
5880
+ \x1B[0m`,
5881
+ missing_project: `
5882
+ \x1B[41m -- Project not found or you do not have access to it. --
5876
5883
  \x1B[0m`
5877
5884
  };
5878
5885
 
@@ -6175,7 +6182,6 @@ Mood: friendly and intelligent.
6175
6182
  console.log("[EXULU] agent tools", agentInstance.tools?.map((x) => x.name + " (" + x.id + ")"));
6176
6183
  const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
6177
6184
  let enabledTools = await getEnabledTools(agentInstance, tools, disabledTools, agents, user);
6178
- console.log("[EXULU] enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
6179
6185
  const variableName = agentInstance.providerapikey;
6180
6186
  const variable = await db3.from("variables").where({ name: variableName }).first();
6181
6187
  if (!variable) {
@@ -6323,11 +6329,12 @@ Mood: friendly and intelligent.
6323
6329
  }
6324
6330
  });
6325
6331
  });
6326
- app.use("/gateway/anthropic/:id", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
6332
+ app.use("/gateway/anthropic/:agent/:project", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
6327
6333
  try {
6328
6334
  if (!req.body.tools) {
6329
6335
  req.body.tools = [];
6330
6336
  }
6337
+ const { db: db3 } = await postgresClient();
6331
6338
  const authenticationResult = await requestValidators.authenticate(req);
6332
6339
  if (!authenticationResult.user?.id) {
6333
6340
  console.log("[EXULU] failed authentication result", authenticationResult);
@@ -6335,20 +6342,35 @@ Mood: friendly and intelligent.
6335
6342
  return;
6336
6343
  }
6337
6344
  const user = authenticationResult.user;
6338
- const { db: db3 } = await postgresClient();
6339
- let query = db3("agents");
6340
- query.select("*");
6341
- query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
6342
- query.where({ id: req.params.id });
6343
- const agent = await query.first();
6345
+ let agentQuery = db3("agents");
6346
+ agentQuery.select("*");
6347
+ agentQuery = applyAccessControl(agentsSchema2(), authenticationResult.user, agentQuery);
6348
+ agentQuery.where({ id: req.params.agent });
6349
+ const agent = await agentQuery.first();
6344
6350
  if (!agent) {
6345
6351
  const arrayBuffer = createCustomAnthropicStreamingMessage(`
6346
- \x1B[41m -- Agent ${req.params.id} not found or you do not have access to it. --
6352
+ \x1B[41m -- Agent ${req.params.agent} not found or you do not have access to it. --
6347
6353
  \x1B[0m`);
6348
6354
  res.setHeader("Content-Type", "application/json");
6349
6355
  res.end(Buffer.from(arrayBuffer));
6350
6356
  return;
6351
6357
  }
6358
+ let project = null;
6359
+ if (!req.body.project || req.body.project === "DEFAULT") {
6360
+ project = null;
6361
+ } else {
6362
+ let projectQuery = db3("projects");
6363
+ projectQuery.select("*");
6364
+ projectQuery = applyAccessControl(projectsSchema2(), authenticationResult.user, projectQuery);
6365
+ projectQuery.where({ id: req.params.project });
6366
+ project = await projectQuery.first();
6367
+ if (!project) {
6368
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_project);
6369
+ res.setHeader("Content-Type", "application/json");
6370
+ res.end(Buffer.from(arrayBuffer));
6371
+ return;
6372
+ }
6373
+ }
6352
6374
  console.log("[EXULU] anthropic proxy called for agent:", agent?.name);
6353
6375
  if (!process.env.NEXTAUTH_SECRET) {
6354
6376
  const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
@@ -6392,10 +6414,47 @@ Mood: friendly and intelligent.
6392
6414
  apiKey: anthropicApiKey
6393
6415
  });
6394
6416
  const tokens = {};
6395
- for await (const event of client2.messages.stream(req.body)) {
6396
- console.log("[EXULU] Event", event);
6397
- if (event.message?.usage) {
6398
- }
6417
+ const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
6418
+ let enabledTools = await getEnabledTools(agent, tools, disabledTools, agents, user);
6419
+ let system = req.body.system;
6420
+ if (Array.isArray(req.body.system)) {
6421
+ system = [
6422
+ ...req.body.system,
6423
+ ...agent ? [
6424
+ {
6425
+ type: "text",
6426
+ text: `
6427
+ You are an agent named: ${agent?.name}
6428
+ Here are some additional instructions for you: ${agent?.instructions}`
6429
+ }
6430
+ ] : [],
6431
+ ...project ? [
6432
+ {
6433
+ type: "text",
6434
+ text: `Additional information:
6435
+
6436
+ The project you are working on is: ${project?.name}
6437
+ Here is some additional information about the project: ${project?.description}`
6438
+ }
6439
+ ] : []
6440
+ ];
6441
+ } else {
6442
+ system = `${req.body.system}
6443
+
6444
+
6445
+ ${agent ? `You are an agent named: ${agent?.name}
6446
+ Here are some additional instructions for you: ${agent?.instructions}` : ""}
6447
+
6448
+ ${project?.id ? `Additional information:
6449
+
6450
+ The project you are working on is: ${project?.name}
6451
+ The project description is: ${project?.description}` : ""}
6452
+ `;
6453
+ }
6454
+ for await (const event of client2.messages.stream({
6455
+ ...req.body,
6456
+ system
6457
+ })) {
6399
6458
  if (event.message?.id) {
6400
6459
  tokens[event.message.id] = {
6401
6460
  input_tokens: event.message.usage.input_tokens,
@@ -6404,11 +6463,45 @@ Mood: friendly and intelligent.
6404
6463
  output_tokens: event.message.usage.output_tokens
6405
6464
  };
6406
6465
  }
6407
- const msg = `event: ${event.type}
6466
+ if (event.message?.type === "tool_use" && event.message?.name?.includes("exulu_")) {
6467
+ const toolName = event.message?.name;
6468
+ console.log("[EXULU] Using tool", toolName);
6469
+ const inputs = event.message?.input;
6470
+ const id = event.message?.id;
6471
+ const tool2 = enabledTools.find((tool3) => tool3.id === toolName.replace("exulu_", ""));
6472
+ if (!tool2 || !tool2.tool.execute) {
6473
+ console.error("[EXULU] Tool not found or not enabled.", toolName);
6474
+ continue;
6475
+ }
6476
+ const toolResult = await tool2.tool.execute(inputs, {
6477
+ toolCallId: id,
6478
+ messages: [{
6479
+ ...event.message,
6480
+ role: "tool"
6481
+ }]
6482
+ });
6483
+ console.log("[EXULU] Tool result", toolResult);
6484
+ const toolResultMessage = {
6485
+ role: "user",
6486
+ content: [
6487
+ {
6488
+ type: "tool_result",
6489
+ tool_use_id: id,
6490
+ content: toolResult
6491
+ }
6492
+ ]
6493
+ };
6494
+ res.write(`event: tool_result
6495
+ data: ${JSON.stringify(toolResultMessage)}
6496
+
6497
+ `);
6498
+ } else {
6499
+ const msg = `event: ${event.type}
6408
6500
  data: ${JSON.stringify(event)}
6409
6501
 
6410
6502
  `;
6411
- res.write(msg);
6503
+ res.write(msg);
6504
+ }
6412
6505
  }
6413
6506
  let totalInputTokens = 0;
6414
6507
  let totalOutputTokens = 0;
@@ -6428,7 +6521,8 @@ data: ${JSON.stringify(event)}
6428
6521
  trigger: statistics.trigger,
6429
6522
  count: 1,
6430
6523
  user: user.id,
6431
- role: user?.role?.id
6524
+ role: user?.role?.id,
6525
+ ...project ? { project: project.id } : {}
6432
6526
  }),
6433
6527
  ...totalInputTokens ? [
6434
6528
  updateStatistic({
@@ -6438,7 +6532,8 @@ data: ${JSON.stringify(event)}
6438
6532
  trigger: statistics.trigger,
6439
6533
  count: totalInputTokens,
6440
6534
  user: user.id,
6441
- role: user?.role?.id
6535
+ role: user?.role?.id,
6536
+ ...project ? { project: project.id } : {}
6442
6537
  })
6443
6538
  ] : [],
6444
6539
  ...totalOutputTokens ? [
@@ -6447,7 +6542,10 @@ data: ${JSON.stringify(event)}
6447
6542
  label: statistics.label,
6448
6543
  type: STATISTICS_TYPE_ENUM.AGENT_RUN,
6449
6544
  trigger: statistics.trigger,
6450
- count: totalOutputTokens
6545
+ count: totalInputTokens,
6546
+ user: user.id,
6547
+ role: user?.role?.id,
6548
+ ...project ? { project: project.id } : {}
6451
6549
  })
6452
6550
  ] : []
6453
6551
  ]);
@@ -7164,99 +7262,161 @@ function getAverage(arr) {
7164
7262
  }
7165
7263
 
7166
7264
  // src/mcp/index.ts
7167
- import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
7265
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7168
7266
  import { randomUUID as randomUUID4 } from "crypto";
7169
- import { StreamableHTTPServerTransport as StreamableHTTPServerTransport2 } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7267
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7170
7268
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
7171
- import { z as z2 } from "zod";
7172
7269
  import "express";
7173
7270
  import "@opentelemetry/api";
7271
+ import CryptoJS5 from "crypto-js";
7272
+ import { z as z2 } from "zod";
7174
7273
  var SESSION_ID_HEADER = "mcp-session-id";
7175
7274
  var ExuluMCP = class {
7176
- server;
7275
+ server = {};
7177
7276
  transports = {};
7178
- express;
7179
7277
  constructor() {
7180
7278
  }
7181
- create = async ({ express: express3, contexts, agents, config, tools, tracer }) => {
7182
- this.express = express3;
7183
- if (!this.server) {
7279
+ configure = async ({ user, agentInstance, allTools, allAgents, allContexts, config, tracer }) => {
7280
+ let server = this.server[agentInstance.id];
7281
+ if (!server) {
7184
7282
  console.log("[EXULU] Creating MCP server.");
7185
- this.server = new McpServer({
7186
- name: "exulu-mcp-server",
7187
- version: "1.0.0"
7283
+ server = {
7284
+ mcp: new McpServer({
7285
+ name: "exulu-mcp-server-" + agentInstance.name + "(" + agentInstance.id + ")",
7286
+ version: "1.0.0"
7287
+ }),
7288
+ tools: {}
7289
+ };
7290
+ this.server[agentInstance.id] = server;
7291
+ }
7292
+ const disabledTools = [];
7293
+ let enabledTools = await getEnabledTools(agentInstance, allTools, disabledTools, allAgents, user);
7294
+ const backend = allAgents.find((a) => a.id === agentInstance.backend);
7295
+ if (!backend) {
7296
+ throw new Error("Agent backend not found for agent " + agentInstance.name + " (" + agentInstance.id + ").");
7297
+ }
7298
+ const agentTool = await backend.tool(agentInstance.id, allAgents);
7299
+ if (agentTool) {
7300
+ enabledTools = [
7301
+ ...enabledTools,
7302
+ agentTool
7303
+ ];
7304
+ }
7305
+ const variableName = agentInstance.providerapikey;
7306
+ const { db: db3 } = await postgresClient();
7307
+ const variable = await db3.from("variables").where({ name: variableName }).first();
7308
+ if (!variable) {
7309
+ throw new Error("Provider API key variable not found.");
7310
+ }
7311
+ let providerapikey = variable.value;
7312
+ if (!variable.encrypted) {
7313
+ throw new Error("Provider API key variable not encrypted, for security reasons you are only allowed to use encrypted variables for provider API keys.");
7314
+ }
7315
+ if (variable.encrypted) {
7316
+ const bytes = CryptoJS5.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
7317
+ providerapikey = bytes.toString(CryptoJS5.enc.Utf8);
7318
+ }
7319
+ console.log("[EXULU] Enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
7320
+ for (const tool2 of enabledTools || []) {
7321
+ if (server.tools[tool2.id]) {
7322
+ continue;
7323
+ }
7324
+ server.mcp.registerTool(sanitizeToolName(tool2.name + "_agent_" + tool2.id), {
7325
+ title: tool2.name + " agent",
7326
+ description: tool2.description,
7327
+ inputSchema: {
7328
+ inputs: tool2.inputSchema || z2.object({})
7329
+ }
7330
+ }, async ({ inputs }, args) => {
7331
+ console.log("[EXULU] MCP tool name", tool2.name);
7332
+ console.log("[EXULU] MCP tool inputs", inputs);
7333
+ console.log("[EXULU] MCP tool args", args);
7334
+ const configValues = agentInstance.tools;
7335
+ const tools = convertToolsArrayToObject(
7336
+ [tool2],
7337
+ allTools,
7338
+ configValues,
7339
+ providerapikey,
7340
+ allContexts,
7341
+ user,
7342
+ config
7343
+ );
7344
+ const convertedTool = tools[sanitizeToolName(tool2.name)];
7345
+ if (!convertedTool?.execute) {
7346
+ console.error("[EXULU] Tool not found in converted tools array.", tools);
7347
+ throw new Error("Tool not found in converted tools array.");
7348
+ }
7349
+ const iterator = await convertedTool.execute(inputs, {
7350
+ toolCallId: tool2.id + "_" + randomUUID4(),
7351
+ messages: []
7352
+ });
7353
+ let result;
7354
+ for await (const value of iterator) {
7355
+ result = value;
7356
+ }
7357
+ console.log("[EXULU] MCP tool result", result);
7358
+ return {
7359
+ content: [{ type: "text", text: JSON.stringify(result) }],
7360
+ structuredContent: result
7361
+ };
7188
7362
  });
7363
+ server.tools[tool2.id] = tool2.name;
7189
7364
  }
7190
- this.server.registerTool(
7191
- "getAgents",
7192
- {
7193
- title: "Get agents",
7194
- description: "Retrieves a list of all available agents"
7195
- },
7196
- async () => ({
7197
- content: agents ? agents.map((agent) => {
7198
- return {
7199
- type: "text",
7200
- text: `${agent.name} - ${agent.description}`
7201
- };
7202
- }) : [{
7203
- type: "text",
7204
- text: "No agents found."
7205
- }]
7206
- })
7207
- );
7208
- this.server.registerResource(
7209
- "greeting",
7210
- new ResourceTemplate("greeting://{name}", { list: void 0 }),
7211
- {
7212
- title: "Greeting Resource",
7213
- // Display name for UI
7214
- description: "Dynamic greeting generator"
7215
- },
7216
- async (uri, { name }) => ({
7217
- contents: [{
7218
- uri: uri.href,
7219
- text: `Hello, ${name}!`
7220
- }]
7221
- })
7222
- );
7223
- this.server.registerPrompt(
7224
- "review-code",
7225
- {
7226
- title: "Code Review",
7227
- description: "Review code for best practices and potential issues",
7228
- argsSchema: { code: z2.string() }
7229
- },
7230
- ({ code }) => ({
7231
- messages: [{
7232
- role: "user",
7233
- content: {
7234
- type: "text",
7235
- text: `Please review this code:
7236
-
7237
- ${code}`
7238
- }
7239
- }]
7240
- })
7241
- );
7365
+ return server.mcp;
7242
7366
  };
7243
- connect = async () => {
7244
- if (!this.express) {
7367
+ create = async ({ express: express3, allTools, allAgents, allContexts, config }) => {
7368
+ if (!express3) {
7245
7369
  throw new Error("Express not initialized.");
7246
7370
  }
7247
7371
  if (!this.server) {
7248
7372
  throw new Error("MCP server not initialized.");
7249
7373
  }
7250
- this.express.post("/mcp", async (req, res) => {
7251
- if (!this.server) {
7252
- throw new Error("MCP server not initialized.");
7374
+ express3.post("/mcp/:agent", async (req, res) => {
7375
+ console.log("[EXULU] MCP request received.", req.params.agent);
7376
+ if (!req.params.agent) {
7377
+ res.status(400).json({
7378
+ error: "Bad Request: No agent ID provided"
7379
+ });
7380
+ return;
7381
+ }
7382
+ const agentInstance = await loadAgent(req.params.agent);
7383
+ if (!agentInstance) {
7384
+ console.error("[EXULU] Agent not found.", req.params.agent);
7385
+ res.status(404).json({
7386
+ error: "Agent not found"
7387
+ });
7388
+ return;
7389
+ }
7390
+ const authenticationResult = await requestValidators.authenticate(req);
7391
+ if (!authenticationResult.user?.id) {
7392
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
7393
+ return;
7394
+ }
7395
+ const user = authenticationResult.user;
7396
+ const hasAccessToAgent = await checkRecordAccess(agentInstance, "read", user);
7397
+ if (!hasAccessToAgent) {
7398
+ res.status(401).json({
7399
+ message: "You don't have access to this agent."
7400
+ });
7401
+ return;
7402
+ }
7403
+ const server = await this.configure({
7404
+ agentInstance,
7405
+ user,
7406
+ allTools,
7407
+ allAgents,
7408
+ allContexts,
7409
+ config
7410
+ });
7411
+ if (!server) {
7412
+ throw new Error("MCP server for agent " + req.params.agent + " not initialized.");
7253
7413
  }
7254
7414
  const sessionId = req.headers[SESSION_ID_HEADER];
7255
7415
  let transport;
7256
7416
  if (sessionId && this.transports[sessionId]) {
7257
7417
  transport = this.transports[sessionId];
7258
7418
  } else if (!sessionId && isInitializeRequest(req.body)) {
7259
- transport = new StreamableHTTPServerTransport2({
7419
+ transport = new StreamableHTTPServerTransport({
7260
7420
  sessionIdGenerator: () => randomUUID4(),
7261
7421
  onsessioninitialized: (sessionId2) => {
7262
7422
  this.transports[sessionId2] = transport;
@@ -7271,13 +7431,14 @@ ${code}`
7271
7431
  delete this.transports[transport.sessionId];
7272
7432
  }
7273
7433
  };
7274
- await this.server.connect(transport);
7434
+ await server.connect(transport);
7275
7435
  } else {
7276
7436
  res.status(400).json({
7277
7437
  error: "Bad Request: No valid session ID provided"
7278
7438
  });
7279
7439
  return;
7280
7440
  }
7441
+ req.headers["exulu-agent-id"] = req.params.agent;
7281
7442
  await transport.handleRequest(req, res, req.body);
7282
7443
  });
7283
7444
  const handleSessionRequest = async (req, res) => {
@@ -7290,9 +7451,10 @@ ${code}`
7290
7451
  const transport = this.transports[sessionId];
7291
7452
  await transport.handleRequest(req, res);
7292
7453
  };
7293
- this.express.get("/mcp", handleSessionRequest);
7294
- this.express.delete("/mcp", handleSessionRequest);
7295
- return this.express;
7454
+ express3.get("/mcp/:agent", handleSessionRequest);
7455
+ express3.delete("/mcp/:agent", handleSessionRequest);
7456
+ console.log("[EXULU] MCP server created.");
7457
+ return express3;
7296
7458
  };
7297
7459
  };
7298
7460
 
@@ -8727,13 +8889,11 @@ var ExuluApp = class {
8727
8889
  const mcp = new ExuluMCP();
8728
8890
  await mcp.create({
8729
8891
  express: app,
8730
- contexts: this._contexts,
8731
- agents: this._agents,
8732
- config: this._config,
8733
- tools: this._tools,
8734
- tracer
8892
+ allTools: this._tools,
8893
+ allAgents: this._agents,
8894
+ allContexts: Object.values(this._contexts ?? {}),
8895
+ config: this._config
8735
8896
  });
8736
- await mcp.connect();
8737
8897
  }
8738
8898
  return app;
8739
8899
  }
@@ -10170,7 +10330,7 @@ var create = ({
10170
10330
  };
10171
10331
 
10172
10332
  // src/index.ts
10173
- import CryptoJS5 from "crypto-js";
10333
+ import CryptoJS6 from "crypto-js";
10174
10334
  var ExuluJobs = {
10175
10335
  redis: redisClient,
10176
10336
  jobs: {
@@ -10207,8 +10367,8 @@ var ExuluVariables = {
10207
10367
  throw new Error(`Variable ${name} not found.`);
10208
10368
  }
10209
10369
  if (variable.encrypted) {
10210
- const bytes = CryptoJS5.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
10211
- variable.value = bytes.toString(CryptoJS5.enc.Utf8);
10370
+ const bytes = CryptoJS6.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
10371
+ variable.value = bytes.toString(CryptoJS6.enc.Utf8);
10212
10372
  }
10213
10373
  return variable.value;
10214
10374
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exulu/backend",
3
3
  "author": "Qventu Bv.",
4
- "version": "1.30.5",
4
+ "version": "1.31.1",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {