Skip to content
Merged
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
36 changes: 36 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,40 @@ Note: this is not a real model, just an enumeration of all action names.



## ApprovalFilter


Granular approval control for specific MCP tools.

Attributes:
always: Tool names that always require human approval before execution.
never: Tool names that never require approval (pre-approved).


| Field | Type | Description |
|-------|------|-------------|
| always | array | List of tool names that always require human approval |
| never | array | List of tool names that never require approval |


## ApprovalsConfiguration


Configuration for human-in-the-loop approvals.

Attributes:
approval_timeout_seconds: How long approval requests remain pending
before expiring.
approval_retention_days: How long to retain decided approvals for audit
purposes before cleanup.


| Field | Type | Description |
|-------|------|-------------|
| approval_timeout_seconds | integer | Seconds before pending approval requests expire |
| approval_retention_days | integer | Days to retain decided approvals before cleanup |


## AuthenticationConfiguration


Expand Down Expand Up @@ -205,6 +239,7 @@ Global service configuration.
| inference | | One LLM provider and one its model might be selected as default ones. When no provider+model pair is specified in REST API calls (query endpoints), the default provider and model are used. |
| conversation_cache | | |
| compaction | | Controls when conversation history is summarized to keep the model's input below the context window limit. Disabled by default — when disabled, requests that exceed the window continue to surface as HTTP 413. |
| approvals | | Settings for human-in-the-loop approval of MCP tool invocations |
| byok_rag | array | BYOK RAG configuration. This configuration can be used to reconfigure Llama Stack through its run.yaml configuration file |
| a2a_state | | Configuration for A2A protocol persistent state storage. |
| quota_handlers | | Quota handlers configuration |
Expand Down Expand Up @@ -419,6 +454,7 @@ Useful resources:
| url | string | URL of the MCP server |
| authorization_headers | object | Headers to send to the MCP server. The map contains the header name and the path to a file containing the header value (secret). There are 3 special cases: 1. Usage of the kubernetes token in the header. To specify this use a string 'kubernetes' instead of the file path. 2. Usage of the client-provided token in the header. To specify this use a string 'client' instead of the file path. 3. Usage of the oauth token in the header. To specify this use a string 'oauth' instead of the file path. |
| headers | array | List of HTTP header names to automatically forward from the incoming request to this MCP server. Headers listed here are extracted from the original client request and included when calling the MCP server. This is useful when infrastructure components (e.g. API gateways) inject headers that MCP servers need, such as x-rh-identity in HCC. Header matching is case-insensitive. These headers are additive with authorization_headers and MCP-HEADERS. |
| require_approval | string or object | When to require human approval for MCP tool invocations. 'always' requires approval for all tools, 'never' auto-approves all tools (default), or use an ApprovalFilter for granular per-tool control. |
| timeout | integer | Timeout in seconds for requests to the MCP server. If not specified, the default timeout from Llama Stack will be used. Note: This field is reserved for future use when Llama Stack adds timeout support. |


Expand Down
72 changes: 70 additions & 2 deletions docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -11400,7 +11400,7 @@
"title": "AllowedToolsFilter",
"description": "Filter configuration for restricting which MCP tools can be used.\n\n:param tool_names: (Optional) List of specific tool names that are allowed"
},
"ApprovalFilter": {
"ApprovalFilter-Input": {
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.

This rename is caused by name conflict between our configuration model and Llama Stack's model that is part of InputToolMCP model.

"properties": {
"always": {
"anyOf": [
Expand Down Expand Up @@ -11435,6 +11435,52 @@
"title": "ApprovalFilter",
"description": "Filter configuration for MCP tool approval requirements.\n\n:param always: (Optional) List of tool names that always require approval\n:param never: (Optional) List of tool names that never require approval"
},
"ApprovalFilter-Output": {
"properties": {
"always": {
"items": {
"type": "string"
},
"type": "array",
"title": "Always require approval",
"description": "List of tool names that always require human approval"
},
"never": {
"items": {
"type": "string"
},
"type": "array",
"title": "Never require approval",
"description": "List of tool names that never require approval"
}
},
"additionalProperties": false,
"type": "object",
"title": "ApprovalFilter",
"description": "Granular approval control for specific MCP tools.\n\nAttributes:\n always: Tool names that always require human approval before execution.\n never: Tool names that never require approval (pre-approved)."
},
"ApprovalsConfiguration": {
"properties": {
"approval_timeout_seconds": {
"type": "integer",
"exclusiveMinimum": 0.0,
"title": "Approval timeout",
"description": "Seconds before pending approval requests expire",
"default": 300
},
"approval_retention_days": {
"type": "integer",
"exclusiveMinimum": 0.0,
"title": "Retention period",
"description": "Days to retain decided approvals before cleanup",
"default": 30
}
},
"additionalProperties": false,
"type": "object",
"title": "ApprovalsConfiguration",
"description": "Configuration for human-in-the-loop approvals.\n\nAttributes:\n approval_timeout_seconds: How long approval requests remain pending\n before expiring.\n approval_retention_days: How long to retain decided approvals for audit\n purposes before cleanup."
},
"Attachment": {
"properties": {
"attachment_type": {
Expand Down Expand Up @@ -12016,6 +12062,11 @@
"title": "Conversation compaction configuration",
"description": "Controls when conversation history is summarized to keep the model's input below the context window limit. Disabled by default \u2014 when disabled, requests that exceed the window continue to surface as HTTP 413."
},
"approvals": {
"$ref": "#/components/schemas/ApprovalsConfiguration",
"title": "Approvals configuration",
"description": "Settings for human-in-the-loop approval of MCP tool invocations"
},
"byok_rag": {
"items": {
"$ref": "#/components/schemas/ByokRag"
Expand Down Expand Up @@ -14264,6 +14315,23 @@
"title": "Propagated headers",
"description": "List of HTTP header names to automatically forward from the incoming request to this MCP server. Headers listed here are extracted from the original client request and included when calling the MCP server. This is useful when infrastructure components (e.g. API gateways) inject headers that MCP servers need, such as x-rh-identity in HCC. Header matching is case-insensitive. These headers are additive with authorization_headers and MCP-HEADERS."
},
"require_approval": {
"anyOf": [
{
"type": "string",
"enum": [
"always",
"never"
]
},
{
"$ref": "#/components/schemas/ApprovalFilter-Output"
}
],
"title": "Approval requirement",
"description": "When to require human approval for tool invocations. 'always' requires approval for all tools, 'never' auto-approves, or use ApprovalFilter for granular control.",
"default": "never"
},
"timeout": {
"anyOf": [
{
Expand Down Expand Up @@ -15269,7 +15337,7 @@
"const": "never"
},
{
"$ref": "#/components/schemas/ApprovalFilter"
"$ref": "#/components/schemas/ApprovalFilter-Input"
}
],
"title": "Require Approval",
Expand Down
15 changes: 15 additions & 0 deletions src/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from log import get_logger
from models.config import (
A2AStateConfiguration,
ApprovalsConfiguration,
AuthenticationConfiguration,
AuthorizationConfiguration,
AzureEntraIdConfiguration,
Expand Down Expand Up @@ -459,6 +460,20 @@ def rag(self) -> "RagConfiguration":
raise LogicError("logic error: configuration is not loaded")
return self._configuration.rag

@property
def approvals_configuration(self) -> ApprovalsConfiguration:
"""Return human-in-the-loop approvals configuration.

Returns:
ApprovalsConfiguration: Settings for MCP tool approval workflow.

Raises:
LogicError: If the configuration has not been loaded.
"""
if self._configuration is None:
raise LogicError("logic error: configuration is not loaded")
return self._configuration.approvals

@property
def okp(self) -> "OkpConfiguration":
"""Return OKP configuration."""
Expand Down
75 changes: 75 additions & 0 deletions src/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,65 @@ def check_service_configuration(self) -> Self:
return self


class ApprovalFilter(ConfigurationBase):
"""Granular approval control for specific MCP tools.

Attributes:
always: Tool names that always require human approval before execution.
never: Tool names that never require approval (pre-approved).
"""

always: list[str] = Field(
default_factory=list,
title="Always require approval",
description="List of tool names that always require human approval",
)
never: list[str] = Field(
default_factory=list,
title="Never require approval",
description="List of tool names that never require approval",
)

@model_validator(mode="after")
def validate_no_overlap(self) -> Self:
"""Ensure no tool appears in both always and never lists.

Raises:
ValueError: If any tool name is present in both lists.

Returns:
Self: The validated model instance.
"""
overlap = set(self.always) & set(self.never)
if overlap:
raise ValueError(
f"Tools cannot be in both always and never lists: {overlap}"
)
return self


class ApprovalsConfiguration(ConfigurationBase):
"""Configuration for human-in-the-loop approvals.

Attributes:
approval_timeout_seconds: How long approval requests remain pending
before expiring.
approval_retention_days: How long to retain decided approvals for audit
purposes before cleanup.
"""

approval_timeout_seconds: PositiveInt = Field(
default=300,
title="Approval timeout",
description="Seconds before pending approval requests expire",
)
approval_retention_days: PositiveInt = Field(
default=30,
title="Retention period",
description="Days to retain decided approvals before cleanup",
)


class ModelContextProtocolServer(ConfigurationBase):
"""Model context protocol server configuration.

Expand Down Expand Up @@ -555,6 +614,16 @@ def validate_headers(cls, value: list[str]) -> list[str]:
seen.add(lower)
return value

require_approval: Literal["always", "never"] | ApprovalFilter = Field(
default="never",
title="Approval requirement",
description=(
"When to require human approval for tool invocations. "
"'always' requires approval for all tools, 'never' auto-approves, "
"or use ApprovalFilter for granular control."
),
)

timeout: Optional[PositiveInt] = Field(
default=None,
title="Request timeout",
Expand Down Expand Up @@ -2051,6 +2120,12 @@ class Configuration(ConfigurationBase):
"window continue to surface as HTTP 413.",
)

approvals: ApprovalsConfiguration = Field(
default_factory=ApprovalsConfiguration,
title="Approvals configuration",
description="Settings for human-in-the-loop approval of MCP tool invocations",
)

byok_rag: list[ByokRag] = Field(
default_factory=list,
title="BYOK RAG configuration",
Expand Down
3 changes: 3 additions & 0 deletions tests/unit/models/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Unit tests for models defined in config.py.
## [test_a2a_state_configuration.py](test_a2a_state_configuration.py)
Unit tests for A2AStateConfiguration.

## [test_approvals_configuration.py](test_approvals_configuration.py)
Unit tests for human-in-the-loop approvals configuration models.
Comment thread
asimurka marked this conversation as resolved.

## [test_authentication_configuration.py](test_authentication_configuration.py)
Unit tests for AuthenticationConfiguration model.

Expand Down
Loading
Loading