[apps] Add tool call meta. (#14647)

- [x] Add resource_uri and other things to _meta to shortcut resource
lookup and speed things up.
This commit is contained in:
Matthew Zeng
2026-03-14 22:24:13 -07:00
committed by GitHub
parent d692b74007
commit 49edf311ac
18 changed files with 288 additions and 166 deletions

View File

@@ -18,6 +18,9 @@ const CONNECTOR_DESCRIPTION: &str = "Plan events and manage your calendar.";
const PROTOCOL_VERSION: &str = "2025-11-25";
const SERVER_NAME: &str = "codex-apps-test";
const SERVER_VERSION: &str = "1.0.0";
pub const CALENDAR_CREATE_EVENT_RESOURCE_URI: &str =
"connector://calendar/tools/calendar_create_event";
const CALENDAR_LIST_EVENTS_RESOURCE_URI: &str = "connector://calendar/tools/calendar_list_events";
#[derive(Clone)]
pub struct AppsTestServer {
@@ -175,7 +178,12 @@ impl Respond for CodexAppsJsonRpcResponder {
"_meta": {
"connector_id": CONNECTOR_ID,
"connector_name": self.connector_name.clone(),
"connector_description": self.connector_description.clone()
"connector_description": self.connector_description.clone(),
"_codex_apps": {
"resource_uri": CALENDAR_CREATE_EVENT_RESOURCE_URI,
"contains_mcp_source": true,
"connector_id": CONNECTOR_ID
}
}
},
{
@@ -192,7 +200,12 @@ impl Respond for CodexAppsJsonRpcResponder {
"_meta": {
"connector_id": CONNECTOR_ID,
"connector_name": self.connector_name.clone(),
"connector_description": self.connector_description.clone()
"connector_description": self.connector_description.clone(),
"_codex_apps": {
"resource_uri": CALENDAR_LIST_EVENTS_RESOURCE_URI,
"contains_mcp_source": true,
"connector_id": CONNECTOR_ID
}
}
}
],
@@ -214,6 +227,7 @@ impl Respond for CodexAppsJsonRpcResponder {
.pointer("/params/arguments/starts_at")
.and_then(Value::as_str)
.unwrap_or_default();
let codex_apps_meta = body.pointer("/params/_meta/_codex_apps").cloned();
ResponseTemplate::new(200).set_body_json(json!({
"jsonrpc": "2.0",
@@ -223,6 +237,9 @@ impl Respond for CodexAppsJsonRpcResponder {
"type": "text",
"text": format!("called {tool_name} for {title} at {starts_at}")
}],
"structuredContent": {
"_codex_apps": codex_apps_meta,
},
"isError": false
}
}))

View File

@@ -943,10 +943,6 @@ async fn includes_apps_guidance_as_developer_message_for_chatgpt_auth() {
.features
.enable(Feature::Apps)
.expect("test config should allow feature update");
config
.features
.disable(Feature::AppsMcpGateway)
.expect("test config should allow feature update");
config.chatgpt_base_url = apps_base_url;
});
let codex = builder
@@ -971,7 +967,8 @@ async fn includes_apps_guidance_as_developer_message_for_chatgpt_auth() {
let request = resp_mock.single_request();
let request_body = request.body_json();
let input = request_body["input"].as_array().expect("input array");
let apps_snippet = "Apps are mentioned in user messages in the format";
let apps_snippet =
"Apps (Connectors) can be explicitly triggered in user messages in the format";
let has_developer_apps_guidance = input.iter().any(|item| {
item.get("role").and_then(|value| value.as_str()) == Some("developer")
@@ -1034,10 +1031,6 @@ async fn omits_apps_guidance_for_api_key_auth_even_when_feature_enabled() {
.features
.enable(Feature::Apps)
.expect("test config should allow feature update");
config
.features
.disable(Feature::AppsMcpGateway)
.expect("test config should allow feature update");
config.chatgpt_base_url = apps_base_url;
});
let codex = builder
@@ -1062,7 +1055,8 @@ async fn omits_apps_guidance_for_api_key_auth_even_when_feature_enabled() {
let request = resp_mock.single_request();
let request_body = request.body_json();
let input = request_body["input"].as_array().expect("input array");
let apps_snippet = "Apps are mentioned in the prompt in the format";
let apps_snippet =
"Apps (Connectors) can be explicitly triggered in user messages in the format";
let has_apps_guidance = input.iter().any(|item| {
item.get("content")

View File

@@ -142,10 +142,6 @@ async fn build_apps_enabled_plugin_test_codex(
.features
.enable(Feature::Apps)
.expect("test config should allow feature update");
config
.features
.disable(Feature::AppsMcpGateway)
.expect("test config should allow feature update");
config.chatgpt_base_url = chatgpt_base_url;
});
Ok(builder

View File

@@ -13,6 +13,7 @@ use codex_protocol::protocol::Op;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::user_input::UserInput;
use core_test_support::apps_test_server::AppsTestServer;
use core_test_support::apps_test_server::CALENDAR_CREATE_EVENT_RESOURCE_URI;
use core_test_support::responses::ResponsesRequest;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
@@ -30,8 +31,9 @@ use pretty_assertions::assert_eq;
use serde_json::Value;
use serde_json::json;
const SEARCH_TOOL_DESCRIPTION_SNIPPETS: [&str; 1] = [
"Tools of the apps (Calendar) are hidden until you search for them with this tool (`tool_search`).",
const SEARCH_TOOL_DESCRIPTION_SNIPPETS: [&str; 2] = [
"You have access to all the tools of the following apps/connectors",
"- Calendar: Plan events and manage your calendar.",
];
const TOOL_SEARCH_TOOL_NAME: &str = "tool_search";
const CALENDAR_CREATE_TOOL: &str = "mcp__codex_apps__calendar_create_event";
@@ -89,10 +91,6 @@ fn configure_apps(config: &mut Config, apps_base_url: &str) {
.features
.enable(Feature::Apps)
.expect("test config should allow feature update");
config
.features
.disable(Feature::AppsMcpGateway)
.expect("test config should allow feature update");
config.chatgpt_base_url = apps_base_url.to_string();
config.model = Some("gpt-5-codex".to_string());
@@ -404,6 +402,19 @@ async fn tool_search_returns_deferred_tools_without_follow_up_tool_injection() -
})),
}
);
assert_eq!(
end.result
.as_ref()
.expect("tool call should succeed")
.structured_content,
Some(json!({
"_codex_apps": {
"resource_uri": CALENDAR_CREATE_EVENT_RESOURCE_URI,
"contains_mcp_source": true,
"connector_id": "calendar",
},
}))
);
wait_for_event(&test.codex, |event| {
matches!(event, EventMsg::TurnComplete(_))