mirror of
https://github.com/openai/codex.git
synced 2026-11-09 20:50:33 +00:00
Expose deferred MCP servers in resource listings (#60423)
## Why Aggregate resource listings omitted dormant MCP servers, leaving callers without their names for explicit queries. ## What changed Include a sorted `deferredServers` list in aggregate `list_mcp_resources` and `list_mcp_resource_templates` results alongside resources from ready servers. Apply the listing filters to deferred servers and omit the field when empty. Discovery leaves dormant servers unstarted; callers can query them by passing `server` explicitly. ## Testing Extend the resource discovery scenario for direct calls and Code Mode to verify deferred server names, confirm discovery leaves the server unstarted, and then list and read resources by server name. GitOrigin-RevId: f95b8beb64bb1329f4ae3245e8c375614fcb3360
This commit is contained in:
@@ -31,6 +31,7 @@ use serde_json::Value as JsonValue;
|
||||
use crate::McpConfig;
|
||||
use crate::McpServerPermissions;
|
||||
use crate::binding_clients::McpBindingClients;
|
||||
use crate::binding_clients::McpResourceListing;
|
||||
use crate::client_tool_catalog::ToolCatalogSnapshot;
|
||||
use crate::connection_manager::McpConnectionSet;
|
||||
use crate::rmcp_client::ManagedClient;
|
||||
@@ -52,7 +53,7 @@ impl McpBinding {
|
||||
pub fn empty(config: Arc<McpConfig>) -> Self {
|
||||
Self::new(
|
||||
Arc::new(McpConnectionSet::empty(config.prefix_mcp_tool_names)),
|
||||
Arc::new(McpBindingClients::new(HashMap::new())),
|
||||
Arc::new(McpBindingClients::default()),
|
||||
config,
|
||||
/*plugins_available*/ false,
|
||||
Vec::new(),
|
||||
@@ -130,7 +131,7 @@ impl McpBinding {
|
||||
pub async fn list_all_resources(
|
||||
&self,
|
||||
include_server: impl Fn(&str) -> bool,
|
||||
) -> HashMap<String, Vec<Resource>> {
|
||||
) -> McpResourceListing<Resource> {
|
||||
self.clients.list_all_resources(include_server).await
|
||||
}
|
||||
|
||||
@@ -154,7 +155,7 @@ impl McpBinding {
|
||||
pub async fn list_all_resource_templates(
|
||||
&self,
|
||||
include_server: impl Fn(&str) -> bool,
|
||||
) -> HashMap<String, Vec<ResourceTemplate>> {
|
||||
) -> McpResourceListing<ResourceTemplate> {
|
||||
self.clients
|
||||
.list_all_resource_templates(|server| {
|
||||
include_server(server) && !self.connections.skips_resource_template_listing(server)
|
||||
|
||||
@@ -17,14 +17,37 @@ use tracing::warn;
|
||||
use crate::pagination::collect_paginated;
|
||||
use crate::rmcp_client::ManagedClient;
|
||||
|
||||
/// The ready clients captured for one model step.
|
||||
/// Items listed by ready servers and names of deferred servers available to query explicitly.
|
||||
pub struct McpResourceListing<T> {
|
||||
pub by_server: HashMap<String, Vec<T>>,
|
||||
pub deferred_servers: Vec<String>,
|
||||
}
|
||||
|
||||
/// The ready clients and dormant server names captured for one model step.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct McpBindingClients {
|
||||
clients: HashMap<String, Arc<ManagedClient>>,
|
||||
deferred_servers: Vec<String>,
|
||||
}
|
||||
|
||||
impl McpBindingClients {
|
||||
pub(crate) fn new(clients: HashMap<String, Arc<ManagedClient>>) -> Self {
|
||||
Self { clients }
|
||||
pub(crate) fn new(
|
||||
clients: HashMap<String, Arc<ManagedClient>>,
|
||||
mut deferred_servers: Vec<String>,
|
||||
) -> Self {
|
||||
deferred_servers.sort_unstable();
|
||||
Self {
|
||||
clients,
|
||||
deferred_servers,
|
||||
}
|
||||
}
|
||||
|
||||
fn deferred_servers(&self, include_server: impl Fn(&str) -> bool) -> Vec<String> {
|
||||
self.deferred_servers
|
||||
.iter()
|
||||
.filter(|name| include_server(name))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn client(&self, server: &str) -> Option<Arc<ManagedClient>> {
|
||||
@@ -77,7 +100,7 @@ impl McpBindingClients {
|
||||
pub(crate) async fn list_all_resources(
|
||||
&self,
|
||||
include_server: impl Fn(&str) -> bool,
|
||||
) -> HashMap<String, Vec<Resource>> {
|
||||
) -> McpResourceListing<Resource> {
|
||||
let mut join_set = JoinSet::new();
|
||||
for (server_name, managed) in self
|
||||
.clients
|
||||
@@ -99,13 +122,16 @@ impl McpBindingClients {
|
||||
(server_name, resources)
|
||||
});
|
||||
}
|
||||
collect_resource_results(&mut join_set, "resources").await
|
||||
McpResourceListing {
|
||||
by_server: collect_resource_results(&mut join_set, "resources").await,
|
||||
deferred_servers: self.deferred_servers(include_server),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn list_all_resource_templates(
|
||||
&self,
|
||||
include_server: impl Fn(&str) -> bool,
|
||||
) -> HashMap<String, Vec<ResourceTemplate>> {
|
||||
) -> McpResourceListing<ResourceTemplate> {
|
||||
let mut join_set = JoinSet::new();
|
||||
for (server_name, managed) in self
|
||||
.clients
|
||||
@@ -127,7 +153,10 @@ impl McpBindingClients {
|
||||
(server_name, templates)
|
||||
});
|
||||
}
|
||||
collect_resource_results(&mut join_set, "resource templates").await
|
||||
McpResourceListing {
|
||||
by_server: collect_resource_results(&mut join_set, "resource templates").await,
|
||||
deferred_servers: self.deferred_servers(include_server),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,10 +107,10 @@ async fn test_step(
|
||||
client_supports_network_policy_meta_capability: false,
|
||||
codex_apps_tools_cache_context: None,
|
||||
});
|
||||
let clients = Arc::new(McpBindingClients::new(HashMap::from([(
|
||||
SERVER_NAME.to_string(),
|
||||
Arc::clone(&managed_client),
|
||||
)])));
|
||||
let clients = Arc::new(McpBindingClients::new(
|
||||
HashMap::from([(SERVER_NAME.to_string(), Arc::clone(&managed_client))]),
|
||||
Vec::new(),
|
||||
));
|
||||
let connections = Arc::new(McpConnectionSet::empty(/*prefix_mcp_tool_names*/ true));
|
||||
let mut config = crate::mcp::tests::test_mcp_config(std::env::temp_dir());
|
||||
config.host_cwd = config.host_cwd.join(label);
|
||||
|
||||
@@ -547,11 +547,21 @@ impl McpConnectionSet {
|
||||
call,
|
||||
);
|
||||
}
|
||||
let deferred_servers = self
|
||||
.servers
|
||||
.iter()
|
||||
.filter(|(_, view)| {
|
||||
view.connection.startup_is_dormant()
|
||||
&& !view.connection.client.cancel_token.is_cancelled()
|
||||
})
|
||||
.map(|(name, _)| name.clone())
|
||||
.collect();
|
||||
let clients = Arc::new(McpBindingClients::new(
|
||||
clients
|
||||
.into_iter()
|
||||
.map(|(server_name, (client, _))| (server_name, client))
|
||||
.collect(),
|
||||
deferred_servers,
|
||||
));
|
||||
McpBinding::new(
|
||||
Arc::clone(self),
|
||||
|
||||
@@ -2341,7 +2341,10 @@ async fn resource_template_listings_skip_only_hosted_apps() -> anyhow::Result<()
|
||||
expected
|
||||
);
|
||||
let all = binding.list_all_resource_templates(|_| true).await;
|
||||
assert_eq!(all.get(name), (!expected.is_empty()).then_some(&expected));
|
||||
assert_eq!(
|
||||
all.by_server.get(name),
|
||||
(!expected.is_empty()).then_some(&expected)
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -6139,11 +6142,11 @@ async fn reconciliation_reuses_connection_without_relisting_regular_tools() -> a
|
||||
.expect("refresh must use the reconciled timeout");
|
||||
assert_eq!(failed.tools(), old_step.tools());
|
||||
assert_eq!(
|
||||
failed.list_all_resources(|_| true).await,
|
||||
failed.list_all_resources(|_| true).await.by_server,
|
||||
HashMap::from([("docs".to_string(), vec![])])
|
||||
);
|
||||
assert_eq!(
|
||||
failed.list_all_resource_templates(|_| true).await,
|
||||
failed.list_all_resource_templates(|_| true).await.by_server,
|
||||
HashMap::from([("docs".to_string(), vec![])])
|
||||
);
|
||||
let retry_started = tokio::time::Instant::now();
|
||||
|
||||
@@ -514,10 +514,10 @@ async fn shared_startup_survives_first_chat_cancellation_and_rejects_released_bi
|
||||
/*selected_plugin_server*/ false,
|
||||
)
|
||||
.unwrap();
|
||||
let bindings = McpBindingClients::new(HashMap::from([(
|
||||
"test_server".to_string(),
|
||||
Arc::new(client.clone()),
|
||||
)]));
|
||||
let bindings = McpBindingClients::new(
|
||||
HashMap::from([("test_server".to_string(), Arc::new(client.clone()))]),
|
||||
Vec::new(),
|
||||
);
|
||||
server.block_listing.store(true, Ordering::Release);
|
||||
let waiting = async {
|
||||
tokio::join!(
|
||||
|
||||
@@ -4,6 +4,7 @@ pub use binding::PreparedMcpCall;
|
||||
pub use binding::SEARCH_CONNECTOR;
|
||||
pub use binding::SearchDiscovery;
|
||||
pub use binding::is_model_search_tool;
|
||||
pub use binding_clients::McpResourceListing;
|
||||
pub use client_capabilities::client_mcp_extensions;
|
||||
pub use client_tool_catalog::CodexAppsToolSnapshot;
|
||||
pub use codex_rmcp_client::McpProtocolMode;
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;
|
||||
use codex_mcp::McpResourceListing;
|
||||
use codex_protocol::items::McpToolCallError;
|
||||
use codex_protocol::items::McpToolCallItem;
|
||||
use codex_protocol::items::McpToolCallStatus;
|
||||
@@ -197,6 +198,8 @@ struct ListResourcesPayload {
|
||||
resources: Vec<ResourceWithServer<Resource>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
next_cursor: Option<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
deferred_servers: Vec<String>,
|
||||
}
|
||||
|
||||
impl ListResourcesPayload {
|
||||
@@ -205,14 +208,16 @@ impl ListResourcesPayload {
|
||||
resources: ResourceWithServer::from_server(&server, result.resources),
|
||||
server: Some(server),
|
||||
next_cursor: result.next_cursor,
|
||||
deferred_servers: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_all_servers(resources_by_server: HashMap<String, Vec<Resource>>) -> Self {
|
||||
fn from_all_servers(listing: McpResourceListing<Resource>) -> Self {
|
||||
Self {
|
||||
server: None,
|
||||
resources: ResourceWithServer::from_all_servers(resources_by_server),
|
||||
resources: ResourceWithServer::from_all_servers(listing.by_server),
|
||||
next_cursor: None,
|
||||
deferred_servers: listing.deferred_servers,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,6 +230,8 @@ struct ListResourceTemplatesPayload {
|
||||
resource_templates: Vec<ResourceWithServer<ResourceTemplate>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
next_cursor: Option<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
deferred_servers: Vec<String>,
|
||||
}
|
||||
|
||||
impl ListResourceTemplatesPayload {
|
||||
@@ -233,14 +240,16 @@ impl ListResourceTemplatesPayload {
|
||||
resource_templates: ResourceWithServer::from_server(&server, result.resource_templates),
|
||||
server: Some(server),
|
||||
next_cursor: result.next_cursor,
|
||||
deferred_servers: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_all_servers(templates_by_server: HashMap<String, Vec<ResourceTemplate>>) -> Self {
|
||||
fn from_all_servers(listing: McpResourceListing<ResourceTemplate>) -> Self {
|
||||
Self {
|
||||
server: None,
|
||||
resource_templates: ResourceWithServer::from_all_servers(templates_by_server),
|
||||
resource_templates: ResourceWithServer::from_all_servers(listing.by_server),
|
||||
next_cursor: None,
|
||||
deferred_servers: listing.deferred_servers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,10 @@ fn list_resources_payload_from_all_servers_is_sorted() {
|
||||
vec![resource("memo://a-1", "a-1"), resource("memo://a-2", "a-2")],
|
||||
);
|
||||
|
||||
let payload = ListResourcesPayload::from_all_servers(map);
|
||||
let payload = ListResourcesPayload::from_all_servers(McpResourceListing {
|
||||
by_server: map,
|
||||
deferred_servers: Vec::new(),
|
||||
});
|
||||
let value = serde_json::to_value(&payload).expect("serialize payload");
|
||||
let uris: Vec<String> = value["resources"]
|
||||
.as_array()
|
||||
@@ -132,7 +135,10 @@ fn list_resource_templates_payload_from_all_servers_is_sorted() {
|
||||
vec![template("memo://alpha/{id}", "alpha")],
|
||||
);
|
||||
|
||||
let payload = ListResourceTemplatesPayload::from_all_servers(templates_by_server);
|
||||
let payload = ListResourceTemplatesPayload::from_all_servers(McpResourceListing {
|
||||
by_server: templates_by_server,
|
||||
deferred_servers: Vec::new(),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
serde_json::to_value(payload).expect("serialize resource templates"),
|
||||
|
||||
@@ -5,9 +5,12 @@ use super::super::rmcp_client::remote_aware_stdio_server_bin;
|
||||
use anyhow::Result;
|
||||
use codex_core::ConfigRefreshOutcome;
|
||||
use codex_features::Feature;
|
||||
use codex_protocol::mcp::McpServerConnectionStatus;
|
||||
use codex_protocol::openai_models::ToolMode;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::McpStartupStatus;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::protocol::SubAgentSource;
|
||||
use core_test_support::apps_test_server::AppsTestServer;
|
||||
use core_test_support::context_snapshot;
|
||||
use core_test_support::context_snapshot::ContextSnapshotOptions;
|
||||
@@ -63,7 +66,7 @@ async fn mcp_resource_messages(tool_mode: ToolMode) -> Result<()> {
|
||||
);
|
||||
}
|
||||
let catalog = serde_json::from_value(json!(messages))?;
|
||||
let test = test_codex()
|
||||
let mut test = test_codex()
|
||||
.with_config(move |config| {
|
||||
super::configure_scenario_catalog(config);
|
||||
config.code_mode.disable_in_process_fallback = true;
|
||||
@@ -96,34 +99,70 @@ async fn mcp_resource_messages(tool_mode: ToolMode) -> Result<()> {
|
||||
.build_with_auto_env(&server)
|
||||
.await?;
|
||||
wait_for_mcp_server(&test.codex, "resources").await?;
|
||||
let mut events = vec![responses::ev_response_created("resources-response")];
|
||||
events.extend(if tool_mode == ToolMode::CodeModeOnly {
|
||||
let script = calls
|
||||
.iter()
|
||||
.map(|(name, arguments)| format!("text(await tools.{name}({arguments}));"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
vec![responses::ev_custom_tool_call(
|
||||
"resources-call",
|
||||
"exec",
|
||||
&script,
|
||||
)]
|
||||
} else {
|
||||
calls
|
||||
.iter()
|
||||
.map(|(name, arguments)| responses::ev_function_call(name, name, arguments))
|
||||
.collect()
|
||||
});
|
||||
events.push(responses::ev_completed("resources-response"));
|
||||
let mut options = test.start_thread_options().await;
|
||||
options.session_source = Some(SessionSource::SubAgent(SubAgentSource::Other(
|
||||
"resource-discovery".to_string(),
|
||||
)));
|
||||
test.codex = test.thread_manager.start_thread(options).await?.thread;
|
||||
let resource_calls = |id: &str, calls: &[(&str, &str)]| {
|
||||
let mut events = vec![responses::ev_response_created(id)];
|
||||
if tool_mode == ToolMode::CodeModeOnly {
|
||||
let script = calls
|
||||
.iter()
|
||||
.map(|(name, args)| format!("text(await tools.{name}({args}));"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
events.push(responses::ev_custom_tool_call(id, "exec", &script));
|
||||
} else {
|
||||
events.extend(calls.iter().map(|(name, args)| {
|
||||
responses::ev_function_call(&format!("{id}-{name}"), name, args)
|
||||
}));
|
||||
}
|
||||
events.push(responses::ev_completed(id));
|
||||
responses::sse(events)
|
||||
};
|
||||
let broad = [
|
||||
("list_mcp_resources", "{}"),
|
||||
("list_mcp_resource_templates", "{}"),
|
||||
];
|
||||
let mock = responses::mount_sse_sequence(
|
||||
&server,
|
||||
vec![responses::sse(events), responses::sse_completed("done")],
|
||||
vec![
|
||||
resource_calls("broad", &broad),
|
||||
responses::sse_completed("broad-done"),
|
||||
resource_calls("resources-call", &calls),
|
||||
responses::sse_completed("done"),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
test.submit_turn("List the available resources and templates, then read the example note.")
|
||||
test.submit_turn("List available MCP resources and templates.")
|
||||
.await?;
|
||||
let (config, _) = test.codex.current_mcp_config_and_runtime_context().await;
|
||||
assert_eq!(
|
||||
test.codex
|
||||
.mcp_connection_statuses(&config)
|
||||
.await
|
||||
.get("resources"),
|
||||
Some(&McpServerConnectionStatus::NotStarted)
|
||||
);
|
||||
test.submit_turn("Query the deferred server by name and read its example note.")
|
||||
.await?;
|
||||
let requests = mock.requests();
|
||||
assert_eq!(requests.len(), 2);
|
||||
assert_eq!(requests.len(), 4);
|
||||
if tool_mode == ToolMode::Direct {
|
||||
for (tool, key) in [
|
||||
("list_mcp_resources", "resources"),
|
||||
("list_mcp_resource_templates", "resourceTemplates"),
|
||||
] {
|
||||
let output = requests[1]
|
||||
.function_call_output_text(&format!("broad-{tool}"))
|
||||
.expect("aggregate result");
|
||||
assert_eq!(
|
||||
serde_json::from_str::<serde_json::Value>(&output)?,
|
||||
json!({key: [], "deferredServers": ["resources"]})
|
||||
);
|
||||
}
|
||||
}
|
||||
let body = requests[0].body_json();
|
||||
for (name, message) in &messages {
|
||||
if tool_mode == ToolMode::CodeModeOnly {
|
||||
@@ -149,17 +188,17 @@ async fn mcp_resource_messages(tool_mode: ToolMode) -> Result<()> {
|
||||
);
|
||||
}
|
||||
}
|
||||
assert!(requests[1].body_contains_text("Example Note"));
|
||||
assert!(requests[1].body_contains_text("memo://codex/{slug}"));
|
||||
assert!(requests[3].body_contains_text("Example Note"));
|
||||
assert!(requests[3].body_contains_text("memo://codex/{slug}"));
|
||||
assert!(
|
||||
requests[1]
|
||||
requests[3]
|
||||
.body_contains_text("This is a sample MCP resource served by the rmcp test server.")
|
||||
);
|
||||
if tool_mode == ToolMode::CodeModeOnly {
|
||||
insta::assert_snapshot!(
|
||||
"mcp_resource_messages",
|
||||
context_snapshot::format_request_history_snapshot(
|
||||
"Catalog resource descriptions and parameter guidance accompany discovery and reading through Code Mode.",
|
||||
"Broad discovery names the dormant server. Calling it by name returns its resources and templates through Code Mode.",
|
||||
&requests,
|
||||
&ContextSnapshotOptions::default().include_request_settings(),
|
||||
)
|
||||
|
||||
+19
-30
@@ -1,8 +1,8 @@
|
||||
---
|
||||
source: core/tests/suite/scenarios_mcp_resource_messages.rs
|
||||
expression: "context_snapshot::format_request_history_snapshot(\"Catalog resource descriptions and parameter guidance accompany discovery and reading through Code Mode.\",\n&requests, &ContextSnapshotOptions::default().include_request_settings(),)"
|
||||
expression: "context_snapshot::format_request_history_snapshot(\"Broad discovery names the dormant server. Calling it by name returns its resources and templates through Code Mode.\",\n&requests, &ContextSnapshotOptions::default().include_request_settings(),)"
|
||||
---
|
||||
Scenario: Catalog resource descriptions and parameter guidance accompany discovery and reading through Code Mode.
|
||||
Scenario: Broad discovery names the dormant server. Calling it by name returns its resources and templates through Code Mode.
|
||||
|
||||
## Window 1
|
||||
Settings:
|
||||
@@ -15,11 +15,10 @@ Settings:
|
||||
stream: true
|
||||
text: {"verbosity":"low"}
|
||||
tool_choice: "auto"
|
||||
tools (7; hash=83df59b286cda93b):
|
||||
tools (6; hash=2fa2390674c481b0):
|
||||
- custom/exec: Run JavaScript code to orchestrate/compose tool calls - Evaluates the provided JavaScript code in...; hash=2051847a9a5d026d
|
||||
- function/wait: Waits on a yielded `exec` cell and returns new output or completion. - Use `wait` only after `exe...; args=[cell_id, max_tokens, terminate, yield_time_ms]; hash=6692d8f6c55be489
|
||||
- function/request_user_input: Request user input for one to three short questions and wait for the response. This tool is only ...; args=[questions]; hash=64a729b25c424fc6
|
||||
- function/request_user_input_async: Ask the user one or more questions during ongoing work. Use this tool only to request missing inf...; args=[questions]; hash=d6d74f33db330162
|
||||
- namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317
|
||||
- function/sleep
|
||||
- namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63
|
||||
@@ -74,27 +73,7 @@ Settings:
|
||||
Deferred tool namespaces:
|
||||
- mcp__resources: Use these tools to exercise the rmcp test server.
|
||||
</tools>
|
||||
02:message/developer:
|
||||
<multi_agent_role>You are `/root`, the primary agent in a team of agents collaborating to fulfill the user's goals.
|
||||
|
||||
At the start of your turn, you are the active agent.
|
||||
You can spawn sub-agents to handle subtasks, and those sub-agents can spawn their own sub-agents.
|
||||
All agents in the team, including the agents that you can assign tasks to, are equally intelligent and capable, and have access to the same set of tools.
|
||||
|
||||
You can use `spawn_agent` to create a new agent, `followup_task` to give an existing agent a new task an...message to a running agent without triggering a turn. [hash=7d40ff8b58977202]
|
||||
`send_message` calls may be read by a human, so ensure they are legible. Always put proper spaces between words and/or numbers.
|
||||
<OMITTED 17 LINES; ~246 TOKENS; hash=780031e2df907944>
|
||||
- All agents use the same current working directory.
|
||||
- As a result, edits made by one agent are immediately visible to all other agents.
|
||||
|
||||
When calling `wait_agent`, prefer longer waits (minutes) to avoid busy polling.
|
||||
|
||||
There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you.
|
||||
|
||||
Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`.</multi_agent_role> [hash=d5928243dc643bcc]
|
||||
03:message/developer:
|
||||
<multi_agent_mode>Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work.</multi_agent_mode> [hash=2b6e037067b4390c]
|
||||
04:message/user:
|
||||
02:message/user:
|
||||
<environment_context>
|
||||
<cwd><CWD></cwd>
|
||||
<shell><HOST_SHELL></shell>
|
||||
@@ -102,14 +81,24 @@ Settings:
|
||||
<timezone><HOST_TIMEZONE></timezone>
|
||||
<filesystem><workspace_roots><root><WORKSPACE_ROOT 1></root></workspace_roots><permission_profile type...e="unrestricted" /></permission_profile></filesystem> [hash=0592734c170299c1]
|
||||
</environment_context>
|
||||
05:message/user:
|
||||
List the available resources and templates, then read the example note.
|
||||
06:configuration_update:reasoning={"effort":"low"}
|
||||
03:message/user:
|
||||
List available MCP resources and templates.
|
||||
04:configuration_update:reasoning={"effort":"low"}
|
||||
-- request 2 (turn) --
|
||||
07:custom_tool_call/exec:text(await tools.list_mcp_resources({"server":"resources"}));
|
||||
05:custom_tool_call/exec:text(await tools.list_mcp_resources({}));
|
||||
text(await tools.list_mcp_resource_templates({}));
|
||||
06:custom_tool_call_output:Script completed
|
||||
Wall time <DURATION> seconds
|
||||
Output:
|
||||
| {"resources":[],"deferredServers":["resources"]} | {"resourceTemplates":[],"deferredServers":["resources"]}
|
||||
-- request 3 (turn) --
|
||||
07:message/user:
|
||||
Query the deferred server by name and read its example note.
|
||||
-- request 4 (turn) --
|
||||
08:custom_tool_call/exec:text(await tools.list_mcp_resources({"server":"resources"}));
|
||||
text(await tools.list_mcp_resource_templates({"server":"resources"}));
|
||||
text(await tools.read_mcp_resource({"server":"resources","uri":"memo://codex/example-note"}));
|
||||
08:custom_tool_call_output:Script completed
|
||||
09:custom_tool_call_output:Script completed
|
||||
Wall time <DURATION> seconds
|
||||
Output:
|
||||
| {"server":"resources","resources":[{"server":"resources","uri":"memo://codex/example-note","name":"examp...ed for integration tests.","mimeType":"text/plain"}]} [hash=75d187638a7af2d4] | {"server":"resources","resourceTemplates":[{"server":"resources","uriTemplate":"memo://codex/{slug}","na... resources used in tests.","mimeType":"text/plain"}]} [hash=b1c7082eb114fc9b] | {"server":"resources","uri":"memo://codex/example-note","contents":[{"uri":"memo://codex/example-note","...mple MCP resource served by the rmcp test server."}]} [hash=45385fc57640b555]
|
||||
|
||||
Reference in New Issue
Block a user