@nexvora/mcp-server 0.3.2 → 0.4.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.
Files changed (69) 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.js +17 -11
  6. package/dist/cli.js.map +1 -1
  7. package/dist/createServer.d.ts +7 -0
  8. package/dist/createServer.d.ts.map +1 -1
  9. package/dist/createServer.js +3 -3
  10. package/dist/createServer.js.map +1 -1
  11. package/dist/tools/nexvora_submit_task.d.ts +7 -4
  12. package/dist/tools/nexvora_submit_task.d.ts.map +1 -1
  13. package/dist/tools/nexvora_submit_task.js +74 -4
  14. package/dist/tools/nexvora_submit_task.js.map +1 -1
  15. package/package.json +5 -1
  16. package/CHANGELOG.md +0 -208
  17. package/docs/setup/chatgpt-desktop.md +0 -120
  18. package/docs/setup/claude-code.md +0 -152
  19. package/docs/setup/cursor.md +0 -129
  20. package/src/NexvoraClient.ts +0 -328
  21. package/src/RateLimiter.ts +0 -74
  22. package/src/__tests__/NexvoraClient.test.ts +0 -424
  23. package/src/__tests__/RateLimiter.test.ts +0 -151
  24. package/src/__tests__/auth/oauth.test.ts +0 -246
  25. package/src/__tests__/cache.test.ts +0 -64
  26. package/src/__tests__/config.test.ts +0 -98
  27. package/src/__tests__/defineTool.test.ts +0 -223
  28. package/src/__tests__/fixtures/config.json +0 -7
  29. package/src/__tests__/integration/agentstack.integration.test.ts +0 -259
  30. package/src/__tests__/integration/auth_refresh.integration.test.ts +0 -227
  31. package/src/__tests__/integration/consulting.integration.test.ts +0 -213
  32. package/src/__tests__/integration/feed.integration.test.ts +0 -200
  33. package/src/__tests__/integration/helpers.ts +0 -118
  34. package/src/__tests__/integration/knowledge.integration.test.ts +0 -194
  35. package/src/__tests__/integration/rate_limiting.integration.test.ts +0 -207
  36. package/src/__tests__/integration/submit_task.integration.test.ts +0 -120
  37. package/src/__tests__/integration/wallet_observatory.integration.test.ts +0 -240
  38. package/src/__tests__/nexvora_agentstack_answer.test.ts +0 -120
  39. package/src/__tests__/nexvora_agentstack_ask.test.ts +0 -140
  40. package/src/__tests__/nexvora_agentstack_search.test.ts +0 -188
  41. package/src/__tests__/nexvora_consulting_book.test.ts +0 -277
  42. package/src/__tests__/nexvora_consulting_search.test.ts +0 -153
  43. package/src/__tests__/nexvora_feed_post.test.ts +0 -147
  44. package/src/__tests__/nexvora_feed_react.test.ts +0 -98
  45. package/src/__tests__/nexvora_knowledge_search.test.ts +0 -148
  46. package/src/__tests__/nexvora_knowledge_subscribe.test.ts +0 -173
  47. package/src/__tests__/nexvora_observatory.test.ts +0 -125
  48. package/src/__tests__/nexvora_wallet_balance.test.ts +0 -165
  49. package/src/auth/oauth.ts +0 -247
  50. package/src/cache.ts +0 -34
  51. package/src/cli.ts +0 -171
  52. package/src/config.ts +0 -70
  53. package/src/createServer.ts +0 -90
  54. package/src/defineTool.ts +0 -120
  55. package/src/index.ts +0 -36
  56. package/src/server/sse.ts +0 -149
  57. package/src/tools/nexvora_agentstack_answer.ts +0 -62
  58. package/src/tools/nexvora_agentstack_ask.ts +0 -70
  59. package/src/tools/nexvora_agentstack_search.ts +0 -82
  60. package/src/tools/nexvora_consulting_book.ts +0 -130
  61. package/src/tools/nexvora_consulting_search.ts +0 -85
  62. package/src/tools/nexvora_feed_post.ts +0 -69
  63. package/src/tools/nexvora_feed_react.ts +0 -48
  64. package/src/tools/nexvora_knowledge_search.ts +0 -81
  65. package/src/tools/nexvora_knowledge_subscribe.ts +0 -90
  66. package/src/tools/nexvora_observatory.ts +0 -87
  67. package/src/tools/nexvora_submit_task.ts +0 -42
  68. package/src/tools/nexvora_wallet_balance.ts +0 -112
  69. 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
- });