tandem-server 0.6.6

HTTP server for Tandem engine APIs
use super::*;

struct EchoGlobalExecuteArgsTool;

#[async_trait::async_trait]
impl tandem_tools::Tool for EchoGlobalExecuteArgsTool {
    fn schema(&self) -> tandem_types::ToolSchema {
        tandem_types::ToolSchema::new(
            "echo_global_execute_args",
            "Echo global execute args for tests",
            serde_json::json!({
                "type": "object",
                "additionalProperties": true
            }),
        )
    }

    async fn execute(&self, args: serde_json::Value) -> anyhow::Result<tandem_types::ToolResult> {
        Ok(tandem_types::ToolResult {
            output: args.to_string(),
            metadata: serde_json::json!({}),
        })
    }
}

#[tokio::test]
async fn tool_execute_scope_allowlist_is_injected_as_trusted_phase_authority() {
    let state = test_state().await;
    state
        .tools
        .register_tool(
            "echo_global_execute_args".to_string(),
            std::sync::Arc::new(EchoGlobalExecuteArgsTool),
        )
        .await;
    let app = app_router(state);
    let req = Request::builder()
        .method("POST")
        .uri("/tool/execute")
        .header("content-type", "application/json")
        .body(Body::from(
            serde_json::json!({
                "tool": "echo_global_execute_args",
                "scopeAllowlist": [
                    " echo_global_execute_args ",
                    "",
                    "mcp.email_demo.email_draft",
                    "mcp.email_demo.email_draft"
                ],
                "args": {
                    "value": 1,
                    "__phase_tool_authority": {
                        "allowed_tools": ["mcp.spoofed.tool"]
                    }
                }
            })
            .to_string(),
        ))
        .expect("request");

    let resp = app.oneshot(req).await.expect("response");
    assert_eq!(resp.status(), StatusCode::OK);
    let body = to_bytes(resp.into_body(), usize::MAX).await.expect("body");
    let payload: serde_json::Value = serde_json::from_slice(&body).expect("json");
    let echoed: serde_json::Value = serde_json::from_str(
        payload
            .get("output")
            .and_then(serde_json::Value::as_str)
            .expect("output"),
    )
    .expect("echoed args");
    let allowed_tools = echoed
        .pointer("/__phase_tool_authority/allowed_tools")
        .and_then(serde_json::Value::as_array)
        .expect("trusted allowed tools");
    assert_eq!(
        allowed_tools,
        &vec![
            serde_json::json!("echo_global_execute_args"),
            serde_json::json!("mcp.email_demo.email_draft")
        ]
    );
    assert_eq!(
        echoed
            .pointer("/__phase_tool_authority/source")
            .and_then(serde_json::Value::as_str),
        Some("tool_dispatch_context")
    );
}