Summary
ENABLED_TOOLS and TOOLSETS filters are enforced at tools/list time only. tools/call dispatches from the full unfiltered tool registry (73 tools). Any user with access to the server endpoint that knows a tool name can invoke it directly. Tool names are not secret since mcp-atlassian is open source. Any direct JSON-RPC call bypasses the restriction entirely.
READ_ONLY_MODE is not affected: it has dual enforcement at list time (_list_tools_mcp) and call time @check_write_access decorator. The developers applied the correct pattern to READ_ONLY_MODE but not to ENABLED_TOOLS or toolsets - confirming this is an implementation oversight.
Impact
Any user with access to the MCP server's HTTP endpoint can invoke any of the 73 registered tools regardless of ENABLED_TOOLS or TOOLSETS configuration - including write and delete operations on Jira issues, Confluence pages, etc. Operators deploying mcp-atlassian via Streamable HTTP rely on ENABLED_TOOLS to enforce least-privilege access; the bypass invalidates that model entirely.
The security impact concentrates in multi-user / HTTP-transport deployments, where the tool filter is a trust boundary between clients. In a single-user stdio deployment there is no second principal to defend against.
Details
In src/mcp_atlassian/servers/main.py, AtlassianMCP overrides _list_tools_mcp and applies the TOOLSETS and ENABLED_TOOLS filters before returning the tool list to clients. The _call_tool_mcp handler is not overridden. FastMCP's default _call_tool_mcp resolves the tool from the local tool manager / mounted servers, the full unfiltered inventory, so the filters never apply at call time.
Proof of Concept
All calls are issued against the HTTP transport, with ENABLED_TOOLS=jira_search configured - only jira_search should be reachable.
Step 1 - negative control: tools/list correctly filters by ENABLED_TOOLS:
req → tools/list
← { "tools": [ { "name": "jira_search" } ] } # only 1 tool; jira_get_issue / jira_create_issue absent
Step 2 - confidentiality bypass: tools/call dispatches jira_get_issue despite its exclusion from the list:
req → tools/call jira_get_issue { "issue_key": "SEC-1" }
← { ... issue fields (summary, status, ...) ... } # executed; not blocked
Step 3 - integrity bypass: tools/call dispatches the write tool jira_create_issue:
req → tools/call jira_create_issue
{ "project_key": "SEC", "summary": "[PoC] ENABLED_TOOLS bypass", "issue_type": "Task" }
← { ... "key": "SEC-<n>" ... } # issue created despite ENABLED_TOOLS=jira_search
Step 1 proves the filter exists and is enforced at list time, so Steps 2–3 are a genuine authorization bypass, not an open/unconfigured endpoint. The same result holds with TOOLSETS=jira_read configured: write tools are absent from tools/list yet execute via tools/call.
Local reproduction
Extract enabled_tools_bypass_tool_authorization.zip:
# Fill Atlassian credentials in docker-compose.yml (ENABLED_TOOLS=jira_search is preset);
# ensure issue SEC-1 exists, or update the project/issue key in poc.sh
docker compose up -d # mcp-atlassian, streamable-http, 0.0.0.0, ENABLED_TOOLS=jira_search
./poc.sh # exits 0 on success
docker compose down -v
Requires Docker, curl, jq, and an Atlassian Cloud site with a Jira project (free tier works). The script runs the three steps above: it confirms tools/list returns only jira_search, then dispatches the excluded jira_get_issue (read) and jira_create_issue (write) via tools/call. The test issue it creates is deleted automatically on exit.
Credit
Discovered by Francisco Rosales of Manifold Security
References
Summary
ENABLED_TOOLSandTOOLSETSfilters are enforced attools/listtime only.tools/calldispatches from the full unfiltered tool registry (73 tools). Any user with access to the server endpoint that knows a tool name can invoke it directly. Tool names are not secret since mcp-atlassian is open source. Any direct JSON-RPC call bypasses the restriction entirely.READ_ONLY_MODEis not affected: it has dual enforcement at list time (_list_tools_mcp) and call time@check_write_access decorator. The developers applied the correct pattern toREAD_ONLY_MODEbut not toENABLED_TOOLSortoolsets- confirming this is an implementation oversight.Impact
Any user with access to the MCP server's HTTP endpoint can invoke any of the 73 registered tools regardless of
ENABLED_TOOLSorTOOLSETSconfiguration - including write and delete operations on Jira issues, Confluence pages, etc. Operators deploying mcp-atlassian via Streamable HTTP rely onENABLED_TOOLSto enforce least-privilege access; the bypass invalidates that model entirely.The security impact concentrates in multi-user / HTTP-transport deployments, where the tool filter is a trust boundary between clients. In a single-user stdio deployment there is no second principal to defend against.
Details
In
src/mcp_atlassian/servers/main.py, AtlassianMCP overrides_list_tools_mcpand applies theTOOLSETSandENABLED_TOOLSfilters before returning the tool list to clients. The_call_tool_mcphandler is not overridden. FastMCP's default_call_tool_mcpresolves the tool from the local tool manager / mounted servers, the full unfiltered inventory, so the filters never apply at call time.Proof of Concept
All calls are issued against the HTTP transport, with
ENABLED_TOOLS=jira_searchconfigured - onlyjira_searchshould be reachable.Step 1 - negative control: tools/list correctly filters by ENABLED_TOOLS:
Step 2 - confidentiality bypass: tools/call dispatches jira_get_issue despite its exclusion from the list:
Step 3 - integrity bypass: tools/call dispatches the write tool jira_create_issue:
Step 1 proves the filter exists and is enforced at list time, so Steps 2–3 are a genuine authorization bypass, not an open/unconfigured endpoint. The same result holds with TOOLSETS=jira_read configured: write tools are absent from tools/list yet execute via tools/call.
Local reproduction
Extract
enabled_tools_bypass_tool_authorization.zip:Credit
Discovered by Francisco Rosales of Manifold Security
References