Support Always Allow for MCP app messages

This commit is contained in:
Leo Shimonaka
2026-05-05 16:54:39 -07:00
parent d7de4dd3ac
commit dd6239a46d
30 changed files with 434 additions and 2 deletions
@@ -6089,6 +6089,16 @@
"boolean",
"null"
]
},
"mcp_app_message_approval_mode": {
"anyOf": [
{
"$ref": "#/definitions/v2/McpAppMessageApprovalMode"
},
{
"type": "null"
}
]
}
},
"type": "object"
@@ -10866,6 +10876,13 @@
"title": "MarketplaceUpgradeResponse",
"type": "object"
},
"McpAppMessageApprovalMode": {
"enum": [
"auto",
"approve_in_thread"
],
"type": "string"
},
"McpAuthStatus": {
"enum": [
"unsupported",
@@ -10990,6 +11007,62 @@
"title": "McpServerOauthLoginResponse",
"type": "object"
},
"McpServerProvider": {
"oneOf": [
{
"properties": {
"type": {
"enum": [
"codex-apps"
],
"title": "CodexAppsMcpServerProviderType",
"type": "string"
}
},
"required": [
"type"
],
"title": "CodexAppsMcpServerProvider",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"mcp"
],
"title": "McpMcpServerProviderType",
"type": "string"
}
},
"required": [
"type"
],
"title": "McpMcpServerProvider",
"type": "object"
},
{
"properties": {
"pluginId": {
"type": "string"
},
"type": {
"enum": [
"plugin-mcp"
],
"title": "PluginMcpMcpServerProviderType",
"type": "string"
}
},
"required": [
"pluginId",
"type"
],
"title": "PluginMcpMcpServerProvider",
"type": "object"
}
]
},
"McpServerRefreshResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "McpServerRefreshResponse",
@@ -11012,6 +11085,9 @@
"name": {
"type": "string"
},
"provider": {
"$ref": "#/definitions/v2/McpServerProvider"
},
"resourceTemplates": {
"items": {
"$ref": "#/definitions/v2/ResourceTemplate"
@@ -11034,6 +11110,7 @@
"required": [
"authStatus",
"name",
"provider",
"resourceTemplates",
"resources",
"tools"
@@ -668,6 +668,16 @@
"boolean",
"null"
]
},
"mcp_app_message_approval_mode": {
"anyOf": [
{
"$ref": "#/definitions/McpAppMessageApprovalMode"
},
{
"type": "null"
}
]
}
},
"type": "object"
@@ -7477,6 +7487,13 @@
"title": "MarketplaceUpgradeResponse",
"type": "object"
},
"McpAppMessageApprovalMode": {
"enum": [
"auto",
"approve_in_thread"
],
"type": "string"
},
"McpAuthStatus": {
"enum": [
"unsupported",
@@ -7601,6 +7618,62 @@
"title": "McpServerOauthLoginResponse",
"type": "object"
},
"McpServerProvider": {
"oneOf": [
{
"properties": {
"type": {
"enum": [
"codex-apps"
],
"title": "CodexAppsMcpServerProviderType",
"type": "string"
}
},
"required": [
"type"
],
"title": "CodexAppsMcpServerProvider",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"mcp"
],
"title": "McpMcpServerProviderType",
"type": "string"
}
},
"required": [
"type"
],
"title": "McpMcpServerProvider",
"type": "object"
},
{
"properties": {
"pluginId": {
"type": "string"
},
"type": {
"enum": [
"plugin-mcp"
],
"title": "PluginMcpMcpServerProviderType",
"type": "string"
}
},
"required": [
"pluginId",
"type"
],
"title": "PluginMcpMcpServerProvider",
"type": "object"
}
]
},
"McpServerRefreshResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "McpServerRefreshResponse",
@@ -7623,6 +7696,9 @@
"name": {
"type": "string"
},
"provider": {
"$ref": "#/definitions/McpServerProvider"
},
"resourceTemplates": {
"items": {
"$ref": "#/definitions/ResourceTemplate"
@@ -7645,6 +7721,7 @@
"required": [
"authStatus",
"name",
"provider",
"resourceTemplates",
"resources",
"tools"
@@ -89,6 +89,16 @@
"boolean",
"null"
]
},
"mcp_app_message_approval_mode": {
"anyOf": [
{
"$ref": "#/definitions/McpAppMessageApprovalMode"
},
{
"type": "null"
}
]
}
},
"type": "object"
@@ -585,6 +595,13 @@
],
"type": "string"
},
"McpAppMessageApprovalMode": {
"enum": [
"auto",
"approve_in_thread"
],
"type": "string"
},
"ProfileV2": {
"additionalProperties": true,
"properties": {
@@ -10,6 +10,62 @@
],
"type": "string"
},
"McpServerProvider": {
"oneOf": [
{
"properties": {
"type": {
"enum": [
"codex-apps"
],
"title": "CodexAppsMcpServerProviderType",
"type": "string"
}
},
"required": [
"type"
],
"title": "CodexAppsMcpServerProvider",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"mcp"
],
"title": "McpMcpServerProviderType",
"type": "string"
}
},
"required": [
"type"
],
"title": "McpMcpServerProvider",
"type": "object"
},
{
"properties": {
"pluginId": {
"type": "string"
},
"type": {
"enum": [
"plugin-mcp"
],
"title": "PluginMcpMcpServerProviderType",
"type": "string"
}
},
"required": [
"pluginId",
"type"
],
"title": "PluginMcpMcpServerProvider",
"type": "object"
}
]
},
"McpServerStatus": {
"properties": {
"authStatus": {
@@ -18,6 +74,9 @@
"name": {
"type": "string"
},
"provider": {
"$ref": "#/definitions/McpServerProvider"
},
"resourceTemplates": {
"items": {
"$ref": "#/definitions/ResourceTemplate"
@@ -40,6 +99,7 @@
"required": [
"authStatus",
"name",
"provider",
"resourceTemplates",
"resources",
"tools"
@@ -2,5 +2,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AppToolApproval } from "./AppToolApproval";
import type { McpAppMessageApprovalMode } from "./McpAppMessageApprovalMode";
export type AppToolsConfig = { [key in string]?: { enabled: boolean | null, approval_mode: AppToolApproval | null, } };
export type AppToolsConfig = { [key in string]?: { enabled: boolean | null, approval_mode: AppToolApproval | null, mcp_app_message_approval_mode: McpAppMessageApprovalMode | null, } };
@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type McpAppMessageApprovalMode = "auto" | "approve_in_thread";
@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type McpServerProvider = { "type": "codex-apps" } | { "type": "mcp" } | { "type": "plugin-mcp", pluginId: string, };
@@ -5,5 +5,6 @@ import type { Resource } from "../Resource";
import type { ResourceTemplate } from "../ResourceTemplate";
import type { Tool } from "../Tool";
import type { McpAuthStatus } from "./McpAuthStatus";
import type { McpServerProvider } from "./McpServerProvider";
export type McpServerStatus = { name: string, tools: { [key in string]?: Tool }, resources: Array<Resource>, resourceTemplates: Array<ResourceTemplate>, authStatus: McpAuthStatus, };
export type McpServerStatus = { name: string, provider: McpServerProvider, tools: { [key in string]?: Tool }, resources: Array<Resource>, resourceTemplates: Array<ResourceTemplate>, authStatus: McpAuthStatus, };
@@ -191,6 +191,7 @@ export type { MarketplaceRemoveResponse } from "./MarketplaceRemoveResponse";
export type { MarketplaceUpgradeErrorInfo } from "./MarketplaceUpgradeErrorInfo";
export type { MarketplaceUpgradeParams } from "./MarketplaceUpgradeParams";
export type { MarketplaceUpgradeResponse } from "./MarketplaceUpgradeResponse";
export type { McpAppMessageApprovalMode } from "./McpAppMessageApprovalMode";
export type { McpAuthStatus } from "./McpAuthStatus";
export type { McpElicitationArrayType } from "./McpElicitationArrayType";
export type { McpElicitationBooleanSchema } from "./McpElicitationBooleanSchema";
@@ -223,6 +224,7 @@ export type { McpServerMigration } from "./McpServerMigration";
export type { McpServerOauthLoginCompletedNotification } from "./McpServerOauthLoginCompletedNotification";
export type { McpServerOauthLoginParams } from "./McpServerOauthLoginParams";
export type { McpServerOauthLoginResponse } from "./McpServerOauthLoginResponse";
export type { McpServerProvider } from "./McpServerProvider";
export type { McpServerRefreshResponse } from "./McpServerRefreshResponse";
export type { McpServerStartupState } from "./McpServerStartupState";
export type { McpServerStatus } from "./McpServerStatus";
@@ -166,6 +166,14 @@ pub enum AppToolApproval {
Approve,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/")]
pub enum McpAppMessageApprovalMode {
Auto,
ApproveInThread,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/")]
@@ -184,6 +192,7 @@ pub struct AppsDefaultConfig {
pub struct AppToolConfig {
pub enabled: Option<bool>,
pub approval_mode: Option<AppToolApproval>,
pub mcp_app_message_approval_mode: Option<McpAppMessageApprovalMode>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
@@ -46,11 +46,28 @@ pub enum McpServerStatusDetail {
ToolsAndAuthOnly,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(tag = "type")]
#[ts(tag = "type")]
#[ts(export_to = "v2/")]
pub enum McpServerProvider {
#[serde(rename = "codex-apps")]
#[ts(rename = "codex-apps")]
CodexApps,
#[serde(rename = "mcp")]
#[ts(rename = "mcp")]
Mcp,
#[serde(rename = "plugin-mcp", rename_all = "camelCase")]
#[ts(rename = "plugin-mcp", rename_all = "camelCase")]
PluginMcp { plugin_id: String },
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct McpServerStatus {
pub name: String,
pub provider: McpServerProvider,
pub tools: std::collections::HashMap<String, McpTool>,
pub resources: Vec<McpResource>,
pub resource_templates: Vec<McpResourceTemplate>,
@@ -234,6 +234,52 @@ async fn write_value_supports_custom_mcp_server_default_tool_approval_mode() ->
Ok(())
}
#[tokio::test]
async fn write_value_supports_custom_mcp_server_message_approval_mode() -> Result<()> {
let tmp = tempdir().expect("tempdir");
std::fs::write(
tmp.path().join(CONFIG_TOML_FILE),
"[mcp_servers.docs]\ncommand = \"docs-server\"\n",
)?;
let service = ConfigManager::without_managed_config_for_tests(tmp.path().to_path_buf());
service
.write_value(ConfigValueWriteParams {
file_path: Some(tmp.path().join(CONFIG_TOML_FILE).display().to_string()),
key_path: "mcp_servers.docs.tools".to_string(),
value: serde_json::json!({
"search": {
"mcp_app_message_approval_mode": "approve_in_thread",
},
}),
merge_strategy: MergeStrategy::Upsert,
expected_version: None,
})
.await
.expect("write mcp server tool mcp_app_message_approval_mode succeeds");
let read = service
.read(ConfigReadParams {
include_layers: false,
cwd: None,
})
.await
.expect("config read succeeds");
assert_eq!(
read.config
.additional
.get("mcp_servers")
.and_then(|servers| servers.get("docs"))
.and_then(|server| server.get("tools"))
.and_then(|tools| tools.get("search"))
.and_then(|tool| tool.get("mcp_app_message_approval_mode")),
Some(&serde_json::json!("approve_in_thread"))
);
Ok(())
}
#[tokio::test]
async fn read_includes_origins_and_layers() {
let tmp = tempdir().expect("tempdir");
@@ -92,6 +92,7 @@ use codex_app_server_protocol::McpResourceReadResponse;
use codex_app_server_protocol::McpServerOauthLoginCompletedNotification;
use codex_app_server_protocol::McpServerOauthLoginParams;
use codex_app_server_protocol::McpServerOauthLoginResponse;
use codex_app_server_protocol::McpServerProvider;
use codex_app_server_protocol::McpServerRefreshResponse;
use codex_app_server_protocol::McpServerStatus;
use codex_app_server_protocol::McpServerStatusDetail;
@@ -320,6 +321,7 @@ use codex_login::complete_device_code_login;
use codex_login::login_with_api_key;
use codex_login::request_device_code;
use codex_login::run_login_server;
use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;
use codex_mcp::McpRuntimeEnvironment;
use codex_mcp::McpServerStatusSnapshot;
use codex_mcp::McpSnapshotDetail;
@@ -318,6 +318,29 @@ impl McpRequestProcessor {
resource_templates,
auth_statuses,
} = snapshot;
let mut provider_by_server: HashMap<String, McpServerProvider> = config
.mcp_servers
.keys()
.cloned()
.map(|name| (name, McpServerProvider::Mcp))
.collect();
for plugin in &mcp_config.plugin_capability_summaries {
for name in &plugin.mcp_server_names {
if effective_servers.contains_key(name) {
provider_by_server.entry(name.clone()).or_insert_with(|| {
McpServerProvider::PluginMcp {
plugin_id: plugin.config_name.clone(),
}
});
}
}
}
if effective_servers.contains_key(CODEX_APPS_MCP_SERVER_NAME) {
provider_by_server.insert(
CODEX_APPS_MCP_SERVER_NAME.to_string(),
McpServerProvider::CodexApps,
);
}
let mut server_names: Vec<String> = config
.mcp_servers
@@ -357,6 +380,10 @@ impl McpRequestProcessor {
.iter()
.map(|name| McpServerStatus {
name: name.clone(),
provider: provider_by_server
.get(name)
.cloned()
.unwrap_or(McpServerProvider::Mcp),
tools: tools_by_server.get(name).cloned().unwrap_or_default(),
resources: resources.get(name).cloned().unwrap_or_default(),
resource_templates: resource_templates.get(name).cloned().unwrap_or_default(),
+1
View File
@@ -88,6 +88,7 @@ pub use marketplace_edit::remove_user_marketplace_config;
pub use mcp_edit::ConfigEditsBuilder;
pub use mcp_edit::load_global_mcp_servers;
pub use mcp_types::AppToolApproval;
pub use mcp_types::McpAppMessageApprovalMode;
pub use mcp_types::McpServerConfig;
pub use mcp_types::McpServerDisabledReason;
pub use mcp_types::McpServerEnvVar;
+7
View File
@@ -13,6 +13,7 @@ use toml_edit::value;
use crate::AppToolApproval;
use crate::CONFIG_TOML_FILE;
use crate::McpAppMessageApprovalMode;
use crate::McpServerConfig;
use crate::McpServerEnvVar;
use crate::McpServerTransportConfig;
@@ -232,6 +233,12 @@ fn serialize_mcp_server(config: &McpServerConfig) -> TomlItem {
AppToolApproval::Approve => "approve",
});
}
if let Some(approval_mode) = tool_config.mcp_app_message_approval_mode {
tool_entry["mcp_app_message_approval_mode"] = value(match approval_mode {
McpAppMessageApprovalMode::Auto => "auto",
McpAppMessageApprovalMode::ApproveInThread => "approve_in_thread",
});
}
tools.insert(name, TomlItem::Table(tool_entry));
}
entry.insert("tools", TomlItem::Table(tools));
+5
View File
@@ -39,12 +39,16 @@ async fn replace_mcp_servers_serializes_per_tool_approval_overrides() -> anyhow:
"search".to_string(),
McpServerToolConfig {
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: Some(
McpAppMessageApprovalMode::ApproveInThread,
),
},
),
(
"read".to_string(),
McpServerToolConfig {
approval_mode: Some(AppToolApproval::Prompt),
mcp_app_message_approval_mode: None,
},
),
]),
@@ -72,6 +76,7 @@ approval_mode = "prompt"
[mcp_servers.docs.tools.search]
approval_mode = "approve"
mcp_app_message_approval_mode = "approve_in_thread"
"#
);
+11
View File
@@ -22,6 +22,13 @@ pub enum AppToolApproval {
Approve,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum McpAppMessageApprovalMode {
Auto,
ApproveInThread,
}
/// Human-readable reason a configured MCP server was disabled after requirements
/// were applied.
///
@@ -54,6 +61,10 @@ pub struct McpServerToolConfig {
/// Approval mode for this tool.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub approval_mode: Option<AppToolApproval>,
/// Approval mode for MCP app messages sent by this tool.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mcp_app_message_approval_mode: Option<McpAppMessageApprovalMode>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]
+3
View File
@@ -320,6 +320,7 @@ fn deserialize_server_config_with_default_tool_approval_mode() {
[tools.search]
approval_mode = "prompt"
mcp_app_message_approval_mode = "approve_in_thread"
"#,
)
.expect("should deserialize default tool approval mode");
@@ -332,11 +333,13 @@ fn deserialize_server_config_with_default_tool_approval_mode() {
cfg.tools.get("search"),
Some(&McpServerToolConfig {
approval_mode: Some(AppToolApproval::Prompt),
mcp_app_message_approval_mode: Some(McpAppMessageApprovalMode::ApproveInThread),
})
);
let serialized = toml::to_string(&cfg).expect("should serialize MCP config");
assert!(serialized.contains("default_tools_approval_mode = \"approve\""));
assert!(serialized.contains("mcp_app_message_approval_mode = \"approve_in_thread\""));
let round_tripped: McpServerConfig =
toml::from_str(&serialized).expect("should deserialize serialized MCP config");
+5
View File
@@ -4,6 +4,7 @@
// definitions that do not contain business logic.
pub use crate::mcp_types::AppToolApproval;
pub use crate::mcp_types::McpAppMessageApprovalMode;
pub use crate::mcp_types::McpServerConfig;
pub use crate::mcp_types::McpServerDisabledReason;
pub use crate::mcp_types::McpServerEnvVar;
@@ -399,6 +400,10 @@ pub struct AppToolConfig {
/// Approval mode for this tool.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub approval_mode: Option<AppToolApproval>,
/// Approval mode for MCP app messages sent by resources returned from this tool.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mcp_app_message_approval_mode: Option<McpAppMessageApprovalMode>,
}
/// Tool settings for a single app.
@@ -325,6 +325,7 @@ approval_mode = "approve"
server.tools.get("search"),
Some(&McpServerToolConfig {
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: None,
})
);
}
+23
View File
@@ -161,6 +161,14 @@
"enabled": {
"description": "Whether this tool is enabled. `Some(true)` explicitly allows this tool.",
"type": "boolean"
},
"mcp_app_message_approval_mode": {
"allOf": [
{
"$ref": "#/definitions/McpAppMessageApprovalMode"
}
],
"description": "Approval mode for MCP app messages sent by resources returned from this tool."
}
},
"type": "object"
@@ -1148,6 +1156,13 @@
},
"type": "object"
},
"McpAppMessageApprovalMode": {
"enum": [
"auto",
"approve_in_thread"
],
"type": "string"
},
"McpServerEnvVar": {
"anyOf": [
{
@@ -1181,6 +1196,14 @@
}
],
"description": "Approval mode for this tool."
},
"mcp_app_message_approval_mode": {
"allOf": [
{
"$ref": "#/definitions/McpAppMessageApprovalMode"
}
],
"description": "Approval mode for MCP app messages sent by this tool."
}
},
"type": "object"
+2
View File
@@ -3787,6 +3787,7 @@ approval_mode = "approve"
server.tools.get("search"),
Some(&McpServerToolConfig {
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: None,
})
);
}
@@ -3830,6 +3831,7 @@ approval_mode = "approve"
tool,
&McpServerToolConfig {
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: None,
}
);
}
+7
View File
@@ -197,6 +197,7 @@ pub fn model_availability_nux_count_edits(shown_count: &HashMap<String, u32>) ->
// TODO(jif) move to a dedicated file
mod document_helpers {
use codex_config::types::AppToolApproval;
use codex_config::types::McpAppMessageApprovalMode;
use codex_config::types::McpServerConfig;
use codex_config::types::McpServerEnvVar;
use codex_config::types::McpServerToolConfig;
@@ -361,6 +362,12 @@ mod document_helpers {
AppToolApproval::Approve => "approve",
});
}
if let Some(approval_mode) = config.mcp_app_message_approval_mode {
entry["mcp_app_message_approval_mode"] = value(match approval_mode {
McpAppMessageApprovalMode::Auto => "auto",
McpAppMessageApprovalMode::ApproveInThread => "approve_in_thread",
});
}
TomlItem::Table(entry)
}
+3
View File
@@ -1,5 +1,6 @@
use super::*;
use codex_config::types::AppToolApproval;
use codex_config::types::McpAppMessageApprovalMode;
use codex_config::types::McpServerToolConfig;
use codex_config::types::McpServerTransportConfig;
use codex_config::types::SessionPickerViewMode;
@@ -1011,6 +1012,7 @@ fn blocking_replace_mcp_servers_serializes_tool_approval_overrides() {
"search".to_string(),
McpServerToolConfig {
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: Some(McpAppMessageApprovalMode::ApproveInThread),
},
)]),
},
@@ -1031,6 +1033,7 @@ default_tools_approval_mode = \"prompt\"
[mcp_servers.docs.tools.search]
approval_mode = \"approve\"
mcp_app_message_approval_mode = \"approve_in_thread\"
";
assert_eq!(raw, expected);
}
+2
View File
@@ -859,6 +859,7 @@ fn app_tool_policy_per_tool_enabled_true_overrides_app_level_disable_flags() {
AppToolConfig {
enabled: Some(true),
approval_mode: None,
mcp_app_message_approval_mode: None,
},
)]),
}),
@@ -1009,6 +1010,7 @@ fn app_tool_policy_matches_prefix_stripped_tool_name_for_tool_config() {
AppToolConfig {
enabled: Some(true),
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: None,
},
)]),
}),
+6
View File
@@ -1527,6 +1527,7 @@ async fn persist_codex_app_tool_approval_writes_tool_override() {
AppToolConfig {
enabled: None,
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: None,
},
)]),
}),
@@ -1567,6 +1568,7 @@ async fn persist_custom_mcp_tool_approval_writes_tool_override() {
tool,
&McpServerToolConfig {
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: None,
}
);
assert!(contents.contains("[mcp_servers.docs.tools.search]"));
@@ -1736,6 +1738,7 @@ async fn maybe_persist_mcp_tool_approval_reloads_session_config() {
&AppToolConfig {
enabled: None,
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: None,
}
);
assert_eq!(mcp_tool_approval_is_remembered(&session, &key).await, true);
@@ -1778,6 +1781,7 @@ async fn maybe_persist_mcp_tool_approval_reloads_session_config_for_custom_serve
tool,
&McpServerToolConfig {
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: None,
}
);
assert_eq!(mcp_tool_approval_is_remembered(&session, &key).await, true);
@@ -1827,6 +1831,7 @@ enabled = true
tool,
&McpServerToolConfig {
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: None,
}
);
assert!(contents.contains(r#"[plugins."sample@test".mcp_servers.sample.tools.search]"#));
@@ -1883,6 +1888,7 @@ async fn maybe_persist_mcp_tool_approval_writes_project_config_for_project_serve
tool,
&McpServerToolConfig {
approval_mode: Some(AppToolApproval::Approve),
mcp_app_message_approval_mode: None,
}
);
assert!(contents.contains("[mcp_servers.docs.tools.search]"));
@@ -12,6 +12,8 @@ use codex_app_server_protocol::MarketplaceRemoveParams;
use codex_app_server_protocol::MarketplaceRemoveResponse;
use codex_app_server_protocol::MarketplaceUpgradeParams;
use codex_app_server_protocol::MarketplaceUpgradeResponse;
#[cfg(test)]
use codex_app_server_protocol::McpServerProvider;
use codex_app_server_protocol::RequestId;
@@ -1044,6 +1046,7 @@ mod tests {
let statuses = vec![
McpServerStatus {
name: "docs".to_string(),
provider: McpServerProvider::Mcp,
tools: HashMap::from([(
"list".to_string(),
Tool {
@@ -1063,6 +1066,7 @@ mod tests {
},
McpServerStatus {
name: "disabled".to_string(),
provider: McpServerProvider::Mcp,
tools: HashMap::new(),
resources: Vec::new(),
resource_templates: Vec::new(),
+2
View File
@@ -37,6 +37,7 @@ use codex_app_server_protocol::FileChangeRequestApprovalParams;
use codex_app_server_protocol::FileUpdateChange;
use codex_app_server_protocol::ItemStartedNotification;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::McpServerProvider;
use codex_app_server_protocol::McpServerStartupState;
use codex_app_server_protocol::McpServerStatusUpdatedNotification;
use codex_app_server_protocol::NetworkApprovalContext as AppServerNetworkApprovalContext;
@@ -111,6 +112,7 @@ async fn handle_mcp_inventory_result_clears_committed_loading_cell() {
app.handle_mcp_inventory_result(
Ok(vec![McpServerStatus {
name: "docs".to_string(),
provider: McpServerProvider::Mcp,
tools: HashMap::new(),
resources: Vec::new(),
resource_templates: Vec::new(),
+4
View File
@@ -48,6 +48,8 @@ use crate::wrapping::adaptive_wrap_lines;
use base64::Engine;
use codex_app_server_protocol::AskForApproval;
use codex_app_server_protocol::McpAuthStatus;
#[cfg(test)]
use codex_app_server_protocol::McpServerProvider;
use codex_app_server_protocol::McpServerStatus;
use codex_app_server_protocol::McpServerStatusDetail;
use codex_app_server_protocol::PermissionProfile as AppServerPermissionProfile;
@@ -4198,6 +4200,7 @@ mod tests {
let statuses = vec![McpServerStatus {
name: "plugin_docs".to_string(),
provider: McpServerProvider::Mcp,
tools: HashMap::from([(
"lookup".to_string(),
Tool {
@@ -4239,6 +4242,7 @@ mod tests {
let statuses = vec![McpServerStatus {
name: "plugin_docs".to_string(),
provider: McpServerProvider::Mcp,
tools: HashMap::from([(
"lookup".to_string(),
Tool {