From 632b0029be9b81975fd4bd01bcaa4347ec6beab4 Mon Sep 17 00:00:00 2001 From: KS Jannette Date: Tue, 11 Aug 2026 18:52:28 -0400 Subject: [PATCH] update tools and prompts to add functionality for 'starring' emails --- package-lock.json | 10 ++--- package.json | 2 +- src/prompts/classify-emails.txt | 17 ++++---- src/prompts/take-action-on-emails.txt | 11 ++---- src/tools/tools-email.ts | 57 ++++++++++++++++++++++++++- 5 files changed, 73 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index 03b5cdb..c90b3c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@modelcontextprotocol/sdk": "^1.26.0", + "@modelcontextprotocol/sdk": "^1.30.0", "googleapis": "^171.4.0", "zod": "^3.25.76" }, @@ -501,12 +501,12 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", - "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", + "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", "license": "MIT", "dependencies": { - "@hono/node-server": "^1.19.9", + "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", diff --git a/package.json b/package.json index b51fbdf..a5d1784 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "author": "", "license": "ISC", "dependencies": { - "@modelcontextprotocol/sdk": "^1.26.0", + "@modelcontextprotocol/sdk": "^1.30.0", "googleapis": "^171.4.0", "zod": "^3.25.76" }, diff --git a/src/prompts/classify-emails.txt b/src/prompts/classify-emails.txt index b34c0a1..86fb385 100644 --- a/src/prompts/classify-emails.txt +++ b/src/prompts/classify-emails.txt @@ -1,6 +1,4 @@ -You are an expert Executive Assistant AI specialized in email management and productivity. Your goal is to organize, prioritize, and summarize incoming emails to maximize user efficiency. - -I have been applying for jobs, which generates a large volume of responses. +You are an expert Executive Assistant AI specialized in email management and productivity. Your goal is to organize, prioritize, and summarize incoming emails to maximize user efficiency. I have been applying for jobs, which generates a large volume of responses. Review the first 200 emails in my inbox. Apply the following instructions to those emails. @@ -8,7 +6,6 @@ STEP 1: Constraints - Do not make up information. STEP 2: CLASSIFY each email into exactly one category. - Category A - Acknowledgement Only The employer confirms receipt of my application but requires no action from me. Typical language: "We received your application," "Your application is under review," "We will contact you if selected." No next steps are requested. @@ -21,15 +18,15 @@ The employer declines to move forward. Typical language: "We have decided to pur Category D - Other The email does not fit into categories A, B, or C. This includes non-job-application emails, newsletters, promotional content, or ambiguous messages that do not clearly match another category. -STEP 3: DELETE emails classified as Category A and Category C. +STEP 3: STAR REVIEWED EMAILS +- For EVERY single email processed and reviewed in this run (Categories A, B, C, and D), apply the Gmail "STARRED" system label or use your email tool to add a star to the message. -STEP 4: ("Anti step") - NEVER DELETE Category B emails. +STEP 4: Confirm, via natural language output in the chat window, the total number of emails fetched for review and successfully starred. -STEP 5: Confirm, via natural language output in the chat window, the total number of emails fetched for review. - -STEP 6: SUMMARIZE emails classified as Category B and Category D. For each, state: +STEP 5: SUMMARIZE emails classified as Category B and Category D. +For each, state: 1. Sender name and email address 2. Email subject line 3. Date and time received 4. Category (B or D) -5. Suggested action I should take \ No newline at end of file +5. Suggested action I should take diff --git a/src/prompts/take-action-on-emails.txt b/src/prompts/take-action-on-emails.txt index b968fcd..9fceac6 100644 --- a/src/prompts/take-action-on-emails.txt +++ b/src/prompts/take-action-on-emails.txt @@ -1,12 +1,9 @@ PHASE 2: TAKE ACTION ON CATEGORY B EMAILS - After completing classification (Phase 1), perform the following actions for each email classified as Category B (Advancement to Next Step). STEP 1: LOG TO SPREADSHEET For each Category B email, extract as much of the following information as possible from the email contents and log it using the log_recruiter_contact tool: - A column exists for: - - Recruiter Name - Recruiter Email - Recruiter Phone/Tel @@ -15,7 +12,7 @@ A column exists for: - Subsequent Contacts: any follow-up communication with this recruiter about this role - Recruiter Call Scheduled: if the email contains scheduling information, extract ALL details — date, time, platform (Zoom, MS Teams, Google Meet, etc.), meeting link, phone number to call — and note whether they confirm having your cell number or might not have it -A row should already exists for the same recruiter email + company/role combination (from a previous email), the tool will UPDATE the existing row. Provide all available fields and the tool handles merging. +If a row already exists for the same recruiter email + company/role combination (from a previous email), the tool will UPDATE the existing row. Provide all available fields and the tool handles merging. STEP 2: CREATE CALENDAR EVENTS Immediately after logging to the spreadsheet, check whether ANY of these fields were populated or updated: @@ -34,11 +31,11 @@ STEP 3: COMPANY INTERVIEW TRACKING When an email indicates advancement beyond the recruiter screen to a company interview: - Populate the Company First Interview or Company Second Interview field with details - Those details should include company contact info (interviewer name, email, phone) -- Then immeidately create a calendar event for the new interview date/time including interviewer name, email, phone +- Then immediately create a calendar event for the new interview date/time including interviewer name, email, phone STRICT CONSTRAINTS: -- Do not make up phone numbers, links, dates, or anything at all. If it does not exist in text wihtin a verifiable email, IT DOES NOT EXIST. -- PAST-DATE CHECK: Before creating a calendar event. 1) verify today's date from an external, reliable source; 2) compare the proposed meeting date/time against today's date; 3) iIf the meeting date has already passed, do NOT create a calendar event for it. Still log it to the spreadsheet, but skip the calendar step. The tool will also enforce this server-side. +- Do not make up phone numbers, links, dates, or anything at all. If it does not exist in text within a verifiable email, IT DOES NOT EXIST. +- PAST-DATE CHECK: Before creating a calendar event: 1) verify today's date from an external, reliable source; 2) compare the proposed meeting date/time against today's date; 3) if the meeting date has already passed, do NOT create a calendar event for it. Still log it to the spreadsheet, but skip the calendar step. The tool will also enforce this server-side. - As described in more detail below, each new date/time value in a scheduling column = one new calendar event. Never skip this. - Always use the same account for calendar events as was used for email fetching. - Do not create duplicate calendar events for the same meeting (same company representative && same job description | same company && same date time) diff --git a/src/tools/tools-email.ts b/src/tools/tools-email.ts index 90cef7f..b8ba0e2 100644 --- a/src/tools/tools-email.ts +++ b/src/tools/tools-email.ts @@ -56,7 +56,7 @@ server.registerTool( description: "Fetch unread emails from a Gmail inbox. Returns sender, date, " + "subject, message ID, and body text for each message. The message IDs " + - "can be passed to delete_emails later. Specify which account to fetch from.", + "must be passed to star_emails after evaluation. Specify which account to fetch from.", inputSchema: { account: accountSchema, maxResults: z @@ -303,3 +303,58 @@ server.registerTool( } } ); +// --------------------------------------------------------------------------- // +// Tool: star_emails +// --------------------------------------------------------------------------- // +server.registerTool( + "star_emails", + { + description: "Mark reviewed emails with a star and mark them as read in Gmail. " + + "Use this for all reviewed messages (Categories A, B, C, and D) " + + "to signal they have been processed. Specify which account the emails belong to.", + inputSchema: { + account: accountSchema, + messageIds: z + .array(z.string()) + .describe("Array of Gmail message IDs to star and mark read"), + }, + }, + async ({ account, messageIds }) => { + try { + if (messageIds.length === 0) { + return { content: [{ type: "text" as const, text: "No message IDs provided." }] }; + } + + const gmail = getGmailClient(account); + + // Batch modify allows updating up to 1000 messages in a single API call + await gmail.users.messages.batchModify({ + userId: "me", + requestBody: { + ids: messageIds, + addLabelIds: ["STARRED"], + removeLabelIds: ["UNREAD"] + } + }); + + return { + content: [ + { + type: "text" as const, + text: `Successfully starred and marked read ${messageIds.length} email(s).`, + }, + ], + }; + } catch (error) { + const errMsg = error instanceof Error ? error.message : String(error); + return { + content: [ + { + type: "text" as const, + text: `Error starring emails: ${errMsg}`, + }, + ], + }; + } + } +);