@access-mcp/announcements 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.
- package/README.md +106 -135
- package/dist/server.d.ts +85 -90
- package/dist/server.js +905 -152
- package/package.json +2 -2
- package/src/index.ts +1 -1
- package/src/server.integration.test.ts +355 -26
- package/src/server.test.ts +1194 -30
- package/src/server.ts +1156 -58
- package/vitest.integration.config.ts +1 -1
package/README.md
CHANGED
|
@@ -1,159 +1,128 @@
|
|
|
1
1
|
# ACCESS Support Announcements MCP Server
|
|
2
2
|
|
|
3
|
-
MCP server
|
|
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
|
-
###
|
|
8
|
-
|
|
7
|
+
### Searching Announcements
|
|
9
8
|
```
|
|
10
9
|
"Recent ACCESS announcements"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"Announcements since January 2024"
|
|
10
|
+
"Search for GPU announcements"
|
|
11
|
+
"Find announcements about machine learning"
|
|
14
12
|
```
|
|
15
13
|
|
|
16
|
-
###
|
|
17
|
-
|
|
14
|
+
### Creating Announcements
|
|
18
15
|
```
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"Bridges-2 system updates"
|
|
22
|
-
"Network-related announcements"
|
|
16
|
+
"Help me create a new announcement"
|
|
17
|
+
"I want to post an announcement about our workshop"
|
|
23
18
|
```
|
|
24
19
|
|
|
25
|
-
###
|
|
26
|
-
|
|
20
|
+
### Managing Your Announcements
|
|
27
21
|
```
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"Allocation proposal news"
|
|
32
|
-
"Python and PyTorch updates"
|
|
22
|
+
"Show my announcements"
|
|
23
|
+
"Update my draft announcement"
|
|
24
|
+
"Delete my announcement"
|
|
33
25
|
```
|
|
34
26
|
|
|
35
27
|
## Tools
|
|
36
28
|
|
|
37
|
-
### search_announcements
|
|
29
|
+
### `search_announcements`
|
|
38
30
|
|
|
39
|
-
Search and filter ACCESS support announcements
|
|
31
|
+
Search and filter ACCESS support announcements (public, read-only).
|
|
40
32
|
|
|
41
33
|
**Parameters:**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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) |
|
|
40
|
+
|
|
41
|
+
**Returns:** `{ total, items: [{ uuid, title, summary, body, published_date, tags, affiliation, affinity_group }] }`
|
|
42
|
+
|
|
43
|
+
**Examples:**
|
|
51
44
|
```javascript
|
|
52
|
-
//
|
|
53
|
-
search_announcements({
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
search_announcements({
|
|
60
|
-
tags: "gpu,nvidia",
|
|
61
|
-
relative_start_date: "-1 year",
|
|
62
|
-
limit: 25
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
// System-specific search - Anvil announcements
|
|
66
|
-
search_announcements({
|
|
67
|
-
ag: "Anvil",
|
|
68
|
-
relative_start_date: "-6 months",
|
|
69
|
-
limit: 15
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
// Machine learning announcements
|
|
73
|
-
search_announcements({
|
|
74
|
-
tags: "machine-learning,ai",
|
|
75
|
-
relative_start_date: "-1 year",
|
|
76
|
-
limit: 20
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
// Combined filters - recent announcements for ACCESS Support
|
|
80
|
-
search_announcements({
|
|
81
|
-
ag: "ACCESS Support",
|
|
82
|
-
tags: "maintenance",
|
|
83
|
-
relative_start_date: "-3 months",
|
|
84
|
-
limit: 10
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
// Exact date range search
|
|
88
|
-
search_announcements({
|
|
89
|
-
tags: "training,professional-development",
|
|
90
|
-
start_date: "2024-01-01",
|
|
91
|
-
end_date: "2024-12-31",
|
|
92
|
-
limit: 25
|
|
93
|
-
})
|
|
45
|
+
// Full-text search
|
|
46
|
+
search_announcements({ query: "GPU computing" })
|
|
47
|
+
|
|
48
|
+
// GPU announcements from the past month
|
|
49
|
+
search_announcements({ tags: "gpu", date: "this_month" })
|
|
50
|
+
|
|
51
|
+
// Combined search
|
|
52
|
+
search_announcements({ query: "workshop", tags: "training", limit: 10 })
|
|
94
53
|
```
|
|
95
54
|
|
|
96
|
-
|
|
55
|
+
### `get_announcement_context`
|
|
97
56
|
|
|
98
|
-
|
|
57
|
+
Get user context and available options before creating an announcement. **Call this first** when creating announcements.
|
|
99
58
|
|
|
100
|
-
**
|
|
101
|
-
- `
|
|
102
|
-
- `
|
|
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)
|
|
103
65
|
|
|
104
|
-
|
|
105
|
-
- `machine-learning`, `ai`, `deep-learning`, `python`, `pytorch`
|
|
106
|
-
- `data-science`, `bioinformatics`, `molecular-dynamics`
|
|
107
|
-
- `mpi`, `openmp`, `cuda`, `singularity`
|
|
66
|
+
### `create_announcement`
|
|
108
67
|
|
|
109
|
-
|
|
110
|
-
- `training`, `professional-development`, `workshop`
|
|
111
|
-
- `allocations-proposal`, `allocation-users`, `allocation-management`
|
|
112
|
-
- `data-transfer`, `storage`, `file-system`
|
|
68
|
+
Create a new ACCESS announcement (saved as draft for staff review).
|
|
113
69
|
|
|
114
|
-
**
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
70
|
+
**Parameters:**
|
|
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) |
|
|
118
81
|
|
|
119
|
-
|
|
82
|
+
**Returns:** `{ success, uuid, title, edit_url }`
|
|
120
83
|
|
|
121
|
-
|
|
122
|
-
- `ACCESS Support`, `DELTA`, `Anvil`, `Bridges-2`, `DARWIN`
|
|
123
|
-
- `Stampede2`, `stampede3`, `osg`
|
|
84
|
+
### `update_announcement`
|
|
124
85
|
|
|
125
|
-
|
|
126
|
-
- `CSSN (Computational Science Support Network)`
|
|
127
|
-
- `Open OnDemand`, `Pegasus`
|
|
128
|
-
- `ACCESS Allocations`
|
|
86
|
+
Update an existing announcement you own.
|
|
129
87
|
|
|
130
|
-
|
|
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) |
|
|
131
99
|
|
|
132
|
-
|
|
100
|
+
**Returns:** `{ success, uuid, title, edit_url }`
|
|
133
101
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
102
|
+
### `delete_announcement`
|
|
103
|
+
|
|
104
|
+
Permanently delete an announcement you own. **Requires explicit user confirmation.**
|
|
105
|
+
|
|
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 |
|
|
111
|
+
|
|
112
|
+
**Important:** For bulk deletes, each announcement must be confirmed individually. General consent ("delete them all") is not sufficient.
|
|
113
|
+
|
|
114
|
+
**Returns:** `{ success, uuid }`
|
|
115
|
+
|
|
116
|
+
### `get_my_announcements`
|
|
117
|
+
|
|
118
|
+
List all announcements created by the authenticated user.
|
|
119
|
+
|
|
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 }] }`
|
|
157
126
|
|
|
158
127
|
## Installation
|
|
159
128
|
|
|
@@ -163,7 +132,7 @@ npm install -g @access-mcp/announcements
|
|
|
163
132
|
|
|
164
133
|
## Configuration
|
|
165
134
|
|
|
166
|
-
|
|
135
|
+
For read-only access (searching public announcements):
|
|
167
136
|
```json
|
|
168
137
|
{
|
|
169
138
|
"mcpServers": {
|
|
@@ -175,27 +144,29 @@ npm install -g @access-mcp/announcements
|
|
|
175
144
|
}
|
|
176
145
|
```
|
|
177
146
|
|
|
178
|
-
|
|
147
|
+
For full access (creating/managing announcements), authentication is required:
|
|
179
148
|
```json
|
|
180
149
|
{
|
|
181
150
|
"mcpServers": {
|
|
182
151
|
"access-announcements": {
|
|
183
|
-
"command": "
|
|
184
|
-
"args": ["/
|
|
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
|
+
}
|
|
185
160
|
}
|
|
186
161
|
}
|
|
187
162
|
}
|
|
188
163
|
```
|
|
189
164
|
|
|
190
|
-
##
|
|
165
|
+
## Resources
|
|
191
166
|
|
|
192
|
-
|
|
193
|
-
# Unit tests
|
|
194
|
-
npm test
|
|
167
|
+
- `accessci://announcements` - Recent announcements (10 most recent)
|
|
195
168
|
|
|
196
|
-
|
|
197
|
-
npm run test:integration
|
|
169
|
+
## Prompts
|
|
198
170
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
```
|
|
171
|
+
- `create_announcement_guide` - Step-by-step guide for creating announcements
|
|
172
|
+
- `manage_announcements_guide` - Guide for viewing, updating, and deleting announcements
|
package/dist/server.d.ts
CHANGED
|
@@ -1,99 +1,94 @@
|
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
description: string;
|
|
21
|
-
};
|
|
22
|
-
relative_start_date: {
|
|
23
|
-
type: string;
|
|
24
|
-
description: string;
|
|
25
|
-
};
|
|
26
|
-
relative_end_date: {
|
|
27
|
-
type: string;
|
|
28
|
-
description: string;
|
|
29
|
-
};
|
|
30
|
-
start_date: {
|
|
31
|
-
type: string;
|
|
32
|
-
description: string;
|
|
33
|
-
format: string;
|
|
34
|
-
};
|
|
35
|
-
end_date: {
|
|
36
|
-
type: string;
|
|
37
|
-
description: string;
|
|
38
|
-
format: string;
|
|
39
|
-
};
|
|
40
|
-
limit: {
|
|
41
|
-
type: string;
|
|
42
|
-
description: string;
|
|
43
|
-
default: number;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
examples: ({
|
|
47
|
-
name: string;
|
|
48
|
-
arguments: {
|
|
49
|
-
relative_start_date: string;
|
|
50
|
-
limit: number;
|
|
51
|
-
tags?: undefined;
|
|
52
|
-
ag?: undefined;
|
|
53
|
-
};
|
|
54
|
-
} | {
|
|
55
|
-
name: string;
|
|
56
|
-
arguments: {
|
|
57
|
-
tags: string;
|
|
58
|
-
relative_start_date: string;
|
|
59
|
-
limit: number;
|
|
60
|
-
ag?: undefined;
|
|
61
|
-
};
|
|
62
|
-
} | {
|
|
63
|
-
name: string;
|
|
64
|
-
arguments: {
|
|
65
|
-
ag: string;
|
|
66
|
-
relative_start_date: string;
|
|
67
|
-
limit: number;
|
|
68
|
-
tags?: undefined;
|
|
69
|
-
};
|
|
70
|
-
})[];
|
|
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
|
+
* Acting user is determined in priority order:
|
|
14
|
+
* 1. X-Acting-User header (from request context)
|
|
15
|
+
* 2. ACTING_USER environment variable (fallback)
|
|
16
|
+
*/
|
|
17
|
+
private getDrupalAuth;
|
|
18
|
+
protected getTools(): Tool[];
|
|
19
|
+
protected getResources(): Resource[];
|
|
20
|
+
protected getPrompts(): Prompt[];
|
|
21
|
+
protected handleGetPrompt(request: {
|
|
22
|
+
params: {
|
|
23
|
+
name: string;
|
|
24
|
+
arguments?: Record<string, string>;
|
|
71
25
|
};
|
|
72
|
-
}
|
|
73
|
-
protected
|
|
74
|
-
|
|
75
|
-
name: string;
|
|
76
|
-
description: string;
|
|
77
|
-
mimeType: string;
|
|
78
|
-
}[];
|
|
79
|
-
handleToolCall(request: any): Promise<{
|
|
80
|
-
content: {
|
|
81
|
-
type: string;
|
|
82
|
-
text: string;
|
|
83
|
-
}[];
|
|
84
|
-
}>;
|
|
85
|
-
handleResourceRead(request: any): Promise<{
|
|
86
|
-
contents: {
|
|
87
|
-
uri: any;
|
|
88
|
-
mimeType: string;
|
|
89
|
-
text: string;
|
|
90
|
-
}[];
|
|
91
|
-
}>;
|
|
26
|
+
}): Promise<GetPromptResult>;
|
|
27
|
+
protected handleToolCall(request: CallToolRequest): Promise<CallToolResult>;
|
|
28
|
+
protected handleResourceRead(request: ReadResourceRequest): Promise<ReadResourceResult>;
|
|
92
29
|
private normalizeLimit;
|
|
93
30
|
private buildAnnouncementsUrl;
|
|
94
31
|
private fetchAnnouncements;
|
|
95
32
|
private enhanceAnnouncements;
|
|
96
33
|
private searchAnnouncements;
|
|
97
|
-
|
|
98
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Get the acting user's ACCESS ID for content attribution.
|
|
36
|
+
*
|
|
37
|
+
* Priority order:
|
|
38
|
+
* 1. X-Acting-User header (from request context)
|
|
39
|
+
* 2. ACTING_USER environment variable (fallback)
|
|
40
|
+
*
|
|
41
|
+
* Returns the ACCESS ID (e.g., "username@access-ci.org")
|
|
42
|
+
*/
|
|
43
|
+
private getActingUserAccessId;
|
|
44
|
+
/**
|
|
45
|
+
* Create a new announcement via Drupal JSON:API
|
|
46
|
+
*
|
|
47
|
+
* The X-Acting-User header (set by DrupalAuthProvider) tells Drupal which
|
|
48
|
+
* ACCESS user is creating the content. Drupal handles user resolution.
|
|
49
|
+
*/
|
|
50
|
+
private createAnnouncement;
|
|
51
|
+
/**
|
|
52
|
+
* Update an existing announcement via Drupal JSON:API
|
|
53
|
+
*/
|
|
54
|
+
private updateAnnouncement;
|
|
55
|
+
/**
|
|
56
|
+
* Delete an announcement via Drupal JSON:API
|
|
57
|
+
*/
|
|
58
|
+
private deleteAnnouncement;
|
|
59
|
+
/**
|
|
60
|
+
* Get announcements created by the acting user
|
|
61
|
+
*/
|
|
62
|
+
private getMyAnnouncements;
|
|
63
|
+
/**
|
|
64
|
+
* Get a user's UUID by their ACCESS ID (e.g., "user@access-ci.org").
|
|
65
|
+
*
|
|
66
|
+
* The Drupal username should match the full ACCESS ID.
|
|
67
|
+
*/
|
|
68
|
+
private getUserUuidByAccessId;
|
|
69
|
+
/**
|
|
70
|
+
* Get announcement context - tags, affinity groups, and options for creating announcements
|
|
71
|
+
*/
|
|
72
|
+
private getAnnouncementContext;
|
|
73
|
+
/**
|
|
74
|
+
* Look up affinity group UUID by ID or name
|
|
75
|
+
*/
|
|
76
|
+
private getAffinityGroupUuid;
|
|
77
|
+
/**
|
|
78
|
+
* Map human-friendly "where to share" labels to Drupal values
|
|
79
|
+
*/
|
|
80
|
+
private static WHERE_TO_SHARE_MAP;
|
|
81
|
+
private normalizeWhereToShare;
|
|
82
|
+
/**
|
|
83
|
+
* Check if tag cache is still valid
|
|
84
|
+
*/
|
|
85
|
+
private isTagCacheValid;
|
|
86
|
+
/**
|
|
87
|
+
* Populate the tag cache with all available tags
|
|
88
|
+
*/
|
|
89
|
+
private populateTagCache;
|
|
90
|
+
/**
|
|
91
|
+
* Get tag UUIDs by their names (with caching)
|
|
92
|
+
*/
|
|
93
|
+
private getTagUuidsByName;
|
|
99
94
|
}
|