From 229fa801d3032b8453fe7ea40b21c6db357ecbec Mon Sep 17 00:00:00 2001 From: Maksim Redzkin Date: Thu, 25 Jun 2026 11:55:01 +0300 Subject: [PATCH] Add searchJobs and fetchJob operations --- package-lock.json | 13 ++- package.json | 4 +- src/linked-api-tools.ts | 4 + src/tools/fetch-job.ts | 32 ++++++++ src/tools/search-jobs.ts | 172 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+), 9 deletions(-) create mode 100644 src/tools/fetch-job.ts create mode 100644 src/tools/search-jobs.ts diff --git a/package-lock.json b/package-lock.json index 954f528..f5f43c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@linkedapi/mcp", - "version": "2.0.1", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@linkedapi/mcp", - "version": "2.0.1", + "version": "2.1.0", "license": "MIT", "dependencies": { - "@linkedapi/node": "^2.0.0", + "@linkedapi/node": "^2.0.4", "@modelcontextprotocol/sdk": "^1.17.4", "zod": "^4.1.1" }, @@ -921,10 +921,9 @@ } }, "node_modules/@linkedapi/node": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@linkedapi/node/-/node-2.0.0.tgz", - "integrity": "sha512-DkHN/cqnSVyG0Whem1IYCZoojWj/8U1+IOvvWzLIXTcbWzYMvWkMSmmF1jWEgwwa0V+Flc60nZR8xGlIaAxpcg==", - "license": "MIT" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@linkedapi/node/-/node-2.0.4.tgz", + "integrity": "sha512-x/Xvkt3/mcJJoW1eZhkSqTvB7XFZVcXNZcAwQBEZfd2AvYhBiZWxKV2OkORetogfRxXHGsIw49ZPKJHadzJvSQ==" }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.26.0", diff --git a/package.json b/package.json index 815b0b2..50b2050 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@linkedapi/mcp", - "version": "2.0.1", + "version": "2.1.0", "description": "MCP server that lets AI assistants control LinkedIn accounts and retrieve real-time data.", "main": "dist/index.js", "bin": { @@ -30,7 +30,7 @@ "author": "Linked API", "license": "MIT", "dependencies": { - "@linkedapi/node": "^2.0.0", + "@linkedapi/node": "^2.0.4", "@modelcontextprotocol/sdk": "^1.17.4", "zod": "^4.1.1" }, diff --git a/src/linked-api-tools.ts b/src/linked-api-tools.ts index 4f469fe..be500e2 100644 --- a/src/linked-api-tools.ts +++ b/src/linked-api-tools.ts @@ -16,6 +16,7 @@ import { CommentOnPostTool } from './tools/comment-on-post.js'; import { CreatePostTool } from './tools/create-post.js'; import { ExecuteCustomWorkflowTool } from './tools/execute-custom-workflow.js'; import { FetchCompanyTool } from './tools/fetch-company.js'; +import { FetchJobTool } from './tools/fetch-job.js'; import { FetchPersonTool } from './tools/fetch-person.js'; import { FetchPostTool } from './tools/fetch-post.js'; import { GetApiUsageTool } from './tools/get-api-usage-stats.js'; @@ -34,6 +35,7 @@ import { RetrievePendingRequestsTool } from './tools/retrieve-pending-requests.j import { RetrievePerformanceTool } from './tools/retrieve-performance.js'; import { RetrieveSSITool } from './tools/retrieve-ssi.js'; import { SearchCompaniesTool } from './tools/search-companies.js'; +import { SearchJobsTool } from './tools/search-jobs.js'; import { SearchPeopleTool } from './tools/search-people.js'; import { SendConnectionRequestTool } from './tools/send-connection-request.js'; import { SendMessageTool } from './tools/send-message.js'; @@ -69,9 +71,11 @@ export class LinkedApiTools { new RemoveConnectionTool(), new SearchCompaniesTool(), new SearchPeopleTool(), + new SearchJobsTool(), new FetchCompanyTool(), new FetchPersonTool(), new FetchPostTool(), + new FetchJobTool(), new ReactToPostTool(), new CommentOnPostTool(), new CreatePostTool(), diff --git a/src/tools/fetch-job.ts b/src/tools/fetch-job.ts new file mode 100644 index 0000000..947fe72 --- /dev/null +++ b/src/tools/fetch-job.ts @@ -0,0 +1,32 @@ +import { OPERATION_NAME, TBaseFetchJobParams } from '@linkedapi/node'; +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import { z } from 'zod'; + +import { OperationTool } from '../utils/linked-api-tool.js'; + +export class FetchJobTool extends OperationTool { + public override readonly name = 'fetch_job'; + public override readonly operationName = OPERATION_NAME.fetchJob; + protected override readonly schema = z.object({ + jobUrl: z.string(), + }); + + public override getTool(): Tool { + return { + name: this.name, + description: + 'Open a LinkedIn job and retrieve its details such as company, location, salary, and description (st.openJob action).', + inputSchema: { + type: 'object', + properties: { + jobUrl: { + type: 'string', + description: + "LinkedIn URL of the job. (e.g., 'https://www.linkedin.com/jobs/view/4416248954/')", + }, + }, + required: ['jobUrl'], + }, + }; + } +} diff --git a/src/tools/search-jobs.ts b/src/tools/search-jobs.ts new file mode 100644 index 0000000..08e8f32 --- /dev/null +++ b/src/tools/search-jobs.ts @@ -0,0 +1,172 @@ +import { OPERATION_NAME, TSearchJobsParams } from '@linkedapi/node'; +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import { z } from 'zod'; + +import { OperationTool } from '../utils/linked-api-tool.js'; + +export class SearchJobsTool extends OperationTool { + public override readonly name = 'search_jobs'; + public override readonly operationName = OPERATION_NAME.searchJobs; + protected override readonly schema = z.object({ + term: z.string().optional(), + limit: z.number().min(1).max(1000).optional(), + filter: z + .object({ + location: z.string().optional(), + datePosted: z.enum(['anyTime', 'past24Hours', 'pastWeek', 'pastMonth']).optional(), + experienceLevels: z + .array( + z.enum([ + 'internship', + 'entryLevel', + 'associate', + 'midSeniorLevel', + 'director', + 'executive', + ]), + ) + .optional(), + employmentTypes: z + .array( + z.enum([ + 'fullTime', + 'partTime', + 'contract', + 'temporary', + 'volunteer', + 'internship', + 'other', + ]), + ) + .optional(), + workplaceTypes: z.array(z.enum(['onSite', 'remote', 'hybrid'])).optional(), + companies: z.array(z.string()).optional(), + industries: z.array(z.string()).optional(), + jobFunctions: z.array(z.string()).optional(), + easyApply: z.boolean().optional(), + hasVerifications: z.boolean().optional(), + under10Applicants: z.boolean().optional(), + inYourNetwork: z.boolean().optional(), + fairChanceEmployer: z.boolean().optional(), + }) + .optional(), + customSearchUrl: z.string().optional(), + }); + + public override getTool(): Tool { + return { + name: this.name, + description: + 'Allows you to search jobs applying various filtering criteria (st.searchJobs action).', + inputSchema: { + type: 'object', + properties: { + term: { + type: 'string', + description: 'Optional. Keyword or phrase to search.', + }, + limit: { + type: 'number', + description: + 'Optional. Number of search results to return. Defaults to 10, with a maximum value of 1000.', + }, + filter: { + type: 'object', + description: + 'Optional. Object that specifies filtering criteria for jobs. When multiple filter fields are specified, they are combined using AND logic.', + properties: { + location: { + type: 'string', + description: 'Optional. Free-form location string.', + }, + datePosted: { + type: 'string', + enum: ['anyTime', 'past24Hours', 'pastWeek', 'pastMonth'], + description: 'Optional. How recently the job was posted.', + }, + experienceLevels: { + type: 'array', + description: 'Optional. Array of experience levels.', + items: { + type: 'string', + enum: [ + 'internship', + 'entryLevel', + 'associate', + 'midSeniorLevel', + 'director', + 'executive', + ], + }, + }, + employmentTypes: { + type: 'array', + description: 'Optional. Array of employment types.', + items: { + type: 'string', + enum: [ + 'fullTime', + 'partTime', + 'contract', + 'temporary', + 'volunteer', + 'internship', + 'other', + ], + }, + }, + workplaceTypes: { + type: 'array', + description: 'Optional. Array of workplace types.', + items: { + type: 'string', + enum: ['onSite', 'remote', 'hybrid'], + }, + }, + companies: { + type: 'array', + description: 'Optional. Array of company names.', + items: { type: 'string' }, + }, + industries: { + type: 'array', + description: 'Optional. Array of industry names.', + items: { type: 'string' }, + }, + jobFunctions: { + type: 'array', + description: 'Optional. Array of job function names.', + items: { type: 'string' }, + }, + easyApply: { + type: 'boolean', + description: 'Optional. When true, only jobs with Easy Apply.', + }, + hasVerifications: { + type: 'boolean', + description: 'Optional. When true, only jobs with verification signals.', + }, + under10Applicants: { + type: 'boolean', + description: 'Optional. When true, only jobs with fewer than 10 applicants.', + }, + inYourNetwork: { + type: 'boolean', + description: 'Optional. When true, only jobs from your network.', + }, + fairChanceEmployer: { + type: 'boolean', + description: 'Optional. When true, only fair chance employer jobs.', + }, + }, + }, + customSearchUrl: { + type: 'string', + description: + 'Optional. URL copied from a LinkedIn jobs search page. When specified, overrides term and filter.', + }, + }, + }, + }; + } +}