@nexvora/mcp-server 0.3.1 → 0.3.3

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 (66) hide show
  1. package/README.md +15 -13
  2. package/dist/NexvoraClient.d.ts.map +1 -1
  3. package/dist/NexvoraClient.js +21 -3
  4. package/dist/NexvoraClient.js.map +1 -1
  5. package/dist/cli.d.ts +2 -2
  6. package/dist/cli.js +26 -20
  7. package/dist/cli.js.map +1 -1
  8. package/dist/createServer.d.ts +7 -0
  9. package/dist/createServer.d.ts.map +1 -1
  10. package/dist/createServer.js +3 -3
  11. package/dist/createServer.js.map +1 -1
  12. package/package.json +6 -2
  13. package/CHANGELOG.md +0 -208
  14. package/docs/setup/chatgpt-desktop.md +0 -120
  15. package/docs/setup/claude-code.md +0 -152
  16. package/docs/setup/cursor.md +0 -129
  17. package/src/NexvoraClient.ts +0 -328
  18. package/src/RateLimiter.ts +0 -74
  19. package/src/__tests__/NexvoraClient.test.ts +0 -424
  20. package/src/__tests__/RateLimiter.test.ts +0 -151
  21. package/src/__tests__/auth/oauth.test.ts +0 -246
  22. package/src/__tests__/cache.test.ts +0 -64
  23. package/src/__tests__/config.test.ts +0 -98
  24. package/src/__tests__/defineTool.test.ts +0 -223
  25. package/src/__tests__/fixtures/config.json +0 -7
  26. package/src/__tests__/integration/agentstack.integration.test.ts +0 -259
  27. package/src/__tests__/integration/auth_refresh.integration.test.ts +0 -227
  28. package/src/__tests__/integration/consulting.integration.test.ts +0 -213
  29. package/src/__tests__/integration/feed.integration.test.ts +0 -200
  30. package/src/__tests__/integration/helpers.ts +0 -118
  31. package/src/__tests__/integration/knowledge.integration.test.ts +0 -194
  32. package/src/__tests__/integration/rate_limiting.integration.test.ts +0 -207
  33. package/src/__tests__/integration/submit_task.integration.test.ts +0 -120
  34. package/src/__tests__/integration/wallet_observatory.integration.test.ts +0 -240
  35. package/src/__tests__/nexvora_agentstack_answer.test.ts +0 -120
  36. package/src/__tests__/nexvora_agentstack_ask.test.ts +0 -140
  37. package/src/__tests__/nexvora_agentstack_search.test.ts +0 -188
  38. package/src/__tests__/nexvora_consulting_book.test.ts +0 -277
  39. package/src/__tests__/nexvora_consulting_search.test.ts +0 -153
  40. package/src/__tests__/nexvora_feed_post.test.ts +0 -147
  41. package/src/__tests__/nexvora_feed_react.test.ts +0 -98
  42. package/src/__tests__/nexvora_knowledge_search.test.ts +0 -148
  43. package/src/__tests__/nexvora_knowledge_subscribe.test.ts +0 -173
  44. package/src/__tests__/nexvora_observatory.test.ts +0 -125
  45. package/src/__tests__/nexvora_wallet_balance.test.ts +0 -165
  46. package/src/auth/oauth.ts +0 -247
  47. package/src/cache.ts +0 -34
  48. package/src/cli.ts +0 -171
  49. package/src/config.ts +0 -70
  50. package/src/createServer.ts +0 -90
  51. package/src/defineTool.ts +0 -120
  52. package/src/index.ts +0 -36
  53. package/src/server/sse.ts +0 -149
  54. package/src/tools/nexvora_agentstack_answer.ts +0 -62
  55. package/src/tools/nexvora_agentstack_ask.ts +0 -70
  56. package/src/tools/nexvora_agentstack_search.ts +0 -82
  57. package/src/tools/nexvora_consulting_book.ts +0 -130
  58. package/src/tools/nexvora_consulting_search.ts +0 -85
  59. package/src/tools/nexvora_feed_post.ts +0 -69
  60. package/src/tools/nexvora_feed_react.ts +0 -48
  61. package/src/tools/nexvora_knowledge_search.ts +0 -81
  62. package/src/tools/nexvora_knowledge_subscribe.ts +0 -90
  63. package/src/tools/nexvora_observatory.ts +0 -87
  64. package/src/tools/nexvora_submit_task.ts +0 -42
  65. package/src/tools/nexvora_wallet_balance.ts +0 -112
  66. package/tsconfig.json +0 -19
@@ -1,200 +0,0 @@
1
- import { http, HttpResponse } from "msw";
2
- import { setupServer } from "msw/node";
3
-
4
- import { nexvora_feed_post } from "../../tools/nexvora_feed_post.js";
5
- import { nexvora_feed_react } from "../../tools/nexvora_feed_react.js";
6
- import {
7
- BASE_URL,
8
- REFRESH_RESPONSE,
9
- makeClient,
10
- makeClientWithStore,
11
- silentAuditHandler,
12
- } from "./helpers.js";
13
-
14
- const FEED_POST_RESPONSE = {
15
- id: "post-abc-001",
16
- agentId: "00000000-0000-0000-0000-000000000001",
17
- content: "Spring AI + MCP is a game changer.",
18
- postType: "OPINION",
19
- createdAt: "2024-01-15T12:00:00Z",
20
- };
21
-
22
- const server = setupServer(silentAuditHandler);
23
-
24
- beforeAll(() => server.listen({ onUnhandledRequest: "warn" }));
25
- afterEach(() => server.resetHandlers());
26
- afterAll(() => server.close());
27
-
28
- // ── nexvora_feed_post ─────────────────────────────────────────────────────────
29
-
30
- describe("nexvora_feed_post integration", () => {
31
- it("happy path: publishes post and returns confirmation with post ID", async () => {
32
- server.use(
33
- http.post(`${BASE_URL}/feed/posts`, () => HttpResponse.json(FEED_POST_RESPONSE, { status: 201 })),
34
- );
35
-
36
- const result = await nexvora_feed_post.handler(
37
- {
38
- agentId: "00000000-0000-0000-0000-000000000001",
39
- content: "Spring AI + MCP is a game changer.",
40
- postType: "OPINION",
41
- },
42
- makeClient(),
43
- );
44
-
45
- expect(result).toContain("Post Published");
46
- expect(result).toContain("post-abc-001");
47
- expect(result).toContain("OPINION");
48
- });
49
-
50
- it("error path: returns permission message on 403 (agent not owned)", async () => {
51
- server.use(
52
- http.post(`${BASE_URL}/feed/posts`, () =>
53
- new Response("Forbidden", { status: 403 }),
54
- ),
55
- );
56
-
57
- const result = await nexvora_feed_post.handler(
58
- {
59
- agentId: "00000000-0000-0000-0000-000000000099",
60
- content: "test post",
61
- postType: "OPINION",
62
- },
63
- makeClient(),
64
- );
65
-
66
- expect(result).toContain("permission");
67
- expect(result).toContain("agent");
68
- });
69
-
70
- it("error path: returns content-filter message on 422", async () => {
71
- server.use(
72
- http.post(`${BASE_URL}/feed/posts`, () =>
73
- new Response("Unprocessable Entity", { status: 422 }),
74
- ),
75
- );
76
-
77
- const result = await nexvora_feed_post.handler(
78
- {
79
- agentId: "00000000-0000-0000-0000-000000000001",
80
- content: "inappropriate content",
81
- postType: "OPINION",
82
- },
83
- makeClient(),
84
- );
85
-
86
- expect(result).toContain("content filter");
87
- });
88
-
89
- it("sends correct JSON body including postType", async () => {
90
- let captured: Record<string, unknown> = {};
91
- server.use(
92
- http.post(`${BASE_URL}/feed/posts`, async ({ request }) => {
93
- captured = (await request.json()) as Record<string, unknown>;
94
- return HttpResponse.json(FEED_POST_RESPONSE, { status: 201 });
95
- }),
96
- );
97
-
98
- await nexvora_feed_post.handler(
99
- {
100
- agentId: "00000000-0000-0000-0000-000000000001",
101
- content: "Discovery: Z-garbage collector reduces latency.",
102
- postType: "DISCOVERY",
103
- },
104
- makeClient(),
105
- );
106
-
107
- expect(captured).toMatchObject({
108
- agentId: "00000000-0000-0000-0000-000000000001",
109
- postType: "DISCOVERY",
110
- });
111
- });
112
-
113
- it("auth refresh: retries post after 401 and returns success", async () => {
114
- const { client, cleanup } = makeClientWithStore();
115
-
116
- let callCount = 0;
117
- server.use(
118
- http.post(`${BASE_URL}/feed/posts`, () => {
119
- if (++callCount === 1) return new Response("Unauthorized", { status: 401 });
120
- return HttpResponse.json(FEED_POST_RESPONSE, { status: 201 });
121
- }),
122
- http.post(`${BASE_URL}/auth/refresh`, () => HttpResponse.json(REFRESH_RESPONSE)),
123
- );
124
-
125
- try {
126
- const result = await nexvora_feed_post.handler(
127
- {
128
- agentId: "00000000-0000-0000-0000-000000000001",
129
- content: "test",
130
- postType: "OPINION",
131
- },
132
- client,
133
- );
134
- expect(result).toContain("Post Published");
135
- expect(callCount).toBe(2);
136
- } finally {
137
- cleanup();
138
- }
139
- });
140
- });
141
-
142
- // ── nexvora_feed_react ────────────────────────────────────────────────────────
143
-
144
- describe("nexvora_feed_react integration", () => {
145
- it("happy path: returns confirmation with emoji and post ID", async () => {
146
- server.use(
147
- http.post(`${BASE_URL}/feed/posts/post-abc-001/react`, () =>
148
- HttpResponse.json({ postId: "post-abc-001", emoji: "🔥", totalReactions: 42 }),
149
- ),
150
- );
151
-
152
- const result = await nexvora_feed_react.handler(
153
- { postId: "post-abc-001", emoji: "🔥" },
154
- makeClient(),
155
- );
156
-
157
- expect(result).toContain("🔥");
158
- expect(result).toContain("post-abc-001");
159
- });
160
-
161
- it("error path: returns 404 message when post not found", async () => {
162
- server.use(
163
- http.post(`${BASE_URL}/feed/posts/missing-post/react`, () =>
164
- new Response("Not Found", { status: 404 }),
165
- ),
166
- );
167
-
168
- const result = await nexvora_feed_react.handler(
169
- { postId: "missing-post", emoji: "👍" },
170
- makeClient(),
171
- );
172
-
173
- expect(result).toContain("missing-post");
174
- expect(result).toContain("not found");
175
- });
176
-
177
- it("auth refresh: retries reaction after 401", async () => {
178
- const { client, cleanup } = makeClientWithStore();
179
-
180
- let callCount = 0;
181
- server.use(
182
- http.post(`${BASE_URL}/feed/posts/post-abc-001/react`, () => {
183
- if (++callCount === 1) return new Response("Unauthorized", { status: 401 });
184
- return HttpResponse.json({ postId: "post-abc-001", emoji: "💡", totalReactions: 1 });
185
- }),
186
- http.post(`${BASE_URL}/auth/refresh`, () => HttpResponse.json(REFRESH_RESPONSE)),
187
- );
188
-
189
- try {
190
- const result = await nexvora_feed_react.handler(
191
- { postId: "post-abc-001", emoji: "💡" },
192
- client,
193
- );
194
- expect(result).toContain("💡");
195
- expect(callCount).toBe(2);
196
- } finally {
197
- cleanup();
198
- }
199
- });
200
- });
@@ -1,118 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as os from "node:os";
3
- import * as path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- import { http, HttpResponse } from "msw";
7
-
8
- import { ConfigManager, type Config } from "../../config.js";
9
- import { NexvoraClient } from "../../NexvoraClient.js";
10
-
11
- export const BASE_URL = "https://api.nxvora.online";
12
- export const ACCESS_TOKEN = "int-test-access-token";
13
- export const REFRESH_TOKEN = "int-test-refresh-token";
14
-
15
- const FIXTURES_DIR = path.join(
16
- path.dirname(fileURLToPath(import.meta.url)),
17
- "..",
18
- "fixtures",
19
- );
20
-
21
- /** Load the config fixture template from __tests__/fixtures/config.json */
22
- export function loadConfigFixture(): Config {
23
- return JSON.parse(
24
- fs.readFileSync(path.join(FIXTURES_DIR, "config.json"), "utf8"),
25
- ) as Config;
26
- }
27
-
28
- /** Bare client — no configStore, no auto-refresh */
29
- export function makeClient(): NexvoraClient {
30
- return new NexvoraClient({ baseUrl: BASE_URL, accessToken: ACCESS_TOKEN });
31
- }
32
-
33
- /** Client backed by a real temp-file ConfigManager for auth-refresh tests */
34
- export function makeClientWithStore(
35
- configOverrides?: Partial<Config>,
36
- ): { client: NexvoraClient; configPath: string; cleanup: () => void } {
37
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nexvora-int-"));
38
- const configPath = path.join(tmpDir, "config.json");
39
-
40
- const base = loadConfigFixture();
41
- const config: Config = { ...base, ...configOverrides };
42
- fs.writeFileSync(configPath, JSON.stringify(config), "utf8");
43
-
44
- const store = new ConfigManager(configPath);
45
- const client = new NexvoraClient({
46
- baseUrl: BASE_URL,
47
- accessToken: config.accessToken,
48
- configStore: store,
49
- });
50
-
51
- return {
52
- client,
53
- configPath,
54
- cleanup: () => fs.rmSync(tmpDir, { recursive: true, force: true }),
55
- };
56
- }
57
-
58
- // ── Common MSW response fixtures ────────────────────────────────────────────
59
-
60
- // Backend region-filters the /wallet payload: non-IN users get only the
61
- // usd* fields, IN users get only the inr* fields. We use the non-IN shape
62
- // as the default fixture.
63
- export const WALLET_RESPONSE = {
64
- coinBalance: 250,
65
- usdEquivalent: "3.00",
66
- coinUsdRate: "0.012",
67
- };
68
-
69
- export const LEDGER_RESPONSE = {
70
- content: [
71
- {
72
- id: "t1",
73
- amount: 100,
74
- txType: "CREDIT",
75
- note: "Task reward",
76
- createdAt: "2024-01-15T10:00:00Z",
77
- },
78
- ],
79
- totalElements: 1,
80
- };
81
-
82
- export const OBSERVATORY_RESPONSE = {
83
- onlineAgentsCount: 42,
84
- tasksCompletedToday: 1200,
85
- tasksInQueue: 8,
86
- totalCoinsInCirculation: 999999,
87
- avgTaskLatencyMs: 1500,
88
- topDonors: [
89
- { agentId: "a1", agentName: "BestAgent", tasksCompleted: 300, reputationScore: 98.5 },
90
- ],
91
- };
92
-
93
- export const REFRESH_RESPONSE = {
94
- accessToken: "refreshed-access-token",
95
- refreshToken: "refreshed-refresh-token",
96
- expiresAt: 9999999999,
97
- };
98
-
99
- // ── Audit capture helper ─────────────────────────────────────────────────────
100
-
101
- export type AuditEntry = { toolName: string; outcome: string; [k: string]: unknown };
102
-
103
- /**
104
- * Returns an MSW handler for POST /mcp/audit that records every call.
105
- * Pass the same `captured` array ref to both the handler and your assertions.
106
- */
107
- export function auditCaptureHandler(captured: AuditEntry[]) {
108
- return http.post(`${BASE_URL}/mcp/audit`, async ({ request }) => {
109
- captured.push((await request.json()) as AuditEntry);
110
- return new Response(null, { status: 204 });
111
- });
112
- }
113
-
114
- /** Silent audit handler that accepts all audit calls with no capture */
115
- export const silentAuditHandler = http.post(
116
- `${BASE_URL}/mcp/audit`,
117
- () => new Response(null, { status: 204 }),
118
- );
@@ -1,194 +0,0 @@
1
- import { http, HttpResponse } from "msw";
2
- import { setupServer } from "msw/node";
3
-
4
- import { nexvora_knowledge_search } from "../../tools/nexvora_knowledge_search.js";
5
- import { nexvora_knowledge_subscribe } from "../../tools/nexvora_knowledge_subscribe.js";
6
- import {
7
- BASE_URL,
8
- REFRESH_RESPONSE,
9
- makeClient,
10
- makeClientWithStore,
11
- silentAuditHandler,
12
- } from "./helpers.js";
13
-
14
- const KB_ID = "00000000-0000-0000-0000-000000000099";
15
-
16
- const KNOWLEDGE_PAGE = {
17
- content: [
18
- {
19
- id: KB_ID,
20
- agentName: "DataBot",
21
- title: "Advanced Spring Security Patterns",
22
- description: "Deep dive into OAuth2, JWT, and method security in Spring Boot 3.",
23
- monthlyPriceCoins: 10,
24
- },
25
- ],
26
- page: 0,
27
- size: 10,
28
- totalElements: 1,
29
- totalPages: 1,
30
- };
31
-
32
- const KB_DETAIL = {
33
- id: KB_ID,
34
- agentName: "DataBot",
35
- title: "Advanced Spring Security Patterns",
36
- monthlyPriceCoins: 10,
37
- };
38
-
39
- const SUBSCRIPTION_RESPONSE = {
40
- id: "sub-001",
41
- knowledgeId: KB_ID,
42
- status: "ACTIVE",
43
- nextBillingAt: "2026-06-07T00:00:00Z",
44
- };
45
-
46
- const server = setupServer(silentAuditHandler);
47
-
48
- beforeAll(() => server.listen({ onUnhandledRequest: "warn" }));
49
- afterEach(() => server.resetHandlers());
50
- afterAll(() => server.close());
51
-
52
- // ── nexvora_knowledge_search ──────────────────────────────────────────────────
53
-
54
- describe("nexvora_knowledge_search integration", () => {
55
- it("happy path: returns formatted knowledge base listings", async () => {
56
- server.use(
57
- http.get(`${BASE_URL}/knowledge`, () => HttpResponse.json(KNOWLEDGE_PAGE)),
58
- );
59
-
60
- const result = await nexvora_knowledge_search.handler({}, makeClient());
61
-
62
- expect(result).toContain("Knowledge Bases");
63
- expect(result).toContain("Advanced Spring Security");
64
- expect(result).toContain("10"); // coins/month
65
- expect(result).toContain("DataBot");
66
- });
67
-
68
- it("error path: returns friendly message on 401", async () => {
69
- server.use(
70
- http.get(`${BASE_URL}/knowledge`, () =>
71
- new Response("Unauthorized", { status: 401 }),
72
- ),
73
- );
74
-
75
- const result = await nexvora_knowledge_search.handler({}, makeClient());
76
-
77
- expect(result).toContain("Not authenticated");
78
- });
79
-
80
- it("auth refresh: retries listing after 401", async () => {
81
- const { client, cleanup } = makeClientWithStore();
82
-
83
- let callCount = 0;
84
- server.use(
85
- http.get(`${BASE_URL}/knowledge`, () => {
86
- if (++callCount === 1) return new Response("Unauthorized", { status: 401 });
87
- return HttpResponse.json(KNOWLEDGE_PAGE);
88
- }),
89
- http.post(`${BASE_URL}/auth/refresh`, () => HttpResponse.json(REFRESH_RESPONSE)),
90
- );
91
-
92
- try {
93
- const result = await nexvora_knowledge_search.handler({}, client);
94
- expect(result).toContain("Advanced Spring Security");
95
- expect(callCount).toBe(2);
96
- } finally {
97
- cleanup();
98
- }
99
- });
100
- });
101
-
102
- // ── nexvora_knowledge_subscribe ───────────────────────────────────────────────
103
-
104
- describe("nexvora_knowledge_subscribe integration", () => {
105
- it("preview mode (confirm=false): returns subscription preview with cost", async () => {
106
- server.use(
107
- http.get(`${BASE_URL}/knowledge/${KB_ID}`, () => HttpResponse.json(KB_DETAIL)),
108
- );
109
-
110
- const result = await nexvora_knowledge_subscribe.handler(
111
- { knowledgeId: KB_ID, confirm: false },
112
- makeClient(),
113
- );
114
-
115
- expect(result).toContain("Subscription Preview");
116
- expect(result).toContain("Advanced Spring Security");
117
- expect(result).toContain("10 coins/month");
118
- expect(result).toContain("confirm: true");
119
- });
120
-
121
- it("confirm mode (confirm=true): creates subscription and returns details", async () => {
122
- server.use(
123
- http.get(`${BASE_URL}/knowledge/${KB_ID}`, () => HttpResponse.json(KB_DETAIL)),
124
- http.post(`${BASE_URL}/knowledge/${KB_ID}/subscribe`, () =>
125
- HttpResponse.json(SUBSCRIPTION_RESPONSE, { status: 201 }),
126
- ),
127
- );
128
-
129
- const result = await nexvora_knowledge_subscribe.handler(
130
- { knowledgeId: KB_ID, confirm: true },
131
- makeClient(),
132
- );
133
-
134
- expect(result).toContain("Subscription Confirmed");
135
- expect(result).toContain("sub-001");
136
- expect(result).toContain("ACTIVE");
137
- });
138
-
139
- it("error path: returns already-subscribed message on 409", async () => {
140
- server.use(
141
- http.get(`${BASE_URL}/knowledge/${KB_ID}`, () => HttpResponse.json(KB_DETAIL)),
142
- http.post(`${BASE_URL}/knowledge/${KB_ID}/subscribe`, () =>
143
- new Response("Conflict", { status: 409 }),
144
- ),
145
- );
146
-
147
- const result = await nexvora_knowledge_subscribe.handler(
148
- { knowledgeId: KB_ID, confirm: true },
149
- makeClient(),
150
- );
151
-
152
- expect(result).toContain("already subscribed");
153
- });
154
-
155
- it("error path: returns not-found message when KB ID is unknown", async () => {
156
- const unknownId = "00000000-ffff-0000-0000-000000000000";
157
- server.use(
158
- http.get(`${BASE_URL}/knowledge/${unknownId}`, () =>
159
- new Response("Not Found", { status: 404 }),
160
- ),
161
- );
162
-
163
- const result = await nexvora_knowledge_subscribe.handler(
164
- { knowledgeId: unknownId, confirm: false },
165
- makeClient(),
166
- );
167
-
168
- expect(result).toContain(unknownId);
169
- expect(result).toContain("not found");
170
- });
171
-
172
- it("auth refresh: fetches KB detail after 401 with refreshed token", async () => {
173
- const { client, cleanup } = makeClientWithStore();
174
-
175
- let kbCalls = 0;
176
- server.use(
177
- http.get(`${BASE_URL}/knowledge/${KB_ID}`, () => {
178
- if (++kbCalls === 1) return new Response("Unauthorized", { status: 401 });
179
- return HttpResponse.json(KB_DETAIL);
180
- }),
181
- http.post(`${BASE_URL}/auth/refresh`, () => HttpResponse.json(REFRESH_RESPONSE)),
182
- );
183
-
184
- try {
185
- const result = await nexvora_knowledge_subscribe.handler(
186
- { knowledgeId: KB_ID, confirm: false },
187
- client,
188
- );
189
- expect(result).toContain("Subscription Preview");
190
- } finally {
191
- cleanup();
192
- }
193
- });
194
- });