ai_crew_sync/tools/
notes.rs1use rmcp::{
2 ErrorData, Json, handler::server::wrapper::Parameters, service::RequestContext, tool,
3 tool_router,
4};
5use schemars::JsonSchema;
6use serde::Deserialize;
7
8use super::{Bus, auth_of};
9use crate::{
10 model::{Ack, NoteInfo, NoteList, NoteRef},
11 store::notes,
12};
13
14fn default_limit() -> i64 {
15 50
16}
17
18#[derive(Debug, Deserialize, JsonSchema)]
19pub struct SetNoteArgs {
20 #[serde(default)]
23 pub scope: Option<String>,
24 pub key: String,
26 pub value: String,
29 #[serde(default)]
31 pub tags: Option<Vec<String>>,
32}
33
34#[derive(Debug, Deserialize, JsonSchema)]
35pub struct GetNoteArgs {
36 #[serde(default)]
38 pub scope: Option<String>,
39 pub key: String,
41}
42
43#[derive(Debug, Deserialize, JsonSchema)]
44pub struct ListNotesArgs {
45 #[serde(default)]
47 pub scope: Option<String>,
48 #[serde(default)]
50 pub tag: Option<String>,
51 #[serde(default = "default_limit")]
53 pub limit: i64,
54}
55
56#[derive(Debug, Deserialize, JsonSchema)]
57pub struct SearchNotesArgs {
58 pub query: String,
60 #[serde(default)]
62 pub scope: Option<String>,
63 #[serde(default = "default_limit")]
65 pub limit: i64,
66}
67
68#[tool_router(router = notes_router, vis = "pub")]
69impl Bus {
70 #[tool(
71 description = "Write or overwrite a shared note: a decision, a gotcha, the state \
72 of a deploy. This is the team's durable memory, readable by every \
73 teammate's agent. Previous versions are kept."
74 )]
75 async fn set_note(
76 &self,
77 ctx: RequestContext<rmcp::RoleServer>,
78 Parameters(args): Parameters<SetNoteArgs>,
79 ) -> Result<Json<NoteInfo>, ErrorData> {
80 let auth = auth_of(&ctx)?;
81 let input = notes::SetInput {
82 scope: args.scope,
83 key: args.key,
84 value: args.value,
85 tags: args.tags,
86 };
87 Ok(Json(notes::set_note(&self.db, &auth, input).await?))
88 }
89
90 #[tool(
91 description = "Read one shared note by scope and key. Returns found=false rather \
92 than erroring when the note does not exist."
93 )]
94 async fn get_note(
95 &self,
96 ctx: RequestContext<rmcp::RoleServer>,
97 Parameters(args): Parameters<GetNoteArgs>,
98 ) -> Result<Json<NoteRef>, ErrorData> {
99 let auth = auth_of(&ctx)?;
100 Ok(Json(
101 notes::get_note(&self.db, &auth, args.scope, &args.key).await?,
102 ))
103 }
104
105 #[tool(
106 description = "List shared notes, optionally filtered by scope or tag. Use this \
107 to discover what the team already wrote down."
108 )]
109 async fn list_notes(
110 &self,
111 ctx: RequestContext<rmcp::RoleServer>,
112 Parameters(args): Parameters<ListNotesArgs>,
113 ) -> Result<Json<NoteList>, ErrorData> {
114 let auth = auth_of(&ctx)?;
115 Ok(Json(
116 notes::list_notes(&self.db, &auth, args.scope, args.tag, args.limit).await?,
117 ))
118 }
119
120 #[tool(description = "Full-text search the team's shared notes.")]
121 async fn search_notes(
122 &self,
123 ctx: RequestContext<rmcp::RoleServer>,
124 Parameters(args): Parameters<SearchNotesArgs>,
125 ) -> Result<Json<NoteList>, ErrorData> {
126 let auth = auth_of(&ctx)?;
127 Ok(Json(
128 notes::search_notes(&self.db, &auth, &args.query, args.scope, args.limit).await?,
129 ))
130 }
131
132 #[tool(description = "Delete a shared note. Its revision history goes with it.")]
133 async fn delete_note(
134 &self,
135 ctx: RequestContext<rmcp::RoleServer>,
136 Parameters(args): Parameters<GetNoteArgs>,
137 ) -> Result<Json<Ack>, ErrorData> {
138 let auth = auth_of(&ctx)?;
139 Ok(Json(
140 notes::delete_note(&self.db, &auth, args.scope, &args.key).await?,
141 ))
142 }
143}