In order to make Codex work with connectors, we add a built-in gateway MCP that acts as a transparent proxy between the client and the connectors. The gateway MCP collects actions that are accessible to the user and sends them down to the user, when a connector action is chosen to be called, the client invokes the action through the gateway MCP as well. - [x] Add the system built-in gateway MCP to list and run connectors. - [x] Add the app server methods and protocol
85 lines
2.6 KiB
Rust
85 lines
2.6 KiB
Rust
use codex_core::config::Config;
|
|
use codex_core::default_client::create_client;
|
|
|
|
use crate::chatgpt_token::get_chatgpt_token_data;
|
|
use crate::chatgpt_token::init_chatgpt_token_from_auth;
|
|
|
|
use anyhow::Context;
|
|
use serde::Serialize;
|
|
use serde::de::DeserializeOwned;
|
|
|
|
/// Make a GET request to the ChatGPT backend API.
|
|
pub(crate) async fn chatgpt_get_request<T: DeserializeOwned>(
|
|
config: &Config,
|
|
path: String,
|
|
) -> anyhow::Result<T> {
|
|
let chatgpt_base_url = &config.chatgpt_base_url;
|
|
init_chatgpt_token_from_auth(&config.codex_home, config.cli_auth_credentials_store_mode)
|
|
.await?;
|
|
|
|
// Make direct HTTP request to ChatGPT backend API with the token
|
|
let client = create_client();
|
|
let url = format!("{chatgpt_base_url}{path}");
|
|
|
|
let token =
|
|
get_chatgpt_token_data().ok_or_else(|| anyhow::anyhow!("ChatGPT token not available"))?;
|
|
|
|
let account_id = token.account_id.ok_or_else(|| {
|
|
anyhow::anyhow!("ChatGPT account ID not available, please re-run `codex login`")
|
|
});
|
|
|
|
let response = client
|
|
.get(&url)
|
|
.bearer_auth(&token.access_token)
|
|
.header("chatgpt-account-id", account_id?)
|
|
.header("Content-Type", "application/json")
|
|
.send()
|
|
.await
|
|
.context("Failed to send request")?;
|
|
|
|
if response.status().is_success() {
|
|
let result: T = response
|
|
.json()
|
|
.await
|
|
.context("Failed to parse JSON response")?;
|
|
Ok(result)
|
|
} else {
|
|
let status = response.status();
|
|
let body = response.text().await.unwrap_or_default();
|
|
anyhow::bail!("Request failed with status {status}: {body}")
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn chatgpt_post_request<T: DeserializeOwned, P: Serialize>(
|
|
config: &Config,
|
|
access_token: &str,
|
|
account_id: &str,
|
|
path: &str,
|
|
payload: &P,
|
|
) -> anyhow::Result<T> {
|
|
let chatgpt_base_url = &config.chatgpt_base_url;
|
|
let client = create_client();
|
|
let url = format!("{chatgpt_base_url}{path}");
|
|
|
|
let response = client
|
|
.post(&url)
|
|
.bearer_auth(access_token)
|
|
.header("chatgpt-account-id", account_id)
|
|
.header("Content-Type", "application/json")
|
|
.json(payload)
|
|
.send()
|
|
.await
|
|
.context("Failed to send request")?;
|
|
|
|
if response.status().is_success() {
|
|
let result: T = response
|
|
.json()
|
|
.await
|
|
.context("Failed to parse JSON response")?;
|
|
Ok(result)
|
|
} else {
|
|
let status = response.status();
|
|
let body = response.text().await.unwrap_or_default();
|
|
anyhow::bail!("Request failed with status {status}: {body}")
|
|
}
|
|
}
|