mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-10-16 07:16:36 +08:00
fix(mcp): dispatch progress with arbitrary-precision JSON
This commit is contained in:
@@ -119,3 +119,15 @@
|
||||
- Docs最终生成exit0且无预渲染错误,60条route完成;13/13 sitemap、canonical与og:url一致(主页仅标准化尾斜杠),14/14 llms公开链接均有实际文件,`llms-full.txt` 34661 bytes;无重复base path。证据为 `docs-ci/docs-mdast-fixed-generate.log`、`docs-ci/docs-validation-summary.json`。独立锁文件保留解析后的Content3.15.2/MDC0.22.2,不再额外覆盖其版本;只有Markdown序列化依赖的2.1.2 override。Docs边界检查通过、既有守卫24/24通过、静态验证7/7通过。
|
||||
- 修复提交写集仅为Docs四文件(含独立生成锁文件)、Pages workflow、MCP现有测试和本计划,共7文件;用户全局gitignore忽略pnpm-lock.yaml,显式纳入这一个必要锁文件,不修改全局规则。其余CLI/TUI/架构/运维文档开发均保留。使用独立索引复核及正常hook提交,不重打tag。
|
||||
- 当前Quality run `37932718117` 最终cancelled:MCP并发进度测试从13:13:36至13:51:24持续等待,触发60分钟job预算;其余任务成功。新增认领 `lime-rs/crates/mcp/src/bridge_client.rs` 的现有测试区,先复现、定位无限等待,再定向验证,不以提高作业timeout掩盖卡死。
|
||||
- CI修复提交 `8e45bf77308ce50d719340a0030f3809a491178a` 已推送main,唯一父为本版release commit;远端v1.153.0仍保持原SHA。正常pre-commit实际执行6个匹配路径全部通过(workflow另有显式静态验证),未跳过hook;独立索引仅同步本轮7路径,所有并行开发保留。
|
||||
- Docs恢复run `37942348705` 的build/deploy均success:https://github.com/limecloud/lime/actions/runs/37942348705 。公开Pages实读首页、sitemap、llms索引/完整文本、API正文和raw Markdown共6个HTTP200;13条sitemap和canonical正确,完整文本34661 bytes,与本地生成一致(`docs-ci/docs-public-summary.json`)。Docs失败已闭环;新Quality run `37942348723` 继续核验MCP等待修正。
|
||||
|
||||
- Quality恢复run `37942348723` 的Rust Full在MCP169项中168通过/1失败,5秒诊断明确为工具response成功但token0没有progress;首次test-only等待修正只揭露问题,未修复根因。Frontend Full、Bridge/Contracts、Integrity、GUI Smoke均成功,Windows继续运行。
|
||||
- 依赖图对照及最小wire复现确认:`execpolicy -> starlark`在完整workspace/CLI图启用`serde_json/arbitrary_precision`,RMCP0.12的untagged通知解析把含浮点`progress/total`的标准`notifications/progress`落到CustomNotification,因此没有调用进度dispatcher。本地只开preserve_order仍通过;开启arbitrary_precision的真实duplex回归继续验证失败。最小复现`mcp-wire-repro.log`同时证明直接解析ProgressNotificationParam成功、同一wire解析ServerNotification落到CustomNotification。
|
||||
- 本轮扩展窄写集至`lime-rs/crates/mcp/src/client_service.rs`和`lime-rs/crates/mcp/Cargo.toml`:仅在唯一current客户端服务边界按标准method恢复强类型进度通知,再交回既有handler;test依赖启用生产图已有arbitrary_precision以稳定覆盖真实transport回归。未知自定义method继续既有路径,非法标准参数返回协议错误;不创建第二dispatcher或runtime、不提高CI预算。
|
||||
- 公开npm五包与provenance/四平台包结构全部核验;真实registry安装后13个文件与已验证tarball SHA256匹配、launcher symlink正确,`lime --version`=1.153.0。已发布CLI完整stdio及human双TTY成功(`npm-public-flow.log`,thread `01a12114-4406-7830-a831-42064abf251a`、turn `turn_0dc86d5284de45bebe09ca43df96722c`)。已发布TUI complete首轮180秒总预算未完成,保留timeout并定向续验,不计为通过。
|
||||
|
||||
- 开启arbitrary_precision的现有真实duplex用例在修复前稳定重现`call A: no progress for token Number(0)`(`mcp-arbitrary-precision-repro.log` exit101),与CI完全相同;边界修复后MCP库172/172通过(`mcp-normalized-tests.log` exit0)。dev依赖只增加已有serde_json特性,不变版本/锁文件;生产统一图与定向测试从此一致。新增回归覆盖浮点wire、非法标准参数拒绝、未知method及extensions保留。
|
||||
- `npm run test:contracts`完整exit0(`contracts-mcp-normalized.log`),`npm run verify:app-version`通过=1.153.0;本轮没有新App Server/IPC方法或schema变化。现有progress隔离断言及有界诊断全部保留。
|
||||
- 公开npm TUI续验已完成complete PTY、输入/导航/补全/推理、stdio reasoning/cold resume、focus/reconnect;最后resize四项中一项未观察到启动输入,原日志保留(`npm-tui-public-retry.log`)。原四项未改代码/超时定向重验4/4通过(`npm-tui-resize-retry.log` exit0),完整入口再次收口以保留最终ledger/event序列断言。
|
||||
- 本次提交仅MCP client_service、MCP dev-dependency manifest与发布计划三文件;之前的bridge_client测试修改已在main。current为唯一MCP服务的标准通知解析,test-only为生产feature图回归,compat/deprecated/dead无新增。用户授权继续完整发布,窄提交/推送沿用既有授权;v1.153.0公开tag保持`3528a3c7`,生产修复进入main,不暗改已发布binary。
|
||||
|
||||
@@ -31,3 +31,5 @@ libc = "0.2"
|
||||
[dev-dependencies]
|
||||
futures.workspace = true
|
||||
tempfile.workspace = true
|
||||
# Match the CLI/workspace feature graph when testing the real MCP wire.
|
||||
serde_json = { workspace = true, features = ["arbitrary_precision"] }
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
use crate::client::LimeMcpClient;
|
||||
use crate::elicitation::ElicitationResponse;
|
||||
use lime_core::DynEmitter;
|
||||
use rmcp::model::{ClientInfo, ClientResult, CustomResult, ServerNotification, ServerRequest};
|
||||
use rmcp::model::{
|
||||
ClientInfo, ClientResult, ConstString, CustomResult, ProgressNotification,
|
||||
ProgressNotificationMethod, ServerNotification, ServerRequest,
|
||||
};
|
||||
use rmcp::service::{NotificationContext, RequestContext, Service};
|
||||
use rmcp::RoleClient;
|
||||
use serde::Serialize;
|
||||
@@ -125,7 +128,7 @@ impl Service<RoleClient> for LimeMcpClientService {
|
||||
) -> Result<(), rmcp::ErrorData> {
|
||||
<LimeMcpClient as Service<RoleClient>>::handle_notification(
|
||||
&self.handler,
|
||||
notification,
|
||||
normalize_notification(notification)?,
|
||||
context,
|
||||
)
|
||||
.await
|
||||
@@ -136,6 +139,34 @@ impl Service<RoleClient> for LimeMcpClientService {
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_notification(
|
||||
notification: ServerNotification,
|
||||
) -> Result<ServerNotification, rmcp::ErrorData> {
|
||||
match notification {
|
||||
ServerNotification::CustomNotification(notification)
|
||||
if notification.method == ProgressNotificationMethod::VALUE =>
|
||||
{
|
||||
// RMCP's untagged enum can classify floating-point progress as custom
|
||||
// when the CLI's dependency graph enables serde_json/arbitrary_precision.
|
||||
let params = serde_json::from_value(notification.params.unwrap_or(Value::Null))
|
||||
.map_err(|error| {
|
||||
rmcp::ErrorData::invalid_params(
|
||||
format!("invalid progress notification: {error}"),
|
||||
None,
|
||||
)
|
||||
})?;
|
||||
Ok(ServerNotification::ProgressNotification(
|
||||
ProgressNotification {
|
||||
method: ProgressNotificationMethod,
|
||||
params,
|
||||
extensions: notification.extensions,
|
||||
},
|
||||
))
|
||||
}
|
||||
notification => Ok(notification),
|
||||
}
|
||||
}
|
||||
|
||||
fn elicitation_response_result(
|
||||
response: ElicitationResponse,
|
||||
) -> Result<CustomResult, rmcp::ErrorData> {
|
||||
@@ -158,3 +189,51 @@ struct CreateElicitationResultWithMeta {
|
||||
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
|
||||
meta: Option<Value>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rmcp::model::{CustomNotification, NumberOrString, ProgressToken};
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn fractional_progress_wire_reaches_typed_handler() {
|
||||
let wire = r#"{"method":"notifications/progress","params":{"progressToken":7,"progress":0.25,"total":1.5,"message":"working"}}"#;
|
||||
let notification: ServerNotification = serde_json::from_str(wire).unwrap();
|
||||
let notification = normalize_notification(notification).unwrap();
|
||||
let ServerNotification::ProgressNotification(notification) = notification else {
|
||||
panic!("standard progress must reach the typed handler");
|
||||
};
|
||||
assert_eq!(
|
||||
notification.params.progress_token,
|
||||
ProgressToken(NumberOrString::Number(7))
|
||||
);
|
||||
assert_eq!(notification.params.progress, 0.25);
|
||||
assert_eq!(notification.params.total, Some(1.5));
|
||||
assert_eq!(notification.params.message.as_deref(), Some("working"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_standard_progress_returns_protocol_error() {
|
||||
for params in [None, Some(json!({"progressToken": 7, "progress": "bad"}))] {
|
||||
let notification = CustomNotification::new(ProgressNotificationMethod::VALUE, params);
|
||||
let error = normalize_notification(notification.into()).unwrap_err();
|
||||
assert_eq!(error.code, rmcp::model::ErrorCode::INVALID_PARAMS);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_notification_keeps_payload_and_extensions() {
|
||||
let params = json!({"progress": 0.25, "domain": "custom"});
|
||||
let mut notification =
|
||||
CustomNotification::new("notifications/custom", Some(params.clone()));
|
||||
notification.extensions.insert(42_u32);
|
||||
let notification = normalize_notification(notification.into()).unwrap();
|
||||
let ServerNotification::CustomNotification(notification) = notification else {
|
||||
panic!("unknown methods must remain custom notifications");
|
||||
};
|
||||
assert_eq!(notification.method, "notifications/custom");
|
||||
assert_eq!(notification.params, Some(params));
|
||||
assert_eq!(notification.extensions.get::<u32>(), Some(&42));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user