mirror of
https://github.com/openai/codex.git
synced 2026-11-13 06:45:07 +00:00
Track connectors detected in external agent sessions (#36959)
## What changed - Return connector candidates found in detected external agent sessions, aggregating session counts by connector name. - Resolve connector names from both cached plugin manifests and project MCP server metadata. - Persist detected connector clues in the session import ledger and merge them into completed imports without case-insensitive duplicates. ## Testing - Add coverage for project MCP metadata detection and for accumulating detected and imported connector candidates. GitOrigin-RevId: 97a7dfe4a61599686b2222cddd9595acec8b4501
This commit is contained in:
1 parent
4bd5b9fd09
commit
92689b6b7b
8 files changed
+399
-19
No files matched your search
@@ -14,6 +14,9 @@ use crate::sessions::ExternalAgentSessionMigration;
|
||||
const PLUGIN_CACHE_DIR: &str = "plugins/cache";
|
||||
const PLUGIN_MANIFEST_PATH: &str = ".cursor-plugin/plugin.json";
|
||||
const MCP_CONFIG_PATH: &str = ".mcp.json";
|
||||
const PROJECT_MCP_DIR: &str = "mcps";
|
||||
const PROJECT_MCP_SERVER_METADATA_PATH: &str = "SERVER_METADATA.json";
|
||||
const AGENT_TRANSCRIPTS_DIR: &str = "agent-transcripts";
|
||||
const MCP_TOOL_CALL: &str = "CallMcpTool";
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -24,17 +27,23 @@ struct PluginManifest {
|
||||
mcp_servers: Option<JsonValue>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ProjectMcpServerMetadata {
|
||||
server_identifier: String,
|
||||
server_name: String,
|
||||
}
|
||||
|
||||
pub(crate) fn detect_cur_session_connectors(
|
||||
sessions: &[ExternalAgentSessionMigration],
|
||||
external_agent_home: &Path,
|
||||
) -> Vec<DetectedConnectorCandidate> {
|
||||
let connector_names_by_server_id = cached_connector_names_by_server_id(external_agent_home);
|
||||
if connector_names_by_server_id.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let cached_connector_names_by_server_id =
|
||||
cached_connector_names_by_server_id(external_agent_home);
|
||||
let mut candidates = BTreeMap::<String, DetectedConnectorCandidate>::new();
|
||||
for session in sessions {
|
||||
let mut connector_names_by_server_id = cached_connector_names_by_server_id.clone();
|
||||
connector_names_by_server_id.extend(project_connector_names_by_server_id(session));
|
||||
for (key, name) in session_connector_names(session, &connector_names_by_server_id) {
|
||||
let candidate = candidates.entry(key).or_insert(DetectedConnectorCandidate {
|
||||
name,
|
||||
@@ -47,6 +56,36 @@ pub(crate) fn detect_cur_session_connectors(
|
||||
candidates.into_values().collect()
|
||||
}
|
||||
|
||||
fn project_connector_names_by_server_id(
|
||||
session: &ExternalAgentSessionMigration,
|
||||
) -> BTreeMap<String, String> {
|
||||
let Some(project_root) = session
|
||||
.path
|
||||
.ancestors()
|
||||
.find(|path| path.file_name().and_then(|name| name.to_str()) == Some(AGENT_TRANSCRIPTS_DIR))
|
||||
.and_then(Path::parent)
|
||||
else {
|
||||
return BTreeMap::new();
|
||||
};
|
||||
let mut connector_names = BTreeMap::new();
|
||||
for server_root in child_directories(&project_root.join(PROJECT_MCP_DIR)) {
|
||||
let Some(metadata) = read_project_mcp_server_metadata(&server_root) else {
|
||||
continue;
|
||||
};
|
||||
let Some(display_name) =
|
||||
crate::sessions::normalized_connector_display_name(Some(&metadata.server_name))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let server_identifier = metadata.server_identifier.trim();
|
||||
if server_identifier.is_empty() {
|
||||
continue;
|
||||
}
|
||||
connector_names.insert(server_identifier.to_lowercase(), display_name);
|
||||
}
|
||||
connector_names
|
||||
}
|
||||
|
||||
fn session_connector_names(
|
||||
session: &ExternalAgentSessionMigration,
|
||||
connector_names_by_server_id: &BTreeMap<String, String>,
|
||||
@@ -134,6 +173,11 @@ fn read_plugin_manifest(version_root: &Path) -> Option<PluginManifest> {
|
||||
serde_json::from_str(&contents).ok()
|
||||
}
|
||||
|
||||
fn read_project_mcp_server_metadata(server_root: &Path) -> Option<ProjectMcpServerMetadata> {
|
||||
let contents = fs::read_to_string(server_root.join(PROJECT_MCP_SERVER_METADATA_PATH)).ok()?;
|
||||
serde_json::from_str(&contents).ok()
|
||||
}
|
||||
|
||||
fn cached_mcp_server_names(version_root: &Path, manifest: &PluginManifest) -> Vec<String> {
|
||||
manifest
|
||||
.mcp_servers
|
||||
|
||||
@@ -83,6 +83,61 @@ fn detects_connector_used_by_session_mcp_tool_call() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_connector_from_project_mcp_server_metadata() {
|
||||
let source_home = TempDir::new().expect("source home");
|
||||
let project_root = source_home.path().join("projects/project");
|
||||
let session_path = project_root
|
||||
.join(AGENT_TRANSCRIPTS_DIR)
|
||||
.join("session-1")
|
||||
.join("session-1.jsonl");
|
||||
fs::create_dir_all(session_path.parent().expect("session parent")).expect("session directory");
|
||||
fs::write(
|
||||
&session_path,
|
||||
serde_json::json!({
|
||||
"role": "assistant",
|
||||
"message": {
|
||||
"content": [{
|
||||
"type": "tool_use",
|
||||
"name": "CallMcpTool",
|
||||
"input": {"server": "plugin-figma-figma", "toolName": "whoami"}
|
||||
}]
|
||||
}
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.expect("session");
|
||||
let server_root = project_root
|
||||
.join(PROJECT_MCP_DIR)
|
||||
.join("plugin-figma-figma");
|
||||
fs::create_dir_all(&server_root).expect("server directory");
|
||||
fs::write(
|
||||
server_root.join(PROJECT_MCP_SERVER_METADATA_PATH),
|
||||
serde_json::json!({
|
||||
"serverIdentifier": "plugin-figma-figma",
|
||||
"serverName": "Figma",
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.expect("server metadata");
|
||||
|
||||
assert_eq!(
|
||||
detect_cur_session_connectors(
|
||||
&[ExternalAgentSessionMigration {
|
||||
path: session_path,
|
||||
cwd: source_home.path().to_path_buf(),
|
||||
title: None,
|
||||
}],
|
||||
source_home.path(),
|
||||
),
|
||||
vec![DetectedConnectorCandidate {
|
||||
name: "Figma".to_string(),
|
||||
session_count: 1,
|
||||
source: DetectedConnectorSource::SessionToolUse,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
fn write_cached_plugin(
|
||||
source_home: &Path,
|
||||
plugin_name: &str,
|
||||
|
||||
Reference in new issue
Block a user