Skip to content

resumeSession silently ignores the mcpServers config field #1113

@fashxp

Description

@fashxp

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

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions