mirror of
https://github.com/openai/codex.git
synced 2026-11-14 21:40:39 +00:00
## Why MCP servers discovered on different executors can share a name. Resolving them by name alone exposes only one server and prevents tools and resources from being addressed independently. ## What changed - Add the disabled-by-default `multi_environment_mcp` feature to expose environment-scoped servers, including multiple installations of the same plugin. - Carry exact `McpServerKey` identities through catalogs, connections, tool calls, resources, and approval tracking. Give environment-scoped tools stable namespaces derived from their environment. - Include `environment_id` in scoped resource listings and accept it in resource requests. Require it for environment-owned servers when multiple environments are selected. - Preserve server ownership through hooks and use the owning environment's permissions for scoped elicitation review. ## Testing Add coverage for same-named servers across environments, independent calls and approvals, resource selection, plugin discovery, and rejection of hook changes to the tool environment. GitOrigin-RevId: 5593372b06a15c16aa3220faa3b19f5d3b20e67e
56 lines
1.9 KiB
Rust
56 lines
1.9 KiB
Rust
//! Exact MCP registration identity, separate from model-visible names and execution location.
|
|
//!
|
|
//! An unscoped key identifies a thread-wide declaration. A scoped key distinguishes
|
|
//! same-named declarations by environment; neither form is a model-facing tool name.
|
|
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
|
|
/// The exact identity of one MCP server registration.
|
|
///
|
|
/// The optional environment is registration scope, not necessarily the server's
|
|
/// execution location. Unscoped registrations may execute on a remote environment.
|
|
/// Its display form is for diagnostics, not for serialization or lookup.
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
|
pub struct McpServerKey {
|
|
#[serde(rename = "server_name")]
|
|
name: String,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
scoped_environment_id: Option<String>,
|
|
}
|
|
|
|
impl McpServerKey {
|
|
/// Identifies a thread-wide declaration by its configured name.
|
|
pub fn unscoped(name: impl Into<String>) -> Self {
|
|
Self {
|
|
name: name.into(),
|
|
scoped_environment_id: None,
|
|
}
|
|
}
|
|
|
|
/// Identifies a declaration contributed by a particular environment.
|
|
pub fn in_environment(environment: impl Into<String>, name: impl Into<String>) -> Self {
|
|
Self {
|
|
name: name.into(),
|
|
scoped_environment_id: Some(environment.into()),
|
|
}
|
|
}
|
|
|
|
pub fn name(&self) -> &str {
|
|
&self.name
|
|
}
|
|
|
|
pub fn scoped_environment_id(&self) -> Option<&str> {
|
|
self.scoped_environment_id.as_deref()
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for McpServerKey {
|
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self.scoped_environment_id() {
|
|
Some(environment) => write!(formatter, "{} (environment {environment})", self.name()),
|
|
None => formatter.write_str(self.name()),
|
|
}
|
|
}
|
|
}
|