Merge request review
What a review does to a merge request: read its changes, its diff versions and its raw diffs; read and write its notes; open, reply to and resolve threaded discussions, inline on a line of the diff or general; and queue draft notes that stay private until they are published, one at a time or all together as a single review.
Approving, merging and the rest of the merge request’s lifecycle are on Merge requests, and so are award emoji on its notes.
Sample questions
Section titled “Sample questions”- “What changed in merge request !15?”
- “Comment on line 42 of src/app.go in merge request !15”
- “Resolve the open threads I started on merge request !15”
- “Publish my draft review”
How to call it
Section titled “How to call it”- Dynamic, the default surface: call
gitlab_execute_actionwithactionset to the action’s ID, such asmr_review.changes_get, and its parameters inparams.gitlab_find_actionfinds an ID from a description of the task. - Meta (
GITLAB_MCP_TOOL_SURFACE=meta): callgitlab_mr_reviewwithactionset to the action’s name, such aschanges_get, and its parameters inparams. - Individual (
GITLAB_MCP_TOOL_SURFACE=individual): call the action’s own tool, such asgitlab_mr_changes_get, with its parameters as the arguments.
Availability
Section titled “Availability”Every tier serves the whole group, on self-managed instances and on GitLab.com alike.
Read-only actions: 10 of 23, the ones a deployment in read-only mode keeps.
Actions
Section titled “Actions”The description of each action, and of each of its parameters, is the text the server serves for it on the default surface, quoted as served. A destructive action runs only once confirmed, unless GITLAB_MCP_YOLO_MODE (or AUTOPILOT) skips that step: the dynamic surface needs confirm: true on gitlab_execute_action, and the other two take a confirm parameter or the client’s prompt (Destructive actions).
| Action | Individual |
|---|---|
mr_review.changes_get | gitlab_mr_changes_get |
mr_review.diff_version_get | gitlab_mr_diff_version_get |
mr_review.diff_versions_list | gitlab_mr_diff_versions_list |
mr_review.discussion_create | gitlab_mr_discussion_create |
mr_review.discussion_get | gitlab_mr_discussion_get |
mr_review.discussion_list | gitlab_mr_discussion_list |
mr_review.discussion_note_delete | gitlab_mr_discussion_note_delete |
mr_review.discussion_note_update | gitlab_mr_discussion_note_update |
mr_review.discussion_reply | gitlab_mr_discussion_reply |
mr_review.discussion_resolve | gitlab_mr_discussion_resolve |
mr_review.draft_note_create | gitlab_mr_draft_note_create |
mr_review.draft_note_delete | gitlab_mr_draft_note_delete |
mr_review.draft_note_get | gitlab_mr_draft_note_get |
mr_review.draft_note_list | gitlab_mr_draft_note_list |
mr_review.draft_note_publish | gitlab_mr_draft_note_publish |
mr_review.draft_note_publish_all | gitlab_mr_draft_note_publish_all |
mr_review.draft_note_update | gitlab_mr_draft_note_update |
mr_review.note_create | gitlab_mr_note_create |
mr_review.note_delete | gitlab_mr_note_delete |
mr_review.note_get | gitlab_mr_note_get |
mr_review.note_list | gitlab_mr_notes_list |
mr_review.note_update | gitlab_mr_note_update |
mr_review.raw_diffs | gitlab_mr_raw_diffs |
mr_review.changes_get
Section titled “mr_review.changes_get”Get the current file diffs (changes) for a merge request. Returns: per-file diffs with old_path, new_path, diff content, file status (added/deleted/renamed), file modes, and truncated_files when GitLab collapses large diffs. See also:
mr_review.diff_versions_list,mr_review.raw_diffs,merge_request.get.
- Meta-tool:
gitlab_mr_review, actionchanges_get - Individual tool:
gitlab_mr_changes_get - Tier: Free
- Behavior: read-only, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
project_id | string/integer | yes | Project ID or URL-encoded path |
order_by | string | no | For keyset pagination, the column to order results by |
page | integer | no | Page number to fetch, 1-based. Defaults to 1. Use the next_page field from the previous response to paginate forward. |
page_token | string | no | Keyset pagination cursor: record id at which to fetch the next page, taken from the previous keyset response. Only used when pagination=‘keyset’. |
pagination | string | no | Pagination method: ‘keyset’ for keyset-based pagination on large ordered result sets, or ‘offset’ (the default). Keyset avoids deep-offset cost. |
per_page | integer | no | Items per page. Defaults to 20, minimum 1, maximum 100. Use 100 to minimize round trips when the result set is large. |
sort | string (asc, desc) | no | Sort order for keyset pagination: ‘asc’ or ‘desc’ |
unidiff | boolean | no | Return diffs in unified diff format (default: false) |
mr_review.diff_version_get
Section titled “mr_review.diff_version_get”Get a single merge request diff version with its commits and file diffs. Returns: the diff version with id, SHAs, state, real_size, the full commit list, and per-file diffs (optionally in unified-diff format). See also:
mr_review.diff_versions_list,mr_review.changes_get.
- Meta-tool:
gitlab_mr_review, actiondiff_version_get - Individual tool:
gitlab_mr_diff_version_get - Tier: Free
- Behavior: read-only, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
project_id | string/integer | yes | Project ID or URL-encoded path |
version_id | integer | yes | Diff version ID |
unidiff | boolean | no | Return diffs in unified diff format (default: false) |
mr_review.diff_versions_list
Section titled “mr_review.diff_versions_list”List all diff versions (historical snapshots) of a merge request. Returns: an array of diff versions with id, head/base/start SHAs, state, real_size, and created_at, plus pagination metadata. See also:
mr_review.diff_version_get,mr_review.changes_get.
- Meta-tool:
gitlab_mr_review, actiondiff_versions_list - Individual tool:
gitlab_mr_diff_versions_list - Tier: Free
- Behavior: read-only, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
project_id | string/integer | yes | Project ID or URL-encoded path |
order_by | string | no | For keyset pagination, the column to order results by |
page | integer | no | Page number to fetch, 1-based. Defaults to 1. Use the next_page field from the previous response to paginate forward. |
page_token | string | no | Keyset pagination cursor: record id at which to fetch the next page, taken from the previous keyset response. Only used when pagination=‘keyset’. |
pagination | string | no | Pagination method: ‘keyset’ for keyset-based pagination on large ordered result sets, or ‘offset’ (the default). Keyset avoids deep-offset cost. |
per_page | integer | no | Items per page. Defaults to 20, minimum 1, maximum 100. Use 100 to minimize round trips when the result set is large. |
sort | string (asc, desc) | no | Sort order for keyset pagination: ‘asc’ or ‘desc’ |
mr_review.discussion_create
Section titled “mr_review.discussion_create”Create a new discussion thread on a merge request, optionally as an inline diff comment. Returns: the created thread with its first note (author, body, resolvable state, diff position). See also:
mr_review.discussion_reply,mr_review.discussion_list,mr_review.changes_get.
- Meta-tool:
gitlab_mr_review, actiondiscussion_create - Individual tool:
gitlab_mr_discussion_create - Tier: Free
- Behavior: writes, not idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
body | string | yes | Discussion body |
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
project_id | string/integer | yes | Project ID or URL-encoded path |
commit_id | string | no | SHA of the commit to anchor the discussion to (optional). |
created_at | string | no | Backdate the discussion creation time (ISO 8601, e.g. 2025-01-01T00:00:00Z). Requires admin or owner rights |
position | object | no | Diff position for inline comments. Omit for general MR discussions. |
mr_review.discussion_get
Section titled “mr_review.discussion_get”Get a single merge request discussion thread by its discussion id. Returns: the thread with every note (author, body, system flag, resolvable/resolved state, diff position). See also:
mr_review.discussion_list,mr_review.discussion_reply,mr_review.discussion_resolve.
- Meta-tool:
gitlab_mr_review, actiondiscussion_get - Individual tool:
gitlab_mr_discussion_get - Tier: Free
- Behavior: read-only, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
discussion_id | string | yes | ID of the discussion |
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
project_id | string/integer | yes | Project ID or URL-encoded path |
mr_review.discussion_list
Section titled “mr_review.discussion_list”List discussion threads on a merge request with ordering and keyset pagination. Returns: discussion threads with their notes (author, body, system flag, resolvable state, diff position) and pagination metadata. See also:
mr_review.discussion_get,mr_review.discussion_create,merge_request.get,mr_review.note_list.
- Meta-tool:
gitlab_mr_review, actiondiscussion_list - Individual tool:
gitlab_mr_discussion_list - Tier: Free
- Behavior: read-only, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
project_id | string/integer | yes | Project ID or URL-encoded path |
order_by | string | no | Column to order keyset-paginated results by (e.g. created_at, updated_at) |
page | integer | no | Page number to fetch, 1-based. Defaults to 1. Use the next_page field from the previous response to paginate forward. |
page_token | string | no | Keyset pagination cursor: record id at which to fetch the next page, taken from the previous keyset response. Only used when pagination=‘keyset’. |
pagination | string | no | Pagination method: ‘keyset’ for keyset-based pagination on large ordered result sets, or ‘offset’ (the default). Keyset avoids deep-offset cost. |
per_page | integer | no | Items per page. Defaults to 20, minimum 1, maximum 100. Use 100 to minimize round trips when the result set is large. |
sort | string (asc, desc) | no | Sort direction (asc or desc) |
mr_review.discussion_note_delete
Section titled “mr_review.discussion_note_delete”Delete a note from a merge request discussion thread (destructive). Returns: a deletion confirmation message. See also:
mr_review.discussion_note_update,mr_review.discussion_get,mr_review.discussion_list.
- Meta-tool:
gitlab_mr_review, actiondiscussion_note_delete - Individual tool:
gitlab_mr_discussion_note_delete - Tier: Free
- Behavior: writes, destructive (needs confirmation), idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
discussion_id | string | yes | ID of the discussion containing the note |
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
note_id | integer | yes | ID of the note to delete |
project_id | string/integer | yes | Project ID or URL-encoded path |
mr_review.discussion_note_update
Section titled “mr_review.discussion_note_update”Update the body or resolved state of a note in a merge request discussion thread. Returns: the updated note. See also:
mr_review.discussion_reply,mr_review.discussion_note_delete,mr_review.discussion_get.
- Meta-tool:
gitlab_mr_review, actiondiscussion_note_update - Individual tool:
gitlab_mr_discussion_note_update - Tier: Free
- Behavior: writes, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
discussion_id | string | yes | ID of the discussion containing the note |
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
note_id | integer | yes | ID of the note to update |
project_id | string/integer | yes | Project ID or URL-encoded path |
body | string | no | New body text (Markdown). Leave empty to keep current body. |
created_at | string | no | Override the note creation time (ISO 8601, e.g. 2025-01-01T00:00:00Z). Requires admin or owner rights |
resolved | boolean | no | Set to true to resolve, false to unresolve. Omit to leave unchanged. |
mr_review.discussion_reply
Section titled “mr_review.discussion_reply”Add a reply note to an existing merge request discussion thread. Returns: the created note (author, body, timestamps, resolvable state). See also:
mr_review.discussion_create,mr_review.discussion_get,mr_review.discussion_note_update.
- Meta-tool:
gitlab_mr_review, actiondiscussion_reply - Individual tool:
gitlab_mr_discussion_reply - Tier: Free
- Behavior: writes, not idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
body | string | yes | Reply body |
discussion_id | string | yes | ID of the discussion to reply to |
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
project_id | string/integer | yes | Project ID or URL-encoded path |
created_at | string | no | Backdate the note creation time (ISO 8601, e.g. 2025-01-01T00:00:00Z). Requires admin or owner rights |
mr_review.discussion_resolve
Section titled “mr_review.discussion_resolve”Resolve or unresolve a merge request discussion thread. Returns: the thread with its updated resolved state and notes. See also:
mr_review.discussion_get,mr_review.discussion_list,mr_review.discussion_reply.
- Meta-tool:
gitlab_mr_review, actiondiscussion_resolve - Individual tool:
gitlab_mr_discussion_resolve - Tier: Free
- Behavior: writes, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
discussion_id | string | yes | ID of the discussion to resolve |
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
project_id | string/integer | yes | Project ID or URL-encoded path |
resolved | boolean | yes | True to resolve, false to unresolve |
mr_review.draft_note_create
Section titled “mr_review.draft_note_create”Create a pending draft note on a merge request. Returns: the created draft note with ID, author, body, and inline position. See also:
mr_review.draft_note_list,mr_review.draft_note_publish,mr_review.draft_note_publish_all.
- Meta-tool:
gitlab_mr_review, actiondraft_note_create - Individual tool:
gitlab_mr_draft_note_create - Tier: Free
- Behavior: writes, not idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
note | string | yes | Note body text (Markdown) |
project_id | string/integer | yes | Project ID or URL-encoded path |
commit_id | string | no | SHA of the commit to comment on |
in_reply_to_discussion_id | string | no | Discussion ID to reply to |
position | object | no | Diff position for inline comments on specific lines. Omit for general MR comments. |
resolve_discussion | boolean | no | Resolve the discussion when published |
mr_review.draft_note_delete
Section titled “mr_review.draft_note_delete”Delete a pending draft note permanently. Returns: a success confirmation naming the note, merge request, and project. See also:
mr_review.draft_note_list,mr_review.draft_note_get.
- Meta-tool:
gitlab_mr_review, actiondraft_note_delete - Individual tool:
gitlab_mr_draft_note_delete - Tier: Free
- Behavior: writes, destructive (needs confirmation), idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
note_id | integer | yes | Draft note ID |
project_id | string/integer | yes | Project ID or URL-encoded path |
mr_review.draft_note_get
Section titled “mr_review.draft_note_get”Get a single draft note from a merge request. Returns: the draft note with author, body, resolve flag, line code, and inline diff position. See also:
mr_review.draft_note_list,mr_review.draft_note_update,mr_review.draft_note_publish.
- Meta-tool:
gitlab_mr_review, actiondraft_note_get - Individual tool:
gitlab_mr_draft_note_get - Tier: Free
- Behavior: read-only, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
note_id | integer | yes | Draft note ID |
project_id | string/integer | yes | Project ID or URL-encoded path |
mr_review.draft_note_list
Section titled “mr_review.draft_note_list”List pending draft notes on a merge request. Returns: each draft note with author, body, optional inline position, line code, and pagination metadata. See also:
mr_review.draft_note_get,mr_review.draft_note_create,mr_review.draft_note_publish_all.
- Meta-tool:
gitlab_mr_review, actiondraft_note_list - Individual tool:
gitlab_mr_draft_note_list - Tier: Free
- Behavior: read-only, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
project_id | string/integer | yes | Project ID or URL-encoded path |
order_by | string | no | Order by: id (default) |
page | integer | no | Page number to fetch, 1-based. Defaults to 1. Use the next_page field from the previous response to paginate forward. |
page_token | string | no | Keyset pagination cursor: record id at which to fetch the next page, taken from the previous keyset response. Only used when pagination=‘keyset’. |
pagination | string | no | Pagination method: ‘keyset’ for keyset-based pagination on large ordered result sets, or ‘offset’ (the default). Keyset avoids deep-offset cost. |
per_page | integer | no | Items per page. Defaults to 20, minimum 1, maximum 100. Use 100 to minimize round trips when the result set is large. |
sort | string (asc, desc) | no | Sort: asc or desc (default) |
mr_review.draft_note_publish
Section titled “mr_review.draft_note_publish”Publish a single draft note as a regular merge request note. Returns: a success confirmation naming the note, merge request, and project. See also:
mr_review.draft_note_publish_all,mr_review.draft_note_list.
- Meta-tool:
gitlab_mr_review, actiondraft_note_publish - Individual tool:
gitlab_mr_draft_note_publish - Tier: Free
- Behavior: writes, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
note_id | integer | yes | Draft note ID |
project_id | string/integer | yes | Project ID or URL-encoded path |
mr_review.draft_note_publish_all
Section titled “mr_review.draft_note_publish_all”Publish all of the user’s pending draft notes on a merge request, optionally with a summary note and a reviewer state. Returns: a success confirmation naming the merge request and project. See also:
mr_review.draft_note_list,mr_review.draft_note_publish.
- Meta-tool:
gitlab_mr_review, actiondraft_note_publish_all - Individual tool:
gitlab_mr_draft_note_publish_all - Tier: Free
- Behavior: writes, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
project_id | string/integer | yes | Project ID or URL-encoded path |
internal | boolean | no | Whether the summary note is internal, visible only to members who can see internal notes. Defaults to false and applies only together with note |
note | string | no | Summary comment GitLab posts on the merge request after publishing the drafts. Markdown is supported |
reviewer_state | string (requested_changes, reviewed) | no | Review state to record for the caller as a reviewer after publishing: requested_changes or reviewed. It does not record a formal approval |
mr_review.draft_note_update
Section titled “mr_review.draft_note_update”Update a pending draft note’s body or position. Returns: the updated draft note. See also:
mr_review.draft_note_get,mr_review.draft_note_publish.
- Meta-tool:
gitlab_mr_review, actiondraft_note_update - Individual tool:
gitlab_mr_draft_note_update - Tier: Free
- Behavior: writes, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
note_id | integer | yes | Draft note ID |
project_id | string/integer | yes | Project ID or URL-encoded path |
note | string | no | Updated note body text (Markdown) |
position | object | no | Updated diff position for inline comments |
mr_review.note_create
Section titled “mr_review.note_create”Add a general comment (note) to a merge request. Returns: the created note with id, author, body, timestamps, and resolvable/internal flags. See also:
mr_review.note_list,merge_request.get,mr_review.discussion_create.
- Meta-tool:
gitlab_mr_review, actionnote_create - Individual tool:
gitlab_mr_note_create - Tier: Free
- Behavior: writes, not idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
body | string | yes | Comment body (Markdown supported) |
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
project_id | string/integer | yes | Project ID or URL-encoded path |
created_at | string | no | Backdate the note to this RFC 3339 timestamp (e.g. 2026-01-15T10:00:00Z). Requires administrator or project/group owner permissions. Ignored otherwise. |
internal | boolean | no | Mark note as internal (visible only to project members) |
merge_request_diff_head_sha | string | no | Required for the deduplication of system notes: SHA referencing the most recent diff version of the merge request. |
mr_review.note_delete
Section titled “mr_review.note_delete”Delete a merge request note permanently. Returns: a success confirmation naming the note, MR, and project. See also:
mr_review.note_get,mr_review.note_list.
- Meta-tool:
gitlab_mr_review, actionnote_delete - Individual tool:
gitlab_mr_note_delete - Tier: Free
- Behavior: writes, destructive (needs confirmation), idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
note_id | integer | yes | ID of the note to delete |
project_id | string/integer | yes | Project ID or URL-encoded path |
mr_review.note_get
Section titled “mr_review.note_get”Get a single merge request note by its ID. Returns: the note with author, body, timestamps, position, and resolvable/internal flags. See also:
mr_review.note_list,mr_review.note_update,mr_review.note_delete.
- Meta-tool:
gitlab_mr_review, actionnote_get - Individual tool:
gitlab_mr_note_get - Tier: Free
- Behavior: read-only, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
note_id | integer | yes | ID of the note to retrieve |
project_id | string/integer | yes | Project ID or URL-encoded path |
mr_review.note_list
Section titled “mr_review.note_list”List all notes (comments) on a merge request. Returns: notes with author, body, system/internal flags, and pagination metadata. See also:
mr_review.note_get,mr_review.note_create,mr_review.discussion_list.
- Meta-tool:
gitlab_mr_review, actionnote_list - Individual tool:
gitlab_mr_notes_list - Tier: Free
- Behavior: read-only, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
project_id | string/integer | yes | Project ID or URL-encoded path |
order_by | string (created_at, updated_at) | no | Order by field (created_at, updated_at) |
page | integer | no | Page number to fetch, 1-based. Defaults to 1. Use the next_page field from the previous response to paginate forward. |
page_token | string | no | Keyset pagination cursor: record id at which to fetch the next page, taken from the previous keyset response. Only used when pagination=‘keyset’. |
pagination | string | no | Pagination method: ‘keyset’ for keyset-based pagination on large ordered result sets, or ‘offset’ (the default). Keyset avoids deep-offset cost. |
per_page | integer | no | Items per page. Defaults to 20, minimum 1, maximum 100. Use 100 to minimize round trips when the result set is large. |
sort | string (asc, desc) | no | Sort direction (asc, desc) |
mr_review.note_update
Section titled “mr_review.note_update”Update a merge request note’s body. Returns: the updated note with new body and updated_at timestamp. See also:
mr_review.note_get,mr_review.note_list,mr_review.note_delete.
- Meta-tool:
gitlab_mr_review, actionnote_update - Individual tool:
gitlab_mr_note_update - Tier: Free
- Behavior: writes, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
body | string | yes | Updated comment body |
merge_request_iid | integer | yes | Merge request IID (project-scoped, not ‘merge_request_id’) |
note_id | integer | yes | ID of the note to update |
project_id | string/integer | yes | Project ID or URL-encoded path |
mr_review.raw_diffs
Section titled “mr_review.raw_diffs”Get the raw unified-diff output for a merge request in plain-text git-apply format. Returns: raw_diff, a single plain-text unified diff for the MR head suitable for patch application or programmatic analysis. See also:
mr_review.changes_get,mr_review.diff_versions_list.
- Meta-tool:
gitlab_mr_review, actionraw_diffs - Individual tool:
gitlab_mr_raw_diffs - Tier: Free
- Behavior: read-only, idempotent
| Parameter | Type | Mandatory | Description |
|---|---|---|---|
merge_request_iid | integer | yes | Merge request internal ID |
project_id | string/integer | yes | Project ID or URL-encoded path |