use crate::{
ResourceProvider,
mcp_integration::core::{ScimMcpServer, ScimToolResult},
mcp_integration::handlers::etag_to_raw_version,
multi_tenant::TenantContext,
operation_handler::ScimOperationRequest,
resource::version::{Http, Raw, ScimVersion},
};
use serde_json::{Value, json};
pub async fn handle_create_user<P: ResourceProvider + Send + Sync + 'static>(
server: &ScimMcpServer<P>,
arguments: Value,
) -> ScimToolResult {
let user_data = match arguments.get("user_data") {
Some(data) => data.clone(),
None => {
return ScimToolResult {
success: false,
content: json!({"error": "Missing user_data parameter"}),
metadata: None,
};
}
};
let tenant_context = arguments
.get("tenant_id")
.and_then(|t| t.as_str())
.map(|id| TenantContext::new(id.to_string(), "mcp-client".to_string()));
let mut request = ScimOperationRequest::create("User".to_string(), user_data);
if let Some(tenant) = tenant_context {
request = request.with_tenant(tenant);
}
let response = server.operation_handler.handle_operation(request).await;
if response.success {
let content = response
.data
.unwrap_or_else(|| json!({"status": "created"}));
let mut metadata = json!({
"operation": "create_user",
"resource_type": "User",
"resource_id": response.metadata.resource_id
});
if let Some(etag) = response.metadata.additional.get("etag") {
if let Some(raw_version) = etag_to_raw_version(etag) {
metadata["version"] = json!(raw_version);
}
}
ScimToolResult {
success: true,
content,
metadata: Some(metadata),
}
} else {
ScimToolResult {
success: false,
content: json!({
"error": response.error.unwrap_or_else(|| "Create failed".to_string()),
"error_code": "CREATE_USER_FAILED"
}),
metadata: None,
}
}
}
pub async fn handle_get_user<P: ResourceProvider + Send + Sync + 'static>(
server: &ScimMcpServer<P>,
arguments: Value,
) -> ScimToolResult {
let user_id = match arguments.get("user_id").and_then(|id| id.as_str()) {
Some(id) => id,
None => {
return ScimToolResult {
success: false,
content: json!({"error": "Missing user_id parameter"}),
metadata: None,
};
}
};
let tenant_context = arguments
.get("tenant_id")
.and_then(|t| t.as_str())
.map(|id| TenantContext::new(id.to_string(), "mcp-client".to_string()));
let mut request = ScimOperationRequest::get("User".to_string(), user_id.to_string());
if let Some(tenant) = tenant_context {
request = request.with_tenant(tenant);
}
let response = server.operation_handler.handle_operation(request).await;
if response.success {
let content = response
.data
.unwrap_or_else(|| json!({"status": "retrieved"}));
let mut metadata = json!({
"operation": "get_user",
"resource_type": "User",
"resource_id": user_id
});
if let Some(etag) = response.metadata.additional.get("etag") {
if let Some(raw_version) = etag_to_raw_version(etag) {
metadata["version"] = json!(raw_version);
}
}
ScimToolResult {
success: true,
content,
metadata: Some(metadata),
}
} else {
let error_msg = response
.error
.unwrap_or_else(|| "User not found".to_string());
ScimToolResult {
success: false,
content: json!({
"error": error_msg,
"error_code": if error_msg.contains("not found") { "USER_NOT_FOUND" } else { "GET_USER_FAILED" },
"user_id": user_id
}),
metadata: Some(json!({
"operation": "get_user",
"resource_id": user_id
})),
}
}
}
pub async fn handle_update_user<P: ResourceProvider + Send + Sync + 'static>(
server: &ScimMcpServer<P>,
arguments: Value,
) -> ScimToolResult {
let user_id = match arguments.get("user_id").and_then(|id| id.as_str()) {
Some(id) => id,
None => {
return ScimToolResult {
success: false,
content: json!({"error": "Missing user_id parameter"}),
metadata: None,
};
}
};
let user_data = match arguments.get("user_data") {
Some(data) => data.clone(),
None => {
return ScimToolResult {
success: false,
content: json!({"error": "Missing user_data parameter"}),
metadata: None,
};
}
};
let tenant_context = arguments
.get("tenant_id")
.and_then(|t| t.as_str())
.map(|id| TenantContext::new(id.to_string(), "mcp-client".to_string()));
let mut request =
ScimOperationRequest::update("User".to_string(), user_id.to_string(), user_data);
if let Some(tenant) = tenant_context {
request = request.with_tenant(tenant);
}
if let Some(expected_version_str) = arguments.get("expected_version").and_then(|v| v.as_str()) {
let version_result = expected_version_str
.parse::<ScimVersion<Http>>()
.map(|v| v.into())
.or_else(|_| expected_version_str.parse::<ScimVersion<Raw>>());
match version_result {
Ok(version) => {
request = request.with_expected_version(version);
}
Err(_) => {
return ScimToolResult {
success: false,
content: json!({
"error": format!("Invalid expected_version format: '{}'. Use raw format (e.g., 'abc123def') or ETag format (e.g., 'W/\"abc123def\"')", expected_version_str),
"error_code": "INVALID_VERSION_FORMAT"
}),
metadata: None,
};
}
}
}
let response = server.operation_handler.handle_operation(request).await;
if response.success {
let content = response
.data
.unwrap_or_else(|| json!({"status": "updated"}));
let mut metadata = json!({
"operation": "update_user",
"resource_type": "User",
"resource_id": user_id
});
if let Some(etag) = response.metadata.additional.get("etag") {
if let Some(raw_version) = etag_to_raw_version(etag) {
metadata["version"] = json!(raw_version);
}
}
ScimToolResult {
success: true,
content,
metadata: Some(metadata),
}
} else {
let error_msg = response
.error
.unwrap_or_else(|| "Update failed".to_string());
let error_code = if error_msg.contains("version mismatch")
|| error_msg.contains("modified by another client")
{
"VERSION_MISMATCH"
} else if error_msg.contains("not found") {
"USER_NOT_FOUND"
} else {
"UPDATE_USER_FAILED"
};
ScimToolResult {
success: false,
content: json!({
"error": error_msg,
"error_code": error_code,
"user_id": user_id
}),
metadata: Some(json!({
"operation": "update_user",
"resource_id": user_id,
"conditional_update": arguments.get("expected_version").is_some()
})),
}
}
}
pub async fn handle_delete_user<P: ResourceProvider + Send + Sync + 'static>(
server: &ScimMcpServer<P>,
arguments: Value,
) -> ScimToolResult {
let user_id = match arguments.get("user_id").and_then(|id| id.as_str()) {
Some(id) => id,
None => {
return ScimToolResult {
success: false,
content: json!({"error": "Missing user_id parameter"}),
metadata: None,
};
}
};
let tenant_context = arguments
.get("tenant_id")
.and_then(|t| t.as_str())
.map(|id| TenantContext::new(id.to_string(), "mcp-client".to_string()));
let mut request = ScimOperationRequest::delete("User".to_string(), user_id.to_string());
if let Some(tenant) = tenant_context {
request = request.with_tenant(tenant);
}
if let Some(expected_version_str) = arguments.get("expected_version").and_then(|v| v.as_str()) {
let version_result = expected_version_str
.parse::<ScimVersion<Http>>()
.map(|v| v.into())
.or_else(|_| expected_version_str.parse::<ScimVersion<Raw>>());
match version_result {
Ok(version) => {
request = request.with_expected_version(version);
}
Err(_) => {
return ScimToolResult {
success: false,
content: json!({
"error": format!("Invalid expected_version format: '{}'. Use raw format (e.g., 'abc123def') or ETag format (e.g., 'W/\"abc123def\"')", expected_version_str),
"error_code": "INVALID_VERSION_FORMAT"
}),
metadata: None,
};
}
}
}
let response = server.operation_handler.handle_operation(request).await;
if response.success {
ScimToolResult {
success: true,
content: json!({"status": "deleted", "user_id": user_id}),
metadata: Some(json!({
"operation": "delete_user",
"resource_type": "User",
"resource_id": user_id,
"conditional_delete": arguments.get("expected_version").is_some()
})),
}
} else {
let error_msg = response
.error
.unwrap_or_else(|| "Delete failed".to_string());
let error_code = if error_msg.contains("version mismatch")
|| error_msg.contains("modified by another client")
{
"VERSION_MISMATCH"
} else if error_msg.contains("not found") {
"USER_NOT_FOUND"
} else {
"DELETE_USER_FAILED"
};
ScimToolResult {
success: false,
content: json!({
"error": error_msg,
"error_code": error_code,
"user_id": user_id
}),
metadata: Some(json!({
"operation": "delete_user",
"resource_id": user_id,
"conditional_delete": arguments.get("expected_version").is_some()
})),
}
}
}