fix(server): inject OpenCode MCP servers once
This commit is contained in:
@@ -26,6 +26,8 @@ Pi import discovery reads Pi's persisted JSONL session files because Pi RPC does
|
||||
|
||||
Pi RPC extension UI dialog requests (`select`, `input`, `editor`, `confirm`) are bridged into Paseo question permissions and answered with `extension_ui_response`. Fire-and-forget extension UI requests such as notifications are intentionally ignored by the provider adapter unless Paseo grows first-class UI for them.
|
||||
|
||||
OpenCode MCP injection is dynamic and session-scoped. Call OpenCode's `mcp.add` endpoint with the MCP server config and do not follow it with `mcp.connect`; `connect` only toggles MCP servers already present in OpenCode's own config. New OpenCode versions return `McpServerNotFoundError`/404 for `connect` after a dynamic add because the server is not config-backed, while older versions silently swallowed the same missing-config path.
|
||||
|
||||
Draft metadata lookups should avoid creating provider sessions when the upstream provider has top-level APIs for that metadata. Prefer `AgentClient.listModels`, `listModes`, `listCommands`, or `listFeatures` over creating a scratch `AgentSession`; scratch sessions can show up as empty native sessions in provider import/history UIs.
|
||||
|
||||
---
|
||||
|
||||
@@ -585,6 +585,46 @@ describe("OpenCode adapter context-window normalization", () => {
|
||||
});
|
||||
|
||||
describe("OpenCode adapter startTurn error handling", () => {
|
||||
test("dynamically adds injected MCP servers without config-backed connect", async () => {
|
||||
const runtime = new TestOpenCodeRuntime();
|
||||
const openCodeClient = new TestOpenCodeClient();
|
||||
runtime.enqueueClient(openCodeClient);
|
||||
const cwd = tmpCwd();
|
||||
const client = new OpenCodeAgentClient(createTestLogger(), undefined, { runtime });
|
||||
|
||||
try {
|
||||
const session = await client.createSession({
|
||||
provider: "opencode",
|
||||
cwd,
|
||||
mcpServers: {
|
||||
paseo: {
|
||||
type: "http",
|
||||
url: "http://127.0.0.1:6767/mcp/agents?callerAgentId=test-agent",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await collectTurnEvents(streamSession(session, "hello"));
|
||||
|
||||
expect(openCodeClient.calls.mcpAdd).toEqual([
|
||||
{
|
||||
directory: cwd,
|
||||
name: "paseo",
|
||||
config: {
|
||||
type: "remote",
|
||||
url: "http://127.0.0.1:6767/mcp/agents?callerAgentId=test-agent",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(openCodeClient.calls.mcpConnect).toEqual([]);
|
||||
|
||||
await session.close();
|
||||
} finally {
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("emits turn_started before live OpenCode timeline items", async () => {
|
||||
const eventsGate = createTestDeferred<void>();
|
||||
const globalEvents = [
|
||||
|
||||
@@ -3170,16 +3170,10 @@ class OpenCodeAgentSession implements AgentSession {
|
||||
config,
|
||||
}),
|
||||
);
|
||||
await this.runMcpOperation("connect", name, () =>
|
||||
this.client.mcp.connect({
|
||||
directory: this.config.cwd,
|
||||
name,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private async runMcpOperation(
|
||||
operation: "add" | "connect",
|
||||
operation: "add",
|
||||
name: string,
|
||||
run: () => Promise<{ error?: unknown }>,
|
||||
): Promise<void> {
|
||||
|
||||
+12
-2
@@ -56,6 +56,8 @@ export class TestOpenCodeClient {
|
||||
eventSubscribe: [] as unknown[],
|
||||
experimentalSessionList: [] as unknown[],
|
||||
globalEvent: [] as unknown[],
|
||||
mcpAdd: [] as unknown[],
|
||||
mcpConnect: [] as unknown[],
|
||||
permissionReply: [] as unknown[],
|
||||
providerList: [] as unknown[],
|
||||
questionReject: [] as unknown[],
|
||||
@@ -74,6 +76,8 @@ export class TestOpenCodeClient {
|
||||
commandListResponse: OpenCodeResponse = { data: [] };
|
||||
eventStream: AsyncIterable<unknown>;
|
||||
experimentalSessionListResponse: OpenCodeResponse = { data: [] };
|
||||
mcpAddResponse: OpenCodeResponse = {};
|
||||
mcpConnectResponse: OpenCodeResponse = {};
|
||||
permissionReplyResponse: OpenCodeResponse = {};
|
||||
providerListResponse: OpenCodeResponse = { data: { connected: [], all: [] } };
|
||||
providerListImplementation: (() => Promise<OpenCodeResponse>) | null = null;
|
||||
@@ -135,8 +139,14 @@ export class TestOpenCodeClient {
|
||||
},
|
||||
},
|
||||
mcp: {
|
||||
add: async () => ({}),
|
||||
connect: async () => ({}),
|
||||
add: async (parameters: unknown) => {
|
||||
this.calls.mcpAdd.push(parameters);
|
||||
return this.mcpAddResponse;
|
||||
},
|
||||
connect: async (parameters: unknown) => {
|
||||
this.calls.mcpConnect.push(parameters);
|
||||
return this.mcpConnectResponse;
|
||||
},
|
||||
},
|
||||
permission: {
|
||||
reply: async (parameters: unknown) => {
|
||||
|
||||
Reference in New Issue
Block a user