A handler that throws McpError produces a double-prefixed message on the client

Aberta
#2,786 4 comentários 0 reações 0 responsáveis Ver no GitHub

Mantenedores costumam responder em até 1 dia

@errmakov já está trabalhando nisso.

Desde 27/9/2026.

  • #2881 de @MustafaKemal0146 — aberto

Avaliação

Dificuldade
3/5
Tempo estimado
1-2 dias
Facilidade para iniciantes
68/100
Tipo de issue
Bug
Clareza
Razoavelmente clara
Status de atividade
Ativa
Stack de tecnologia
typescript

Direção de pesquisa

Comece em Protocol._onresponse e McpError.fromError, seguindo tanto _requestResolvers quanto os caminhos normais de resposta descritos no relatório. Reproduza a rejeição de callTool por meio de InMemoryTransport e, em seguida, verifique se a mensagem de erro recebe o prefixo apenas uma vez, sem causar regressão na ramificação UrlElicitationRequired. Está concluído quando as respostas McpError lançadas chegam ao cliente com um único prefixo de erro MCP.

Escrita pelo modelo de indexação a partir do texto da issue.

Descrição

v1

What happens

A request handler that throws McpError produces a message the client shows with the prefix twice.
Reproduced with the SDK alone over InMemoryTransport on 1.24.3, and the code path is unchanged in the
current 1.30.0:

server.setRequestHandler(CallToolRequestSchema, async () => {
  throw new McpError(ErrorCode.MethodNotFound, "Unknown tool: nope");
});
// ...
try { await client.callTool({ arguments: {}, name: "nope" }); }
catch (e) { console.log("client received:", e.message); }
server threw:   MCP error -32601: Unknown tool: nope
client received: MCP error -32601: MCP error -32601: Unknown tool: nope

Why

Three steps, each defensible alone:

  1. McpError's constructor calls super(`MCP error ${code}: ${message}`), so .message already carries
    the prefix.
  2. The server serialises a thrown error as message: error.message, so the prefix travels inside the
    JSON-RPC error.message field.
  3. Protocol._onresponse converts it back with McpError.fromError(response.error.code, response.error.message, response.error.data), whose default branch returns
    new McpError(code, message, data) and prefixes what is already prefixed. I confirmed at runtime that
    this is the path a callTool rejection takes, by counting calls into fromError: exactly one.

_onresponse also holds a new McpError(...) conversion in its _requestResolvers branch, for queued
responses, which double-prefixes for the same reason.

Throwing McpError is the SDK's own mechanism and shared/protocol throws it in several places itself, so
this is the default result rather than a misuse.

What I expected

One prefix. Either the JSON-RPC error.message carries the bare message, or the client stops re-wrapping a
message that already has the prefix.

Not checked

Only InMemoryTransport, and only a callTool rejection. The one branch of fromError that does not take
the default path is UrlElicitationRequired carrying elicitations, which returns
UrlElicitationRequiredError; that class calls super with the same code, so I would expect it to prefix
too, but I did not exercise it.

Linguagem predominante
TypeScript
Estrelas
13.7k
Forks
2.4k
Merge médio
2d 7h
PRs com merge (30d)
26

Preparar o ambiente

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de modelcontextprotocol/typescript-sdk

Todas as issues de modelcontextprotocol/typescript-sdk

Issues semelhantes

Mais issues de TypeScript

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.