@freelancercom/phabricator-mcp 2.0.8 → 2.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -290,6 +290,7 @@ To allowlist all tools including write operations, use `"mcp__phabricator__*"` i
290
290
  | Tool | Description |
291
291
  |------|-------------|
292
292
  | `phabricator_blog_search` | Search Phame blogs |
293
+ | `phabricator_blog_edit` | Create or edit a Phame blog |
293
294
  | `phabricator_blog_post_search` | Search blog posts |
294
295
  | `phabricator_blog_post_create` | Create a new blog post |
295
296
  | `phabricator_blog_post_edit` | Edit an existing blog post |
@@ -8,7 +8,7 @@ export function registerConpherenceTools(server, client) {
8
8
  ids: z.array(z.coerce.number()).optional().describe('Room IDs'),
9
9
  phids: z.array(z.string()).optional().describe('Room PHIDs'),
10
10
  participants: z.array(z.string()).optional().describe('Participant user PHIDs'),
11
- query: z.string().optional().describe('Full-text search query'),
11
+ fulltext: z.string().optional().describe('Search for rooms containing these words'),
12
12
  })).optional().describe('Search constraints'),
13
13
  order: z.string().optional().describe('Result order'),
14
14
  limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
@@ -10,7 +10,7 @@ export function registerDifferentialTools(server, client) {
10
10
  authorPHIDs: z.array(z.string()).optional().describe('Author PHIDs'),
11
11
  reviewerPHIDs: z.array(z.string()).optional().describe('Reviewer PHIDs'),
12
12
  repositoryPHIDs: z.array(z.string()).optional().describe('Repository PHIDs'),
13
- statuses: z.array(z.string()).optional().describe('Statuses: needs-review, needs-revision, accepted, published, abandoned, changes-planned'),
13
+ statuses: z.array(z.string()).optional().describe('Statuses: needs-review, needs-revision, accepted, published, abandoned, changes-planned, draft'),
14
14
  responsiblePHIDs: z.array(z.string()).optional().describe('User PHIDs who are responsible (as author or reviewer)'),
15
15
  affectedPaths: z.array(z.string()).optional().describe('File paths affected by the revision'),
16
16
  createdStart: z.coerce.number().optional().describe('Created after (epoch timestamp)'),
@@ -162,6 +162,8 @@ export function registerDiffusionTools(server, client) {
162
162
  status: z.enum(['active', 'inactive']).optional().describe('Repository status'),
163
163
  addProjectPHIDs: z.array(z.string()).optional().describe('Project PHIDs to add'),
164
164
  removeProjectPHIDs: z.array(z.string()).optional().describe('Project PHIDs to remove'),
165
+ addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
166
+ removeSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to remove'),
165
167
  space: z.string().optional().describe('Space PHID (for multi-space installations)'),
166
168
  }, async (params) => {
167
169
  const transactions = [];
@@ -192,6 +194,12 @@ export function registerDiffusionTools(server, client) {
192
194
  if (params.removeProjectPHIDs !== undefined) {
193
195
  transactions.push({ type: 'projects.remove', value: params.removeProjectPHIDs });
194
196
  }
197
+ if (params.addSubscriberPHIDs !== undefined) {
198
+ transactions.push({ type: 'subscribers.add', value: params.addSubscriberPHIDs });
199
+ }
200
+ if (params.removeSubscriberPHIDs !== undefined) {
201
+ transactions.push({ type: 'subscribers.remove', value: params.removeSubscriberPHIDs });
202
+ }
195
203
  if (params.space !== undefined) {
196
204
  transactions.push({ type: 'space', value: params.space });
197
205
  }
@@ -5,11 +5,20 @@ export function registerFileTools(server, client) {
5
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
6
  name: z.string().describe('Filename with extension (e.g. "screenshot.png")'),
7
7
  data_base64: z.string().describe('Base64-encoded file content'),
8
+ viewPolicy: z.string().optional().describe('File visibility policy (e.g., "public", "users", or a custom policy PHID)'),
9
+ canCDN: z.boolean().optional().describe('Whether the file can be served over CDN (for public assets)'),
8
10
  }, async (params) => {
9
- const phid = await client.call('file.upload', {
11
+ const apiParams = {
10
12
  name: params.name,
11
13
  data_base64: params.data_base64,
12
- });
14
+ };
15
+ if (params.viewPolicy !== undefined) {
16
+ apiParams.viewPolicy = params.viewPolicy;
17
+ }
18
+ if (params.canCDN !== undefined) {
19
+ apiParams.canCDN = params.canCDN;
20
+ }
21
+ const phid = await client.call('file.upload', apiParams);
13
22
  return { content: [{ type: 'text', text: phid }] };
14
23
  });
15
24
  // Search files
@@ -10,6 +10,7 @@ export function registerHarbormasterTools(server, client) {
10
10
  objectPHIDs: z.array(z.string()).optional().describe('Object PHIDs (revision or commit PHIDs)'),
11
11
  containerPHIDs: z.array(z.string()).optional().describe('Container PHIDs'),
12
12
  statuses: z.array(z.string()).optional().describe('Buildable statuses'),
13
+ manual: z.boolean().optional().describe('Filter to manual buildables only (true) or automated only (false)'),
13
14
  })).optional().describe('Search constraints'),
14
15
  order: z.string().optional().describe('Result order'),
15
16
  limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
@@ -26,7 +27,7 @@ export function registerHarbormasterTools(server, client) {
26
27
  ids: z.array(z.coerce.number()).optional().describe('Build IDs'),
27
28
  phids: z.array(z.string()).optional().describe('Build PHIDs'),
28
29
  buildables: z.array(z.string()).optional().describe('Buildable PHIDs'),
29
- plans: z.array(z.string()).optional().describe('Build plan PHIDs'),
30
+ plans: z.array(z.string()).optional().describe('Build plan PHIDs (use phabricator_build_plan_search to find these)'),
30
31
  statuses: z.array(z.string()).optional().describe('Build statuses: building, passed, failed, aborted, error, paused'),
31
32
  initiators: z.array(z.string()).optional().describe('PHIDs of users/objects that initiated the build'),
32
33
  })).optional().describe('Search constraints'),
@@ -46,10 +47,12 @@ export function registerHarbormasterTools(server, client) {
46
47
  phids: z.array(z.string()).optional().describe('Target PHIDs'),
47
48
  buildPHIDs: z.array(z.string()).optional().describe('Build PHIDs'),
48
49
  statuses: z.array(z.string()).optional().describe('Target statuses'),
49
- dateCreatedStart: z.coerce.number().optional().describe('Created after (epoch timestamp)'),
50
- dateCreatedEnd: z.coerce.number().optional().describe('Created before (epoch timestamp)'),
51
- dateModifiedStart: z.coerce.number().optional().describe('Modified after (epoch timestamp)'),
52
- dateModifiedEnd: z.coerce.number().optional().describe('Modified before (epoch timestamp)'),
50
+ createdStart: z.coerce.number().optional().describe('Created after (epoch timestamp)'),
51
+ createdEnd: z.coerce.number().optional().describe('Created before (epoch timestamp)'),
52
+ startedStart: z.coerce.number().optional().describe('Started executing after (epoch timestamp)'),
53
+ startedEnd: z.coerce.number().optional().describe('Started executing before (epoch timestamp)'),
54
+ completedStart: z.coerce.number().optional().describe('Completed after (epoch timestamp)'),
55
+ completedEnd: z.coerce.number().optional().describe('Completed before (epoch timestamp)'),
53
56
  })).optional().describe('Search constraints'),
54
57
  order: z.string().optional().describe('Result order'),
55
58
  limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
@@ -21,8 +21,8 @@ export function registerManiphestTools(server, client) {
21
21
  modifiedEnd: z.coerce.number().optional().describe('Modified before (epoch timestamp)'),
22
22
  parentIDs: z.array(z.coerce.number()).optional().describe('Parent task IDs'),
23
23
  subtaskIDs: z.array(z.coerce.number()).optional().describe('Subtask IDs'),
24
- hasParents: z.boolean().optional().describe('Filter to tasks that have open parent tasks'),
25
- hasSubtasks: z.boolean().optional().describe('Filter to tasks that have open subtasks'),
24
+ hasParents: z.boolean().optional().describe('true: only tasks with open parent tasks. false: only tasks without. Omit to show all.'),
25
+ hasSubtasks: z.boolean().optional().describe('true: only tasks with open subtasks. false: only tasks without. Omit to show all.'),
26
26
  spaces: z.array(z.string()).optional().describe('Filter by Space PHIDs (for multi-space installations)'),
27
27
  closedStart: z.coerce.number().optional().describe('Closed after (epoch timestamp)'),
28
28
  closedEnd: z.coerce.number().optional().describe('Closed before (epoch timestamp)'),
@@ -24,6 +24,61 @@ export function registerPhameTools(server, client) {
24
24
  const result = await client.call('phame.blog.search', params);
25
25
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
26
26
  });
27
+ // Edit a blog
28
+ server.tool('phabricator_blog_edit', 'Create or edit a Phame blog. Omit objectIdentifier to create a new blog (name is required for creation).', {
29
+ objectIdentifier: z.string().optional().describe('Blog PHID or ID. Omit to create a new blog.'),
30
+ name: z.string().optional().describe('Blog name'),
31
+ subtitle: z.string().optional().describe('Blog subtitle'),
32
+ description: z.string().optional().describe('Blog description (Remarkup)'),
33
+ fullDomain: z.string().optional().describe('Custom full domain for the blog'),
34
+ parentSite: z.string().optional().describe('Parent site name'),
35
+ parentDomain: z.string().optional().describe('Parent domain URL'),
36
+ status: z.string().optional().describe('Blog status'),
37
+ addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
38
+ removeSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to remove'),
39
+ comment: z.string().optional().describe('Add a comment alongside the edit (supports Remarkup)'),
40
+ }, async (params) => {
41
+ const transactions = [];
42
+ if (params.name !== undefined) {
43
+ transactions.push({ type: 'name', value: params.name });
44
+ }
45
+ if (params.subtitle !== undefined) {
46
+ transactions.push({ type: 'subtitle', value: params.subtitle });
47
+ }
48
+ if (params.description !== undefined) {
49
+ transactions.push({ type: 'description', value: params.description });
50
+ }
51
+ if (params.fullDomain !== undefined) {
52
+ transactions.push({ type: 'fullDomain', value: params.fullDomain });
53
+ }
54
+ if (params.parentSite !== undefined) {
55
+ transactions.push({ type: 'parentSite', value: params.parentSite });
56
+ }
57
+ if (params.parentDomain !== undefined) {
58
+ transactions.push({ type: 'parentDomain', value: params.parentDomain });
59
+ }
60
+ if (params.status !== undefined) {
61
+ transactions.push({ type: 'status', value: params.status });
62
+ }
63
+ if (params.addSubscriberPHIDs !== undefined) {
64
+ transactions.push({ type: 'subscribers.add', value: params.addSubscriberPHIDs });
65
+ }
66
+ if (params.removeSubscriberPHIDs !== undefined) {
67
+ transactions.push({ type: 'subscribers.remove', value: params.removeSubscriberPHIDs });
68
+ }
69
+ if (params.comment !== undefined) {
70
+ transactions.push({ type: 'comment', value: params.comment });
71
+ }
72
+ if (transactions.length === 0) {
73
+ return { content: [{ type: 'text', text: 'No changes specified' }] };
74
+ }
75
+ const apiParams = { transactions };
76
+ if (params.objectIdentifier !== undefined) {
77
+ apiParams.objectIdentifier = params.objectIdentifier;
78
+ }
79
+ const result = await client.call('phame.blog.edit', apiParams);
80
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
81
+ });
27
82
  // Search blog posts
28
83
  server.tool('phabricator_blog_post_search', 'Search Phame blog posts', {
29
84
  queryKey: z.string().optional().describe('Built-in query: "all", "live"'),
@@ -31,7 +86,7 @@ export function registerPhameTools(server, client) {
31
86
  ids: z.array(z.coerce.number()).optional().describe('Post IDs'),
32
87
  phids: z.array(z.string()).optional().describe('Post PHIDs'),
33
88
  blogPHIDs: z.array(z.string()).optional().describe('Filter by blog PHIDs'),
34
- visibility: z.array(z.string()).optional().describe('Visibility: "published", "draft", "archived" (or numeric: "1", "0", "2")'),
89
+ visibility: z.array(z.coerce.number()).optional().describe('Visibility values: 0 (draft), 1 (published), 2 (archived)'),
35
90
  subscribers: z.array(z.string()).optional().describe('Subscriber user/project PHIDs'),
36
91
  projects: z.array(z.string()).optional().describe('Project PHIDs'),
37
92
  query: z.string().optional().describe('Full-text search query'),
@@ -54,7 +109,7 @@ export function registerPhameTools(server, client) {
54
109
  body: z.string().describe('Post body content (supports Remarkup)'),
55
110
  blogPHID: z.string().describe('PHID of the blog to post to'),
56
111
  subtitle: z.string().optional().describe('Post subtitle'),
57
- visibility: z.string().optional().describe('Visibility: "published", "draft", "archived" (default: draft)'),
112
+ visibility: z.coerce.number().optional().describe('Visibility: 0 (draft, default), 1 (published), 2 (archived)'),
58
113
  addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
59
114
  }, async (params) => {
60
115
  const transactions = [
@@ -80,7 +135,7 @@ export function registerPhameTools(server, client) {
80
135
  title: z.string().optional().describe('New post title'),
81
136
  subtitle: z.string().optional().describe('New post subtitle'),
82
137
  body: z.string().optional().describe('New post body content (supports Remarkup)'),
83
- visibility: z.string().optional().describe('Visibility: "published", "draft", "archived"'),
138
+ visibility: z.coerce.number().optional().describe('Visibility: 0 (draft), 1 (published), 2 (archived)'),
84
139
  blogPHID: z.string().optional().describe('Move post to a different blog (PHID)'),
85
140
  addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
86
141
  removeSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to remove'),
@@ -8,7 +8,7 @@ export function registerPhidTools(server, client) {
8
8
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
9
9
  });
10
10
  // Query PHID details
11
- server.tool('phabricator_phid_query', 'Get detailed information about PHIDs', {
11
+ server.tool('phabricator_phid_query', 'Get handle information (name, URI, type, status) for PHIDs. For full object data, use application-specific search tools (e.g., phabricator_task_search, phabricator_revision_search).', {
12
12
  phids: z.array(z.string()).describe('PHIDs to query'),
13
13
  }, async (params) => {
14
14
  const result = await client.call('phid.query', { phids: params.phids });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freelancercom/phabricator-mcp",
3
- "version": "2.0.8",
3
+ "version": "2.0.10",
4
4
  "description": "MCP server for Phabricator Conduit API - manage tasks, code reviews, repositories, and more",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",