Skip to content

Commit 00b9cc2

Browse files
committed
Wire MCP approval to MCP resolution
1 parent 533d968 commit 00b9cc2

2 files changed

Lines changed: 56 additions & 3 deletions

File tree

src/utils/responses.py

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

99
from fastapi import HTTPException
1010
from llama_stack_api import OpenAIResponseObject
11+
from llama_stack_api.openai_responses import (
12+
ApprovalFilter,
13+
)
1114
from llama_stack_api.openai_responses import (
1215
OpenAIResponseContentPartRefusal as ContentPartRefusal,
1316
)
@@ -734,10 +737,15 @@ async def get_mcp_tools(
734737
authorization = headers.pop("Authorization", None)
735738
tools.append(
736739
InputToolMCP(
737-
type="mcp",
738740
server_label=mcp_server.name,
739741
server_url=mcp_server.url,
740-
require_approval="never",
742+
require_approval=(
743+
mcp_server.require_approval
744+
if isinstance(mcp_server.require_approval, str)
745+
else ApprovalFilter.model_validate(
746+
mcp_server.require_approval.model_dump()
747+
)
748+
),
741749
headers=headers or None,
742750
authorization=authorization,
743751
)

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)