refactor: Enforce strict typed JsonSchema for all MCP tools
This commit is contained in:
1 parent
686fea683d
commit
2d3aaed289
31 files changed
+2892
-308
No files matched your search
+29
-18
@@ -710,32 +710,43 @@
|
||||
}
|
||||
}
|
||||
|
||||
// --- SSE Activity Feed ---
|
||||
function setupSSE() {
|
||||
const es = new EventSource('/sse');
|
||||
// --- WebSocket Activity Feed ---
|
||||
function setupWS() {
|
||||
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const ws = new WebSocket(`${protocol}//${location.host}/ws?client=ui`);
|
||||
const feed = document.getElementById('activity-feed');
|
||||
|
||||
es.addEventListener('activity', function(event) {
|
||||
if (event.data.startsWith('/messages?')) return;
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.className = 'feed-entry';
|
||||
const time = new Date().toLocaleTimeString();
|
||||
div.innerHTML = `<span class="time">[${time}]</span> ${event.data}`;
|
||||
feed.appendChild(div);
|
||||
|
||||
// Auto-scroll logic
|
||||
const isScrolledToBottom = feed.scrollHeight - feed.clientHeight <= feed.scrollTop + 20;
|
||||
if (isScrolledToBottom) {
|
||||
feed.scrollTop = feed.scrollHeight;
|
||||
ws.onmessage = function(event) {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.type === 'activity') {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'feed-entry';
|
||||
const time = new Date().toLocaleTimeString();
|
||||
div.innerHTML = `<span class="time">[${time}]</span> ${data.data}`;
|
||||
feed.appendChild(div);
|
||||
|
||||
// Auto-scroll logic
|
||||
const isScrolledToBottom = feed.scrollHeight - feed.clientHeight <= feed.scrollTop + 20;
|
||||
if (isScrolledToBottom) {
|
||||
feed.scrollTop = feed.scrollHeight;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore non-JSON or other messages for now
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
console.log("WebSocket closed, attempting to reconnect in 3s...");
|
||||
setTimeout(setupWS, 3000);
|
||||
};
|
||||
}
|
||||
|
||||
// --- Start ---
|
||||
loadGraph();
|
||||
loadTasks();
|
||||
setupSSE();
|
||||
setupWS();
|
||||
|
||||
// Listen to theme toggle changes to update graph font colors
|
||||
const observer = new MutationObserver(() => updateGraphData());
|
||||
|
||||
+81
-59
@@ -39,7 +39,8 @@ impl MemoryHandler {
|
||||
}
|
||||
"tools/list" => {
|
||||
let tools = vec![
|
||||
crate::mcp::tool_def::<CreateEntitiesTool>("create_entities", "Create new entities in the knowledge graph."),
|
||||
crate::mcp::tool_def::<crate::tools::QueryGraphPathTool>("query_graph_path", "Traverse the knowledge graph to find a path between two entities."),
|
||||
crate::mcp::tool_def::<CreateEntitiesTool>("create_entities", "Create new entities in the knowledge graph."),
|
||||
crate::mcp::tool_def::<CreateRelationsTool>("create_relations", "Create new relations between entities in the knowledge graph."),
|
||||
crate::mcp::tool_def::<AddObservationsTool>("add_observations", "Add new observations to existing entities in the knowledge graph."),
|
||||
crate::mcp::tool_def::<DeleteEntitiesTool>("delete_entities", "Delete entities from the knowledge graph."),
|
||||
@@ -120,7 +121,68 @@ impl MemoryHandler {
|
||||
.unwrap_or(serde_json::Value::Object(Default::default()));
|
||||
|
||||
let result: Result<String, String> = match name {
|
||||
"create_entities" => {
|
||||
"query_graph_path" => {
|
||||
let req: crate::tools::QueryGraphPathTool = match parse_args(args.clone()) {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": format!("Invalid args: {}", e)}] })))
|
||||
};
|
||||
let graph = self.state.get_full_graph();
|
||||
let max_depth = req.max_depth.unwrap_or(5);
|
||||
let mut queue = std::collections::VecDeque::new();
|
||||
let mut visited = std::collections::HashSet::new();
|
||||
let mut parents: std::collections::HashMap<String, (String, String)> = std::collections::HashMap::new();
|
||||
|
||||
queue.push_back(req.start_node.clone());
|
||||
visited.insert(req.start_node.clone());
|
||||
|
||||
let mut found = false;
|
||||
let mut current_depth = 0;
|
||||
let mut nodes_at_current_depth = 1;
|
||||
let mut nodes_at_next_depth = 0;
|
||||
|
||||
while let Some(current) = queue.pop_front() {
|
||||
if current == req.end_node {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
nodes_at_current_depth -= 1;
|
||||
if current_depth < max_depth {
|
||||
for rel in &graph.relations {
|
||||
if rel.from == current && !visited.contains(&rel.to) {
|
||||
visited.insert(rel.to.clone());
|
||||
parents.insert(rel.to.clone(), (current.clone(), rel.relation_type.clone()));
|
||||
queue.push_back(rel.to.clone());
|
||||
nodes_at_next_depth += 1;
|
||||
} else if rel.to == current && !visited.contains(&rel.from) {
|
||||
visited.insert(rel.from.clone());
|
||||
parents.insert(rel.from.clone(), (current.clone(), format!("inverse({})", rel.relation_type)));
|
||||
queue.push_back(rel.from.clone());
|
||||
nodes_at_next_depth += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if nodes_at_current_depth == 0 {
|
||||
current_depth += 1;
|
||||
nodes_at_current_depth = nodes_at_next_depth;
|
||||
nodes_at_next_depth = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
let mut path = Vec::new();
|
||||
let mut curr = req.end_node.clone();
|
||||
while curr != req.start_node {
|
||||
let (parent, rel) = parents.get(&curr).unwrap();
|
||||
path.push(format!("({}) --[{}]--> ({})", parent, rel, curr));
|
||||
curr = parent.clone();
|
||||
}
|
||||
path.reverse();
|
||||
Ok(format!("Path found:\n{}", path.join("\n")))
|
||||
} else {
|
||||
Ok(format!("No path found between {} and {} within depth {}", req.start_node, req.end_node, max_depth))
|
||||
}
|
||||
}
|
||||
"create_entities" => {
|
||||
let req: CreateEntitiesTool = match parse_args(args.clone()) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
@@ -131,19 +193,12 @@ impl MemoryHandler {
|
||||
}
|
||||
};
|
||||
self.state.write_to_local_delta(|g| {
|
||||
for e_val in req.entities {
|
||||
if let Some(e) = if e_val.is_string() {
|
||||
serde_json::from_str(e_val.as_str().unwrap()).ok()
|
||||
} else {
|
||||
serde_json::from_value(e_val).ok()
|
||||
} {
|
||||
let entity: Entity = e;
|
||||
if !entity.name.is_empty() {
|
||||
if let Ok(idx) = self.state.search_index.read() {
|
||||
let _ = idx.index_entity(&entity);
|
||||
}
|
||||
g.entities.insert(entity.name.clone(), entity);
|
||||
for entity in req.entities {
|
||||
if !entity.name.is_empty() {
|
||||
if let Ok(idx) = self.state.search_index.read() {
|
||||
let _ = idx.index_entity(&entity);
|
||||
}
|
||||
g.entities.insert(entity.name.clone(), entity);
|
||||
}
|
||||
}
|
||||
}).await;
|
||||
@@ -160,16 +215,9 @@ impl MemoryHandler {
|
||||
}
|
||||
};
|
||||
self.state.write_to_local_delta(|g| {
|
||||
for r_val in req.relations {
|
||||
if let Some(r) = if r_val.is_string() {
|
||||
serde_json::from_str(r_val.as_str().unwrap()).ok()
|
||||
} else {
|
||||
serde_json::from_value(r_val).ok()
|
||||
} {
|
||||
let relation: Relation = r;
|
||||
if !relation.from.is_empty() && !relation.to.is_empty() {
|
||||
g.relations.push(relation);
|
||||
}
|
||||
for relation in req.relations {
|
||||
if !relation.from.is_empty() && !relation.to.is_empty() {
|
||||
g.relations.push(relation);
|
||||
}
|
||||
}
|
||||
}).await;
|
||||
@@ -185,20 +233,10 @@ impl MemoryHandler {
|
||||
));
|
||||
}
|
||||
};
|
||||
#[derive(Deserialize)]
|
||||
struct ObsInput {
|
||||
#[serde(rename = "entityName")]
|
||||
entity_name: String,
|
||||
contents: Vec<String>,
|
||||
}
|
||||
let full = self.state.get_full_graph();
|
||||
self.state.write_to_local_delta(|g| {
|
||||
for o_val in req.observations {
|
||||
if let Some(o) = if o_val.is_string() {
|
||||
serde_json::from_str::<ObsInput>(o_val.as_str().unwrap()).ok()
|
||||
} else {
|
||||
serde_json::from_value(o_val).ok()
|
||||
} && let Some(full_e) = full.entities.get(&o.entity_name)
|
||||
for o in req.observations {
|
||||
if let Some(full_e) = full.entities.get(&o.entity_name)
|
||||
{
|
||||
let mut e =
|
||||
g.entities.get(&o.entity_name).cloned().unwrap_or_else(
|
||||
@@ -248,19 +286,9 @@ impl MemoryHandler {
|
||||
));
|
||||
}
|
||||
};
|
||||
#[derive(Deserialize)]
|
||||
struct ObsDel {
|
||||
#[serde(rename = "entityName")]
|
||||
entity_name: String,
|
||||
observations: Vec<String>,
|
||||
}
|
||||
self.state.apply_sync_write(|master| {
|
||||
for d_val in req.deletions {
|
||||
if let Some(d) = if d_val.is_string() {
|
||||
serde_json::from_str::<ObsDel>(d_val.as_str().unwrap()).ok()
|
||||
} else {
|
||||
serde_json::from_value(d_val).ok()
|
||||
} && let Some(e) = master.entities.get_mut(&d.entity_name)
|
||||
for d in req.deletions {
|
||||
if let Some(e) = master.entities.get_mut(&d.entity_name)
|
||||
{
|
||||
let to_rem: HashSet<_> = d.observations.into_iter().collect();
|
||||
e.observations.retain(|o| !to_rem.contains(o));
|
||||
@@ -281,17 +309,11 @@ impl MemoryHandler {
|
||||
};
|
||||
self.state.apply_sync_write(|master| {
|
||||
let mut to_rem = HashSet::new();
|
||||
for r_val in req.relations {
|
||||
if let Some(r) = if r_val.is_string() {
|
||||
serde_json::from_str::<Relation>(r_val.as_str().unwrap()).ok()
|
||||
} else {
|
||||
serde_json::from_value(r_val).ok()
|
||||
} {
|
||||
to_rem.insert(format!(
|
||||
"{}|{}|{}|{}",
|
||||
r.from, r.to, r.relation_type, r.namespace
|
||||
));
|
||||
}
|
||||
for r in req.relations {
|
||||
to_rem.insert(format!(
|
||||
"{}|{}|{}|{}",
|
||||
r.from, r.to, r.relation_type, r.namespace
|
||||
));
|
||||
}
|
||||
master.relations.retain(|r| {
|
||||
!to_rem.contains(&format!(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use schemars::JsonSchema;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct CodeChange {
|
||||
@@ -17,7 +18,7 @@ pub struct StickyNote {
|
||||
pub fn default_namespace() -> String {
|
||||
"global".to_string()
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct Entity {
|
||||
pub name: String,
|
||||
#[serde(rename = "entityType")]
|
||||
@@ -29,7 +30,7 @@ pub struct Entity {
|
||||
#[serde(default)]
|
||||
pub git_branch: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, JsonSchema)]
|
||||
pub struct Relation {
|
||||
pub from: String,
|
||||
pub to: String,
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
use futures_util::StreamExt;
|
||||
use std::sync::Arc;
|
||||
use tokio::io::AsyncBufReadExt;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio_util::io::StreamReader;
|
||||
|
||||
pub fn run_proxy(target_url: &str) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let (msg_tx, mut msg_rx) = mpsc::channel::<String>(100);
|
||||
let (shutdown_tx, mut shutdown_rx) = mpsc::channel::<()>(1);
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let stdin = std::io::stdin();
|
||||
let mut handle = stdin.lock();
|
||||
let mut buffer = String::new();
|
||||
while let Ok(bytes) = std::io::BufRead::read_line(&mut handle, &mut buffer) {
|
||||
if bytes == 0 {
|
||||
break;
|
||||
}
|
||||
let _ = msg_tx.blocking_send(buffer.clone());
|
||||
buffer.clear();
|
||||
}
|
||||
let _ = shutdown_tx.blocking_send(());
|
||||
});
|
||||
|
||||
let target_url = target_url.to_string();
|
||||
let post_url = Arc::new(RwLock::new(String::new()));
|
||||
let post_url_clone = Arc::clone(&post_url);
|
||||
|
||||
let client = reqwest::Client::builder().build().unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(msg) = msg_rx.recv().await {
|
||||
loop {
|
||||
let url = post_url_clone.read().await.clone();
|
||||
if !url.is_empty() {
|
||||
let res = client
|
||||
.post(&url)
|
||||
.header("Accept", "application/json, text/event-stream")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(msg.clone())
|
||||
.send()
|
||||
.await;
|
||||
|
||||
if res.is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if shutdown_rx.try_recv().is_ok() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let sse_url = format!("{}/sse", target_url);
|
||||
let client = reqwest::Client::builder().build().unwrap();
|
||||
|
||||
match client
|
||||
.get(&sse_url)
|
||||
.header("Accept", "text/event-stream")
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(resp) => {
|
||||
if resp.status() == reqwest::StatusCode::GONE {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let stream = resp.bytes_stream().map(|res| {
|
||||
res.map_err(std::io::Error::other)
|
||||
});
|
||||
let mut reader = tokio::io::BufReader::new(StreamReader::new(stream));
|
||||
let mut line = String::new();
|
||||
let mut is_message = false;
|
||||
let mut is_endpoint = false;
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = shutdown_rx.recv() => {
|
||||
return Ok(false);
|
||||
}
|
||||
res = reader.read_line(&mut line) => {
|
||||
match res {
|
||||
Ok(bytes) => {
|
||||
if bytes == 0 { break; }
|
||||
let trimmed = line.trim();
|
||||
if trimmed.starts_with("event: message") {
|
||||
is_message = true;
|
||||
is_endpoint = false;
|
||||
} else if trimmed.starts_with("event: endpoint") {
|
||||
is_endpoint = true;
|
||||
is_message = false;
|
||||
} else if let Some(stripped) = trimmed.strip_prefix("data: ") {
|
||||
if is_message {
|
||||
println!("{}", stripped);
|
||||
is_message = false;
|
||||
} else if is_endpoint {
|
||||
let mut p = post_url.write().await;
|
||||
*p = format!("{}{}", target_url, stripped);
|
||||
is_endpoint = false;
|
||||
}
|
||||
}
|
||||
line.clear();
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*post_url.write().await = String::new();
|
||||
Ok(true)
|
||||
}
|
||||
Err(_) => Ok(true),
|
||||
}
|
||||
})
|
||||
}
|
||||
+286
-46
@@ -1,276 +1,516 @@
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Create new entities in the knowledge graph.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct CreateEntitiesTool {
|
||||
pub entities: Vec<serde_json::Value>,
|
||||
/// Array of entities to create.
|
||||
pub entities: Vec<crate::models::Entity>,
|
||||
}
|
||||
|
||||
/// Create new relations between entities in the knowledge graph.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct CreateRelationsTool {
|
||||
pub relations: Vec<serde_json::Value>,
|
||||
/// Array of relations to create.
|
||||
pub relations: Vec<crate::models::Relation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ObservationInput {
|
||||
#[serde(rename = "entityName")]
|
||||
pub entity_name: String,
|
||||
pub contents: Vec<String>,
|
||||
}
|
||||
|
||||
/// Add new observations to existing entities in the knowledge graph.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct AddObservationsTool {
|
||||
pub observations: Vec<serde_json::Value>,
|
||||
/// Array of observations to add.
|
||||
pub observations: Vec<ObservationInput>,
|
||||
}
|
||||
|
||||
/// Delete entities from the knowledge graph.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct DeleteEntitiesTool {
|
||||
/// Array of entity names to delete.
|
||||
#[serde(rename = "entityNames")]
|
||||
pub entity_names: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct DeleteObservationsTool {
|
||||
pub deletions: Vec<serde_json::Value>,
|
||||
}
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct DeleteRelationsTool {
|
||||
pub relations: Vec<serde_json::Value>,
|
||||
}
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ReadGraphTool {
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct SearchNodesTool {
|
||||
pub query: String,
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct OpenNodesTool {
|
||||
pub names: Vec<String>,
|
||||
}
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct LogCodeChangeTool {
|
||||
#[serde(rename = "filePath")]
|
||||
pub file_path: String,
|
||||
pub description: String,
|
||||
pub git_commit: Option<String>,
|
||||
pub git_branch: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct QueryRecentChangesTool {}
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct VisualizeGraphTool {
|
||||
pub query: Option<String>,
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct AddStickyNoteTool {
|
||||
pub content: String,
|
||||
}
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ReadStickyNotesTool {}
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct CondenseEntityTool {
|
||||
pub struct DeleteObservationInput {
|
||||
#[serde(rename = "entityName")]
|
||||
pub entity_name: String,
|
||||
pub observations: Vec<String>,
|
||||
}
|
||||
|
||||
/// Delete observations from existing entities.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct DeleteObservationsTool {
|
||||
/// Array of observation deletions.
|
||||
pub deletions: Vec<DeleteObservationInput>,
|
||||
}
|
||||
|
||||
/// Delete relations between entities.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct DeleteRelationsTool {
|
||||
/// Array of relations to delete.
|
||||
pub relations: Vec<crate::models::Relation>,
|
||||
}
|
||||
|
||||
/// Read the entire knowledge graph.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ReadGraphTool {
|
||||
/// Optional namespace to restrict the read to.
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
|
||||
/// Search for entities in the knowledge graph by name or type.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct SearchNodesTool {
|
||||
/// The search query.
|
||||
pub query: String,
|
||||
/// Optional namespace to restrict the search to.
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
|
||||
/// Open and retrieve full details of specific nodes in the knowledge graph.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct OpenNodesTool {
|
||||
/// Array of entity names to open.
|
||||
pub names: Vec<String>,
|
||||
}
|
||||
|
||||
/// Log a significant code change or refactor in the memory system.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct LogCodeChangeTool {
|
||||
/// The path of the file that was changed.
|
||||
#[serde(rename = "filePath")]
|
||||
pub file_path: String,
|
||||
/// A description of the change.
|
||||
pub description: String,
|
||||
/// The associated git commit hash, if any.
|
||||
pub git_commit: Option<String>,
|
||||
/// The associated git branch, if any.
|
||||
pub git_branch: Option<String>,
|
||||
}
|
||||
|
||||
/// Query recently logged code changes.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct QueryRecentChangesTool {}
|
||||
|
||||
/// Generate a visual representation of the knowledge graph.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct VisualizeGraphTool {
|
||||
/// Optional search query to filter the graph before visualization.
|
||||
pub query: Option<String>,
|
||||
/// Optional namespace to restrict the visualization to.
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
|
||||
/// Add a sticky note for unstructured thoughts or reminders.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct AddStickyNoteTool {
|
||||
/// The content of the sticky note.
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
/// Read all active sticky notes.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ReadStickyNotesTool {}
|
||||
|
||||
/// Condense or summarize an entity's observations to reduce size.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct CondenseEntityTool {
|
||||
/// The name of the entity to condense.
|
||||
#[serde(rename = "entityName")]
|
||||
pub entity_name: String,
|
||||
/// The condensed observations that will replace the existing ones.
|
||||
pub summarized_observations: Vec<String>,
|
||||
}
|
||||
|
||||
/// Add a new task to the task tracker.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct AddTaskTool {
|
||||
/// The title of the task.
|
||||
pub title: String,
|
||||
/// A detailed description of the task.
|
||||
pub description: String,
|
||||
/// The associated git branch, if any.
|
||||
pub git_branch: Option<String>,
|
||||
}
|
||||
|
||||
/// Update the status of an existing task.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct UpdateTaskStatusTool {
|
||||
/// The ID of the task to update.
|
||||
pub id: String,
|
||||
/// The new status of the task (e.g., 'pending' or 'completed').
|
||||
#[schemars(description = "Must be 'pending' or 'completed'")]
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
/// List all currently active tasks.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ListActiveTasksTool {
|
||||
/// Optional git branch to filter tasks by.
|
||||
pub git_branch: Option<String>,
|
||||
}
|
||||
|
||||
/// Store a reusable code snippet.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct StoreSnippetTool {
|
||||
/// The name of the snippet.
|
||||
pub name: String,
|
||||
/// The programming language of the snippet.
|
||||
pub language: String,
|
||||
/// The code snippet content.
|
||||
pub code: String,
|
||||
/// A description of what the snippet does.
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
/// Search through stored code snippets.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct SearchSnippetsTool {
|
||||
/// The search query.
|
||||
pub query: String,
|
||||
}
|
||||
|
||||
/// Delete a stored code snippet.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct DeleteSnippetTool {
|
||||
/// The name of the snippet to delete.
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// Log an architectural decision record (ADR).
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct LogDecisionTool {
|
||||
/// The title of the decision.
|
||||
pub title: String,
|
||||
/// The context or problem requiring a decision.
|
||||
pub context: String,
|
||||
/// The decision made.
|
||||
pub decision: String,
|
||||
/// The consequence of the decision.
|
||||
pub consequence: String,
|
||||
}
|
||||
|
||||
/// Query architectural decision records.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct QueryDecisionsTool {
|
||||
/// Optional search query.
|
||||
pub query: Option<String>,
|
||||
}
|
||||
|
||||
/// Merge two entities in the knowledge graph into one.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct MergeEntitiesTool {
|
||||
/// The name of the entity to merge from (will be deleted).
|
||||
#[serde(rename = "sourceEntity")]
|
||||
pub source_entity: String,
|
||||
/// The name of the entity to merge into.
|
||||
#[serde(rename = "targetEntity")]
|
||||
pub target_entity: String,
|
||||
}
|
||||
|
||||
/// Find orphaned entities (entities without any relations) in the graph.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct FindOrphansTool {}
|
||||
|
||||
/// Record a user preference or behavior to adapt future interactions.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct LearnPreferenceTool {
|
||||
/// The key for the preference.
|
||||
pub key: String,
|
||||
/// The value of the preference.
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
/// Read all learned user preferences.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ReadPreferencesTool {}
|
||||
|
||||
/// Log a complex error and its fix for future reference.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct LogErrorFixTool {
|
||||
/// The error signature or stack trace.
|
||||
pub signature: String,
|
||||
/// The solution applied to fix the error.
|
||||
pub solution: String,
|
||||
/// The associated git commit hash, if any.
|
||||
pub git_commit: Option<String>,
|
||||
/// The associated git branch, if any.
|
||||
pub git_branch: Option<String>,
|
||||
}
|
||||
|
||||
/// Search through previously logged error fixes.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct SearchErrorFixesTool {
|
||||
/// The search query.
|
||||
pub query: String,
|
||||
}
|
||||
|
||||
/// Pin a file to keep it explicitly in the context workspace.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct PinFileTool {
|
||||
/// The namespace to pin the file in.
|
||||
pub namespace: String,
|
||||
/// The absolute path of the file to pin.
|
||||
pub file_path: String,
|
||||
/// The associated git branch, if any.
|
||||
pub git_branch: Option<String>,
|
||||
}
|
||||
|
||||
/// Unpin a file from the context workspace.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct UnpinFileTool {
|
||||
/// The namespace the file is pinned in.
|
||||
pub namespace: String,
|
||||
/// The absolute path of the file to unpin.
|
||||
pub file_path: String,
|
||||
}
|
||||
|
||||
/// List all currently pinned files.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ListPinnedFilesTool {
|
||||
/// Optional namespace to filter by.
|
||||
pub namespace: Option<String>,
|
||||
/// Optional git branch to filter by.
|
||||
pub git_branch: Option<String>,
|
||||
}
|
||||
|
||||
/// Add a summary of the current session.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct AddSessionSummaryTool {
|
||||
/// The summary content.
|
||||
pub summary: String,
|
||||
/// The namespace to add the summary to.
|
||||
pub namespace: String,
|
||||
}
|
||||
|
||||
/// Get a timeline of major project events.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct GetProjectTimelineTool {
|
||||
/// Optional namespace to restrict the timeline to.
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
|
||||
/// Leave a memo for the next session or agent.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct LeaveHandoffMemoTool {
|
||||
/// The content of the memo.
|
||||
pub content: String,
|
||||
/// The namespace to leave the memo in.
|
||||
pub namespace: String,
|
||||
}
|
||||
|
||||
/// Read pending handoff memos.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ReadHandoffMemosTool {
|
||||
/// Optional namespace to restrict the read to.
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
|
||||
/// Clear handoff memos after reading.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ClearHandoffMemosTool {
|
||||
/// Array of memo IDs to clear.
|
||||
pub ids: Vec<String>,
|
||||
}
|
||||
|
||||
/// Update the environment fingerprint (e.g., OS, tool versions).
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct UpdateEnvFingerprintTool {
|
||||
/// The namespace to update the fingerprint for.
|
||||
pub namespace: String,
|
||||
/// A map of tool names to their versions.
|
||||
pub tool_versions: std::collections::HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// Read the environment fingerprint.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ReadEnvFingerprintTool {
|
||||
/// The namespace to read the fingerprint for.
|
||||
pub namespace: String,
|
||||
}
|
||||
|
||||
/// Log a required environment variable or configuration.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct LogEnvRequirementTool {
|
||||
/// The namespace.
|
||||
pub namespace: String,
|
||||
/// The environment variable key (e.g., DATABASE_URL).
|
||||
pub key: String,
|
||||
/// A description of what the variable is used for.
|
||||
pub description: String,
|
||||
/// Whether the variable contains a secret.
|
||||
pub is_secret: bool,
|
||||
}
|
||||
|
||||
/// Add a new milestone.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct AddMilestoneTool {
|
||||
/// The title of the milestone.
|
||||
pub title: String,
|
||||
/// The namespace for the milestone.
|
||||
pub namespace: String,
|
||||
}
|
||||
|
||||
/// Update the status of a milestone.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct UpdateMilestoneTool {
|
||||
/// The ID of the milestone to update.
|
||||
pub id: String,
|
||||
/// The new status (e.g., 'active', 'completed').
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
/// List all milestones.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ListMilestonesTool {
|
||||
/// Optional namespace to restrict the list to.
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
|
||||
/// Generate a standup report for a specific time window.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct GenerateStandupReportTool {
|
||||
/// The namespace to generate the report for.
|
||||
pub namespace: String,
|
||||
/// The number of hours to look back for activity.
|
||||
pub hours_lookback: u64,
|
||||
}
|
||||
|
||||
/// Register a new infrastructure environment.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct RegisterEnvironmentTool {
|
||||
/// The namespace.
|
||||
pub namespace: String,
|
||||
/// The name of the environment (e.g., 'staging', 'prod').
|
||||
pub name: String,
|
||||
/// The URL or connection string for the environment.
|
||||
pub url: String,
|
||||
/// A description of the environment.
|
||||
pub description: String,
|
||||
/// Whether a VPN is required to access the environment.
|
||||
pub requires_vpn: bool,
|
||||
}
|
||||
|
||||
/// Get details about a registered environment.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct GetEnvironmentDetailsTool {
|
||||
/// The namespace to retrieve details for.
|
||||
pub namespace: String,
|
||||
}
|
||||
|
||||
/// Add an item to the PR checklist.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct AddPrChecklistItemTool {
|
||||
/// The namespace.
|
||||
pub namespace: String,
|
||||
/// The description of the checklist item.
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
/// Get the PR checklist.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct GetPrChecklistTool {
|
||||
/// The namespace.
|
||||
pub namespace: String,
|
||||
}
|
||||
|
||||
/// Clear the PR checklist.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ClearPrChecklistTool {
|
||||
/// The namespace.
|
||||
pub namespace: String,
|
||||
}
|
||||
|
||||
/// Log a technical debt record.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct LogTechDebtTool {
|
||||
/// The namespace.
|
||||
pub namespace: String,
|
||||
/// A description of the technical debt.
|
||||
pub description: String,
|
||||
/// The ideal solution to resolve the debt.
|
||||
pub ideal_solution: String,
|
||||
/// The associated git commit hash, if any.
|
||||
pub git_commit: Option<String>,
|
||||
/// The associated git branch, if any.
|
||||
pub git_branch: Option<String>,
|
||||
}
|
||||
|
||||
/// Resolve a technical debt record.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ResolveTechDebtTool {
|
||||
/// The ID of the technical debt record to resolve.
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
/// List technical debt records.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ListTechDebtTool {
|
||||
/// The namespace.
|
||||
pub namespace: String,
|
||||
/// Whether to include resolved technical debt in the results.
|
||||
pub include_resolved: bool,
|
||||
}
|
||||
|
||||
/// Save the current context workspace.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct SaveContextWorkspaceTool {
|
||||
/// The namespace for the workspace.
|
||||
pub namespace: String,
|
||||
/// The name to save the workspace as.
|
||||
pub name: String,
|
||||
/// Array of pinned file paths.
|
||||
pub pinned_files: Vec<String>,
|
||||
/// Array of active task IDs.
|
||||
pub active_task_ids: Vec<String>,
|
||||
}
|
||||
|
||||
/// Load a saved context workspace.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct LoadContextWorkspaceTool {
|
||||
/// The namespace of the workspace.
|
||||
pub namespace: String,
|
||||
/// The name of the workspace to load.
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// List all saved context workspaces.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct ListContextWorkspacesTool {
|
||||
/// The namespace to list workspaces for.
|
||||
pub namespace: String,
|
||||
}
|
||||
|
||||
/// Search across all memory stores (Graph, Tasks, Snippets, ADRs, etc.).
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct OmniSearchTool {
|
||||
/// The search query.
|
||||
pub query: String,
|
||||
/// Optional namespace to restrict the search to.
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
|
||||
/// Get a health digest of the project.
|
||||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct GetProjectHealthTool {
|
||||
/// The namespace to get health for.
|
||||
pub namespace: String,
|
||||
}
|
||||
|
||||
/// Traverse the knowledge graph to find a path between two entities.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct QueryGraphPathTool {
|
||||
/// The starting entity name.
|
||||
pub start_node: String,
|
||||
/// The ending entity name.
|
||||
pub end_node: String,
|
||||
/// Optional maximum depth to search.
|
||||
pub max_depth: Option<u32>,
|
||||
}
|
||||
Reference in new issue
Block a user