@exulu/backend 1.30.5 → 1.31.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/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.0](https://github.com/Qventu/exulu-backend/compare/v1.30.5...v1.31.0) (2025-11-06)
2
2
 
3
3
 
4
- ### Bug Fixes
4
+ ### Features
5
5
 
6
- * bug if queue enum is empty ([85ef7bc](https://github.com/Qventu/exulu-backend/commit/85ef7bc8c8ff03e1890cb12cb82d6537ec65588f))
6
+ * implement per-agent MCP servers with project-level tracking ([63c8887](https://github.com/Qventu/exulu-backend/commit/63c88870d344e430625530d6e8eb93004d9af898))
package/dist/index.cjs CHANGED
@@ -437,8 +437,6 @@ var sanitizeName = (name) => {
437
437
 
438
438
  // src/registry/classes.ts
439
439
  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
440
 
443
441
  // src/registry/utils/graphql.ts
444
442
  var import_schema = require("@graphql-tools/schema");
@@ -1141,6 +1139,10 @@ var statisticsSchema = {
1141
1139
  {
1142
1140
  name: "role",
1143
1141
  type: "uuid"
1142
+ },
1143
+ {
1144
+ name: "project",
1145
+ type: "uuid"
1144
1146
  }
1145
1147
  ]
1146
1148
  };
@@ -4040,7 +4042,6 @@ type StatisticsResult {
4040
4042
  }
4041
4043
  `;
4042
4044
  const fullSDL = typeDefs + mutationDefs + modelDefs + genericTypes;
4043
- console.log("[EXULU] Full SDL:", fullSDL);
4044
4045
  const schema = (0, import_schema.makeExecutableSchema)({
4045
4046
  typeDefs: fullSDL,
4046
4047
  resolvers
@@ -4633,7 +4634,6 @@ var createUppyRoutes = async (app, config) => {
4633
4634
  };
4634
4635
 
4635
4636
  // src/registry/classes.ts
4636
- var import_streamableHttp2 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
4637
4637
  var s3Client2;
4638
4638
  function sanitizeToolName(name) {
4639
4639
  if (typeof name !== "string") return "";
@@ -4875,7 +4875,6 @@ var ExuluAgent2 = class {
4875
4875
  return typeof model === "string" ? model : model?.modelId || "";
4876
4876
  }
4877
4877
  // Exports the agent as a tool that can be used by another agent
4878
- // todo test this
4879
4878
  tool = async (instance, agents) => {
4880
4879
  const agentInstance = await loadAgent(instance);
4881
4880
  if (!agentInstance) {
@@ -5794,6 +5793,7 @@ var updateStatistic = async (statistic) => {
5794
5793
  const existing = await db3.from("tracking").where({
5795
5794
  ...statistic.user ? { user: statistic.user } : {},
5796
5795
  ...statistic.role ? { role: statistic.role } : {},
5796
+ ...statistic.project ? { project: statistic.project } : {},
5797
5797
  name: statistic.name,
5798
5798
  label: statistic.label,
5799
5799
  type: statistic.type,
@@ -5807,16 +5807,14 @@ var updateStatistic = async (statistic) => {
5807
5807
  total: statistic.count ?? 1,
5808
5808
  createdAt: currentDate,
5809
5809
  ...statistic.user ? { user: statistic.user } : {},
5810
- ...statistic.role ? { role: statistic.role } : {}
5810
+ ...statistic.role ? { role: statistic.role } : {},
5811
+ ...statistic.project ? { project: statistic.project } : {}
5811
5812
  });
5812
5813
  } else {
5813
5814
  await db3.from("tracking").update({
5814
5815
  total: db3.raw("total + ?", [statistic.count ?? 1])
5815
5816
  }).where({
5816
- name: statistic.name,
5817
- label: statistic.label,
5818
- type: statistic.type,
5819
- createdAt: currentDate
5817
+ id: existing.id
5820
5818
  });
5821
5819
  }
5822
5820
  };
@@ -5906,6 +5904,9 @@ var CLAUDE_MESSAGES = {
5906
5904
  \x1B[0m`,
5907
5905
  not_enabled: `
5908
5906
  \x1B[41m -- The agent you selected does not have a valid API key set for it. --
5907
+ \x1B[0m`,
5908
+ missing_project: `
5909
+ \x1B[41m -- Project not found or you do not have access to it. --
5909
5910
  \x1B[0m`
5910
5911
  };
5911
5912
 
@@ -6208,7 +6209,6 @@ Mood: friendly and intelligent.
6208
6209
  console.log("[EXULU] agent tools", agentInstance.tools?.map((x) => x.name + " (" + x.id + ")"));
6209
6210
  const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
6210
6211
  let enabledTools = await getEnabledTools(agentInstance, tools, disabledTools, agents, user);
6211
- console.log("[EXULU] enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
6212
6212
  const variableName = agentInstance.providerapikey;
6213
6213
  const variable = await db3.from("variables").where({ name: variableName }).first();
6214
6214
  if (!variable) {
@@ -6356,11 +6356,12 @@ Mood: friendly and intelligent.
6356
6356
  }
6357
6357
  });
6358
6358
  });
6359
- app.use("/gateway/anthropic/:id", import_express3.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
6359
+ app.use("/gateway/anthropic/:agent/:project", import_express3.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
6360
6360
  try {
6361
6361
  if (!req.body.tools) {
6362
6362
  req.body.tools = [];
6363
6363
  }
6364
+ const { db: db3 } = await postgresClient();
6364
6365
  const authenticationResult = await requestValidators.authenticate(req);
6365
6366
  if (!authenticationResult.user?.id) {
6366
6367
  console.log("[EXULU] failed authentication result", authenticationResult);
@@ -6368,20 +6369,35 @@ Mood: friendly and intelligent.
6368
6369
  return;
6369
6370
  }
6370
6371
  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();
6372
+ let agentQuery = db3("agents");
6373
+ agentQuery.select("*");
6374
+ agentQuery = applyAccessControl(agentsSchema2(), authenticationResult.user, agentQuery);
6375
+ agentQuery.where({ id: req.params.agent });
6376
+ const agent = await agentQuery.first();
6377
6377
  if (!agent) {
6378
6378
  const arrayBuffer = createCustomAnthropicStreamingMessage(`
6379
- \x1B[41m -- Agent ${req.params.id} not found or you do not have access to it. --
6379
+ \x1B[41m -- Agent ${req.params.agent} not found or you do not have access to it. --
6380
6380
  \x1B[0m`);
6381
6381
  res.setHeader("Content-Type", "application/json");
6382
6382
  res.end(Buffer.from(arrayBuffer));
6383
6383
  return;
6384
6384
  }
6385
+ let project = null;
6386
+ if (!req.body.project || req.body.project === "DEFAULT") {
6387
+ project = null;
6388
+ } else {
6389
+ let projectQuery = db3("projects");
6390
+ projectQuery.select("*");
6391
+ projectQuery = applyAccessControl(projectsSchema2(), authenticationResult.user, projectQuery);
6392
+ projectQuery.where({ id: req.params.project });
6393
+ project = await projectQuery.first();
6394
+ if (!project) {
6395
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_project);
6396
+ res.setHeader("Content-Type", "application/json");
6397
+ res.end(Buffer.from(arrayBuffer));
6398
+ return;
6399
+ }
6400
+ }
6385
6401
  console.log("[EXULU] anthropic proxy called for agent:", agent?.name);
6386
6402
  if (!process.env.NEXTAUTH_SECRET) {
6387
6403
  const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
@@ -6425,10 +6441,47 @@ Mood: friendly and intelligent.
6425
6441
  apiKey: anthropicApiKey
6426
6442
  });
6427
6443
  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
- }
6444
+ const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
6445
+ let enabledTools = await getEnabledTools(agent, tools, disabledTools, agents, user);
6446
+ let system = req.body.system;
6447
+ if (Array.isArray(req.body.system)) {
6448
+ system = [
6449
+ ...req.body.system,
6450
+ ...agent ? [
6451
+ {
6452
+ type: "text",
6453
+ text: `
6454
+ You are an agent named: ${agent?.name}
6455
+ Here are some additional instructions for you: ${agent?.instructions}`
6456
+ }
6457
+ ] : [],
6458
+ ...project ? [
6459
+ {
6460
+ type: "text",
6461
+ text: `Additional information:
6462
+
6463
+ The project you are working on is: ${project?.name}
6464
+ Here is some additional information about the project: ${project?.description}`
6465
+ }
6466
+ ] : []
6467
+ ];
6468
+ } else {
6469
+ system = `${req.body.system}
6470
+
6471
+
6472
+ ${agent ? `You are an agent named: ${agent?.name}
6473
+ Here are some additional instructions for you: ${agent?.instructions}` : ""}
6474
+
6475
+ ${project?.id ? `Additional information:
6476
+
6477
+ The project you are working on is: ${project?.name}
6478
+ The project description is: ${project?.description}` : ""}
6479
+ `;
6480
+ }
6481
+ for await (const event of client2.messages.stream({
6482
+ ...req.body,
6483
+ system
6484
+ })) {
6432
6485
  if (event.message?.id) {
6433
6486
  tokens[event.message.id] = {
6434
6487
  input_tokens: event.message.usage.input_tokens,
@@ -6437,11 +6490,45 @@ Mood: friendly and intelligent.
6437
6490
  output_tokens: event.message.usage.output_tokens
6438
6491
  };
6439
6492
  }
6440
- const msg = `event: ${event.type}
6493
+ if (event.message?.type === "tool_use" && event.message?.name?.includes("exulu_")) {
6494
+ const toolName = event.message?.name;
6495
+ console.log("[EXULU] Using tool", toolName);
6496
+ const inputs = event.message?.input;
6497
+ const id = event.message?.id;
6498
+ const tool2 = enabledTools.find((tool3) => tool3.id === toolName.replace("exulu_", ""));
6499
+ if (!tool2 || !tool2.tool.execute) {
6500
+ console.error("[EXULU] Tool not found or not enabled.", toolName);
6501
+ continue;
6502
+ }
6503
+ const toolResult = await tool2.tool.execute(inputs, {
6504
+ toolCallId: id,
6505
+ messages: [{
6506
+ ...event.message,
6507
+ role: "tool"
6508
+ }]
6509
+ });
6510
+ console.log("[EXULU] Tool result", toolResult);
6511
+ const toolResultMessage = {
6512
+ role: "user",
6513
+ content: [
6514
+ {
6515
+ type: "tool_result",
6516
+ tool_use_id: id,
6517
+ content: toolResult
6518
+ }
6519
+ ]
6520
+ };
6521
+ res.write(`event: tool_result
6522
+ data: ${JSON.stringify(toolResultMessage)}
6523
+
6524
+ `);
6525
+ } else {
6526
+ const msg = `event: ${event.type}
6441
6527
  data: ${JSON.stringify(event)}
6442
6528
 
6443
6529
  `;
6444
- res.write(msg);
6530
+ res.write(msg);
6531
+ }
6445
6532
  }
6446
6533
  let totalInputTokens = 0;
6447
6534
  let totalOutputTokens = 0;
@@ -6461,7 +6548,8 @@ data: ${JSON.stringify(event)}
6461
6548
  trigger: statistics.trigger,
6462
6549
  count: 1,
6463
6550
  user: user.id,
6464
- role: user?.role?.id
6551
+ role: user?.role?.id,
6552
+ ...project ? { project: project.id } : {}
6465
6553
  }),
6466
6554
  ...totalInputTokens ? [
6467
6555
  updateStatistic({
@@ -6471,7 +6559,8 @@ data: ${JSON.stringify(event)}
6471
6559
  trigger: statistics.trigger,
6472
6560
  count: totalInputTokens,
6473
6561
  user: user.id,
6474
- role: user?.role?.id
6562
+ role: user?.role?.id,
6563
+ ...project ? { project: project.id } : {}
6475
6564
  })
6476
6565
  ] : [],
6477
6566
  ...totalOutputTokens ? [
@@ -6480,7 +6569,10 @@ data: ${JSON.stringify(event)}
6480
6569
  label: statistics.label,
6481
6570
  type: STATISTICS_TYPE_ENUM.AGENT_RUN,
6482
6571
  trigger: statistics.trigger,
6483
- count: totalOutputTokens
6572
+ count: totalInputTokens,
6573
+ user: user.id,
6574
+ role: user?.role?.id,
6575
+ ...project ? { project: project.id } : {}
6484
6576
  })
6485
6577
  ] : []
6486
6578
  ]);
@@ -7199,97 +7291,159 @@ function getAverage(arr) {
7199
7291
  // src/mcp/index.ts
7200
7292
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
7201
7293
  var import_node_crypto4 = require("crypto");
7202
- var import_streamableHttp3 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
7294
+ var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
7203
7295
  var import_types = require("@modelcontextprotocol/sdk/types.js");
7204
- var import_zod2 = require("zod");
7205
7296
  var import_express4 = require("express");
7206
7297
  var import_api3 = require("@opentelemetry/api");
7298
+ var import_crypto_js5 = __toESM(require("crypto-js"), 1);
7299
+ var import_zod2 = require("zod");
7207
7300
  var SESSION_ID_HEADER = "mcp-session-id";
7208
7301
  var ExuluMCP = class {
7209
- server;
7302
+ server = {};
7210
7303
  transports = {};
7211
- express;
7212
7304
  constructor() {
7213
7305
  }
7214
- create = async ({ express: express3, contexts, agents, config, tools, tracer }) => {
7215
- this.express = express3;
7216
- if (!this.server) {
7306
+ configure = async ({ user, agentInstance, allTools, allAgents, allContexts, config, tracer }) => {
7307
+ let server = this.server[agentInstance.id];
7308
+ if (!server) {
7217
7309
  console.log("[EXULU] Creating MCP server.");
7218
- this.server = new import_mcp.McpServer({
7219
- name: "exulu-mcp-server",
7220
- version: "1.0.0"
7310
+ server = {
7311
+ mcp: new import_mcp.McpServer({
7312
+ name: "exulu-mcp-server-" + agentInstance.name + "(" + agentInstance.id + ")",
7313
+ version: "1.0.0"
7314
+ }),
7315
+ tools: {}
7316
+ };
7317
+ this.server[agentInstance.id] = server;
7318
+ }
7319
+ const disabledTools = [];
7320
+ let enabledTools = await getEnabledTools(agentInstance, allTools, disabledTools, allAgents, user);
7321
+ const backend = allAgents.find((a) => a.id === agentInstance.backend);
7322
+ if (!backend) {
7323
+ throw new Error("Agent backend not found for agent " + agentInstance.name + " (" + agentInstance.id + ").");
7324
+ }
7325
+ const agentTool = await backend.tool(agentInstance.id, allAgents);
7326
+ if (agentTool) {
7327
+ enabledTools = [
7328
+ ...enabledTools,
7329
+ agentTool
7330
+ ];
7331
+ }
7332
+ const variableName = agentInstance.providerapikey;
7333
+ const { db: db3 } = await postgresClient();
7334
+ const variable = await db3.from("variables").where({ name: variableName }).first();
7335
+ if (!variable) {
7336
+ throw new Error("Provider API key variable not found.");
7337
+ }
7338
+ let providerapikey = variable.value;
7339
+ if (!variable.encrypted) {
7340
+ throw new Error("Provider API key variable not encrypted, for security reasons you are only allowed to use encrypted variables for provider API keys.");
7341
+ }
7342
+ if (variable.encrypted) {
7343
+ const bytes = import_crypto_js5.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
7344
+ providerapikey = bytes.toString(import_crypto_js5.default.enc.Utf8);
7345
+ }
7346
+ console.log("[EXULU] Enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
7347
+ for (const tool2 of enabledTools || []) {
7348
+ if (server.tools[tool2.id]) {
7349
+ continue;
7350
+ }
7351
+ server.mcp.registerTool(sanitizeToolName(tool2.name + "_agent_" + tool2.id), {
7352
+ title: tool2.name + " agent",
7353
+ description: tool2.description,
7354
+ inputSchema: {
7355
+ inputs: tool2.inputSchema || import_zod2.z.object({})
7356
+ }
7357
+ }, async ({ inputs }, args) => {
7358
+ console.log("[EXULU] MCP tool name", tool2.name);
7359
+ console.log("[EXULU] MCP tool inputs", inputs);
7360
+ console.log("[EXULU] MCP tool args", args);
7361
+ const configValues = agentInstance.tools;
7362
+ const tools = convertToolsArrayToObject(
7363
+ [tool2],
7364
+ allTools,
7365
+ configValues,
7366
+ providerapikey,
7367
+ allContexts,
7368
+ user,
7369
+ config
7370
+ );
7371
+ const convertedTool = tools[sanitizeToolName(tool2.name)];
7372
+ if (!convertedTool?.execute) {
7373
+ console.error("[EXULU] Tool not found in converted tools array.", tools);
7374
+ throw new Error("Tool not found in converted tools array.");
7375
+ }
7376
+ const iterator = await convertedTool.execute(inputs, {
7377
+ toolCallId: tool2.id + "_" + (0, import_node_crypto4.randomUUID)(),
7378
+ messages: []
7379
+ });
7380
+ let result;
7381
+ for await (const value of iterator) {
7382
+ result = value;
7383
+ }
7384
+ console.log("[EXULU] MCP tool result", result);
7385
+ return {
7386
+ content: [{ type: "text", text: JSON.stringify(result) }],
7387
+ structuredContent: result
7388
+ };
7221
7389
  });
7390
+ server.tools[tool2.id] = tool2.name;
7222
7391
  }
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
- );
7392
+ return server.mcp;
7275
7393
  };
7276
- connect = async () => {
7277
- if (!this.express) {
7394
+ create = async ({ express: express3, allTools, allAgents, allContexts, config }) => {
7395
+ if (!express3) {
7278
7396
  throw new Error("Express not initialized.");
7279
7397
  }
7280
7398
  if (!this.server) {
7281
7399
  throw new Error("MCP server not initialized.");
7282
7400
  }
7283
- this.express.post("/mcp", async (req, res) => {
7284
- if (!this.server) {
7285
- throw new Error("MCP server not initialized.");
7401
+ express3.post("/mcp/:agent", async (req, res) => {
7402
+ console.log("[EXULU] MCP request received.", req.params.agent);
7403
+ if (!req.params.agent) {
7404
+ res.status(400).json({
7405
+ error: "Bad Request: No agent ID provided"
7406
+ });
7407
+ return;
7408
+ }
7409
+ const agentInstance = await loadAgent(req.params.agent);
7410
+ if (!agentInstance) {
7411
+ console.error("[EXULU] Agent not found.", req.params.agent);
7412
+ res.status(404).json({
7413
+ error: "Agent not found"
7414
+ });
7415
+ return;
7416
+ }
7417
+ const authenticationResult = await requestValidators.authenticate(req);
7418
+ if (!authenticationResult.user?.id) {
7419
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
7420
+ return;
7421
+ }
7422
+ const user = authenticationResult.user;
7423
+ const hasAccessToAgent = await checkRecordAccess(agentInstance, "read", user);
7424
+ if (!hasAccessToAgent) {
7425
+ res.status(401).json({
7426
+ message: "You don't have access to this agent."
7427
+ });
7428
+ return;
7429
+ }
7430
+ const server = await this.configure({
7431
+ agentInstance,
7432
+ user,
7433
+ allTools,
7434
+ allAgents,
7435
+ allContexts,
7436
+ config
7437
+ });
7438
+ if (!server) {
7439
+ throw new Error("MCP server for agent " + req.params.agent + " not initialized.");
7286
7440
  }
7287
7441
  const sessionId = req.headers[SESSION_ID_HEADER];
7288
7442
  let transport;
7289
7443
  if (sessionId && this.transports[sessionId]) {
7290
7444
  transport = this.transports[sessionId];
7291
7445
  } else if (!sessionId && (0, import_types.isInitializeRequest)(req.body)) {
7292
- transport = new import_streamableHttp3.StreamableHTTPServerTransport({
7446
+ transport = new import_streamableHttp.StreamableHTTPServerTransport({
7293
7447
  sessionIdGenerator: () => (0, import_node_crypto4.randomUUID)(),
7294
7448
  onsessioninitialized: (sessionId2) => {
7295
7449
  this.transports[sessionId2] = transport;
@@ -7304,13 +7458,14 @@ ${code}`
7304
7458
  delete this.transports[transport.sessionId];
7305
7459
  }
7306
7460
  };
7307
- await this.server.connect(transport);
7461
+ await server.connect(transport);
7308
7462
  } else {
7309
7463
  res.status(400).json({
7310
7464
  error: "Bad Request: No valid session ID provided"
7311
7465
  });
7312
7466
  return;
7313
7467
  }
7468
+ req.headers["exulu-agent-id"] = req.params.agent;
7314
7469
  await transport.handleRequest(req, res, req.body);
7315
7470
  });
7316
7471
  const handleSessionRequest = async (req, res) => {
@@ -7323,9 +7478,10 @@ ${code}`
7323
7478
  const transport = this.transports[sessionId];
7324
7479
  await transport.handleRequest(req, res);
7325
7480
  };
7326
- this.express.get("/mcp", handleSessionRequest);
7327
- this.express.delete("/mcp", handleSessionRequest);
7328
- return this.express;
7481
+ express3.get("/mcp/:agent", handleSessionRequest);
7482
+ express3.delete("/mcp/:agent", handleSessionRequest);
7483
+ console.log("[EXULU] MCP server created.");
7484
+ return express3;
7329
7485
  };
7330
7486
  };
7331
7487
 
@@ -8760,13 +8916,11 @@ var ExuluApp = class {
8760
8916
  const mcp = new ExuluMCP();
8761
8917
  await mcp.create({
8762
8918
  express: app,
8763
- contexts: this._contexts,
8764
- agents: this._agents,
8765
- config: this._config,
8766
- tools: this._tools,
8767
- tracer
8919
+ allTools: this._tools,
8920
+ allAgents: this._agents,
8921
+ allContexts: Object.values(this._contexts ?? {}),
8922
+ config: this._config
8768
8923
  });
8769
- await mcp.connect();
8770
8924
  }
8771
8925
  return app;
8772
8926
  }
@@ -10203,7 +10357,7 @@ var create = ({
10203
10357
  };
10204
10358
 
10205
10359
  // src/index.ts
10206
- var import_crypto_js5 = __toESM(require("crypto-js"), 1);
10360
+ var import_crypto_js6 = __toESM(require("crypto-js"), 1);
10207
10361
  var ExuluJobs = {
10208
10362
  redis: redisClient,
10209
10363
  jobs: {
@@ -10240,8 +10394,8 @@ var ExuluVariables = {
10240
10394
  throw new Error(`Variable ${name} not found.`);
10241
10395
  }
10242
10396
  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);
10397
+ const bytes = import_crypto_js6.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
10398
+ variable.value = bytes.toString(import_crypto_js6.default.enc.Utf8);
10245
10399
  }
10246
10400
  return variable.value;
10247
10401
  }
package/dist/index.js CHANGED
@@ -385,8 +385,6 @@ var sanitizeName = (name) => {
385
385
 
386
386
  // src/registry/classes.ts
387
387
  import CryptoJS2 from "crypto-js";
388
- import "@modelcontextprotocol/sdk/client/streamableHttp.js";
389
- import "ai/mcp-stdio";
390
388
 
391
389
  // src/registry/utils/graphql.ts
392
390
  import { makeExecutableSchema } from "@graphql-tools/schema";
@@ -1089,6 +1087,10 @@ var statisticsSchema = {
1089
1087
  {
1090
1088
  name: "role",
1091
1089
  type: "uuid"
1090
+ },
1091
+ {
1092
+ name: "project",
1093
+ type: "uuid"
1092
1094
  }
1093
1095
  ]
1094
1096
  };
@@ -3988,7 +3990,6 @@ type StatisticsResult {
3988
3990
  }
3989
3991
  `;
3990
3992
  const fullSDL = typeDefs + mutationDefs + modelDefs + genericTypes;
3991
- console.log("[EXULU] Full SDL:", fullSDL);
3992
3993
  const schema = makeExecutableSchema({
3993
3994
  typeDefs: fullSDL,
3994
3995
  resolvers
@@ -4600,7 +4601,6 @@ var createUppyRoutes = async (app, config) => {
4600
4601
  };
4601
4602
 
4602
4603
  // src/registry/classes.ts
4603
- import "@modelcontextprotocol/sdk/server/streamableHttp.js";
4604
4604
  var s3Client2;
4605
4605
  function sanitizeToolName(name) {
4606
4606
  if (typeof name !== "string") return "";
@@ -4842,7 +4842,6 @@ var ExuluAgent2 = class {
4842
4842
  return typeof model === "string" ? model : model?.modelId || "";
4843
4843
  }
4844
4844
  // Exports the agent as a tool that can be used by another agent
4845
- // todo test this
4846
4845
  tool = async (instance, agents) => {
4847
4846
  const agentInstance = await loadAgent(instance);
4848
4847
  if (!agentInstance) {
@@ -5761,6 +5760,7 @@ var updateStatistic = async (statistic) => {
5761
5760
  const existing = await db3.from("tracking").where({
5762
5761
  ...statistic.user ? { user: statistic.user } : {},
5763
5762
  ...statistic.role ? { role: statistic.role } : {},
5763
+ ...statistic.project ? { project: statistic.project } : {},
5764
5764
  name: statistic.name,
5765
5765
  label: statistic.label,
5766
5766
  type: statistic.type,
@@ -5774,16 +5774,14 @@ var updateStatistic = async (statistic) => {
5774
5774
  total: statistic.count ?? 1,
5775
5775
  createdAt: currentDate,
5776
5776
  ...statistic.user ? { user: statistic.user } : {},
5777
- ...statistic.role ? { role: statistic.role } : {}
5777
+ ...statistic.role ? { role: statistic.role } : {},
5778
+ ...statistic.project ? { project: statistic.project } : {}
5778
5779
  });
5779
5780
  } else {
5780
5781
  await db3.from("tracking").update({
5781
5782
  total: db3.raw("total + ?", [statistic.count ?? 1])
5782
5783
  }).where({
5783
- name: statistic.name,
5784
- label: statistic.label,
5785
- type: statistic.type,
5786
- createdAt: currentDate
5784
+ id: existing.id
5787
5785
  });
5788
5786
  }
5789
5787
  };
@@ -5873,6 +5871,9 @@ var CLAUDE_MESSAGES = {
5873
5871
  \x1B[0m`,
5874
5872
  not_enabled: `
5875
5873
  \x1B[41m -- The agent you selected does not have a valid API key set for it. --
5874
+ \x1B[0m`,
5875
+ missing_project: `
5876
+ \x1B[41m -- Project not found or you do not have access to it. --
5876
5877
  \x1B[0m`
5877
5878
  };
5878
5879
 
@@ -6175,7 +6176,6 @@ Mood: friendly and intelligent.
6175
6176
  console.log("[EXULU] agent tools", agentInstance.tools?.map((x) => x.name + " (" + x.id + ")"));
6176
6177
  const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
6177
6178
  let enabledTools = await getEnabledTools(agentInstance, tools, disabledTools, agents, user);
6178
- console.log("[EXULU] enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
6179
6179
  const variableName = agentInstance.providerapikey;
6180
6180
  const variable = await db3.from("variables").where({ name: variableName }).first();
6181
6181
  if (!variable) {
@@ -6323,11 +6323,12 @@ Mood: friendly and intelligent.
6323
6323
  }
6324
6324
  });
6325
6325
  });
6326
- app.use("/gateway/anthropic/:id", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
6326
+ app.use("/gateway/anthropic/:agent/:project", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
6327
6327
  try {
6328
6328
  if (!req.body.tools) {
6329
6329
  req.body.tools = [];
6330
6330
  }
6331
+ const { db: db3 } = await postgresClient();
6331
6332
  const authenticationResult = await requestValidators.authenticate(req);
6332
6333
  if (!authenticationResult.user?.id) {
6333
6334
  console.log("[EXULU] failed authentication result", authenticationResult);
@@ -6335,20 +6336,35 @@ Mood: friendly and intelligent.
6335
6336
  return;
6336
6337
  }
6337
6338
  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();
6339
+ let agentQuery = db3("agents");
6340
+ agentQuery.select("*");
6341
+ agentQuery = applyAccessControl(agentsSchema2(), authenticationResult.user, agentQuery);
6342
+ agentQuery.where({ id: req.params.agent });
6343
+ const agent = await agentQuery.first();
6344
6344
  if (!agent) {
6345
6345
  const arrayBuffer = createCustomAnthropicStreamingMessage(`
6346
- \x1B[41m -- Agent ${req.params.id} not found or you do not have access to it. --
6346
+ \x1B[41m -- Agent ${req.params.agent} not found or you do not have access to it. --
6347
6347
  \x1B[0m`);
6348
6348
  res.setHeader("Content-Type", "application/json");
6349
6349
  res.end(Buffer.from(arrayBuffer));
6350
6350
  return;
6351
6351
  }
6352
+ let project = null;
6353
+ if (!req.body.project || req.body.project === "DEFAULT") {
6354
+ project = null;
6355
+ } else {
6356
+ let projectQuery = db3("projects");
6357
+ projectQuery.select("*");
6358
+ projectQuery = applyAccessControl(projectsSchema2(), authenticationResult.user, projectQuery);
6359
+ projectQuery.where({ id: req.params.project });
6360
+ project = await projectQuery.first();
6361
+ if (!project) {
6362
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_project);
6363
+ res.setHeader("Content-Type", "application/json");
6364
+ res.end(Buffer.from(arrayBuffer));
6365
+ return;
6366
+ }
6367
+ }
6352
6368
  console.log("[EXULU] anthropic proxy called for agent:", agent?.name);
6353
6369
  if (!process.env.NEXTAUTH_SECRET) {
6354
6370
  const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.missing_nextauth_secret);
@@ -6392,10 +6408,47 @@ Mood: friendly and intelligent.
6392
6408
  apiKey: anthropicApiKey
6393
6409
  });
6394
6410
  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
- }
6411
+ const disabledTools = req.body.disabledTools ? req.body.disabledTools : [];
6412
+ let enabledTools = await getEnabledTools(agent, tools, disabledTools, agents, user);
6413
+ let system = req.body.system;
6414
+ if (Array.isArray(req.body.system)) {
6415
+ system = [
6416
+ ...req.body.system,
6417
+ ...agent ? [
6418
+ {
6419
+ type: "text",
6420
+ text: `
6421
+ You are an agent named: ${agent?.name}
6422
+ Here are some additional instructions for you: ${agent?.instructions}`
6423
+ }
6424
+ ] : [],
6425
+ ...project ? [
6426
+ {
6427
+ type: "text",
6428
+ text: `Additional information:
6429
+
6430
+ The project you are working on is: ${project?.name}
6431
+ Here is some additional information about the project: ${project?.description}`
6432
+ }
6433
+ ] : []
6434
+ ];
6435
+ } else {
6436
+ system = `${req.body.system}
6437
+
6438
+
6439
+ ${agent ? `You are an agent named: ${agent?.name}
6440
+ Here are some additional instructions for you: ${agent?.instructions}` : ""}
6441
+
6442
+ ${project?.id ? `Additional information:
6443
+
6444
+ The project you are working on is: ${project?.name}
6445
+ The project description is: ${project?.description}` : ""}
6446
+ `;
6447
+ }
6448
+ for await (const event of client2.messages.stream({
6449
+ ...req.body,
6450
+ system
6451
+ })) {
6399
6452
  if (event.message?.id) {
6400
6453
  tokens[event.message.id] = {
6401
6454
  input_tokens: event.message.usage.input_tokens,
@@ -6404,11 +6457,45 @@ Mood: friendly and intelligent.
6404
6457
  output_tokens: event.message.usage.output_tokens
6405
6458
  };
6406
6459
  }
6407
- const msg = `event: ${event.type}
6460
+ if (event.message?.type === "tool_use" && event.message?.name?.includes("exulu_")) {
6461
+ const toolName = event.message?.name;
6462
+ console.log("[EXULU] Using tool", toolName);
6463
+ const inputs = event.message?.input;
6464
+ const id = event.message?.id;
6465
+ const tool2 = enabledTools.find((tool3) => tool3.id === toolName.replace("exulu_", ""));
6466
+ if (!tool2 || !tool2.tool.execute) {
6467
+ console.error("[EXULU] Tool not found or not enabled.", toolName);
6468
+ continue;
6469
+ }
6470
+ const toolResult = await tool2.tool.execute(inputs, {
6471
+ toolCallId: id,
6472
+ messages: [{
6473
+ ...event.message,
6474
+ role: "tool"
6475
+ }]
6476
+ });
6477
+ console.log("[EXULU] Tool result", toolResult);
6478
+ const toolResultMessage = {
6479
+ role: "user",
6480
+ content: [
6481
+ {
6482
+ type: "tool_result",
6483
+ tool_use_id: id,
6484
+ content: toolResult
6485
+ }
6486
+ ]
6487
+ };
6488
+ res.write(`event: tool_result
6489
+ data: ${JSON.stringify(toolResultMessage)}
6490
+
6491
+ `);
6492
+ } else {
6493
+ const msg = `event: ${event.type}
6408
6494
  data: ${JSON.stringify(event)}
6409
6495
 
6410
6496
  `;
6411
- res.write(msg);
6497
+ res.write(msg);
6498
+ }
6412
6499
  }
6413
6500
  let totalInputTokens = 0;
6414
6501
  let totalOutputTokens = 0;
@@ -6428,7 +6515,8 @@ data: ${JSON.stringify(event)}
6428
6515
  trigger: statistics.trigger,
6429
6516
  count: 1,
6430
6517
  user: user.id,
6431
- role: user?.role?.id
6518
+ role: user?.role?.id,
6519
+ ...project ? { project: project.id } : {}
6432
6520
  }),
6433
6521
  ...totalInputTokens ? [
6434
6522
  updateStatistic({
@@ -6438,7 +6526,8 @@ data: ${JSON.stringify(event)}
6438
6526
  trigger: statistics.trigger,
6439
6527
  count: totalInputTokens,
6440
6528
  user: user.id,
6441
- role: user?.role?.id
6529
+ role: user?.role?.id,
6530
+ ...project ? { project: project.id } : {}
6442
6531
  })
6443
6532
  ] : [],
6444
6533
  ...totalOutputTokens ? [
@@ -6447,7 +6536,10 @@ data: ${JSON.stringify(event)}
6447
6536
  label: statistics.label,
6448
6537
  type: STATISTICS_TYPE_ENUM.AGENT_RUN,
6449
6538
  trigger: statistics.trigger,
6450
- count: totalOutputTokens
6539
+ count: totalInputTokens,
6540
+ user: user.id,
6541
+ role: user?.role?.id,
6542
+ ...project ? { project: project.id } : {}
6451
6543
  })
6452
6544
  ] : []
6453
6545
  ]);
@@ -7164,99 +7256,161 @@ function getAverage(arr) {
7164
7256
  }
7165
7257
 
7166
7258
  // src/mcp/index.ts
7167
- import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
7259
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7168
7260
  import { randomUUID as randomUUID4 } from "crypto";
7169
- import { StreamableHTTPServerTransport as StreamableHTTPServerTransport2 } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7261
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7170
7262
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
7171
- import { z as z2 } from "zod";
7172
7263
  import "express";
7173
7264
  import "@opentelemetry/api";
7265
+ import CryptoJS5 from "crypto-js";
7266
+ import { z as z2 } from "zod";
7174
7267
  var SESSION_ID_HEADER = "mcp-session-id";
7175
7268
  var ExuluMCP = class {
7176
- server;
7269
+ server = {};
7177
7270
  transports = {};
7178
- express;
7179
7271
  constructor() {
7180
7272
  }
7181
- create = async ({ express: express3, contexts, agents, config, tools, tracer }) => {
7182
- this.express = express3;
7183
- if (!this.server) {
7273
+ configure = async ({ user, agentInstance, allTools, allAgents, allContexts, config, tracer }) => {
7274
+ let server = this.server[agentInstance.id];
7275
+ if (!server) {
7184
7276
  console.log("[EXULU] Creating MCP server.");
7185
- this.server = new McpServer({
7186
- name: "exulu-mcp-server",
7187
- version: "1.0.0"
7277
+ server = {
7278
+ mcp: new McpServer({
7279
+ name: "exulu-mcp-server-" + agentInstance.name + "(" + agentInstance.id + ")",
7280
+ version: "1.0.0"
7281
+ }),
7282
+ tools: {}
7283
+ };
7284
+ this.server[agentInstance.id] = server;
7285
+ }
7286
+ const disabledTools = [];
7287
+ let enabledTools = await getEnabledTools(agentInstance, allTools, disabledTools, allAgents, user);
7288
+ const backend = allAgents.find((a) => a.id === agentInstance.backend);
7289
+ if (!backend) {
7290
+ throw new Error("Agent backend not found for agent " + agentInstance.name + " (" + agentInstance.id + ").");
7291
+ }
7292
+ const agentTool = await backend.tool(agentInstance.id, allAgents);
7293
+ if (agentTool) {
7294
+ enabledTools = [
7295
+ ...enabledTools,
7296
+ agentTool
7297
+ ];
7298
+ }
7299
+ const variableName = agentInstance.providerapikey;
7300
+ const { db: db3 } = await postgresClient();
7301
+ const variable = await db3.from("variables").where({ name: variableName }).first();
7302
+ if (!variable) {
7303
+ throw new Error("Provider API key variable not found.");
7304
+ }
7305
+ let providerapikey = variable.value;
7306
+ if (!variable.encrypted) {
7307
+ throw new Error("Provider API key variable not encrypted, for security reasons you are only allowed to use encrypted variables for provider API keys.");
7308
+ }
7309
+ if (variable.encrypted) {
7310
+ const bytes = CryptoJS5.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
7311
+ providerapikey = bytes.toString(CryptoJS5.enc.Utf8);
7312
+ }
7313
+ console.log("[EXULU] Enabled tools", enabledTools?.map((x) => x.name + " (" + x.id + ")"));
7314
+ for (const tool2 of enabledTools || []) {
7315
+ if (server.tools[tool2.id]) {
7316
+ continue;
7317
+ }
7318
+ server.mcp.registerTool(sanitizeToolName(tool2.name + "_agent_" + tool2.id), {
7319
+ title: tool2.name + " agent",
7320
+ description: tool2.description,
7321
+ inputSchema: {
7322
+ inputs: tool2.inputSchema || z2.object({})
7323
+ }
7324
+ }, async ({ inputs }, args) => {
7325
+ console.log("[EXULU] MCP tool name", tool2.name);
7326
+ console.log("[EXULU] MCP tool inputs", inputs);
7327
+ console.log("[EXULU] MCP tool args", args);
7328
+ const configValues = agentInstance.tools;
7329
+ const tools = convertToolsArrayToObject(
7330
+ [tool2],
7331
+ allTools,
7332
+ configValues,
7333
+ providerapikey,
7334
+ allContexts,
7335
+ user,
7336
+ config
7337
+ );
7338
+ const convertedTool = tools[sanitizeToolName(tool2.name)];
7339
+ if (!convertedTool?.execute) {
7340
+ console.error("[EXULU] Tool not found in converted tools array.", tools);
7341
+ throw new Error("Tool not found in converted tools array.");
7342
+ }
7343
+ const iterator = await convertedTool.execute(inputs, {
7344
+ toolCallId: tool2.id + "_" + randomUUID4(),
7345
+ messages: []
7346
+ });
7347
+ let result;
7348
+ for await (const value of iterator) {
7349
+ result = value;
7350
+ }
7351
+ console.log("[EXULU] MCP tool result", result);
7352
+ return {
7353
+ content: [{ type: "text", text: JSON.stringify(result) }],
7354
+ structuredContent: result
7355
+ };
7188
7356
  });
7357
+ server.tools[tool2.id] = tool2.name;
7189
7358
  }
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
- );
7359
+ return server.mcp;
7242
7360
  };
7243
- connect = async () => {
7244
- if (!this.express) {
7361
+ create = async ({ express: express3, allTools, allAgents, allContexts, config }) => {
7362
+ if (!express3) {
7245
7363
  throw new Error("Express not initialized.");
7246
7364
  }
7247
7365
  if (!this.server) {
7248
7366
  throw new Error("MCP server not initialized.");
7249
7367
  }
7250
- this.express.post("/mcp", async (req, res) => {
7251
- if (!this.server) {
7252
- throw new Error("MCP server not initialized.");
7368
+ express3.post("/mcp/:agent", async (req, res) => {
7369
+ console.log("[EXULU] MCP request received.", req.params.agent);
7370
+ if (!req.params.agent) {
7371
+ res.status(400).json({
7372
+ error: "Bad Request: No agent ID provided"
7373
+ });
7374
+ return;
7375
+ }
7376
+ const agentInstance = await loadAgent(req.params.agent);
7377
+ if (!agentInstance) {
7378
+ console.error("[EXULU] Agent not found.", req.params.agent);
7379
+ res.status(404).json({
7380
+ error: "Agent not found"
7381
+ });
7382
+ return;
7383
+ }
7384
+ const authenticationResult = await requestValidators.authenticate(req);
7385
+ if (!authenticationResult.user?.id) {
7386
+ res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
7387
+ return;
7388
+ }
7389
+ const user = authenticationResult.user;
7390
+ const hasAccessToAgent = await checkRecordAccess(agentInstance, "read", user);
7391
+ if (!hasAccessToAgent) {
7392
+ res.status(401).json({
7393
+ message: "You don't have access to this agent."
7394
+ });
7395
+ return;
7396
+ }
7397
+ const server = await this.configure({
7398
+ agentInstance,
7399
+ user,
7400
+ allTools,
7401
+ allAgents,
7402
+ allContexts,
7403
+ config
7404
+ });
7405
+ if (!server) {
7406
+ throw new Error("MCP server for agent " + req.params.agent + " not initialized.");
7253
7407
  }
7254
7408
  const sessionId = req.headers[SESSION_ID_HEADER];
7255
7409
  let transport;
7256
7410
  if (sessionId && this.transports[sessionId]) {
7257
7411
  transport = this.transports[sessionId];
7258
7412
  } else if (!sessionId && isInitializeRequest(req.body)) {
7259
- transport = new StreamableHTTPServerTransport2({
7413
+ transport = new StreamableHTTPServerTransport({
7260
7414
  sessionIdGenerator: () => randomUUID4(),
7261
7415
  onsessioninitialized: (sessionId2) => {
7262
7416
  this.transports[sessionId2] = transport;
@@ -7271,13 +7425,14 @@ ${code}`
7271
7425
  delete this.transports[transport.sessionId];
7272
7426
  }
7273
7427
  };
7274
- await this.server.connect(transport);
7428
+ await server.connect(transport);
7275
7429
  } else {
7276
7430
  res.status(400).json({
7277
7431
  error: "Bad Request: No valid session ID provided"
7278
7432
  });
7279
7433
  return;
7280
7434
  }
7435
+ req.headers["exulu-agent-id"] = req.params.agent;
7281
7436
  await transport.handleRequest(req, res, req.body);
7282
7437
  });
7283
7438
  const handleSessionRequest = async (req, res) => {
@@ -7290,9 +7445,10 @@ ${code}`
7290
7445
  const transport = this.transports[sessionId];
7291
7446
  await transport.handleRequest(req, res);
7292
7447
  };
7293
- this.express.get("/mcp", handleSessionRequest);
7294
- this.express.delete("/mcp", handleSessionRequest);
7295
- return this.express;
7448
+ express3.get("/mcp/:agent", handleSessionRequest);
7449
+ express3.delete("/mcp/:agent", handleSessionRequest);
7450
+ console.log("[EXULU] MCP server created.");
7451
+ return express3;
7296
7452
  };
7297
7453
  };
7298
7454
 
@@ -8727,13 +8883,11 @@ var ExuluApp = class {
8727
8883
  const mcp = new ExuluMCP();
8728
8884
  await mcp.create({
8729
8885
  express: app,
8730
- contexts: this._contexts,
8731
- agents: this._agents,
8732
- config: this._config,
8733
- tools: this._tools,
8734
- tracer
8886
+ allTools: this._tools,
8887
+ allAgents: this._agents,
8888
+ allContexts: Object.values(this._contexts ?? {}),
8889
+ config: this._config
8735
8890
  });
8736
- await mcp.connect();
8737
8891
  }
8738
8892
  return app;
8739
8893
  }
@@ -10170,7 +10324,7 @@ var create = ({
10170
10324
  };
10171
10325
 
10172
10326
  // src/index.ts
10173
- import CryptoJS5 from "crypto-js";
10327
+ import CryptoJS6 from "crypto-js";
10174
10328
  var ExuluJobs = {
10175
10329
  redis: redisClient,
10176
10330
  jobs: {
@@ -10207,8 +10361,8 @@ var ExuluVariables = {
10207
10361
  throw new Error(`Variable ${name} not found.`);
10208
10362
  }
10209
10363
  if (variable.encrypted) {
10210
- const bytes = CryptoJS5.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
10211
- variable.value = bytes.toString(CryptoJS5.enc.Utf8);
10364
+ const bytes = CryptoJS6.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
10365
+ variable.value = bytes.toString(CryptoJS6.enc.Utf8);
10212
10366
  }
10213
10367
  return variable.value;
10214
10368
  }
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.0",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {