Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions crates/cli/src/commands/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,6 @@ async fn show_project(client: &OrchestratorClient, id_or_name: &str, json: bool)
if let Some(desc) = &project.description {
println!(" Description: {}", desc);
}
println!(" Agents: {}", project.agent_count);
println!(" Workflows: {}", project.workflow_count);
println!(" Created: {}", project.created_at.format("%Y-%m-%d %H:%M:%S UTC"));
println!(" Updated: {}", project.updated_at.format("%Y-%m-%d %H:%M:%S UTC"));

Expand Down
12 changes: 8 additions & 4 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,12 +712,16 @@ async fn main() -> Result<()> {
}
Commands::Project { command } => {
let token = load_token_or_warn();
let url = gateway_url("orchestrator");
let mut client = OrchestratorClient::new(url);
let orch_url = gateway_url("orchestrator");
// Project CRUD now lives in core; association operations stay on the
// orchestrator. Build a client that routes CRUD to core and keeps
// associations on the orchestrator base URL.
let core_api_url = format!("{}/api/v1", client::core_url());
let mut orch_client = OrchestratorClient::new(orch_url).with_core_url(core_api_url);
if let Some(t) = token {
client = client.with_token(t);
orch_client = orch_client.with_token(t);
}
command.execute(&client, cli.json).await?;
command.execute(&orch_client, cli.json).await?;
}
Commands::Control => {
agentd_tui::run_control().await?;
Expand Down
12 changes: 12 additions & 0 deletions crates/common/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,8 @@ impl Default for CorePamConfig {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct McpConfig {
/// Core service URL (API gateway). Defaults to `"http://localhost:17000"`.
pub core_url: String,
/// Orchestrator service URL. Defaults to `"http://localhost:17006"`.
pub orchestrator_url: String,
/// Notify service URL. Defaults to `"http://localhost:17004"`.
Expand All @@ -478,6 +480,7 @@ pub struct McpConfig {
impl Default for McpConfig {
fn default() -> Self {
Self {
core_url: "http://localhost:17000".to_string(),
orchestrator_url: "http://localhost:17006".to_string(),
notify_url: "http://localhost:17004".to_string(),
ask_url: "http://localhost:17001".to_string(),
Expand Down Expand Up @@ -731,6 +734,7 @@ impl ValidateConfig for MonitorConfig {
impl ValidateConfig for McpConfig {
fn validate(&self) -> Result<()> {
for (name, url) in [
("mcp.core_url", self.core_url.as_str()),
("mcp.orchestrator_url", self.orchestrator_url.as_str()),
("mcp.notify_url", self.notify_url.as_str()),
("mcp.ask_url", self.ask_url.as_str()),
Expand Down Expand Up @@ -1164,6 +1168,11 @@ fn merge(base: AgentdConfig, file: AgentdConfig) -> AgentdConfig {
},
},
mcp: McpConfig {
core_url: pick(
&base.services.mcp.core_url,
&file.services.mcp.core_url,
&d.services.mcp.core_url,
),
orchestrator_url: pick(
&base.services.mcp.orchestrator_url,
&file.services.mcp.orchestrator_url,
Expand Down Expand Up @@ -1368,6 +1377,9 @@ fn apply_env_overrides(cfg: &mut AgentdConfig) {
}

// ── MCP ───────────────────────────────────────────────────────────────
if let Ok(v) = env::var("AGENTD_MCP_CORE_URL") {
cfg.services.mcp.core_url = v;
}
if let Ok(v) = env::var("AGENTD_MCP_ORCHESTRATOR_URL") {
cfg.services.mcp.orchestrator_url = v;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/api/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ mod tests {
async fn test_app() -> (Router, tempfile::TempDir) {
let (conn, tmp) = create_test_connection().await;
let storage = Storage::new(conn).await.unwrap();
let state = AppState { storage };
let state = AppState::new(storage);
let app = crate::api::create_router(state);
(app, tmp)
}
Expand Down
5 changes: 5 additions & 0 deletions crates/mcp/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ impl AgentdClient {
Self { inner: Client::new(), config }
}

/// Returns the base URL for the core service (API gateway).
pub fn core_url(&self) -> &str {
&self.config.core_url
}

/// Returns the base URL for the orchestrator service.
pub fn orchestrator_url(&self) -> &str {
&self.config.orchestrator_url
Expand Down
9 changes: 9 additions & 0 deletions crates/mcp/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use std::env;
/// Configuration for connecting to agentd services.
#[derive(Debug, Clone)]
pub struct AgentdMcpConfig {
/// Core service URL (API gateway, default: `http://127.0.0.1:17000`)
pub core_url: String,
/// Orchestrator service URL (default: `http://127.0.0.1:17006`)
pub orchestrator_url: String,
/// Communicate service URL (default: `http://127.0.0.1:17010`)
Expand Down Expand Up @@ -41,6 +43,7 @@ impl AgentdMcpConfig {
///
/// | Variable | Default |
/// |---------------------------------|-----------------------------|
/// | `AGENTD_CORE_URL` | `http://127.0.0.1:17000` |
/// | `AGENTD_ORCHESTRATOR_URL` | `http://127.0.0.1:17006` |
/// | `AGENTD_COMMUNICATE_URL` | `http://127.0.0.1:17010` |
/// | `AGENTD_MEMORY_URL` | `http://127.0.0.1:17008` |
Expand All @@ -58,6 +61,7 @@ impl AgentdMcpConfig {
let base = shared.services.mcp;

Self {
core_url: env::var("AGENTD_CORE_URL").unwrap_or(base.core_url),
orchestrator_url: env::var("AGENTD_ORCHESTRATOR_URL").unwrap_or(base.orchestrator_url),
communicate_url: env::var("AGENTD_COMMUNICATE_URL").unwrap_or(base.communicate_url),
memory_url: env::var("AGENTD_MEMORY_URL").unwrap_or(base.memory_url),
Expand All @@ -80,6 +84,7 @@ impl AgentdMcpConfig {
impl ValidateConfig for AgentdMcpConfig {
fn validate(&self) -> Result<()> {
let urls = [
("mcp.core_url", self.core_url.as_str()),
("mcp.orchestrator_url", self.orchestrator_url.as_str()),
("mcp.communicate_url", self.communicate_url.as_str()),
("mcp.memory_url", self.memory_url.as_str()),
Expand Down Expand Up @@ -111,6 +116,7 @@ mod tests {
fn test_defaults() {
let _g = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let vars = [
"AGENTD_CORE_URL",
"AGENTD_ORCHESTRATOR_URL",
"AGENTD_COMMUNICATE_URL",
"AGENTD_MEMORY_URL",
Expand All @@ -133,6 +139,7 @@ mod tests {
env::set_var("AGENTD_CONFIG", "/nonexistent/agentd-mcp-test-config.toml");

let config = AgentdMcpConfig::from_env();
assert_eq!(config.core_url, "http://localhost:17000");
assert_eq!(config.orchestrator_url, "http://localhost:17006");
assert_eq!(config.communicate_url, "http://localhost:17010");
assert_eq!(config.memory_url, "http://localhost:17008");
Expand Down Expand Up @@ -168,6 +175,7 @@ mod tests {
fn test_validate_default_passes() {
let _g = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let vars = [
"AGENTD_CORE_URL",
"AGENTD_ORCHESTRATOR_URL",
"AGENTD_COMMUNICATE_URL",
"AGENTD_MEMORY_URL",
Expand Down Expand Up @@ -195,6 +203,7 @@ mod tests {
#[test]
fn test_validate_bad_url_fails() {
let config = AgentdMcpConfig {
core_url: "http://127.0.0.1:17000".to_string(),
orchestrator_url: "not-a-url".to_string(),
communicate_url: "http://127.0.0.1:17010".to_string(),
memory_url: "http://127.0.0.1:17008".to_string(),
Expand Down
16 changes: 6 additions & 10 deletions crates/mcp/src/tools/orchestrator_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ struct ProjectDetail {
description: Option<String>,
created_at: String,
updated_at: String,
agent_count: u64,
workflow_count: u64,
}

#[derive(Debug, Deserialize)]
Expand Down Expand Up @@ -302,13 +300,13 @@ pub async fn run_get_conversation_summary(client: &AgentdClient, agent_id: &str)
}

pub async fn run_list_projects(client: &AgentdClient, limit: Option<u32>) -> String {
let base = client.orchestrator_url();
let base = client.core_url();
let limit_val = limit.unwrap_or(50).clamp(1, 200);
let url = format!("{base}/projects?limit={limit_val}");
let url = format!("{base}/api/v1/projects?limit={limit_val}");

let resp = match client.inner.get(&url).send().await {
Ok(r) => r,
Err(e) => return format!("Error: orchestrator unreachable at {base}: {e}"),
Err(e) => return format!("Error: core unreachable at {base}: {e}"),
};
if !resp.status().is_success() {
return format!("Error: HTTP {} listing projects", resp.status());
Expand Down Expand Up @@ -338,12 +336,12 @@ pub async fn run_list_projects(client: &AgentdClient, limit: Option<u32>) -> Str
}

pub async fn run_get_project(client: &AgentdClient, project_id: &str) -> String {
let base = client.orchestrator_url();
let url = format!("{base}/projects/{project_id}");
let base = client.core_url();
let url = format!("{base}/api/v1/projects/{project_id}");

let resp = match client.inner.get(&url).send().await {
Ok(r) => r,
Err(e) => return format!("Error: orchestrator unreachable at {base}: {e}"),
Err(e) => return format!("Error: core unreachable at {base}: {e}"),
};
if resp.status() == reqwest::StatusCode::NOT_FOUND {
return format!("Project `{project_id}` not found.");
Expand All @@ -361,8 +359,6 @@ pub async fn run_get_project(client: &AgentdClient, project_id: &str) -> String
if let Some(ref d) = p.description {
out.push_str(&format!("- **Description**: {d}\n"));
}
out.push_str(&format!("- **Agents**: {}\n", p.agent_count));
out.push_str(&format!("- **Workflows**: {}\n", p.workflow_count));
out.push_str(&format!("- **Created**: {}\n", p.created_at));
out.push_str(&format!("- **Updated**: {}\n", p.updated_at));
out
Expand Down
1 change: 1 addition & 0 deletions crates/mcp/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ pub async fn mock_monitor_server() -> MockServer {
/// Build an `AgentdClient` pointed at the given mock servers.
pub fn test_client(orch_addr: &str, notify_addr: &str, monitor_addr: &str) -> AgentdClient {
let config = Arc::new(AgentdMcpConfig {
core_url: "http://127.0.0.1:1".to_string(), // unused
orchestrator_url: orch_addr.to_string(),
communicate_url: "http://127.0.0.1:1".to_string(), // unused
memory_url: "http://127.0.0.1:1".to_string(),
Expand Down
Loading