test(agents): cover optional MCP through registered provider contracts

This commit is contained in:
Mohamed Boudra
2026-10-29 09:04:20 +01:00
parent 12a33ea21a
commit 2325a2f32d
2 changed files with 33 additions and 8 deletions
+9
View File
@@ -44,6 +44,15 @@ fall back to creating a fresh agent when continuation was requested.
Hub owns conversation keys, trigger policy, execution records, and the mapping to workspace/agent
IDs. None of these routing concepts are part of the generic daemon RPCs. Creation accepts ordinary
provider configuration, exact MCP tool policy, environment, and workspace/worktree selection.
`server_info.features.optionalMcpSupport` advertises optional MCP launch requirements.
Hub sends `toolPolicy.enforcement: "if-supported"` and
`mcpServerRequirements: { hub: "optional" }` only when this flag is true.
Without it, Hub preserves its existing strict tool-policy request. Available
providers remain selectable; providers lacking strict approval support need an
updated daemon to launch from Hub. Required external/plugin servers and explicitly
required tool policies keep their existing refusal semantics.
Private session configuration is persisted for recovery and is not exposed in agent snapshots.
Provider controls remain provider-native; see [providers.md](providers.md).
@@ -19,6 +19,7 @@ import {
type AgentManagerEvent,
type ManagedAgent,
} from "./agent-manager.js";
import { buildProviderRegistry } from "./provider-registry.js";
import { AgentStorage } from "./agent-storage.js";
import { InMemoryAgentTimelineStore } from "./agent-timeline-store.js";
import { toAgentPayload } from "./agent-projections.js";
@@ -445,7 +446,7 @@ class EnvProbeAgentClient extends TestAgentClient {
}
class TestAgentSession implements AgentSession {
readonly provider = "codex" as const;
readonly provider: AgentProvider;
readonly capabilities = TEST_CAPABILITIES;
readonly id = randomUUID();
private runtimeModel: string | null = null;
@@ -453,7 +454,9 @@ class TestAgentSession implements AgentSession {
private turnIdCounter = 0;
private interrupted = false;
constructor(private readonly config: AgentSessionConfig) {}
constructor(private readonly config: AgentSessionConfig) {
this.provider = config.provider;
}
async run(): Promise<AgentRunResult> {
return {
@@ -3822,9 +3825,13 @@ test("resumeAgentFromPersistence drops stored internal paseo MCP when runtime in
expect(snapshot.config.mcpServers).toBeUndefined();
});
test.each([true, false])(
"createAgent permits optional approval with MCP capability %s",
async (supportsMcp) => {
test.each([
{ provider: "pi", supportsMcp: true },
{ provider: "pi", supportsMcp: false },
{ provider: "codex", supportsMcp: false },
])(
"createAgent permits optional approval for $provider with MCP capability $supportsMcp",
async ({ provider, supportsMcp }) => {
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-test-"));
const storagePath = join(workdir, "agents");
const storage = new AgentStorage(storagePath, logger);
@@ -3838,11 +3845,20 @@ test.each([true, false])(
}
}
const client = new CaptureClient();
const client = new CaptureClient(provider);
const manager = new AgentManager({
clients: {
codex: client,
[provider]: client,
},
providerDefinitions:
provider === "pi"
? {
pi: {
enabled: true,
applyToolPolicy: buildProviderRegistry(logger).pi.applyToolPolicy,
},
}
: undefined,
registry: storage,
logger,
mcpBaseUrl: "http://127.0.0.1:6767/mcp/agents",
@@ -3851,7 +3867,7 @@ test.each([true, false])(
const snapshot = await manager.createAgent(
{
provider: "codex",
provider,
mcpServerRequirements: { paseo: "optional" },
cwd: workdir,
toolPolicy: {