@freelancercom/phabricator-mcp 1.0.5 → 2.0.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 +99 -6
- package/dist/tools/audit.d.ts +3 -0
- package/dist/tools/audit.js +14 -0
- package/dist/tools/conpherence.d.ts +3 -0
- package/dist/tools/conpherence.js +90 -0
- package/dist/tools/differential.js +70 -7
- package/dist/tools/diffusion.js +79 -2
- package/dist/tools/feed.d.ts +3 -0
- package/dist/tools/feed.js +13 -0
- package/dist/tools/file.d.ts +3 -0
- package/dist/tools/file.js +39 -0
- package/dist/tools/harbormaster.d.ts +3 -0
- package/dist/tools/harbormaster.js +104 -0
- package/dist/tools/index.js +12 -0
- package/dist/tools/maniphest.js +73 -2
- package/dist/tools/owners.d.ts +3 -0
- package/dist/tools/owners.js +27 -0
- package/dist/tools/paste.js +43 -1
- package/dist/tools/phame.js +19 -1
- package/dist/tools/phriction.js +23 -4
- package/dist/tools/project.js +29 -8
- package/dist/tools/user.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -170,11 +170,20 @@ Add to your `~/.claude/settings.json`:
|
|
|
170
170
|
"permissions": {
|
|
171
171
|
"allow": [
|
|
172
172
|
"mcp__phabricator__phabricator_task_search",
|
|
173
|
+
"mcp__phabricator__phabricator_task_status_search",
|
|
174
|
+
"mcp__phabricator__phabricator_task_priority_search",
|
|
173
175
|
"mcp__phabricator__phabricator_revision_search",
|
|
174
176
|
"mcp__phabricator__phabricator_diff_search",
|
|
175
|
-
"
|
|
177
|
+
"mcp__phabricator__phabricator_diff_raw",
|
|
178
|
+
"mcp__phabricator__phabricator_changeset_search",
|
|
176
179
|
"mcp__phabricator__phabricator_repository_search",
|
|
177
180
|
"mcp__phabricator__phabricator_commit_search",
|
|
181
|
+
"mcp__phabricator__phabricator_repository_browse",
|
|
182
|
+
"mcp__phabricator__phabricator_repository_file_content",
|
|
183
|
+
"mcp__phabricator__phabricator_branch_search",
|
|
184
|
+
"mcp__phabricator__phabricator_tag_search",
|
|
185
|
+
"mcp__phabricator__phabricator_repository_file_history",
|
|
186
|
+
"mcp__phabricator__phabricator_repository_code_search",
|
|
178
187
|
"mcp__phabricator__phabricator_user_whoami",
|
|
179
188
|
"mcp__phabricator__phabricator_user_search",
|
|
180
189
|
"mcp__phabricator__phabricator_project_search",
|
|
@@ -183,6 +192,18 @@ Add to your `~/.claude/settings.json`:
|
|
|
183
192
|
"mcp__phabricator__phabricator_document_search",
|
|
184
193
|
"mcp__phabricator__phabricator_blog_search",
|
|
185
194
|
"mcp__phabricator__phabricator_blog_post_search",
|
|
195
|
+
"mcp__phabricator__phabricator_file_search",
|
|
196
|
+
"mcp__phabricator__phabricator_file_info",
|
|
197
|
+
"mcp__phabricator__phabricator_buildable_search",
|
|
198
|
+
"mcp__phabricator__phabricator_build_search",
|
|
199
|
+
"mcp__phabricator__phabricator_build_target_search",
|
|
200
|
+
"mcp__phabricator__phabricator_build_log_search",
|
|
201
|
+
"mcp__phabricator__phabricator_build_plan_search",
|
|
202
|
+
"mcp__phabricator__phabricator_owners_search",
|
|
203
|
+
"mcp__phabricator__phabricator_feed_query",
|
|
204
|
+
"mcp__phabricator__phabricator_conpherence_search",
|
|
205
|
+
"mcp__phabricator__phabricator_conpherence_read",
|
|
206
|
+
"mcp__phabricator__phabricator_audit_query",
|
|
186
207
|
"mcp__phabricator__phabricator_phid_lookup",
|
|
187
208
|
"mcp__phabricator__phabricator_phid_query",
|
|
188
209
|
"mcp__phabricator__phabricator_transaction_search",
|
|
@@ -204,15 +225,19 @@ To allowlist all tools including write operations, use `"mcp__phabricator__*"` i
|
|
|
204
225
|
| `phabricator_task_create` | Create a new task |
|
|
205
226
|
| `phabricator_task_edit` | Edit an existing task |
|
|
206
227
|
| `phabricator_task_add_comment` | Add a comment to a task |
|
|
228
|
+
| `phabricator_task_status_search` | List all available task statuses on the instance |
|
|
229
|
+
| `phabricator_task_priority_search` | List all available task priorities on the instance |
|
|
207
230
|
|
|
208
231
|
### Code Reviews (Differential)
|
|
209
232
|
|
|
210
233
|
| Tool | Description |
|
|
211
234
|
|------|-------------|
|
|
212
235
|
| `phabricator_revision_search` | Search code review revisions |
|
|
213
|
-
| `phabricator_revision_edit` | Edit a revision (add reviewers, comment, etc.) |
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
236
|
+
| `phabricator_revision_edit` | Edit a revision (accept, reject, abandon, add reviewers, comment, etc.) |
|
|
237
|
+
| `phabricator_revision_inline_comment` | Create an inline comment on a specific line of a diff |
|
|
238
|
+
| `phabricator_diff_raw` | Get the raw diff/patch content for a diff by ID |
|
|
239
|
+
| `phabricator_diff_search` | Search diffs (code change snapshots within a revision) |
|
|
240
|
+
| `phabricator_changeset_search` | Search changesets (individual changed files within a diff) |
|
|
216
241
|
|
|
217
242
|
### Repositories (Diffusion)
|
|
218
243
|
|
|
@@ -220,6 +245,12 @@ To allowlist all tools including write operations, use `"mcp__phabricator__*"` i
|
|
|
220
245
|
|------|-------------|
|
|
221
246
|
| `phabricator_repository_search` | Search repositories |
|
|
222
247
|
| `phabricator_commit_search` | Search commits |
|
|
248
|
+
| `phabricator_repository_browse` | Browse a repository directory tree |
|
|
249
|
+
| `phabricator_repository_file_content` | Read file contents from a repository |
|
|
250
|
+
| `phabricator_branch_search` | List branches in a repository |
|
|
251
|
+
| `phabricator_tag_search` | List tags in a repository |
|
|
252
|
+
| `phabricator_repository_file_history` | Get commit history for a file path |
|
|
253
|
+
| `phabricator_repository_code_search` | Search (grep) file contents within a repository |
|
|
223
254
|
|
|
224
255
|
### Users
|
|
225
256
|
|
|
@@ -233,7 +264,7 @@ To allowlist all tools including write operations, use `"mcp__phabricator__*"` i
|
|
|
233
264
|
| Tool | Description |
|
|
234
265
|
|------|-------------|
|
|
235
266
|
| `phabricator_project_search` | Search projects |
|
|
236
|
-
| `phabricator_project_edit` |
|
|
267
|
+
| `phabricator_project_edit` | Create or edit a project |
|
|
237
268
|
| `phabricator_column_search` | Search workboard columns |
|
|
238
269
|
|
|
239
270
|
### Pastes
|
|
@@ -242,13 +273,15 @@ To allowlist all tools including write operations, use `"mcp__phabricator__*"` i
|
|
|
242
273
|
|------|-------------|
|
|
243
274
|
| `phabricator_paste_search` | Search pastes |
|
|
244
275
|
| `phabricator_paste_create` | Create a paste |
|
|
276
|
+
| `phabricator_paste_edit` | Edit an existing paste |
|
|
245
277
|
|
|
246
278
|
### Wiki (Phriction)
|
|
247
279
|
|
|
248
280
|
| Tool | Description |
|
|
249
281
|
|------|-------------|
|
|
250
282
|
| `phabricator_document_search` | Search wiki documents |
|
|
251
|
-
| `phabricator_document_edit` |
|
|
283
|
+
| `phabricator_document_edit` | Create or edit a wiki document |
|
|
284
|
+
| `phabricator_document_add_comment` | Add a comment to a wiki document |
|
|
252
285
|
|
|
253
286
|
### Blogs (Phame)
|
|
254
287
|
|
|
@@ -266,6 +299,53 @@ To allowlist all tools including write operations, use `"mcp__phabricator__*"` i
|
|
|
266
299
|
|------|-------------|
|
|
267
300
|
| `phabricator_transaction_search` | Search transactions (comments, status changes, etc.) on any object |
|
|
268
301
|
|
|
302
|
+
### Files
|
|
303
|
+
|
|
304
|
+
| Tool | Description |
|
|
305
|
+
|------|-------------|
|
|
306
|
+
| `phabricator_file_upload` | Upload a file and get an ID for embedding in descriptions/comments via `{F<id>}` |
|
|
307
|
+
| `phabricator_file_search` | Search for files |
|
|
308
|
+
| `phabricator_file_info` | Get file metadata (name, size, MIME type, URI) |
|
|
309
|
+
|
|
310
|
+
### Builds (Harbormaster)
|
|
311
|
+
|
|
312
|
+
| Tool | Description |
|
|
313
|
+
|------|-------------|
|
|
314
|
+
| `phabricator_buildable_search` | Search buildables (revisions/commits with builds) |
|
|
315
|
+
| `phabricator_build_search` | Search builds (CI/build results) |
|
|
316
|
+
| `phabricator_build_target_search` | Search build targets (individual build steps) |
|
|
317
|
+
| `phabricator_build_log_search` | Search build logs (output from build steps) |
|
|
318
|
+
| `phabricator_build_command` | Report build status to Harbormaster (pass, fail, work) |
|
|
319
|
+
| `phabricator_build_plan_search` | Search build plans (CI pipeline configurations) |
|
|
320
|
+
|
|
321
|
+
### Code Ownership (Owners)
|
|
322
|
+
|
|
323
|
+
| Tool | Description |
|
|
324
|
+
|------|-------------|
|
|
325
|
+
| `phabricator_owners_search` | Search code ownership packages |
|
|
326
|
+
|
|
327
|
+
### Activity Feed
|
|
328
|
+
|
|
329
|
+
| Tool | Description |
|
|
330
|
+
|------|-------------|
|
|
331
|
+
| `phabricator_feed_query` | Query the activity feed (recent task updates, revision changes, commits, etc.) |
|
|
332
|
+
|
|
333
|
+
### Chat (Conpherence)
|
|
334
|
+
|
|
335
|
+
| Tool | Description |
|
|
336
|
+
|------|-------------|
|
|
337
|
+
| `phabricator_conpherence_search` | Search chat rooms/threads |
|
|
338
|
+
| `phabricator_conpherence_create` | Create a new chat room/thread |
|
|
339
|
+
| `phabricator_conpherence_edit` | Edit a chat room (rename, manage participants) |
|
|
340
|
+
| `phabricator_conpherence_read` | Read messages from a chat thread |
|
|
341
|
+
| `phabricator_conpherence_send` | Send a message to a chat thread |
|
|
342
|
+
|
|
343
|
+
### Audits
|
|
344
|
+
|
|
345
|
+
| Tool | Description |
|
|
346
|
+
|------|-------------|
|
|
347
|
+
| `phabricator_audit_query` | Search commit audit requests |
|
|
348
|
+
|
|
269
349
|
### PHID Utilities
|
|
270
350
|
|
|
271
351
|
| Tool | Description |
|
|
@@ -288,12 +368,25 @@ Once connected, just ask your AI assistant to perform Phabricator tasks in natur
|
|
|
288
368
|
- "Create a task titled 'Fix login bug' in project Backend"
|
|
289
369
|
- "Add a comment to T12345 saying the fix is ready for review"
|
|
290
370
|
- "Close task T12345"
|
|
371
|
+
- "What custom fields are available for incident tasks?"
|
|
372
|
+
- "Set the start date and root cause category on T12345"
|
|
373
|
+
- "Make T456 a subtask of T123"
|
|
374
|
+
- "Upload this screenshot and add it to the description of T789"
|
|
291
375
|
|
|
292
376
|
**Code Reviews**
|
|
293
377
|
- "Show my open diffs"
|
|
294
378
|
- "What's the status of D6789?"
|
|
295
379
|
- "Review the code changes in D6789"
|
|
296
380
|
- "Add @alice as a reviewer to D6789"
|
|
381
|
+
- "Accept D6789"
|
|
382
|
+
- "Leave an inline comment on line 42 of src/index.ts in D6789"
|
|
383
|
+
|
|
384
|
+
**Repositories & Builds**
|
|
385
|
+
- "Show me the contents of src/config.ts in repo Backend"
|
|
386
|
+
- "Browse the /src directory in the main repo"
|
|
387
|
+
- "Is the build passing on D6789?"
|
|
388
|
+
- "Show me the build logs for D6789"
|
|
389
|
+
- "Who owns the code in /src/auth/?"
|
|
297
390
|
|
|
298
391
|
**Search & Lookup**
|
|
299
392
|
- "Find user john.doe"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerAuditTools(server, client) {
|
|
3
|
+
// Query audits
|
|
4
|
+
server.tool('phabricator_audit_query', 'Search commit audit requests. Find commits needing audit, or audits by a specific user. Uses the audit.query endpoint (no modern replacement available).', {
|
|
5
|
+
auditorPHIDs: z.array(z.string()).optional().describe('Auditor user/project PHIDs'),
|
|
6
|
+
commitPHIDs: z.array(z.string()).optional().describe('Commit PHIDs to check audit status for'),
|
|
7
|
+
status: z.string().optional().describe('Audit status filter: "audit-none", "audit-needs-audit", "audit-accepted", "audit-concern-raised", "audit-requested"'),
|
|
8
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
9
|
+
offset: z.coerce.number().optional().describe('Result offset for pagination'),
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
const result = await client.call('audit.query', params);
|
|
12
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { jsonCoerce } from './coerce.js';
|
|
3
|
+
export function registerConpherenceTools(server, client) {
|
|
4
|
+
// Search chat rooms
|
|
5
|
+
server.tool('phabricator_conpherence_search', 'Search Conpherence chat rooms/threads', {
|
|
6
|
+
queryKey: z.string().optional().describe('Built-in query: "all", "participant"'),
|
|
7
|
+
constraints: jsonCoerce(z.object({
|
|
8
|
+
ids: z.array(z.coerce.number()).optional().describe('Room IDs'),
|
|
9
|
+
phids: z.array(z.string()).optional().describe('Room PHIDs'),
|
|
10
|
+
participants: z.array(z.string()).optional().describe('Participant user PHIDs'),
|
|
11
|
+
})).optional().describe('Search constraints'),
|
|
12
|
+
attachments: jsonCoerce(z.object({
|
|
13
|
+
participants: z.boolean().optional().describe('Include participant details'),
|
|
14
|
+
})).optional().describe('Data attachments'),
|
|
15
|
+
order: z.string().optional().describe('Result order'),
|
|
16
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
17
|
+
after: z.string().optional().describe('Pagination cursor'),
|
|
18
|
+
}, async (params) => {
|
|
19
|
+
const result = await client.call('conpherence.search', params);
|
|
20
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
21
|
+
});
|
|
22
|
+
// Read messages in a thread
|
|
23
|
+
server.tool('phabricator_conpherence_read', 'Read messages from a Conpherence chat room/thread (returned in reverse chronological order). Uses conpherence.querythread (the only Conduit method that returns message content).', {
|
|
24
|
+
roomID: z.coerce.number().describe('Numeric room ID (use phabricator_conpherence_search to find it)'),
|
|
25
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum messages to return'),
|
|
26
|
+
offset: z.coerce.number().optional().describe('Result offset for pagination'),
|
|
27
|
+
}, async (params) => {
|
|
28
|
+
const result = await client.call('conpherence.querythread', {
|
|
29
|
+
ids: [params.roomID],
|
|
30
|
+
limit: params.limit,
|
|
31
|
+
offset: params.offset,
|
|
32
|
+
});
|
|
33
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
34
|
+
});
|
|
35
|
+
// Create a new thread
|
|
36
|
+
server.tool('phabricator_conpherence_create', 'Create a new Conpherence chat room/thread', {
|
|
37
|
+
title: z.string().describe('Thread title'),
|
|
38
|
+
message: z.string().optional().describe('Initial message (supports Remarkup)'),
|
|
39
|
+
participantPHIDs: z.array(z.string()).optional().describe('Participant user PHIDs to add'),
|
|
40
|
+
}, async (params) => {
|
|
41
|
+
const transactions = [
|
|
42
|
+
{ type: 'title', value: params.title },
|
|
43
|
+
];
|
|
44
|
+
if (params.message !== undefined) {
|
|
45
|
+
transactions.push({ type: 'comment', value: params.message });
|
|
46
|
+
}
|
|
47
|
+
if (params.participantPHIDs !== undefined) {
|
|
48
|
+
transactions.push({ type: 'participants.add', value: params.participantPHIDs });
|
|
49
|
+
}
|
|
50
|
+
const result = await client.call('conpherence.edit', { transactions });
|
|
51
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
52
|
+
});
|
|
53
|
+
// Edit an existing thread
|
|
54
|
+
server.tool('phabricator_conpherence_edit', 'Edit a Conpherence chat room/thread. Rename it or manage participants.', {
|
|
55
|
+
objectIdentifier: z.string().describe('Room ID or PHID'),
|
|
56
|
+
title: z.string().optional().describe('New room title'),
|
|
57
|
+
addParticipantPHIDs: z.array(z.string()).optional().describe('Participant PHIDs to add'),
|
|
58
|
+
removeParticipantPHIDs: z.array(z.string()).optional().describe('Participant PHIDs to remove'),
|
|
59
|
+
}, async (params) => {
|
|
60
|
+
const transactions = [];
|
|
61
|
+
if (params.title !== undefined) {
|
|
62
|
+
transactions.push({ type: 'title', value: params.title });
|
|
63
|
+
}
|
|
64
|
+
if (params.addParticipantPHIDs !== undefined) {
|
|
65
|
+
transactions.push({ type: 'participants.add', value: params.addParticipantPHIDs });
|
|
66
|
+
}
|
|
67
|
+
if (params.removeParticipantPHIDs !== undefined) {
|
|
68
|
+
transactions.push({ type: 'participants.remove', value: params.removeParticipantPHIDs });
|
|
69
|
+
}
|
|
70
|
+
if (transactions.length === 0) {
|
|
71
|
+
return { content: [{ type: 'text', text: 'No changes specified' }] };
|
|
72
|
+
}
|
|
73
|
+
const result = await client.call('conpherence.edit', {
|
|
74
|
+
objectIdentifier: params.objectIdentifier,
|
|
75
|
+
transactions,
|
|
76
|
+
});
|
|
77
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
78
|
+
});
|
|
79
|
+
// Send a message
|
|
80
|
+
server.tool('phabricator_conpherence_send', 'Send a message to a Conpherence chat room/thread', {
|
|
81
|
+
objectIdentifier: z.string().describe('Room ID or PHID'),
|
|
82
|
+
message: z.string().describe('Message text (supports Remarkup)'),
|
|
83
|
+
}, async (params) => {
|
|
84
|
+
const result = await client.call('conpherence.edit', {
|
|
85
|
+
objectIdentifier: params.objectIdentifier,
|
|
86
|
+
transactions: [{ type: 'comment', value: params.message }],
|
|
87
|
+
});
|
|
88
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -3,7 +3,7 @@ import { jsonCoerce } from './coerce.js';
|
|
|
3
3
|
export function registerDifferentialTools(server, client) {
|
|
4
4
|
// Search revisions
|
|
5
5
|
server.tool('phabricator_revision_search', 'Search Differential revisions (code reviews)', {
|
|
6
|
-
queryKey: z.string().optional().describe('Built-in query: "all", "active", "authored", "waiting"'),
|
|
6
|
+
queryKey: z.string().optional().describe('Built-in query: "all", "active", "authored", "waiting", "reviewable"'),
|
|
7
7
|
constraints: jsonCoerce(z.object({
|
|
8
8
|
ids: z.array(z.coerce.number()).optional().describe('Revision IDs'),
|
|
9
9
|
phids: z.array(z.string()).optional().describe('Revision PHIDs'),
|
|
@@ -11,21 +11,29 @@ export function registerDifferentialTools(server, client) {
|
|
|
11
11
|
reviewerPHIDs: z.array(z.string()).optional().describe('Reviewer PHIDs'),
|
|
12
12
|
repositoryPHIDs: z.array(z.string()).optional().describe('Repository PHIDs'),
|
|
13
13
|
statuses: z.array(z.string()).optional().describe('Statuses: needs-review, needs-revision, accepted, published, abandoned, changes-planned'),
|
|
14
|
+
responsiblePHIDs: z.array(z.string()).optional().describe('User PHIDs who are responsible (as author or reviewer)'),
|
|
15
|
+
affectedPaths: z.array(z.string()).optional().describe('File paths affected by the revision'),
|
|
16
|
+
createdStart: z.coerce.number().optional().describe('Created after (epoch timestamp)'),
|
|
17
|
+
createdEnd: z.coerce.number().optional().describe('Created before (epoch timestamp)'),
|
|
18
|
+
modifiedStart: z.coerce.number().optional().describe('Modified after (epoch timestamp)'),
|
|
19
|
+
modifiedEnd: z.coerce.number().optional().describe('Modified before (epoch timestamp)'),
|
|
20
|
+
query: z.string().optional().describe('Full-text search query'),
|
|
14
21
|
})).optional().describe('Search constraints'),
|
|
15
22
|
attachments: jsonCoerce(z.object({
|
|
16
23
|
reviewers: z.boolean().optional().describe('Include reviewers'),
|
|
17
24
|
subscribers: z.boolean().optional().describe('Include subscribers'),
|
|
18
25
|
projects: z.boolean().optional().describe('Include projects'),
|
|
26
|
+
'reviewers-extra': z.boolean().optional().describe('Include detailed reviewer info with status (accepted, rejected, etc.)'),
|
|
19
27
|
})).optional().describe('Data attachments'),
|
|
20
|
-
order: z.string().optional().describe('Result order'),
|
|
21
|
-
limit: z.coerce.number().max(100).optional().describe('Maximum results'),
|
|
28
|
+
order: z.string().optional().describe('Result order: "newest", "oldest", "updated", "relevance"'),
|
|
29
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
22
30
|
after: z.string().optional().describe('Pagination cursor'),
|
|
23
31
|
}, async (params) => {
|
|
24
32
|
const result = await client.call('differential.revision.search', params);
|
|
25
33
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
26
34
|
});
|
|
27
35
|
// Edit revision
|
|
28
|
-
server.tool('phabricator_revision_edit', 'Edit a Differential revision', {
|
|
36
|
+
server.tool('phabricator_revision_edit', 'Edit a Differential revision. Supports actions like accept, reject, abandon, request-review, plan-changes, and commandeer. Can also add/remove reviewers, subscribers, and comments.', {
|
|
29
37
|
objectIdentifier: z.string().describe('Revision PHID or ID (e.g., "D123")'),
|
|
30
38
|
title: z.string().optional().describe('New title'),
|
|
31
39
|
summary: z.string().optional().describe('New summary'),
|
|
@@ -35,6 +43,10 @@ export function registerDifferentialTools(server, client) {
|
|
|
35
43
|
addProjectPHIDs: z.array(z.string()).optional().describe('Add projects'),
|
|
36
44
|
removeProjectPHIDs: z.array(z.string()).optional().describe('Remove projects'),
|
|
37
45
|
comment: z.string().optional().describe('Add a comment'),
|
|
46
|
+
action: z.enum(['accept', 'reject', 'abandon', 'reclaim', 'request-review', 'resign', 'commandeer', 'plan-changes']).optional().describe('Revision action to take'),
|
|
47
|
+
addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
|
|
48
|
+
removeSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to remove'),
|
|
49
|
+
repositoryPHID: z.string().optional().describe('Repository PHID to associate with the revision'),
|
|
38
50
|
}, async (params) => {
|
|
39
51
|
const transactions = [];
|
|
40
52
|
if (params.title !== undefined) {
|
|
@@ -61,6 +73,18 @@ export function registerDifferentialTools(server, client) {
|
|
|
61
73
|
if (params.comment !== undefined) {
|
|
62
74
|
transactions.push({ type: 'comment', value: params.comment });
|
|
63
75
|
}
|
|
76
|
+
if (params.action !== undefined) {
|
|
77
|
+
transactions.push({ type: 'action', value: params.action });
|
|
78
|
+
}
|
|
79
|
+
if (params.addSubscriberPHIDs !== undefined) {
|
|
80
|
+
transactions.push({ type: 'subscribers.add', value: params.addSubscriberPHIDs });
|
|
81
|
+
}
|
|
82
|
+
if (params.removeSubscriberPHIDs !== undefined) {
|
|
83
|
+
transactions.push({ type: 'subscribers.remove', value: params.removeSubscriberPHIDs });
|
|
84
|
+
}
|
|
85
|
+
if (params.repositoryPHID !== undefined) {
|
|
86
|
+
transactions.push({ type: 'repository', value: params.repositoryPHID });
|
|
87
|
+
}
|
|
64
88
|
if (transactions.length === 0) {
|
|
65
89
|
return { content: [{ type: 'text', text: 'No changes specified' }] };
|
|
66
90
|
}
|
|
@@ -71,7 +95,7 @@ export function registerDifferentialTools(server, client) {
|
|
|
71
95
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
72
96
|
});
|
|
73
97
|
// Get raw diff content
|
|
74
|
-
server.tool('
|
|
98
|
+
server.tool('phabricator_diff_raw', 'Get the raw diff/patch content for a Differential diff by diff ID. Use phabricator_diff_search to find the diff ID from a revision PHID first.', {
|
|
75
99
|
diffID: z.coerce.number().describe('The diff ID (numeric, e.g., 1392561). Use phabricator_diff_search to find this from a revision.'),
|
|
76
100
|
}, async (params) => {
|
|
77
101
|
const result = await client.call('differential.getrawdiff', {
|
|
@@ -80,7 +104,8 @@ export function registerDifferentialTools(server, client) {
|
|
|
80
104
|
return { content: [{ type: 'text', text: result }] };
|
|
81
105
|
});
|
|
82
106
|
// Search diffs
|
|
83
|
-
server.tool('phabricator_diff_search', 'Search Differential diffs', {
|
|
107
|
+
server.tool('phabricator_diff_search', 'Search Differential diffs (code change snapshots within a revision). A revision may have multiple diffs as it gets updated.', {
|
|
108
|
+
queryKey: z.string().optional().describe('Built-in query: "all"'),
|
|
84
109
|
constraints: jsonCoerce(z.object({
|
|
85
110
|
ids: z.array(z.coerce.number()).optional().describe('Diff IDs'),
|
|
86
111
|
phids: z.array(z.string()).optional().describe('Diff PHIDs'),
|
|
@@ -89,10 +114,48 @@ export function registerDifferentialTools(server, client) {
|
|
|
89
114
|
attachments: jsonCoerce(z.object({
|
|
90
115
|
commits: z.boolean().optional().describe('Include commit info'),
|
|
91
116
|
})).optional().describe('Data attachments'),
|
|
92
|
-
|
|
117
|
+
order: z.string().optional().describe('Result order: "newest", "oldest"'),
|
|
118
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
93
119
|
after: z.string().optional().describe('Pagination cursor'),
|
|
94
120
|
}, async (params) => {
|
|
95
121
|
const result = await client.call('differential.diff.search', params);
|
|
96
122
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
97
123
|
});
|
|
124
|
+
// Search changesets (changed files within a diff)
|
|
125
|
+
server.tool('phabricator_changeset_search', 'Search changesets (individual changed files) within a Differential diff. Use phabricator_diff_search to find the diff PHID first.', {
|
|
126
|
+
queryKey: z.string().optional().describe('Built-in query: "all"'),
|
|
127
|
+
constraints: jsonCoerce(z.object({
|
|
128
|
+
diffPHIDs: z.array(z.string()).optional().describe('Diff PHIDs to list changesets for'),
|
|
129
|
+
})).optional().describe('Search constraints'),
|
|
130
|
+
attachments: jsonCoerce(z.object({
|
|
131
|
+
hunks: z.boolean().optional().describe('Include diff hunks (actual changed content)'),
|
|
132
|
+
})).optional().describe('Data attachments'),
|
|
133
|
+
order: z.string().optional().describe('Result order'),
|
|
134
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
135
|
+
after: z.string().optional().describe('Pagination cursor'),
|
|
136
|
+
}, async (params) => {
|
|
137
|
+
const result = await client.call('differential.changeset.search', params);
|
|
138
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
139
|
+
});
|
|
140
|
+
// Create inline comment on a diff
|
|
141
|
+
server.tool('phabricator_revision_inline_comment', 'Create an inline comment on a specific line of a Differential diff. The comment will appear as a draft — publish it by calling phabricator_revision_edit with a comment on the same revision.', {
|
|
142
|
+
revisionID: z.coerce.number().describe('Numeric revision ID (e.g., 123). Do not include the "D" prefix.'),
|
|
143
|
+
diffID: z.coerce.number().describe('Diff ID to comment on. Use phabricator_diff_search to find this.'),
|
|
144
|
+
filePath: z.string().describe('Path to the file being commented on'),
|
|
145
|
+
lineNumber: z.coerce.number().describe('Line number in the file'),
|
|
146
|
+
lineLength: z.coerce.number().optional().describe('Number of lines the comment spans (default: 0 for single line)'),
|
|
147
|
+
content: z.string().describe('Comment text (supports Remarkup)'),
|
|
148
|
+
isNewFile: z.boolean().optional().describe('Whether the line number refers to the new file (true) or old file (false). Default: true'),
|
|
149
|
+
}, async (params) => {
|
|
150
|
+
const result = await client.call('differential.createinline', {
|
|
151
|
+
revisionID: params.revisionID,
|
|
152
|
+
diffID: params.diffID,
|
|
153
|
+
filePath: params.filePath,
|
|
154
|
+
lineNumber: params.lineNumber,
|
|
155
|
+
lineLength: params.lineLength ?? 0,
|
|
156
|
+
content: params.content,
|
|
157
|
+
isNewFile: (params.isNewFile ?? true) ? 1 : 0,
|
|
158
|
+
});
|
|
159
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
160
|
+
});
|
|
98
161
|
}
|
package/dist/tools/diffusion.js
CHANGED
|
@@ -19,7 +19,7 @@ export function registerDiffusionTools(server, client) {
|
|
|
19
19
|
projects: z.boolean().optional().describe('Include projects'),
|
|
20
20
|
})).optional().describe('Data attachments'),
|
|
21
21
|
order: z.string().optional().describe('Result order'),
|
|
22
|
-
limit: z.coerce.number().max(100).optional().describe('Maximum results'),
|
|
22
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
23
23
|
after: z.string().optional().describe('Pagination cursor'),
|
|
24
24
|
}, async (params) => {
|
|
25
25
|
const result = await client.call('diffusion.repository.search', params);
|
|
@@ -27,6 +27,7 @@ export function registerDiffusionTools(server, client) {
|
|
|
27
27
|
});
|
|
28
28
|
// Search commits
|
|
29
29
|
server.tool('phabricator_commit_search', 'Search Diffusion commits', {
|
|
30
|
+
queryKey: z.string().optional().describe('Built-in query: "all", "authored"'),
|
|
30
31
|
constraints: jsonCoerce(z.object({
|
|
31
32
|
ids: z.array(z.coerce.number()).optional().describe('Commit IDs'),
|
|
32
33
|
phids: z.array(z.string()).optional().describe('Commit PHIDs'),
|
|
@@ -38,12 +39,88 @@ export function registerDiffusionTools(server, client) {
|
|
|
38
39
|
attachments: jsonCoerce(z.object({
|
|
39
40
|
projects: z.boolean().optional().describe('Include projects'),
|
|
40
41
|
subscribers: z.boolean().optional().describe('Include subscribers'),
|
|
42
|
+
auditors: z.boolean().optional().describe('Include auditor info'),
|
|
41
43
|
})).optional().describe('Data attachments'),
|
|
42
44
|
order: z.string().optional().describe('Result order'),
|
|
43
|
-
limit: z.coerce.number().max(100).optional().describe('Maximum results'),
|
|
45
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
44
46
|
after: z.string().optional().describe('Pagination cursor'),
|
|
45
47
|
}, async (params) => {
|
|
46
48
|
const result = await client.call('diffusion.commit.search', params);
|
|
47
49
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
48
50
|
});
|
|
51
|
+
// Browse repository file tree
|
|
52
|
+
server.tool('phabricator_repository_browse', 'Browse a repository directory tree at a given path and commit/branch', {
|
|
53
|
+
path: z.string().describe('Path to browse (e.g., "/", "/src/")'),
|
|
54
|
+
repository: z.string().optional().describe('Repository callsign, short name, or PHID'),
|
|
55
|
+
commit: z.string().optional().describe('Commit hash or branch name (default: HEAD)'),
|
|
56
|
+
}, async (params) => {
|
|
57
|
+
const result = await client.call('diffusion.browsequery', {
|
|
58
|
+
path: params.path,
|
|
59
|
+
repository: params.repository,
|
|
60
|
+
commit: params.commit,
|
|
61
|
+
});
|
|
62
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
63
|
+
});
|
|
64
|
+
// Read file content from repository
|
|
65
|
+
server.tool('phabricator_repository_file_content', 'Read file contents from a Diffusion repository at a given path and commit/branch', {
|
|
66
|
+
path: z.string().describe('File path in the repository (e.g., "src/index.ts")'),
|
|
67
|
+
repository: z.string().optional().describe('Repository callsign, short name, or PHID'),
|
|
68
|
+
commit: z.string().optional().describe('Commit hash or branch name (default: HEAD)'),
|
|
69
|
+
}, async (params) => {
|
|
70
|
+
const result = await client.call('diffusion.filecontentquery', {
|
|
71
|
+
path: params.path,
|
|
72
|
+
repository: params.repository,
|
|
73
|
+
commit: params.commit,
|
|
74
|
+
});
|
|
75
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
76
|
+
});
|
|
77
|
+
// List branches
|
|
78
|
+
server.tool('phabricator_branch_search', 'List branches in a Diffusion repository', {
|
|
79
|
+
repository: z.string().describe('Repository callsign, short name, or PHID'),
|
|
80
|
+
contains: z.string().optional().describe('Only branches containing this commit'),
|
|
81
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
82
|
+
offset: z.coerce.number().optional().describe('Result offset for pagination'),
|
|
83
|
+
}, async (params) => {
|
|
84
|
+
const result = await client.call('diffusion.branchquery', params);
|
|
85
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
86
|
+
});
|
|
87
|
+
// List tags
|
|
88
|
+
server.tool('phabricator_tag_search', 'List tags in a Diffusion repository', {
|
|
89
|
+
repository: z.string().describe('Repository callsign, short name, or PHID'),
|
|
90
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
91
|
+
offset: z.coerce.number().optional().describe('Result offset for pagination'),
|
|
92
|
+
}, async (params) => {
|
|
93
|
+
const result = await client.call('diffusion.tagsquery', params);
|
|
94
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
95
|
+
});
|
|
96
|
+
// File commit history
|
|
97
|
+
server.tool('phabricator_repository_file_history', 'Get commit history for a file path in a Diffusion repository', {
|
|
98
|
+
path: z.string().describe('File path in the repository'),
|
|
99
|
+
repository: z.string().optional().describe('Repository callsign, short name, or PHID'),
|
|
100
|
+
commit: z.string().optional().describe('Commit hash or branch to start from (default: HEAD)'),
|
|
101
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
102
|
+
offset: z.coerce.number().optional().describe('Result offset for pagination'),
|
|
103
|
+
}, async (params) => {
|
|
104
|
+
const result = await client.call('diffusion.historyquery', params);
|
|
105
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
106
|
+
});
|
|
107
|
+
// Search file contents in repository
|
|
108
|
+
server.tool('phabricator_repository_code_search', 'Search (grep) file contents within a Diffusion repository', {
|
|
109
|
+
path: z.string().optional().describe('Directory path to search within (default: root)'),
|
|
110
|
+
repository: z.string().describe('Repository callsign, short name, or PHID'),
|
|
111
|
+
query: z.string().describe('Search query / pattern'),
|
|
112
|
+
commit: z.string().optional().describe('Commit hash or branch (default: HEAD)'),
|
|
113
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
114
|
+
offset: z.coerce.number().optional().describe('Result offset for pagination'),
|
|
115
|
+
}, async (params) => {
|
|
116
|
+
const result = await client.call('diffusion.searchquery', {
|
|
117
|
+
path: params.path ?? '/',
|
|
118
|
+
repository: params.repository,
|
|
119
|
+
grep: params.query,
|
|
120
|
+
commit: params.commit,
|
|
121
|
+
limit: params.limit,
|
|
122
|
+
offset: params.offset,
|
|
123
|
+
});
|
|
124
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
125
|
+
});
|
|
49
126
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerFeedTools(server, client) {
|
|
3
|
+
// Query activity feed
|
|
4
|
+
server.tool('phabricator_feed_query', 'Query the Phabricator activity feed. Returns recent activity (task updates, revision changes, commits, etc.) as an object keyed by story PHID. Uses feed.query (the only Conduit method for feed data).', {
|
|
5
|
+
filterPHIDs: z.array(z.string()).optional().describe('Only show activity involving these PHIDs (user, project, task, etc.)'),
|
|
6
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
7
|
+
after: z.string().optional().describe('Cursor for pagination (chronological key from previous results)'),
|
|
8
|
+
before: z.string().optional().describe('Cursor for reverse pagination'),
|
|
9
|
+
}, async (params) => {
|
|
10
|
+
const result = await client.call('feed.query', params);
|
|
11
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { jsonCoerce } from './coerce.js';
|
|
3
|
+
export function registerFileTools(server, client) {
|
|
4
|
+
// Upload a file
|
|
5
|
+
server.tool('phabricator_file_upload', 'Upload a file to Phabricator. Returns a file PHID that can be used with phabricator_file_info to get the file ID for embedding in Remarkup via {F<id>}.', {
|
|
6
|
+
name: z.string().describe('Filename with extension (e.g. "screenshot.png")'),
|
|
7
|
+
data_base64: z.string().describe('Base64-encoded file content'),
|
|
8
|
+
}, async (params) => {
|
|
9
|
+
const phid = await client.call('file.upload', {
|
|
10
|
+
name: params.name,
|
|
11
|
+
data_base64: params.data_base64,
|
|
12
|
+
});
|
|
13
|
+
return { content: [{ type: 'text', text: phid }] };
|
|
14
|
+
});
|
|
15
|
+
// Search files
|
|
16
|
+
server.tool('phabricator_file_search', 'Search for files in Phabricator', {
|
|
17
|
+
queryKey: z.string().optional().describe('Built-in query: "all", "authored"'),
|
|
18
|
+
constraints: jsonCoerce(z.object({
|
|
19
|
+
ids: z.array(z.coerce.number()).optional().describe('File IDs'),
|
|
20
|
+
phids: z.array(z.string()).optional().describe('File PHIDs'),
|
|
21
|
+
authorPHIDs: z.array(z.string()).optional().describe('Author PHIDs'),
|
|
22
|
+
names: z.array(z.string()).optional().describe('File names'),
|
|
23
|
+
})).optional().describe('Search constraints'),
|
|
24
|
+
order: z.string().optional().describe('Result order'),
|
|
25
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
26
|
+
after: z.string().optional().describe('Pagination cursor'),
|
|
27
|
+
}, async (params) => {
|
|
28
|
+
const result = await client.call('file.search', params);
|
|
29
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
30
|
+
});
|
|
31
|
+
// Get file info
|
|
32
|
+
server.tool('phabricator_file_info', 'Get metadata about a file (name, size, MIME type, URI). Use the returned URI to download. Provide at least one of id or phid. Uses file.info (the only Conduit method that returns download URIs).', {
|
|
33
|
+
id: z.coerce.number().optional().describe('File ID (provide this or phid)'),
|
|
34
|
+
phid: z.string().optional().describe('File PHID (provide this or id)'),
|
|
35
|
+
}, async (params) => {
|
|
36
|
+
const result = await client.call('file.info', params);
|
|
37
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { jsonCoerce } from './coerce.js';
|
|
3
|
+
export function registerHarbormasterTools(server, client) {
|
|
4
|
+
// Search buildables
|
|
5
|
+
server.tool('phabricator_buildable_search', 'Search Harbormaster buildables (objects that can be built, like revisions or commits)', {
|
|
6
|
+
queryKey: z.string().optional().describe('Built-in query: "all"'),
|
|
7
|
+
constraints: jsonCoerce(z.object({
|
|
8
|
+
ids: z.array(z.coerce.number()).optional().describe('Buildable IDs'),
|
|
9
|
+
phids: z.array(z.string()).optional().describe('Buildable PHIDs'),
|
|
10
|
+
objectPHIDs: z.array(z.string()).optional().describe('Object PHIDs (revision or commit PHIDs)'),
|
|
11
|
+
containerPHIDs: z.array(z.string()).optional().describe('Container PHIDs'),
|
|
12
|
+
statuses: z.array(z.string()).optional().describe('Buildable statuses'),
|
|
13
|
+
})).optional().describe('Search constraints'),
|
|
14
|
+
attachments: jsonCoerce(z.object({
|
|
15
|
+
builds: z.boolean().optional().describe('Include builds for each buildable'),
|
|
16
|
+
})).optional().describe('Data attachments'),
|
|
17
|
+
order: z.string().optional().describe('Result order'),
|
|
18
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
19
|
+
after: z.string().optional().describe('Pagination cursor'),
|
|
20
|
+
}, async (params) => {
|
|
21
|
+
const result = await client.call('harbormaster.buildable.search', params);
|
|
22
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
23
|
+
});
|
|
24
|
+
// Search builds
|
|
25
|
+
server.tool('phabricator_build_search', 'Search Harbormaster builds (CI/build results)', {
|
|
26
|
+
queryKey: z.string().optional().describe('Built-in query: "all"'),
|
|
27
|
+
constraints: jsonCoerce(z.object({
|
|
28
|
+
ids: z.array(z.coerce.number()).optional().describe('Build IDs'),
|
|
29
|
+
phids: z.array(z.string()).optional().describe('Build PHIDs'),
|
|
30
|
+
buildablePHIDs: z.array(z.string()).optional().describe('Buildable PHIDs'),
|
|
31
|
+
buildPlanPHIDs: z.array(z.string()).optional().describe('Build plan PHIDs'),
|
|
32
|
+
statuses: z.array(z.string()).optional().describe('Build statuses: building, passed, failed, aborted, error, paused, deadlocked'),
|
|
33
|
+
})).optional().describe('Search constraints'),
|
|
34
|
+
attachments: jsonCoerce(z.object({
|
|
35
|
+
targets: z.boolean().optional().describe('Include build targets for each build'),
|
|
36
|
+
})).optional().describe('Data attachments'),
|
|
37
|
+
order: z.string().optional().describe('Result order'),
|
|
38
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
39
|
+
after: z.string().optional().describe('Pagination cursor'),
|
|
40
|
+
}, async (params) => {
|
|
41
|
+
const result = await client.call('harbormaster.build.search', params);
|
|
42
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
43
|
+
});
|
|
44
|
+
// Search build targets
|
|
45
|
+
server.tool('phabricator_build_target_search', 'Search Harbormaster build targets (individual build steps within a build)', {
|
|
46
|
+
queryKey: z.string().optional().describe('Built-in query: "all"'),
|
|
47
|
+
constraints: jsonCoerce(z.object({
|
|
48
|
+
ids: z.array(z.coerce.number()).optional().describe('Target IDs'),
|
|
49
|
+
phids: z.array(z.string()).optional().describe('Target PHIDs'),
|
|
50
|
+
buildPHIDs: z.array(z.string()).optional().describe('Build PHIDs'),
|
|
51
|
+
})).optional().describe('Search constraints'),
|
|
52
|
+
order: z.string().optional().describe('Result order'),
|
|
53
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
54
|
+
after: z.string().optional().describe('Pagination cursor'),
|
|
55
|
+
}, async (params) => {
|
|
56
|
+
const result = await client.call('harbormaster.target.search', params);
|
|
57
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
58
|
+
});
|
|
59
|
+
// Search build logs
|
|
60
|
+
server.tool('phabricator_build_log_search', 'Search Harbormaster build logs (output from build steps). Use phabricator_build_target_search to find target PHIDs first.', {
|
|
61
|
+
queryKey: z.string().optional().describe('Built-in query: "all"'),
|
|
62
|
+
constraints: jsonCoerce(z.object({
|
|
63
|
+
ids: z.array(z.coerce.number()).optional().describe('Log IDs'),
|
|
64
|
+
phids: z.array(z.string()).optional().describe('Log PHIDs'),
|
|
65
|
+
buildTargetPHIDs: z.array(z.string()).optional().describe('Build target PHIDs'),
|
|
66
|
+
})).optional().describe('Search constraints'),
|
|
67
|
+
attachments: jsonCoerce(z.object({
|
|
68
|
+
content: z.boolean().optional().describe('Include actual log text content'),
|
|
69
|
+
})).optional().describe('Data attachments'),
|
|
70
|
+
order: z.string().optional().describe('Result order'),
|
|
71
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
72
|
+
after: z.string().optional().describe('Pagination cursor'),
|
|
73
|
+
}, async (params) => {
|
|
74
|
+
const result = await client.call('harbormaster.log.search', params);
|
|
75
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
76
|
+
});
|
|
77
|
+
// Send build command
|
|
78
|
+
server.tool('phabricator_build_command', 'Report build status to Harbormaster. Used by external build systems to notify Phabricator of build results. Provide the build target PHID (use phabricator_build_target_search to find it).', {
|
|
79
|
+
buildTargetPHID: z.string().describe('Build target PHID to send the message to. Use phabricator_build_target_search to find this.'),
|
|
80
|
+
type: z.enum(['pass', 'fail', 'work']).describe('Message type: "pass" (build succeeded), "fail" (build failed), "work" (build is still running)'),
|
|
81
|
+
}, async (params) => {
|
|
82
|
+
const result = await client.call('harbormaster.sendmessage', {
|
|
83
|
+
buildTargetPHID: params.buildTargetPHID,
|
|
84
|
+
type: params.type,
|
|
85
|
+
});
|
|
86
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
87
|
+
});
|
|
88
|
+
// Search build plans
|
|
89
|
+
server.tool('phabricator_build_plan_search', 'Search Harbormaster build plans (CI pipeline configurations)', {
|
|
90
|
+
queryKey: z.string().optional().describe('Built-in query: "all", "active"'),
|
|
91
|
+
constraints: jsonCoerce(z.object({
|
|
92
|
+
ids: z.array(z.coerce.number()).optional().describe('Build plan IDs'),
|
|
93
|
+
phids: z.array(z.string()).optional().describe('Build plan PHIDs'),
|
|
94
|
+
statuses: z.array(z.string()).optional().describe('Plan statuses'),
|
|
95
|
+
query: z.string().optional().describe('Full-text search query'),
|
|
96
|
+
})).optional().describe('Search constraints'),
|
|
97
|
+
order: z.string().optional().describe('Result order'),
|
|
98
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
99
|
+
after: z.string().optional().describe('Pagination cursor'),
|
|
100
|
+
}, async (params) => {
|
|
101
|
+
const result = await client.call('harbormaster.buildplan.search', params);
|
|
102
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
103
|
+
});
|
|
104
|
+
}
|
package/dist/tools/index.js
CHANGED
|
@@ -11,6 +11,12 @@ import { registerPhrictionTools } from './phriction.js';
|
|
|
11
11
|
import { registerPhidTools } from './phid.js';
|
|
12
12
|
import { registerPhameTools } from './phame.js';
|
|
13
13
|
import { registerTransactionTools } from './transaction.js';
|
|
14
|
+
import { registerFileTools } from './file.js';
|
|
15
|
+
import { registerHarbormasterTools } from './harbormaster.js';
|
|
16
|
+
import { registerOwnersTools } from './owners.js';
|
|
17
|
+
import { registerFeedTools } from './feed.js';
|
|
18
|
+
import { registerConpherenceTools } from './conpherence.js';
|
|
19
|
+
import { registerAuditTools } from './audit.js';
|
|
14
20
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
21
|
const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf-8'));
|
|
16
22
|
export function registerAllTools(server, client) {
|
|
@@ -27,4 +33,10 @@ export function registerAllTools(server, client) {
|
|
|
27
33
|
registerPhidTools(server, client);
|
|
28
34
|
registerPhameTools(server, client);
|
|
29
35
|
registerTransactionTools(server, client);
|
|
36
|
+
registerFileTools(server, client);
|
|
37
|
+
registerHarbormasterTools(server, client);
|
|
38
|
+
registerOwnersTools(server, client);
|
|
39
|
+
registerFeedTools(server, client);
|
|
40
|
+
registerConpherenceTools(server, client);
|
|
41
|
+
registerAuditTools(server, client);
|
|
30
42
|
}
|
package/dist/tools/maniphest.js
CHANGED
|
@@ -7,7 +7,7 @@ export function registerManiphestTools(server, client) {
|
|
|
7
7
|
constraints: jsonCoerce(z.object({
|
|
8
8
|
ids: z.array(z.coerce.number()).optional().describe('Task IDs to search for'),
|
|
9
9
|
phids: z.array(z.string()).optional().describe('Task PHIDs to search for'),
|
|
10
|
-
|
|
10
|
+
assignedPHIDs: z.array(z.string()).optional().describe('Assigned user PHIDs'),
|
|
11
11
|
authorPHIDs: z.array(z.string()).optional().describe('Author PHIDs'),
|
|
12
12
|
statuses: z.array(z.string()).optional().describe('Task statuses: open, resolved, wontfix, invalid, spite, duplicate'),
|
|
13
13
|
priorities: z.array(z.coerce.number()).optional().describe('Priority levels'),
|
|
@@ -15,11 +15,20 @@ export function registerManiphestTools(server, client) {
|
|
|
15
15
|
columnPHIDs: z.array(z.string()).optional().describe('Workboard column PHIDs'),
|
|
16
16
|
projectPHIDs: z.array(z.string()).optional().describe('Project PHIDs (tasks tagged with these projects)'),
|
|
17
17
|
query: z.string().optional().describe('Full-text search query'),
|
|
18
|
+
createdStart: z.coerce.number().optional().describe('Created after (epoch timestamp)'),
|
|
19
|
+
createdEnd: z.coerce.number().optional().describe('Created before (epoch timestamp)'),
|
|
20
|
+
modifiedStart: z.coerce.number().optional().describe('Modified after (epoch timestamp)'),
|
|
21
|
+
modifiedEnd: z.coerce.number().optional().describe('Modified before (epoch timestamp)'),
|
|
22
|
+
parentIDs: z.array(z.coerce.number()).optional().describe('Parent task IDs'),
|
|
23
|
+
subtaskIDs: z.array(z.coerce.number()).optional().describe('Subtask IDs'),
|
|
24
|
+
hasParents: z.boolean().optional().describe('Filter to tasks that have parent tasks'),
|
|
25
|
+
hasSubtasks: z.boolean().optional().describe('Filter to tasks that have subtasks'),
|
|
18
26
|
})).optional().describe('Search constraints'),
|
|
19
27
|
attachments: jsonCoerce(z.object({
|
|
20
28
|
columns: z.boolean().optional().describe('Include workboard column info'),
|
|
21
29
|
projects: z.boolean().optional().describe('Include project info'),
|
|
22
30
|
subscribers: z.boolean().optional().describe('Include subscriber info'),
|
|
31
|
+
'custom-fields': z.boolean().optional().describe('Include custom field values in results'),
|
|
23
32
|
})).optional().describe('Data attachments to include'),
|
|
24
33
|
order: z.string().optional().describe('Result order: "priority", "updated", "newest", "oldest"'),
|
|
25
34
|
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
@@ -37,6 +46,11 @@ export function registerManiphestTools(server, client) {
|
|
|
37
46
|
projectPHIDs: z.array(z.string()).optional().describe('Project PHIDs to tag'),
|
|
38
47
|
subscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs'),
|
|
39
48
|
status: z.string().optional().describe('Initial status'),
|
|
49
|
+
subtype: z.string().optional().describe('Task subtype (e.g. "default", "incident")'),
|
|
50
|
+
parentPHIDs: z.array(z.string()).optional().describe('Parent task PHIDs'),
|
|
51
|
+
subtaskPHIDs: z.array(z.string()).optional().describe('Subtask PHIDs'),
|
|
52
|
+
comment: z.string().optional().describe('Initial comment on the task (supports Remarkup)'),
|
|
53
|
+
customFields: jsonCoerce(z.record(z.string(), z.unknown())).optional().describe('Custom field transactions. Keys are transaction types (e.g. "custom.my-field"), values are the field values. Check your Phabricator Conduit console (conduit/method/maniphest.edit/) for available fields.'),
|
|
40
54
|
}, async (params) => {
|
|
41
55
|
const transactions = [
|
|
42
56
|
{ type: 'title', value: params.title },
|
|
@@ -59,6 +73,23 @@ export function registerManiphestTools(server, client) {
|
|
|
59
73
|
if (params.status !== undefined) {
|
|
60
74
|
transactions.push({ type: 'status', value: params.status });
|
|
61
75
|
}
|
|
76
|
+
if (params.subtype !== undefined) {
|
|
77
|
+
transactions.push({ type: 'subtype', value: params.subtype });
|
|
78
|
+
}
|
|
79
|
+
if (params.parentPHIDs !== undefined) {
|
|
80
|
+
transactions.push({ type: 'parents.set', value: params.parentPHIDs });
|
|
81
|
+
}
|
|
82
|
+
if (params.subtaskPHIDs !== undefined) {
|
|
83
|
+
transactions.push({ type: 'subtasks.set', value: params.subtaskPHIDs });
|
|
84
|
+
}
|
|
85
|
+
if (params.comment !== undefined) {
|
|
86
|
+
transactions.push({ type: 'comment', value: params.comment });
|
|
87
|
+
}
|
|
88
|
+
if (params.customFields !== undefined) {
|
|
89
|
+
for (const [key, value] of Object.entries(params.customFields)) {
|
|
90
|
+
transactions.push({ type: key, value });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
62
93
|
const result = await client.call('maniphest.edit', { transactions });
|
|
63
94
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
64
95
|
});
|
|
@@ -70,11 +101,18 @@ export function registerManiphestTools(server, client) {
|
|
|
70
101
|
ownerPHID: z.string().nullable().optional().describe('New owner PHID (null to unassign)'),
|
|
71
102
|
priority: z.string().optional().describe('New priority'),
|
|
72
103
|
status: z.string().optional().describe('New status: open, resolved, wontfix, invalid, spite, duplicate'),
|
|
104
|
+
subtype: z.string().optional().describe('Task subtype (e.g. "default", "incident")'),
|
|
73
105
|
addProjectPHIDs: z.array(z.string()).optional().describe('Project PHIDs to add'),
|
|
74
106
|
removeProjectPHIDs: z.array(z.string()).optional().describe('Project PHIDs to remove'),
|
|
75
107
|
addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
|
|
76
108
|
removeSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to remove'),
|
|
109
|
+
addParentPHIDs: z.array(z.string()).optional().describe('Parent task PHIDs to add'),
|
|
110
|
+
removeParentPHIDs: z.array(z.string()).optional().describe('Parent task PHIDs to remove'),
|
|
111
|
+
addSubtaskPHIDs: z.array(z.string()).optional().describe('Subtask PHIDs to add'),
|
|
112
|
+
removeSubtaskPHIDs: z.array(z.string()).optional().describe('Subtask PHIDs to remove'),
|
|
77
113
|
columnPHID: z.string().optional().describe('Move to workboard column'),
|
|
114
|
+
comment: z.string().optional().describe('Add a comment alongside the edit (supports Remarkup)'),
|
|
115
|
+
customFields: jsonCoerce(z.record(z.string(), z.unknown())).optional().describe('Custom field transactions. Keys are transaction types (e.g. "custom.my-field"), values are the field values. Check your Phabricator Conduit console (conduit/method/maniphest.edit/) for available fields.'),
|
|
78
116
|
}, async (params) => {
|
|
79
117
|
const transactions = [];
|
|
80
118
|
if (params.title !== undefined) {
|
|
@@ -92,6 +130,9 @@ export function registerManiphestTools(server, client) {
|
|
|
92
130
|
if (params.status !== undefined) {
|
|
93
131
|
transactions.push({ type: 'status', value: params.status });
|
|
94
132
|
}
|
|
133
|
+
if (params.subtype !== undefined) {
|
|
134
|
+
transactions.push({ type: 'subtype', value: params.subtype });
|
|
135
|
+
}
|
|
95
136
|
if (params.addProjectPHIDs !== undefined) {
|
|
96
137
|
transactions.push({ type: 'projects.add', value: params.addProjectPHIDs });
|
|
97
138
|
}
|
|
@@ -104,8 +145,28 @@ export function registerManiphestTools(server, client) {
|
|
|
104
145
|
if (params.removeSubscriberPHIDs !== undefined) {
|
|
105
146
|
transactions.push({ type: 'subscribers.remove', value: params.removeSubscriberPHIDs });
|
|
106
147
|
}
|
|
148
|
+
if (params.addParentPHIDs !== undefined) {
|
|
149
|
+
transactions.push({ type: 'parents.add', value: params.addParentPHIDs });
|
|
150
|
+
}
|
|
151
|
+
if (params.removeParentPHIDs !== undefined) {
|
|
152
|
+
transactions.push({ type: 'parents.remove', value: params.removeParentPHIDs });
|
|
153
|
+
}
|
|
154
|
+
if (params.addSubtaskPHIDs !== undefined) {
|
|
155
|
+
transactions.push({ type: 'subtasks.add', value: params.addSubtaskPHIDs });
|
|
156
|
+
}
|
|
157
|
+
if (params.removeSubtaskPHIDs !== undefined) {
|
|
158
|
+
transactions.push({ type: 'subtasks.remove', value: params.removeSubtaskPHIDs });
|
|
159
|
+
}
|
|
107
160
|
if (params.columnPHID !== undefined) {
|
|
108
|
-
transactions.push({ type: 'column', value: [params.columnPHID] });
|
|
161
|
+
transactions.push({ type: 'column', value: [{ columnPHID: params.columnPHID }] });
|
|
162
|
+
}
|
|
163
|
+
if (params.comment !== undefined) {
|
|
164
|
+
transactions.push({ type: 'comment', value: params.comment });
|
|
165
|
+
}
|
|
166
|
+
if (params.customFields !== undefined) {
|
|
167
|
+
for (const [key, value] of Object.entries(params.customFields)) {
|
|
168
|
+
transactions.push({ type: key, value });
|
|
169
|
+
}
|
|
109
170
|
}
|
|
110
171
|
if (transactions.length === 0) {
|
|
111
172
|
return { content: [{ type: 'text', text: 'No changes specified' }] };
|
|
@@ -127,4 +188,14 @@ export function registerManiphestTools(server, client) {
|
|
|
127
188
|
});
|
|
128
189
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
129
190
|
});
|
|
191
|
+
// Search available task statuses
|
|
192
|
+
server.tool('phabricator_task_status_search', 'List all available task statuses configured on this Phabricator instance', {}, async () => {
|
|
193
|
+
const result = await client.call('maniphest.status.search');
|
|
194
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
195
|
+
});
|
|
196
|
+
// Search available task priorities
|
|
197
|
+
server.tool('phabricator_task_priority_search', 'List all available task priorities configured on this Phabricator instance', {}, async () => {
|
|
198
|
+
const result = await client.call('maniphest.priority.search');
|
|
199
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
200
|
+
});
|
|
130
201
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { jsonCoerce } from './coerce.js';
|
|
3
|
+
export function registerOwnersTools(server, client) {
|
|
4
|
+
// Search code ownership packages
|
|
5
|
+
server.tool('phabricator_owners_search', 'Search Owners packages (code ownership). Find who owns a code path or list ownership packages.', {
|
|
6
|
+
queryKey: z.string().optional().describe('Built-in query: "all", "active"'),
|
|
7
|
+
constraints: jsonCoerce(z.object({
|
|
8
|
+
ids: z.array(z.coerce.number()).optional().describe('Package IDs'),
|
|
9
|
+
phids: z.array(z.string()).optional().describe('Package PHIDs'),
|
|
10
|
+
owners: z.array(z.string()).optional().describe('Owner user or project PHIDs'),
|
|
11
|
+
repositoryPHIDs: z.array(z.string()).optional().describe('Repository PHIDs'),
|
|
12
|
+
paths: z.array(z.array(z.string())).optional().describe('Code paths as [repositoryPHID, path] pairs (e.g. [["PHID-REPO-xxx", "/src/foo.ts"]])'),
|
|
13
|
+
statuses: z.array(z.string()).optional().describe('Package statuses'),
|
|
14
|
+
query: z.string().optional().describe('Full-text search query'),
|
|
15
|
+
})).optional().describe('Search constraints'),
|
|
16
|
+
attachments: jsonCoerce(z.object({
|
|
17
|
+
owners: z.boolean().optional().describe('Include owner details'),
|
|
18
|
+
paths: z.boolean().optional().describe('Include owned paths'),
|
|
19
|
+
})).optional().describe('Data attachments'),
|
|
20
|
+
order: z.string().optional().describe('Result order'),
|
|
21
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
22
|
+
after: z.string().optional().describe('Pagination cursor'),
|
|
23
|
+
}, async (params) => {
|
|
24
|
+
const result = await client.call('owners.search', params);
|
|
25
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
26
|
+
});
|
|
27
|
+
}
|
package/dist/tools/paste.js
CHANGED
|
@@ -15,7 +15,7 @@ export function registerPasteTools(server, client) {
|
|
|
15
15
|
content: z.boolean().optional().describe('Include paste content'),
|
|
16
16
|
})).optional().describe('Data attachments'),
|
|
17
17
|
order: z.string().optional().describe('Result order'),
|
|
18
|
-
limit: z.coerce.number().max(100).optional().describe('Maximum results'),
|
|
18
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
19
19
|
after: z.string().optional().describe('Pagination cursor'),
|
|
20
20
|
}, async (params) => {
|
|
21
21
|
const result = await client.call('paste.search', params);
|
|
@@ -27,6 +27,7 @@ export function registerPasteTools(server, client) {
|
|
|
27
27
|
content: z.string().describe('Paste content'),
|
|
28
28
|
language: z.string().optional().describe('Syntax highlighting language'),
|
|
29
29
|
status: z.string().optional().describe('Status: active or archived'),
|
|
30
|
+
addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
|
|
30
31
|
}, async (params) => {
|
|
31
32
|
const transactions = [
|
|
32
33
|
{ type: 'text', value: params.content },
|
|
@@ -40,7 +41,48 @@ export function registerPasteTools(server, client) {
|
|
|
40
41
|
if (params.status !== undefined) {
|
|
41
42
|
transactions.push({ type: 'status', value: params.status });
|
|
42
43
|
}
|
|
44
|
+
if (params.addSubscriberPHIDs !== undefined) {
|
|
45
|
+
transactions.push({ type: 'subscribers.add', value: params.addSubscriberPHIDs });
|
|
46
|
+
}
|
|
43
47
|
const result = await client.call('paste.edit', { transactions });
|
|
44
48
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
45
49
|
});
|
|
50
|
+
// Edit paste
|
|
51
|
+
server.tool('phabricator_paste_edit', 'Edit an existing Phabricator paste', {
|
|
52
|
+
objectIdentifier: z.string().describe('Paste PHID or ID (e.g., "P123")'),
|
|
53
|
+
title: z.string().optional().describe('New title'),
|
|
54
|
+
content: z.string().optional().describe('New content'),
|
|
55
|
+
language: z.string().optional().describe('Syntax highlighting language'),
|
|
56
|
+
status: z.string().optional().describe('Status: active or archived'),
|
|
57
|
+
addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
|
|
58
|
+
removeSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to remove'),
|
|
59
|
+
}, async (params) => {
|
|
60
|
+
const transactions = [];
|
|
61
|
+
if (params.title !== undefined) {
|
|
62
|
+
transactions.push({ type: 'title', value: params.title });
|
|
63
|
+
}
|
|
64
|
+
if (params.content !== undefined) {
|
|
65
|
+
transactions.push({ type: 'text', value: params.content });
|
|
66
|
+
}
|
|
67
|
+
if (params.language !== undefined) {
|
|
68
|
+
transactions.push({ type: 'language', value: params.language });
|
|
69
|
+
}
|
|
70
|
+
if (params.status !== undefined) {
|
|
71
|
+
transactions.push({ type: 'status', value: params.status });
|
|
72
|
+
}
|
|
73
|
+
if (params.addSubscriberPHIDs !== undefined) {
|
|
74
|
+
transactions.push({ type: 'subscribers.add', value: params.addSubscriberPHIDs });
|
|
75
|
+
}
|
|
76
|
+
if (params.removeSubscriberPHIDs !== undefined) {
|
|
77
|
+
transactions.push({ type: 'subscribers.remove', value: params.removeSubscriberPHIDs });
|
|
78
|
+
}
|
|
79
|
+
if (transactions.length === 0) {
|
|
80
|
+
return { content: [{ type: 'text', text: 'No changes specified' }] };
|
|
81
|
+
}
|
|
82
|
+
const result = await client.call('paste.edit', {
|
|
83
|
+
objectIdentifier: params.objectIdentifier,
|
|
84
|
+
transactions,
|
|
85
|
+
});
|
|
86
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
87
|
+
});
|
|
46
88
|
}
|
package/dist/tools/phame.js
CHANGED
|
@@ -9,6 +9,9 @@ export function registerPhameTools(server, client) {
|
|
|
9
9
|
phids: z.array(z.string()).optional().describe('Blog PHIDs'),
|
|
10
10
|
query: z.string().optional().describe('Full-text search query'),
|
|
11
11
|
})).optional().describe('Search constraints'),
|
|
12
|
+
attachments: jsonCoerce(z.object({
|
|
13
|
+
subscribers: z.boolean().optional().describe('Include subscriber details'),
|
|
14
|
+
})).optional().describe('Data attachments'),
|
|
12
15
|
order: z.string().optional().describe('Result order'),
|
|
13
16
|
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
14
17
|
after: z.string().optional().describe('Cursor for pagination'),
|
|
@@ -23,9 +26,12 @@ export function registerPhameTools(server, client) {
|
|
|
23
26
|
ids: z.array(z.coerce.number()).optional().describe('Post IDs'),
|
|
24
27
|
phids: z.array(z.string()).optional().describe('Post PHIDs'),
|
|
25
28
|
blogPHIDs: z.array(z.string()).optional().describe('Filter by blog PHIDs'),
|
|
26
|
-
visibility: z.array(z.
|
|
29
|
+
visibility: z.array(z.coerce.number()).optional().describe('Visibility: 1 (published), 0 (draft), 2 (archived). Note: use these numeric codes in search; use string names like "published" in create/edit.'),
|
|
27
30
|
query: z.string().optional().describe('Full-text search query'),
|
|
28
31
|
})).optional().describe('Search constraints'),
|
|
32
|
+
attachments: jsonCoerce(z.object({
|
|
33
|
+
content: z.boolean().optional().describe('Include blog post body content'),
|
|
34
|
+
})).optional().describe('Data attachments'),
|
|
29
35
|
order: z.string().optional().describe('Result order'),
|
|
30
36
|
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
31
37
|
after: z.string().optional().describe('Cursor for pagination'),
|
|
@@ -40,6 +46,7 @@ export function registerPhameTools(server, client) {
|
|
|
40
46
|
blogPHID: z.string().describe('PHID of the blog to post to'),
|
|
41
47
|
subtitle: z.string().optional().describe('Post subtitle'),
|
|
42
48
|
visibility: z.string().optional().describe('Visibility: "published", "draft", "archived" (default: draft)'),
|
|
49
|
+
addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
|
|
43
50
|
}, async (params) => {
|
|
44
51
|
const transactions = [
|
|
45
52
|
{ type: 'title', value: params.title },
|
|
@@ -52,6 +59,9 @@ export function registerPhameTools(server, client) {
|
|
|
52
59
|
if (params.visibility !== undefined) {
|
|
53
60
|
transactions.push({ type: 'visibility', value: params.visibility });
|
|
54
61
|
}
|
|
62
|
+
if (params.addSubscriberPHIDs !== undefined) {
|
|
63
|
+
transactions.push({ type: 'subscribers.add', value: params.addSubscriberPHIDs });
|
|
64
|
+
}
|
|
55
65
|
const result = await client.call('phame.post.edit', { transactions });
|
|
56
66
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
57
67
|
});
|
|
@@ -63,6 +73,8 @@ export function registerPhameTools(server, client) {
|
|
|
63
73
|
body: z.string().optional().describe('New post body content (supports Remarkup)'),
|
|
64
74
|
visibility: z.string().optional().describe('Visibility: "published", "draft", "archived"'),
|
|
65
75
|
blogPHID: z.string().optional().describe('Move post to a different blog (PHID)'),
|
|
76
|
+
addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
|
|
77
|
+
removeSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to remove'),
|
|
66
78
|
}, async (params) => {
|
|
67
79
|
const transactions = [];
|
|
68
80
|
if (params.title !== undefined) {
|
|
@@ -80,6 +92,12 @@ export function registerPhameTools(server, client) {
|
|
|
80
92
|
if (params.blogPHID !== undefined) {
|
|
81
93
|
transactions.push({ type: 'blog', value: params.blogPHID });
|
|
82
94
|
}
|
|
95
|
+
if (params.addSubscriberPHIDs !== undefined) {
|
|
96
|
+
transactions.push({ type: 'subscribers.add', value: params.addSubscriberPHIDs });
|
|
97
|
+
}
|
|
98
|
+
if (params.removeSubscriberPHIDs !== undefined) {
|
|
99
|
+
transactions.push({ type: 'subscribers.remove', value: params.removeSubscriberPHIDs });
|
|
100
|
+
}
|
|
83
101
|
if (transactions.length === 0) {
|
|
84
102
|
return { content: [{ type: 'text', text: 'No changes specified' }] };
|
|
85
103
|
}
|
package/dist/tools/phriction.js
CHANGED
|
@@ -16,17 +16,19 @@ export function registerPhrictionTools(server, client) {
|
|
|
16
16
|
content: z.boolean().optional().describe('Include document content'),
|
|
17
17
|
})).optional().describe('Data attachments'),
|
|
18
18
|
order: z.string().optional().describe('Result order'),
|
|
19
|
-
limit: z.coerce.number().max(100).optional().describe('Maximum results'),
|
|
19
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
20
20
|
after: z.string().optional().describe('Pagination cursor'),
|
|
21
21
|
}, async (params) => {
|
|
22
22
|
const result = await client.call('phriction.document.search', params);
|
|
23
23
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
24
24
|
});
|
|
25
25
|
// Edit wiki document
|
|
26
|
-
server.tool('phabricator_document_edit', '
|
|
27
|
-
|
|
26
|
+
server.tool('phabricator_document_edit', 'Create or edit a Phriction wiki document. To create, provide a new slug with title and content.', {
|
|
27
|
+
objectIdentifier: z.string().describe('Document slug, PHID, or ID (e.g., "projects/myproject/")'),
|
|
28
28
|
title: z.string().optional().describe('Document title'),
|
|
29
29
|
content: z.string().optional().describe('Document content (Remarkup)'),
|
|
30
|
+
addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
|
|
31
|
+
removeSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to remove'),
|
|
30
32
|
}, async (params) => {
|
|
31
33
|
const transactions = [];
|
|
32
34
|
if (params.title !== undefined) {
|
|
@@ -35,13 +37,30 @@ export function registerPhrictionTools(server, client) {
|
|
|
35
37
|
if (params.content !== undefined) {
|
|
36
38
|
transactions.push({ type: 'content', value: params.content });
|
|
37
39
|
}
|
|
40
|
+
if (params.addSubscriberPHIDs !== undefined) {
|
|
41
|
+
transactions.push({ type: 'subscribers.add', value: params.addSubscriberPHIDs });
|
|
42
|
+
}
|
|
43
|
+
if (params.removeSubscriberPHIDs !== undefined) {
|
|
44
|
+
transactions.push({ type: 'subscribers.remove', value: params.removeSubscriberPHIDs });
|
|
45
|
+
}
|
|
38
46
|
if (transactions.length === 0) {
|
|
39
47
|
return { content: [{ type: 'text', text: 'No changes specified' }] };
|
|
40
48
|
}
|
|
41
49
|
const result = await client.call('phriction.document.edit', {
|
|
42
|
-
objectIdentifier: params.
|
|
50
|
+
objectIdentifier: params.objectIdentifier,
|
|
43
51
|
transactions,
|
|
44
52
|
});
|
|
45
53
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
46
54
|
});
|
|
55
|
+
// Add comment to document
|
|
56
|
+
server.tool('phabricator_document_add_comment', 'Add a comment to a Phriction wiki document', {
|
|
57
|
+
objectIdentifier: z.string().describe('Document slug, PHID, or ID (e.g., "projects/myproject/")'),
|
|
58
|
+
comment: z.string().describe('Comment text (supports Remarkup)'),
|
|
59
|
+
}, async (params) => {
|
|
60
|
+
const result = await client.call('phriction.document.edit', {
|
|
61
|
+
objectIdentifier: params.objectIdentifier,
|
|
62
|
+
transactions: [{ type: 'comment', value: params.comment }],
|
|
63
|
+
});
|
|
64
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
65
|
+
});
|
|
47
66
|
}
|
package/dist/tools/project.js
CHANGED
|
@@ -22,26 +22,33 @@ export function registerProjectTools(server, client) {
|
|
|
22
22
|
ancestors: z.boolean().optional().describe('Include ancestors'),
|
|
23
23
|
})).optional().describe('Data attachments'),
|
|
24
24
|
order: z.string().optional().describe('Result order'),
|
|
25
|
-
limit: z.coerce.number().max(100).optional().describe('Maximum results'),
|
|
25
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
26
26
|
after: z.string().optional().describe('Pagination cursor'),
|
|
27
27
|
}, async (params) => {
|
|
28
28
|
const result = await client.call('project.search', params);
|
|
29
29
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
30
30
|
});
|
|
31
31
|
// Edit project
|
|
32
|
-
server.tool('phabricator_project_edit', '
|
|
33
|
-
objectIdentifier: z.string().describe('Project PHID or ID'),
|
|
32
|
+
server.tool('phabricator_project_edit', 'Create or edit a Phabricator project. Omit objectIdentifier to create a new project (name is required for creation).', {
|
|
33
|
+
objectIdentifier: z.string().optional().describe('Project PHID or ID. Omit to create a new project.'),
|
|
34
34
|
name: z.string().optional().describe('New name'),
|
|
35
35
|
description: z.string().optional().describe('New description'),
|
|
36
36
|
icon: z.string().optional().describe('New icon'),
|
|
37
37
|
color: z.string().optional().describe('New color'),
|
|
38
38
|
addMemberPHIDs: z.array(z.string()).optional().describe('Add members'),
|
|
39
39
|
removeMemberPHIDs: z.array(z.string()).optional().describe('Remove members'),
|
|
40
|
+
addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
|
|
41
|
+
removeSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to remove'),
|
|
42
|
+
slug: z.string().optional().describe('Project URL slug (replaces ALL existing slugs with this one)'),
|
|
43
|
+
comment: z.string().optional().describe('Add a comment alongside the edit (supports Remarkup)'),
|
|
40
44
|
}, async (params) => {
|
|
41
45
|
const transactions = [];
|
|
42
46
|
if (params.name !== undefined) {
|
|
43
47
|
transactions.push({ type: 'name', value: params.name });
|
|
44
48
|
}
|
|
49
|
+
if (params.slug !== undefined) {
|
|
50
|
+
transactions.push({ type: 'slugs', value: [params.slug] });
|
|
51
|
+
}
|
|
45
52
|
if (params.description !== undefined) {
|
|
46
53
|
transactions.push({ type: 'description', value: params.description });
|
|
47
54
|
}
|
|
@@ -57,24 +64,38 @@ export function registerProjectTools(server, client) {
|
|
|
57
64
|
if (params.removeMemberPHIDs !== undefined) {
|
|
58
65
|
transactions.push({ type: 'members.remove', value: params.removeMemberPHIDs });
|
|
59
66
|
}
|
|
67
|
+
if (params.addSubscriberPHIDs !== undefined) {
|
|
68
|
+
transactions.push({ type: 'subscribers.add', value: params.addSubscriberPHIDs });
|
|
69
|
+
}
|
|
70
|
+
if (params.removeSubscriberPHIDs !== undefined) {
|
|
71
|
+
transactions.push({ type: 'subscribers.remove', value: params.removeSubscriberPHIDs });
|
|
72
|
+
}
|
|
73
|
+
if (params.comment !== undefined) {
|
|
74
|
+
transactions.push({ type: 'comment', value: params.comment });
|
|
75
|
+
}
|
|
60
76
|
if (transactions.length === 0) {
|
|
61
77
|
return { content: [{ type: 'text', text: 'No changes specified' }] };
|
|
62
78
|
}
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
79
|
+
const apiParams = { transactions };
|
|
80
|
+
if (params.objectIdentifier !== undefined) {
|
|
81
|
+
apiParams.objectIdentifier = params.objectIdentifier;
|
|
82
|
+
}
|
|
83
|
+
const result = await client.call('project.edit', apiParams);
|
|
67
84
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
68
85
|
});
|
|
69
86
|
// Search workboard columns
|
|
70
87
|
server.tool('phabricator_column_search', 'Search project workboard columns', {
|
|
88
|
+
queryKey: z.string().optional().describe('Built-in query: "all"'),
|
|
71
89
|
constraints: jsonCoerce(z.object({
|
|
72
90
|
ids: z.array(z.coerce.number()).optional().describe('Column IDs'),
|
|
73
91
|
phids: z.array(z.string()).optional().describe('Column PHIDs'),
|
|
74
92
|
projects: z.array(z.string()).optional().describe('Project PHIDs'),
|
|
75
93
|
})).optional().describe('Search constraints'),
|
|
94
|
+
attachments: jsonCoerce(z.object({
|
|
95
|
+
items: z.boolean().optional().describe('Include items (tasks) in each column'),
|
|
96
|
+
})).optional().describe('Data attachments'),
|
|
76
97
|
order: z.string().optional().describe('Result order'),
|
|
77
|
-
limit: z.coerce.number().max(100).optional().describe('Maximum results'),
|
|
98
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
78
99
|
after: z.string().optional().describe('Pagination cursor'),
|
|
79
100
|
}, async (params) => {
|
|
80
101
|
const result = await client.call('project.column.search', params);
|
package/dist/tools/user.js
CHANGED
|
@@ -24,7 +24,7 @@ export function registerUserTools(server, client) {
|
|
|
24
24
|
availability: z.boolean().optional().describe('Include availability info'),
|
|
25
25
|
})).optional().describe('Data attachments'),
|
|
26
26
|
order: z.string().optional().describe('Result order'),
|
|
27
|
-
limit: z.coerce.number().max(100).optional().describe('Maximum results'),
|
|
27
|
+
limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
|
|
28
28
|
after: z.string().optional().describe('Pagination cursor'),
|
|
29
29
|
}, async (params) => {
|
|
30
30
|
const result = await client.call('user.search', params);
|
package/package.json
CHANGED