@nex-ai/nex 0.1.34 → 0.1.35

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.
Files changed (69) hide show
  1. package/dist/index.js +5 -0
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp/channel.d.ts +28 -0
  4. package/dist/mcp/channel.js +135 -0
  5. package/dist/mcp/channel.js.map +1 -0
  6. package/dist/mcp/client.d.ts +20 -0
  7. package/dist/mcp/client.js +130 -0
  8. package/dist/mcp/client.js.map +1 -0
  9. package/dist/mcp/config.d.ts +14 -0
  10. package/dist/mcp/config.js +33 -0
  11. package/dist/mcp/config.js.map +1 -0
  12. package/dist/mcp/context-files.d.ts +9 -0
  13. package/dist/mcp/context-files.js +90 -0
  14. package/dist/mcp/context-files.js.map +1 -0
  15. package/dist/mcp/file-manifest.d.ts +27 -0
  16. package/dist/mcp/file-manifest.js +64 -0
  17. package/dist/mcp/file-manifest.js.map +1 -0
  18. package/dist/mcp/file-scanner.d.ts +17 -0
  19. package/dist/mcp/file-scanner.js +77 -0
  20. package/dist/mcp/file-scanner.js.map +1 -0
  21. package/dist/mcp/index.d.ts +2 -0
  22. package/dist/mcp/index.js +51 -0
  23. package/dist/mcp/index.js.map +1 -0
  24. package/dist/mcp/rate-limiter.d.ts +14 -0
  25. package/dist/mcp/rate-limiter.js +60 -0
  26. package/dist/mcp/rate-limiter.js.map +1 -0
  27. package/dist/mcp/server.d.ts +6 -0
  28. package/dist/mcp/server.js +42 -0
  29. package/dist/mcp/server.js.map +1 -0
  30. package/dist/mcp/session-store.d.ts +16 -0
  31. package/dist/mcp/session-store.js +70 -0
  32. package/dist/mcp/session-store.js.map +1 -0
  33. package/dist/mcp/tools/context.d.ts +3 -0
  34. package/dist/mcp/tools/context.js +65 -0
  35. package/dist/mcp/tools/context.js.map +1 -0
  36. package/dist/mcp/tools/insights.d.ts +3 -0
  37. package/dist/mcp/tools/insights.js +24 -0
  38. package/dist/mcp/tools/insights.js.map +1 -0
  39. package/dist/mcp/tools/integrations.d.ts +3 -0
  40. package/dist/mcp/tools/integrations.js +27 -0
  41. package/dist/mcp/tools/integrations.js.map +1 -0
  42. package/dist/mcp/tools/lists.d.ts +3 -0
  43. package/dist/mcp/tools/lists.js +101 -0
  44. package/dist/mcp/tools/lists.js.map +1 -0
  45. package/dist/mcp/tools/notes.d.ts +3 -0
  46. package/dist/mcp/tools/notes.js +52 -0
  47. package/dist/mcp/tools/notes.js.map +1 -0
  48. package/dist/mcp/tools/records.d.ts +3 -0
  49. package/dist/mcp/tools/records.js +74 -0
  50. package/dist/mcp/tools/records.js.map +1 -0
  51. package/dist/mcp/tools/register.d.ts +3 -0
  52. package/dist/mcp/tools/register.js +30 -0
  53. package/dist/mcp/tools/register.js.map +1 -0
  54. package/dist/mcp/tools/relationships.d.ts +3 -0
  55. package/dist/mcp/tools/relationships.js +47 -0
  56. package/dist/mcp/tools/relationships.js.map +1 -0
  57. package/dist/mcp/tools/scan.d.ts +3 -0
  58. package/dist/mcp/tools/scan.js +37 -0
  59. package/dist/mcp/tools/scan.js.map +1 -0
  60. package/dist/mcp/tools/schema.d.ts +3 -0
  61. package/dist/mcp/tools/schema.js +108 -0
  62. package/dist/mcp/tools/schema.js.map +1 -0
  63. package/dist/mcp/tools/search.d.ts +3 -0
  64. package/dist/mcp/tools/search.js +8 -0
  65. package/dist/mcp/tools/search.js.map +1 -0
  66. package/dist/mcp/tools/tasks.d.ts +3 -0
  67. package/dist/mcp/tools/tasks.js +88 -0
  68. package/dist/mcp/tools/tasks.js.map +1 -0
  69. package/package.json +6 -3
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Core file scanner — walks project directories, detects changed files,
3
+ * and ingests them into Nex via the developer API.
4
+ */
5
+ import { readdirSync, statSync, readFileSync } from "node:fs";
6
+ import { join, relative, extname } from "node:path";
7
+ import { readManifest, writeManifest, isChanged, markIngested } from "./file-manifest.js";
8
+ function walkDir(dir, cwd, config, depth, results) {
9
+ if (depth > config.scanDepth)
10
+ return;
11
+ let entries;
12
+ try {
13
+ entries = readdirSync(dir, { withFileTypes: true });
14
+ }
15
+ catch {
16
+ return;
17
+ }
18
+ for (const entry of entries) {
19
+ const fullPath = join(dir, entry.name);
20
+ if (entry.isDirectory()) {
21
+ if (config.ignoreDirs.includes(entry.name))
22
+ continue;
23
+ walkDir(fullPath, cwd, config, depth + 1, results);
24
+ }
25
+ else if (entry.isFile()) {
26
+ const ext = extname(entry.name).toLowerCase();
27
+ if (!config.extensions.includes(ext))
28
+ continue;
29
+ try {
30
+ const stat = statSync(fullPath);
31
+ results.push({ absolutePath: fullPath, relativePath: relative(cwd, fullPath), stat });
32
+ }
33
+ catch {
34
+ // stat failed — skip
35
+ }
36
+ }
37
+ }
38
+ }
39
+ export async function scanAndIngest(client, rateLimiter, cwd, config) {
40
+ const result = { scanned: 0, ingested: 0, skipped: 0, errors: 0 };
41
+ if (!config.enabled)
42
+ return result;
43
+ const manifest = readManifest();
44
+ const candidates = [];
45
+ walkDir(cwd, cwd, config, 0, candidates);
46
+ result.scanned = candidates.length;
47
+ const changed = candidates
48
+ .filter((f) => isChanged(f.absolutePath, f.stat, manifest))
49
+ .sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)
50
+ .slice(0, config.maxFilesPerScan);
51
+ result.skipped = candidates.length - changed.length;
52
+ for (const file of changed) {
53
+ if (!rateLimiter.canProceed()) {
54
+ process.stderr.write(`[nex-scan] Rate limited — stopping after ${result.ingested} files\n`);
55
+ result.skipped += changed.length - result.ingested - result.errors;
56
+ break;
57
+ }
58
+ try {
59
+ let content = readFileSync(file.absolutePath, "utf-8");
60
+ if (content.length > config.maxFileSize) {
61
+ content = content.slice(0, config.maxFileSize) + "\n[...truncated]";
62
+ }
63
+ const context = `file-scan:${file.relativePath}`;
64
+ await client.post("/v1/context/text", { content, context });
65
+ rateLimiter.recordRequest();
66
+ markIngested(file.absolutePath, file.stat, context, manifest);
67
+ result.ingested++;
68
+ }
69
+ catch (err) {
70
+ process.stderr.write(`[nex-scan] Failed to ingest ${file.relativePath}: ${err instanceof Error ? err.message : String(err)}\n`);
71
+ result.errors++;
72
+ }
73
+ }
74
+ writeManifest(manifest);
75
+ return result;
76
+ }
77
+ //# sourceMappingURL=file-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-scanner.js","sourceRoot":"","sources":["../../src/mcp/file-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
+ import { createServer } from "./server.js";
5
+ import { createServer as createHttpServer } from "node:http";
6
+ import { loadApiKey } from "./config.js";
7
+ import { startChannel } from "./channel.js";
8
+ const apiKey = loadApiKey();
9
+ if (!apiKey) {
10
+ console.error("No API key found (checked NEX_API_KEY env and ~/.nex/config.json). Starting in registration-only mode. Use the 'register' tool to create an account and get an API key. Once registered, all context, search, and scan tools become available.");
11
+ }
12
+ const transport = process.env.MCP_TRANSPORT ?? "stdio";
13
+ async function main() {
14
+ const { server, client } = createServer(apiKey);
15
+ if (transport === "http") {
16
+ const port = parseInt(process.env.MCP_PORT ?? "3001", 10);
17
+ const httpTransport = new StreamableHTTPServerTransport({
18
+ sessionIdGenerator: undefined,
19
+ });
20
+ const httpServer = createHttpServer(async (req, res) => {
21
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
22
+ if (url.pathname === "/mcp") {
23
+ await httpTransport.handleRequest(req, res);
24
+ }
25
+ else if (url.pathname === "/health") {
26
+ res.writeHead(200, { "Content-Type": "application/json" });
27
+ res.end(JSON.stringify({ status: "ok" }));
28
+ }
29
+ else {
30
+ res.writeHead(404);
31
+ res.end("Not found");
32
+ }
33
+ });
34
+ await server.connect(httpTransport);
35
+ startChannel(server.server, client);
36
+ httpServer.listen(port, () => {
37
+ console.error(`Nex MCP server running on http://localhost:${port}/mcp`);
38
+ });
39
+ }
40
+ else {
41
+ const stdioTransport = new StdioServerTransport();
42
+ await server.connect(stdioTransport);
43
+ startChannel(server.server, client);
44
+ console.error("Nex MCP server running on stdio");
45
+ }
46
+ }
47
+ main().catch((err) => {
48
+ console.error("Fatal error:", err);
49
+ process.exit(1);
50
+ });
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;AAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;IACZ,OAAO,CAAC,KAAK,CAAC,gPAAgP,CAAC,CAAC;AAClQ,CAAC;AAED,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC;AAEvD,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEhD,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;QAE1D,MAAM,aAAa,GAAG,IAAI,6BAA6B,CAAC;YACtD,kBAAkB,EAAE,SAAS;SAC9B,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YACrD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,oBAAoB,IAAI,EAAE,CAAC,CAAC;YAChE,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;gBAC5B,MAAM,aAAa,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC9C,CAAC;iBAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACtC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACpC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YAC3B,OAAO,CAAC,KAAK,CAAC,8CAA8C,IAAI,MAAM,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,cAAc,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAClD,MAAM,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACrC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpC,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface RateLimiterConfig {
2
+ maxRequests: number;
3
+ windowMs: number;
4
+ dataDir: string;
5
+ }
6
+ export declare class RateLimiter {
7
+ private config;
8
+ private filePath;
9
+ constructor(config?: Partial<RateLimiterConfig>);
10
+ private readTimestamps;
11
+ private writeTimestamps;
12
+ canProceed(): boolean;
13
+ recordRequest(): void;
14
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * File-based sliding window rate limiter.
3
+ * Designed for Nex /text endpoint (10 req/min).
4
+ *
5
+ * Persists timestamps to a JSON file so rate limits are respected
6
+ * across invocations.
7
+ */
8
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { homedir } from "node:os";
11
+ const DEFAULTS = {
12
+ maxRequests: 10,
13
+ windowMs: 60_000,
14
+ dataDir: join(homedir(), ".nex"),
15
+ };
16
+ export class RateLimiter {
17
+ config;
18
+ filePath;
19
+ constructor(config) {
20
+ this.config = { ...DEFAULTS, ...config };
21
+ this.filePath = join(this.config.dataDir, "rate-limiter.json");
22
+ mkdirSync(this.config.dataDir, { recursive: true });
23
+ }
24
+ readTimestamps() {
25
+ try {
26
+ const raw = readFileSync(this.filePath, "utf-8");
27
+ const data = JSON.parse(raw);
28
+ if (Array.isArray(data))
29
+ return data;
30
+ return [];
31
+ }
32
+ catch {
33
+ return [];
34
+ }
35
+ }
36
+ writeTimestamps(timestamps) {
37
+ try {
38
+ writeFileSync(this.filePath, JSON.stringify(timestamps), "utf-8");
39
+ }
40
+ catch {
41
+ // Best-effort
42
+ }
43
+ }
44
+ canProceed() {
45
+ const now = Date.now();
46
+ const timestamps = this.readTimestamps().filter((t) => now - t < this.config.windowMs);
47
+ if (timestamps.length >= this.config.maxRequests) {
48
+ this.writeTimestamps(timestamps);
49
+ return false;
50
+ }
51
+ return true;
52
+ }
53
+ recordRequest() {
54
+ const now = Date.now();
55
+ const timestamps = this.readTimestamps().filter((t) => now - t < this.config.windowMs);
56
+ timestamps.push(now);
57
+ this.writeTimestamps(timestamps);
58
+ }
59
+ }
60
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/mcp/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAQlC,MAAM,QAAQ,GAAsB;IAClC,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,MAAM;IAChB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,OAAO,WAAW;IACd,MAAM,CAAoB;IAC1B,QAAQ,CAAS;IAEzB,YAAY,MAAmC;QAC7C,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;QACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QAC/D,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IAEO,cAAc;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YACrC,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,UAAoB;QAC1C,IAAI,CAAC;YACH,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;IAED,UAAU;QACR,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvF,IAAI,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACjD,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YACjC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,aAAa;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvF,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { NexApiClient } from "./client.js";
3
+ export declare function createServer(apiKey?: string): {
4
+ server: McpServer;
5
+ client: NexApiClient;
6
+ };
@@ -0,0 +1,42 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { NexApiClient } from "./client.js";
3
+ import { registerContextTools } from "./tools/context.js";
4
+ import { registerSearchTools } from "./tools/search.js";
5
+ import { registerSchemaTools } from "./tools/schema.js";
6
+ import { registerRecordTools } from "./tools/records.js";
7
+ import { registerRelationshipTools } from "./tools/relationships.js";
8
+ import { registerListTools } from "./tools/lists.js";
9
+ import { registerTaskTools } from "./tools/tasks.js";
10
+ import { registerNoteTools } from "./tools/notes.js";
11
+ import { registerInsightTools } from "./tools/insights.js";
12
+ import { registerRegistrationTools } from "./tools/register.js";
13
+ import { registerScanTools } from "./tools/scan.js";
14
+ import { registerIntegrationTools } from "./tools/integrations.js";
15
+ export function createServer(apiKey) {
16
+ const server = new McpServer({ name: "nex", version: "0.1.0" }, {
17
+ capabilities: {
18
+ experimental: { "claude/channel": {} },
19
+ },
20
+ instructions: 'Events from the nex channel arrive as <channel source="nex" type="...">. ' +
21
+ "They contain daily digest summaries (type=daily_digest) and proactive " +
22
+ "notifications (type=proactive_notification) about important context " +
23
+ "changes — deals, meetings, relationships, tasks. These are one-way " +
24
+ "informational: acknowledge them naturally and incorporate into your " +
25
+ "awareness. No reply expected.",
26
+ });
27
+ const client = new NexApiClient(apiKey);
28
+ registerRegistrationTools(server, client);
29
+ registerContextTools(server, client);
30
+ registerSearchTools(server, client);
31
+ registerSchemaTools(server, client);
32
+ registerRecordTools(server, client);
33
+ registerRelationshipTools(server, client);
34
+ registerListTools(server, client);
35
+ registerTaskTools(server, client);
36
+ registerNoteTools(server, client);
37
+ registerInsightTools(server, client);
38
+ registerScanTools(server, client);
39
+ registerIntegrationTools(server, client);
40
+ return { server, client };
41
+ }
42
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,MAAM,UAAU,YAAY,CAAC,MAAe;IAI1C,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EACjC;QACE,YAAY,EAAE;YACZ,YAAY,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE;SACvC;QACD,YAAY,EACV,2EAA2E;YAC3E,wEAAwE;YACxE,sEAAsE;YACtE,qEAAqE;YACrE,sEAAsE;YACtE,+BAA+B;KAClC,CACF,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IAExC,yBAAyB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,yBAAyB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,wBAAwB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface SessionStoreConfig {
2
+ maxSize: number;
3
+ dataDir: string;
4
+ }
5
+ export declare class SessionStore {
6
+ private filePath;
7
+ private maxSize;
8
+ constructor(config?: Partial<SessionStoreConfig>);
9
+ private readStore;
10
+ private writeStore;
11
+ get(sessionKey: string): string | undefined;
12
+ set(sessionKey: string, nexSessionId: string): void;
13
+ delete(sessionKey: string): boolean;
14
+ get size(): number;
15
+ clear(): void;
16
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * File-based session store for MCP session ID mapping.
3
+ * Persists session mappings to ~/.nex/mcp-sessions.json.
4
+ */
5
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { homedir } from "node:os";
8
+ const DEFAULT_MAX = 100;
9
+ const DEFAULT_DATA_DIR = join(homedir(), ".nex");
10
+ export class SessionStore {
11
+ filePath;
12
+ maxSize;
13
+ constructor(config) {
14
+ const dataDir = config?.dataDir ?? DEFAULT_DATA_DIR;
15
+ this.maxSize = config?.maxSize ?? DEFAULT_MAX;
16
+ this.filePath = join(dataDir, "mcp-sessions.json");
17
+ mkdirSync(dataDir, { recursive: true });
18
+ }
19
+ readStore() {
20
+ try {
21
+ const raw = readFileSync(this.filePath, "utf-8");
22
+ const data = JSON.parse(raw);
23
+ if (data && typeof data === "object" && !Array.isArray(data)) {
24
+ return data;
25
+ }
26
+ return {};
27
+ }
28
+ catch {
29
+ return {};
30
+ }
31
+ }
32
+ writeStore(store) {
33
+ try {
34
+ writeFileSync(this.filePath, JSON.stringify(store), "utf-8");
35
+ }
36
+ catch {
37
+ // Best-effort
38
+ }
39
+ }
40
+ get(sessionKey) {
41
+ const store = this.readStore();
42
+ return store[sessionKey];
43
+ }
44
+ set(sessionKey, nexSessionId) {
45
+ const store = this.readStore();
46
+ store[sessionKey] = nexSessionId;
47
+ const keys = Object.keys(store);
48
+ while (keys.length > this.maxSize) {
49
+ const oldest = keys.shift();
50
+ delete store[oldest];
51
+ }
52
+ this.writeStore(store);
53
+ }
54
+ delete(sessionKey) {
55
+ const store = this.readStore();
56
+ if (sessionKey in store) {
57
+ delete store[sessionKey];
58
+ this.writeStore(store);
59
+ return true;
60
+ }
61
+ return false;
62
+ }
63
+ get size() {
64
+ return Object.keys(this.readStore()).length;
65
+ }
66
+ clear() {
67
+ this.writeStore({});
68
+ }
69
+ }
70
+ //# sourceMappingURL=session-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/mcp/session-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAOlC,MAAM,WAAW,GAAG,GAAG,CAAC;AACxB,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,OAAO,YAAY;IACf,QAAQ,CAAS;IACjB,OAAO,CAAS;IAExB,YAAY,MAAoC;QAC9C,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,IAAI,gBAAgB,CAAC;QACpD,IAAI,CAAC,OAAO,GAAG,MAAM,EAAE,OAAO,IAAI,WAAW,CAAC;QAC9C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QACnD,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;IAEO,SAAS;QACf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7D,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,UAAU,CAAC,KAA6B;QAC9C,IAAI,CAAC;YACH,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;IAED,GAAG,CAAC,UAAkB;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAED,GAAG,CAAC,UAAkB,EAAE,YAAoB;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC/B,KAAK,CAAC,UAAU,CAAC,GAAG,YAAY,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,EAAG,CAAC;YAC7B,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,CAAC,UAAkB;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC/B,IAAI,UAAU,IAAI,KAAK,EAAE,CAAC;YACxB,OAAO,KAAK,CAAC,UAAU,CAAC,CAAC;YACzB,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,IAAI;QACN,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC;IAC9C,CAAC;IAED,KAAK;QACH,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { NexApiClient } from "../client.js";
3
+ export declare function registerContextTools(server: McpServer, client: NexApiClient): void;
@@ -0,0 +1,65 @@
1
+ import { z } from "zod";
2
+ export function registerContextTools(server, client) {
3
+ server.tool("query_context", "Query the Nex context graph with a natural language question. Returns an AI-generated answer with supporting entities and evidence. Use for open-ended questions about contacts, companies, relationships, or history.", {
4
+ query: z.string().describe("Natural language question about your contacts, companies, or relationships"),
5
+ session_id: z.string().optional().describe("Session ID for multi-turn conversational continuity"),
6
+ }, { readOnlyHint: true, openWorldHint: true }, async ({ query, session_id }) => {
7
+ const body = { query };
8
+ if (session_id)
9
+ body.session_id = session_id;
10
+ const result = await client.post("/v1/context/ask", body);
11
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
12
+ });
13
+ server.tool("add_context", "Ingest unstructured text (meeting notes, emails, conversation transcripts) into the Nex context graph. Automatically extracts entities, relationships, and insights. Returns an artifact_id — use get_artifact_status to check processing results.", {
14
+ content: z.string().describe("The text content to process (meeting notes, email, conversation transcript, etc.)"),
15
+ context: z.string().optional().describe("Additional context about the text, e.g. 'Sales call notes' or 'Email from client'"),
16
+ }, { readOnlyHint: false }, async ({ content, context }) => {
17
+ const body = { content };
18
+ if (context !== undefined)
19
+ body.context = context;
20
+ const result = await client.post("/v1/context/text", body);
21
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22
+ });
23
+ server.tool("get_artifact_status", "Check the processing status and results of a previously submitted text artifact. Poll until status is 'completed' or 'failed'. Returns extracted entities, relationships, and insights.", { artifact_id: z.string().describe("The artifact ID returned by add_context") }, { readOnlyHint: true }, async ({ artifact_id }) => {
24
+ const result = await client.get(`/v1/context/artifacts/${encodeURIComponent(artifact_id)}`);
25
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
26
+ });
27
+ server.tool("create_list_job", "Create an AI-powered list generation job. Uses natural language to search the context graph and generate a curated list of contacts or companies. Returns a job_id — use get_list_job_status to poll for results.", {
28
+ query: z.string().describe("Natural language description of the list you want, e.g. 'high priority contacts in enterprise deals'"),
29
+ object_type: z.enum(["contact", "company"]).optional().describe("Type of entities to search for (default: contact)"),
30
+ limit: z.number().optional().describe("Maximum number of results (default: 50, max: 100)"),
31
+ include_attributes: z.boolean().optional().describe("Include full attribute values for each entity"),
32
+ }, { readOnlyHint: false }, async ({ query, object_type, limit, include_attributes }) => {
33
+ const body = { query };
34
+ if (object_type)
35
+ body.object_type = object_type;
36
+ if (limit !== undefined)
37
+ body.limit = limit;
38
+ if (include_attributes !== undefined)
39
+ body.include_attributes = include_attributes;
40
+ const result = await client.post("/v1/context/list/jobs", body);
41
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
42
+ });
43
+ server.tool("get_list_job_status", "Check the status and results of an AI list generation job. Poll until status is 'completed' or 'failed'. Returns matched entities with reasons and highlights.", {
44
+ job_id: z.string().describe("The job ID returned by create_list_job"),
45
+ include_attributes: z.boolean().optional().describe("Include full attribute values for each entity"),
46
+ }, { readOnlyHint: true }, async ({ job_id, include_attributes }) => {
47
+ const params = new URLSearchParams();
48
+ if (include_attributes)
49
+ params.set("include_attributes", "true");
50
+ const qs = params.toString();
51
+ const path = `/v1/context/list/jobs/${encodeURIComponent(job_id)}${qs ? `?${qs}` : ""}`;
52
+ const result = await client.get(path);
53
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
54
+ });
55
+ server.tool("search_entities", "Search for entities (people, companies, topics) in the Nex knowledge base. Returns a structured list with names, types, and mention counts.", { query: z.string().describe("Search query to find entities") }, { readOnlyHint: true }, async ({ query }) => {
56
+ const result = await client.post("/v1/context/ask", { query });
57
+ const entities = result.entity_references ?? [];
58
+ if (entities.length === 0) {
59
+ return { content: [{ type: "text", text: "No matching entities found." }] };
60
+ }
61
+ const lines = entities.map(e => `- ${e.name} (${e.type})${e.count ? ` — ${e.count} mentions` : ""}`);
62
+ return { content: [{ type: "text", text: `Found ${entities.length} entities:\n${lines.join("\n")}` }] };
63
+ });
64
+ }
65
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/mcp/tools/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { NexApiClient } from "../client.js";
3
+ export declare function registerInsightTools(server: McpServer, client: NexApiClient): void;
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ export function registerInsightTools(server, client) {
3
+ server.tool("get_insights", "Query insights by time window. Returns discovered opportunities, risks, relationship changes, milestones, and other insights. Use 'last' for a duration window (e.g. '30m', '2h') or 'from'/'to' for an absolute time range.", {
4
+ last: z.string().optional().describe("Duration window, e.g. '30m', '2h', '1h30m'"),
5
+ from: z.string().optional().describe("Start of time range in RFC3339 format"),
6
+ to: z.string().optional().describe("End of time range in RFC3339 format"),
7
+ limit: z.number().optional().describe("Max results (default: 20, max: 100)"),
8
+ }, { readOnlyHint: true }, async ({ last, from, to, limit }) => {
9
+ const params = new URLSearchParams();
10
+ if (last)
11
+ params.set("last", last);
12
+ if (from)
13
+ params.set("from", from);
14
+ if (to)
15
+ params.set("to", to);
16
+ if (limit !== undefined)
17
+ params.set("limit", String(limit));
18
+ const qs = params.toString();
19
+ const path = `/v1/insights${qs ? `?${qs}` : ""}`;
20
+ const result = await client.get(path);
21
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22
+ });
23
+ }
24
+ //# sourceMappingURL=insights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.js","sourceRoot":"","sources":["../../../src/mcp/tools/insights.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,MAAoB;IAC1E,MAAM,CAAC,IAAI,CACT,cAAc,EACd,8NAA8N,EAC9N;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;QAClF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;QAC7E,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;QACzE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;KAC7E,EACD,EAAE,YAAY,EAAE,IAAI,EAAE,EACtB,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAClC,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,IAAI;YAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,IAAI;YAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,EAAE;YAAE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC7B,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5D,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACjD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAChF,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { NexApiClient } from "../client.js";
3
+ export declare function registerIntegrationTools(server: McpServer, client: NexApiClient): void;
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ export function registerIntegrationTools(server, client) {
3
+ server.tool("list_integrations", "List all available third-party integrations and their connection status. Calendar integrations (Google Calendar, Outlook Calendar) enable the Nex Meeting Bot which joins calls on any platform (Google Meet, Zoom, Webex, Teams, etc.) and feeds transcripts into the context graph. To connect, use connect_integration. To disconnect, use disconnect_integration.", {}, { readOnlyHint: true }, async () => {
4
+ const result = await client.get("/v1/integrations/");
5
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
6
+ });
7
+ server.tool("connect_integration", "Start connecting a third-party integration via OAuth. Returns an auth_url to open in the browser and a connect_id to poll for status. Calendar integrations (type: 'calendar') enable the Nex Meeting Bot which auto-joins calls and processes transcripts.", {
8
+ type: z.enum(["email", "calendar", "crm", "messaging"]).describe("Integration type"),
9
+ provider: z.enum(["google", "microsoft", "attio", "slack", "salesforce", "hubspot"]).describe("Integration provider"),
10
+ }, { readOnlyHint: false }, async ({ type, provider }) => {
11
+ const result = await client.post(`/v1/integrations/${encodeURIComponent(type)}/${encodeURIComponent(provider)}/connect`);
12
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
13
+ });
14
+ server.tool("get_connect_status", "Check the status of an in-progress OAuth connection. Poll this after connect_integration until status is 'connected'.", {
15
+ connect_id: z.string().describe("Connect ID returned from connect_integration"),
16
+ }, { readOnlyHint: true }, async ({ connect_id }) => {
17
+ const result = await client.get(`/v1/integrations/connect/${encodeURIComponent(connect_id)}/status`);
18
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
19
+ });
20
+ server.tool("disconnect_integration", "Disconnect a third-party integration by connection ID. Get connection IDs from list_integrations.", {
21
+ connection_id: z.string().describe("Connection ID to disconnect"),
22
+ }, { readOnlyHint: false, destructiveHint: true }, async ({ connection_id }) => {
23
+ const result = await client.delete(`/v1/integrations/connections/${encodeURIComponent(connection_id)}`);
24
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25
+ });
26
+ }
27
+ //# sourceMappingURL=integrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../../src/mcp/tools/integrations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,UAAU,wBAAwB,CAAC,MAAiB,EAAE,MAAoB;IAC9E,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,uWAAuW,EACvW,EAAE,EACF,EAAE,YAAY,EAAE,IAAI,EAAE,EACtB,KAAK,IAAI,EAAE;QACT,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACrD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAChF,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,qBAAqB,EACrB,6PAA6P,EAC7P;QACE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC;QACpF,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,sBAAsB,CAAC;KACtH,EACD,EAAE,YAAY,EAAE,KAAK,EAAE,EACvB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,oBAAoB,kBAAkB,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACzH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAChF,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,oBAAoB,EACpB,uHAAuH,EACvH;QACE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;KAChF,EACD,EAAE,YAAY,EAAE,IAAI,EAAE,EACtB,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;QACvB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,4BAA4B,kBAAkB,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QACrG,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAChF,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,mGAAmG,EACnG;QACE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;KAClE,EACD,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,EAC9C,KAAK,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE;QAC1B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,gCAAgC,kBAAkB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACxG,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAChF,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { NexApiClient } from "../client.js";
3
+ export declare function registerListTools(server: McpServer, client: NexApiClient): void;