whistler-mcp 1.0.6 → 1.0.8

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +20 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whistler-mcp",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "An MCP Server for retrieving placement data, job roles, and platform statistics from the Whistler API.",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -109,7 +109,7 @@ const toolsDefinition = {
109
109
 
110
110
  // Read PDF
111
111
  view_job_pdf: {
112
- description: "Read the specific textual content of a company's job role PDF. This will consume 1 from the user's daily PDF view limit. Ensure you pass the exact `pdfUrl` obtained from `get_company_roles`.",
112
+ description: "Extract and read the textual content of a company's job role PDF. (Consumes 1 daily view).",
113
113
  schema: z.object({ pdfUrl: z.string() }),
114
114
  inputSchema: { type: "object", properties: { pdfUrl: { type: "string" } }, required: ["pdfUrl"] }
115
115
  }
@@ -169,7 +169,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
169
169
  if (!loginData.token) throw new Error('Login succeeded but no token was returned.');
170
170
 
171
171
  saveToken(loginData.token, loginData);
172
- result = { success: true, message: `Successfully authenticated! Token saved securely.` };
172
+ result = { success: true, message: `Authenticated!` };
173
173
  break;
174
174
  }
175
175
  case 'signup_to_whistler': {
@@ -191,12 +191,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
191
191
  if (!signupData.token) throw new Error('Signup succeeded but no token was returned.');
192
192
 
193
193
  saveToken(signupData.token, signupData);
194
- result = { success: true, message: `Account created and authenticated successfully! Welcome to Whistler.` };
194
+ result = { success: true, message: `Account created!` };
195
195
  break;
196
196
  }
197
197
  case 'logout_whistler':
198
198
  clearToken();
199
- result = { success: true, message: 'Logged out successfully. Token cleared.' };
199
+ result = { success: true, message: 'Logged out.' };
200
200
  break;
201
201
  case 'get_companies':
202
202
  result = await invokeApi(`/pdfs/companies${params.year ? '?year='+params.year : ''}`);
@@ -217,6 +217,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
217
217
  result = await invokeApi('/buy-bid', 'POST', params);
218
218
  break;
219
219
  case 'view_job_pdf': {
220
+ // Guard: pdfUrl must exist and look like a valid API path
221
+ if (!params.pdfUrl || params.pdfUrl === 'undefined' || params.pdfUrl === 'null') {
222
+ result = {
223
+ success: false,
224
+ message: "This job does not have a PDF available. The 'pdfUrl' field was not returned by get_company_roles, which means no JD PDF exists for this role."
225
+ };
226
+ break;
227
+ }
228
+ if (!params.pdfUrl.startsWith('/api/pdfs/')) {
229
+ result = {
230
+ success: false,
231
+ message: `Invalid pdfUrl format: "${params.pdfUrl}". You must pass the exact 'pdfUrl' value returned by get_company_roles (e.g. /api/pdfs/secure/<id>).`
232
+ };
233
+ break;
234
+ }
220
235
  // pdfUrl from the backend is a relative path like /api/pdfs/secure/:id
221
236
  // invokeApiBinary prepends API_BASE_URL which already ends in /api,
222
237
  // so we must strip the leading /api prefix to avoid doubling it.
@@ -228,7 +243,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
228
243
  success: true,
229
244
  text: pdfData.text,
230
245
  pages: pdfData.numpages,
231
- message: "PDF successfully read and your daily limit has been updated."
246
+ message: "PDF read successfully."
232
247
  };
233
248
  break;
234
249
  }