Repository navigation
execute allowlist omits the space-scoped /tasks paths when toolsets are narrowed #110
Description
Describe the bug
When --toolsets narrows the active set, the execute path allowlist rejects the space-scoped forms of /tasks even though the tasks toolset is enabled. /api/tasks resolves, but /api/{spaceId}/tasks and /api/spaces/{spaceIdentifier}/tasks are both rejected as pathNotAllowed.
The rejection uses the message reserved for a path that belongs to no toolset, rather than the one for a path whose toolset is disabled — so the server is reporting that these paths are absent from the allowlist table entirely, which matches what the table contains.
This is the difference between a reachable endpoint and a useful one. /api/tasks is the unscoped listing; the space-scoped forms are the ones documented to accept project, states, fromCompletedDate and toCompletedDate. With them blocked there is no way to ask what happened to a given project's tasks over a date range.
Steps to reproduce
Start the server with toolsets narrowed, including tasks:
npx -y @octopusdeploy/mcp-server --toolsets projects,deployments,releases,tasks --read-only
Call execute:
{ "method": "GET", "path": "/api/Spaces-1/tasks" }Actual:
{
"success": false,
"reason": "pathNotAllowed",
"message": "Path '/api/Spaces-1/tasks' is not on the execute allowlist for any toolset. If this is a legitimate Octopus endpoint not yet covered, file an issue against the MCP server."
}/api/spaces/Spaces-1/tasks behaves identically. /api/tasks is accepted.
With --toolsets all (the default) the allowlist is skipped altogether, so this only appears when toolsets are narrowed.
Expected behavior
With the tasks toolset enabled, all three documented prefixes resolve.
The server's own catalog documents both space prefixes. grep_llms_txt for GET /tasks returns:
- `GET /tasks` - List all of the tasks in the supplied Octopus Deploy Space. The results will be
sorted from newest to oldest. | Prefixes (pick one): `/{spaceId}`, `/spaces/{spaceIdentifier}`
| ?active, batch, description, environment, fromCompletedDate, fromQueueDate, fromStartDate,
..., project, ..., states, take, tenant, tenantTag, toCompletedDate, ...
→ TaskResourceCollection
Since the execute tool description instructs callers to "Discover endpoints with grep_llms_txt", the documented discovery path currently leads to a path the allowlist rejects.
Root cause
src/helpers/pathAllowlist.ts:91:
tasks: ["/api/tasks", "/api/tasks/**"],Every other toolset that covers a space-scoped resource builds its patterns from the spaceScoped() helper (23 call sites in that table), which emits all four forms:
function spaceScoped(suffix: string): readonly string[] {
return [
`/api/*/${suffix}`,
`/api/*/${suffix}/**`,
`/api/spaces/*/${suffix}`,
`/api/spaces/*/${suffix}/**`,
];
}tasks is the only entry in TOOLSET_PATH_PATTERNS that does not use it, and the only one written as a single-line literal.
Suggested fix
The events toolset already handles exactly this shape — a resource with both space-scoped and unscoped endpoints:
events: [
// Space-scoped audit log endpoints.
...spaceScoped("events"),
// Unscoped metadata endpoints (categories, groups, agents, documenttypes).
"/api/events",
"/api/events/**",
],So tasks becomes:
tasks: [
...spaceScoped("tasks"),
"/api/tasks",
"/api/tasks/**",
],Environment and versions
- MCP Client: Claude Code
- Octopus MCP Server Version: 2.3.2
- Octopus Server Version: 2026.4.1480
- Started with
--toolsets projects,deployments,releases,tasks
Related
#108. The two compound: /api/tasks is the one accepted form, but with query dropped it cannot be filtered either. Until both are fixed, task history is unreachable by project or date window through execute by any route — which is what sent me looking at the allowlist in the first place.
Activity