jobseek-mcp 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -114,6 +114,23 @@ Upload a PDF resume to JobSeek.
114
114
  ### `analyze_matches`
115
115
  Review your recent job matches from the Chrome extension.
116
116
 
117
+ ### `my_applications`
118
+ Get a list of your job applications with their current status, match scores, and status history with dates.
119
+
120
+ **Example:** "Show me my recent applications"
121
+
122
+ ### `update_application_status`
123
+ Update the status of a job application. Perfect for tracking application progress from email updates (Gmail parsing).
124
+
125
+ **Parameters:**
126
+ - `company` (required) - Company name to find the application
127
+ - `jobTitle` (optional) - Job title to match (useful if you applied to multiple roles at same company)
128
+ - `status` (required) - One of: `received`, `interviewing`, `rejected`, `offer`, `accepted`, `declined`
129
+ - `date` (optional) - Date of the status change (YYYY-MM-DD format, defaults to today)
130
+ - `notes` (optional) - Additional context (e.g., "Interview with Sarah scheduled")
131
+
132
+ **Example:** "Update my Acme Corp application to interviewing status - interview scheduled for January 15th with the hiring manager"
133
+
117
134
  ## Automated Job Search (with Claude for Chrome)
118
135
 
119
136
  The most powerful feature! Use the `/auto_job_search` prompt to have Claude:
package/dist/server.js CHANGED
@@ -9,6 +9,7 @@ import { getResumeLinkTool, handleGetResumeLink } from "./tools/get-resume-link.
9
9
  import { myApplicationsTool, handleMyApplications } from "./tools/my-applications.js";
10
10
  import { getFormDataTool, handleGetFormData } from "./tools/get-form-data.js";
11
11
  import { evaluateJobTool, handleEvaluateJob } from "./tools/evaluate-job.js";
12
+ import { updateApplicationStatusTool, handleUpdateApplicationStatus } from "./tools/update-application-status.js";
12
13
  import { allResources, handleReadResource } from "./resources.js";
13
14
  import { allPrompts, getPromptMessages } from "./prompts.js";
14
15
  export function createServer(apiKey) {
@@ -39,6 +40,7 @@ export function createServer(apiKey) {
39
40
  myApplicationsTool,
40
41
  getFormDataTool,
41
42
  evaluateJobTool,
43
+ updateApplicationStatusTool,
42
44
  ],
43
45
  };
44
46
  });
@@ -64,6 +66,8 @@ export function createServer(apiKey) {
64
66
  return handleGetFormData(args, JOBSEEK_API_URL, apiKey);
65
67
  case "evaluate_job":
66
68
  return handleEvaluateJob(args, JOBSEEK_API_URL, apiKey);
69
+ case "update_application_status":
70
+ return handleUpdateApplicationStatus(args, JOBSEEK_API_URL, apiKey);
67
71
  default:
68
72
  throw new Error(`Unknown tool: ${name}`);
69
73
  }
@@ -97,19 +97,33 @@ export async function handleMyApplications(args, apiUrl, apiKey) {
97
97
  };
98
98
  for (const app of applications) {
99
99
  const emoji = statusEmoji[app.status.toLowerCase()] || '📄';
100
- const date = new Date(app.createdAt).toLocaleDateString('en-US', {
100
+ const createdDate = new Date(app.createdAt).toLocaleDateString('en-US', {
101
101
  month: 'short',
102
102
  day: 'numeric',
103
103
  year: 'numeric'
104
104
  });
105
+ // Get most recent status date from history
106
+ const latestHistory = app.statusHistory && app.statusHistory.length > 0
107
+ ? app.statusHistory[app.statusHistory.length - 1]
108
+ : null;
109
+ const statusDate = latestHistory?.date
110
+ ? new Date(latestHistory.date).toLocaleDateString('en-US', {
111
+ month: 'short',
112
+ day: 'numeric',
113
+ year: 'numeric'
114
+ })
115
+ : createdDate;
105
116
  output += `## ${emoji} ${app.job.title}\n`;
106
117
  output += `**${app.job.company}**`;
107
118
  if (app.job.location)
108
119
  output += ` • ${app.job.location}`;
109
120
  output += '\n';
110
- output += `- Status: **${app.status}**\n`;
121
+ output += `- Status: **${app.status}** (${statusDate})\n`;
122
+ if (latestHistory?.notes) {
123
+ output += `- Notes: ${latestHistory.notes}\n`;
124
+ }
111
125
  output += `- Match Score: ${app.job.matchScore}%\n`;
112
- output += `- Applied: ${date}\n`;
126
+ output += `- Applied: ${createdDate}\n`;
113
127
  output += `- Has Cover Letter: ${app.coverLetter ? 'Yes' : 'No'}\n`;
114
128
  output += `- Has Custom Resume: ${app.customResume ? 'Yes' : 'No'}\n`;
115
129
  output += '\n';
@@ -0,0 +1,37 @@
1
+ export declare const updateApplicationStatusTool: {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: string;
6
+ properties: {
7
+ company: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ jobTitle: {
12
+ type: string;
13
+ description: string;
14
+ };
15
+ status: {
16
+ type: string;
17
+ enum: readonly ["received", "interviewing", "rejected", "offer", "accepted", "declined"];
18
+ description: string;
19
+ };
20
+ date: {
21
+ type: string;
22
+ description: string;
23
+ };
24
+ notes: {
25
+ type: string;
26
+ description: string;
27
+ };
28
+ };
29
+ required: string[];
30
+ };
31
+ };
32
+ export declare function handleUpdateApplicationStatus(args: unknown, apiUrl: string, apiKey?: string): Promise<{
33
+ content: Array<{
34
+ type: string;
35
+ text: string;
36
+ }>;
37
+ }>;
@@ -0,0 +1,131 @@
1
+ import { z } from "zod";
2
+ // Valid status values
3
+ const VALID_STATUSES = ['received', 'interviewing', 'rejected', 'offer', 'accepted', 'declined'];
4
+ // Tool definition
5
+ export const updateApplicationStatusTool = {
6
+ name: "update_application_status",
7
+ description: `Update the status of a job application. Use this after parsing Gmail for job application emails to track interview requests, rejections, offers, etc.
8
+
9
+ Statuses:
10
+ - received: Application was received/acknowledged
11
+ - interviewing: Interview scheduled or in progress
12
+ - rejected: Application was rejected
13
+ - offer: Received a job offer
14
+ - accepted: Accepted an offer
15
+ - declined: Declined an offer
16
+
17
+ The tool finds the application by company name (and optionally job title).`,
18
+ inputSchema: {
19
+ type: "object",
20
+ properties: {
21
+ company: {
22
+ type: "string",
23
+ description: "The company name (required). Used to find the matching application."
24
+ },
25
+ jobTitle: {
26
+ type: "string",
27
+ description: "The job title (optional). Helps find the correct application if user applied to multiple positions at the same company."
28
+ },
29
+ status: {
30
+ type: "string",
31
+ enum: VALID_STATUSES,
32
+ description: "The new status: received, interviewing, rejected, offer, accepted, or declined"
33
+ },
34
+ date: {
35
+ type: "string",
36
+ description: "The date of the status change (YYYY-MM-DD format). Defaults to today if not provided."
37
+ },
38
+ notes: {
39
+ type: "string",
40
+ description: "Optional notes about this status update (e.g., 'Interview scheduled with John Smith', 'Received confirmation email')"
41
+ }
42
+ },
43
+ required: ["company", "status"]
44
+ }
45
+ };
46
+ // Input validation
47
+ const UpdateApplicationStatusInput = z.object({
48
+ company: z.string(),
49
+ jobTitle: z.string().optional(),
50
+ status: z.enum(VALID_STATUSES),
51
+ date: z.string().optional(),
52
+ notes: z.string().optional(),
53
+ });
54
+ // Tool handler
55
+ export async function handleUpdateApplicationStatus(args, apiUrl, apiKey) {
56
+ const input = UpdateApplicationStatusInput.parse(args);
57
+ if (!apiKey) {
58
+ return {
59
+ content: [{
60
+ type: "text",
61
+ text: `**API Key Required**\n\nGet your API key at: ${apiUrl}/dashboard/api-keys`
62
+ }]
63
+ };
64
+ }
65
+ try {
66
+ const response = await fetch(`${apiUrl}/api/applications/update-status`, {
67
+ method: 'PATCH',
68
+ headers: {
69
+ "Authorization": `Bearer ${apiKey}`,
70
+ "Content-Type": "application/json",
71
+ },
72
+ body: JSON.stringify({
73
+ company: input.company,
74
+ jobTitle: input.jobTitle,
75
+ status: input.status,
76
+ date: input.date,
77
+ notes: input.notes
78
+ })
79
+ });
80
+ if (!response.ok) {
81
+ if (response.status === 401) {
82
+ return {
83
+ content: [{
84
+ type: "text",
85
+ text: `**Invalid API Key**\n\nGenerate a new key at: ${apiUrl}/dashboard/api-keys`
86
+ }]
87
+ };
88
+ }
89
+ if (response.status === 404) {
90
+ return {
91
+ content: [{
92
+ type: "text",
93
+ text: `**Application Not Found**\n\nNo application found for company "${input.company}"${input.jobTitle ? ` with title "${input.jobTitle}"` : ''}.\n\nMake sure the application exists in JobSeek first.`
94
+ }]
95
+ };
96
+ }
97
+ const error = await response.json().catch(() => ({}));
98
+ return {
99
+ content: [{
100
+ type: "text",
101
+ text: `**Error**\n\n${error.error || 'Failed to update application status'}`
102
+ }]
103
+ };
104
+ }
105
+ const result = await response.json();
106
+ // Format status emoji
107
+ const statusEmoji = {
108
+ 'received': '📬',
109
+ 'interviewing': '🎯',
110
+ 'rejected': '❌',
111
+ 'offer': '🎉',
112
+ 'accepted': '✅',
113
+ 'declined': '🚫',
114
+ };
115
+ const emoji = statusEmoji[input.status] || '📋';
116
+ return {
117
+ content: [{
118
+ type: "text",
119
+ text: `${emoji} **Status Updated**\n\n**${result.job.title}** at **${result.job.company}**\n\nStatus: **${input.status}**${input.date ? `\nDate: ${input.date}` : ''}${input.notes ? `\nNotes: ${input.notes}` : ''}\n\n---\nView application: ${apiUrl}/dashboard/applications`
120
+ }]
121
+ };
122
+ }
123
+ catch (error) {
124
+ return {
125
+ content: [{
126
+ type: "text",
127
+ text: `**Error**\n\nFailed to update application status: ${error.message}`
128
+ }]
129
+ };
130
+ }
131
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jobseek-mcp",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "JobSeek MCP Server - AI-powered job search automation for Claude. Enables automated job searching with Claude for Chrome browser control.",
5
5
  "author": "Shawn Mitchell",
6
6
  "license": "MIT",