Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/utils/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from fastapi import HTTPException
from llama_stack_api import OpenAIResponseObject
from llama_stack_api.openai_responses import ApprovalFilter
from llama_stack_api.openai_responses import (
OpenAIResponseContentPartRefusal as ContentPartRefusal,
)
Expand Down Expand Up @@ -732,12 +733,20 @@ async def get_mcp_tools(
continue

authorization = headers.pop("Authorization", None)

require_approval = (
mcp_server.require_approval
if isinstance(mcp_server.require_approval, str)
else ApprovalFilter(
always=mcp_server.require_approval.always or None,
never=mcp_server.require_approval.never or None,
)
)
tools.append(
InputToolMCP(
type="mcp",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set up by the model automatically

server_label=mcp_server.name,
server_url=mcp_server.url,
require_approval="never",
require_approval=require_approval,
headers=headers or None,
authorization=authorization,
)
Expand Down
47 changes: 46 additions & 1 deletion tests/unit/utils/test_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
AllowedToolsFilter,
OpenAIResponseInputToolChoiceAllowedTools,
)
from llama_stack_api.openai_responses import ApprovalFilter as LlamaStackApprovalFilter
from llama_stack_api.openai_responses import (
OpenAIResponseInputTool as InputTool,
)
Expand Down Expand Up @@ -60,7 +61,7 @@

import constants
from models.api.requests import QueryRequest
from models.config import ByokRag, ModelContextProtocolServer
from models.config import ApprovalFilter, ByokRag, ModelContextProtocolServer
from utils.responses import (
_build_chunk_attributes,
_merge_tools,
Expand Down Expand Up @@ -405,6 +406,50 @@ async def test_get_mcp_tools_without_auth(self, mocker: MockerFixture) -> None:
assert tools_no_auth[0].server_label == "fs"
assert tools_no_auth[0].server_url == "http://localhost:3000"
assert tools_no_auth[0].headers is None
assert all(tool.require_approval == "never" for tool in tools_no_auth)

@pytest.mark.asyncio
async def test_get_mcp_tools_require_approval_always(
self, mocker: MockerFixture
) -> None:
"""Test get_mcp_tools passes require_approval='always' from config."""
server = ModelContextProtocolServer(
name="strict",
url="http://localhost:3000",
provider_id="mcp",
require_approval="always",
)
mock_config = mocker.Mock()
mock_config.mcp_servers = [server]
mocker.patch("utils.responses.configuration", mock_config)

tools = await get_mcp_tools(token=None)
assert len(tools) == 1
assert tools[0].require_approval == "always"

@pytest.mark.asyncio
async def test_get_mcp_tools_require_approval_filter(
self, mocker: MockerFixture
) -> None:
"""Test get_mcp_tools translates ApprovalFilter to Llama Stack format."""
server = ModelContextProtocolServer(
name="github",
url="http://localhost:3000",
provider_id="mcp",
require_approval=ApprovalFilter(
always=["create_issue"],
never=["list_repos"],
),
)
mock_config = mocker.Mock()
mock_config.mcp_servers = [server]
mocker.patch("utils.responses.configuration", mock_config)

tools = await get_mcp_tools(token=None)
assert len(tools) == 1
assert isinstance(tools[0].require_approval, LlamaStackApprovalFilter)
assert tools[0].require_approval.always == ["create_issue"]
assert tools[0].require_approval.never == ["list_repos"]

@pytest.mark.asyncio
async def test_get_mcp_tools_with_kubernetes_auth(
Expand Down
Loading