From d07827697d99154c0ae13b55b2ac93b627195345 Mon Sep 17 00:00:00 2001 From: paarths-collab Date: Sat, 30 May 2026 14:15:17 +0530 Subject: [PATCH] feat(campaigns): add get_campaign_memory + update_campaign_memory tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proxy GET/PATCH /api/v1/campaigns/:id/memory so Max can recall and record durable per-campaign memory (ICP, decisions, notes) — keeps simultaneous campaigns separate. Brings the server to 91 tools. Co-Authored-By: Claude Opus 4.8 --- features/pilot-tools/campaigns/repository.ts | 16 ++++++++++++++++ features/pilot-tools/campaigns/schema.ts | 15 +++++++++++++++ features/pilot-tools/campaigns/tools.ts | 13 +++++++++++++ 3 files changed, 44 insertions(+) diff --git a/features/pilot-tools/campaigns/repository.ts b/features/pilot-tools/campaigns/repository.ts index d1c94f8..46a879f 100644 --- a/features/pilot-tools/campaigns/repository.ts +++ b/features/pilot-tools/campaigns/repository.ts @@ -13,6 +13,22 @@ export async function getCampaign(token: string, id: string): Promise return fetchWithRetry(apiUrl(`/api/v1/campaigns/${id}`), { headers: authHeaders(token) }); } +export async function getCampaignMemory(token: string, id: string): Promise { + return fetchWithRetry(apiUrl(`/api/v1/campaigns/${id}/memory`), { headers: authHeaders(token) }); +} + +export async function updateCampaignMemory( + token: string, + id: string, + patch: Record, +): Promise { + return fetchWithRetry(apiUrl(`/api/v1/campaigns/${id}/memory`), { + method: "PATCH", + headers: authHeaders(token), + body: JSON.stringify(patch), + }); +} + export async function createCampaign(token: string, body: Record): Promise { return fetchWithRetry(apiUrl(`/api/v1/campaigns`), { method: "POST", diff --git a/features/pilot-tools/campaigns/schema.ts b/features/pilot-tools/campaigns/schema.ts index 16ed75f..ee79860 100644 --- a/features/pilot-tools/campaigns/schema.ts +++ b/features/pilot-tools/campaigns/schema.ts @@ -16,6 +16,21 @@ export const getCampaignSchema = z.object({ id: z.string().uuid().describe("Campaign UUID"), }); +export const getCampaignMemorySchema = z.object({ + ...withToken, + id: z.string().uuid().describe("Campaign UUID"), +}); + +export const updateCampaignMemorySchema = z.object({ + ...withToken, + id: z.string().uuid().describe("Campaign UUID"), + memory: z + .record(z.unknown()) + .describe( + "Partial memory object to merge; top-level keys replace (send the full array to change decisions/notes). e.g. { summary, icp, decisions: [], notes: [] }", + ), +}); + export const createCampaignSchema = z.object({ ...withToken, name: z.string().min(1).max(255).describe("Campaign name"), diff --git a/features/pilot-tools/campaigns/tools.ts b/features/pilot-tools/campaigns/tools.ts index b0d5fc6..7f8bb39 100644 --- a/features/pilot-tools/campaigns/tools.ts +++ b/features/pilot-tools/campaigns/tools.ts @@ -16,6 +16,19 @@ export function registerCampaignTools(server: McpServer): void { inputSchema: S.getCampaignSchema, }, async (input) => callApi(input.bearer_token, (t) => repo.getCampaign(t, input.id))); + server.registerTool("get_campaign_memory", { + title: "Get campaign memory", + description: "Read Max's durable memory for a campaign (ICP, decisions, notes). Recall this when working on one of several simultaneous campaigns so you keep them straight.", + inputSchema: S.getCampaignMemorySchema, + }, async (input) => callApi(input.bearer_token, (t) => repo.getCampaignMemory(t, input.id))); + + server.registerTool("update_campaign_memory", { + title: "Update campaign memory", + description: "Record/update Max's durable memory for a campaign. Pass a partial 'memory' object (top-level keys merge; send the full array to change decisions/notes). Use it to remember ICP, decisions, and progress per campaign.", + inputSchema: S.updateCampaignMemorySchema, + }, async (input) => callApi(input.bearer_token, (t) => + repo.updateCampaignMemory(t, input.id, input.memory as Record))); + server.registerTool("create_campaign", { title: "Create campaign", description: "Create a new campaign in draft state. Requires name, included_lists, and accounts. Won't send until launched.",