feat(notes): add_note MCP tool with a per-day agent cap
Agents can write into the user's daily notes, but only within limits: max 10 agent notes a day, max 500 characters, add only — no edit, no delete. A new from_agent column separates their lines from the user's so the cap counts the right ones and the UI can mark them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
cf093e3178
commit
ae0c7d3771
+59
@@ -0,0 +1,59 @@
|
||||
using System.ComponentModel;
|
||||
using ClaudeDo.Data.Repositories;
|
||||
using ClaudeDo.Worker.Hub;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace ClaudeDo.Worker.External;
|
||||
|
||||
public sealed record AddNoteResult(bool Saved, string Id, int AgentNotesToday, int RemainingToday);
|
||||
|
||||
[McpServerToolType]
|
||||
public sealed class NotesMcpTools
|
||||
{
|
||||
/// <summary>Agent-written notes allowed per day. The user's own notes are never counted or
|
||||
/// capped — this only stops an agent from burying the day in its own bullets.</summary>
|
||||
public const int DailyAgentCap = 10;
|
||||
|
||||
/// <summary>A note is a line, not a report. Longer output belongs in a task or a finding.</summary>
|
||||
public const int MaxLength = 500;
|
||||
|
||||
private readonly DailyNoteRepository _notes;
|
||||
private readonly HubBroadcaster _broadcaster;
|
||||
|
||||
public NotesMcpTools(DailyNoteRepository notes, HubBroadcaster broadcaster)
|
||||
{
|
||||
_notes = notes;
|
||||
_broadcaster = broadcaster;
|
||||
}
|
||||
|
||||
[McpServerTool, Description(
|
||||
"Add one short line to the user's notes for today — the day log they read in ClaudeDo, " +
|
||||
"not a log for you. Write only what the user would want to reread later and would " +
|
||||
"otherwise lose: a decision they made, something you found that changes their plans, " +
|
||||
"something they asked you to remember for the day. Task status, your progress and " +
|
||||
"anything already visible in the task list are noise here. When unsure, don't. " +
|
||||
"Capped at 10 agent notes per day; you cannot edit or delete notes, so a bad one stays " +
|
||||
"until the user removes it.")]
|
||||
public async Task<AddNoteResult> AddNote(
|
||||
[Description("The note itself: one plain sentence, no markdown, no prefix, max 500 characters.")] string text,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var trimmed = (text ?? "").Trim();
|
||||
if (trimmed.Length == 0)
|
||||
throw new InvalidOperationException("text is required.");
|
||||
if (trimmed.Length > MaxLength)
|
||||
throw new InvalidOperationException(
|
||||
$"Note is {trimmed.Length} characters, the limit is {MaxLength}. Shorten it to one line.");
|
||||
|
||||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||
var used = await _notes.CountAgentNotesAsync(today, cancellationToken);
|
||||
if (used >= DailyAgentCap)
|
||||
throw new InvalidOperationException(
|
||||
$"Daily cap reached: {DailyAgentCap} agent notes already written today. " +
|
||||
"Nothing was saved — tell the user instead of retrying.");
|
||||
|
||||
var note = await _notes.AddAsync(today, trimmed, fromAgent: true, cancellationToken);
|
||||
await _broadcaster.NotesUpdated();
|
||||
return new AddNoteResult(true, note.Id, used + 1, DailyAgentCap - used - 1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user