@access-mcp/announcements 0.1.0 → 0.3.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/README.md CHANGED
@@ -1,234 +1,128 @@
1
1
  # ACCESS Support Announcements MCP Server
2
2
 
3
- MCP server providing access to ACCESS support announcements, service updates, maintenance notices, and community communications. Stay informed about system changes, training opportunities, policy updates, and community events through the official ACCESS Support portal.
3
+ MCP server for ACCESS support announcements, service updates, maintenance notices, and community communications. Supports both searching public announcements and creating/managing announcements for authenticated users.
4
4
 
5
5
  ## Usage Examples
6
6
 
7
- ### **Stay Updated on Recent Activity**
8
-
9
- ```
10
- "What's new with ACCESS?"
11
- "Show me recent announcements"
12
- "Any updates from ACCESS Support this week?"
13
- "What are the latest community announcements?"
14
- ```
15
-
16
- ### **Monitor System Maintenance & Issues**
17
-
7
+ ### Searching Announcements
18
8
  ```
19
- "Are there any GPU maintenance announcements?"
20
- "Show me maintenance notices for DELTA"
21
- "What system updates have been posted recently?"
22
- "Any announcements about Bridges-2 issues?"
23
- "Tell me about network-related announcements"
9
+ "Recent ACCESS announcements"
10
+ "Search for GPU announcements"
11
+ "Find announcements about machine learning"
24
12
  ```
25
13
 
26
- ### **Find Training & Educational Content**
27
-
14
+ ### Creating Announcements
28
15
  ```
29
- "Are there any training workshops announced?"
30
- "Show me machine learning training announcements"
31
- "What professional development opportunities are available?"
32
- "Any upcoming webinars or educational events?"
16
+ "Help me create a new announcement"
17
+ "I want to post an announcement about our workshop"
33
18
  ```
34
19
 
35
- ### **Track Specific Systems or Communities**
36
-
20
+ ### Managing Your Announcements
37
21
  ```
38
- "Show me all announcements about Anvil"
39
- "What updates are there for the CSSN community?"
40
- "Any news from the Open OnDemand team?"
41
- "Show me Pegasus-related announcements"
42
- ```
43
-
44
- ### **Search by Topic or Technology**
45
-
46
- ```
47
- "Find announcements about cloud computing"
48
- "Show me AI and machine learning updates"
49
- "Any announcements about allocation proposals?"
50
- "What's new with data science resources?"
51
- "Find announcements about Python or PyTorch"
52
- ```
53
-
54
- ### **Time-Based Searches**
55
-
56
- ```
57
- "Show me announcements from the past month"
58
- "What was announced last week?"
59
- "Find announcements since January 1st, 2024"
60
- "Show me announcements from the past 6 months"
22
+ "Show my announcements"
23
+ "Update my draft announcement"
24
+ "Delete my announcement"
61
25
  ```
62
26
 
63
27
  ## Tools
64
28
 
65
- ### get_announcements
29
+ ### `search_announcements`
66
30
 
67
- Search ACCESS support announcements with comprehensive filtering options.
31
+ Search and filter ACCESS support announcements (public, read-only).
68
32
 
69
33
  **Parameters:**
70
- - `tags` - Filter by topics (comma-separated). Examples: 'gpu,nvidia', 'machine-learning,ai', 'training,workshop'
71
- - `ag` - Filter by system/community group. Examples: 'DELTA', 'Anvil', 'ACCESS Support', 'CSSN (Computational Science Support Network)'
72
- - `relative_start_date` - Filter from relative date. Examples: 'today', '-1 week', '-1 month', '-3 months'
73
- - `relative_end_date` - Filter to relative date. Examples: 'now', 'today', '-1 week', '+1 week'
74
- - `start_date` - Filter from exact date (YYYY-MM-DD). Example: '2024-01-01'
75
- - `end_date` - Filter to exact date (YYYY-MM-DD). Example: '2024-12-31'
76
- - `limit` - Maximum results (default: 20)
77
-
78
- **Usage Examples:**
79
- ```javascript
80
- // Recent GPU-related announcements
81
- get_announcements({
82
- tags: "gpu,nvidia",
83
- relative_start_date: "-1 month",
84
- limit: 10
85
- })
86
-
87
- // DELTA system announcements from this year
88
- get_announcements({
89
- ag: "DELTA",
90
- start_date: "2024-01-01",
91
- limit: 15
92
- })
93
-
94
- // Training announcements from the past 3 months
95
- get_announcements({
96
- tags: "training,professional-development",
97
- relative_start_date: "-3 months",
98
- limit: 20
99
- })
100
- ```
34
+ | Parameter | Type | Description |
35
+ |-----------|------|-------------|
36
+ | `query` | string | Full-text search across title, body, and summary |
37
+ | `tags` | string | Filter by topics (e.g., "gpu", "machine-learning", "training") |
38
+ | `date` | enum | Time period: `today`, `this_week` (last 7 days), `this_month` (last 30 days), `past` (last year) |
39
+ | `limit` | number | Max results (default: 25) |
101
40
 
102
- ### get_announcements_by_tags
41
+ **Returns:** `{ total, items: [{ uuid, title, summary, body, published_date, tags, affiliation, affinity_group }] }`
103
42
 
104
- Find announcements about specific topics or systems.
43
+ **Examples:**
44
+ ```javascript
45
+ // Full-text search
46
+ search_announcements({ query: "GPU computing" })
105
47
 
106
- **Parameters:**
107
- - `tags` (required) - Specific topics (comma-separated). Examples: 'gpu,nvidia', 'data-science,python', 'cloud-computing'
108
- - `limit` - Maximum results (default: 10)
48
+ // GPU announcements from the past month
49
+ search_announcements({ tags: "gpu", date: "this_month" })
109
50
 
110
- **Usage Examples:**
111
- ```javascript
112
- // Machine learning and AI announcements
113
- get_announcements_by_tags({
114
- tags: "machine-learning,ai,deep-learning",
115
- limit: 15
116
- })
117
-
118
- // Cloud computing updates
119
- get_announcements_by_tags({
120
- tags: "cloud-computing,openstack",
121
- limit: 10
122
- })
51
+ // Combined search
52
+ search_announcements({ query: "workshop", tags: "training", limit: 10 })
123
53
  ```
124
54
 
125
- ### get_announcements_by_affinity_group
126
-
127
- Get announcements for specific ACCESS systems or community groups.
55
+ ### `get_announcement_context`
128
56
 
129
- **Parameters:**
130
- - `ag` (required) - System or community name. Examples: 'DELTA', 'Anvil', 'Bridges-2', 'ACCESS Support', 'Open OnDemand'
131
- - `limit` - Maximum results (default: 10)
57
+ Get user context and available options before creating an announcement. **Call this first** when creating announcements.
132
58
 
133
- **Usage Examples:**
134
- ```javascript
135
- // All DELTA-related announcements
136
- get_announcements_by_affinity_group({
137
- ag: "DELTA",
138
- limit: 20
139
- })
140
-
141
- // Open OnDemand community updates
142
- get_announcements_by_affinity_group({
143
- ag: "Open OnDemand",
144
- limit: 10
145
- })
146
- ```
59
+ **Returns:**
60
+ - `tags`: Available tags for announcements
61
+ - `affinity_groups`: Groups the user coordinates (empty if not a coordinator)
62
+ - `is_coordinator`: Boolean - whether user can associate announcements with affinity groups
63
+ - `affiliations`: Available affiliation options ("ACCESS Collaboration", "Community")
64
+ - `where_to_share_options`: Available sharing options (for coordinators)
147
65
 
148
- ### get_recent_announcements
66
+ ### `create_announcement`
149
67
 
150
- Get the latest announcements from a recent time period.
68
+ Create a new ACCESS announcement (saved as draft for staff review).
151
69
 
152
70
  **Parameters:**
153
- - `period` - Time period to look back. Examples: '1 week', '2 weeks', '1 month', '3 months' (default: '1 month')
154
- - `limit` - Maximum results (default: 10)
71
+ | Parameter | Type | Required | Description |
72
+ |-----------|------|----------|-------------|
73
+ | `title` | string | Yes | Clear, specific headline (under 100 characters) |
74
+ | `body` | string | Yes | Full content. HTML supported (`<p>`, `<a>`, `<strong>`, `<em>`, `<ul>`, `<li>`) |
75
+ | `summary` | string | Yes | Brief teaser (1-2 sentences) for listings |
76
+ | `tags` | array | No | Tag names to categorize the announcement |
77
+ | `affiliation` | string | No | "ACCESS Collaboration" or "Community" (default) |
78
+ | `affinity_group` | string | No | Group name/UUID (coordinators only) |
79
+ | `external_link` | object | No | `{ uri: "https://...", title: "Link text" }` |
80
+ | `where_to_share` | array | No | Where to publish: "Announcements page", "Bi-Weekly Digest" (all users), "Affinity Group page", "Email to Affinity Group" (coordinators only) |
155
81
 
156
- **Usage Examples:**
157
- ```javascript
158
- // Past week's announcements
159
- get_recent_announcements({
160
- period: "1 week",
161
- limit: 15
162
- })
163
-
164
- // Past 3 months overview
165
- get_recent_announcements({
166
- period: "3 months",
167
- limit: 25
168
- })
169
- ```
82
+ **Returns:** `{ success, uuid, title, edit_url }`
170
83
 
171
- ## Popular Tags
84
+ ### `update_announcement`
172
85
 
173
- Based on real ACCESS support announcements, here are commonly used tags:
86
+ Update an existing announcement you own.
174
87
 
175
- **Systems & Resources:**
176
- - `gpu`, `nvidia`, `anvil`, `delta`, `bridges-2`, `stampede2`, `stampede3`
177
- - `cloud-computing`, `openstack`, `kubernetes`, `containers`
88
+ **Parameters:**
89
+ | Parameter | Type | Required | Description |
90
+ |-----------|------|----------|-------------|
91
+ | `uuid` | string | Yes | Announcement UUID (from `get_my_announcements`) |
92
+ | `title` | string | No | New title |
93
+ | `body` | string | No | New body content |
94
+ | `summary` | string | No | New summary |
95
+ | `tags` | array | No | New tags |
96
+ | `affinity_group` | string | No | New affinity group |
97
+ | `external_link` | object | No | New external link |
98
+ | `where_to_share` | array | No | Where to publish (see create_announcement for options) |
178
99
 
179
- **Technologies:**
180
- - `machine-learning`, `ai`, `deep-learning`, `python`, `pytorch`
181
- - `data-science`, `bioinformatics`, `molecular-dynamics`
182
- - `mpi`, `openmp`, `cuda`, `singularity`
100
+ **Returns:** `{ success, uuid, title, edit_url }`
183
101
 
184
- **Activities & Services:**
185
- - `training`, `professional-development`, `workshop`
186
- - `allocations-proposal`, `allocation-users`, `allocation-management`
187
- - `data-transfer`, `storage`, `file-system`
102
+ ### `delete_announcement`
188
103
 
189
- **Topics:**
190
- - `maintenance`, `performance-tuning`, `job-submission`
191
- - `security`, `networking`, `hpc-operations`
192
- - `documentation`, `community-outreach`
104
+ Permanently delete an announcement you own. **Requires explicit user confirmation.**
193
105
 
194
- ## Common Affinity Groups
106
+ **Parameters:**
107
+ | Parameter | Type | Required | Description |
108
+ |-----------|------|----------|-------------|
109
+ | `uuid` | string | Yes | Announcement UUID |
110
+ | `confirmed` | boolean | Yes | Must be `true` - only set after showing the user the title/status and getting explicit confirmation |
195
111
 
196
- **ACCESS Systems:**
197
- - `ACCESS Support`, `DELTA`, `Anvil`, `Bridges-2`, `DARWIN`
198
- - `Stampede2`, `stampede3`, `osg`
112
+ **Important:** For bulk deletes, each announcement must be confirmed individually. General consent ("delete them all") is not sufficient.
199
113
 
200
- **Community Groups:**
201
- - `CSSN (Computational Science Support Network)`
202
- - `Open OnDemand`, `Pegasus`
203
- - `ACCESS Allocations`
114
+ **Returns:** `{ success, uuid }`
204
115
 
205
- ## Response Format
116
+ ### `get_my_announcements`
206
117
 
207
- All tools return enhanced JSON responses with:
118
+ List all announcements created by the authenticated user.
208
119
 
209
- ```json
210
- {
211
- "total_announcements": 42,
212
- "filtered_announcements": 10,
213
- "announcements": [
214
- {
215
- "title": "Scheduled Maintenance: DELTA GPU Nodes",
216
- "body": "DELTA GPU nodes will undergo maintenance...",
217
- "date": "2024-03-15",
218
- "formatted_date": "March 15, 2024",
219
- "author": "ACCESS Support",
220
- "tags": ["maintenance", "gpu", "delta"],
221
- "affinity_groups": ["DELTA"],
222
- "body_preview": "DELTA GPU nodes will undergo maintenance from 9 AM to 5 PM..."
223
- }
224
- ],
225
- "popular_tags": ["gpu", "maintenance", "delta"],
226
- "filters_applied": {
227
- "tags": "gpu,maintenance",
228
- "date_range": "-1 month to now"
229
- }
230
- }
231
- ```
120
+ **Parameters:**
121
+ | Parameter | Type | Description |
122
+ |-----------|------|-------------|
123
+ | `limit` | number | Max results (default: 50) |
124
+
125
+ **Returns:** `{ total, items: [{ uuid, nid, title, status, created, published_date, summary, edit_url }] }`
232
126
 
233
127
  ## Installation
234
128
 
@@ -238,7 +132,7 @@ npm install -g @access-mcp/announcements
238
132
 
239
133
  ## Configuration
240
134
 
241
- ### Claude Desktop
135
+ For read-only access (searching public announcements):
242
136
  ```json
243
137
  {
244
138
  "mcpServers": {
@@ -250,27 +144,29 @@ npm install -g @access-mcp/announcements
250
144
  }
251
145
  ```
252
146
 
253
- ### Local Development
147
+ For full access (creating/managing announcements), authentication is required:
254
148
  ```json
255
149
  {
256
150
  "mcpServers": {
257
151
  "access-announcements": {
258
- "command": "/opt/homebrew/bin/node",
259
- "args": ["/path/to/access_mcp/packages/announcements/dist/index.js"]
152
+ "command": "npx",
153
+ "args": ["@access-mcp/announcements"],
154
+ "env": {
155
+ "DRUPAL_API_URL": "https://support.access-ci.org",
156
+ "DRUPAL_USERNAME": "your-username",
157
+ "DRUPAL_PASSWORD": "your-password",
158
+ "ACTING_USER_UID": "12345"
159
+ }
260
160
  }
261
161
  }
262
162
  }
263
163
  ```
264
164
 
265
- ## Testing
165
+ ## Resources
266
166
 
267
- ```bash
268
- # Unit tests
269
- npm test
167
+ - `accessci://announcements` - Recent announcements (10 most recent)
270
168
 
271
- # Integration tests (requires API access)
272
- npm run test:integration
169
+ ## Prompts
273
170
 
274
- # All tests
275
- npm run test:all
276
- ```
171
+ - `create_announcement_guide` - Step-by-step guide for creating announcements
172
+ - `manage_announcements_guide` - Guide for viewing, updating, and deleting announcements
package/dist/index.js CHANGED
@@ -1,24 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { AnnouncementsServer } from "./server.js";
3
3
  async function main() {
4
- // Check if we should run as HTTP server (for deployment)
5
- const port = process.env.PORT;
6
4
  const server = new AnnouncementsServer();
7
- if (port) {
8
- // Running in HTTP mode (deployment)
9
- await server.start({ httpPort: parseInt(port) });
10
- // Keep the process running in HTTP mode
11
- process.on('SIGINT', () => {
12
- console.log('Shutting down server...');
13
- process.exit(0);
14
- });
15
- // Keep the event loop alive
16
- setInterval(() => { }, 1000 * 60 * 60); // Heartbeat every hour
17
- }
18
- else {
19
- // Running in MCP mode (stdio)
20
- await server.start();
21
- }
5
+ const port = process.env.PORT ? parseInt(process.env.PORT, 10) : undefined;
6
+ await server.start(port ? { httpPort: port } : undefined);
22
7
  }
23
8
  main().catch((error) => {
24
9
  // Log errors to stderr and exit
package/dist/server.d.ts CHANGED
@@ -1,156 +1,80 @@
1
- import { BaseAccessServer } from "@access-mcp/shared";
1
+ import { BaseAccessServer, Tool, Resource, CallToolResult } from "@access-mcp/shared";
2
+ import { CallToolRequest, ReadResourceRequest, ReadResourceResult, GetPromptResult, Prompt } from "@modelcontextprotocol/sdk/types.js";
2
3
  export declare class AnnouncementsServer extends BaseAccessServer {
4
+ private drupalAuth?;
5
+ private tagCache;
6
+ private tagCacheExpiry?;
7
+ private static TAG_CACHE_TTL_MS;
3
8
  constructor();
4
- protected getTools(): ({
5
- name: string;
6
- description: string;
7
- inputSchema: {
8
- type: string;
9
- properties: {
10
- tags: {
11
- type: string;
12
- description: string;
13
- required?: undefined;
14
- };
15
- ag: {
16
- type: string;
17
- description: string;
18
- required?: undefined;
19
- };
20
- affiliation: {
21
- type: string;
22
- description: string;
23
- };
24
- relative_start_date: {
25
- type: string;
26
- description: string;
27
- };
28
- relative_end_date: {
29
- type: string;
30
- description: string;
31
- };
32
- start_date: {
33
- type: string;
34
- description: string;
35
- };
36
- end_date: {
37
- type: string;
38
- description: string;
39
- };
40
- limit: {
41
- type: string;
42
- description: string;
43
- default: number;
44
- };
45
- period?: undefined;
46
- };
47
- required?: undefined;
9
+ /**
10
+ * Get or create the Drupal auth provider for JSON:API write operations.
11
+ * Requires DRUPAL_API_URL, DRUPAL_USERNAME, and DRUPAL_PASSWORD env vars.
12
+ */
13
+ private getDrupalAuth;
14
+ protected getTools(): Tool[];
15
+ protected getResources(): Resource[];
16
+ protected getPrompts(): Prompt[];
17
+ protected handleGetPrompt(request: {
18
+ params: {
19
+ name: string;
20
+ arguments?: Record<string, string>;
48
21
  };
49
- } | {
50
- name: string;
51
- description: string;
52
- inputSchema: {
53
- type: string;
54
- properties: {
55
- tags: {
56
- type: string;
57
- description: string;
58
- required: boolean;
59
- };
60
- limit: {
61
- type: string;
62
- description: string;
63
- default: number;
64
- };
65
- ag?: undefined;
66
- affiliation?: undefined;
67
- relative_start_date?: undefined;
68
- relative_end_date?: undefined;
69
- start_date?: undefined;
70
- end_date?: undefined;
71
- period?: undefined;
72
- };
73
- required: string[];
74
- };
75
- } | {
76
- name: string;
77
- description: string;
78
- inputSchema: {
79
- type: string;
80
- properties: {
81
- ag: {
82
- type: string;
83
- description: string;
84
- required: boolean;
85
- };
86
- limit: {
87
- type: string;
88
- description: string;
89
- default: number;
90
- };
91
- tags?: undefined;
92
- affiliation?: undefined;
93
- relative_start_date?: undefined;
94
- relative_end_date?: undefined;
95
- start_date?: undefined;
96
- end_date?: undefined;
97
- period?: undefined;
98
- };
99
- required: string[];
100
- };
101
- } | {
102
- name: string;
103
- description: string;
104
- inputSchema: {
105
- type: string;
106
- properties: {
107
- period: {
108
- type: string;
109
- description: string;
110
- default: string;
111
- };
112
- limit: {
113
- type: string;
114
- description: string;
115
- default: number;
116
- };
117
- tags?: undefined;
118
- ag?: undefined;
119
- affiliation?: undefined;
120
- relative_start_date?: undefined;
121
- relative_end_date?: undefined;
122
- start_date?: undefined;
123
- end_date?: undefined;
124
- };
125
- required?: undefined;
126
- };
127
- })[];
128
- protected getResources(): {
129
- uri: string;
130
- name: string;
131
- description: string;
132
- mimeType: string;
133
- }[];
134
- handleToolCall(request: any): Promise<{
135
- content: {
136
- type: string;
137
- text: string;
138
- }[];
139
- }>;
140
- handleResourceRead(request: any): Promise<{
141
- contents: {
142
- uri: any;
143
- mimeType: string;
144
- text: string;
145
- }[];
146
- }>;
22
+ }): Promise<GetPromptResult>;
23
+ protected handleToolCall(request: CallToolRequest): Promise<CallToolResult>;
24
+ protected handleResourceRead(request: ReadResourceRequest): Promise<ReadResourceResult>;
25
+ private normalizeLimit;
147
26
  private buildAnnouncementsUrl;
148
27
  private fetchAnnouncements;
149
28
  private enhanceAnnouncements;
150
- private getAnnouncements;
151
- private getAnnouncementsByTags;
152
- private getAnnouncementsByAffinityGroup;
153
- private getRecentAnnouncements;
154
- private getPopularTags;
155
- private getAffinityGroups;
29
+ private searchAnnouncements;
30
+ /**
31
+ * Get the acting user's UID from environment variable.
32
+ * This identifies who the announcement should be attributed to.
33
+ */
34
+ private getActingUserUid;
35
+ /**
36
+ * Create a new announcement via Drupal JSON:API
37
+ */
38
+ private createAnnouncement;
39
+ /**
40
+ * Update an existing announcement via Drupal JSON:API
41
+ */
42
+ private updateAnnouncement;
43
+ /**
44
+ * Delete an announcement via Drupal JSON:API
45
+ */
46
+ private deleteAnnouncement;
47
+ /**
48
+ * Get announcements created by the acting user
49
+ */
50
+ private getMyAnnouncements;
51
+ /**
52
+ * Get a user's UUID by their Drupal user ID (internal helper)
53
+ */
54
+ private getUserUuidByUid;
55
+ /**
56
+ * Get announcement context - tags, affinity groups, and options for creating announcements
57
+ */
58
+ private getAnnouncementContext;
59
+ /**
60
+ * Look up affinity group UUID by ID or name
61
+ */
62
+ private getAffinityGroupUuid;
63
+ /**
64
+ * Map human-friendly "where to share" labels to Drupal values
65
+ */
66
+ private static WHERE_TO_SHARE_MAP;
67
+ private normalizeWhereToShare;
68
+ /**
69
+ * Check if tag cache is still valid
70
+ */
71
+ private isTagCacheValid;
72
+ /**
73
+ * Populate the tag cache with all available tags
74
+ */
75
+ private populateTagCache;
76
+ /**
77
+ * Get tag UUIDs by their names (with caching)
78
+ */
79
+ private getTagUuidsByName;
156
80
  }