vtcode-webmcp 0.181.2

Authenticated WebMCP bridge and workspace adapter for VT Code
Documentation
use super::*;
use crate::FilesystemWorkspace;
use tempfile::TempDir;

#[tokio::test]
async fn explanation_requests_require_origin_bound_tokens_and_report_unsupported() {
    let temp = TempDir::new().expect("workspace");
    let adapter = Arc::new(FilesystemWorkspace::new(temp.path(), [], false).await.expect("adapter"));
    let origin = "https://example.test";
    let server = WebmcpServer::new(
        adapter,
        WebmcpServerConfig {
            allowed_origins: vec![origin.to_owned(), "https://other.test".to_owned()],
            ..Default::default()
        },
    )
    .expect("server");
    let pairing = server.begin_pairing_for_origin(origin).expect("pairing");
    let session = server
        .state
        .pairing
        .pair(pairing.code(), origin)
        .expect("authenticated session");
    let token = session.token().to_owned();
    let reference = vtcode_memory::explanation::EvidenceRef {
        session_id: "session".into(),
        offset: 0,
        length: 1,
        digest: "0".repeat(64),
        item_id: None,
    };
    let requests = [
        BridgeRequest::ExplanationGet {
            request_id: "get".into(),
            token: token.clone(),
            scope: vtcode_memory::explanation::ExplanationScope::Task,
            offset: 0,
        },
        BridgeRequest::ExplanationEvidence {
            request_id: "evidence".into(),
            token: token.clone(),
            reference: reference.clone(),
            offset: 0,
        },
        BridgeRequest::ExplanationNavigate {
            request_id: "navigate".into(),
            token: token.clone(),
            reference,
        },
    ];
    for request in &requests {
        let dispatch = || Arc::clone(&server.state.dispatch);
        assert!(matches!(
            dispatch_request(dispatch(), origin.into(), "wrong-token".into(), request.clone()).await,
            Err(WebmcpError::Unauthorized)
        ));
        let error = dispatch_request(dispatch(), origin.into(), token.clone(), request.clone())
            .await
            .expect_err("headless adapter does not provide explanations");
        assert!(matches!(error, WebmcpError::Unsupported(_)));
        assert_eq!(response_for_error("operation", error).error.expect("error payload").code, "unsupported");
    }
    assert!(matches!(
        dispatch_request(
            Arc::clone(&server.state.dispatch),
            "https://other.test".into(),
            token.clone(),
            requests[0].clone()
        )
        .await,
        Err(WebmcpError::Unauthorized)
    ));
    for request in requests {
        assert!(matches!(
            dispatch_request(Arc::clone(&server.state.dispatch), origin.into(), token.clone(), request).await,
            Err(WebmcpError::Unauthorized)
        ));
    }
    let status = server.state.adapter.status().await.expect("status");
    assert!(!status.explanations_available);
    assert!(!status.turns_available);
    assert!(std::fs::read_dir(temp.path()).expect("workspace files").next().is_none());
}

#[tokio::test]
async fn server_requires_explicit_origins_and_remote_flags() {
    let temp = TempDir::new().expect("temp dir");
    let adapter = Arc::new(FilesystemWorkspace::new(temp.path(), [], false).await.expect("adapter"));
    assert!(matches!(
        WebmcpServer::new(adapter.clone(), WebmcpServerConfig::default()),
        Err(WebmcpError::InvalidRequest(_))
    ));
    let config = WebmcpServerConfig {
        host: "0.0.0.0".to_string(),
        allowed_origins: vec!["https://example.test".to_string()],
        ..Default::default()
    };
    assert!(matches!(WebmcpServer::new(adapter, config), Err(WebmcpError::InvalidRequest(_))));

    let remote_config = WebmcpServerConfig {
        host: "0.0.0.0".to_string(),
        allowed_origins: vec!["https://example.test".to_string()],
        allow_remote: true,
        public_url: Some("wss://bridge.example.test/webmcp".to_string()),
        ..Default::default()
    };
    let temp = TempDir::new().expect("temp dir");
    let adapter = Arc::new(FilesystemWorkspace::new(temp.path(), [], false).await.expect("adapter"));
    assert!(matches!(WebmcpServer::new(adapter, remote_config), Err(WebmcpError::InvalidRequest(_))));

    let temp = TempDir::new().expect("temp dir");
    let adapter = Arc::new(FilesystemWorkspace::new(temp.path(), [], false).await.expect("adapter"));
    let valid_proxy_config = WebmcpServerConfig {
        allowed_origins: vec!["https://example.test".to_string()],
        allow_remote: true,
        public_url: Some("wss://bridge.example.test/webmcp".to_string()),
        ..Default::default()
    };
    assert!(WebmcpServer::new(adapter.clone(), valid_proxy_config).is_ok());
    let invalid_public_url_config = WebmcpServerConfig {
        allowed_origins: vec!["https://example.test".to_string()],
        public_url: Some("ws://bridge.example.test/webmcp".to_string()),
        ..Default::default()
    };
    assert!(matches!(WebmcpServer::new(adapter, invalid_public_url_config), Err(WebmcpError::InvalidRequest(_))));

    let temp = TempDir::new().expect("temp dir");
    let adapter = Arc::new(FilesystemWorkspace::new(temp.path(), [], false).await.expect("adapter"));
    let invalid_public_url_config = WebmcpServerConfig {
        allowed_origins: vec!["https://example.test".to_string()],
        allow_remote: true,
        public_url: Some("wss://".to_string()),
        ..Default::default()
    };
    assert!(matches!(WebmcpServer::new(adapter, invalid_public_url_config), Err(WebmcpError::InvalidRequest(_))));
}