everything/tasks: tasks/update checks MAX_ANSWER_BYTES against string length, so non-ASCII answers exceed the documented byte budget
维护者通常 1 天内回复
评估
- 难度
- 1/5
- 预计耗时
- 1 小时以内
- 新手友好度
- 78/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- typescript
- 领域
- backend
调研方向
大小检查位于 src/everything/server/tasks.ts 中 tasks/update 处理程序的第 411-424 行附近,其中 JSON.stringify(answered).length 与 MAX_ANSWER_BYTES 进行比较。请先阅读该代码块,然后发送一个包含 33,000 个 é 字符的答案以复现问题,并确认它被接受。当度量按 UTF-8 字节计数,使该负载返回 -32602 错误且任务保持不变时,即完成。
由索引模型根据 Issue 内容生成。
描述
| Field | Value |
|---|---|
| Labels (suggested) | bug, everything, tasks |
| Reproduced on | commit 163a5e935756 (default branch at the time of analysis) |
| Introduced by | commit 27b2d2415cdc0ad13782edb8b6c3cecd7846b78a — "feat(everything): serve both protocol eras and demonstrate …" |
Summary
The everything server's tasks/update handler limits the size of client-supplied answers with
MAX_ANSWER_BYTES, but computes the size with JSON.stringify(answered).length. In JavaScript that is
the number of UTF-16 code units, not bytes. For any non-ASCII answer the guard under-counts by up to
3× and lets a payload that is larger than the documented byte budget through, after which the oversized
answers are handed to task.behavior.receive() and retained in the task.
Affected code
src/everything/server/tasks.ts, lines 411–424 at 163a5e935756:
const answered = Object.fromEntries(
Object.entries(inputResponses).filter(([key]) =>
outstanding.includes(key),
),
);
const size = JSON.stringify(answered).length; // <- UTF-16 code units, not bytes
if (size > MAX_ANSWER_BYTES) {
throw new ProtocolError(
ProtocolErrorCode.InvalidParams,
`Invalid params for tasks/update: the answers are ${size} bytes, over the ${MAX_ANSWER_BYTES}-byte limit; the task is unchanged`,
);
}
if (Object.keys(answered).length > 0) {
task.behavior.receive(answered, now);
}
The constant is named MAX_ANSWER_BYTES and the error message promises a byte comparison
("the answers are N bytes"), so the unit mismatch is internal to the handler, not a documentation
question.
Expected behavior
tasks/update rejects the request with -32602 Invalid params because the serialized answers are
66 012 bytes, which is over the 65 536-byte budget; the task is left unchanged.
Actual behavior
Calling tasks/update with one outstanding answer whose text is 33 000 é characters, the handler accepts the payload and retains it in the task (MAX_ANSWER_BYTES = 65536).
Payload: one outstanding answer whose text is 33 000 U+00E9 (é) characters.
MAX_ANSWER_BYTES = 65536.
{"mode":"bug","outcome":{"accepted":true,"received":66012}}
TASKS_UPDATE_BYTE_BUDGET_BYPASSED
JSON.stringify(...).length= 33 012 → passes the guard.- UTF-8
Buffer.byteLength(..., "utf8")= 66 012 → 476 bytes over the limit. - The task retained the answer (
received: 66012), so the documented limit was not enforced.
Control: the same probe against a copy of tasks.ts with line 416 changed to
Buffer.byteLength(JSON.stringify(answered), "utf8") produces
{"mode":"control","outcome":{"accepted":false,
"error":"-32602: Invalid params for tasks/update: the answers are 66012 bytes, over the 65536-byte limit; the task is unchanged"}}
CONTROL_REJECTED_BY_BYTES
and retained is undefined — the divergence disappears with that one line.
Why it matters
- The limit is a resource guard for retained task state (the answer is stored until the task settles).
Non-ASCII answers — ordinary for any non-English client, or for base64/emoji content — get up to a 3×
larger effective budget. 33 000 characters of CJK/Cyrillic/emoji-style text becomes ~99 KB against a
stated 64 KiB ceiling. - The rejection message itself reports a byte count that is not a byte count, which makes the failure
mode confusing for client authors.
Suggested fix
Use a byte-accurate measure:
const size = Buffer.byteLength(JSON.stringify(answered), "utf8");
(new TextEncoder().encode(...).length if the file must stay runtime-agnostic). Note that both forms
count the UTF-8 serialization of the JSON text, which is what the limit documents.
- 主要语言
- TypeScript
- 星标
- 91.7k
- 派生
- 11.9k
- 平均合并
- 3 小时 48 分钟
- 30 天内合并 PR
- 180
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
modelcontextprotocol/servers 的其他 Issue
-
documentation server-memory v2
难度 2/5 1-3 小时 新手友好度 68/100
modelcontextprotocol/servers#5457 ·
维护者通常 1 天内回复
-
memory: lock heartbeat refreshes a lock file the process no longer owns可能已有人在做 @cliffhall 今天认领。 未关闭bug server-memory v2
难度 2/5 1-3 小时 新手友好度 68/100
modelcontextprotocol/servers#5418 · 1 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
memory: add descriptions to the top-level array parameters of create_entities, create_relations, add_observations and delete_observations可能已有人在做 @cliffhall 今天认领。 未关闭enhancement server-memory v2
难度 2/5 1-3 小时 新手友好度 88/100
modelcontextprotocol/servers#5416 · 2 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
everything: bind the HTTP transports to loopback by default, with HOST to widen可能已有人在做 @cliffhall 今天认领。 未关闭enhancement server-everything v2
难度 2/5 1-3 小时 新手友好度 72/100
modelcontextprotocol/servers#5371 · 1 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
TypeScript Dockerfiles (filesystem, memory, sequentialthinking) ignore the workspace lockfile and overrides可能已有人在做 @cliffhall 今天认领。 未关闭chore server-filesystem server-memory server-sequentialthinking v2
难度 2/5 1-3 小时 新手友好度 78/100
modelcontextprotocol/servers#5345 · 已指派 1 人 ·
维护者通常 1 天内回复
查看 modelcontextprotocol/servers 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 83/100
appandflow/stim#5258 ·
维护者通常 1 天内回复
-
area/docs framework/hugo help wanted language/markdown
难度 1/5 1 小时以内 新手友好度 88/100
维护者通常 1 天内回复
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
难度 1/5 1 小时以内 新手友好度 68/100
lingdojo/kana-dojo#33660 · 1 条评论 · 5 个 reaction ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 78/100
drizzle-team/drizzle-orm#6639 ·
维护者通常 4 天内回复
-
enhancement
难度 2/5 1-3 小时 新手友好度 76/100