llmconveyors-mcp 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LLM Conveyors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,232 @@
1
+ # llmconveyors-mcp
2
+
3
+ [![npm version](https://img.shields.io/npm/v/llmconveyors-mcp.svg)](https://www.npmjs.com/package/llmconveyors-mcp)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org)
6
+
7
+ MCP server that connects AI agents to the [LLM Conveyors](https://llmconveyors.com) platform — run Job Hunter, B2B Sales, and other AI agents directly from Claude, Cursor, or any MCP-compatible client.
8
+
9
+ <p align="center">
10
+ <img src="https://llmconveyors.com/assets/logo.png" alt="LLM Conveyors" width="200" />
11
+ </p>
12
+
13
+ ## What is LLM Conveyors?
14
+
15
+ A community-driven AI agent platform with pay-per-action pricing ($1–5 per completed action). Instead of $100/mo SaaS subscriptions, you pay only for real outputs — a resume scored, a company researched, a cold email generated.
16
+
17
+ **Live Agents:**
18
+ - **Job Hunter** — Tailored CVs, cover letters, and cold emails for job applications
19
+ - **B2B Sales** — Deep company research and personalized sales outreach
20
+
21
+ ## Quick Start
22
+
23
+ ### 1. Get an API key
24
+
25
+ Sign up at [llmconveyors.com](https://llmconveyors.com) and create an API key from Settings → API Keys.
26
+
27
+ ### 2. Add to your MCP client
28
+
29
+ <details>
30
+ <summary><strong>Claude Desktop</strong></summary>
31
+
32
+ Add to `claude_desktop_config.json`:
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "llmconveyors": {
38
+ "command": "npx",
39
+ "args": ["-y", "llmconveyors-mcp"],
40
+ "env": {
41
+ "LLMC_API_KEY": "llmc_your_key_here"
42
+ }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+ </details>
48
+
49
+ <details>
50
+ <summary><strong>Claude Code</strong></summary>
51
+
52
+ ```bash
53
+ claude mcp add llmconveyors -- npx -y llmconveyors-mcp
54
+ ```
55
+
56
+ Set the env var in your shell or `.env`:
57
+ ```bash
58
+ export LLMC_API_KEY=llmc_your_key_here
59
+ ```
60
+ </details>
61
+
62
+ <details>
63
+ <summary><strong>Cursor</strong></summary>
64
+
65
+ Add to `.cursor/mcp.json`:
66
+
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "llmconveyors": {
71
+ "command": "npx",
72
+ "args": ["-y", "llmconveyors-mcp"],
73
+ "env": {
74
+ "LLMC_API_KEY": "llmc_your_key_here"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+ </details>
81
+
82
+ <details>
83
+ <summary><strong>Windsurf</strong></summary>
84
+
85
+ Add to `~/.codeium/windsurf/mcp_config.json`:
86
+
87
+ ```json
88
+ {
89
+ "mcpServers": {
90
+ "llmconveyors": {
91
+ "command": "npx",
92
+ "args": ["-y", "llmconveyors-mcp"],
93
+ "env": {
94
+ "LLMC_API_KEY": "llmc_your_key_here"
95
+ }
96
+ }
97
+ }
98
+ }
99
+ ```
100
+ </details>
101
+
102
+ ### 3. Start using it
103
+
104
+ Ask your AI agent:
105
+
106
+ > "Run the Job Hunter agent for the Senior Engineer role at Anthropic. Here's the job description: ..."
107
+
108
+ > "Score my resume against this job posting for ATS compatibility."
109
+
110
+ > "Research Stripe and draft a B2B cold email for our developer tools product."
111
+
112
+ ## Available Tools (39)
113
+
114
+ ### Agents
115
+ | Tool | Description |
116
+ |------|-------------|
117
+ | `job-hunter-run` | Run the Job Hunter agent — generates tailored CV, cover letter, and cold email |
118
+ | `b2b-sales-run` | Run the B2B Sales agent — researches a company and generates sales outreach |
119
+ | `agent-status` | Check the status of a running agent job |
120
+ | `agent-manifest` | Get input fields, capabilities, and billing info for an agent |
121
+
122
+ ### Resume
123
+ | Tool | Description |
124
+ |------|-------------|
125
+ | `resume-validate` | Validate a resume in JSON Resume format |
126
+ | `resume-render` | Render a resume to PDF |
127
+ | `resume-preview` | Preview a resume as HTML |
128
+ | `resume-themes` | List available resume themes |
129
+ | `resume-import-rx` | Import from Reactive Resume format |
130
+ | `resume-export-rx` | Export to Reactive Resume format |
131
+
132
+ ### Master Resumes
133
+ | Tool | Description |
134
+ |------|-------------|
135
+ | `master-resume-create` | Create a new master resume |
136
+ | `master-resume-list` | List all master resumes |
137
+ | `master-resume-get` | Get a master resume by ID |
138
+ | `master-resume-update` | Update a master resume |
139
+ | `master-resume-delete` | Delete a master resume |
140
+
141
+ ### Upload & Parse
142
+ | Tool | Description |
143
+ |------|-------------|
144
+ | `upload-resume` | Upload and parse a resume file (base64) |
145
+ | `upload-job-file` | Upload and parse a job description file (base64) |
146
+ | `upload-job-text` | Parse a job description from plain text |
147
+
148
+ ### ATS Scoring
149
+ | Tool | Description |
150
+ |------|-------------|
151
+ | `ats-score` | Score a resume against a job description for ATS compatibility |
152
+
153
+ ### Sessions
154
+ | Tool | Description |
155
+ |------|-------------|
156
+ | `session-create` | Create a new session |
157
+ | `session-list` | List sessions with optional filtering |
158
+ | `session-get` | Get a session by ID |
159
+ | `session-hydrate` | Get full session with artifacts and logs |
160
+ | `session-delete` | Delete a session |
161
+
162
+ ### Settings & API Keys
163
+ | Tool | Description |
164
+ |------|-------------|
165
+ | `settings-profile` | Get user profile (credits, plan) |
166
+ | `settings-preferences-get` | Get user preferences |
167
+ | `settings-preferences-update` | Update user preferences |
168
+ | `settings-usage-summary` | Get usage summary |
169
+ | `settings-usage-logs` | Get paginated usage logs |
170
+ | `api-key-create` | Create a new API key |
171
+ | `api-key-list` | List all API keys |
172
+ | `api-key-revoke` | Revoke an API key |
173
+ | `api-key-rotate` | Rotate an API key |
174
+
175
+ ### Content & Sharing
176
+ | Tool | Description |
177
+ |------|-------------|
178
+ | `content-save` | Save a source document for AI generation context |
179
+ | `content-delete-generation` | Delete a generation and its artifacts |
180
+ | `share-create` | Create a public share link for generated content |
181
+ | `share-stats` | Get share link statistics |
182
+ | `share-get-public` | Get a public share by slug |
183
+
184
+ ### Documents
185
+ | Tool | Description |
186
+ |------|-------------|
187
+ | `document-download` | Download an artifact by storage path |
188
+
189
+ ## API Key Scopes
190
+
191
+ Your API key needs the right scopes for the tools you want to use:
192
+
193
+ | Scope | Tools |
194
+ |-------|-------|
195
+ | `jobs:read` | `agent-status`, `agent-manifest` |
196
+ | `jobs:write` | `job-hunter-run` |
197
+ | `sales:write` | `b2b-sales-run` |
198
+ | `sessions:read` | `session-list`, `session-get`, `session-hydrate` |
199
+ | `sessions:write` | `session-create`, `session-delete` |
200
+ | `resume:read` | `resume-themes`, `master-resume-list`, `master-resume-get` |
201
+ | `resume:write` | `resume-validate`, `resume-render`, `resume-preview`, `resume-import-rx`, `resume-export-rx`, `master-resume-create`, `master-resume-update`, `master-resume-delete` |
202
+ | `upload:write` | `upload-resume`, `upload-job-file`, `upload-job-text` |
203
+ | `ats:write` | `ats-score` |
204
+ | `settings:read` | `settings-profile`, `settings-preferences-get`, `settings-usage-summary`, `settings-usage-logs`, `api-key-list` |
205
+ | `settings:write` | `settings-preferences-update`, `api-key-create`, `api-key-revoke`, `api-key-rotate` |
206
+
207
+ ## Development
208
+
209
+ ```bash
210
+ git clone https://github.com/llmconveyors/llmconveyors-mcp.git
211
+ cd llmconveyors-mcp
212
+ npm install
213
+ npm run build
214
+
215
+ # Test locally
216
+ LLMC_API_KEY=llmc_your_key node dist/index.js
217
+ ```
218
+
219
+ ## Requirements
220
+
221
+ - Node.js >= 18
222
+ - An LLM Conveyors API key ([get one here](https://llmconveyors.com))
223
+
224
+ ## Links
225
+
226
+ - [LLM Conveyors Platform](https://llmconveyors.com)
227
+ - [API Documentation](https://llmconveyors.com/docs/api)
228
+ - [TypeScript SDK](https://www.npmjs.com/package/llmconveyors)
229
+
230
+ ## License
231
+
232
+ MIT — see [LICENSE](LICENSE) for details.
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,808 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { LLMConveyors } from "llmconveyors";
7
+
8
+ // src/tools/agents.ts
9
+ import { z } from "zod";
10
+ function registerAgentTools(server2, client2) {
11
+ server2.tool(
12
+ "job-hunter-run",
13
+ "Run the Job Hunter agent \u2014 generates a tailored CV, cover letter, and cold email for a job application. Returns artifacts when complete.",
14
+ {
15
+ companyName: z.string().describe("Target company name"),
16
+ jobTitle: z.string().describe("Job title to apply for"),
17
+ jobDescription: z.string().describe("Full job description text"),
18
+ masterResumeId: z.string().optional().describe("ID of a stored master resume to use"),
19
+ theme: z.string().optional().describe("Resume theme (Even, StackOverflow, Class, Professional)"),
20
+ contactName: z.string().optional().describe("Hiring manager or recruiter name"),
21
+ contactEmail: z.string().optional().describe("Contact email for cold outreach"),
22
+ mode: z.enum(["standard", "cold_outreach"]).optional().describe("Generation mode")
23
+ },
24
+ async (params) => {
25
+ try {
26
+ const result = await client2.agents.run("job-hunter", {
27
+ companyName: params.companyName,
28
+ jobTitle: params.jobTitle,
29
+ jobDescription: params.jobDescription,
30
+ masterResumeId: params.masterResumeId,
31
+ theme: params.theme,
32
+ contactName: params.contactName,
33
+ contactEmail: params.contactEmail,
34
+ mode: params.mode
35
+ });
36
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
37
+ } catch (err) {
38
+ const message = err instanceof Error ? err.message : String(err);
39
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
40
+ }
41
+ }
42
+ );
43
+ server2.tool(
44
+ "b2b-sales-run",
45
+ "Run the B2B Sales agent \u2014 researches a company and generates personalized sales outreach.",
46
+ {
47
+ companyName: z.string().describe("Target company name"),
48
+ companyWebsite: z.string().describe("Target company website URL"),
49
+ strategy: z.string().optional().describe("Sales strategy or approach")
50
+ },
51
+ async (params) => {
52
+ try {
53
+ const result = await client2.agents.run("b2b-sales", {
54
+ companyName: params.companyName,
55
+ companyWebsite: params.companyWebsite,
56
+ strategy: params.strategy
57
+ });
58
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
59
+ } catch (err) {
60
+ const message = err instanceof Error ? err.message : String(err);
61
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
62
+ }
63
+ }
64
+ );
65
+ server2.tool(
66
+ "agent-status",
67
+ "Check the status of a running agent job.",
68
+ {
69
+ agentType: z.enum(["job-hunter", "b2b-sales"]).describe("Agent type"),
70
+ jobId: z.string().describe("Job ID returned from a generate call")
71
+ },
72
+ async (params) => {
73
+ try {
74
+ const result = await client2.agents.getStatus(params.agentType, params.jobId, {
75
+ include: ["logs", "artifacts"]
76
+ });
77
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
78
+ } catch (err) {
79
+ const message = err instanceof Error ? err.message : String(err);
80
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
81
+ }
82
+ }
83
+ );
84
+ server2.tool(
85
+ "agent-manifest",
86
+ "Get the manifest (input fields, capabilities, billing) for an agent type.",
87
+ {
88
+ agentType: z.enum(["job-hunter", "b2b-sales"]).describe("Agent type")
89
+ },
90
+ async (params) => {
91
+ try {
92
+ const result = await client2.agents.getManifest(params.agentType);
93
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
94
+ } catch (err) {
95
+ const message = err instanceof Error ? err.message : String(err);
96
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
97
+ }
98
+ }
99
+ );
100
+ }
101
+
102
+ // src/tools/ats.ts
103
+ import { z as z2 } from "zod";
104
+ function registerAtsTools(server2, client2) {
105
+ server2.tool(
106
+ "ats-score",
107
+ "Score a resume against a job description for ATS compatibility. Returns overall score, grade, keyword matches, and improvement suggestions.",
108
+ {
109
+ resumeText: z2.string().describe("Resume as plain text"),
110
+ jobDescription: z2.string().describe("Job description as plain text")
111
+ },
112
+ async (params) => {
113
+ try {
114
+ const result = await client2.ats.score({
115
+ resumeText: params.resumeText,
116
+ jobDescription: params.jobDescription
117
+ });
118
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
119
+ } catch (err) {
120
+ const message = err instanceof Error ? err.message : String(err);
121
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
122
+ }
123
+ }
124
+ );
125
+ }
126
+
127
+ // src/tools/resume.ts
128
+ import { z as z3 } from "zod";
129
+ function registerResumeTools(server2, client2) {
130
+ server2.tool(
131
+ "resume-validate",
132
+ "Validate a resume in JSON Resume format. Returns validation errors and warnings.",
133
+ {
134
+ resume: z3.record(z3.unknown()).describe("Resume object in JSON Resume format")
135
+ },
136
+ async (params) => {
137
+ try {
138
+ const result = await client2.resume.validate({ resume: params.resume });
139
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
140
+ } catch (err) {
141
+ const message = err instanceof Error ? err.message : String(err);
142
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
143
+ }
144
+ }
145
+ );
146
+ server2.tool(
147
+ "resume-render",
148
+ "Render a resume to PDF. Returns a URL to download the generated PDF.",
149
+ {
150
+ resume: z3.record(z3.unknown()).describe("Resume object in JSON Resume format"),
151
+ theme: z3.string().optional().describe("Theme name (e.g. Even, StackOverflow, Class, Professional)"),
152
+ format: z3.string().optional().describe("Output format")
153
+ },
154
+ async (params) => {
155
+ try {
156
+ const result = await client2.resume.render({
157
+ resume: params.resume,
158
+ theme: params.theme,
159
+ format: params.format
160
+ });
161
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
162
+ } catch (err) {
163
+ const message = err instanceof Error ? err.message : String(err);
164
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
165
+ }
166
+ }
167
+ );
168
+ server2.tool(
169
+ "resume-preview",
170
+ "Preview a resume as HTML. Returns rendered HTML string.",
171
+ {
172
+ resume: z3.record(z3.unknown()).describe("Resume object in JSON Resume format"),
173
+ theme: z3.string().optional().describe("Theme name")
174
+ },
175
+ async (params) => {
176
+ try {
177
+ const result = await client2.resume.preview({
178
+ resume: params.resume,
179
+ theme: params.theme
180
+ });
181
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
182
+ } catch (err) {
183
+ const message = err instanceof Error ? err.message : String(err);
184
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
185
+ }
186
+ }
187
+ );
188
+ server2.tool(
189
+ "resume-themes",
190
+ "List all available resume themes. Returns theme IDs, names, and descriptions.",
191
+ {},
192
+ async () => {
193
+ try {
194
+ const result = await client2.resume.themes();
195
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
196
+ } catch (err) {
197
+ const message = err instanceof Error ? err.message : String(err);
198
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
199
+ }
200
+ }
201
+ );
202
+ server2.tool(
203
+ "resume-import-rx",
204
+ "Import a resume from Reactive Resume (RxResume) format into JSON Resume format.",
205
+ {
206
+ data: z3.record(z3.unknown()).describe("RxResume data object to import")
207
+ },
208
+ async (params) => {
209
+ try {
210
+ const result = await client2.resume.importRxResume({ data: params.data });
211
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
212
+ } catch (err) {
213
+ const message = err instanceof Error ? err.message : String(err);
214
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
215
+ }
216
+ }
217
+ );
218
+ server2.tool(
219
+ "resume-export-rx",
220
+ "Export a JSON Resume to Reactive Resume (RxResume) format.",
221
+ {
222
+ resume: z3.record(z3.unknown()).describe("Resume object in JSON Resume format")
223
+ },
224
+ async (params) => {
225
+ try {
226
+ const result = await client2.resume.exportRxResume({ resume: params.resume });
227
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
228
+ } catch (err) {
229
+ const message = err instanceof Error ? err.message : String(err);
230
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
231
+ }
232
+ }
233
+ );
234
+ server2.tool(
235
+ "master-resume-create",
236
+ "Create a new master resume. Returns the saved master resume with its ID.",
237
+ {
238
+ label: z3.string().describe("Label/name for this master resume"),
239
+ rawText: z3.string().describe("Raw resume text content"),
240
+ isDefault: z3.boolean().optional().describe("Set as default master resume")
241
+ },
242
+ async (params) => {
243
+ try {
244
+ const result = await client2.resume.createMaster({
245
+ label: params.label,
246
+ rawText: params.rawText,
247
+ isDefault: params.isDefault
248
+ });
249
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
250
+ } catch (err) {
251
+ const message = err instanceof Error ? err.message : String(err);
252
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
253
+ }
254
+ }
255
+ );
256
+ server2.tool(
257
+ "master-resume-list",
258
+ "List all master resumes. Returns an array of master resume objects.",
259
+ {},
260
+ async () => {
261
+ try {
262
+ const result = await client2.resume.listMasters();
263
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
264
+ } catch (err) {
265
+ const message = err instanceof Error ? err.message : String(err);
266
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
267
+ }
268
+ }
269
+ );
270
+ server2.tool(
271
+ "master-resume-get",
272
+ "Get a master resume by ID. Returns the full master resume object.",
273
+ {
274
+ id: z3.string().describe("Master resume ID")
275
+ },
276
+ async (params) => {
277
+ try {
278
+ const result = await client2.resume.getMaster(params.id);
279
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
280
+ } catch (err) {
281
+ const message = err instanceof Error ? err.message : String(err);
282
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
283
+ }
284
+ }
285
+ );
286
+ server2.tool(
287
+ "master-resume-update",
288
+ "Update a master resume by ID. Returns the updated master resume.",
289
+ {
290
+ id: z3.string().describe("Master resume ID"),
291
+ label: z3.string().optional().describe("Updated label/name"),
292
+ rawText: z3.string().optional().describe("Updated raw resume text"),
293
+ isDefault: z3.boolean().optional().describe("Set as default master resume")
294
+ },
295
+ async (params) => {
296
+ try {
297
+ const result = await client2.resume.updateMaster(params.id, {
298
+ label: params.label,
299
+ rawText: params.rawText,
300
+ isDefault: params.isDefault
301
+ });
302
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
303
+ } catch (err) {
304
+ const message = err instanceof Error ? err.message : String(err);
305
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
306
+ }
307
+ }
308
+ );
309
+ server2.tool(
310
+ "master-resume-delete",
311
+ "Delete a master resume by ID.",
312
+ {
313
+ id: z3.string().describe("Master resume ID")
314
+ },
315
+ async (params) => {
316
+ try {
317
+ await client2.resume.deleteMaster(params.id);
318
+ return { content: [{ type: "text", text: JSON.stringify({ deleted: true, id: params.id }) }] };
319
+ } catch (err) {
320
+ const message = err instanceof Error ? err.message : String(err);
321
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
322
+ }
323
+ }
324
+ );
325
+ }
326
+
327
+ // src/tools/upload.ts
328
+ import { z as z4 } from "zod";
329
+ function registerUploadTools(server2, client2) {
330
+ server2.tool(
331
+ "upload-resume",
332
+ "Upload and parse a resume file. Accepts base64-encoded file content. Returns parsed resume data.",
333
+ {
334
+ fileBase64: z4.string().describe("Base64-encoded file content (PDF, DOCX, etc.)"),
335
+ filename: z4.string().describe("Original filename with extension (e.g. resume.pdf)"),
336
+ contentType: z4.string().optional().describe("MIME type (e.g. application/pdf)")
337
+ },
338
+ async (params) => {
339
+ try {
340
+ const buffer = Buffer.from(params.fileBase64, "base64");
341
+ const result = await client2.upload.resume(buffer, {
342
+ filename: params.filename,
343
+ contentType: params.contentType
344
+ });
345
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
346
+ } catch (err) {
347
+ const message = err instanceof Error ? err.message : String(err);
348
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
349
+ }
350
+ }
351
+ );
352
+ server2.tool(
353
+ "upload-job-file",
354
+ "Upload and parse a job description file. Accepts base64-encoded file content. Returns parsed job data.",
355
+ {
356
+ fileBase64: z4.string().describe("Base64-encoded file content (PDF, DOCX, etc.)"),
357
+ filename: z4.string().describe("Original filename with extension"),
358
+ contentType: z4.string().optional().describe("MIME type")
359
+ },
360
+ async (params) => {
361
+ try {
362
+ const buffer = Buffer.from(params.fileBase64, "base64");
363
+ const result = await client2.upload.jobFile(buffer, {
364
+ filename: params.filename,
365
+ contentType: params.contentType
366
+ });
367
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
368
+ } catch (err) {
369
+ const message = err instanceof Error ? err.message : String(err);
370
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
371
+ }
372
+ }
373
+ );
374
+ server2.tool(
375
+ "upload-job-text",
376
+ "Upload a job description as plain text. Returns parsed job data.",
377
+ {
378
+ text: z4.string().describe("Job description text"),
379
+ source: z4.string().optional().describe("Source URL or identifier for the job posting")
380
+ },
381
+ async (params) => {
382
+ try {
383
+ const result = await client2.upload.jobText({
384
+ text: params.text,
385
+ source: params.source
386
+ });
387
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
388
+ } catch (err) {
389
+ const message = err instanceof Error ? err.message : String(err);
390
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
391
+ }
392
+ }
393
+ );
394
+ }
395
+
396
+ // src/tools/sessions.ts
397
+ import { z as z5 } from "zod";
398
+ function registerSessionTools(server2, client2) {
399
+ server2.tool(
400
+ "session-create",
401
+ "Create a new session. Returns the created session object with its ID.",
402
+ {
403
+ metadata: z5.record(z5.unknown()).optional().describe("Optional session metadata (e.g. agentType, source)")
404
+ },
405
+ async (params) => {
406
+ try {
407
+ const result = await client2.sessions.create({
408
+ metadata: params.metadata
409
+ });
410
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
411
+ } catch (err) {
412
+ const message = err instanceof Error ? err.message : String(err);
413
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
414
+ }
415
+ }
416
+ );
417
+ server2.tool(
418
+ "session-list",
419
+ "List sessions with optional filtering and pagination. Returns an array of session objects.",
420
+ {
421
+ page: z5.number().optional().describe("Page number for pagination"),
422
+ limit: z5.number().optional().describe("Number of sessions per page"),
423
+ agentType: z5.string().optional().describe("Filter by agent type")
424
+ },
425
+ async (params) => {
426
+ try {
427
+ const result = await client2.sessions.list({
428
+ page: params.page,
429
+ limit: params.limit,
430
+ agentType: params.agentType
431
+ });
432
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
433
+ } catch (err) {
434
+ const message = err instanceof Error ? err.message : String(err);
435
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
436
+ }
437
+ }
438
+ );
439
+ server2.tool(
440
+ "session-get",
441
+ "Get a session by ID. Returns the session object with generation history.",
442
+ {
443
+ id: z5.string().describe("Session ID")
444
+ },
445
+ async (params) => {
446
+ try {
447
+ const result = await client2.sessions.get(params.id);
448
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
449
+ } catch (err) {
450
+ const message = err instanceof Error ? err.message : String(err);
451
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
452
+ }
453
+ }
454
+ );
455
+ server2.tool(
456
+ "session-hydrate",
457
+ "Hydrate a session \u2014 returns the full session with all artifacts and logs.",
458
+ {
459
+ id: z5.string().describe("Session ID")
460
+ },
461
+ async (params) => {
462
+ try {
463
+ const result = await client2.sessions.hydrate(params.id);
464
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
465
+ } catch (err) {
466
+ const message = err instanceof Error ? err.message : String(err);
467
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
468
+ }
469
+ }
470
+ );
471
+ server2.tool(
472
+ "session-delete",
473
+ "Delete a session by ID.",
474
+ {
475
+ id: z5.string().describe("Session ID")
476
+ },
477
+ async (params) => {
478
+ try {
479
+ await client2.sessions.delete(params.id);
480
+ return { content: [{ type: "text", text: JSON.stringify({ deleted: true, id: params.id }) }] };
481
+ } catch (err) {
482
+ const message = err instanceof Error ? err.message : String(err);
483
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
484
+ }
485
+ }
486
+ );
487
+ }
488
+
489
+ // src/tools/settings.ts
490
+ import { z as z6 } from "zod";
491
+ function registerSettingsTools(server2, client2) {
492
+ server2.tool(
493
+ "settings-profile",
494
+ "Get the current user's profile. Returns email, name, plan, and credit balance.",
495
+ {},
496
+ async () => {
497
+ try {
498
+ const result = await client2.settings.getProfile();
499
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
500
+ } catch (err) {
501
+ const message = err instanceof Error ? err.message : String(err);
502
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
503
+ }
504
+ }
505
+ );
506
+ server2.tool(
507
+ "settings-preferences-get",
508
+ "Get the current user's preferences.",
509
+ {},
510
+ async () => {
511
+ try {
512
+ const result = await client2.settings.getPreferences();
513
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
514
+ } catch (err) {
515
+ const message = err instanceof Error ? err.message : String(err);
516
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
517
+ }
518
+ }
519
+ );
520
+ server2.tool(
521
+ "settings-preferences-update",
522
+ "Update the current user's preferences. Returns the updated preferences.",
523
+ {
524
+ preferences: z6.record(z6.unknown()).describe("Preferences object to update")
525
+ },
526
+ async (params) => {
527
+ try {
528
+ const result = await client2.settings.updatePreferences({
529
+ preferences: params.preferences
530
+ });
531
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
532
+ } catch (err) {
533
+ const message = err instanceof Error ? err.message : String(err);
534
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
535
+ }
536
+ }
537
+ );
538
+ server2.tool(
539
+ "settings-usage-summary",
540
+ "Get a summary of the user's API usage and credit consumption.",
541
+ {},
542
+ async () => {
543
+ try {
544
+ const result = await client2.settings.getUsageSummary();
545
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
546
+ } catch (err) {
547
+ const message = err instanceof Error ? err.message : String(err);
548
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
549
+ }
550
+ }
551
+ );
552
+ server2.tool(
553
+ "settings-usage-logs",
554
+ "Get paginated usage logs. Returns individual usage entries with action, credits, and timestamps.",
555
+ {
556
+ offset: z6.number().optional().describe("Offset for pagination"),
557
+ limit: z6.number().optional().describe("Number of entries to return")
558
+ },
559
+ async (params) => {
560
+ try {
561
+ const result = await client2.settings.getUsageLogs({
562
+ offset: params.offset,
563
+ limit: params.limit
564
+ });
565
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
566
+ } catch (err) {
567
+ const message = err instanceof Error ? err.message : String(err);
568
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
569
+ }
570
+ }
571
+ );
572
+ server2.tool(
573
+ "api-key-create",
574
+ "Create a new platform API key. The key value is shown ONLY in this response \u2014 save it immediately.",
575
+ {
576
+ label: z6.string().describe("Human-readable label for the API key"),
577
+ scopes: z6.array(z6.enum([
578
+ "jobs:write",
579
+ "jobs:read",
580
+ "sales:write",
581
+ "sales:read",
582
+ "sessions:read",
583
+ "sessions:write",
584
+ "settings:read",
585
+ "settings:write",
586
+ "upload:write",
587
+ "resume:read",
588
+ "resume:write",
589
+ "ats:write",
590
+ "webhook:read",
591
+ "webhook:write"
592
+ ])).describe("Permission scopes for the key")
593
+ },
594
+ async (params) => {
595
+ try {
596
+ const result = await client2.settings.createApiKey({
597
+ label: params.label,
598
+ scopes: params.scopes
599
+ });
600
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
601
+ } catch (err) {
602
+ const message = err instanceof Error ? err.message : String(err);
603
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
604
+ }
605
+ }
606
+ );
607
+ server2.tool(
608
+ "api-key-list",
609
+ "List all platform API keys. Returns key metadata (hash, name, scopes) \u2014 NOT the key values.",
610
+ {},
611
+ async () => {
612
+ try {
613
+ const result = await client2.settings.listApiKeys();
614
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
615
+ } catch (err) {
616
+ const message = err instanceof Error ? err.message : String(err);
617
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
618
+ }
619
+ }
620
+ );
621
+ server2.tool(
622
+ "api-key-revoke",
623
+ "Revoke (delete) a platform API key by its hash.",
624
+ {
625
+ hash: z6.string().describe("API key hash to revoke")
626
+ },
627
+ async (params) => {
628
+ try {
629
+ await client2.settings.revokeApiKey(params.hash);
630
+ return { content: [{ type: "text", text: JSON.stringify({ revoked: true, hash: params.hash }) }] };
631
+ } catch (err) {
632
+ const message = err instanceof Error ? err.message : String(err);
633
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
634
+ }
635
+ }
636
+ );
637
+ server2.tool(
638
+ "api-key-rotate",
639
+ "Rotate a platform API key \u2014 revokes the old key and returns a new one. Save the new key immediately.",
640
+ {
641
+ hash: z6.string().describe("API key hash to rotate")
642
+ },
643
+ async (params) => {
644
+ try {
645
+ const result = await client2.settings.rotateApiKey(params.hash);
646
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
647
+ } catch (err) {
648
+ const message = err instanceof Error ? err.message : String(err);
649
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
650
+ }
651
+ }
652
+ );
653
+ }
654
+
655
+ // src/tools/content.ts
656
+ import { z as z7 } from "zod";
657
+ function registerContentTools(server2, client2) {
658
+ server2.tool(
659
+ "content-save",
660
+ "Save a source document for use as context in AI generation. Returns success status.",
661
+ {
662
+ docType: z7.enum([
663
+ "original_cv",
664
+ "extensive_cv",
665
+ "cover_letter",
666
+ "cv_strategy",
667
+ "cover_letter_strategy",
668
+ "cold_email_strategy",
669
+ "recon_strategy",
670
+ "company_context"
671
+ ]).describe("Type of source document"),
672
+ content: z7.string().describe("Document content text")
673
+ },
674
+ async (params) => {
675
+ try {
676
+ const result = await client2.content.save({
677
+ docType: params.docType,
678
+ content: params.content
679
+ });
680
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
681
+ } catch (err) {
682
+ const message = err instanceof Error ? err.message : String(err);
683
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
684
+ }
685
+ }
686
+ );
687
+ server2.tool(
688
+ "content-delete-generation",
689
+ "Delete a generation and all its artifacts from a session.",
690
+ {
691
+ id: z7.string().describe("Generation ID to delete"),
692
+ sessionId: z7.string().describe("Session ID that owns the generation")
693
+ },
694
+ async (params) => {
695
+ try {
696
+ const httpClient = client2.content.httpClient;
697
+ const result = await httpClient.request(
698
+ `/content/generations/${encodeURIComponent(params.id)}`,
699
+ { method: "DELETE", query: { sessionId: params.sessionId } }
700
+ );
701
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
702
+ } catch (err) {
703
+ const message = err instanceof Error ? err.message : String(err);
704
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
705
+ }
706
+ }
707
+ );
708
+ }
709
+
710
+ // src/tools/shares.ts
711
+ import { z as z8 } from "zod";
712
+ function registerSharesTools(server2, client2) {
713
+ server2.tool(
714
+ "share-create",
715
+ "Create a shareable public link for a generation's artifacts. Returns a slug and public URL.",
716
+ {
717
+ sessionId: z8.string().describe("Session ID containing the generation"),
718
+ generationId: z8.string().describe("Generation ID to share")
719
+ },
720
+ async (params) => {
721
+ try {
722
+ const result = await client2.shares.create({
723
+ sessionId: params.sessionId,
724
+ generationId: params.generationId
725
+ });
726
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
727
+ } catch (err) {
728
+ const message = err instanceof Error ? err.message : String(err);
729
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
730
+ }
731
+ }
732
+ );
733
+ server2.tool(
734
+ "share-stats",
735
+ "Get statistics about your shared links (view counts, etc.).",
736
+ {},
737
+ async () => {
738
+ try {
739
+ const result = await client2.shares.getStats();
740
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
741
+ } catch (err) {
742
+ const message = err instanceof Error ? err.message : String(err);
743
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
744
+ }
745
+ }
746
+ );
747
+ server2.tool(
748
+ "share-get-public",
749
+ "Get a publicly shared resource by its slug.",
750
+ {
751
+ slug: z8.string().describe("Public share slug")
752
+ },
753
+ async (params) => {
754
+ try {
755
+ const result = await client2.shares.getPublic(params.slug);
756
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
757
+ } catch (err) {
758
+ const message = err instanceof Error ? err.message : String(err);
759
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
760
+ }
761
+ }
762
+ );
763
+ }
764
+
765
+ // src/tools/documents.ts
766
+ import { z as z9 } from "zod";
767
+ function registerDocumentTools(server2, client2) {
768
+ server2.tool(
769
+ "document-download",
770
+ "Download a document by its storage path. Returns the file content or a download URL.",
771
+ {
772
+ path: z9.string().describe("Document storage path")
773
+ },
774
+ async (params) => {
775
+ try {
776
+ const response = await client2.documents.download(params.path);
777
+ const text = await response.text();
778
+ return { content: [{ type: "text", text }] };
779
+ } catch (err) {
780
+ const message = err instanceof Error ? err.message : String(err);
781
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
782
+ }
783
+ }
784
+ );
785
+ }
786
+
787
+ // src/index.ts
788
+ var apiKey = process.env.LLMC_API_KEY;
789
+ if (!apiKey) {
790
+ console.error("LLMC_API_KEY environment variable is required");
791
+ process.exit(1);
792
+ }
793
+ var client = new LLMConveyors({ apiKey });
794
+ var server = new McpServer({
795
+ name: "llmconveyors",
796
+ version: "0.1.0"
797
+ });
798
+ registerAgentTools(server, client);
799
+ registerAtsTools(server, client);
800
+ registerResumeTools(server, client);
801
+ registerUploadTools(server, client);
802
+ registerSessionTools(server, client);
803
+ registerSettingsTools(server, client);
804
+ registerContentTools(server, client);
805
+ registerSharesTools(server, client);
806
+ registerDocumentTools(server, client);
807
+ var transport = new StdioServerTransport();
808
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "llmconveyors-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server that connects AI agents to LLM Conveyors — run Job Hunter, B2B Sales, and other AI agents from Claude, Cursor, or any MCP client",
5
+ "type": "module",
6
+ "bin": {
7
+ "llmconveyors-mcp": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "files": [
12
+ "dist",
13
+ "LICENSE",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsup src/index.ts --format esm --dts --clean",
18
+ "dev": "tsup src/index.ts --format esm --watch",
19
+ "typecheck": "tsc --noEmit",
20
+ "start": "node dist/index.js",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "mcp-server",
26
+ "model-context-protocol",
27
+ "llm-conveyors",
28
+ "ai-agents",
29
+ "claude",
30
+ "cursor",
31
+ "job-hunting",
32
+ "resume",
33
+ "ats",
34
+ "b2b-sales",
35
+ "cold-email",
36
+ "ai-tools"
37
+ ],
38
+ "author": "LLM Conveyors <hello@llmconveyors.com> (https://llmconveyors.com)",
39
+ "license": "MIT",
40
+ "homepage": "https://github.com/llmconveyors/llmconveyors-mcp#readme",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/llmconveyors/llmconveyors-mcp.git"
44
+ },
45
+ "bugs": {
46
+ "url": "https://github.com/llmconveyors/llmconveyors-mcp/issues"
47
+ },
48
+ "engines": {
49
+ "node": ">=18.0.0"
50
+ },
51
+ "dependencies": {
52
+ "@modelcontextprotocol/sdk": "^1.12.1",
53
+ "llmconveyors": "^0.1.0",
54
+ "zod": "^3.24.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "^20.11.0",
58
+ "tsup": "^8.0.0",
59
+ "typescript": "^5.5.0"
60
+ }
61
+ }