jobseek-mcp 0.3.0 → 0.4.0

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/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSche
5
5
  import { optimizeResumeTool, handleOptimizeResume } from "./tools/optimize-resume.js";
6
6
  import { downloadResumePdfTool, handleDownloadResumePdf } from "./tools/download-resume-pdf.js";
7
7
  import { uploadResumeTool, handleUploadResume } from "./tools/upload-resume.js";
8
+ import { launchPadTool, handleLaunchPad } from "./tools/launch-pad.js";
8
9
  import { allResources, handleReadResource } from "./resources.js";
9
10
  // Configuration
10
11
  const JOBSEEK_API_URL = process.env.JOBSEEK_API_URL || "https://jobseek-iota.vercel.app";
@@ -12,7 +13,7 @@ const JOBSEEK_API_KEY = process.env.JOBSEEK_API_KEY;
12
13
  // Create the MCP server
13
14
  const server = new Server({
14
15
  name: "jobseek-mcp",
15
- version: "0.3.0",
16
+ version: "0.4.0",
16
17
  }, {
17
18
  capabilities: {
18
19
  tools: {},
@@ -26,6 +27,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
26
27
  optimizeResumeTool,
27
28
  downloadResumePdfTool,
28
29
  uploadResumeTool,
30
+ launchPadTool,
29
31
  ],
30
32
  };
31
33
  });
@@ -39,6 +41,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
39
41
  return handleDownloadResumePdf(args, JOBSEEK_API_URL, JOBSEEK_API_KEY);
40
42
  case "upload_resume":
41
43
  return handleUploadResume(args, JOBSEEK_API_URL, JOBSEEK_API_KEY);
44
+ case "launch_pad":
45
+ return handleLaunchPad(args, JOBSEEK_API_URL, JOBSEEK_API_KEY);
42
46
  default:
43
47
  throw new Error(`Unknown tool: ${name}`);
44
48
  }
@@ -0,0 +1,20 @@
1
+ export declare const launchPadTool: {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: string;
6
+ properties: {
7
+ query: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ };
12
+ required: never[];
13
+ };
14
+ };
15
+ export declare function handleLaunchPad(args: unknown, apiUrl: string, apiKey?: string): Promise<{
16
+ content: Array<{
17
+ type: string;
18
+ text: string;
19
+ }>;
20
+ }>;
@@ -0,0 +1,109 @@
1
+ import { z } from "zod";
2
+ import { exec } from "child_process";
3
+ import { promisify } from "util";
4
+ const execAsync = promisify(exec);
5
+ // Tool definition
6
+ export const launchPadTool = {
7
+ name: "launch_pad",
8
+ description: "Open job search tabs across major job boards (LinkedIn, Indeed, Glassdoor, ZipRecruiter, Wellfound, Dice). Uses your resume to determine the search query, or you can provide a custom query.",
9
+ inputSchema: {
10
+ type: "object",
11
+ properties: {
12
+ query: {
13
+ type: "string",
14
+ description: "Optional custom search query. If not provided, uses your most recent job title from your resume."
15
+ }
16
+ },
17
+ required: []
18
+ }
19
+ };
20
+ // Input validation
21
+ const LaunchPadInput = z.object({
22
+ query: z.string().optional()
23
+ });
24
+ // Tool handler
25
+ export async function handleLaunchPad(args, apiUrl, apiKey) {
26
+ const input = LaunchPadInput.parse(args);
27
+ if (!apiKey) {
28
+ return {
29
+ content: [{
30
+ type: "text",
31
+ text: `❌ **API Key Required**\n\nGet your API key at: ${apiUrl}/dashboard/api-keys`
32
+ }]
33
+ };
34
+ }
35
+ try {
36
+ // Fetch launchpad links from API
37
+ const queryParam = input.query ? `?query=${encodeURIComponent(input.query)}` : '';
38
+ const response = await fetch(`${apiUrl}/api/launchpad${queryParam}`, {
39
+ headers: {
40
+ "Authorization": `Bearer ${apiKey}`,
41
+ },
42
+ });
43
+ if (!response.ok) {
44
+ if (response.status === 401) {
45
+ return {
46
+ content: [{
47
+ type: "text",
48
+ text: `❌ **Invalid API Key**\n\nGenerate a new key at: ${apiUrl}/dashboard/api-keys`
49
+ }]
50
+ };
51
+ }
52
+ const error = await response.json().catch(() => ({}));
53
+ return {
54
+ content: [{
55
+ type: "text",
56
+ text: `❌ **Error**\n\n${error.error || 'Failed to get launchpad links'}`
57
+ }]
58
+ };
59
+ }
60
+ const data = await response.json();
61
+ const { searchQuery, links } = data;
62
+ // Open each URL in the default browser
63
+ const opened = [];
64
+ for (const link of links) {
65
+ try {
66
+ // Works on macOS. For cross-platform: use 'start' on Windows, 'xdg-open' on Linux
67
+ await execAsync(`open "${link.url}"`);
68
+ opened.push(link.name);
69
+ }
70
+ catch (err) {
71
+ console.error(`Failed to open ${link.name}:`, err);
72
+ }
73
+ }
74
+ if (opened.length === 0) {
75
+ return {
76
+ content: [{
77
+ type: "text",
78
+ text: `❌ **Failed to open tabs**\n\nCould not open browser tabs. You may need to open them manually.`
79
+ }]
80
+ };
81
+ }
82
+ return {
83
+ content: [{
84
+ type: "text",
85
+ text: `🚀 **Launch Pad Activated!**
86
+
87
+ **Search Query:** ${searchQuery}
88
+
89
+ **Opened ${opened.length} job boards:**
90
+ ${opened.map(name => `- ${name}`).join('\n')}
91
+
92
+ ---
93
+
94
+ **Next steps:**
95
+ 1. Browse the job listings
96
+ 2. When you find interesting jobs, click the JobSeek extension to analyze them
97
+ 3. Return here to discuss your matches: "What are my top matches from today?"`
98
+ }]
99
+ };
100
+ }
101
+ catch (error) {
102
+ return {
103
+ content: [{
104
+ type: "text",
105
+ text: `❌ **Error**\n\nFailed to launch job boards: ${error.message}`
106
+ }]
107
+ };
108
+ }
109
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jobseek-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "JobSeek MCP Server - AI-powered job search automation for Claude Code",
5
5
  "author": "Shawn Mitchell",
6
6
  "license": "MIT",