fix(auth): re-catalog mcp tools on quality-only edits in refresh change-detection

This commit is contained in:
pnoker
2026-06-19 07:52:43 +08:00
parent 12d611d315
commit b83ccc831a
2 changed files with 44 additions and 6 deletions
@@ -159,6 +159,26 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
record.setEnableFlag(Boolean.TRUE.equals(quality.getHidden()) ? (byte) 1 : (byte) 0);
}
/**
* True when any persisted catalog column differs between the existing row and the freshly-built
* candidate — including the quality fields (title/remark/hints) that a JSON-only edit can change
* without altering the schema hash. Keeps the catalog in sync with x-dc3-ai/description edits.
*/
static boolean toolChanged(McpToolRecord existing, McpToolRecord candidate) {
return !Objects.equals(existing.getSchemaHash(), candidate.getSchemaHash())
|| !Objects.equals(existing.getPermissionCode(), candidate.getPermissionCode())
|| !Objects.equals(existing.getApiPath(), candidate.getApiPath())
|| !Objects.equals(existing.getRiskLevel(), candidate.getRiskLevel())
|| !Objects.equals(existing.getEnableFlag(), candidate.getEnableFlag())
|| !Objects.equals(existing.getToolExt(), candidate.getToolExt())
|| !Objects.equals(existing.getToolTitle(), candidate.getToolTitle())
|| !Objects.equals(existing.getRemark(), candidate.getRemark())
|| !Objects.equals(existing.getReadOnlyHint(), candidate.getReadOnlyHint())
|| !Objects.equals(existing.getDestructiveHint(), candidate.getDestructiveHint())
|| !Objects.equals(existing.getIdempotentHint(), candidate.getIdempotentHint())
|| !Objects.equals(existing.getOpenWorldHint(), candidate.getOpenWorldHint());
}
private static String normalizeRisk(String declared) {
String value = StringUtils.upperCase(StringUtils.trimToEmpty(declared));
return switch (value) {
@@ -464,12 +484,7 @@ public class OAuthMcpRuntimeServiceImpl implements OAuthMcpRuntimeService {
if (existing == null) {
candidate.setId(IdWorker.getId());
changed += oauthMcpMapper.insertTool(candidate);
} else if (!Objects.equals(existing.getSchemaHash(), candidate.getSchemaHash())
|| !Objects.equals(existing.getPermissionCode(), candidate.getPermissionCode())
|| !Objects.equals(existing.getApiPath(), candidate.getApiPath())
|| !Objects.equals(existing.getRiskLevel(), candidate.getRiskLevel())
|| !Objects.equals(existing.getEnableFlag(), candidate.getEnableFlag())
|| !Objects.equals(existing.getToolExt(), candidate.getToolExt())) {
} else if (toolChanged(existing, candidate)) {
candidate.setId(existing.getId());
changed += oauthMcpMapper.updateTool(candidate);
}
@@ -74,4 +74,27 @@ class ToolQualityMergeTest {
assertThat(r.getEnableFlag()).isEqualTo((byte) 1); // hidden -> disabled
}
@Test
void toolChangedDetectsHintOnlyEdit() {
McpToolRecord existing = record("GET");
OAuthMcpRuntimeServiceImpl.applyQuality(existing, ToolQuality.builder()
.riskLevel("LOW").destructive(false).idempotent(true).openWorld(false).hidden(false).build());
McpToolRecord candidate = record("GET");
OAuthMcpRuntimeServiceImpl.applyQuality(candidate, ToolQuality.builder()
.riskLevel("LOW").destructive(true).idempotent(true).openWorld(false).hidden(false).build());
// schemaHash/riskLevel/enableFlag/toolExt unchanged; only destructiveHint differs.
assertThat(OAuthMcpRuntimeServiceImpl.toolChanged(existing, candidate)).isTrue();
}
@Test
void toolChangedIsFalseForIdenticalRecords() {
McpToolRecord a = record("GET");
OAuthMcpRuntimeServiceImpl.applyQuality(a, ToolQuality.builder().riskLevel("LOW").build());
a.setSchemaHash("h");
McpToolRecord b = record("GET");
OAuthMcpRuntimeServiceImpl.applyQuality(b, ToolQuality.builder().riskLevel("LOW").build());
b.setSchemaHash("h");
assertThat(OAuthMcpRuntimeServiceImpl.toolChanged(a, b)).isFalse();
}
}