@juicedresume/mcp 0.2.1 → 0.3.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 +50 -5
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -228,6 +228,11 @@ var Styling = z.object({
|
|
|
228
228
|
// em x 1000 (we treat as px-ish)
|
|
229
229
|
headingsLine: z.enum(["none", "underline", "left-bar", "boxed"]).default("none"),
|
|
230
230
|
spacing: z.enum(["compact", "regular", "spacious"]).default("regular"),
|
|
231
|
+
// Fine-grained, MS-Word-style spacing control (px @96dpi). Drive the gap
|
|
232
|
+
// between sections and between items within a section. Defaults match the
|
|
233
|
+
// previous hardcoded single-column values (mb-4 = 16px, ~10px item gaps).
|
|
234
|
+
sectionSpacing: z.number().default(16),
|
|
235
|
+
itemSpacing: z.number().default(10),
|
|
231
236
|
bulletGlyph: z.enum(["disc", "dash", "arrow", "square", "check", "none"]).default("disc"),
|
|
232
237
|
layout: z.enum(["single", "sidebar-left", "sidebar-right", "two-column"]).default("single"),
|
|
233
238
|
subtitlePlacement: z.enum(["below", "inline", "right"]).default("below"),
|
|
@@ -237,9 +242,9 @@ var Styling = z.object({
|
|
|
237
242
|
photoSize: z.number().default(72),
|
|
238
243
|
// Page margins in px @96dpi (used by the preview slicer + PDF export).
|
|
239
244
|
// Defaults match the previous hardcoded values: ~0.5in top/bottom, ~0.6in sides.
|
|
240
|
-
pageMarginX: z.number().default(
|
|
241
|
-
pageMarginTop: z.number().default(
|
|
242
|
-
pageMarginBottom: z.number().default(
|
|
245
|
+
pageMarginX: z.number().default(30),
|
|
246
|
+
pageMarginTop: z.number().default(30),
|
|
247
|
+
pageMarginBottom: z.number().default(30)
|
|
243
248
|
});
|
|
244
249
|
var Locale = z.object({
|
|
245
250
|
language: z.string().default("en-GB"),
|
|
@@ -2894,10 +2899,38 @@ import path from "path";
|
|
|
2894
2899
|
var ROOT = path.resolve(process.env.JUICED_DATA_DIR || path.join(process.cwd(), "../..", "data"));
|
|
2895
2900
|
var RESUMES = path.join(ROOT, "resumes");
|
|
2896
2901
|
var VERSIONS = path.join(ROOT, "versions");
|
|
2902
|
+
var CLOUD_TOKEN = (process.env.JUICEDRESUME_TOKEN || "").trim();
|
|
2903
|
+
var CLOUD_URL = (process.env.JUICEDRESUME_URL || "https://juicedresume.com").replace(/\/+$/, "");
|
|
2904
|
+
var CLOUD = CLOUD_TOKEN.length > 0;
|
|
2897
2905
|
async function ensure(dir) {
|
|
2898
2906
|
await fs.mkdir(dir, { recursive: true });
|
|
2899
2907
|
}
|
|
2908
|
+
async function cloudReq(method, p, body) {
|
|
2909
|
+
let res;
|
|
2910
|
+
try {
|
|
2911
|
+
res = await fetch(`${CLOUD_URL}/api/mcp${p}`, {
|
|
2912
|
+
method,
|
|
2913
|
+
headers: {
|
|
2914
|
+
authorization: `Bearer ${CLOUD_TOKEN}`,
|
|
2915
|
+
...body !== void 0 ? { "content-type": "application/json" } : {}
|
|
2916
|
+
},
|
|
2917
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
2918
|
+
});
|
|
2919
|
+
} catch (e) {
|
|
2920
|
+
throw new Error(`JuicedResume: could not reach ${CLOUD_URL} (${e?.message || e}). Check JUICEDRESUME_URL / network.`);
|
|
2921
|
+
}
|
|
2922
|
+
if (res.status === 401) throw new Error("JuicedResume: invalid or missing JUICEDRESUME_TOKEN \u2014 rotate it in Settings \u2192 AI on juicedresume.com.");
|
|
2923
|
+
if (res.status === 402) throw new Error("JuicedResume: your plan does not include MCP/cloud access (Basic or Pro required).");
|
|
2924
|
+
if (res.status === 404) return null;
|
|
2925
|
+
if (!res.ok) {
|
|
2926
|
+
const detail = await res.text().catch(() => "");
|
|
2927
|
+
throw new Error(`JuicedResume cloud ${method} ${p} failed: ${res.status} ${detail}`);
|
|
2928
|
+
}
|
|
2929
|
+
const ct = res.headers.get("content-type") || "";
|
|
2930
|
+
return ct.includes("application/json") ? res.json() : null;
|
|
2931
|
+
}
|
|
2900
2932
|
async function listResumes() {
|
|
2933
|
+
if (CLOUD) return await cloudReq("GET", "/resumes") ?? [];
|
|
2901
2934
|
await ensure(RESUMES);
|
|
2902
2935
|
const files = await fs.readdir(RESUMES);
|
|
2903
2936
|
const out = [];
|
|
@@ -2912,6 +2945,10 @@ async function listResumes() {
|
|
|
2912
2945
|
return out.sort((a, b) => a.updatedAt < b.updatedAt ? 1 : -1);
|
|
2913
2946
|
}
|
|
2914
2947
|
async function getResume(id) {
|
|
2948
|
+
if (CLOUD) {
|
|
2949
|
+
const data = await cloudReq("GET", `/resumes/${id}`);
|
|
2950
|
+
return data ? Resume.parse(data) : null;
|
|
2951
|
+
}
|
|
2915
2952
|
try {
|
|
2916
2953
|
const raw = await fs.readFile(path.join(RESUMES, `${id}.json`), "utf8");
|
|
2917
2954
|
return Resume.parse(JSON.parse(raw));
|
|
@@ -2920,9 +2957,13 @@ async function getResume(id) {
|
|
|
2920
2957
|
}
|
|
2921
2958
|
}
|
|
2922
2959
|
async function saveResume(r) {
|
|
2960
|
+
const next = { ...r, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2961
|
+
if (CLOUD) {
|
|
2962
|
+
await cloudReq("PUT", `/resumes/${r.id}`, next);
|
|
2963
|
+
return next;
|
|
2964
|
+
}
|
|
2923
2965
|
await ensure(RESUMES);
|
|
2924
2966
|
await ensure(path.join(VERSIONS, r.id));
|
|
2925
|
-
const next = { ...r, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2926
2967
|
const file = path.join(RESUMES, `${r.id}.json`);
|
|
2927
2968
|
try {
|
|
2928
2969
|
const prev = await fs.readFile(file, "utf8");
|
|
@@ -2945,6 +2986,10 @@ async function createBlankResume(name = "Untitled") {
|
|
|
2945
2986
|
return await saveResume(blank);
|
|
2946
2987
|
}
|
|
2947
2988
|
async function deleteResume(id) {
|
|
2989
|
+
if (CLOUD) {
|
|
2990
|
+
await cloudReq("DELETE", `/resumes/${id}`);
|
|
2991
|
+
return;
|
|
2992
|
+
}
|
|
2948
2993
|
try {
|
|
2949
2994
|
await fs.unlink(path.join(RESUMES, `${id}.json`));
|
|
2950
2995
|
} catch {
|
|
@@ -3280,7 +3325,7 @@ function runOnce(cmd, args, cwd) {
|
|
|
3280
3325
|
import { promises as fs3 } from "fs";
|
|
3281
3326
|
import path3 from "path";
|
|
3282
3327
|
var server = new Server(
|
|
3283
|
-
{ name: "juiced-resume", version: "0.
|
|
3328
|
+
{ name: "juiced-resume", version: "0.3.0" },
|
|
3284
3329
|
{ capabilities: { tools: {} } }
|
|
3285
3330
|
);
|
|
3286
3331
|
var idArg = z2.object({ id: z2.string() });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juicedresume/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Model Context Protocol server for JuicedResume — score, tailor, and edit your resume from any MCP client (Claude Code, Claude Desktop, Cursor).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"@juiced/scoring": "*",
|
|
49
49
|
"@types/node": "^22.9.0",
|
|
50
50
|
"esbuild": "^0.24.2",
|
|
51
|
+
"esbuild-wasm": "^0.24.2",
|
|
51
52
|
"tsx": "^4.19.2",
|
|
52
53
|
"typescript": "^5.6.3"
|
|
53
54
|
},
|