github-copilot-sdk 1.0.1

Rust SDK for programmatic control of the GitHub Copilot CLI via JSON-RPC.
use github_copilot_sdk::rpc::{
    McpConfigAddRequest, McpConfigDisableRequest, McpConfigEnableRequest, McpConfigRemoveRequest,
    McpConfigUpdateRequest,
};
use serde_json::json;

use super::support::with_e2e_context;

#[tokio::test]
async fn should_call_server_mcp_config_rpcs() {
    with_e2e_context(
        "rpc_mcp_config",
        "should_call_server_mcp_config_rpcs",
        |ctx| {
            Box::pin(async move {
                let server_name = "rust-sdk-test-mcp-config";
                let client = ctx.start_client().await;
                let config = client.rpc().mcp().config();
                let _ = config
                    .remove(McpConfigRemoveRequest {
                        name: server_name.to_string(),
                    })
                    .await;

                let initial = config.list().await.expect("initial list");
                assert!(!initial.servers.contains_key(server_name));

                config
                    .add(McpConfigAddRequest {
                        name: server_name.to_string(),
                        config: json!({ "command": "node", "args": [] }),
                    })
                    .await
                    .expect("add");
                let after_add = config.list().await.expect("list after add");
                assert!(after_add.servers.contains_key(server_name));

                config
                    .update(McpConfigUpdateRequest {
                        name: server_name.to_string(),
                        config: json!({ "command": "node", "args": ["--version"] }),
                    })
                    .await
                    .expect("update");
                let after_update = config.list().await.expect("list after update");
                let updated = after_update
                    .servers
                    .get(server_name)
                    .expect("updated server");
                assert_eq!(
                    updated.get("command").and_then(|v| v.as_str()),
                    Some("node")
                );
                assert_eq!(
                    updated
                        .get("args")
                        .and_then(|v| v.as_array())
                        .and_then(|args| args.first())
                        .and_then(|v| v.as_str()),
                    Some("--version")
                );

                config
                    .disable(McpConfigDisableRequest {
                        names: vec![server_name.to_string()],
                    })
                    .await
                    .expect("disable");
                config
                    .enable(McpConfigEnableRequest {
                        names: vec![server_name.to_string()],
                    })
                    .await
                    .expect("enable");
                config
                    .remove(McpConfigRemoveRequest {
                        name: server_name.to_string(),
                    })
                    .await
                    .expect("remove");

                let after_remove = config.list().await.expect("list after remove");
                assert!(!after_remove.servers.contains_key(server_name));

                client.stop().await.expect("stop client");
            })
        },
    )
    .await;
}

#[tokio::test]
async fn should_round_trip_http_mcp_oauth_config_rpc() {
    with_e2e_context(
        "rpc_mcp_config",
        "should_round_trip_http_mcp_oauth_config_rpc",
        |ctx| {
            Box::pin(async move {
                let server_name = "rust-sdk-http-oauth-mcp-config";
                let client = ctx.start_client().await;
                let config = client.rpc().mcp().config();
                let _ = config
                    .remove(McpConfigRemoveRequest {
                        name: server_name.to_string(),
                    })
                    .await;

                config
                    .add(McpConfigAddRequest {
                        name: server_name.to_string(),
                        config: json!({
                            "type": "http",
                            "url": "https://example.com/mcp",
                            "headers": { "Authorization": "Bearer token" },
                            "oauthClientId": "client-id",
                            "oauthPublicClient": false,
                            "oauthGrantType": "client_credentials",
                            "tools": ["*"],
                            "timeout": 3000
                        }),
                    })
                    .await
                    .expect("add");
                let after_add = config.list().await.expect("list after add");
                let added = after_add.servers.get(server_name).expect("added server");
                assert_eq!(added.get("type").and_then(|v| v.as_str()), Some("http"));
                assert_eq!(
                    added.get("url").and_then(|v| v.as_str()),
                    Some("https://example.com/mcp")
                );
                assert_eq!(
                    added
                        .get("headers")
                        .and_then(|v| v.get("Authorization"))
                        .and_then(|v| v.as_str()),
                    Some("Bearer token")
                );
                assert_eq!(
                    added.get("oauthClientId").and_then(|v| v.as_str()),
                    Some("client-id")
                );
                assert_eq!(
                    added.get("oauthPublicClient").and_then(|v| v.as_bool()),
                    Some(false)
                );
                assert_eq!(
                    added.get("oauthGrantType").and_then(|v| v.as_str()),
                    Some("client_credentials")
                );

                config
                    .update(McpConfigUpdateRequest {
                        name: server_name.to_string(),
                        config: json!({
                            "type": "http",
                            "url": "https://example.com/updated-mcp",
                            "oauthClientId": "updated-client-id",
                            "oauthPublicClient": true,
                            "oauthGrantType": "authorization_code",
                            "tools": ["updated-tool"],
                            "timeout": 4000
                        }),
                    })
                    .await
                    .expect("update");
                let after_update = config.list().await.expect("list after update");
                let updated = after_update
                    .servers
                    .get(server_name)
                    .expect("updated server");
                assert_eq!(
                    updated.get("url").and_then(|v| v.as_str()),
                    Some("https://example.com/updated-mcp")
                );
                assert_eq!(
                    updated.get("oauthClientId").and_then(|v| v.as_str()),
                    Some("updated-client-id")
                );
                assert_eq!(
                    updated.get("oauthPublicClient").and_then(|v| v.as_bool()),
                    Some(true)
                );
                assert_eq!(
                    updated.get("oauthGrantType").and_then(|v| v.as_str()),
                    Some("authorization_code")
                );
                assert_eq!(
                    updated
                        .get("tools")
                        .and_then(|v| v.as_array())
                        .and_then(|tools| tools.first())
                        .and_then(|v| v.as_str()),
                    Some("updated-tool")
                );
                assert_eq!(updated.get("timeout").and_then(|v| v.as_i64()), Some(4000));

                config
                    .remove(McpConfigRemoveRequest {
                        name: server_name.to_string(),
                    })
                    .await
                    .expect("remove");
                let after_remove = config.list().await.expect("list after remove");
                assert!(!after_remove.servers.contains_key(server_name));

                client.stop().await.expect("stop client");
            })
        },
    )
    .await;
}