feat(mcp): add script preview testing tool (#6417)

* add endpoint to test script

* add same for flow

* better tool spec + remove flow preview from tools

* fix

* fix wrong required fields

* feat(mcp): add warning for missing required fields in schema properties

- Add stderr warning when x-mcp-required-fields contains fields not found in body schema properties
- Prevents silent misconfigurations in MCP tool generation
- Helps debug schema validation issues

Co-authored-by: centdix <centdix@users.noreply.github.com>

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: centdix <centdix@users.noreply.github.com>
This commit is contained in:
centdix
2025-08-20 14:50:46 +02:00
committed by GitHub
parent 26a47ee699
commit ae49737676
4 changed files with 192 additions and 2 deletions

View File

@@ -177,12 +177,14 @@ pub fn workspaced_service() -> Router {
.layer(ce_headers.clone()),
)
.route("/run/preview", post(run_preview_script))
.route("/run_wait_result/preview", post(run_wait_result_preview_script))
.route(
"/run/preview_bundle",
post(run_bundle_preview_script).layer(axum::extract::DefaultBodyLimit::disable()),
)
.route("/add_batch_jobs/:n", post(add_batch_jobs))
.route("/run/preview_flow", post(run_preview_flow_job))
.route("/run_wait_result/preview_flow", post(run_wait_result_preview_flow))
.route("/list", get(list_jobs))
.route(
"/list_selected_job_groups",
@@ -5207,6 +5209,28 @@ async fn run_preview_script(
Ok((StatusCode::CREATED, uuid.to_string()))
}
async fn run_wait_result_preview_script(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Extension(user_db): Extension<UserDB>,
Path(w_id): Path<String>,
Query(run_query): Query<RunJobQuery>,
Json(preview): Json<Preview>,
) -> error::Result<Response> {
let (_status_code, uuid) = run_preview_script(
authed.clone(),
Extension(db.clone()),
Extension(user_db.clone()),
Path(w_id.clone()),
Query(run_query.clone()),
Json(preview)
).await?;
let uuid = uuid.parse::<Uuid>().map_err(|_| Error::BadRequest("Invalid UUID".to_string()))?;
let result = run_wait_result(&db, uuid, w_id, None, &authed.username).await;
return result;
}
async fn run_bundle_preview_script(
authed: ApiAuthed,
Extension(db): Extension<DB>,
@@ -5865,6 +5889,20 @@ async fn run_preview_flow_job(
Ok((StatusCode::CREATED, uuid.to_string()))
}
async fn run_wait_result_preview_flow(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Extension(user_db): Extension<UserDB>,
Path(w_id): Path<String>,
Query(run_query): Query<RunJobQuery>,
Json(raw_flow): Json<PreviewFlow>,
) -> error::Result<Response> {
let (_status_code, uuid) = run_preview_flow_job(authed.clone(), Extension(db.clone()), Extension(user_db.clone()), Path(w_id.clone()), Query(run_query.clone()), Json(raw_flow)).await?;
let uuid = uuid.parse::<Uuid>().map_err(|_| Error::BadRequest("Invalid UUID".to_string()))?;
let result = run_wait_result(&db, uuid, w_id, None, &authed.username).await;
return result;
}
pub async fn run_job_by_hash(
authed: ApiAuthed,
Extension(db): Extension<DB>,

View File

@@ -564,6 +564,86 @@ pub fn all_tools() -> Vec<EndpointTool> {
})),
body_schema: None,
},
EndpointTool {
name: Cow::Borrowed("runScriptPreviewAndWaitResult"),
description: Cow::Borrowed("run script preview and wait for result"),
instructions: Cow::Borrowed("Allows testing a script before deploying it. For typescript code, the language to send is either bun or deno. By default, send bun if no deno specific code is detected."),
path: Cow::Borrowed("/w/{workspace}/jobs/run_wait_result/preview"),
method: Cow::Borrowed("POST"),
path_params_schema: None,
query_params_schema: None,
body_schema: Some(serde_json::json!({
"type": "object",
"properties": {
"content": {
"type": "string",
"description": "The code to run"
},
"path": {
"type": "string",
"description": "The path to the script"
},
"script_hash": {
"type": "string",
"description": "The hash of the script"
},
"args": {
"type": "object",
"description": "The arguments to pass to the script or flow",
"additionalProperties": {}
},
"language": {
"type": "string",
"enum": [
"python3",
"deno",
"go",
"bash",
"powershell",
"postgresql",
"mysql",
"bigquery",
"snowflake",
"mssql",
"oracledb",
"graphql",
"nativets",
"bun",
"php",
"rust",
"ansible",
"csharp",
"nu",
"java",
"ruby",
"duckdb"
]
},
"tag": {
"type": "string"
},
"kind": {
"type": "string",
"enum": [
"code",
"identity",
"http"
]
},
"dedicated_worker": {
"type": "boolean"
},
"lock": {
"type": "string"
}
},
"required": [
"args",
"content",
"language"
]
})),
},
EndpointTool {
name: Cow::Borrowed("listQueue"),
description: Cow::Borrowed("list all queued jobs"),