@danainnovations/cortex-mcp 1.0.39 → 1.0.40

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/index.d.ts CHANGED
@@ -141,7 +141,7 @@ declare const AVAILABLE_MCPS: readonly [{
141
141
  }, {
142
142
  readonly name: "m365";
143
143
  readonly displayName: "Microsoft 365";
144
- readonly description: "Email, calendar, OneDrive, Teams, meetings, contacts, tasks, notes (32 tools)";
144
+ readonly description: "Email, calendar, OneDrive, Teams, meetings, contacts, tasks, notes (33 tools)";
145
145
  readonly serverName: "cortex-m365";
146
146
  readonly authMode: "personal";
147
147
  }, {
@@ -189,4 +189,119 @@ declare function readCredentials(): CortexCredentials | null;
189
189
  */
190
190
  declare function getEffectiveApiKey(): string;
191
191
 
192
- export { AVAILABLE_MCPS, type ClientType, type CortexCredentials, CortexHttpClient, type CortexMcpConfig, DEFAULT_API_KEY, DEFAULT_SERVER_URL, type DetectedClient, MCP_NAMES, configureClaudeCode, configureClaudeDesktop, configureClient, configureCursor, createConfig, detectClients, generateStdioSnippet, getEffectiveApiKey, isValidApiKey, readConfig, readCredentials, startStdioServer, validateApiKeyRemote, writeConfig };
192
+ /** Metadata for a single MCP tool */
193
+ interface ToolInfo {
194
+ name: string;
195
+ description: string;
196
+ inputSchema: Record<string, unknown>;
197
+ }
198
+ /** Base error class for all Cortex SDK errors */
199
+ declare class CortexError extends Error {
200
+ readonly mcpName?: string | undefined;
201
+ constructor(message: string, mcpName?: string | undefined);
202
+ }
203
+ /**
204
+ * Thrown when a tool executes but returns isError: true
205
+ * (e.g., bad SOQL query, missing OAuth connection, invalid record ID).
206
+ */
207
+ declare class CortexToolError extends CortexError {
208
+ readonly mcpName: string;
209
+ readonly toolName: string;
210
+ constructor(message: string, mcpName: string, toolName: string);
211
+ }
212
+ /**
213
+ * Thrown on transport/auth failures — the tool never executed
214
+ * (e.g., invalid API key, network error, server down).
215
+ */
216
+ declare class CortexNetworkError extends CortexError {
217
+ readonly code?: number | undefined;
218
+ constructor(message: string, code?: number | undefined, mcpName?: string);
219
+ }
220
+ /**
221
+ * Provides a call interface for a single Cortex MCP.
222
+ * Lazily initializes the underlying HTTP session on first use.
223
+ */
224
+ declare class MCPNamespace {
225
+ readonly mcpName: string;
226
+ private readonly client;
227
+ private initPromise;
228
+ constructor(mcpName: string, serverUrl: string, apiKey: string);
229
+ private ensureInitialized;
230
+ private doInit;
231
+ /**
232
+ * Call a tool on this MCP. Returns the parsed result.
233
+ *
234
+ * @example
235
+ * const ns = new MCPNamespace('salesforce', serverUrl, apiKey);
236
+ * const result = await ns.call('run_soql_query', {
237
+ * query: 'SELECT Id, Name FROM Account LIMIT 10'
238
+ * });
239
+ */
240
+ call(toolName: string, args?: Record<string, unknown>): Promise<unknown>;
241
+ /** List all tools available on this MCP. */
242
+ listTools(): Promise<ToolInfo[]>;
243
+ /** Release the session. The next call() will re-initialize. */
244
+ close(): void;
245
+ }
246
+
247
+ interface CortexClientOptions {
248
+ /** API key. Defaults to auto-resolved key (env > credentials > config > shared). */
249
+ apiKey?: string;
250
+ /** Cortex server URL. Defaults to https://cortex-bice.vercel.app */
251
+ serverUrl?: string;
252
+ }
253
+ /**
254
+ * High-level SDK client for calling Cortex MCP tools from custom apps.
255
+ * Auto-resolves credentials and lazily initializes per-MCP sessions.
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * import { CortexClient } from '@danainnovations/cortex-mcp';
260
+ *
261
+ * const cortex = new CortexClient();
262
+ *
263
+ * // Salesforce — no Connected App needed
264
+ * const accounts = await cortex.salesforce.call('run_soql_query', {
265
+ * query: 'SELECT Id, Name FROM Account LIMIT 10'
266
+ * });
267
+ *
268
+ * // Microsoft 365
269
+ * const emails = await cortex.m365.call('list_emails', { count: 5 });
270
+ *
271
+ * // GitHub
272
+ * const repos = await cortex.github.call('list_repositories', { per_page: 5 });
273
+ *
274
+ * // Cleanup when done
275
+ * cortex.close();
276
+ * ```
277
+ */
278
+ declare class CortexClient {
279
+ private readonly apiKey;
280
+ private readonly serverUrl;
281
+ private readonly namespaces;
282
+ constructor(options?: CortexClientOptions);
283
+ get asana(): MCPNamespace;
284
+ get github(): MCPNamespace;
285
+ get vercel(): MCPNamespace;
286
+ get supabase(): MCPNamespace;
287
+ get m365(): MCPNamespace;
288
+ get salesforce(): MCPNamespace;
289
+ get monday(): MCPNamespace;
290
+ get slack(): MCPNamespace;
291
+ get bestbuy(): MCPNamespace;
292
+ /**
293
+ * Call any MCP tool when the MCP name is determined at runtime.
294
+ *
295
+ * @example
296
+ * const result = await cortex.call('github', 'list_repositories', { per_page: 5 });
297
+ */
298
+ call(mcpName: string, toolName: string, args?: Record<string, unknown>): Promise<unknown>;
299
+ /**
300
+ * Release all sessions. Safe to call multiple times.
301
+ * The next call after close() will re-initialize automatically.
302
+ */
303
+ close(): void;
304
+ private getNamespace;
305
+ }
306
+
307
+ export { AVAILABLE_MCPS, type ClientType, CortexClient, type CortexClientOptions, type CortexCredentials, CortexError, CortexHttpClient, type CortexMcpConfig, CortexNetworkError, CortexToolError, DEFAULT_API_KEY, DEFAULT_SERVER_URL, type DetectedClient, MCPNamespace, MCP_NAMES, type ToolInfo, configureClaudeCode, configureClaudeDesktop, configureClient, configureCursor, createConfig, detectClients, generateStdioSnippet, getEffectiveApiKey, isValidApiKey, readConfig, readCredentials, startStdioServer, validateApiKeyRemote, writeConfig };
package/dist/index.js CHANGED
@@ -33,7 +33,7 @@ var AVAILABLE_MCPS = [
33
33
  {
34
34
  name: "m365",
35
35
  displayName: "Microsoft 365",
36
- description: "Email, calendar, OneDrive, Teams, meetings, contacts, tasks, notes (32 tools)",
36
+ description: "Email, calendar, OneDrive, Teams, meetings, contacts, tasks, notes (33 tools)",
37
37
  serverName: "cortex-m365",
38
38
  authMode: "personal"
39
39
  },
@@ -107,7 +107,8 @@ var CortexHttpClient = class {
107
107
  const headers = {
108
108
  "Content-Type": "application/json",
109
109
  "x-api-key": this.apiKey,
110
- "mcp-protocol-version": PROTOCOL_VERSION
110
+ "mcp-protocol-version": PROTOCOL_VERSION,
111
+ "x-cortex-client": "cortex-mcp-stdio"
111
112
  };
112
113
  if (this.sessionId) {
113
114
  headers["mcp-session-id"] = this.sessionId;
@@ -117,7 +118,7 @@ var CortexHttpClient = class {
117
118
  method: "POST",
118
119
  headers,
119
120
  body: JSON.stringify(body),
120
- signal: AbortSignal.timeout(3e4)
121
+ signal: AbortSignal.timeout(6e4)
121
122
  });
122
123
  const newSessionId = response.headers.get("mcp-session-id");
123
124
  if (newSessionId) {
@@ -142,7 +143,8 @@ var CortexHttpClient = class {
142
143
  const headers = {
143
144
  "Content-Type": "application/json",
144
145
  "x-api-key": this.apiKey,
145
- "mcp-protocol-version": PROTOCOL_VERSION
146
+ "mcp-protocol-version": PROTOCOL_VERSION,
147
+ "x-cortex-client": "cortex-mcp-stdio"
146
148
  };
147
149
  if (this.sessionId) {
148
150
  headers["mcp-session-id"] = this.sessionId;
@@ -184,7 +186,7 @@ import {
184
186
  ListToolsRequestSchema
185
187
  } from "@modelcontextprotocol/sdk/types.js";
186
188
  var UPLOAD_TOOLS = /* @__PURE__ */ new Set(["upload_file", "upload_file_to_sharepoint"]);
187
- var INLINE_UPLOAD_MAX = 3.5 * 1024 * 1024;
189
+ var INLINE_UPLOAD_MAX = 3 * 1024 * 1024;
188
190
  function overrideUploadToolSchema(tool) {
189
191
  const name = tool.name;
190
192
  const baseName = name.includes("__") ? name.split("__").pop() : name;
@@ -310,48 +312,57 @@ async function handleLocalFileUpload(cortex, toolName, args) {
310
312
  };
311
313
  }
312
314
  const fileBuffer = readFileSync(filePath);
313
- const chunkSize = 10 * 1024 * 1024;
315
+ const chunkSize = 2.5 * 1024 * 1024;
314
316
  const total = fileBuffer.length;
315
- let lastResponse = null;
317
+ let driveItem = {};
316
318
  for (let start = 0; start < total; start += chunkSize) {
317
319
  const end = Math.min(start + chunkSize, total);
318
320
  const chunk = fileBuffer.subarray(start, end);
321
+ const chunkBase64 = Buffer.from(chunk).toString("base64");
319
322
  console.error(
320
- `[cortex-mcp] Uploading chunk ${start}-${end - 1}/${total}`
323
+ `[cortex-mcp] Uploading chunk ${start}-${end - 1}/${total} via backend relay`
321
324
  );
322
- lastResponse = await fetch(uploadUrl, {
323
- method: "PUT",
324
- headers: {
325
- "Content-Length": String(chunk.length),
326
- "Content-Range": `bytes ${start}-${end - 1}/${total}`
327
- },
328
- body: chunk,
329
- signal: AbortSignal.timeout(12e4)
330
- // 2 min per chunk
325
+ const chunkResponse = await cortex.callTool(`${prefix}upload_file_chunk`, {
326
+ upload_url: uploadUrl,
327
+ chunk: chunkBase64,
328
+ range_start: start,
329
+ range_end: end - 1,
330
+ total_size: total
331
331
  });
332
- if (!lastResponse.ok && lastResponse.status !== 202) {
333
- const errText = await lastResponse.text();
332
+ if (chunkResponse.error) {
333
+ return {
334
+ content: [{ type: "text", text: chunkResponse.error.message }],
335
+ isError: true
336
+ };
337
+ }
338
+ const chunkResult = chunkResponse.result;
339
+ let chunkData;
340
+ try {
341
+ chunkData = JSON.parse(chunkResult.content[0].text);
342
+ } catch {
334
343
  return {
335
344
  content: [{ type: "text", text: JSON.stringify({
336
345
  success: false,
337
- error: `Upload chunk failed (${lastResponse.status}): ${errText.slice(0, 200)}`
346
+ error: "Failed to parse chunk upload response from backend"
338
347
  }) }],
339
348
  isError: true
340
349
  };
341
350
  }
342
- }
343
- let driveItem = {};
344
- if (lastResponse) {
345
- try {
346
- driveItem = await lastResponse.json();
347
- } catch {
351
+ if (!chunkData.success) {
352
+ return {
353
+ content: [{ type: "text", text: JSON.stringify(chunkData) }],
354
+ isError: true
355
+ };
356
+ }
357
+ if (chunkData.status === 200 || chunkData.status === 201) {
358
+ driveItem = chunkData.data ?? {};
348
359
  }
349
360
  }
350
361
  return {
351
362
  content: [{ type: "text", text: JSON.stringify({
352
363
  success: true,
353
364
  file: driveItem,
354
- message: `Uploaded '${args.path}' (${(fileSize / 1024).toFixed(1)}KB) via upload session`
365
+ message: `Uploaded '${args.path}' (${(fileSize / 1024 / 1024).toFixed(1)}MB) via upload session`
355
366
  }) }],
356
367
  isError: false
357
368
  };
@@ -694,11 +705,181 @@ function getEffectiveApiKey() {
694
705
  if (config?.apiKey) return config.apiKey;
695
706
  return DEFAULT_API_KEY;
696
707
  }
708
+
709
+ // src/client/mcp-namespace.ts
710
+ var CortexError = class extends Error {
711
+ constructor(message, mcpName) {
712
+ super(message);
713
+ this.mcpName = mcpName;
714
+ this.name = "CortexError";
715
+ }
716
+ };
717
+ var CortexToolError = class extends CortexError {
718
+ constructor(message, mcpName, toolName) {
719
+ super(message, mcpName);
720
+ this.mcpName = mcpName;
721
+ this.toolName = toolName;
722
+ this.name = "CortexToolError";
723
+ }
724
+ };
725
+ var CortexNetworkError = class extends CortexError {
726
+ constructor(message, code, mcpName) {
727
+ super(message, mcpName);
728
+ this.code = code;
729
+ this.name = "CortexNetworkError";
730
+ }
731
+ };
732
+ var MCPNamespace = class {
733
+ constructor(mcpName, serverUrl, apiKey) {
734
+ this.mcpName = mcpName;
735
+ this.client = new CortexHttpClient(serverUrl, apiKey, mcpName);
736
+ }
737
+ client;
738
+ initPromise = null;
739
+ async ensureInitialized() {
740
+ if (this.initPromise !== null) return this.initPromise;
741
+ this.initPromise = this.doInit();
742
+ return this.initPromise;
743
+ }
744
+ async doInit() {
745
+ const response = await this.client.initialize();
746
+ if (response.error) {
747
+ this.initPromise = null;
748
+ throw new CortexNetworkError(
749
+ response.error.message,
750
+ response.error.code,
751
+ this.mcpName
752
+ );
753
+ }
754
+ }
755
+ /**
756
+ * Call a tool on this MCP. Returns the parsed result.
757
+ *
758
+ * @example
759
+ * const ns = new MCPNamespace('salesforce', serverUrl, apiKey);
760
+ * const result = await ns.call('run_soql_query', {
761
+ * query: 'SELECT Id, Name FROM Account LIMIT 10'
762
+ * });
763
+ */
764
+ async call(toolName, args = {}) {
765
+ await this.ensureInitialized();
766
+ const response = await this.client.callTool(toolName, args);
767
+ if (response.error) {
768
+ throw new CortexNetworkError(
769
+ response.error.message,
770
+ response.error.code,
771
+ this.mcpName
772
+ );
773
+ }
774
+ const result = response.result;
775
+ const text = result?.content?.[0]?.text ?? "";
776
+ if (result?.isError) {
777
+ throw new CortexToolError(text, this.mcpName, toolName);
778
+ }
779
+ return parseText(text);
780
+ }
781
+ /** List all tools available on this MCP. */
782
+ async listTools() {
783
+ await this.ensureInitialized();
784
+ const response = await this.client.listTools();
785
+ if (response.error) {
786
+ throw new CortexNetworkError(
787
+ response.error.message,
788
+ response.error.code,
789
+ this.mcpName
790
+ );
791
+ }
792
+ const result = response.result;
793
+ return result?.tools ?? [];
794
+ }
795
+ /** Release the session. The next call() will re-initialize. */
796
+ close() {
797
+ this.initPromise = null;
798
+ }
799
+ };
800
+ function parseText(text) {
801
+ if (!text) return text;
802
+ try {
803
+ return JSON.parse(text);
804
+ } catch {
805
+ return text;
806
+ }
807
+ }
808
+
809
+ // src/client/cortex-client.ts
810
+ var CortexClient = class {
811
+ apiKey;
812
+ serverUrl;
813
+ namespaces = /* @__PURE__ */ new Map();
814
+ constructor(options = {}) {
815
+ this.apiKey = options.apiKey ?? getEffectiveApiKey();
816
+ this.serverUrl = options.serverUrl ?? DEFAULT_SERVER_URL;
817
+ }
818
+ get asana() {
819
+ return this.getNamespace("asana");
820
+ }
821
+ get github() {
822
+ return this.getNamespace("github");
823
+ }
824
+ get vercel() {
825
+ return this.getNamespace("vercel");
826
+ }
827
+ get supabase() {
828
+ return this.getNamespace("supabase");
829
+ }
830
+ get m365() {
831
+ return this.getNamespace("m365");
832
+ }
833
+ get salesforce() {
834
+ return this.getNamespace("salesforce");
835
+ }
836
+ get monday() {
837
+ return this.getNamespace("monday");
838
+ }
839
+ get slack() {
840
+ return this.getNamespace("slack");
841
+ }
842
+ get bestbuy() {
843
+ return this.getNamespace("bestbuy");
844
+ }
845
+ /**
846
+ * Call any MCP tool when the MCP name is determined at runtime.
847
+ *
848
+ * @example
849
+ * const result = await cortex.call('github', 'list_repositories', { per_page: 5 });
850
+ */
851
+ async call(mcpName, toolName, args = {}) {
852
+ return this.getNamespace(mcpName).call(toolName, args);
853
+ }
854
+ /**
855
+ * Release all sessions. Safe to call multiple times.
856
+ * The next call after close() will re-initialize automatically.
857
+ */
858
+ close() {
859
+ for (const ns of this.namespaces.values()) {
860
+ ns.close();
861
+ }
862
+ this.namespaces.clear();
863
+ }
864
+ getNamespace(name) {
865
+ let ns = this.namespaces.get(name);
866
+ if (!ns) {
867
+ ns = new MCPNamespace(name, this.serverUrl, this.apiKey);
868
+ this.namespaces.set(name, ns);
869
+ }
870
+ return ns;
871
+ }
872
+ };
697
873
  export {
698
874
  AVAILABLE_MCPS,
875
+ CortexClient,
876
+ CortexError,
699
877
  CortexHttpClient,
878
+ CortexNetworkError,
879
+ CortexToolError,
700
880
  DEFAULT_API_KEY,
701
881
  DEFAULT_SERVER_URL,
882
+ MCPNamespace,
702
883
  MCP_NAMES,
703
884
  configureClaudeCode,
704
885
  configureClaudeDesktop,