状态:Phase 0-3 已实施,Phase 4 自动验证已完成(人工场景见 §6.2) 背景:生产不允许同账号多端登录(联调可用环境变量开启);MCP 相关能力不再使用,全部移除。 关联审计:
CaseRoutingDataSource/CaseDataSourceRegistry/CaseContextHolder/ 跨案件接口.
| 位置 | 内容 |
|---|---|
| 后端主代码 | module/agent/mcp/:AgentScopeMcpToolProvider、AgentScopeToolCallback、McpCaseSession;vo/McpServerInfoVO |
| 后端接口 | GET /capability/mcp 及 AgentCapabilityService.mcpServerInfo() |
| 配置 | application.yaml 的 spring.ai.mcp.server 段;SaTokenWebConfig 的 4 条 MCP 白名单 |
| 依赖 | spring-ai-starter-mcp-server-webmvc、spring-ai-starter-mcp-server |
| 测试 | AgentScopeMcpToolProviderTest、McpCaseSessionSlotsTest |
| 前端 | 无功能性 MCP;仅 4 个 MCP 专用工具展示名和注释 |
| 数据库 | 无 MCP 表/列/种子数据 |
/capability/mcp 接口、service 方法、VO 及 Impl 中的 @Value/字段/joinPathspring.ai 配置段、2 个 Maven starter、sa-token MCP 白名单spring-ai-bom 与 spring-ai-tika-document-reader(Tika 仍被文件解析使用)python ai-server/scripts/gen_field_labels.py,清除 field-labels.json 中的 MCP 类ai-electron/data/codex-home/config.toml 的 [mcp_servers.mcpsse]QUICK_START.md、AI_AGENT.md、docs/design/ai-gateway-user-sticky-routing.mdgrep -ri mcp ai-server/src ai-electron/frontend/src ai-electron/electron ai-server/pom.xml 无业务命中agentscope-harness 会传递引入 io.modelcontextprotocol.sdk:mcp。本期已在 ai-server/pom.xml 对 agentscope-harness 增加 exclusion,产物 jar 中不再包含任何 mcp-*.jar;AiServerApplicationTests.contextLoads() 与全量单测已回归通过。
sa-token 配置从 application-dev.yaml 移到基础 application.yaml,避免新增 prod/test profile 时漏配而退回 Sa-Token 默认值is-concurrent: ${SA_TOKEN_CONCURRENT:false}:默认生产单端;联调启动加 SA_TOKEN_CONCURRENT=true 可多端is-share: false、max-login-count: -1 保留并补注释SysUserService、VerifyPwdDTO、QUICK_START.md 的说明ownerToken:联调多端时,一个 token 退出/换案不会关闭另一个 token 正在使用的案件
CaseDataSourceRegistry.OpenedCase 增加 ownerTokenCaseInfoService.open/closeOwnedCases 按 token 过滤SysUserService.releaseOpenedCases(userId, token):登出按 token,管理员踢人按用户CaseSessionListener.doLogout 按 token;doKickout/doReplaced 按用户新增 common/context/CaseAccessGuard:
requireOpenCaseId():无案件 400requireCurrentCase(requestedCaseId):请求 caseId 与当前案件不一致 409requireOwnedCurrentCase(caseId):再校验 case_info.owner_idowner_id == null 的案件记 warn 并放行,待数据迁移后收紧AgentChatServiceImpl:createSession / listSessions / getSession / listMessages / requireSessionInOpenCaseInsightServiceImpl:createSession / listSessions / listClues / getMessageTrace / requireSessionAiCleanService:run / judge / revert / dataPageChatAttachmentService.upload:请求 caseId 必须等于当前案件AgentPythonFileController:workspaceId 必须等于当前案件requireSessionInOpenCase / InsightServiceImpl.requireSession 去掉“无案件放行”appWindow.ts 新增 reloadSmartWindowIfOpen():只在工作台已打开时 reloadIfOpencase.ts 的 afteropenCaseAction 换案后调用,避免工作台继续用旧 workspaceIdAPP_RESET 关闭工作台SpecialDateService.create/update 增加 @DS("slave") + @Transactional
saveBatch 走 SqlSession.insert,不经过 mapper 代理,mapper 上的 @DS 不生效saveSpecialDate(self-invocation 不生效)EntryRecMapper 补 @DS("slave")common/utils/WorkspacePathGuard:归一化 + 符号链接 + toRealPath 三层校验/dm/preFile:目录/文件都必须在 workspace/<当前caseId> 内/dm/doClean:优先用 DB 中 fileId 对应路径,并限制在当前案件;不再信任客户端 filePath/sys/downloadFile:下载路径必须在当前案件 workspace 内/py/files/{workspaceId}/...:workspaceId 必须等于当前案件CaseInfoService.open:dbPath 必须存在、非空;不限制所在目录(允许历史案件/外部挂载目录)CaseDataSourceRegistry.open:文件不存在/为空直接拒绝,避免 DuckDB 静默建空库CaseInfoService.open:先校验容量与 dbPath → 先打开新数据源 → 再关闭旧数据源;失败不丢旧案件CaseActivityProbe,有在途清洗/治理任务返回 409CaseDataCache.initCache 失败只记日志,不阻断开案delete 删除当前案件时清 token、SSE、GOVERN_CONFIGCaseDataCleanupService:按 caseId 清理聊天/研判/AI 清洗/附件(行、文件、向量)GovernCache.getSql() 不再写静态 TABLE_MAPPINGS,消除并发串表名CaseDataCache.cleanGovernConfig 在 exit/delete/closeOwnedCases/登出/回收路径调用SseDTO 增加 caseId,发送时按当前上下文补齐;前端进度页丢弃非当前案件的帧strict: true:@DS 拼错直接报错,不再静默回退主库mvn -pl ai-server -am -DskipTests compile / packageSqlAnalysisToolTest:9/9(含 read_csv/glob/read_parquet 拒绝)AgentCapabilityToolkitTest:1/1SpecialDateServiceRoutingTest:1/1(@DS + @Transactional)CaseAccessGuardTest:4/4(无案件/上下文不一致/他人案件/本人案件)AiServerApplicationTests.contextLoads():1/1(含 strict=true、MCP SDK exclusion)pnpm build 通过pnpm type:check:改动文件无新增错误(仓库存在既有 src/trans 等类型错误)mcp-*.jar、无 MCP class/mcpstreamable、/mcpsse、/capability/mcp 均不可用;Agent 对话/工具正常special_date;失败回滚../../other-case/x.xlsx、绝对路径、符号链接均被拒绝-- 无归属案件
select id, name, db_path from case_info where owner_id is null;
-- db_path 为空(无法开案;目录位置不再限制,只要文件存在即可)
select id, name, db_path from case_info
where db_path is null or trim(db_path) = '';
-- 主库孤儿 AI 数据(示例:不存在的 case)
select distinct case_id from agent_chat_session
where case_id not in (select id from case_info);
select distinct case_id from agent_insight_session
where case_id not in (select id from case_info);
select distinct case_id from ai_clean_job
where case_id not in (select id from case_info);
owner_id == null 的历史案件仍按 warn 放行,需迁移后收紧。