fix(mcp): keep pre-snake_case tool names as deprecated aliases (#1782)

#1665 renamed getDocument, listSpaces, listDocuments, listMemories and whoAmI to snake_case with no aliases. Clients with a cached tool list (ChatGPT, Codex, Claude) still call the old names and fail 100% of the time (~2k errors from ~300 users in the last 10 days, per PostHog). The old names are now registered again with the same handler and a "Deprecated alias" description. They stay listed because ChatGPT rejects calls to tools missing from tools/list.
This commit is contained in:
MaheshtheDev 2026-10-07 18:51:36 +00:00
parent 3535ff700d
commit 203fb6e482
2 changed files with 45 additions and 1 deletions

View file

@ -10,9 +10,13 @@ import {
const EXPECTED_TOOLS = [
"add_memory",
"fetch-graph-data",
"getDocument",
"get_document",
"get_profile",
"guided-save",
"listDocuments",
"listMemories",
"listSpaces",
"list_documents",
"list_memories",
"list_spaces",
@ -23,6 +27,7 @@ const EXPECTED_TOOLS = [
"select-space",
"set-active-tag",
"upload-file",
"whoAmI",
"who_am_i",
]
const describeWithAuth = describe.skipIf(!OAUTH_CREDENTIALS_AVAILABLE)

View file

@ -16,7 +16,46 @@ import type { ToolDeps } from "./types"
import * as uploadFile from "./upload-file"
import * as whoAmI from "./who-am-i"
export function registerAllTools(deps: ToolDeps) {
// Renamed to snake_case in #1665; clients with a cached tool list still call the old names.
// Remove once PostHog shows ~0 $mcp_tool_call events for these names for 2 weeks.
const LEGACY_TOOL_NAMES: Record<string, string> = {
get_document: "getDocument",
list_spaces: "listSpaces",
list_documents: "listDocuments",
list_memories: "listMemories",
who_am_i: "whoAmI",
}
type LooseRegisterTool = (
name: string,
config: { title?: string; description?: string },
cb: unknown,
) => ReturnType<ToolDeps["server"]["registerTool"]>
// Aliases must stay listed: ChatGPT rejects calls to tools missing from tools/list.
function withLegacyToolNames(server: ToolDeps["server"]): ToolDeps["server"] {
const register = server.registerTool.bind(server) as LooseRegisterTool
const registerTool: LooseRegisterTool = (name, config, cb) => {
const tool = register(name, config, cb)
const legacyName = LEGACY_TOOL_NAMES[name]
if (legacyName) {
register(
legacyName,
{
...config,
...(config.title ? { title: `${config.title} (deprecated)` } : {}),
description: `Deprecated: use ${name}.`,
},
cb,
)
}
return tool
}
return { registerTool: registerTool as ToolDeps["server"]["registerTool"] }
}
export function registerAllTools(toolDeps: ToolDeps) {
const deps = { ...toolDeps, server: withLegacyToolNames(toolDeps.server) }
searchMemory.register(deps)
getProfile.register(deps)
listDocuments.register(deps)