github / github/copilot-sdk

`resumeSession` silently ignores the `mcpServers` config field

Đang mở
#1,113 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Java
Star
10.5k
Fork
1.5k
Merge trung bình
1 ngày 11 giờ
Pull request đã merge (30 ngày)
128

Mô tả

`CopilotClient.resumeSession(id, { mcpServers: ... })` does not apply the `mcpServers` value passed on resume. The servers connected during the
original `createSession` call remain connected; any servers declared on resume are never loaded. Other fields in `ResumeSessionConfig` (`tools`,
`skillDirectories`, `systemMessage`) *are* applied on the same call, so the behaviour is asymmetric.

### Reproduction

```js
import { CopilotClient } from '@github/copilot-sdk'

const baseConfig = {
model: 'gpt-4.1',
streaming: true,
onPermissionRequest: async () => ({ decision: 'allow', persist: false })
}

// Two different public MCP reference servers — distinct tool sets.
const mcpEverything = {
type: 'local',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-everything'],
tools: '*'
}
const mcpMemory = {
type: 'local',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-memory'],
tools: '*'
}

const LIST_PROMPT =
'Pick any MCP tool you actually have access to right now and call it ' +
'(any reasonable args). Then in one short sentence, name the tool you ' +
'just called and which MCP server it belongs to.'

async function ask (session, prompt) {
const response = await session.sendAndWait({ prompt }, 60_000)
return response?.data?.content ?? ''
}

const client = new CopilotClient({ autoStart: true })
await client.start()

const logServers = (phase) => (event) => {
if (event.type === 'session.mcp_servers_loaded') {
const names = (event.data?.servers ?? []).map(s => s.name)
console.log(` [SDK event in ${phase}] session.mcp_servers_loaded → ${JSON.stringify(names)}`)
}
}

console.log('\n=== 1. createSession with mcpServers = { everything } ===')
const sessionA = await client.createSession({
...baseConfig,
mcpServers: { everything: mcpEverything }
})
sessionA.on(logServers('createSession'))
console.log(' LLM reply: ' + await ask(sessionA, LIST_PROMPT))

console.log('\n=== 2. resumeSession(sameId, mcpServers = { memory }) ===')
const sessionB = await client.resumeSession(sessionA.id ?? sessionA.sessionId, {
...baseConfig,
mcpServers: { memory: mcpMemory } // ← DIFFERENT server
})
sessionB.on(logServers('resumeSession'))
console.log(' LLM reply: ' + await ask(sessionB, LIST_PROMPT))

await client.disconnect?.()
process.exit(0)
```

#### Run with:
```
npm i @github/copilot-sdk
node repro.mjs
```
(Node 22, `npx` on PATH, and a logged-in Copilot CLI — run
`./node_modules/.bin/copilot login` once.)

### Observed behavior

```
=== 1. createSession with mcpServers = { everything } ===
[SDK event in createSession] session.mcp_servers_loaded → ["everything"]
LLM reply: I called the get-sum tool from the "everything" MCP server.

=== 2. resumeSession(sameId, mcpServers = { memory }) ===
[SDK event in resumeSession] session.mcp_servers_loaded → ["everything"]
LLM reply: I called the get-tiny-image tool from the "everything" MCP server.
```

After step 2 the SDK is still publishing `["everything"]` in `session.mcp_servers_loaded`, and the LLM can only call tools from `everything` — even though the resume config explicitly passed `{ memory: ... }` and did **not** include `everything`.

### Expected behavior

After `resumeSession(id, { mcpServers: { memory: ... } })`:

- `session.mcp_servers_loaded` should emit `["memory"]`.
- The previously-loaded `everything` server should be disconnected (or at least no longer be visible as a tool source).
- The LLM should only be able to call tools from `memory` (e.g. `read_graph`, `create_entities`).

### Impact

Prevents agent-switching mid-conversation (same use case as #567). Only workaround is `deleteSession` + `createSession` — which wipes history, defeating the point of `resumeSession`.

### Environment
- `@github/copilot-sdk` 0.2.1 and 0.2.2 (both affected)
- CLI binary 1.0.34, Node 22.14, Linux

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Start at the CopilotClient.resumeSession entry point and compare its mcpServers handling with createSession, using the provided reproduction and session.mcp_servers_loaded event as checks. Done means resuming with { memory: ... } emits only ["memory"], disconnects or hides the previous everything server, and exposes only the replacement server's tools.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
javascript
Lĩnh vực
api
Loại issue
Lỗi
Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
68/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.