mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
## Why #27198 made the extension-owned `codex_apps` MCP connection the hosted plugin runtime, but its `mcp/skill` resources still bypassed the skills extension. App-server could list and read those resources through generic MCP APIs, but a thread with no selected environment did not expose them in the model's skills catalog or load their `SKILL.md` through `$skill`. Hosted skills should stay remote while using the same typed catalog, source authority, deduplication, bounded contextual catalog, and selected-skill prompt injection as host and executor skills. They should not be downloaded or exposed as ambient filesystem paths. ## What changed - Add a session-scoped `McpResourceClient` over the replaceable MCP connection manager so resource list/read calls follow startup and refresh replacements. - Add a `BackendSkillProvider` that pages `codex_apps` resources, accepts bounded and validated `mcp/skill` entries, and reads a selected skill's `SKILL.md` through the same MCP connection. - Register the remote provider in app-server and include it in the skills catalog even when a thread has no selected capability roots or executor. - Contribute hosted skill metadata through the bounded `AvailableSkillsInstructions` developer-context path, exclude remote entries from per-turn catalog injection, and classify `<skills>` messages as contextual developer content so rollback can trim and rebuild them correctly. ## Testing - Extend the app-server MCP resource integration test with `environments: []` to exercise two-page discovery, filter a non-`mcp/skill` resource, verify the escaped developer catalog entry and user-role `<skill>` fragment containing the fetched `SKILL.md`, and preserve generic MCP resource reads. - Add core event-mapping coverage that classifies `<skills>` developer messages as contextual history.
67 lines
2.0 KiB
Rust
67 lines
2.0 KiB
Rust
use std::future::Future;
|
|
use std::pin::Pin;
|
|
use std::sync::Arc;
|
|
|
|
mod executor;
|
|
mod host;
|
|
mod orchestrator;
|
|
|
|
use codex_core_skills::HostLoadedSkills;
|
|
use codex_mcp::McpResourceClient;
|
|
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
|
|
|
use crate::catalog::SkillAuthority;
|
|
use crate::catalog::SkillCatalog;
|
|
use crate::catalog::SkillPackageId;
|
|
use crate::catalog::SkillProviderResult;
|
|
use crate::catalog::SkillReadResult;
|
|
use crate::catalog::SkillResourceId;
|
|
use crate::catalog::SkillSearchResult;
|
|
|
|
pub use executor::ExecutorSkillProvider;
|
|
pub use host::HostSkillProvider;
|
|
pub use orchestrator::OrchestratorSkillProvider;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct SkillListQuery {
|
|
pub turn_id: String,
|
|
pub executor_roots: Vec<SelectedCapabilityRoot>,
|
|
pub host: Option<Arc<HostLoadedSkills>>,
|
|
pub include_host_skills: bool,
|
|
pub include_bundled_skills: bool,
|
|
pub include_orchestrator_skills: bool,
|
|
pub mcp_resources: Option<Arc<McpResourceClient>>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct SkillReadRequest {
|
|
pub authority: SkillAuthority,
|
|
pub package: SkillPackageId,
|
|
pub resource: SkillResourceId,
|
|
pub host: Option<Arc<HostLoadedSkills>>,
|
|
pub mcp_resources: Option<Arc<McpResourceClient>>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct SkillSearchRequest {
|
|
pub authority: SkillAuthority,
|
|
pub package: SkillPackageId,
|
|
pub query: String,
|
|
}
|
|
|
|
pub type SkillProviderFuture<'a, T> =
|
|
Pin<Box<dyn Future<Output = SkillProviderResult<T>> + Send + 'a>>;
|
|
|
|
/// Source-specific skill catalog and resource access.
|
|
///
|
|
/// Implementations must preserve authority boundaries: a resource listed by a
|
|
/// provider must be read or searched through the same provider/authority rather
|
|
/// than converted into an ambient local path.
|
|
pub trait SkillProvider: Send + Sync {
|
|
fn list(&self, query: SkillListQuery) -> SkillProviderFuture<'_, SkillCatalog>;
|
|
|
|
fn read(&self, request: SkillReadRequest) -> SkillProviderFuture<'_, SkillReadResult>;
|
|
|
|
fn search(&self, request: SkillSearchRequest) -> SkillProviderFuture<'_, SkillSearchResult>;
|
|
}
|