chore(secrets-mcp): 0.5.1 — 移除 entry type 归一化,MCP 参数兼容字符串形式
All checks were successful
Secrets MCP — Build & Release / 检查 / 构建 / 发版 (push) Successful in 4m22s
Secrets MCP — Build & Release / 部署 secrets-mcp (push) Successful in 6s

- 去掉 taxonomy 对 entry type 的自动映射与 metadata.subtype 回填;仅 trim 后入库
- MCP tools:Vec/Map/bool 等可选字段支持 JSON 内嵌字符串解析,并改进解析失败提示
- 新增 deser 单元测试;README/AGENTS 与 models 注释同步

Made-with: Cursor
This commit is contained in:
voson
2026-04-04 21:27:33 +08:00
parent 0ffb81e57f
commit aefad33870
9 changed files with 335 additions and 142 deletions

View File

@@ -1,111 +1,4 @@
use serde_json::{Map, Value};
fn normalize_token(input: &str) -> String {
input.trim().to_lowercase().replace('_', "-")
}
fn normalize_subtype_token(input: &str) -> String {
normalize_token(input)
}
fn map_legacy_entry_type(input: &str) -> Option<(&'static str, &'static str)> {
match input {
"log-ingestion-endpoint" => Some(("service", "log-ingestion")),
"cloud-api" => Some(("service", "cloud-api")),
"git-server" => Some(("service", "git")),
"mqtt-broker" => Some(("service", "mqtt-broker")),
"database" => Some(("service", "database")),
"monitoring-dashboard" => Some(("service", "monitoring")),
"dns-api" => Some(("service", "dns-api")),
"notification-webhook" => Some(("service", "webhook")),
"api-endpoint" => Some(("service", "api-endpoint")),
"credential" | "credential-key" => Some(("service", "credential")),
"key" => Some(("service", "credential")),
_ => None,
}
}
/// Normalize entry `type` and optionally backfill `metadata.subtype` for legacy values.
///
/// This keeps backward compatibility:
/// - stable primary types stay unchanged
/// - known legacy long-tail types are mapped to `service` + `metadata.subtype`
/// - unknown values are kept (normalized to kebab-case) instead of hard failing
pub fn normalize_entry_type_and_metadata(
entry_type: &str,
metadata: &mut Map<String, Value>,
) -> String {
let original_raw = entry_type.trim();
let normalized = normalize_token(original_raw);
if normalized.is_empty() {
return String::new();
}
if let Some((mapped_type, mapped_subtype)) = map_legacy_entry_type(&normalized) {
if !metadata.contains_key("subtype") {
metadata.insert(
"subtype".to_string(),
Value::String(mapped_subtype.to_string()),
);
}
if !metadata.contains_key("_original_type") && original_raw != mapped_type {
metadata.insert(
"_original_type".to_string(),
Value::String(original_raw.to_string()),
);
}
return mapped_type.to_string();
}
if let Some(subtype) = metadata.get_mut("subtype")
&& let Some(s) = subtype.as_str()
{
*subtype = Value::String(normalize_subtype_token(s));
}
normalized
}
/// Canonical secret type options for UI dropdowns.
pub const SECRET_TYPE_OPTIONS: &[&str] = &[
"text", "password", "token", "api-key", "ssh-key", "url", "phone", "id-card",
];
#[cfg(test)]
mod tests {
use super::*;
use serde_json::{Map, Value};
#[test]
fn normalize_entry_type_maps_legacy_type_and_backfills_metadata() {
let mut metadata = Map::new();
let normalized = normalize_entry_type_and_metadata("git-server", &mut metadata);
assert_eq!(normalized, "service");
assert_eq!(
metadata.get("subtype"),
Some(&Value::String("git".to_string()))
);
assert_eq!(
metadata.get("_original_type"),
Some(&Value::String("git-server".to_string()))
);
}
#[test]
fn normalize_entry_type_normalizes_existing_subtype() {
let mut metadata = Map::new();
metadata.insert(
"subtype".to_string(),
Value::String("Cloud_API".to_string()),
);
let normalized = normalize_entry_type_and_metadata("service", &mut metadata);
assert_eq!(normalized, "service");
assert_eq!(
metadata.get("subtype"),
Some(&Value::String("cloud-api".to_string()))
);
}
}