@access-mcp/announcements 0.1.0 → 0.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@access-mcp/announcements",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server for ACCESS Support Announcements API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "author": "ACCESS-CI",
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@access-mcp/shared": "file:../shared",
29
+ "@access-mcp/shared": "^0.3.3",
30
30
  "@modelcontextprotocol/sdk": "^0.5.0",
31
31
  "axios": "^1.7.0"
32
32
  },
@@ -35,4 +35,4 @@
35
35
  "typescript": "^5.0.0",
36
36
  "vitest": "^1.0.0"
37
37
  }
38
- }
38
+ }
package/src/index.ts CHANGED
@@ -3,25 +3,9 @@
3
3
  import { AnnouncementsServer } from "./server.js";
4
4
 
5
5
  async function main() {
6
- // Check if we should run as HTTP server (for deployment)
7
- const port = process.env.PORT;
8
-
9
6
  const server = new AnnouncementsServer();
10
-
11
- if (port) {
12
- // Running in HTTP mode (deployment)
13
- await server.start({ httpPort: parseInt(port) });
14
- // Keep the process running in HTTP mode
15
- process.on('SIGINT', () => {
16
- console.log('Shutting down server...');
17
- process.exit(0);
18
- });
19
- // Keep the event loop alive
20
- setInterval(() => {}, 1000 * 60 * 60); // Heartbeat every hour
21
- } else {
22
- // Running in MCP mode (stdio)
23
- await server.start();
24
- }
7
+ const port = process.env.PORT ? parseInt(process.env.PORT, 10) : undefined;
8
+ await server.start(port ? { httpPort: port } : undefined);
25
9
  }
26
10
 
27
11
  main().catch((error) => {
@@ -16,7 +16,7 @@ describe("AnnouncementsServer Integration Tests", () => {
16
16
  it("should fetch real announcements from API", async () => {
17
17
  const result = await server["handleToolCall"]({
18
18
  params: {
19
- name: "get_announcements",
19
+ name: "search_announcements",
20
20
  arguments: {
21
21
  limit: 5,
22
22
  },
@@ -27,21 +27,18 @@ describe("AnnouncementsServer Integration Tests", () => {
27
27
  const responseData = JSON.parse(result.content[0].text);
28
28
 
29
29
  // Check structure
30
- expect(responseData).toHaveProperty("total_announcements");
31
- expect(responseData).toHaveProperty("announcements");
32
- expect(responseData).toHaveProperty("popular_tags");
33
- expect(responseData).toHaveProperty("filters_applied");
34
-
35
- // Announcements should be an array
36
- expect(Array.isArray(responseData.announcements)).toBe(true);
30
+ expect(responseData).toHaveProperty("total");
31
+ expect(responseData).toHaveProperty("items");
32
+
33
+ // Items should be an array
34
+ expect(Array.isArray(responseData.items)).toBe(true);
37
35
 
38
36
  // If there are announcements, check their structure
39
- if (responseData.announcements.length > 0) {
40
- const firstAnnouncement = responseData.announcements[0];
37
+ if (responseData.items.length > 0) {
38
+ const firstAnnouncement = responseData.items[0];
41
39
  expect(firstAnnouncement).toHaveProperty("title");
42
40
  expect(firstAnnouncement).toHaveProperty("body");
43
- expect(firstAnnouncement).toHaveProperty("date");
44
- expect(firstAnnouncement).toHaveProperty("formatted_date");
41
+ expect(firstAnnouncement).toHaveProperty("published_date");
45
42
  expect(firstAnnouncement).toHaveProperty("tags");
46
43
  expect(Array.isArray(firstAnnouncement.tags)).toBe(true);
47
44
  }
@@ -50,7 +47,7 @@ describe("AnnouncementsServer Integration Tests", () => {
50
47
  it("should filter by tags", async () => {
51
48
  const result = await server["handleToolCall"]({
52
49
  params: {
53
- name: "get_announcements_by_tags",
50
+ name: "search_announcements",
54
51
  arguments: {
55
52
  tags: "maintenance",
56
53
  limit: 3,
@@ -60,14 +57,14 @@ describe("AnnouncementsServer Integration Tests", () => {
60
57
 
61
58
  expect(result.isError).toBeFalsy();
62
59
  const responseData = JSON.parse(result.content[0].text);
63
-
64
- expect(responseData).toHaveProperty("announcements");
65
- expect(Array.isArray(responseData.announcements)).toBe(true);
66
-
60
+
61
+ expect(responseData).toHaveProperty("items");
62
+ expect(Array.isArray(responseData.items)).toBe(true);
63
+
67
64
  // If maintenance announcements exist, they should contain the tag
68
- if (responseData.announcements.length > 0) {
69
- const hasMaintenanceTag = responseData.announcements.some((ann: any) =>
70
- ann.tags && ann.tags.some((tag: string) =>
65
+ if (responseData.items.length > 0) {
66
+ const hasMaintenanceTag = responseData.items.some((ann: any) =>
67
+ ann.tags && ann.tags.some((tag: string) =>
71
68
  tag.toLowerCase().includes("maintenance")
72
69
  )
73
70
  );
@@ -79,10 +76,9 @@ describe("AnnouncementsServer Integration Tests", () => {
79
76
  it("should handle date range filters", async () => {
80
77
  const result = await server["handleToolCall"]({
81
78
  params: {
82
- name: "get_announcements",
79
+ name: "search_announcements",
83
80
  arguments: {
84
- relative_start_date: "-1month",
85
- relative_end_date: "today",
81
+ date: "this_month",
86
82
  limit: 10,
87
83
  },
88
84
  },
@@ -90,17 +86,16 @@ describe("AnnouncementsServer Integration Tests", () => {
90
86
 
91
87
  expect(result.isError).toBeFalsy();
92
88
  const responseData = JSON.parse(result.content[0].text);
93
-
94
- expect(responseData).toHaveProperty("announcements");
95
- expect(responseData.filters_applied).toHaveProperty("date_range");
96
-
89
+
90
+ expect(responseData).toHaveProperty("items");
91
+
97
92
  // All announcements should be within the last month
98
- if (responseData.announcements.length > 0) {
93
+ if (responseData.items.length > 0) {
99
94
  const oneMonthAgo = new Date();
100
95
  oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
101
-
102
- responseData.announcements.forEach((ann: any) => {
103
- const annDate = new Date(ann.date);
96
+
97
+ responseData.items.forEach((ann: any) => {
98
+ const annDate = new Date(ann.published_date);
104
99
  expect(annDate.getTime()).toBeGreaterThanOrEqual(oneMonthAgo.getTime());
105
100
  });
106
101
  }
@@ -111,26 +106,25 @@ describe("AnnouncementsServer Integration Tests", () => {
111
106
  it("should fetch announcements from the past week", async () => {
112
107
  const result = await server["handleToolCall"]({
113
108
  params: {
114
- name: "get_recent_announcements",
109
+ name: "search_announcements",
115
110
  arguments: {
116
- period: "1 week",
111
+ date: "this_week",
117
112
  },
118
113
  },
119
114
  } as any);
120
115
 
121
116
  expect(result.isError).toBeFalsy();
122
117
  const responseData = JSON.parse(result.content[0].text);
123
-
124
- expect(responseData).toHaveProperty("announcements");
125
- expect(responseData.filters_applied.date_range).toContain("week");
126
-
118
+
119
+ expect(responseData).toHaveProperty("items");
120
+
127
121
  // Check that announcements are from the past week if any exist
128
- if (responseData.announcements.length > 0) {
122
+ if (responseData.items.length > 0) {
129
123
  const oneWeekAgo = new Date();
130
124
  oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
131
-
132
- responseData.announcements.forEach((ann: any) => {
133
- const annDate = new Date(ann.date);
125
+
126
+ responseData.items.forEach((ann: any) => {
127
+ const annDate = new Date(ann.published_date);
134
128
  expect(annDate.getTime()).toBeGreaterThanOrEqual(oneWeekAgo.getTime());
135
129
  });
136
130
  }
@@ -139,32 +133,29 @@ describe("AnnouncementsServer Integration Tests", () => {
139
133
  it("should handle today filter", async () => {
140
134
  const result = await server["handleToolCall"]({
141
135
  params: {
142
- name: "get_recent_announcements",
136
+ name: "search_announcements",
143
137
  arguments: {
144
- period: "0 days",
138
+ date: "today",
145
139
  },
146
140
  },
147
141
  } as any);
148
142
 
149
143
  expect(result.isError).toBeFalsy();
150
144
  const responseData = JSON.parse(result.content[0].text);
151
-
152
- expect(responseData).toHaveProperty("announcements");
153
- expect(responseData.filters_applied.date_range).toContain("0 days");
154
-
145
+
146
+ expect(responseData).toHaveProperty("items");
147
+
155
148
  // Today's announcements might be empty, that's okay
156
- expect(Array.isArray(responseData.announcements)).toBe(true);
149
+ expect(Array.isArray(responseData.items)).toBe(true);
157
150
  }, 10000);
158
151
  });
159
152
 
160
- describe("get_announcements_by_affinity_group", () => {
161
- it("should handle affinity group filtering", async () => {
162
- // We don't know specific affinity group IDs, so test with a made-up one
153
+ describe("get_announcements with limit", () => {
154
+ it("should respect limit parameter", async () => {
163
155
  const result = await server["handleToolCall"]({
164
156
  params: {
165
- name: "get_announcements_by_affinity_group",
157
+ name: "search_announcements",
166
158
  arguments: {
167
- ag: "test-group-123",
168
159
  limit: 5,
169
160
  },
170
161
  },
@@ -172,49 +163,49 @@ describe("AnnouncementsServer Integration Tests", () => {
172
163
 
173
164
  expect(result.isError).toBeFalsy();
174
165
  const responseData = JSON.parse(result.content[0].text);
175
-
176
- // Should return a valid response even if no announcements match
177
- expect(responseData).toHaveProperty("announcements");
178
- expect(Array.isArray(responseData.announcements)).toBe(true);
179
- expect(responseData.filters_applied).toHaveProperty("affinity_group");
180
- expect(responseData.filters_applied.affinity_group).toBe("test-group-123");
166
+
167
+ // Should return a valid response
168
+ expect(responseData).toHaveProperty("items");
169
+ expect(Array.isArray(responseData.items)).toBe(true);
170
+ if (responseData.items.length > 0) {
171
+ expect(responseData.items.length).toBeLessThanOrEqual(5);
172
+ }
181
173
  }, 10000);
182
174
  });
183
175
 
184
176
  describe("API Error Handling", () => {
185
- it("should handle invalid parameters gracefully", async () => {
177
+ it("should handle search with no parameters", async () => {
186
178
  const result = await server["handleToolCall"]({
187
179
  params: {
188
- name: "get_announcements",
189
- arguments: {
190
- beginning_date: "invalid-date",
191
- },
180
+ name: "search_announcements",
181
+ arguments: {},
192
182
  },
193
183
  } as any);
194
184
 
195
- // The API might accept this or reject it
196
- // Just verify we get a response
185
+ // Should return default results
197
186
  expect(result).toBeDefined();
198
187
  expect(result.content).toBeDefined();
188
+ const responseData = JSON.parse(result.content[0].text);
189
+ expect(responseData).toHaveProperty("items");
199
190
  }, 10000);
200
191
 
201
192
  it("should handle empty results", async () => {
202
193
  const result = await server["handleToolCall"]({
203
194
  params: {
204
- name: "get_announcements",
195
+ name: "search_announcements",
205
196
  arguments: {
206
197
  tags: "nonexistent-tag-xyz-123-456",
207
- exact_match: true,
208
198
  },
209
199
  },
210
200
  } as any);
211
201
 
212
202
  expect(result.isError).toBeFalsy();
213
203
  const responseData = JSON.parse(result.content[0].text);
214
-
215
- expect(responseData.total_announcements).toBe(0);
216
- expect(responseData.announcements).toEqual([]);
217
- expect(responseData.message).toContain("No announcements found");
204
+
205
+ // May have 0 or few results
206
+ expect(responseData).toHaveProperty("total");
207
+ expect(responseData).toHaveProperty("items");
208
+ expect(Array.isArray(responseData.items)).toBe(true);
218
209
  }, 10000);
219
210
  });
220
211
  });