Move MCP tool naming mode into manager (#21576)

## Why

The `non_prefixed_mcp_tool_names` feature should be applied where MCP
tools become model-visible, not by remapping names later in core.
Keeping the decision in `McpConnectionManager` construction makes
`ToolInfo` the single shaped view that spec building, deferred tool
search, routing, and unavailable-tool placeholders can consume directly.

This also preserves the existing external behavior while the feature is
off, and keeps the feature-on behavior for code mode and hooks explicit
at the manager boundary.

## What Changed

- Add `McpToolNameMode` to `codex-mcp` and flow it through `McpConfig`
into `McpConnectionManager::new`.
- Normalize MCP `ToolInfo` names in the manager using either
legacy-prefixed namespaces or non-prefixed namespaces; the legacy path
adds `mcp__` without restoring the old trailing namespace suffix.
- Remove the core-side MCP name remapping path so specs, tool search,
session resolution, and unavailable-tool placeholder construction use
the manager-provided `ToolName` values directly.
- Keep code mode flattening on the `__` namespace separator.
- Preserve hook compatibility by giving non-prefixed MCP hook names
legacy `mcp__...` matcher aliases.
- Add/adjust integration and unit coverage for non-prefixed code-mode
behavior, hook matching with the feature on and off, and manager-level
legacy prefixing.

## Testing

- `cargo test -p codex-mcp --lib`
- `cargo test -p codex-core --lib tools::spec::tests -- --nocapture`
- `cargo test -p codex-core --lib mcp_tools -- --nocapture`
- `cargo test -p codex-core --lib mcp_tool_exposure -- --nocapture`
- `cargo test -p codex-core --test all mcp_tool -- --nocapture`
- `cargo test -p codex-core --test all search_tool -- --nocapture`
- `cargo test -p codex-core --test all hooks_mcp -- --nocapture`
- `cargo test -p codex-core --test all
code_mode_uses_non_prefixed_mcp_tool_names_when_feature_enabled --
--nocapture`
- `cargo test -p codex-tools`
- `cargo test -p codex-features`
This commit is contained in:
pakrym-oai authored and GitHub committed 2026-05-26 08:21:15 -07:00
1 parent b637fd26aa
commit ff7513cd83
30 files changed
+611 -146

No files matched your search

+62 -3
View File
@@ -200,7 +200,11 @@ async fn run_code_mode_turn_with_rmcp_model(
code: &str,
model: &'static str,
) -> Result<(TestCodex, ResponseMock)> {
run_code_mode_turn_with_rmcp_config(server, prompt, code, model, /*code_mode_only*/ false).await
run_code_mode_turn_with_rmcp_config(
server, prompt, code, model, /*code_mode_only*/ false,
/*non_prefixed_mcp_tool_names*/ false,
)
.await
}
async fn run_code_mode_turn_with_rmcp_mode(
@@ -209,8 +213,15 @@ async fn run_code_mode_turn_with_rmcp_mode(
code: &str,
code_mode_only: bool,
) -> Result<(TestCodex, ResponseMock)> {
run_code_mode_turn_with_rmcp_config(server, prompt, code, "test-gpt-5.1-codex", code_mode_only)
.await
run_code_mode_turn_with_rmcp_config(
server,
prompt,
code,
"test-gpt-5.1-codex",
code_mode_only,
/*non_prefixed_mcp_tool_names*/ false,
)
.await
}
async fn run_code_mode_turn_with_rmcp_config(
@@ -219,6 +230,7 @@ async fn run_code_mode_turn_with_rmcp_config(
code: &str,
model: &'static str,
code_mode_only: bool,
non_prefixed_mcp_tool_names: bool,
) -> Result<(TestCodex, ResponseMock)> {
let rmcp_test_server_bin = stdio_server_bin()?;
let mut builder = test_codex().with_model(model).with_config(move |config| {
@@ -227,6 +239,9 @@ async fn run_code_mode_turn_with_rmcp_config(
} else {
config.features.enable(Feature::CodeMode)
};
if non_prefixed_mcp_tool_names {
let _ = config.features.enable(Feature::NonPrefixedMcpToolNames);
}
let mut servers = config.mcp_servers.get().clone();
servers.insert(
@@ -2598,6 +2613,50 @@ contentLength=0"
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn code_mode_uses_non_prefixed_mcp_tool_names_when_feature_enabled() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = responses::start_mock_server().await;
let code = r#"
const result = await tools.rmcp__echo({ message: "ping" });
text(JSON.stringify({
hasNonPrefixedEcho: typeof tools.rmcp__echo === "function",
hasPrefixedEcho: typeof tools.mcp__rmcp__echo === "function",
echo: result.structuredContent?.echo ?? "missing",
}));
"#;
let (_test, second_mock) = run_code_mode_turn_with_rmcp_config(
&server,
"use exec to inspect non-prefixed MCP names",
code,
"test-gpt-5.1-codex",
/*code_mode_only*/ false,
/*non_prefixed_mcp_tool_names*/ true,
)
.await?;
let req = second_mock.single_request();
let (output, success) = custom_tool_output_body_and_success(&req, "call-1");
assert_ne!(
success,
Some(false),
"exec non-prefixed rmcp access failed unexpectedly: {output}"
);
let parsed: Value = serde_json::from_str(&output)?;
assert_eq!(
parsed,
serde_json::json!({
"hasNonPrefixedEcho": true,
"hasPrefixedEcho": false,
"echo": "ECHOING: ping",
})
);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn code_mode_exposes_namespaced_mcp_tools_on_global_tools_object() -> Result<()> {
skip_if_no_network!(Ok(()));
+98 -28
View File
@@ -9,6 +9,7 @@ use codex_config::types::AppToolApproval;
use codex_config::types::McpServerConfig;
use codex_config::types::McpServerTransportConfig;
use codex_core::config::Config;
use codex_features::Feature;
use core_test_support::hooks::trust_discovered_hooks;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
@@ -26,11 +27,18 @@ use serde_json::Value;
use serde_json::json;
const RMCP_SERVER: &str = "rmcp";
const RMCP_NAMESPACE: &str = "mcp__rmcp__";
const RMCP_PREFIXED_NAMESPACE: &str = "mcp__rmcp";
const RMCP_UNPREFIXED_NAMESPACE: &str = "rmcp";
const RMCP_ECHO_TOOL_NAME: &str = "mcp__rmcp__echo";
const RMCP_HOOK_MATCHER: &str = "mcp__rmcp__.*";
const RMCP_HOOK_MATCHER: &str = RMCP_ECHO_TOOL_NAME;
const RMCP_ECHO_MESSAGE: &str = "hook e2e ping";
fn enable_mcp_tool_name_features(config: &mut Config, prefix_mcp_tool_names: bool) {
if !prefix_mcp_tool_names {
let _ = config.features.enable(Feature::NonPrefixedMcpToolNames);
}
}
fn write_pre_tool_use_hook(home: &Path, reason: &str) -> Result<()> {
let script_path = home.join("pre_tool_use_hook.py");
let log_path = home.join("pre_tool_use_hook_log.jsonl");
@@ -207,13 +215,35 @@ fn enable_hooks_and_rmcp_server(
config: &mut Config,
rmcp_test_server_bin: String,
approval_mode: AppToolApproval,
prefix_mcp_tool_names: bool,
) {
trust_discovered_hooks(config);
enable_mcp_tool_name_features(config, prefix_mcp_tool_names);
insert_rmcp_test_server(config, rmcp_test_server_bin, approval_mode);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn pre_tool_use_blocks_mcp_tool_before_execution() -> Result<()> {
async fn pre_tool_use_blocks_mcp_tool_before_execution_with_legacy_prefixed_names() -> Result<()> {
pre_tool_use_blocks_mcp_tool_before_execution(
/*prefix_mcp_tool_names*/ true,
RMCP_PREFIXED_NAMESPACE,
)
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn pre_tool_use_blocks_mcp_tool_before_execution_with_non_prefixed_names() -> Result<()> {
pre_tool_use_blocks_mcp_tool_before_execution(
/*prefix_mcp_tool_names*/ false,
RMCP_UNPREFIXED_NAMESPACE,
)
.await
}
async fn pre_tool_use_blocks_mcp_tool_before_execution(
prefix_mcp_tool_names: bool,
mcp_namespace: &'static str,
) -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
@@ -224,7 +254,7 @@ async fn pre_tool_use_blocks_mcp_tool_before_execution() -> Result<()> {
vec![
sse(vec![
ev_response_created("resp-1"),
ev_function_call_with_namespace(call_id, RMCP_NAMESPACE, "echo", &arguments),
ev_function_call_with_namespace(call_id, mcp_namespace, "echo", &arguments),
ev_completed("resp-1"),
]),
sse(vec![
@@ -245,7 +275,12 @@ async fn pre_tool_use_blocks_mcp_tool_before_execution() -> Result<()> {
}
})
.with_config(move |config| {
enable_hooks_and_rmcp_server(config, rmcp_test_server_bin, AppToolApproval::Approve);
enable_hooks_and_rmcp_server(
config,
rmcp_test_server_bin,
AppToolApproval::Approve,
prefix_mcp_tool_names,
);
})
.build(&server)
.await?;
@@ -256,10 +291,9 @@ async fn pre_tool_use_blocks_mcp_tool_before_execution() -> Result<()> {
let requests = responses.requests();
assert_eq!(requests.len(), 2);
let output_item = requests[1].function_call_output(call_id);
let output = output_item
.get("output")
.and_then(Value::as_str)
.expect("blocked MCP tool output string");
let Some(output) = output_item.get("output").and_then(Value::as_str) else {
panic!("blocked MCP tool output should be a string: {output_item:?}");
};
assert!(
output.contains(&format!(
"Tool call blocked by PreToolUse hook: {block_reason}. Tool: {RMCP_ECHO_TOOL_NAME}"
@@ -283,9 +317,12 @@ async fn pre_tool_use_blocks_mcp_tool_before_execution() -> Result<()> {
"tool_input": { "message": RMCP_ECHO_MESSAGE },
})
);
let transcript_path = hook_inputs[0]["transcript_path"]
.as_str()
.expect("pre tool use hook transcript_path");
let Some(transcript_path) = hook_inputs[0]["transcript_path"].as_str() else {
panic!(
"pre tool use hook transcript_path should be a string: {:?}",
hook_inputs[0]["transcript_path"]
);
};
assert!(
Path::new(transcript_path).exists(),
"pre tool use hook transcript_path should be materialized on disk",
@@ -306,7 +343,7 @@ async fn pre_tool_use_rewrites_mcp_tool_before_execution() -> Result<()> {
&server,
sse(vec![
ev_response_created("resp-1"),
ev_function_call_with_namespace(call_id, RMCP_NAMESPACE, "echo", &arguments),
ev_function_call_with_namespace(call_id, RMCP_PREFIXED_NAMESPACE, "echo", &arguments),
ev_completed("resp-1"),
]),
)
@@ -329,7 +366,12 @@ async fn pre_tool_use_rewrites_mcp_tool_before_execution() -> Result<()> {
}
})
.with_config(move |config| {
enable_hooks_and_rmcp_server(config, rmcp_test_server_bin, AppToolApproval::Approve);
enable_hooks_and_rmcp_server(
config,
rmcp_test_server_bin,
AppToolApproval::Approve,
/*prefix_mcp_tool_names*/ true,
);
})
.build(&server)
.await?;
@@ -339,10 +381,9 @@ async fn pre_tool_use_rewrites_mcp_tool_before_execution() -> Result<()> {
let final_request = final_mock.single_request();
let output_item = final_request.function_call_output(call_id);
let output = output_item
.get("output")
.and_then(Value::as_str)
.expect("MCP tool output string");
let Some(output) = output_item.get("output").and_then(Value::as_str) else {
panic!("MCP tool output should be a string: {output_item:?}");
};
assert!(
output.contains(&format!("ECHOING: {rewritten_message}")),
"MCP tool should execute the rewritten input",
@@ -365,7 +406,29 @@ async fn pre_tool_use_rewrites_mcp_tool_before_execution() -> Result<()> {
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_tool_use_records_mcp_tool_payload_and_context() -> Result<()> {
async fn post_tool_use_records_mcp_tool_payload_and_context_with_legacy_prefixed_names()
-> Result<()> {
post_tool_use_records_mcp_tool_payload_and_context(
/*prefix_mcp_tool_names*/ true,
RMCP_PREFIXED_NAMESPACE,
)
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_tool_use_records_mcp_tool_payload_and_context_with_non_prefixed_names() -> Result<()>
{
post_tool_use_records_mcp_tool_payload_and_context(
/*prefix_mcp_tool_names*/ false,
RMCP_UNPREFIXED_NAMESPACE,
)
.await
}
async fn post_tool_use_records_mcp_tool_payload_and_context(
prefix_mcp_tool_names: bool,
mcp_namespace: &'static str,
) -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
@@ -375,7 +438,7 @@ async fn post_tool_use_records_mcp_tool_payload_and_context() -> Result<()> {
&server,
sse(vec![
ev_response_created("resp-1"),
ev_function_call_with_namespace(call_id, RMCP_NAMESPACE, "echo", &arguments),
ev_function_call_with_namespace(call_id, mcp_namespace, "echo", &arguments),
ev_completed("resp-1"),
]),
)
@@ -399,7 +462,12 @@ async fn post_tool_use_records_mcp_tool_payload_and_context() -> Result<()> {
}
})
.with_config(move |config| {
enable_hooks_and_rmcp_server(config, rmcp_test_server_bin, AppToolApproval::Approve);
enable_hooks_and_rmcp_server(
config,
rmcp_test_server_bin,
AppToolApproval::Approve,
prefix_mcp_tool_names,
);
})
.build(&server)
.await?;
@@ -415,10 +483,9 @@ async fn post_tool_use_records_mcp_tool_payload_and_context() -> Result<()> {
"follow-up request should include MCP post tool use additional context",
);
let output_item = final_request.function_call_output(call_id);
let output = output_item
.get("output")
.and_then(Value::as_str)
.expect("MCP tool output string");
let Some(output) = output_item.get("output").and_then(Value::as_str) else {
panic!("MCP tool output should be a string: {output_item:?}");
};
assert!(
output.contains(&format!("ECHOING: {RMCP_ECHO_MESSAGE}")),
"MCP tool output should still reach the model",
@@ -449,9 +516,12 @@ async fn post_tool_use_records_mcp_tool_payload_and_context() -> Result<()> {
},
})
);
let transcript_path = hook_inputs[0]["transcript_path"]
.as_str()
.expect("post tool use hook transcript_path");
let Some(transcript_path) = hook_inputs[0]["transcript_path"].as_str() else {
panic!(
"post tool use hook transcript_path should be a string: {:?}",
hook_inputs[0]["transcript_path"]
);
};
assert!(
Path::new(transcript_path).exists(),
"post tool use hook transcript_path should be materialized on disk",
+1 -1
View File
@@ -340,7 +340,7 @@ async fn explicit_plugin_mentions_inject_plugin_guidance() -> Result<()> {
"expected plugin app tools to become visible for this turn: {request_tools:?}"
);
let echo_tool = request
.tool_by_name("mcp__sample__", "echo")
.tool_by_name("mcp__sample", "echo")
.expect("plugin MCP tool should be present");
let echo_description = echo_tool
.get("description")
+14 -14
View File
@@ -382,7 +382,7 @@ async fn call_cwd_tool(
server_name: &str,
call_id: &str,
) -> anyhow::Result<Value> {
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
mount_sse_once(
server,
responses::sse(vec![
@@ -467,7 +467,7 @@ async fn stdio_server_round_trip() -> anyhow::Result<()> {
let call_id = "call-123";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
let call_mock = mount_sse_once(
&server,
@@ -723,7 +723,7 @@ async fn stdio_mcp_tool_call_includes_sandbox_state_meta() -> anyhow::Result<()>
let call_id = "sandbox-meta-call";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
let call_mock = mount_sse_once(
&server,
@@ -817,7 +817,7 @@ async fn stdio_mcp_parallel_tool_calls_default_false_runs_serially() -> anyhow::
let first_call_id = "sync-serial-1";
let second_call_id = "sync-serial-2";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
let args = json!({ "sleep_after_ms": 100 }).to_string();
mount_sse_once(
@@ -930,7 +930,7 @@ async fn stdio_mcp_read_only_tool_calls_run_concurrently_without_server_opt_in()
let first_call_id = "sync-read-only-1";
let second_call_id = "sync-read-only-2";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
// The stdio MCP test server holds each sync call at this barrier until both
// calls arrive. A serial scheduler times out inside the server instead of
// returning the structured `{ "result": "ok" }` result asserted below.
@@ -1025,7 +1025,7 @@ async fn stdio_mcp_parallel_tool_calls_opt_in_runs_concurrently() -> anyhow::Res
let first_call_id = "sync-1";
let second_call_id = "sync-2";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
let args = json!({
"sleep_after_ms": 100,
"barrier": {
@@ -1110,7 +1110,7 @@ async fn stdio_image_responses_round_trip() -> anyhow::Result<()> {
let call_id = "img-1";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
// First stream: model decides to call the image tool.
mount_sse_once(
@@ -1247,7 +1247,7 @@ async fn stdio_image_responses_preserve_original_detail_metadata() -> anyhow::Re
let call_id = "img-original-detail-1";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
mount_sse_once(
&server,
@@ -1333,7 +1333,7 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re
let call_id = "img-text-only-1";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
let text_only_model_slug = "rmcp-text-only-model";
let models_mock = mount_models_once(
@@ -1480,7 +1480,7 @@ async fn stdio_server_propagates_whitelisted_env_vars() -> anyhow::Result<()> {
let call_id = "call-1234";
let server_name = "rmcp_whitelist";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
mount_sse_once(
&server,
@@ -1594,7 +1594,7 @@ async fn stdio_server_propagates_explicit_local_env_var_source() -> anyhow::Resu
let server = responses::start_mock_server().await;
let call_id = "call-local-source";
let server_name = "rmcp_local_source";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
let env_name = "MCP_TEST_LOCAL_SOURCE";
let expected_env_value = "propagated-explicit-local-source";
@@ -1687,7 +1687,7 @@ async fn remote_stdio_env_var_source_does_not_copy_local_env() -> anyhow::Result
let server = responses::start_mock_server().await;
let call_id = "call-remote-source";
let server_name = "rmcp_remote_source";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
let env_name = "MCP_TEST_REMOTE_SOURCE_ONLY";
mount_sse_once(
@@ -1859,7 +1859,7 @@ async fn streamable_http_tool_call_round_trip() -> anyhow::Result<()> {
let call_id = "call-456";
let server_name = "rmcp_http";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
mount_sse_once(
&server,
@@ -2026,7 +2026,7 @@ async fn streamable_http_with_oauth_round_trip_impl() -> anyhow::Result<()> {
let call_id = "call-789";
let server_name = "rmcp_http_oauth";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
mount_sse_once(
&server,
+7 -7
View File
@@ -1059,13 +1059,13 @@ async fn tool_search_indexes_only_enabled_non_app_mcp_tools() -> Result<()> {
"non-app MCP tools should be hidden before search in large-search mode: {first_request_tools:?}"
);
assert!(
!first_request_tools.iter().any(|name| name == "mcp__rmcp__"),
!first_request_tools.iter().any(|name| name == "mcp__rmcp"),
"non-app MCP namespace should be hidden before search in large-search mode: {first_request_tools:?}"
);
let echo_tools = tool_search_output_tools(&requests[1], echo_call_id);
let echo_output = json!({ "tools": echo_tools });
let rmcp_echo_tool = namespace_child_tool(&echo_output, "mcp__rmcp__", "echo")
let rmcp_echo_tool = namespace_child_tool(&echo_output, "mcp__rmcp", "echo")
.expect("tool_search should return rmcp echo as a namespace child tool");
assert_eq!(
rmcp_echo_tool.get("type").and_then(Value::as_str),
@@ -1075,7 +1075,7 @@ async fn tool_search_indexes_only_enabled_non_app_mcp_tools() -> Result<()> {
let image_tools = tool_search_output_tools(&requests[1], image_call_id);
let found_rmcp_image_tool = image_tools
.iter()
.filter(|tool| tool.get("name").and_then(Value::as_str) == Some("mcp__rmcp__"))
.filter(|tool| tool.get("name").and_then(Value::as_str) == Some("mcp__rmcp"))
.flat_map(|namespace| namespace.get("tools").and_then(Value::as_array))
.flatten()
.any(|tool| tool.get("name").and_then(Value::as_str).is_some());
@@ -1111,7 +1111,7 @@ async fn tool_search_surfaced_mcp_tool_errors_are_returned_to_model() -> Result<
]),
sse(vec![
ev_response_created("resp-2"),
ev_function_call_with_namespace(tool_call_id, "mcp__rmcp__", "echo", "{}"),
ev_function_call_with_namespace(tool_call_id, "mcp__rmcp", "echo", "{}"),
ev_completed("resp-2"),
]),
sse(vec![
@@ -1213,12 +1213,12 @@ async fn tool_search_surfaced_mcp_tool_errors_are_returned_to_model() -> Result<
"first request should advertise tool_search: {first_request_tools:?}"
);
assert!(
!first_request_tools.iter().any(|name| name == "mcp__rmcp__"),
!first_request_tools.iter().any(|name| name == "mcp__rmcp"),
"deferred rmcp namespace should not be directly exposed before search: {first_request_tools:?}"
);
assert!(
tool_search_output_has_namespace_child(&requests[1], search_call_id, "mcp__rmcp__", "echo"),
tool_search_output_has_namespace_child(&requests[1], search_call_id, "mcp__rmcp", "echo"),
"tool_search should return the rmcp echo tool"
);
@@ -1324,7 +1324,7 @@ async fn tool_search_uses_non_app_mcp_server_instructions_as_namespace_descripti
let tools = tool_search_output_tools(&requests[1], search_call_id);
let rmcp_namespace = tools
.iter()
.find(|tool| tool.get("name").and_then(Value::as_str) == Some("mcp__rmcp__"))
.find(|tool| tool.get("name").and_then(Value::as_str) == Some("mcp__rmcp"))
.expect("tool_search should return the rmcp namespace");
assert_eq!(
rmcp_namespace.get("description").and_then(Value::as_str),
+1 -1
View File
@@ -330,7 +330,7 @@ async fn mcp_call_marks_thread_memory_mode_polluted_when_configured() -> Result<
let server = start_mock_server().await;
let call_id = "call-123";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
mount_sse_once(
&server,
responses::sse(vec![
+3 -3
View File
@@ -343,7 +343,7 @@ async fn mcp_tool_call_output_exceeds_limit_truncated_for_model() -> Result<()>
let call_id = "rmcp-truncated";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
// Build a very large message to exceed 10KiB once serialized.
let large_msg = "long-message-with-newlines-".repeat(6000);
@@ -446,7 +446,7 @@ async fn mcp_image_output_preserves_image_and_no_text_summary() -> Result<()> {
let call_id = "rmcp-image-no-trunc";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
mount_sse_once(
&server,
@@ -733,7 +733,7 @@ async fn mcp_tool_call_output_not_truncated_with_custom_limit() -> Result<()> {
let call_id = "rmcp-untruncated";
let server_name = "rmcp";
let namespace = format!("mcp__{server_name}__");
let namespace = format!("mcp__{server_name}");
let large_msg = "a".repeat(80_000);
let args_json = serde_json::json!({ "message": large_msg });