fix: agent usability improvements - unified schema casing, fixed silent handler failures, and made namespaces strictly optional

This commit is contained in:
Riz Ashraf committed 2026-09-26 08:07:28 +01:00
1 parent 5dd413c397
commit be85b58be4
3 files changed
+51 -14

No files matched your search

+18
View File
@@ -152,13 +152,25 @@ impl McpTool for CreateRelationsHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: CreateRelationsTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: CreateRelationsTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut missing_nodes = std::collections::HashSet::new();
state.modify_graph(|g| { state.modify_graph(|g| {
for relation in req.relations { for relation in req.relations {
if !relation.from.is_empty() && !relation.to.is_empty() { if !relation.from.is_empty() && !relation.to.is_empty() {
let from_exists = g.entities.contains_key(&relation.from);
let to_exists = g.entities.contains_key(&relation.to);
if from_exists && to_exists {
g.relations.push(relation); g.relations.push(relation);
} else {
if !from_exists { missing_nodes.insert(relation.from); }
if !to_exists { missing_nodes.insert(relation.to); }
}
} }
} }
}); });
if !missing_nodes.is_empty() {
let missing: Vec<_> = missing_nodes.into_iter().collect();
return Ok(format!("Warning: Relations dropped due to missing entities: {}", missing.join(", ")));
}
Ok("Relations created".to_string()) Ok("Relations created".to_string())
} }
} }
@@ -177,13 +189,19 @@ impl McpTool for AddObservationsHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: AddObservationsTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: AddObservationsTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut missing_entities = Vec::new();
state.modify_graph(|g| { state.modify_graph(|g| {
for o in req.observations { for o in req.observations {
if let Some(e) = g.entities.get_mut(&o.entity_name) { if let Some(e) = g.entities.get_mut(&o.entity_name) {
e.observations.extend(o.contents); e.observations.extend(o.contents);
} else {
missing_entities.push(o.entity_name);
} }
} }
}); });
if !missing_entities.is_empty() {
return Ok(format!("Warning: Observations dropped for missing entities: {}", missing_entities.join(", ")));
}
Ok("Observations added".to_string()) Ok("Observations added".to_string())
} }
} }
+8 -8
View File
@@ -21,7 +21,7 @@ pub fn default_namespace() -> String {
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct Entity { pub struct Entity {
pub name: String, pub name: String,
#[serde(rename = "entityType")] #[serde(alias = "entityType")]
pub entity_type: String, pub entity_type: String,
#[serde(default)] #[serde(default)]
pub observations: Vec<String>, pub observations: Vec<String>,
@@ -34,7 +34,7 @@ pub struct Entity {
pub struct Relation { pub struct Relation {
pub from: String, pub from: String,
pub to: String, pub to: String,
#[serde(rename = "relationType")] #[serde(alias = "relationType")]
pub relation_type: String, pub relation_type: String,
#[serde(default = "default_namespace")] #[serde(default = "default_namespace")]
pub namespace: String, pub namespace: String,
@@ -50,7 +50,7 @@ pub struct KnowledgeGraph {
pub struct AcceptanceCriteria { pub struct AcceptanceCriteria {
pub id: String, pub id: String,
pub description: String, pub description: String,
#[serde(alias = "is_met", rename = "isMet")] #[serde(alias = "is_met")]
pub is_met: bool, pub is_met: bool,
} }
@@ -60,19 +60,19 @@ pub struct Task {
pub title: String, pub title: String,
pub status: String, pub status: String,
pub description: String, pub description: String,
#[serde(alias = "created_at", rename = "createdAt")] #[serde(alias = "created_at")]
pub created_at: u64, pub created_at: u64,
#[serde(alias = "updated_at", rename = "updatedAt")] #[serde(alias = "updated_at")]
pub updated_at: u64, pub updated_at: u64,
#[serde(alias = "git_branch", rename = "gitBranch")] #[serde(alias = "git_branch")]
pub git_branch: Option<String>, pub git_branch: Option<String>,
#[serde(default)] #[serde(default)]
#[serde(alias = "parent_id", rename = "parentId")] #[serde(alias = "parent_id")]
pub parent_id: Option<String>, pub parent_id: Option<String>,
#[serde(default)] #[serde(default)]
pub dependencies: Vec<String>, pub dependencies: Vec<String>,
#[serde(default)] #[serde(default)]
#[serde(alias = "acceptance_criteria", rename = "acceptanceCriteria")] #[serde(alias = "acceptance_criteria")]
pub acceptance_criteria: Vec<AcceptanceCriteria>, pub acceptance_criteria: Vec<AcceptanceCriteria>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, Default)] #[derive(Debug, Clone, Serialize, Deserialize, Default)]
+25 -6
View File
@@ -17,7 +17,7 @@ pub struct CreateRelationsTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ObservationInput { pub struct ObservationInput {
#[serde(rename = "entityName")] #[serde(alias = "entityName")]
pub entity_name: String, pub entity_name: String,
pub contents: Vec<String>, pub contents: Vec<String>,
} }
@@ -33,13 +33,13 @@ pub struct AddObservationsTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DeleteEntitiesTool { pub struct DeleteEntitiesTool {
/// Array of entity names to delete. /// Array of entity names to delete.
#[serde(rename = "entityNames")] #[serde(alias = "entityNames")]
pub entity_names: Vec<String>, pub entity_names: Vec<String>,
} }
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DeleteObservationInput { pub struct DeleteObservationInput {
#[serde(rename = "entityName")] #[serde(alias = "entityName")]
pub entity_name: String, pub entity_name: String,
pub observations: Vec<String>, pub observations: Vec<String>,
} }
@@ -134,7 +134,7 @@ pub struct ClearStickyNotesTool {}
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct CondenseEntityTool { pub struct CondenseEntityTool {
/// The name of the entity to condense. /// The name of the entity to condense.
#[serde(rename = "entityName")] #[serde(alias = "entityName")]
pub entity_name: String, pub entity_name: String,
/// The condensed observations that will replace the existing ones. /// The condensed observations that will replace the existing ones.
pub summarized_observations: Vec<String>, pub summarized_observations: Vec<String>,
@@ -230,10 +230,10 @@ pub struct QueryDecisionsTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct MergeEntitiesTool { pub struct MergeEntitiesTool {
/// The name of the entity to merge from (will be deleted). /// The name of the entity to merge from (will be deleted).
#[serde(rename = "sourceEntity")] #[serde(alias = "sourceEntity")]
pub source_entity: String, pub source_entity: String,
/// The name of the entity to merge into. /// The name of the entity to merge into.
#[serde(rename = "targetEntity")] #[serde(alias = "targetEntity")]
pub target_entity: String, pub target_entity: String,
} }
@@ -278,6 +278,7 @@ pub struct SearchErrorFixesTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct PinFileTool { pub struct PinFileTool {
/// The namespace to pin the file in. /// The namespace to pin the file in.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// The absolute path of the file to pin. /// The absolute path of the file to pin.
pub file_path: String, pub file_path: String,
@@ -289,6 +290,7 @@ pub struct PinFileTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UnpinFileTool { pub struct UnpinFileTool {
/// The namespace the file is pinned in. /// The namespace the file is pinned in.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// The absolute path of the file to unpin. /// The absolute path of the file to unpin.
pub file_path: String, pub file_path: String,
@@ -309,6 +311,7 @@ pub struct AddSessionSummaryTool {
/// The summary content. /// The summary content.
pub summary: String, pub summary: String,
/// The namespace to add the summary to. /// The namespace to add the summary to.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
} }
@@ -326,6 +329,7 @@ pub struct LeaveHandoffMemoTool {
/// The content of the memo. /// The content of the memo.
pub content: String, pub content: String,
/// The namespace to leave the memo in. /// The namespace to leave the memo in.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
} }
@@ -347,6 +351,7 @@ pub struct ClearHandoffMemosTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UpdateEnvFingerprintTool { pub struct UpdateEnvFingerprintTool {
/// The namespace to update the fingerprint for. /// The namespace to update the fingerprint for.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// A map of tool names to their versions. /// A map of tool names to their versions.
pub tool_versions: std::collections::HashMap<String, String>, pub tool_versions: std::collections::HashMap<String, String>,
@@ -356,6 +361,7 @@ pub struct UpdateEnvFingerprintTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ReadEnvFingerprintTool { pub struct ReadEnvFingerprintTool {
/// The namespace to read the fingerprint for. /// The namespace to read the fingerprint for.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
} }
@@ -363,6 +369,7 @@ pub struct ReadEnvFingerprintTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LogEnvRequirementTool { pub struct LogEnvRequirementTool {
/// The namespace. /// The namespace.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// The environment variable key (e.g., DATABASE_URL). /// The environment variable key (e.g., DATABASE_URL).
pub key: String, pub key: String,
@@ -378,6 +385,7 @@ pub struct AddMilestoneTool {
/// The title of the milestone. /// The title of the milestone.
pub title: String, pub title: String,
/// The namespace for the milestone. /// The namespace for the milestone.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
} }
@@ -401,6 +409,7 @@ pub struct ListMilestonesTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GenerateStandupReportTool { pub struct GenerateStandupReportTool {
/// The namespace to generate the report for. /// The namespace to generate the report for.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// The number of hours to look back for activity. /// The number of hours to look back for activity.
pub hours_lookback: u64, pub hours_lookback: u64,
@@ -410,6 +419,7 @@ pub struct GenerateStandupReportTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct RegisterEnvironmentTool { pub struct RegisterEnvironmentTool {
/// The namespace. /// The namespace.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// The name of the environment (e.g., 'staging', 'prod'). /// The name of the environment (e.g., 'staging', 'prod').
pub name: String, pub name: String,
@@ -425,6 +435,7 @@ pub struct RegisterEnvironmentTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GetEnvironmentDetailsTool { pub struct GetEnvironmentDetailsTool {
/// The namespace to retrieve details for. /// The namespace to retrieve details for.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
} }
@@ -432,6 +443,7 @@ pub struct GetEnvironmentDetailsTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct AddPrChecklistItemTool { pub struct AddPrChecklistItemTool {
/// The namespace. /// The namespace.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// The description of the checklist item. /// The description of the checklist item.
pub description: String, pub description: String,
@@ -441,6 +453,7 @@ pub struct AddPrChecklistItemTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GetPrChecklistTool { pub struct GetPrChecklistTool {
/// The namespace. /// The namespace.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
} }
@@ -448,6 +461,7 @@ pub struct GetPrChecklistTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ClearPrChecklistTool { pub struct ClearPrChecklistTool {
/// The namespace. /// The namespace.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
} }
@@ -455,6 +469,7 @@ pub struct ClearPrChecklistTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LogTechDebtTool { pub struct LogTechDebtTool {
/// The namespace. /// The namespace.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// A description of the technical debt. /// A description of the technical debt.
pub description: String, pub description: String,
@@ -477,6 +492,7 @@ pub struct ResolveTechDebtTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ListTechDebtTool { pub struct ListTechDebtTool {
/// The namespace. /// The namespace.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// Whether to include resolved technical debt in the results. /// Whether to include resolved technical debt in the results.
pub include_resolved: bool, pub include_resolved: bool,
@@ -486,6 +502,7 @@ pub struct ListTechDebtTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct SaveContextWorkspaceTool { pub struct SaveContextWorkspaceTool {
/// The namespace for the workspace. /// The namespace for the workspace.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// The name to save the workspace as. /// The name to save the workspace as.
pub name: String, pub name: String,
@@ -499,6 +516,7 @@ pub struct SaveContextWorkspaceTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LoadContextWorkspaceTool { pub struct LoadContextWorkspaceTool {
/// The namespace of the workspace. /// The namespace of the workspace.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
/// The name of the workspace to load. /// The name of the workspace to load.
pub name: String, pub name: String,
@@ -524,6 +542,7 @@ pub struct OmniSearchTool {
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GetProjectHealthTool { pub struct GetProjectHealthTool {
/// The namespace to get health for. /// The namespace to get health for.
#[serde(default = "crate::models::default_namespace")]
pub namespace: String, pub namespace: String,
} }