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