Skip to content

Commit ec56369

Browse files
swhittmatthallett1justfinethanku
authored andcommitted
[extensions] Add update_professional_contact tool to CRM extension (NateBJones-Projects#133)
* Add update_professional_contact tool to CRM extension Adds the ability to update existing contact fields (name, company, title, email, phone, tags, notes, follow_up_date, etc.) which was proposed in NateBJones-Projects#93 but never implemented. Only provided fields are updated, and the existing updated_at trigger handles timestamping. * Allow clearing follow_up_date by passing null or empty string Fixes the case where a follow-up date, once set, could never be cleared — leaving contacts permanently stuck in get_follow_ups_due. * [extensions] Document contact update tool --------- Co-authored-by: Matt Hallett <matthallett@gmail.com> Co-authored-by: Jonathan Edwards <justfinethanku@gmail.com>
1 parent 9ae7830 commit ec56369

2 files changed

Lines changed: 62 additions & 3 deletions

File tree

extensions/professional-crm/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ If you build Extension 6, the `link_contact_to_professional_crm` tool works in r
160160
4. **`get_contact_history`** — Get a contact's full profile + all interactions ordered by date
161161
5. **`create_opportunity`** — Create an opportunity/deal linked to a contact (title, description, stage, value, expected_close_date)
162162
6. **`get_follow_ups_due`** — List contacts with follow_up_date in the past or next N days
163-
7. **`link_thought_to_contact`****CROSS-EXTENSION BRIDGE** — Takes a thought_id and contact_id, retrieves the thought from your core Open Brain, and links it to the contact record
163+
7. **`update_professional_contact`** — Update only the fields you provide on an existing contact, including setting or clearing `follow_up_date`
164+
8. **`link_thought_to_contact`****CROSS-EXTENSION BRIDGE** — Takes a thought_id and contact_id, retrieves the thought from your core Open Brain, and links it to the contact record
164165

165166
## Expected Outcome
166167

extensions/professional-crm/index.ts

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Extension 5: Professional CRM MCP Server (Remote Edge Function)
33
*
44
* Provides tools for managing professional contacts, interactions, and opportunities:
5-
* - Contact management with rich metadata
5+
* - Contact management with rich metadata (add, update, search)
66
* - Interaction logging with auto-updating last_contacted
77
* - Opportunity/pipeline tracking
88
* - Follow-up reminders
@@ -365,7 +365,65 @@ app.post("*", async (c) => {
365365
},
366366
);
367367

368-
// Tool 7: link_thought_to_contact (CROSS-EXTENSION BRIDGE)
368+
// Tool 7: update_professional_contact
369+
server.tool(
370+
"update_professional_contact",
371+
"Update an existing professional contact's details (name, title, company, email, etc.)",
372+
{
373+
contact_id: z.string().describe("Contact ID (UUID)"),
374+
name: z.string().optional().describe("Updated full name"),
375+
company: z.string().optional().describe("Updated company name"),
376+
title: z.string().optional().describe("Updated job title"),
377+
email: z.string().optional().describe("Updated email address"),
378+
phone: z.string().optional().describe("Updated phone number"),
379+
linkedin_url: z.string().optional().describe("Updated LinkedIn profile URL"),
380+
how_we_met: z.string().optional().describe("Updated context for how you met"),
381+
tags: z.array(z.string()).optional().describe("Replace tags (e.g., ['ai', 'consulting'])"),
382+
notes: z.string().optional().describe("Replace notes with new content"),
383+
follow_up_date: z.string().nullable().optional().describe("Set or update follow-up date (YYYY-MM-DD), or null/empty string to clear"),
384+
},
385+
async ({ contact_id, ...fields }) => {
386+
const updates: Record<string, unknown> = {};
387+
for (const [key, value] of Object.entries(fields)) {
388+
if (key === "follow_up_date" && (value === null || value === "")) {
389+
updates[key] = null;
390+
} else if (value !== undefined) {
391+
updates[key] = value;
392+
}
393+
}
394+
395+
if (Object.keys(updates).length === 0) {
396+
throw new Error("No fields provided to update");
397+
}
398+
399+
const { data, error } = await supabase
400+
.from("professional_contacts")
401+
.update(updates)
402+
.eq("id", contact_id)
403+
.eq("user_id", userId)
404+
.select()
405+
.single();
406+
407+
if (error) {
408+
throw new Error(`Failed to update contact: ${error.message}`);
409+
}
410+
411+
return {
412+
content: [
413+
{
414+
type: "text",
415+
text: JSON.stringify({
416+
success: true,
417+
message: `Updated contact: ${data.name}`,
418+
contact: data,
419+
}, null, 2),
420+
},
421+
],
422+
};
423+
},
424+
);
425+
426+
// Tool 8: link_thought_to_contact (CROSS-EXTENSION BRIDGE)
369427
server.tool(
370428
"link_thought_to_contact",
371429
"CROSS-EXTENSION: Link a thought from your core Open Brain to a professional contact",

0 commit comments

Comments
 (0)