Merge pull request #3087 from code-yeongyu/fix/prepublish-mcp-regressions

fix: resolve MCP transcript cache and disable override regressions
This commit is contained in:
YeonGyu-Kim
2026-04-04 01:46:25 +09:00
committed by GitHub
4 changed files with 125 additions and 1 deletions
@@ -138,6 +138,41 @@ describe("getSystemMcpServerNames", () => {
}
})
it("removes a server name when a higher-precedence config disables it", async () => {
// given
writeFileSync(join(TEST_HOME, ".claude.json"), JSON.stringify({
mcpServers: {
playwright: {
command: "npx",
args: ["@playwright/mcp@latest"],
},
},
}))
writeFileSync(join(TEST_DIR, ".mcp.json"), JSON.stringify({
mcpServers: {
playwright: {
command: "npx",
args: ["@playwright/mcp@latest"],
disabled: true,
},
},
}))
const originalCwd = process.cwd()
process.chdir(TEST_DIR)
try {
// when
const { getSystemMcpServerNames } = await import("./loader")
const names = getSystemMcpServerNames()
// then
expect(names.has("playwright")).toBe(false)
} finally {
process.chdir(originalCwd)
}
})
it("merges server names from multiple .mcp.json files", async () => {
// given
mkdirSync(join(TEST_DIR, ".claude"), { recursive: true })
@@ -59,7 +59,10 @@ export function getSystemMcpServerNames(): Set<string> {
if (!config?.mcpServers) continue
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
if (serverConfig.disabled) continue
if (serverConfig.disabled) {
names.delete(name)
continue
}
if (!shouldLoadMcpServer(serverConfig, cwd)) continue
names.add(name)
}
@@ -99,4 +99,82 @@ describe("transcript caching", () => {
expect(client.session.messages).toHaveBeenCalledTimes(2)
})
it("keeps intermediate tool calls across sequential transcript rebuilds", async () => {
// given
const client = createMockClient([])
// when
const firstPath = await buildTranscriptFromSession(
client,
"ses_sequential",
"/tmp",
"bash",
{ command: "echo first" }
)
const secondPath = await buildTranscriptFromSession(
client,
"ses_sequential",
"/tmp",
"read",
{ filePath: "/tmp/second.txt" }
)
const thirdPath = await buildTranscriptFromSession(
client,
"ses_sequential",
"/tmp",
"write",
{ filePath: "/tmp/third.txt", content: "third" }
)
// then
expect(firstPath).not.toBeNull()
expect(secondPath).not.toBeNull()
expect(thirdPath).not.toBeNull()
if (thirdPath) {
const content = readFileSync(thirdPath, "utf-8")
expect(content).toContain("Bash")
expect(content).toContain("Read")
expect(content).toContain("Write")
}
deleteTempTranscript(firstPath)
deleteTempTranscript(secondPath)
deleteTempTranscript(thirdPath)
})
it("cleans up previous temp transcript files when rebuilding cached transcripts", async () => {
// given
const client = createMockClient([])
// when
const firstPath = await buildTranscriptFromSession(
client,
"ses_cleanup",
"/tmp",
"bash",
{ command: "echo first" }
)
const secondPath = await buildTranscriptFromSession(
client,
"ses_cleanup",
"/tmp",
"read",
{ filePath: "/tmp/second.txt" }
)
// then
expect(firstPath).not.toBeNull()
expect(secondPath).not.toBeNull()
if (firstPath && secondPath) {
expect(existsSync(firstPath)).toBe(false)
expect(existsSync(secondPath)).toBe(true)
}
deleteTempTranscript(firstPath)
deleteTempTranscript(secondPath)
})
})
@@ -165,10 +165,12 @@ export async function buildTranscriptFromSession(
): Promise<string | null> {
try {
let baseEntries: string[]
let previousTempPath: string | null = null
const cached = transcriptCache.get(sessionId)
if (cached && isCacheValid(cached)) {
baseEntries = cached.baseEntries
previousTempPath = cached.tempPath
} else {
// Fetch full session messages (only on first call or cache expiry)
const response = await client.session.messages({
@@ -199,6 +201,10 @@ export async function buildTranscriptFromSession(
// Append current tool call
const allEntries = [...baseEntries, buildCurrentEntry(currentToolName, currentToolInput)]
if (previousTempPath) {
try { unlinkSync(previousTempPath) } catch { /* ignore */ }
}
const tempPath = join(
tmpdir(),
`opencode-transcript-${sessionId}-${randomUUID()}.jsonl`
@@ -208,7 +214,9 @@ export async function buildTranscriptFromSession(
// Update cache temp path for cleanup tracking
const cacheEntry = transcriptCache.get(sessionId)
if (cacheEntry) {
cacheEntry.baseEntries = allEntries
cacheEntry.tempPath = tempPath
cacheEntry.createdAt = Date.now()
}
return tempPath