forked from 1b-agent/opencadstudio-upstream
feat(mcp): expose entity creation schemas on demand
Expose creation fields, constraints and examples for the 11 supported types without expanding startup tool schemas. Share the creation contract and constructor with execution to keep accepted inputs consistent. Validation: MCP, contract/factory and entity creation regression tests.
This commit is contained in:
1 parent
a5435a6df6
commit
97afb57333
10 files changed
+1118
-488
No files matched your search
@@ -0,0 +1,42 @@
|
||||
//! Offline validation of application-owned JSON Schema contracts.
|
||||
use serde_json::Value;
|
||||
|
||||
/// Only application-owned schemas enter this cache; client payloads are instances,
|
||||
/// never schemas. Disable JSON Schema HTTP/file resolution at the dependency level.
|
||||
pub fn validate_shape(schema: &Value, value: &Value, path: &str) -> Result<(), String> {
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex, OnceLock},
|
||||
};
|
||||
static VALIDATORS: OnceLock<Mutex<HashMap<String, Arc<jsonschema::Validator>>>> =
|
||||
OnceLock::new();
|
||||
let key = schema.to_string();
|
||||
let validator = {
|
||||
let mut cache = VALIDATORS
|
||||
.get_or_init(Default::default)
|
||||
.lock()
|
||||
.expect("schema cache poisoned");
|
||||
if let Some(validator) = cache.get(&key) {
|
||||
Arc::clone(validator)
|
||||
} else {
|
||||
let validator = Arc::new(
|
||||
jsonschema::validator_for(schema).expect("application-owned schema must compile"),
|
||||
);
|
||||
cache.insert(key, Arc::clone(&validator));
|
||||
validator
|
||||
}
|
||||
};
|
||||
validator.validate(value).map_err(|error| {
|
||||
let field = match error.kind() {
|
||||
jsonschema::error::ValidationErrorKind::Required { property } => property.as_str(),
|
||||
jsonschema::error::ValidationErrorKind::AdditionalProperties { unexpected } => {
|
||||
unexpected.first().map(String::as_str)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let suffix = field
|
||||
.map(|key| format!("/{}", key.replace('~', "~0").replace('/', "~1")))
|
||||
.unwrap_or_default();
|
||||
format!("{path}{}{suffix}: {error}", error.instance_path())
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user