job-tracker-student-mcp 0.1.0 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1
4
+
5
+ - Added optional `deadline_after` and `deadline_before` to `list_applications` to list applications due in a date range. Both dates are included.
6
+ - Added `delete_application` to remove a duplicate or mistaken application by id.
7
+ - Added `list_applications` to list applications with optional status and company filters to see ids without exporting a full report.
8
+ - Added an optional `query` to `list_applications` that searches role, notes and source text, for example referral or LinkedIn.
9
+ - Added `update_application` to change the deadline, notes, role or company of an existing application.
10
+
3
11
  ## 0.1.0
4
12
 
5
13
  - First version of the job application tracker with add_application, update_status, list_upcoming and export_report.
package/README.md CHANGED
@@ -16,7 +16,7 @@ npx -y job-tracker-student-mcp
16
16
  2. "Move application 1 to interview on 2026-11-20 at 14:30."
17
17
  3. "What deadlines and interviews do I have in the next two weeks?"
18
18
 
19
- A free, open source [MCP](https://modelcontextprotocol.io) server named `job-tracker-student-mcp` on npm. Track internships, jobs, interviews and deadlines, see what is coming up, and export your list as CSV or Markdown. Runs on your computer with Claude Desktop, Claude Code, Cursor, VS Code and Windsurf. No internet, no login, no API keys. Your applications are saved in one JSON file that you can open, back up or delete. Free forever.
19
+ A free, open source [MCP](https://modelcontextprotocol.io) server named `job-tracker-student-mcp` on npm. Track internships, jobs, interviews and deadlines, see what is coming up, and export your list as CSV or Markdown. Runs on your computer with Claude Desktop, Claude Code, Cursor, VS Code and Windsurf. No internet, no login, no API keys. Your applications are saved in one JSON file that you can open, back up or delete.
20
20
 
21
21
  ## Install
22
22
 
@@ -141,15 +141,21 @@ More things you can ask:
141
141
  - "Export all my applications as a CSV."
142
142
  - "Show only my offers as a Markdown table."
143
143
  - "Which deadlines have already passed?"
144
+ - "Delete application 3, it was a duplicate."
145
+ - "Which applications mention a referral?"
146
+ - "Which applications have a deadline in November 2026?"
144
147
 
145
148
  ## Tools
146
149
 
147
- There are four tools. `list_upcoming` and `export_report` are read only. `add_application` and `update_status` write to the local JSON file and nothing else.
150
+ There are seven tools. `list_applications`, `list_upcoming` and `export_report` are read only. `add_application`, `update_application`, `update_status` and `delete_application` write to the local JSON file and nothing else.
148
151
 
149
152
  | Tool | What it does | Parameters | Example prompt |
150
153
  | --- | --- | --- | --- |
151
154
  | `add_application` | Add a job or internship with company, role, deadline and source. | `company`, `role`, `deadline`, `source`, `type`, `status`, `notes` | "Add a Software Intern role at Shopify with a deadline of 2026-11-15, found on LinkedIn." |
152
155
  | `update_status` | Move an application between applied, interview, offer and rejected. | `id`, `status`, `interview_date`, `note` | "Move application 1 to interview on 2026-11-20 at 14:30." |
156
+ | `update_application` | Update the deadline, notes, role or company of an existing application. | `id`, `company`, `role`, `deadline`, `notes` | "Update application 1 to set the deadline to 2026-11-20 and add a note about the referral." |
157
+ | `delete_application` | Remove a duplicate or mistaken application for good. | `id` | "Delete application 3, it was a duplicate." |
158
+ | `list_applications` | List applications with optional status, company, text query and deadline range filters to see ids. The query searches role, notes and source. | `status`, `company`, `query`, `deadline_after`, `deadline_before` | "Which applications have a deadline in November 2026?" |
153
159
  | `list_upcoming` | Show upcoming deadlines and interviews sorted by date, with reminders. | `days`, `include_overdue`, `today` | "What deadlines and interviews do I have in the next two weeks?" |
154
160
  | `export_report` | Export applications as CSV or Markdown. | `format`, `status` | "Export all my applications as a CSV." |
155
161
 
@@ -173,7 +179,7 @@ Applications are saved in `~/.job-tracker-student-mcp/applications.json`. Set th
173
179
 
174
180
  ## Comparison
175
181
 
176
- This tracker is built for job applications. Some servers focus on course or learning management systems, and some are general task trackers.
182
+ This tracker is built for job applications. Some servers focus on course or learning management systems, and some are general task trackers.
177
183
 
178
184
  Compared with course focused servers, this one works offline with no login to a learning system and no keys. It is job specific, with statuses for applied, interview, offer and rejected, and it tracks deadlines and interview dates with reminders.
179
185
 
@@ -225,6 +231,26 @@ npm test
225
231
 
226
232
  The tests call every tool through a real MCP client and save to a temporary folder, so they need no account or network. They need the newer Node.js from the Node version section above, because of the test runner.
227
233
 
234
+ ## More free student MCP servers
235
+
236
+ All free, open source and read only where they touch your accounts. Browse the full list at [brightspacemcp.com/more-mcps](https://brightspacemcp.com/more-mcps/).
237
+
238
+ | Server | What it does |
239
+ | --- | --- |
240
+ | [Brightspace MCP](https://github.com/AnonymousOnyx22/brightspace-d2l-mcp) | D2L Brightspace for AI. Due dates with real submission status, grades, a target grade calculator and course files. |
241
+ | [Canvas MCP](https://github.com/AnonymousOnyx22/canvas-lms-student-mcp) | Canvas LMS for AI, read only. Due dates, missing work, grades and announcements. |
242
+ | [Moodle MCP](https://github.com/AnonymousOnyx22/moodle-student-mcp) | Moodle for AI, read only. Due dates, overdue work, grades and announcements. |
243
+ | [Google Classroom MCP](https://github.com/AnonymousOnyx22/google-classroom-student-mcp) | Google Classroom for AI, read only. Todos, due dates, missing work and grades. |
244
+ | [GPA Calculator MCP](https://github.com/AnonymousOnyx22/gpa-calculator-mcp) | GPA, grade conversion and the grade you need on the final, for AI. Works offline, no account. |
245
+ | [Citations MCP](https://github.com/AnonymousOnyx22/student-citations-mcp) | APA, MLA and Chicago citations from a DOI, an ISBN or typed details. No account. |
246
+ | [Study Planner MCP](https://github.com/AnonymousOnyx22/study-planner-mcp) | Exam study schedules, spaced repetition dates, pomodoro plans and assignment milestones. Works offline. |
247
+ | [Paper Finder MCP](https://github.com/AnonymousOnyx22/paper-finder-student-mcp) | Search scholarly papers, find open access copies and related work through OpenAlex. No key needed. |
248
+ | [BibTeX Reference Manager MCP](https://github.com/AnonymousOnyx22/bibtex-reference-manager-mcp) | Clean, convert, dedupe and lint BibTeX, RIS and CSL JSON reference lists, and make cite keys. Works offline. |
249
+ | [Open Textbook Finder MCP](https://github.com/AnonymousOnyx22/open-textbook-finder-mcp) | Find free legal copies of textbooks on OpenStax, DOAB and Open Library, look up editions by ISBN and suggest cheaper older ones. |
250
+ | [STEM Calculator MCP](https://github.com/AnonymousOnyx22/stem-calc-student-mcp) | Chemistry and physics homework math: molar mass, balancing equations, grams to moles, unit conversion and significant figures. Works offline. |
251
+ | [Essay Stats MCP](https://github.com/AnonymousOnyx22/essay-stats-mcp) | Offline writing metrics for essays. Word and page counts, readability scores, passive voice, repeated openers, filler words, long sentences and word limit checks, so students can check their work against assignment requirements. No account, no network. |
252
+ | [Syllabus to Calendar MCP](https://github.com/AnonymousOnyx22/syllabus-calendar-mcp) | Syllabus to calendar MCP server. Extract dates from syllabus text, build an ics calendar, expand recurring classes and merge calendars. Works offline, no account. |
253
+
228
254
  ## License
229
255
 
230
256
  MIT
@@ -2,7 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { registerTools } from "./core/tools.js";
3
3
  import { tools } from "./tools.js";
4
4
  import { Store } from "./tracker.js";
5
- export const VERSION = "0.1.0";
5
+ export const VERSION = "0.1.1";
6
6
  export function createServer(store = new Store()) {
7
7
  const server = new McpServer({ name: "job-tracker-student-mcp", version: VERSION });
8
8
  registerTools(server, tools, store);
@@ -71,7 +71,7 @@ export const tools = [
71
71
  title: "Update status",
72
72
  readOnly: false,
73
73
  description: "Move an application to another stage: applied, interview, offer or rejected. " +
74
- "Inputs: id of the application (from add_application or export_report), the new status, an optional interview_date as YYYY-MM-DD or YYYY-MM-DDTHH:MM, and an optional note that is added to the notes. " +
74
+ "Inputs: id of the application (from add_application or list_applications), the new status, an optional interview_date as YYYY-MM-DD or YYYY-MM-DDTHH:MM, and an optional note that is added to the notes. " +
75
75
  "When the status is interview, pass interview_date so list_upcoming can remind you. " +
76
76
  'Example prompt: "Move application 1 to interview on 2026-11-20 at 14:30."',
77
77
  inputSchema: {
@@ -88,7 +88,7 @@ export const tools = [
88
88
  const apps = store.list();
89
89
  const app = apps.find((a) => a.id === args.id);
90
90
  if (!app)
91
- throw new Error(`There is no application with id ${args.id}. Use export_report to see the ids.`);
91
+ throw new Error(`There is no application with id ${args.id}. Use list_applications to see the ids.`);
92
92
  const when = args.interview_date ? checkWhen(args.interview_date.trim()) : null;
93
93
  const previous = app.status;
94
94
  app.status = args.status;
@@ -104,6 +104,109 @@ export const tools = [
104
104
  return { text: `${brief(app)} ${moved}${at}.`, structured: { previous_status: previous, application: app } };
105
105
  },
106
106
  },
107
+ {
108
+ name: "update_application",
109
+ title: "Update application",
110
+ readOnly: false,
111
+ description: "Update the deadline, notes, role or company of an existing application. " +
112
+ "Inputs: id of the application (from add_application or list_applications), and any of company, role, deadline as YYYY-MM-DD and notes. Only the fields you pass are changed; the rest stay the same. " +
113
+ "Pass an empty deadline to clear it. " +
114
+ 'Example prompt: "Update application 1 to set the deadline to 2026-11-20 and add a note about the referral."',
115
+ inputSchema: {
116
+ id: z.number().int().positive().describe("The id of the application."),
117
+ company: z.string().min(1).optional().describe("New company name."),
118
+ role: z.string().min(1).optional().describe("New role title."),
119
+ deadline: z.string().optional().describe("New deadline as YYYY-MM-DD, or an empty string to clear it."),
120
+ notes: z.string().optional().describe("New notes, replacing the old ones."),
121
+ },
122
+ outputSchema: { application: appFields.describe("The application after the change.") },
123
+ run: async (args, store) => {
124
+ const apps = store.list();
125
+ const app = apps.find((a) => a.id === args.id);
126
+ if (!app)
127
+ throw new Error(`There is no application with id ${args.id}. Use list_applications to see the ids.`);
128
+ if (args.company !== undefined)
129
+ app.company = args.company.trim();
130
+ if (args.role !== undefined)
131
+ app.role = args.role.trim();
132
+ if (args.deadline !== undefined) {
133
+ const d = args.deadline.trim();
134
+ app.deadline = d ? checkDeadline(d) : null;
135
+ }
136
+ if (args.notes !== undefined)
137
+ app.notes = clean(args.notes);
138
+ app.updated = todayOr();
139
+ store.save(apps);
140
+ return { text: `Updated ${brief(app)}.`, structured: { application: app } };
141
+ },
142
+ },
143
+ {
144
+ name: "delete_application",
145
+ title: "Delete application",
146
+ readOnly: false,
147
+ description: "Remove an application from the tracker, for example a duplicate or a mistaken entry. " +
148
+ "Inputs: id of the application (from add_application or list_applications). The application is deleted from the local file and cannot be brought back, and the ids of the other applications do not change. " +
149
+ 'Example prompt: "Delete application 3, it was a duplicate."',
150
+ inputSchema: {
151
+ id: z.number().int().positive().describe("The id of the application to delete."),
152
+ },
153
+ outputSchema: { deleted: appFields.describe("The application that was removed.") },
154
+ run: async (args, store) => {
155
+ const apps = store.list();
156
+ const app = apps.find((a) => a.id === args.id);
157
+ if (!app)
158
+ throw new Error(`There is no application with id ${args.id}. Use list_applications to see the ids.`);
159
+ store.save(apps.filter((a) => a.id !== args.id));
160
+ return { text: `Deleted ${brief(app)}.`, structured: { deleted: app } };
161
+ },
162
+ },
163
+ {
164
+ name: "list_applications",
165
+ title: "List applications",
166
+ readOnly: true,
167
+ description: "List applications with optional status and company filters, an optional text query and an optional deadline range. " +
168
+ "Inputs: optional status to filter by stage, an optional company substring to match (case insensitive), an optional query that searches the role, notes and source text (case insensitive), for example referral or LinkedIn, and optional deadline_after and deadline_before as YYYY-MM-DD. Both dates are included, and applications without a deadline are left out when you pass either one. " +
169
+ "Returns all matching applications as a structured list with their ids, so you can see application ids without exporting a full report. " +
170
+ 'Example prompt: "Which applications have a deadline in November 2026?"',
171
+ inputSchema: {
172
+ status: z.enum(STATUSES).optional().describe("Only list applications in this stage."),
173
+ company: z.string().optional().describe("Only list applications whose company name contains this text (case insensitive)."),
174
+ query: z.string().optional().describe("Only list applications whose role, notes or source contains this text (case insensitive)."),
175
+ deadline_after: z.string().optional().describe("Only list applications with a deadline on or after this YYYY-MM-DD date."),
176
+ deadline_before: z.string().optional().describe("Only list applications with a deadline on or before this YYYY-MM-DD date."),
177
+ },
178
+ outputSchema: {
179
+ count: z.number().describe("How many applications match."),
180
+ applications: z.array(appFields).describe("The matching applications."),
181
+ },
182
+ run: async (args, store) => {
183
+ const after = args.deadline_after?.trim() ? checkDeadline(args.deadline_after.trim()) : null;
184
+ const before = args.deadline_before?.trim() ? checkDeadline(args.deadline_before.trim()) : null;
185
+ const all = store.list();
186
+ let apps = all;
187
+ if (args.status)
188
+ apps = apps.filter((a) => a.status === args.status);
189
+ if (args.company && args.company.trim()) {
190
+ const needle = args.company.trim().toLowerCase();
191
+ apps = apps.filter((a) => a.company.toLowerCase().includes(needle));
192
+ }
193
+ const query = args.query ? args.query.trim().toLowerCase() : "";
194
+ if (query) {
195
+ apps = apps.filter((a) => [a.role, a.notes, a.source].some((f) => (f ?? "").toLowerCase().includes(query)));
196
+ }
197
+ if (after)
198
+ apps = apps.filter((a) => a.deadline !== null && a.deadline >= after);
199
+ if (before)
200
+ apps = apps.filter((a) => a.deadline !== null && a.deadline <= before);
201
+ apps = [...apps].sort((a, b) => a.id - b.id);
202
+ const label = args.status ? ` in the ${args.status} stage` : "";
203
+ const comp = (args.company && args.company.trim() ? ` for company matching "${args.company.trim()}"` : "") + (query ? ` with "${args.query.trim()}" in the role, notes or source` : "") + (after ? ` with a deadline on or after ${after}` : "") + (before ? ` with a deadline on or before ${before}` : "");
204
+ return {
205
+ text: `Found ${apps.length} ${apps.length === 1 ? "application" : "applications"}${label}${comp}.\n\n${apps.map((a) => `#${a.id} ${a.role} at ${a.company} [${a.status}]`).join("\n") || "None."}`,
206
+ structured: { count: apps.length, applications: apps },
207
+ };
208
+ },
209
+ },
107
210
  {
108
211
  name: "list_upcoming",
109
212
  title: "List upcoming",
@@ -62,7 +62,7 @@ export function checkWhen(text) {
62
62
  export function todayOr(text) {
63
63
  if (text !== undefined) {
64
64
  if (!isDate(text))
65
- throw new Error(`The date "${text}" is not a real date. Use YYYY-MM-DD.`);
65
+ throw new Error(`The date "${text}" is not a real date. Use YYYY-MM-DD, for example 2026-11-15.`);
66
66
  return text;
67
67
  }
68
68
  const now = new Date();
package/llms.txt CHANGED
@@ -1,12 +1,12 @@
1
1
  # job-tracker-student-mcp
2
2
 
3
- > Job application tracker MCP server. Track internships, jobs, interviews and deadlines offline with reminders and status updates. Works offline, no account. Free and open source, works with Claude Desktop, Claude Code, Cursor, VS Code and Windsurf.
3
+ > Job application tracker MCP server. Track internships, jobs, interviews and deadlines offline, with status updates and a list of what is coming up. No account. Free and open source, works with Claude Desktop, Claude Code, Cursor, VS Code and Windsurf.
4
4
 
5
5
  ## Setup
6
6
  - Run with npx -y job-tracker-student-mcp
7
7
  - Works offline, no account. No API keys, no passwords and no network calls.
8
8
  - Applications are saved in one JSON file at ~/.job-tracker-student-mcp/applications.json. Set the JOB_TRACKER_FILE environment variable to use another path.
9
- - add_application and update_status write to that file. list_upcoming and export_report only read it.
9
+ - add_application, update_status, update_application and delete_application write to that file. list_applications, list_upcoming and export_report only read it.
10
10
 
11
11
  ## Stages and dates
12
12
  - An application is in one of four stages: applied, interview, offer or rejected.
@@ -30,7 +30,7 @@ Output: One sentence naming the new application, its id, type, status and deadli
30
30
  Structured output: application with id, company, role, type, status, deadline, interview_date, source, notes, added and updated.
31
31
 
32
32
  ### update_status
33
- Move an application to another stage: applied, interview, offer or rejected. Inputs: id of the application (from add_application or export_report), the new status, an optional interview_date as YYYY-MM-DD or YYYY-MM-DDTHH:MM, and an optional note that is added to the notes. When the status is interview, pass interview_date so list_upcoming can remind you. Example prompt: "Move application 1 to interview on 2026-11-20 at 14:30."
33
+ Move an application to another stage: applied, interview, offer or rejected. Inputs: id of the application (from add_application or list_applications), the new status, an optional interview_date as YYYY-MM-DD or YYYY-MM-DDTHH:MM, and an optional note that is added to the notes. When the status is interview, pass interview_date so list_upcoming can remind you. Example prompt: "Move application 1 to interview on 2026-11-20 at 14:30."
34
34
  Parameters: id, status, interview_date, note.
35
35
  - id: The id of the application. Required.
36
36
  - status: The stage: "applied", "interview", "offer" or "rejected". Required.
@@ -40,6 +40,38 @@ Example call: `{"name":"update_status","arguments":{"id":1,"status":"interview",
40
40
  Output: One sentence saying the application moved from one stage to another, with the interview date when there is one.
41
41
  Structured output: previous_status, and application with id, company, role, type, status, deadline, interview_date, source, notes, added and updated.
42
42
 
43
+ ### update_application
44
+ Update the deadline, notes, role or company of an existing application. Inputs: id of the application (from add_application or list_applications), and any of company, role, deadline as YYYY-MM-DD and notes. Only the fields you pass are changed; the rest stay the same. Pass an empty deadline to clear it. Example prompt: "Update application 1 to set the deadline to 2026-11-20 and add a note about the referral."
45
+ Parameters: id, company, role, deadline, notes.
46
+ - id: The id of the application. Required.
47
+ - company: New company name.
48
+ - role: New role title.
49
+ - deadline: New deadline as YYYY-MM-DD, or an empty string to clear it.
50
+ - notes: New notes, replacing the old ones.
51
+ Example call: `{"name":"update_application","arguments":{"id":1,"deadline":"2026-11-20","notes":"Referred by a friend"}}`
52
+ Output: One sentence naming the updated application.
53
+ Structured output: application with id, company, role, type, status, deadline, interview_date, source, notes, added and updated.
54
+
55
+ ### delete_application
56
+ Remove an application from the tracker, for example a duplicate or a mistaken entry. Inputs: id of the application (from add_application or list_applications). The application is deleted from the local file and cannot be brought back, and the ids of the other applications do not change. Example prompt: "Delete application 3, it was a duplicate."
57
+ Parameters: id.
58
+ - id: The id of the application to delete. Required.
59
+ Example call: `{"name":"delete_application","arguments":{"id":3}}`
60
+ Output: One sentence naming the deleted application.
61
+ Structured output: deleted with id, company, role, type, status, deadline, interview_date, source, notes, added and updated.
62
+
63
+ ### list_applications
64
+ List applications with optional status and company filters, an optional text query and an optional deadline range. Inputs: optional status to filter by stage, an optional company substring to match (case insensitive), an optional query that searches the role, notes and source text (case insensitive), for example referral or LinkedIn, and optional deadline_after and deadline_before as YYYY-MM-DD. Both dates are included, and applications without a deadline are left out when you pass either one. Returns all matching applications as a structured list with their ids, so you can see application ids without exporting a full report. Example prompt: "Which applications have a deadline in November 2026?"
65
+ Parameters: status, company, query, deadline_after, deadline_before.
66
+ - status: Only list applications in this stage.
67
+ - company: Only list applications whose company name contains this text (case insensitive).
68
+ - query: Only list applications whose role, notes or source contains this text (case insensitive).
69
+ - deadline_after: Only list applications with a deadline on or after this YYYY-MM-DD date.
70
+ - deadline_before: Only list applications with a deadline on or before this YYYY-MM-DD date.
71
+ Example call: `{"name":"list_applications","arguments":{}}`
72
+ Output: A sentence with the number of applications found, then a short list of id, role and company with status.
73
+ Structured output: count and applications with id, company, role, type, status, deadline, interview_date, source, notes, added and updated.
74
+
43
75
  ### list_upcoming
44
76
  Show upcoming deadlines and interviews sorted by date, with a reminder for each one. Deadlines come from applications still in the applied stage, and interviews come from applications in the interview stage that have an interview_date. Rejected applications and offers are left out. Inputs: optional days, how far ahead to look (default 14), include_overdue (default true) to also show deadlines that already passed, and an optional today as YYYY-MM-DD to ask as if it were another day. Example prompt: "What deadlines and interviews do I have in the next two weeks?"
45
77
  Parameters: days, include_overdue, today.
@@ -59,6 +91,13 @@ Example call: `{"name":"export_report","arguments":{"format":"csv"}}`
59
91
  Output: A sentence with the number of applications exported, then the report.
60
92
  Structured output: format, count and content.
61
93
 
94
+ ## More things you can ask
95
+ - "Export all my applications as a CSV."
96
+ - "Which deadlines have already passed?"
97
+ - "Delete application 3, it was a duplicate."
98
+ - "Which applications mention a referral?"
99
+ - "Which applications have a deadline in November 2026?"
100
+
62
101
  ## FAQ
63
102
 
64
103
  ### Does it send reminders on its own?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job-tracker-student-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Job application tracker MCP server that works offline. Track internships, jobs, interviews and deadlines, see what is coming up, and export to CSV or Markdown. No account and no API keys.",
5
5
  "type": "module",
6
6
  "main": "build/src/index.js",
@@ -42,7 +42,6 @@
42
42
  "job search",
43
43
  "job hunt",
44
44
  "interview tracker",
45
- "interview scheduler",
46
45
  "deadline reminders",
47
46
  "application deadlines",
48
47
  "application status",