Skip to content

Commit 542be8b

Browse files
committed
Wire MCP approval to MCP resolution
1 parent 4dddbde commit 542be8b

2 files changed

Lines changed: 57 additions & 3 deletions

File tree

src/utils/responses.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from fastapi import HTTPException
1010
from llama_stack_api import OpenAIResponseObject
11+
from llama_stack_api.openai_responses import ApprovalFilter
1112
from llama_stack_api.openai_responses import (
1213
OpenAIResponseContentPartRefusal as ContentPartRefusal,
1314
)
@@ -732,12 +733,20 @@ async def get_mcp_tools(
732733
continue
733734

734735
authorization = headers.pop("Authorization", None)
736+
737+
require_approval = (
738+
mcp_server.require_approval
739+
if isinstance(mcp_server.require_approval, str)
740+
else ApprovalFilter(
741+
always=mcp_server.require_approval.always or None,
742+
never=mcp_server.require_approval.never or None,
743+
)
744+
)
735745
tools.append(
736746
InputToolMCP(
737-
type="mcp",
738747
server_label=mcp_server.name,
739748
server_url=mcp_server.url,
740-
require_approval="never",
749+
require_approval=require_approval,
741750
headers=headers or None,
742751
authorization=authorization,
743752
)

tests/unit/utils/test_responses.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
AllowedToolsFilter,
1313
OpenAIResponseInputToolChoiceAllowedTools,
1414
)
15+
from llama_stack_api.openai_responses import ApprovalFilter as LlamaStackApprovalFilter
1516
from llama_stack_api.openai_responses import (
1617
OpenAIResponseInputTool as InputTool,
1718
)
@@ -60,7 +61,7 @@
6061

6162
import constants
6263
from models.api.requests import QueryRequest
63-
from models.config import ByokRag, ModelContextProtocolServer
64+
from models.config import ApprovalFilter, ByokRag, ModelContextProtocolServer
6465
from utils.responses import (
6566
_build_chunk_attributes,
6667
_merge_tools,
@@ -405,6 +406,50 @@ async def test_get_mcp_tools_without_auth(self, mocker: MockerFixture) -> None:
405406
assert tools_no_auth[0].server_label == "fs"
406407
assert tools_no_auth[0].server_url == "http://localhost:3000"
407408
assert tools_no_auth[0].headers is None
409+
assert all(tool.require_approval == "never" for tool in tools_no_auth)
410+
411+
@pytest.mark.asyncio
412+
async def test_get_mcp_tools_require_approval_always(
413+
self, mocker: MockerFixture
414+
) -> None:
415+
"""Test get_mcp_tools passes require_approval='always' from config."""
416+
server = ModelContextProtocolServer(
417+
name="strict",
418+
url="http://localhost:3000",
419+
provider_id="mcp",
420+
require_approval="always",
421+
)
422+
mock_config = mocker.Mock()
423+
mock_config.mcp_servers = [server]
424+
mocker.patch("utils.responses.configuration", mock_config)
425+
426+
tools = await get_mcp_tools(token=None)
427+
assert len(tools) == 1
428+
assert tools[0].require_approval == "always"
429+
430+
@pytest.mark.asyncio
431+
async def test_get_mcp_tools_require_approval_filter(
432+
self, mocker: MockerFixture
433+
) -> None:
434+
"""Test get_mcp_tools translates ApprovalFilter to Llama Stack format."""
435+
server = ModelContextProtocolServer(
436+
name="github",
437+
url="http://localhost:3000",
438+
provider_id="mcp",
439+
require_approval=ApprovalFilter(
440+
always=["create_issue"],
441+
never=["list_repos"],
442+
),
443+
)
444+
mock_config = mocker.Mock()
445+
mock_config.mcp_servers = [server]
446+
mocker.patch("utils.responses.configuration", mock_config)
447+
448+
tools = await get_mcp_tools(token=None)
449+
assert len(tools) == 1
450+
assert isinstance(tools[0].require_approval, LlamaStackApprovalFilter)
451+
assert tools[0].require_approval.always == ["create_issue"]
452+
assert tools[0].require_approval.never == ["list_repos"]
408453

409454
@pytest.mark.asyncio
410455
async def test_get_mcp_tools_with_kubernetes_auth(

0 commit comments

Comments
 (0)