@freelancercom/phabricator-mcp 2.0.3 → 2.0.5

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
@@ -175,7 +175,7 @@ Add to your `~/.claude/settings.json`:
175
175
  "mcp__phabricator__phabricator_revision_search",
176
176
  "mcp__phabricator__phabricator_diff_search",
177
177
  "mcp__phabricator__phabricator_diff_raw",
178
- "mcp__phabricator__phabricator_changeset_search",
178
+ "mcp__phabricator__phabricator_revision_paths",
179
179
  "mcp__phabricator__phabricator_repository_search",
180
180
  "mcp__phabricator__phabricator_commit_search",
181
181
  "mcp__phabricator__phabricator_repository_browse",
@@ -237,7 +237,7 @@ To allowlist all tools including write operations, use `"mcp__phabricator__*"` i
237
237
  | `phabricator_revision_inline_comment` | Create an inline comment on a specific line of a diff |
238
238
  | `phabricator_diff_raw` | Get the raw diff/patch content for a diff by ID |
239
239
  | `phabricator_diff_search` | Search diffs (code change snapshots within a revision) |
240
- | `phabricator_changeset_search` | Search changesets (individual changed files within a diff) |
240
+ | `phabricator_revision_paths` | Get the list of changed file paths for a revision |
241
241
 
242
242
  ### Repositories (Diffusion)
243
243
 
@@ -251,6 +251,7 @@ To allowlist all tools including write operations, use `"mcp__phabricator__*"` i
251
251
  | `phabricator_tag_search` | List tags in a repository |
252
252
  | `phabricator_repository_file_history` | Get commit history for a file path |
253
253
  | `phabricator_repository_code_search` | Search (grep) file contents within a repository |
254
+ | `phabricator_repository_edit` | Create or edit a Diffusion repository |
254
255
 
255
256
  ### Users
256
257
 
@@ -4,7 +4,7 @@ export function registerAuditTools(server, client) {
4
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
5
  auditorPHIDs: z.array(z.string()).optional().describe('Auditor user/project PHIDs'),
6
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"'),
7
+ status: z.string().optional().describe('Audit status filter: "audit-status-any" (default), "audit-status-open", "audit-status-concern", "audit-status-accepted", "audit-status-partial"'),
8
8
  limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
9
9
  offset: z.coerce.number().optional().describe('Result offset for pagination'),
10
10
  }, async (params) => {
@@ -41,7 +41,7 @@ export function registerConpherenceTools(server, client) {
41
41
  participantPHIDs: z.array(z.string()).optional().describe('Participant user PHIDs to add'),
42
42
  }, async (params) => {
43
43
  const transactions = [
44
- { type: 'title', value: params.title },
44
+ { type: 'name', value: params.title },
45
45
  ];
46
46
  if (params.message !== undefined) {
47
47
  transactions.push({ type: 'comment', value: params.message });
@@ -56,13 +56,17 @@ export function registerConpherenceTools(server, client) {
56
56
  server.tool('phabricator_conpherence_edit', 'Edit a Conpherence chat room/thread. Rename it or manage participants.', {
57
57
  objectIdentifier: z.string().describe('Room ID or PHID'),
58
58
  title: z.string().optional().describe('New room title'),
59
+ topic: z.string().optional().describe('Room topic/description'),
59
60
  addParticipantPHIDs: z.array(z.string()).optional().describe('Participant PHIDs to add'),
60
61
  removeParticipantPHIDs: z.array(z.string()).optional().describe('Participant PHIDs to remove'),
61
62
  comment: z.string().optional().describe('Send a message alongside the edit (supports Remarkup)'),
62
63
  }, async (params) => {
63
64
  const transactions = [];
64
65
  if (params.title !== undefined) {
65
- transactions.push({ type: 'title', value: params.title });
66
+ transactions.push({ type: 'name', value: params.title });
67
+ }
68
+ if (params.topic !== undefined) {
69
+ transactions.push({ type: 'topic', value: params.topic });
66
70
  }
67
71
  if (params.addParticipantPHIDs !== undefined) {
68
72
  transactions.push({ type: 'participants.add', value: params.addParticipantPHIDs });
@@ -45,7 +45,7 @@ export function registerDifferentialTools(server, client) {
45
45
  addProjectPHIDs: z.array(z.string()).optional().describe('Add projects'),
46
46
  removeProjectPHIDs: z.array(z.string()).optional().describe('Remove projects'),
47
47
  comment: z.string().optional().describe('Add a comment'),
48
- action: z.enum(['accept', 'reject', 'abandon', 'reclaim', 'request-review', 'resign', 'commandeer', 'plan-changes', 'close']).optional().describe('Revision action to take'),
48
+ action: z.enum(['accept', 'reject', 'abandon', 'reclaim', 'reopen', 'request-review', 'resign', 'commandeer', 'plan-changes', 'close', 'draft']).optional().describe('Revision action to take'),
49
49
  addSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to add'),
50
50
  removeSubscriberPHIDs: z.array(z.string()).optional().describe('Subscriber PHIDs to remove'),
51
51
  repositoryPHID: z.string().optional().describe('Repository PHID to associate with the revision'),
@@ -127,21 +127,13 @@ export function registerDifferentialTools(server, client) {
127
127
  const result = await client.call('differential.diff.search', params);
128
128
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
129
129
  });
130
- // Search changesets (changed files within a diff)
131
- server.tool('phabricator_changeset_search', 'Search changesets (individual changed files) within a Differential diff. Use phabricator_diff_search to find the diff PHID first.', {
132
- queryKey: z.string().optional().describe('Built-in query: "all"'),
133
- constraints: jsonCoerce(z.object({
134
- diffPHIDs: z.array(z.string()).optional().describe('Diff PHIDs to list changesets for'),
135
- })).optional().describe('Search constraints'),
136
- attachments: jsonCoerce(z.object({
137
- hunks: z.boolean().optional().describe('Include diff hunks (actual changed content)'),
138
- })).optional().describe('Data attachments'),
139
- order: z.string().optional().describe('Result order'),
140
- limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
141
- after: z.string().optional().describe('Cursor for next-page pagination'),
142
- before: z.string().optional().describe('Cursor for previous-page pagination'),
130
+ // Get changed file paths for a revision
131
+ server.tool('phabricator_revision_paths', 'Get the list of changed file paths for a Differential revision. Returns an array of file path strings.', {
132
+ revision_id: z.coerce.number().describe('Numeric revision ID (e.g., 123). Do not include the "D" prefix.'),
143
133
  }, async (params) => {
144
- const result = await client.call('differential.changeset.search', params);
134
+ const result = await client.call('differential.getcommitpaths', {
135
+ revision_id: params.revision_id,
136
+ });
145
137
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
146
138
  });
147
139
  // Create inline comment on a diff
@@ -32,11 +32,12 @@ export function registerDiffusionTools(server, client) {
32
32
  constraints: jsonCoerce(z.object({
33
33
  ids: z.array(z.coerce.number()).optional().describe('Commit IDs'),
34
34
  phids: z.array(z.string()).optional().describe('Commit PHIDs'),
35
- repositoryPHIDs: z.array(z.string()).optional().describe('Repository PHIDs'),
35
+ repositories: z.array(z.string()).optional().describe('Repository PHIDs'),
36
36
  identifiers: z.array(z.string()).optional().describe('Commit identifiers (hashes)'),
37
- authorPHIDs: z.array(z.string()).optional().describe('Author PHIDs'),
37
+ authors: z.array(z.string()).optional().describe('Author PHIDs'),
38
+ auditors: z.array(z.string()).optional().describe('Auditor user/project PHIDs'),
38
39
  responsiblePHIDs: z.array(z.string()).optional().describe('User PHIDs responsible (as author or auditor)'),
39
- statuses: z.array(z.string()).optional().describe('Audit statuses: audited, needs-audit, concern-raised, partially-audited'),
40
+ statuses: z.array(z.string()).optional().describe('Audit statuses: none, needs-audit, concern-raised, partially-audited, audited, needs-verification'),
40
41
  query: z.string().optional().describe('Full-text search query'),
41
42
  })).optional().describe('Search constraints'),
42
43
  attachments: jsonCoerce(z.object({
@@ -57,12 +58,11 @@ export function registerDiffusionTools(server, client) {
57
58
  path: z.string().describe('Path to browse (e.g., "/", "/src/")'),
58
59
  repository: z.string().optional().describe('Repository callsign, short name, or PHID'),
59
60
  commit: z.string().optional().describe('Commit hash or branch name (default: HEAD)'),
61
+ needValidityOnly: z.boolean().optional().describe('Only check path validity without loading the full tree'),
62
+ limit: z.coerce.number().optional().describe('Maximum entries to return'),
63
+ offset: z.coerce.number().optional().describe('Result offset for pagination'),
60
64
  }, async (params) => {
61
- const result = await client.call('diffusion.browsequery', {
62
- path: params.path,
63
- repository: params.repository,
64
- commit: params.commit,
65
- });
65
+ const result = await client.call('diffusion.browsequery', params);
66
66
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
67
67
  });
68
68
  // Read file content from repository
@@ -89,6 +89,8 @@ export function registerDiffusionTools(server, client) {
89
89
  server.tool('phabricator_branch_search', 'List branches in a Diffusion repository', {
90
90
  repository: z.string().describe('Repository callsign, short name, or PHID'),
91
91
  contains: z.string().optional().describe('Only branches containing this commit'),
92
+ patterns: z.array(z.string()).optional().describe('Filter branches by glob patterns'),
93
+ closed: z.boolean().optional().describe('Filter by open/closed status (Mercurial only)'),
92
94
  limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
93
95
  offset: z.coerce.number().optional().describe('Result offset for pagination'),
94
96
  }, async (params) => {
@@ -98,6 +100,9 @@ export function registerDiffusionTools(server, client) {
98
100
  // List tags
99
101
  server.tool('phabricator_tag_search', 'List tags in a Diffusion repository', {
100
102
  repository: z.string().describe('Repository callsign, short name, or PHID'),
103
+ names: z.array(z.string()).optional().describe('Filter to specific tag names'),
104
+ commit: z.string().optional().describe('Show tags reachable from this commit'),
105
+ needMessages: z.boolean().optional().describe('Include tag messages in results'),
101
106
  limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
102
107
  offset: z.coerce.number().optional().describe('Result offset for pagination'),
103
108
  }, async (params) => {
@@ -109,6 +114,9 @@ export function registerDiffusionTools(server, client) {
109
114
  path: z.string().describe('File path in the repository'),
110
115
  repository: z.string().optional().describe('Repository callsign, short name, or PHID'),
111
116
  commit: z.string().optional().describe('Commit hash or branch to start from (default: HEAD)'),
117
+ against: z.string().optional().describe('Compare against another commit'),
118
+ needDirectChanges: z.boolean().optional().describe('Include direct change info per path entry'),
119
+ needChildChanges: z.boolean().optional().describe('Include child change info per path entry'),
112
120
  limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
113
121
  offset: z.coerce.number().optional().describe('Result offset for pagination'),
114
122
  }, async (params) => {
@@ -134,4 +142,59 @@ export function registerDiffusionTools(server, client) {
134
142
  });
135
143
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
136
144
  });
145
+ // Create or edit a repository
146
+ server.tool('phabricator_repository_edit', 'Create or edit a Diffusion repository. To create, omit objectIdentifier and provide vcs + name.', {
147
+ objectIdentifier: z.string().optional().describe('Repository PHID, ID, callsign, or short name (omit to create new)'),
148
+ vcs: z.enum(['git', 'hg', 'svn']).optional().describe('Version control system (required for creation)'),
149
+ name: z.string().optional().describe('Repository name'),
150
+ callsign: z.string().optional().describe('Repository callsign (short uppercase identifier)'),
151
+ shortName: z.string().optional().describe('Repository short name (URL slug)'),
152
+ description: z.string().optional().describe('Repository description (Remarkup)'),
153
+ defaultBranch: z.string().optional().describe('Default branch name'),
154
+ status: z.enum(['active', 'inactive']).optional().describe('Repository status'),
155
+ addProjectPHIDs: z.array(z.string()).optional().describe('Project PHIDs to add'),
156
+ removeProjectPHIDs: z.array(z.string()).optional().describe('Project PHIDs to remove'),
157
+ space: z.string().optional().describe('Space PHID (for multi-space installations)'),
158
+ }, async (params) => {
159
+ const transactions = [];
160
+ if (params.vcs !== undefined) {
161
+ transactions.push({ type: 'vcs', value: params.vcs });
162
+ }
163
+ if (params.name !== undefined) {
164
+ transactions.push({ type: 'name', value: params.name });
165
+ }
166
+ if (params.callsign !== undefined) {
167
+ transactions.push({ type: 'callsign', value: params.callsign });
168
+ }
169
+ if (params.shortName !== undefined) {
170
+ transactions.push({ type: 'shortName', value: params.shortName });
171
+ }
172
+ if (params.description !== undefined) {
173
+ transactions.push({ type: 'description', value: params.description });
174
+ }
175
+ if (params.defaultBranch !== undefined) {
176
+ transactions.push({ type: 'defaultBranch', value: params.defaultBranch });
177
+ }
178
+ if (params.status !== undefined) {
179
+ transactions.push({ type: 'status', value: params.status });
180
+ }
181
+ if (params.addProjectPHIDs !== undefined) {
182
+ transactions.push({ type: 'projects.add', value: params.addProjectPHIDs });
183
+ }
184
+ if (params.removeProjectPHIDs !== undefined) {
185
+ transactions.push({ type: 'projects.remove', value: params.removeProjectPHIDs });
186
+ }
187
+ if (params.space !== undefined) {
188
+ transactions.push({ type: 'space', value: params.space });
189
+ }
190
+ if (transactions.length === 0) {
191
+ return { content: [{ type: 'text', text: 'No changes specified' }] };
192
+ }
193
+ const apiParams = { transactions };
194
+ if (params.objectIdentifier !== undefined) {
195
+ apiParams.objectIdentifier = params.objectIdentifier;
196
+ }
197
+ const result = await client.call('diffusion.repository.edit', apiParams);
198
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
199
+ });
137
200
  }
@@ -3,7 +3,7 @@ export function registerFeedTools(server, client) {
3
3
  // Query activity feed
4
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
5
  filterPHIDs: z.array(z.string()).optional().describe('Only show activity involving these PHIDs (user, project, task, etc.)'),
6
- view: z.enum(['data', 'text', 'html']).optional().describe('Output format: "data" (structured, default), "text" (human-readable), "html" (rendered HTML)'),
6
+ view: z.enum(['data', 'text', 'html', 'html-summary']).optional().describe('Output format: "data" (structured, default), "text" (human-readable), "html" (rendered HTML), "html-summary" (title only)'),
7
7
  limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
8
8
  after: z.string().optional().describe('Cursor for pagination (chronological key from previous results)'),
9
9
  before: z.string().optional().describe('Cursor for reverse pagination'),
@@ -19,7 +19,7 @@ export function registerFileTools(server, client) {
19
19
  ids: z.array(z.coerce.number()).optional().describe('File IDs'),
20
20
  phids: z.array(z.string()).optional().describe('File PHIDs'),
21
21
  authorPHIDs: z.array(z.string()).optional().describe('Author PHIDs'),
22
- names: z.array(z.string()).optional().describe('File names'),
22
+ name: z.string().optional().describe('File name substring search'),
23
23
  dateCreatedStart: z.coerce.number().optional().describe('Created after (epoch timestamp)'),
24
24
  dateCreatedEnd: z.coerce.number().optional().describe('Created before (epoch timestamp)'),
25
25
  })).optional().describe('Search constraints'),
@@ -28,9 +28,9 @@ export function registerHarbormasterTools(server, client) {
28
28
  constraints: jsonCoerce(z.object({
29
29
  ids: z.array(z.coerce.number()).optional().describe('Build IDs'),
30
30
  phids: z.array(z.string()).optional().describe('Build PHIDs'),
31
- buildablePHIDs: z.array(z.string()).optional().describe('Buildable PHIDs'),
32
- buildPlanPHIDs: z.array(z.string()).optional().describe('Build plan PHIDs'),
33
- statuses: z.array(z.string()).optional().describe('Build statuses: building, passed, failed, aborted, error, paused, deadlocked'),
31
+ buildables: z.array(z.string()).optional().describe('Buildable PHIDs'),
32
+ plans: z.array(z.string()).optional().describe('Build plan PHIDs'),
33
+ statuses: z.array(z.string()).optional().describe('Build statuses: building, passed, failed, aborted, error, paused'),
34
34
  initiatorPHIDs: z.array(z.string()).optional().describe('PHIDs of users/objects that initiated the build'),
35
35
  })).optional().describe('Search constraints'),
36
36
  attachments: jsonCoerce(z.object({
@@ -122,8 +122,8 @@ export function registerHarbormasterTools(server, client) {
122
122
  constraints: jsonCoerce(z.object({
123
123
  ids: z.array(z.coerce.number()).optional().describe('Build plan IDs'),
124
124
  phids: z.array(z.string()).optional().describe('Build plan PHIDs'),
125
- statuses: z.array(z.string()).optional().describe('Plan statuses'),
126
- query: z.string().optional().describe('Full-text search query'),
125
+ statuses: z.array(z.string()).optional().describe('Plan statuses: "active", "disabled"'),
126
+ match: z.string().optional().describe('Search for build plans by name substring'),
127
127
  })).optional().describe('Search constraints'),
128
128
  order: z.string().optional().describe('Result order'),
129
129
  limit: z.coerce.number().max(100).optional().describe('Maximum results (max 100)'),
@@ -13,7 +13,7 @@ export function registerManiphestTools(server, client) {
13
13
  priorities: z.array(z.coerce.number()).optional().describe('Priority levels'),
14
14
  subtypes: z.array(z.string()).optional().describe('Task subtypes'),
15
15
  columnPHIDs: z.array(z.string()).optional().describe('Workboard column PHIDs'),
16
- projectPHIDs: z.array(z.string()).optional().describe('Project PHIDs (tasks tagged with these projects)'),
16
+ projects: 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
18
  createdStart: z.coerce.number().optional().describe('Created after (epoch timestamp)'),
19
19
  createdEnd: z.coerce.number().optional().describe('Created before (epoch timestamp)'),
@@ -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 parent tasks'),
25
- hasSubtasks: z.boolean().optional().describe('Filter to tasks that have subtasks'),
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'),
26
26
  spacePHIDs: z.array(z.string()).optional().describe('Filter by Space PHIDs (for multi-space installations)'),
27
27
  })).optional().describe('Search constraints'),
28
28
  attachments: jsonCoerce(z.object({
@@ -8,8 +8,8 @@ export function registerOwnersTools(server, client) {
8
8
  ids: z.array(z.coerce.number()).optional().describe('Package IDs'),
9
9
  phids: z.array(z.string()).optional().describe('Package PHIDs'),
10
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"]])'),
11
+ repositories: z.array(z.string()).optional().describe('Repository PHIDs'),
12
+ paths: z.array(z.string()).optional().describe('Code paths to search for (e.g. "/src/foo.ts")'),
13
13
  statuses: z.array(z.string()).optional().describe('Package statuses'),
14
14
  dominion: z.array(z.string()).optional().describe('Ownership strength: "strong" (default) or "weak"'),
15
15
  autoReview: z.array(z.string()).optional().describe('Auto-review setting: "none", "subscribe", "review", "block"'),
@@ -7,7 +7,7 @@ export function registerPasteTools(server, client) {
7
7
  constraints: jsonCoerce(z.object({
8
8
  ids: z.array(z.coerce.number()).optional().describe('Paste IDs'),
9
9
  phids: z.array(z.string()).optional().describe('Paste PHIDs'),
10
- authorPHIDs: z.array(z.string()).optional().describe('Author PHIDs'),
10
+ authors: z.array(z.string()).optional().describe('Author PHIDs'),
11
11
  languages: z.array(z.string()).optional().describe('Languages'),
12
12
  statuses: z.array(z.string()).optional().describe('Statuses: active, archived'),
13
13
  query: z.string().optional().describe('Full-text search query'),
@@ -8,7 +8,8 @@ export function registerPhrictionTools(server, client) {
8
8
  ids: z.array(z.coerce.number()).optional().describe('Document IDs'),
9
9
  phids: z.array(z.string()).optional().describe('Document PHIDs'),
10
10
  paths: z.array(z.string()).optional().describe('Document paths'),
11
- ancestorPaths: z.array(z.string()).optional().describe('Ancestor paths to search under'),
11
+ parentPaths: z.array(z.string()).optional().describe('Parent paths (direct children only)'),
12
+ ancestorPaths: z.array(z.string()).optional().describe('Ancestor paths to search under (any depth)'),
12
13
  statuses: z.array(z.string()).optional().describe('Document statuses'),
13
14
  query: z.string().optional().describe('Full-text search query'),
14
15
  })).optional().describe('Search constraints'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freelancercom/phabricator-mcp",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
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",