Skip to content

Commit 2cc5ec7

Browse files
feat: route Geometra MCP through JobForge launcher
1 parent 9db0352 commit 2cc5ec7

8 files changed

Lines changed: 106 additions & 7 deletions

File tree

bin/create-job-forge.mjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,11 @@ const opencodeCfg = {
240240
mcp: {
241241
geometra: {
242242
type: 'local',
243-
command: ['npx', '-y', '@geometra/mcp@1.61.3'],
243+
command: ['npx', '--no-install', 'job-forge', 'mcp:geometra'],
244+
environment: {
245+
GEOMETRA_STEALTH: '1',
246+
GEOMETRA_BROWSER: 'stealth',
247+
},
244248
enabled: true,
245249
},
246250
gmail: {

bin/geometra-mcp-launcher.mjs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env node
2+
3+
import { spawn } from 'node:child_process';
4+
import { existsSync } from 'node:fs';
5+
import { dirname, resolve } from 'node:path';
6+
import { fileURLToPath } from 'node:url';
7+
8+
const DEFAULT_FALLBACK_PACKAGE = '@geometra/mcp@1.61.3';
9+
const RESOLVE_ONLY_FLAG = '--job-forge-resolve-target';
10+
11+
function normalizeEnv(value) {
12+
if (typeof value !== 'string') return null;
13+
const trimmed = value.trim();
14+
return trimmed.length > 0 ? trimmed : null;
15+
}
16+
17+
function resolveExplicitPath(rawPath) {
18+
const resolvedPath = resolve(rawPath);
19+
if (!existsSync(resolvedPath)) {
20+
throw new Error(`JOB_FORGE_GEOMETRA_MCP_PATH points to a missing file: ${resolvedPath}`);
21+
}
22+
return resolvedPath;
23+
}
24+
25+
export function resolveGeometraMcpLaunchTarget() {
26+
const explicitPath = normalizeEnv(process.env.JOB_FORGE_GEOMETRA_MCP_PATH);
27+
if (explicitPath) {
28+
const resolvedPath = resolveExplicitPath(explicitPath);
29+
return {
30+
source: 'env-path',
31+
command: process.execPath,
32+
args: [resolvedPath],
33+
resolvedPath,
34+
};
35+
}
36+
37+
const scriptDir = dirname(fileURLToPath(import.meta.url));
38+
const siblingRepoPath = resolve(scriptDir, '../../geometra/mcp/dist/index.js');
39+
if (existsSync(siblingRepoPath)) {
40+
return {
41+
source: 'sibling-repo',
42+
command: process.execPath,
43+
args: [siblingRepoPath],
44+
resolvedPath: siblingRepoPath,
45+
};
46+
}
47+
48+
const packageSpec = normalizeEnv(process.env.JOB_FORGE_GEOMETRA_MCP_PACKAGE) ?? DEFAULT_FALLBACK_PACKAGE;
49+
return {
50+
source: 'npm-package',
51+
command: 'npx',
52+
args: ['-y', packageSpec],
53+
packageSpec,
54+
};
55+
}
56+
57+
export async function main(argv = process.argv.slice(2)) {
58+
const target = resolveGeometraMcpLaunchTarget();
59+
60+
if (argv.length === 1 && argv[0] === RESOLVE_ONLY_FLAG) {
61+
process.stdout.write(`${JSON.stringify(target, null, 2)}\n`);
62+
return;
63+
}
64+
65+
const child = spawn(target.command, [...target.args, ...argv], {
66+
stdio: 'inherit',
67+
env: process.env,
68+
});
69+
70+
child.on('error', (error) => {
71+
console.error(error instanceof Error ? error.message : String(error));
72+
process.exit(1);
73+
});
74+
75+
child.on('exit', (code, signal) => {
76+
if (signal) {
77+
process.kill(process.pid, signal);
78+
return;
79+
}
80+
process.exit(code ?? 0);
81+
});
82+
}
83+
84+
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
85+
void main();
86+
}

bin/job-forge.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* normalize Run normalize-statuses.mjs
1717
* pdf Run generate-pdf.mjs
1818
* sync-check Run cv-sync-check.mjs
19+
* mcp:geometra Launch Geometra MCP via JobForge's local/npm resolver
1920
* tokens Run scripts/token-usage-report.mjs
2021
* trace:* Inspect local agent transcripts via iso-trace
2122
* telemetry:* Summarize JobForge pipeline status from traces + tracker files
@@ -55,6 +56,7 @@ const commands = {
5556
normalize: 'normalize-statuses.mjs',
5657
pdf: 'generate-pdf.mjs',
5758
'sync-check': 'cv-sync-check.mjs',
59+
'mcp:geometra': 'bin/geometra-mcp-launcher.mjs',
5860
tokens: 'scripts/token-usage-report.mjs',
5961
sync: 'bin/sync.mjs',
6062
// Deterministic helpers — agents call these instead of deriving values
@@ -244,6 +246,7 @@ Commands:
244246
normalize Normalize status values across the tracker
245247
pdf Generate ATS-optimized CV PDF from cv.md
246248
sync-check Lint: verify cv.md and profile.yml are filled in
249+
mcp:geometra Launch Geometra MCP, preferring local JobForge/Geometra dev wiring
247250
tokens Show opencode token usage and cost by session/day
248251
trace Pass through to iso-trace (e.g. job-forge trace sources)
249252
trace:list List recent local agent sessions (defaults: --since 7d --cwd project)

docs/SETUP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ Use it to identify which sessions or models are consuming the most tokens. The `
214214
`sync-check` requires `cv.md` and `config/profile.yml` with the fields checked in `cv-sync-check.mjs`. Until you finish the profile and CV steps, that is normal.
215215

216216
**PDF generation fails**
217-
The scaffolded `opencode.json` already registers Geometra MCP; if it's not running, check `opencode mcp list` and verify the scaffolded config under the `mcp.geometra` key — its `command` MUST be `["npx", "-y", "@geometra/mcp@1.61.3"]` and `enabled: true`. Geometra manages Chromium via its built-in proxy. JobForge passes `stealth: true` for portal sessions so Geometra launches CloakBrowser's patched Chromium. For standalone CLI usage (outside opencode), `generate-pdf.mjs` also works with standalone Playwright/Chromium — install with `npx playwright install chromium`.
217+
The scaffolded `opencode.json` already registers Geometra MCP; if it's not running, check `opencode mcp list` and verify the scaffolded config under the `mcp.geometra` key — its `command` MUST be `["npx", "--no-install", "job-forge", "mcp:geometra"]`, `enabled: true`, and its `environment` should include `GEOMETRA_STEALTH=1` (or equivalently `GEOMETRA_BROWSER=stealth`) so proxy-backed portal sessions default to CloakBrowser's patched Chromium. `job-forge mcp:geometra` prefers `JOB_FORGE_GEOMETRA_MCP_PATH` when set, otherwise a sibling `../geometra/mcp/dist/index.js` checkout for local JobForge development, and otherwise falls back to the pinned npm package. Geometra manages Chromium via its built-in proxy. JobForge still passes `stealth: true` for portal sessions explicitly; the env block keeps the default aligned for auto-spawned sessions and local debugging. For standalone CLI usage (outside opencode), `generate-pdf.mjs` also works with standalone Playwright/Chromium — install with `npx playwright install chromium`.
218218

219219
**Symlinks are missing or pointing to a stale path**
220220
Run `npx job-forge sync` (or `npm run sync`) to recreate them. This happens if you move the project directory after installing, or if `postinstall` didn't run (rare — check `npm install` output for errors).

iso/mcp.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
"servers": {
33
"geometra": {
44
"command": "npx",
5-
"args": ["-y", "@geometra/mcp@1.61.3"]
5+
"args": ["--no-install", "job-forge", "mcp:geometra"],
6+
"env": {
7+
"GEOMETRA_STEALTH": "1",
8+
"GEOMETRA_BROWSER": "stealth"
9+
}
610
},
711
"gmail": {
812
"command": "npx",

modes/reference-portals.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ The Geometra MCP partitions its reusable-proxy pool by proxy identity and browse
128128
"geometra": {
129129
"type": "stdio",
130130
"command": "npx",
131-
"args": ["-y", "@geometra/mcp@1.61.3"]
131+
"args": ["--no-install", "job-forge", "mcp:geometra"]
132132
},
133133
"gmail": {
134134
"type": "stdio",
@@ -139,4 +139,6 @@ The Geometra MCP partitions its reusable-proxy pool by proxy identity and browse
139139
}
140140
```
141141

142+
`job-forge mcp:geometra` resolves Geometra in this order: `JOB_FORGE_GEOMETRA_MCP_PATH` if set, then a sibling local `../geometra/mcp/dist/index.js` checkout for maintainers working across both repos, then the pinned npm fallback.
143+
142144
To check or modify MCP settings, edit `opencode.json` in the project root.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "job-forge",
3-
"version": "2.14.39",
3+
"version": "2.14.40",
44
"description": "AI-powered job search pipeline built on opencode",
55
"type": "module",
66
"bin": {

0 commit comments

Comments
 (0)