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.
|
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.
|
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", () => {
|
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 () => {
|
test("emits turn_started before live OpenCode timeline items", async () => {
|
||||||
const eventsGate = createTestDeferred<void>();
|
const eventsGate = createTestDeferred<void>();
|
||||||
const globalEvents = [
|
const globalEvents = [
|
||||||
|
|||||||
@@ -3170,16 +3170,10 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
config,
|
config,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
await this.runMcpOperation("connect", name, () =>
|
|
||||||
this.client.mcp.connect({
|
|
||||||
directory: this.config.cwd,
|
|
||||||
name,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async runMcpOperation(
|
private async runMcpOperation(
|
||||||
operation: "add" | "connect",
|
operation: "add",
|
||||||
name: string,
|
name: string,
|
||||||
run: () => Promise<{ error?: unknown }>,
|
run: () => Promise<{ error?: unknown }>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|||||||
+12
-2
@@ -56,6 +56,8 @@ export class TestOpenCodeClient {
|
|||||||
eventSubscribe: [] as unknown[],
|
eventSubscribe: [] as unknown[],
|
||||||
experimentalSessionList: [] as unknown[],
|
experimentalSessionList: [] as unknown[],
|
||||||
globalEvent: [] as unknown[],
|
globalEvent: [] as unknown[],
|
||||||
|
mcpAdd: [] as unknown[],
|
||||||
|
mcpConnect: [] as unknown[],
|
||||||
permissionReply: [] as unknown[],
|
permissionReply: [] as unknown[],
|
||||||
providerList: [] as unknown[],
|
providerList: [] as unknown[],
|
||||||
questionReject: [] as unknown[],
|
questionReject: [] as unknown[],
|
||||||
@@ -74,6 +76,8 @@ export class TestOpenCodeClient {
|
|||||||
commandListResponse: OpenCodeResponse = { data: [] };
|
commandListResponse: OpenCodeResponse = { data: [] };
|
||||||
eventStream: AsyncIterable<unknown>;
|
eventStream: AsyncIterable<unknown>;
|
||||||
experimentalSessionListResponse: OpenCodeResponse = { data: [] };
|
experimentalSessionListResponse: OpenCodeResponse = { data: [] };
|
||||||
|
mcpAddResponse: OpenCodeResponse = {};
|
||||||
|
mcpConnectResponse: OpenCodeResponse = {};
|
||||||
permissionReplyResponse: OpenCodeResponse = {};
|
permissionReplyResponse: OpenCodeResponse = {};
|
||||||
providerListResponse: OpenCodeResponse = { data: { connected: [], all: [] } };
|
providerListResponse: OpenCodeResponse = { data: { connected: [], all: [] } };
|
||||||
providerListImplementation: (() => Promise<OpenCodeResponse>) | null = null;
|
providerListImplementation: (() => Promise<OpenCodeResponse>) | null = null;
|
||||||
@@ -135,8 +139,14 @@ export class TestOpenCodeClient {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mcp: {
|
mcp: {
|
||||||
add: async () => ({}),
|
add: async (parameters: unknown) => {
|
||||||
connect: async () => ({}),
|
this.calls.mcpAdd.push(parameters);
|
||||||
|
return this.mcpAddResponse;
|
||||||
|
},
|
||||||
|
connect: async (parameters: unknown) => {
|
||||||
|
this.calls.mcpConnect.push(parameters);
|
||||||
|
return this.mcpConnectResponse;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
permission: {
|
permission: {
|
||||||
reply: async (parameters: unknown) => {
|
reply: async (parameters: unknown) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user