@access-mcp/events 0.2.0 → 0.3.1

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.
@@ -1,13 +1,16 @@
1
- import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
1
+ import { describe, it, expect, beforeEach, vi, afterEach, Mock } from "vitest";
2
2
  import { EventsServer } from "../server.js";
3
- import axios from "axios";
3
+ import { createRequire } from "module";
4
+ const require = createRequire(import.meta.url);
5
+ const { version } = require("../../package.json");
4
6
 
5
- // Mock axios
6
- vi.mock("axios");
7
+ interface MockHttpClient {
8
+ get: Mock<(url: string) => Promise<{ status: number; data: unknown }>>;
9
+ }
7
10
 
8
11
  describe("EventsServer", () => {
9
12
  let server: EventsServer;
10
- let mockHttpClient: any;
13
+ let mockHttpClient: MockHttpClient;
11
14
 
12
15
  beforeEach(() => {
13
16
  server = new EventsServer();
@@ -32,18 +35,15 @@ describe("EventsServer", () => {
32
35
  it("should initialize with correct server name and version", () => {
33
36
  expect(server).toBeDefined();
34
37
  expect(server["serverName"]).toBe("access-mcp-events");
35
- expect(server["version"]).toBe("0.2.0");
38
+ expect(server["version"]).toBe(version);
36
39
  expect(server["baseURL"]).toBe("https://support.access-ci.org");
37
40
  });
38
41
 
39
42
  it("should provide the correct tools", () => {
40
43
  const tools = server["getTools"]();
41
44
 
42
- expect(tools).toHaveLength(4);
43
- expect(tools.map((t) => t.name)).toContain("get_events");
44
- expect(tools.map((t) => t.name)).toContain("get_upcoming_events");
45
- expect(tools.map((t) => t.name)).toContain("search_events");
46
- expect(tools.map((t) => t.name)).toContain("get_events_by_tag");
45
+ expect(tools).toHaveLength(1);
46
+ expect(tools[0].name).toBe("search_events");
47
47
  });
48
48
 
49
49
  it("should provide the correct resources", () => {
@@ -51,140 +51,94 @@ describe("EventsServer", () => {
51
51
 
52
52
  expect(resources).toHaveLength(4);
53
53
  expect(resources.map((r) => r.uri)).toContain("accessci://events");
54
- expect(resources.map((r) => r.uri)).toContain(
55
- "accessci://events/upcoming",
56
- );
57
- expect(resources.map((r) => r.uri)).toContain(
58
- "accessci://events/workshops",
59
- );
60
- expect(resources.map((r) => r.uri)).toContain(
61
- "accessci://events/webinars",
62
- );
54
+ expect(resources.map((r) => r.uri)).toContain("accessci://events/upcoming");
55
+ expect(resources.map((r) => r.uri)).toContain("accessci://events/workshops");
56
+ expect(resources.map((r) => r.uri)).toContain("accessci://events/webinars");
63
57
  });
64
58
  });
65
59
 
66
60
  describe("URL Building", () => {
67
- it("should build correct URLs with v2.1 endpoint", () => {
61
+ it("should build correct URLs with v2.2 endpoint", () => {
68
62
  const url = server["buildEventsUrl"]({});
69
- expect(url).toContain("/api/2.1/events");
63
+ expect(url).toContain("/api/2.2/events");
70
64
  });
71
65
 
72
- it("should build correct URLs with relative date filters", () => {
73
- const url = server["buildEventsUrl"]({
74
- beginning_date_relative: "today",
75
- end_date_relative: "+1week",
76
- });
77
-
66
+ it("should map 'date: today' to beginning_date_relative", () => {
67
+ const url = server["buildEventsUrl"]({ date: "today" });
78
68
  expect(url).toContain("beginning_date_relative=today");
79
- expect(url).toContain("end_date_relative=%2B1week");
80
69
  });
81
70
 
82
- it("should build correct URLs with timezone parameter", () => {
83
- const url = server["buildEventsUrl"]({
84
- beginning_date_relative: "today",
85
- timezone: "America/New_York",
86
- });
87
-
71
+ it("should map 'date: upcoming' to beginning_date_relative", () => {
72
+ const url = server["buildEventsUrl"]({ date: "upcoming" });
88
73
  expect(url).toContain("beginning_date_relative=today");
89
- expect(url).toContain("timezone=America%2FNew_York");
90
74
  });
91
75
 
92
- it("should build correct URLs with absolute date filters", () => {
93
- const url = server["buildEventsUrl"]({
94
- beginning_date: "2024-01-01",
95
- end_date: "2024-12-31",
96
- });
97
-
98
- expect(url).toContain("beginning_date=2024-01-01");
99
- expect(url).toContain("end_date=2024-12-31");
100
- });
101
-
102
- it("should build correct URLs with faceted filters", () => {
103
- const url = server["buildEventsUrl"]({
104
- event_type: "workshop",
105
- skill_level: "beginner",
106
- event_tags: "python",
107
- event_affiliation: "ACCESS",
108
- });
109
-
110
- expect(url).toContain("f%5B0%5D=custom_event_type%3Aworkshop");
111
- expect(url).toContain("f%5B1%5D=custom_event_affiliation%3AACCESS");
112
- expect(url).toContain("f%5B2%5D=skill_level%3Abeginner");
113
- expect(url).toContain("f%5B3%5D=custom_event_tags%3Apython");
76
+ it("should map 'date: past' to date range", () => {
77
+ const url = server["buildEventsUrl"]({ date: "past" });
78
+ expect(url).toContain("beginning_date_relative=-1year");
79
+ expect(url).toContain("end_date_relative=today");
114
80
  });
115
81
 
116
- it("should build correct URLs with mixed filters", () => {
117
- const url = server["buildEventsUrl"]({
118
- beginning_date_relative: "today",
119
- end_date: "2024-12-31",
120
- event_type: "webinar",
121
- skill_level: "intermediate",
122
- });
123
-
82
+ it("should map 'date: this_week' to date range", () => {
83
+ const url = server["buildEventsUrl"]({ date: "this_week" });
124
84
  expect(url).toContain("beginning_date_relative=today");
125
- expect(url).toContain("end_date=2024-12-31");
126
- expect(url).toContain("f%5B0%5D=custom_event_type%3Awebinar");
127
- expect(url).toContain("f%5B1%5D=skill_level%3Aintermediate");
85
+ expect(url).toContain("end_date_relative=%2B1week");
128
86
  });
129
87
 
130
- it("should build correct URLs with timezone and mixed parameters", () => {
131
- const url = server["buildEventsUrl"]({
132
- beginning_date_relative: "today",
133
- end_date_relative: "+1month",
134
- timezone: "Europe/Berlin",
135
- event_type: "workshop",
136
- });
137
-
88
+ it("should map 'date: this_month' to date range", () => {
89
+ const url = server["buildEventsUrl"]({ date: "this_month" });
138
90
  expect(url).toContain("beginning_date_relative=today");
139
91
  expect(url).toContain("end_date_relative=%2B1month");
140
- expect(url).toContain("timezone=Europe%2FBerlin");
92
+ });
93
+
94
+ it("should map 'type' to faceted filter", () => {
95
+ const url = server["buildEventsUrl"]({ type: "workshop" });
141
96
  expect(url).toContain("f%5B0%5D=custom_event_type%3Aworkshop");
142
97
  });
143
98
 
144
- it("should not include timezone parameter when not provided", () => {
145
- const url = server["buildEventsUrl"]({
146
- beginning_date_relative: "today",
147
- });
99
+ it("should map 'tags' to faceted filter", () => {
100
+ const url = server["buildEventsUrl"]({ tags: "python" });
101
+ expect(url).toContain("f%5B0%5D=custom_event_tags%3Apython");
102
+ });
148
103
 
149
- expect(url).toContain("beginning_date_relative=today");
150
- expect(url).not.toContain("timezone=");
104
+ it("should map 'skill' to faceted filter", () => {
105
+ const url = server["buildEventsUrl"]({ skill: "beginner" });
106
+ expect(url).toContain("f%5B0%5D=skill_level%3Abeginner");
151
107
  });
152
108
 
153
- it("should handle various timezone formats", () => {
154
- const timezones = [
155
- "UTC",
156
- "America/New_York",
157
- "America/Los_Angeles",
158
- "Europe/London",
159
- "Asia/Tokyo"
160
- ];
161
-
162
- timezones.forEach(tz => {
163
- const url = server["buildEventsUrl"]({
164
- beginning_date_relative: "today",
165
- timezone: tz,
166
- });
167
- expect(url).toContain(`timezone=${encodeURIComponent(tz)}`);
109
+ it("should build URLs with multiple faceted filters", () => {
110
+ const url = server["buildEventsUrl"]({
111
+ type: "workshop",
112
+ tags: "python",
113
+ skill: "beginner",
168
114
  });
115
+
116
+ expect(url).toContain("f%5B0%5D=custom_event_type%3Aworkshop");
117
+ expect(url).toContain("f%5B1%5D=custom_event_tags%3Apython");
118
+ expect(url).toContain("f%5B2%5D=skill_level%3Abeginner");
169
119
  });
170
120
 
171
- it("should include search_api_fulltext parameter", () => {
121
+ it("should include search_api_fulltext for query parameter", () => {
172
122
  const url = server["buildEventsUrl"]({
173
- search_api_fulltext: "python machine learning",
174
- beginning_date_relative: "today",
123
+ query: "python machine learning",
175
124
  });
176
125
 
177
126
  expect(url).toContain("search_api_fulltext=python+machine+learning");
178
- expect(url).toContain("beginning_date_relative=today");
179
127
  });
180
128
 
181
- it("should not include search parameter when not provided", () => {
129
+ it("should build URLs with mixed universal parameters", () => {
182
130
  const url = server["buildEventsUrl"]({
183
- beginning_date_relative: "today",
131
+ query: "gpu",
132
+ date: "this_week",
133
+ type: "webinar",
134
+ skill: "intermediate",
184
135
  });
185
136
 
186
- expect(url).not.toContain("search_api_fulltext");
137
+ expect(url).toContain("search_api_fulltext=gpu");
187
138
  expect(url).toContain("beginning_date_relative=today");
139
+ expect(url).toContain("end_date_relative=%2B1week");
140
+ expect(url).toContain("f%5B0%5D=custom_event_type%3Awebinar");
141
+ expect(url).toContain("f%5B1%5D=skill_level%3Aintermediate");
188
142
  });
189
143
  });
190
144
 
@@ -194,12 +148,12 @@ describe("EventsServer", () => {
194
148
  id: "1",
195
149
  title: "Python Workshop",
196
150
  description: "Learn Python basics",
197
- date: "2024-08-30T09:00:00",
198
- date_1: "2024-08-30T17:00:00",
151
+ start_date: "2024-08-30T09:00:00",
152
+ end_date: "2024-08-30T17:00:00",
199
153
  location: "Online",
200
154
  event_type: "workshop",
201
155
  event_affiliation: "ACCESS",
202
- custom_event_tags: "python,programming,beginner",
156
+ tags: ["python", "programming", "beginner"],
203
157
  skill_level: "beginner",
204
158
  speakers: "Dr. Smith",
205
159
  contact: "events@example.com",
@@ -212,12 +166,12 @@ describe("EventsServer", () => {
212
166
  id: "2",
213
167
  title: "Machine Learning Webinar",
214
168
  description: "Introduction to ML",
215
- date: "2024-09-01T14:00:00",
216
- date_1: "2024-09-01T15:30:00",
169
+ start_date: "2024-09-01T14:00:00",
170
+ end_date: "2024-09-01T15:30:00",
217
171
  location: "Virtual",
218
172
  event_type: "webinar",
219
173
  event_affiliation: "Community",
220
- custom_event_tags: "machine-learning,ai,python",
174
+ tags: ["machine-learning", "ai", "python"],
221
175
  skill_level: "intermediate",
222
176
  speakers: "Prof. Johnson",
223
177
  contact: "ml@example.com",
@@ -228,7 +182,7 @@ describe("EventsServer", () => {
228
182
  },
229
183
  ];
230
184
 
231
- describe("get_events", () => {
185
+ describe("search_events", () => {
232
186
  it("should get events with no filters", async () => {
233
187
  mockHttpClient.get.mockResolvedValue({
234
188
  status: 200,
@@ -236,30 +190,31 @@ describe("EventsServer", () => {
236
190
  });
237
191
 
238
192
  const result = await server["handleToolCall"]({
193
+ method: "tools/call",
239
194
  params: {
240
- name: "get_events",
195
+ name: "search_events",
241
196
  arguments: {},
242
197
  },
243
198
  });
244
199
 
245
200
  expect(mockHttpClient.get).toHaveBeenCalled();
246
201
  const responseData = JSON.parse(result.content[0].text);
247
- expect(responseData.total_events).toBe(2);
248
- expect(responseData.events).toHaveLength(2);
202
+ expect(responseData.total).toBe(2);
203
+ expect(responseData.items).toHaveLength(2);
249
204
  });
250
205
 
251
- it("should get events with date filters", async () => {
206
+ it("should get events with date filter", async () => {
252
207
  mockHttpClient.get.mockResolvedValue({
253
208
  status: 200,
254
209
  data: mockEventsData,
255
210
  });
256
211
 
257
- const result = await server["handleToolCall"]({
212
+ await server["handleToolCall"]({
213
+ method: "tools/call",
258
214
  params: {
259
- name: "get_events",
215
+ name: "search_events",
260
216
  arguments: {
261
- beginning_date_relative: "today",
262
- end_date_relative: "+1month",
217
+ date: "this_month",
263
218
  },
264
219
  },
265
220
  });
@@ -269,433 +224,158 @@ describe("EventsServer", () => {
269
224
  expect(calledUrl).toContain("end_date_relative=%2B1month");
270
225
  });
271
226
 
272
- it("should get events with faceted filters", async () => {
227
+ it("should get events with type filter", async () => {
273
228
  mockHttpClient.get.mockResolvedValue({
274
229
  status: 200,
275
230
  data: [mockEventsData[0]], // Only workshop
276
231
  });
277
232
 
278
- const result = await server["handleToolCall"]({
279
- params: {
280
- name: "get_events",
281
- arguments: {
282
- event_type: "workshop",
283
- skill_level: "beginner",
284
- },
285
- },
286
- });
287
-
288
- const calledUrl = mockHttpClient.get.mock.calls[0][0];
289
- expect(calledUrl).toContain("custom_event_type%3Aworkshop");
290
- expect(calledUrl).toContain("skill_level%3Abeginner");
291
- });
292
-
293
- it("should apply limit to events", async () => {
294
- mockHttpClient.get.mockResolvedValue({
295
- status: 200,
296
- data: mockEventsData,
297
- });
298
-
299
- const result = await server["handleToolCall"]({
300
- params: {
301
- name: "get_events",
302
- arguments: {
303
- limit: 1,
304
- },
305
- },
306
- });
307
-
308
- const responseData = JSON.parse(result.content[0].text);
309
- expect(responseData.events).toHaveLength(1);
310
- });
311
-
312
- it("should enhance events with calculated fields", async () => {
313
- mockHttpClient.get.mockResolvedValue({
314
- status: 200,
315
- data: mockEventsData,
316
- });
317
-
318
- const result = await server["handleToolCall"]({
319
- params: {
320
- name: "get_events",
321
- arguments: {},
322
- },
323
- });
324
-
325
- const responseData = JSON.parse(result.content[0].text);
326
- const event = responseData.events[0];
327
-
328
- // Check enhanced fields
329
- expect(event.tags).toEqual(["python", "programming", "beginner"]);
330
- expect(event.duration_hours).toBe(8); // 9am to 5pm
331
- expect(event.starts_in_hours).toBeDefined();
332
- });
333
-
334
- it("should extract popular tags", async () => {
335
- mockHttpClient.get.mockResolvedValue({
336
- status: 200,
337
- data: mockEventsData,
338
- });
339
-
340
- const result = await server["handleToolCall"]({
341
- params: {
342
- name: "get_events",
343
- arguments: {},
344
- },
345
- });
346
-
347
- const responseData = JSON.parse(result.content[0].text);
348
- expect(responseData.popular_tags).toContain("python");
349
- expect(responseData.popular_tags).toContain("machine-learning");
350
- });
351
-
352
- it("should include timezone parameter in URL when provided", async () => {
353
- mockHttpClient.get.mockResolvedValue({
354
- status: 200,
355
- data: mockEventsData,
356
- });
357
-
358
- const result = await server["handleToolCall"]({
359
- params: {
360
- name: "get_events",
361
- arguments: {
362
- beginning_date_relative: "today",
363
- timezone: "America/New_York",
364
- },
365
- },
366
- });
367
-
368
- const calledUrl = mockHttpClient.get.mock.calls[0][0];
369
- expect(calledUrl).toContain("timezone=America%2FNew_York");
370
- expect(calledUrl).toContain("beginning_date_relative=today");
371
- });
372
-
373
- it("should include API info with timezone used", async () => {
374
- mockHttpClient.get.mockResolvedValue({
375
- status: 200,
376
- data: mockEventsData,
377
- });
378
-
379
- const result = await server["handleToolCall"]({
380
- params: {
381
- name: "get_events",
382
- arguments: {
383
- beginning_date_relative: "today",
384
- timezone: "Europe/London",
385
- },
386
- },
387
- });
388
-
389
- const responseData = JSON.parse(result.content[0].text);
390
- expect(responseData.api_info).toBeDefined();
391
- expect(responseData.api_info.endpoint_version).toBe("2.1");
392
- expect(responseData.api_info.timezone_used).toBe("Europe/London");
393
- });
394
-
395
- it("should default to UTC timezone when not specified", async () => {
396
- mockHttpClient.get.mockResolvedValue({
397
- status: 200,
398
- data: mockEventsData,
399
- });
400
-
401
- const result = await server["handleToolCall"]({
402
- params: {
403
- name: "get_events",
404
- arguments: {
405
- beginning_date_relative: "today",
406
- },
407
- },
408
- });
409
-
410
- const responseData = JSON.parse(result.content[0].text);
411
- expect(responseData.api_info.timezone_used).toBe("UTC");
412
- });
413
- });
414
-
415
- describe("get_upcoming_events", () => {
416
- it("should get upcoming events with default limit", async () => {
417
- mockHttpClient.get.mockResolvedValue({
418
- status: 200,
419
- data: mockEventsData,
420
- });
421
-
422
- const result = await server["handleToolCall"]({
423
- params: {
424
- name: "get_upcoming_events",
425
- arguments: {},
426
- },
427
- });
428
-
429
- const calledUrl = mockHttpClient.get.mock.calls[0][0];
430
- expect(calledUrl).toContain("beginning_date_relative=today");
431
- const responseData = JSON.parse(result.content[0].text);
432
- expect(responseData.events).toBeDefined();
433
- });
434
-
435
- it("should filter upcoming events by type", async () => {
436
- mockHttpClient.get.mockResolvedValue({
437
- status: 200,
438
- data: [mockEventsData[1]], // Only webinar
439
- });
440
-
441
- const result = await server["handleToolCall"]({
233
+ await server["handleToolCall"]({
234
+ method: "tools/call",
442
235
  params: {
443
- name: "get_upcoming_events",
236
+ name: "search_events",
444
237
  arguments: {
445
- event_type: "webinar",
446
- limit: 10,
238
+ type: "workshop",
447
239
  },
448
240
  },
449
241
  });
450
242
 
451
243
  const calledUrl = mockHttpClient.get.mock.calls[0][0];
452
- expect(calledUrl).toContain("custom_event_type%3Awebinar");
244
+ expect(calledUrl).toContain("f%5B0%5D=custom_event_type%3Aworkshop");
453
245
  });
454
246
 
455
- it("should pass timezone parameter to get_events", async () => {
247
+ it("should get events with skill filter", async () => {
456
248
  mockHttpClient.get.mockResolvedValue({
457
249
  status: 200,
458
- data: mockEventsData,
250
+ data: [mockEventsData[0]],
459
251
  });
460
252
 
461
- const result = await server["handleToolCall"]({
253
+ await server["handleToolCall"]({
254
+ method: "tools/call",
462
255
  params: {
463
- name: "get_upcoming_events",
256
+ name: "search_events",
464
257
  arguments: {
465
- timezone: "America/Chicago",
466
- limit: 5,
258
+ skill: "beginner",
467
259
  },
468
260
  },
469
261
  });
470
262
 
471
263
  const calledUrl = mockHttpClient.get.mock.calls[0][0];
472
- expect(calledUrl).toContain("beginning_date_relative=today");
473
- expect(calledUrl).toContain("timezone=America%2FChicago");
264
+ expect(calledUrl).toContain("f%5B0%5D=skill_level%3Abeginner");
474
265
  });
475
- });
476
266
 
477
- describe("search_events", () => {
478
- it("should search events by query in title", async () => {
267
+ it("should get events with tags filter", async () => {
479
268
  mockHttpClient.get.mockResolvedValue({
480
269
  status: 200,
481
270
  data: mockEventsData,
482
271
  });
483
272
 
484
- const result = await server["handleToolCall"]({
273
+ await server["handleToolCall"]({
274
+ method: "tools/call",
485
275
  params: {
486
276
  name: "search_events",
487
277
  arguments: {
488
- query: "Python",
278
+ tags: "python",
489
279
  },
490
280
  },
491
281
  });
492
282
 
493
- const responseData = JSON.parse(result.content[0].text);
494
- expect(responseData.search_query).toBe("Python");
495
- expect(responseData.total_matches).toBe(2); // Both events have 'python' in tags
496
- expect(responseData.events[0].title).toContain("Python");
283
+ const calledUrl = mockHttpClient.get.mock.calls[0][0];
284
+ expect(calledUrl).toContain("f%5B0%5D=custom_event_tags%3Apython");
497
285
  });
498
286
 
499
- it("should use API native search instead of client-side filtering", async () => {
287
+ it("should apply limit to events", async () => {
500
288
  mockHttpClient.get.mockResolvedValue({
501
289
  status: 200,
502
290
  data: mockEventsData,
503
291
  });
504
292
 
505
293
  const result = await server["handleToolCall"]({
294
+ method: "tools/call",
506
295
  params: {
507
296
  name: "search_events",
508
297
  arguments: {
509
- query: "ML",
298
+ limit: 1,
510
299
  },
511
300
  },
512
301
  });
513
302
 
514
- const calledUrl = mockHttpClient.get.mock.calls[0][0];
515
- expect(calledUrl).toContain("search_api_fulltext=ML");
516
-
517
303
  const responseData = JSON.parse(result.content[0].text);
518
- expect(responseData.search_query).toBe("ML");
519
- expect(responseData.search_method).toBe("API native full-text search");
520
- expect(responseData.total_matches).toBe(mockEventsData.length); // API returns raw count
304
+ expect(responseData.items).toHaveLength(1);
521
305
  });
522
306
 
523
- it("should include search scope information", async () => {
307
+ it("should enhance events with calculated fields", async () => {
524
308
  mockHttpClient.get.mockResolvedValue({
525
309
  status: 200,
526
310
  data: mockEventsData,
527
311
  });
528
312
 
529
313
  const result = await server["handleToolCall"]({
314
+ method: "tools/call",
530
315
  params: {
531
316
  name: "search_events",
532
- arguments: {
533
- query: "machine-learning",
534
- },
317
+ arguments: {},
535
318
  },
536
319
  });
537
320
 
538
- const calledUrl = mockHttpClient.get.mock.calls[0][0];
539
- expect(calledUrl).toContain("search_api_fulltext=machine-learning");
540
-
541
321
  const responseData = JSON.parse(result.content[0].text);
542
- expect(responseData.search_scope).toBe("titles, descriptions, speakers, tags, location, event type");
543
- });
322
+ const event = responseData.items[0];
544
323
 
545
- it("should search with custom date range", async () => {
546
- mockHttpClient.get.mockResolvedValue({
547
- status: 200,
548
- data: mockEventsData,
549
- });
550
-
551
- const result = await server["handleToolCall"]({
552
- params: {
553
- name: "search_events",
554
- arguments: {
555
- query: "workshop",
556
- beginning_date_relative: "-1month",
557
- limit: 5,
558
- },
559
- },
560
- });
561
-
562
- const calledUrl = mockHttpClient.get.mock.calls[0][0];
563
- expect(calledUrl).toContain("beginning_date_relative=-1month");
324
+ // Check enhanced fields
325
+ expect(event.tags).toEqual(["python", "programming", "beginner"]);
326
+ expect(event.duration_hours).toBe(8); // 9am to 5pm
327
+ expect(event.starts_in_hours).toBeDefined();
564
328
  });
565
329
 
566
- it("should include timezone parameter in search", async () => {
330
+ it("should search events by query", async () => {
567
331
  mockHttpClient.get.mockResolvedValue({
568
332
  status: 200,
569
333
  data: mockEventsData,
570
334
  });
571
335
 
572
336
  const result = await server["handleToolCall"]({
337
+ method: "tools/call",
573
338
  params: {
574
339
  name: "search_events",
575
340
  arguments: {
576
- query: "python",
577
- timezone: "Asia/Tokyo",
578
- },
579
- },
580
- });
581
-
582
- const calledUrl = mockHttpClient.get.mock.calls[0][0];
583
- expect(calledUrl).toContain("timezone=Asia%2FTokyo");
584
- expect(calledUrl).toContain("beginning_date_relative=today");
585
- });
586
- });
587
-
588
- describe("get_events_by_tag", () => {
589
- it("should get events by tag for upcoming time range", async () => {
590
- mockHttpClient.get.mockResolvedValue({
591
- status: 200,
592
- data: mockEventsData,
593
- });
594
-
595
- const result = await server["handleToolCall"]({
596
- params: {
597
- name: "get_events_by_tag",
598
- arguments: {
599
- tag: "python",
600
- time_range: "upcoming",
341
+ query: "Python",
601
342
  },
602
343
  },
603
344
  });
604
345
 
605
346
  const calledUrl = mockHttpClient.get.mock.calls[0][0];
606
- expect(calledUrl).toContain("custom_event_tags%3Apython");
607
- expect(calledUrl).toContain("beginning_date_relative=today");
347
+ expect(calledUrl).toContain("search_api_fulltext=Python");
608
348
 
609
349
  const responseData = JSON.parse(result.content[0].text);
610
- expect(responseData.tag).toBe("python");
611
- expect(responseData.time_range).toBe("upcoming");
612
- });
613
-
614
- it("should get events by tag for this week", async () => {
615
- mockHttpClient.get.mockResolvedValue({
616
- status: 200,
617
- data: mockEventsData,
618
- });
619
-
620
- const result = await server["handleToolCall"]({
621
- params: {
622
- name: "get_events_by_tag",
623
- arguments: {
624
- tag: "ai",
625
- time_range: "this_week",
626
- },
627
- },
628
- });
629
-
630
- const calledUrl = mockHttpClient.get.mock.calls[0][0];
631
- expect(calledUrl).toContain("beginning_date_relative=today");
632
- expect(calledUrl).toContain("end_date_relative=%2B1week");
350
+ expect(responseData.total).toBe(2);
351
+ expect(responseData.items).toHaveLength(2);
633
352
  });
634
353
 
635
- it("should get events by tag for this month", async () => {
354
+ it("should combine multiple universal parameters", async () => {
636
355
  mockHttpClient.get.mockResolvedValue({
637
356
  status: 200,
638
- data: mockEventsData,
357
+ data: [mockEventsData[0]],
639
358
  });
640
359
 
641
- const result = await server["handleToolCall"]({
360
+ await server["handleToolCall"]({
361
+ method: "tools/call",
642
362
  params: {
643
- name: "get_events_by_tag",
644
- arguments: {
645
- tag: "programming",
646
- time_range: "this_month",
647
- },
648
- },
649
- });
650
-
651
- const calledUrl = mockHttpClient.get.mock.calls[0][0];
652
- expect(calledUrl).toContain("beginning_date_relative=today");
653
- expect(calledUrl).toContain("end_date_relative=%2B1month");
654
- });
655
-
656
- it("should get all events by tag", async () => {
657
- mockHttpClient.get.mockResolvedValue({
658
- status: 200,
659
- data: mockEventsData,
660
- });
661
-
662
- const result = await server["handleToolCall"]({
663
- params: {
664
- name: "get_events_by_tag",
665
- arguments: {
666
- tag: "beginner",
667
- time_range: "all",
668
- },
669
- },
670
- });
671
-
672
- const calledUrl = mockHttpClient.get.mock.calls[0][0];
673
- expect(calledUrl).not.toContain("beginning_date_relative");
674
- expect(calledUrl).not.toContain("end_date_relative");
675
- });
676
-
677
- it("should include timezone parameter for time-based ranges", async () => {
678
- mockHttpClient.get.mockResolvedValue({
679
- status: 200,
680
- data: mockEventsData,
681
- });
682
-
683
- const result = await server["handleToolCall"]({
684
- params: {
685
- name: "get_events_by_tag",
363
+ name: "search_events",
686
364
  arguments: {
687
- tag: "ai",
688
- time_range: "this_week",
689
- timezone: "Australia/Sydney",
365
+ query: "Python",
366
+ date: "this_week",
367
+ type: "workshop",
368
+ skill: "beginner",
690
369
  },
691
370
  },
692
371
  });
693
372
 
694
373
  const calledUrl = mockHttpClient.get.mock.calls[0][0];
695
- expect(calledUrl).toContain("custom_event_tags%3Aai");
374
+ expect(calledUrl).toContain("search_api_fulltext=Python");
696
375
  expect(calledUrl).toContain("beginning_date_relative=today");
697
376
  expect(calledUrl).toContain("end_date_relative=%2B1week");
698
- expect(calledUrl).toContain("timezone=Australia%2FSydney");
377
+ expect(calledUrl).toContain("f%5B0%5D=custom_event_type%3Aworkshop");
378
+ expect(calledUrl).toContain("f%5B1%5D=skill_level%3Abeginner");
699
379
  });
700
380
  });
701
381
 
@@ -707,13 +387,14 @@ describe("EventsServer", () => {
707
387
  });
708
388
 
709
389
  const result = await server["handleToolCall"]({
390
+ method: "tools/call",
710
391
  params: {
711
- name: "get_events",
392
+ name: "search_events",
712
393
  arguments: {},
713
394
  },
714
395
  });
715
396
 
716
- expect(result.content[0].text).toContain("Error");
397
+ expect(result.content[0].text).toContain("error");
717
398
  expect(result.content[0].text).toContain("404");
718
399
  });
719
400
 
@@ -721,17 +402,19 @@ describe("EventsServer", () => {
721
402
  mockHttpClient.get.mockRejectedValue(new Error("Network error"));
722
403
 
723
404
  const result = await server["handleToolCall"]({
405
+ method: "tools/call",
724
406
  params: {
725
- name: "get_events",
407
+ name: "search_events",
726
408
  arguments: {},
727
409
  },
728
410
  });
729
411
 
730
- expect(result.content[0].text).toContain("Error");
412
+ expect(result.content[0].text).toContain("error");
731
413
  });
732
414
 
733
415
  it("should handle unknown tools", async () => {
734
416
  const result = await server["handleToolCall"]({
417
+ method: "tools/call",
735
418
  params: {
736
419
  name: "unknown_tool",
737
420
  arguments: {},
@@ -749,9 +432,9 @@ describe("EventsServer", () => {
749
432
  id: "1",
750
433
  title: "Test Event",
751
434
  event_type: "workshop",
752
- date: "2024-08-30T09:00:00",
753
- date_1: "2024-08-30T17:00:00",
754
- custom_event_tags: "test",
435
+ start_date: "2024-08-30T09:00:00",
436
+ end_date: "2024-08-30T17:00:00",
437
+ tags: ["test"],
755
438
  },
756
439
  ];
757
440
 
@@ -816,38 +499,4 @@ describe("EventsServer", () => {
816
499
  }).rejects.toThrow("Unknown resource");
817
500
  });
818
501
  });
819
-
820
- describe("Utility Methods", () => {
821
- it("should extract popular tags correctly", () => {
822
- const events = [
823
- { tags: ["python", "ai", "machine-learning"] },
824
- { tags: ["python", "data-science"] },
825
- { tags: ["ai", "gpu"] },
826
- { tags: ["python"] },
827
- { tags: ["machine-learning"] },
828
- ];
829
-
830
- const popularTags = server["getPopularTags"](events);
831
- expect(popularTags[0]).toBe("python"); // Most frequent (3 times)
832
- expect(popularTags[1]).toBe("ai"); // Second most frequent (2 times)
833
- expect(popularTags[2]).toBe("machine-learning"); // Also 2 times
834
- expect(popularTags).toHaveLength(Math.min(10, 5)); // Should return up to 10 tags
835
- });
836
-
837
- it("should handle empty events for popular tags", () => {
838
- const popularTags = server["getPopularTags"]([]);
839
- expect(popularTags).toEqual([]);
840
- });
841
-
842
- it("should handle events without tags", () => {
843
- const events = [
844
- { title: "Event 1" },
845
- { title: "Event 2", tags: null },
846
- { title: "Event 3", tags: undefined },
847
- ];
848
-
849
- const popularTags = server["getPopularTags"](events);
850
- expect(popularTags).toEqual([]);
851
- });
852
- });
853
502
  });