Concurrent requests overwrite each other's instrumentation data

Aberta
#657 0 comentários 0 reações 0 responsáveis Ver no GitHub

Mantenedores costumam responder em até 1 dia

@atesgoral já está trabalhando nisso.

Desde 6/11/2026.

  • #668 de @atesgoral — aberto

Avaliação

Dificuldade
3/5
Tempo estimado
1-2 dias
Facilidade para iniciantes
35/100
Tipo de issue
Bug
Clareza
Claramente especificada
Status de atividade
Estagnada
Stack de tecnologia
ruby

Direção de pesquisa

Start with lib/mcp/instrumentation.rb, especially instrument_call and add_instrumentation_data, then read the concurrent shared server test in test/mcp/server_test.rb and the tool call instrumentation in lib/mcp/server.rb. Reproduce the interleaving described in the issue and check how request data reaches the callback; done when concurrent requests produce separate records with the correct tool names. An open pull request (#668) is already addressing this issue.

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

Descrição

bug

Instrumentation#instrument_call keeps the per-request record in @instrumentation_data, an instance variable on the shared Server (instrumentation.rb:8). When two requests run at the same time on one server, for example Streamable HTTP on a threaded Rack server, the second request replaces the hash.

The first request then writes its later fields (client, error, duration) into the second request's hash and passes that hash to instrumentation_callback (instrumentation.rb:43, :48). The first request's own record, with its tool_name and tool_arguments (server.rb:1408), is never sent to the callback. around_request gets the original hash (instrumentation.rb:17) but misses the fields written after the swap.

Concurrent use of one server is supported (see test "#handle concurrent requests on a shared server report exceptions independently" in test/mcp/server_test.rb).

Repro

Run from the repository root with bundle exec ruby -I lib repro.rb:

require "mcp"

entered = Queue.new
release = Queue.new
slow_tool = MCP::Tool.define(name: "slow_tool") do
  entered << true
  release.pop
  MCP::Tool::Response.new([{ type: "text", text: "slow" }])
end
fast_tool = MCP::Tool.define(name: "fast_tool") do
  MCP::Tool::Response.new([{ type: "text", text: "fast" }])
end

records = Queue.new
configuration = MCP::Configuration.new
configuration.instrumentation_callback = ->(data) { records << data.dup }
server = MCP::Server.new(name: "example", tools: [slow_tool, fast_tool], configuration: configuration)

call = ->(id, name) { server.handle({ jsonrpc: "2.0", id: id, method: "tools/call", params: { name: name, arguments: {} } }) }
thread = Thread.new { call.call(1, "slow_tool") }
entered.pop
call.call(2, "fast_tool")
release << true
thread.join

p(2.times.map { records.pop[:tool_name] })

Expected: ["fast_tool", "slow_tool"]

Actual: ["fast_tool", "fast_tool"]

Suggested fix

Keep the record local to the request instead of on the shared server. For example, create the hash in instrument_call and pass it to add_instrumentation_data, or keep it in a fiber-local value that instrument_call sets and restores.

Linguagem predominante
Ruby
Estrelas
940
Forks
143
Merge médio
1d 5h
PRs com merge (30d)
48

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/ruby-sdk

Todas as issues de modelcontextprotocol/ruby-sdk

Issues semelhantes

Mais issues de Ruby

Receba novas issues na sua caixa de entrada

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