modelcontextprotocol / modelcontextprotocol/python-sdk

coerce_request_id() folds non-canonical numeric strings, conflating wire-distinct JSON-RPC ids

Ouverte Adaptée aux débutants
#3,432 4 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

v1 v2
Langage dominant
Python
Étoiles
24.3k
Forks
4k
Merge moyen
1 j 1 h
PR mergées (30 j)
31

Description

Initial Checks
Release line

2.x (current stable)

Description

On current main, coerce_request_id() in src/mcp/shared/dispatcher.py folds a stringified id to int with a bare int(request_id). That accepts a lot more than a canonical integer string, so ids that are distinct on the JSON-RPC wire collapse onto one correlation key:

coerce_request_id('007')   -> 7
coerce_request_id('+7')    -> 7
coerce_request_id('1_000') -> 1000
coerce_request_id(' 7 ')   -> 7
coerce_request_id('٧')     -> 7

This shared key backs _pending (response correlation), _in_flight (cancellation), and progress-token routing. So a peer, or a caller using CallOptions["request_id"], that uses e.g. 10 and "1_0" as two ids has them merged. A notifications/cancelled for one hits the other, and the first can't be cancelled. It's the same cross-wiring class as #3060, but between ids that are genuinely different on the wire.

JSON-RPC 2.0 treats String and Number ids as distinct value types, and the function's own intent (and its test, "7" -> 7) is the canonical "peer stringified an int" case. The forms above aren't that. They're an artifact of Python's int(). The docstring says "matches the TS SDK", but JS Number("1_000") is NaN and Number("0x10") is 16, so today's behavior matches neither.

The narrow fix is to fold only when the string equals str(int(s)), which keeps the intended "7" -> 7 and "-3" -> -3 behavior and leaves every other string a distinct id. I have a patch and a regression test for it (both dispatchers share the function) and I'm happy to open a PR once this is triaged and assigned.

Disclosure: I used an AI coding assistant to help explore the code and draft this. I verified the reproduction and understand the fix myself.

Example Code
from mcp.shared.dispatcher import coerce_request_id

for raw in ("007", "+7", "1_000", " 7 ", "٧"):
    print(repr(raw), "->", repr(coerce_request_id(raw)))
# every line prints an int, so all five distinct wire ids share one key
Python & MCP Python SDK

Python 3.11.14, python-sdk main @ 5bc9e07, macOS

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez dans src/mcp/shared/dispatcher.py, au niveau de coerce_request_id(), et lisez son test existant pour comprendre le comportement attendu de convertir "7" en 7. Ajoutez une couverture de régression pour les chaînes numériques non canoniques et vérifiez que des wire ids distincts restent distincts dans les réponses en attente, l’annulation et le routage des progress-tokens.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
api, backend
Type d'issue
Bug
Difficulté
2/5
Temps estimé
1-3 heures
Activité
Active
Clarté
Clairement spécifiée
Accessibilité débutants
84/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.