Add approval mode selection
This commit is contained in:
@@ -15,7 +15,7 @@ use axum::{
|
||||
use crank_core::{
|
||||
ApprovalRequest, ApprovalRequestId, ApprovalRequestStatus, AuthProfile, CoordinationStateStore,
|
||||
InvocationLevel, InvocationLog, InvocationLogId, InvocationSource, InvocationStatus,
|
||||
PlatformApiKeyScope, SecretId,
|
||||
OperationApprovalMode, PlatformApiKeyScope, SecretId,
|
||||
};
|
||||
use crank_registry::{
|
||||
ApprovalRequestRecord, CreateApprovalRequest, CreateInvocationLogRequest,
|
||||
@@ -79,6 +79,8 @@ pub(super) struct AppState {
|
||||
struct InitializeParams {
|
||||
#[serde(rename = "protocolVersion")]
|
||||
protocol_version: String,
|
||||
#[serde(default)]
|
||||
capabilities: Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -955,7 +957,7 @@ async fn handle_base_tool_call(
|
||||
let tool = execution.tool;
|
||||
let arguments = execution.arguments;
|
||||
let operation = runtime_operation(&tool);
|
||||
if let Some(response) = maybe_create_pending_approval(
|
||||
if let Some(response) = maybe_handle_approval_policy(
|
||||
&state,
|
||||
session,
|
||||
message,
|
||||
@@ -1052,7 +1054,7 @@ async fn handle_base_tool_call(
|
||||
}
|
||||
}
|
||||
|
||||
async fn maybe_create_pending_approval(
|
||||
async fn maybe_handle_approval_policy(
|
||||
state: &Arc<AppState>,
|
||||
session: &SessionState,
|
||||
message: &Value,
|
||||
@@ -1066,6 +1068,42 @@ async fn maybe_create_pending_approval(
|
||||
return None;
|
||||
}
|
||||
|
||||
match policy.mode {
|
||||
OperationApprovalMode::Custom => {
|
||||
maybe_create_custom_pending_approval(
|
||||
state,
|
||||
session,
|
||||
message,
|
||||
response_mode,
|
||||
tool,
|
||||
arguments,
|
||||
transport_request_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
OperationApprovalMode::Elicitation => Some(handle_elicitation_approval(
|
||||
session,
|
||||
message,
|
||||
response_mode,
|
||||
tool,
|
||||
arguments,
|
||||
policy.elicitation_message.as_deref(),
|
||||
transport_request_id,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn maybe_create_custom_pending_approval(
|
||||
state: &Arc<AppState>,
|
||||
session: &SessionState,
|
||||
message: &Value,
|
||||
response_mode: ResponseMode,
|
||||
tool: &PublishedAgentTool,
|
||||
arguments: &Value,
|
||||
transport_request_id: &str,
|
||||
) -> Option<Response> {
|
||||
let policy = tool.operation.execution_config.approval_policy.as_ref()?;
|
||||
|
||||
let approval_id = ApprovalRequestId::new(format!("approval_{}", uuid::Uuid::now_v7().simple()));
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let expires_at = now + time::Duration::seconds(i64::from(policy.ttl_seconds));
|
||||
@@ -1145,6 +1183,54 @@ async fn maybe_create_pending_approval(
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_elicitation_approval(
|
||||
session: &SessionState,
|
||||
message: &Value,
|
||||
response_mode: ResponseMode,
|
||||
tool: &PublishedAgentTool,
|
||||
arguments: &Value,
|
||||
elicitation_message: Option<&str>,
|
||||
transport_request_id: &str,
|
||||
) -> Response {
|
||||
if !session.supports_elicitation {
|
||||
return tool_error_response(
|
||||
message,
|
||||
response_mode,
|
||||
&session.protocol_version,
|
||||
generic_tool_error_contract(
|
||||
"approval_elicitation_not_supported",
|
||||
"operation requires MCP Elicitation, but the MCP client did not advertise elicitation capability",
|
||||
transport_request_id,
|
||||
false,
|
||||
Some(
|
||||
"Выберите Custom MCP Approval или подключите MCP-клиент с поддержкой elicitation.",
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let payload_preview = tool
|
||||
.operation
|
||||
.execution_config
|
||||
.approval_policy
|
||||
.as_ref()
|
||||
.and_then(|policy| policy.show_payload_preview.then(|| arguments.clone()))
|
||||
.unwrap_or(Value::Null);
|
||||
|
||||
success_tool_response(
|
||||
message,
|
||||
response_mode,
|
||||
&session.protocol_version,
|
||||
json!({
|
||||
"status": "elicitation_required",
|
||||
"message": elicitation_message.unwrap_or("Confirm operation execution."),
|
||||
"tool": tool.tool_name,
|
||||
"payload_preview": payload_preview,
|
||||
"note": "This MCP client advertised elicitation support. Full elicitation/create continuation is handled by compatible client integrations.",
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn approval_url_for(tool: &PublishedAgentTool, approval_id: &ApprovalRequestId) -> String {
|
||||
format!(
|
||||
"/v1/{}/{}/approvals/{}",
|
||||
@@ -1191,12 +1277,17 @@ async fn handle_initialize(
|
||||
};
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let expires_at = add_millis(now, TRANSPORT_SESSION_TTL_MS);
|
||||
let supports_elicitation = initialize_params
|
||||
.capabilities
|
||||
.get("elicitation")
|
||||
.is_some_and(Value::is_object);
|
||||
let session_id = match state
|
||||
.sessions
|
||||
.create(
|
||||
protocol_version,
|
||||
&path.workspace_slug,
|
||||
&path.agent_slug,
|
||||
supports_elicitation,
|
||||
now,
|
||||
Some(expires_at),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user