@linkegringo/mcp 1.0.8 → 1.0.9
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.d.ts +35 -1
- package/dist/index.js +226 -5
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -467,4 +467,38 @@ declare function handleSubmitJobResult(input: SubmitJobResultInput): Promise<{
|
|
|
467
467
|
};
|
|
468
468
|
}>;
|
|
469
469
|
|
|
470
|
-
|
|
470
|
+
declare const watchLinkeGringoInputSchema: z.ZodObject<{
|
|
471
|
+
timeoutSeconds: z.ZodOptional<z.ZodNumber>;
|
|
472
|
+
bridgeUrl: z.ZodOptional<z.ZodString>;
|
|
473
|
+
}, "strip", z.ZodTypeAny, {
|
|
474
|
+
timeoutSeconds?: number | undefined;
|
|
475
|
+
bridgeUrl?: string | undefined;
|
|
476
|
+
}, {
|
|
477
|
+
timeoutSeconds?: number | undefined;
|
|
478
|
+
bridgeUrl?: string | undefined;
|
|
479
|
+
}>;
|
|
480
|
+
type WatchLinkeGringoInput = z.infer<typeof watchLinkeGringoInputSchema>;
|
|
481
|
+
declare function waitForNextJob(bridgeUrl: string, timeoutMs: number): Promise<BridgeJob | null>;
|
|
482
|
+
declare function handleWatchLinkeGringo(input?: WatchLinkeGringoInput): Promise<{
|
|
483
|
+
content: {
|
|
484
|
+
type: "text";
|
|
485
|
+
text: string;
|
|
486
|
+
}[];
|
|
487
|
+
structuredData: {
|
|
488
|
+
jobFound: boolean;
|
|
489
|
+
job: BridgeJob<any, any>;
|
|
490
|
+
};
|
|
491
|
+
} | {
|
|
492
|
+
content: {
|
|
493
|
+
type: "text";
|
|
494
|
+
text: string;
|
|
495
|
+
}[];
|
|
496
|
+
structuredData: {
|
|
497
|
+
status: string;
|
|
498
|
+
timeoutSeconds: number;
|
|
499
|
+
pendingCount: number;
|
|
500
|
+
serverOnline: boolean;
|
|
501
|
+
};
|
|
502
|
+
}>;
|
|
503
|
+
|
|
504
|
+
export { type AuditIssue, type AuditProfileInput, type BridgeJob, type BridgeServerOptions, type ConvertToXyzBulletInput, type GenerateHeadlineInput, type GetPendingJobInput, type InstallResult, type InstallerOptions, type JobStatus, type JobType, type McpTarget, type SimulateRecruiterSearchInput, type SparseExperience, type SubmitJobResultInput, type WatchLinkeGringoInput, type WatcherOptions, auditProfileInputSchema, bridgeJobStore, completeRemoteOrLocalJob, convertToXyzBulletInputSchema, createBridgeHttpServer, createLinkeGringoMcpServer, detectSparseExperiences, failRemoteOrLocalJob, formatGoogleXyzBullet, generateHeadlineInputSchema, getExperienceBulletCount, getMcpConfigsForSystem, getPendingJobInputSchema, getRemoteOrLocalPendingJob, handleAuditProfile, handleConvertToXyzBullet, handleGenerateHeadline, handleGetPendingJob, handleSimulateRecruiterSearch, handleSubmitJobResult, handleWatchLinkeGringo, installMcpServerConfig, parseArgs, runInstaller, simulateRecruiterSearchInputSchema, startBridgeServer, startBridgeWatcher, stopBridgeServer, submitJobResultInputSchema, waitForNextJob, watchLinkeGringoInputSchema };
|
package/dist/index.js
CHANGED
|
@@ -1396,6 +1396,214 @@ O front-end em \`localhost:5173\` acabou de receber a resposta formatada e atual
|
|
|
1396
1396
|
}
|
|
1397
1397
|
}
|
|
1398
1398
|
|
|
1399
|
+
// src/tools/watch-linkegringo.ts
|
|
1400
|
+
import { z as z13 } from "zod";
|
|
1401
|
+
import http from "http";
|
|
1402
|
+
var watchLinkeGringoInputSchema = z13.object({
|
|
1403
|
+
timeoutSeconds: z13.number().optional().describe(
|
|
1404
|
+
"Tempo m\xE1ximo em segundos para aguardar a chegada de um novo job da interface web do LinkeGringo (padr\xE3o: 60s, m\xE1x: 300s). Se j\xE1 houver um job na fila, retorna imediatamente."
|
|
1405
|
+
),
|
|
1406
|
+
bridgeUrl: z13.string().optional().describe("URL base do bridge HTTP local do LinkeGringo (padr\xE3o: http://127.0.0.1:5174)")
|
|
1407
|
+
});
|
|
1408
|
+
async function waitForNextJob(bridgeUrl, timeoutMs) {
|
|
1409
|
+
const localPending = bridgeJobStore.getPendingJob();
|
|
1410
|
+
if (localPending) {
|
|
1411
|
+
return localPending;
|
|
1412
|
+
}
|
|
1413
|
+
return new Promise((resolve) => {
|
|
1414
|
+
let resolved = false;
|
|
1415
|
+
let req = null;
|
|
1416
|
+
let timer = null;
|
|
1417
|
+
const cleanup = () => {
|
|
1418
|
+
if (timer) {
|
|
1419
|
+
clearTimeout(timer);
|
|
1420
|
+
timer = null;
|
|
1421
|
+
}
|
|
1422
|
+
if (req) {
|
|
1423
|
+
try {
|
|
1424
|
+
req.destroy();
|
|
1425
|
+
} catch {
|
|
1426
|
+
}
|
|
1427
|
+
req = null;
|
|
1428
|
+
}
|
|
1429
|
+
bridgeJobStore.off("job:created", onLocalJob);
|
|
1430
|
+
};
|
|
1431
|
+
const done = (job) => {
|
|
1432
|
+
if (resolved) return;
|
|
1433
|
+
resolved = true;
|
|
1434
|
+
cleanup();
|
|
1435
|
+
resolve(job);
|
|
1436
|
+
};
|
|
1437
|
+
const onLocalJob = (job) => {
|
|
1438
|
+
done(job);
|
|
1439
|
+
};
|
|
1440
|
+
bridgeJobStore.on("job:created", onLocalJob);
|
|
1441
|
+
timer = setTimeout(() => {
|
|
1442
|
+
done(null);
|
|
1443
|
+
}, timeoutMs);
|
|
1444
|
+
try {
|
|
1445
|
+
const parsed = new URL("/api/jobs/stream", bridgeUrl);
|
|
1446
|
+
req = http.get(
|
|
1447
|
+
{
|
|
1448
|
+
hostname: parsed.hostname,
|
|
1449
|
+
port: parsed.port || 5174,
|
|
1450
|
+
path: parsed.pathname,
|
|
1451
|
+
headers: {
|
|
1452
|
+
Accept: "text/event-stream",
|
|
1453
|
+
"Cache-Control": "no-cache",
|
|
1454
|
+
Connection: "keep-alive"
|
|
1455
|
+
}
|
|
1456
|
+
},
|
|
1457
|
+
(res) => {
|
|
1458
|
+
if (res.statusCode !== 200) {
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
let buffer = "";
|
|
1462
|
+
res.setEncoding("utf8");
|
|
1463
|
+
res.on("data", (chunk) => {
|
|
1464
|
+
buffer += chunk;
|
|
1465
|
+
const lines = buffer.split("\n\n");
|
|
1466
|
+
buffer = lines.pop() || "";
|
|
1467
|
+
for (const block of lines) {
|
|
1468
|
+
const trimmed = block.trim();
|
|
1469
|
+
if (!trimmed.startsWith("data:")) continue;
|
|
1470
|
+
const jsonStr = trimmed.replace(/^data:\s*/, "");
|
|
1471
|
+
try {
|
|
1472
|
+
const event = JSON.parse(jsonStr);
|
|
1473
|
+
if (event.type === "connected") {
|
|
1474
|
+
if (event.pendingCount > 0) {
|
|
1475
|
+
getRemoteOrLocalPendingJob().then((pending) => {
|
|
1476
|
+
if (pending) done(pending);
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
} else if (event.type === "job_created" && event.job) {
|
|
1480
|
+
done(event.job);
|
|
1481
|
+
}
|
|
1482
|
+
} catch {
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
res.on("end", () => {
|
|
1487
|
+
if (!resolved) {
|
|
1488
|
+
const pending = bridgeJobStore.getPendingJob();
|
|
1489
|
+
if (pending) {
|
|
1490
|
+
done(pending);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
res.on("error", () => {
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
);
|
|
1498
|
+
req.on("error", () => {
|
|
1499
|
+
});
|
|
1500
|
+
} catch {
|
|
1501
|
+
}
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
function formatJobResponse(job) {
|
|
1505
|
+
let instructions = "";
|
|
1506
|
+
let details = "";
|
|
1507
|
+
if (job.type === "parse_and_diagnose") {
|
|
1508
|
+
const p = job.payload || {};
|
|
1509
|
+
details = `
|
|
1510
|
+
- **Tipo de A\xE7\xE3o**: Extra\xE7\xE3o Multimodal e Diagn\xF3stico de Perfil (parseAndDiagnose)
|
|
1511
|
+
- **Cargo Almejado**: ${p.targetRole || "N\xE3o especificado"}
|
|
1512
|
+
- **Possui PDF Base64**: ${Boolean(p.pdfBase64)} (${p.pdfBase64 ? Math.round(p.pdfBase64.length / 1024) + " KB" : "N/A"})
|
|
1513
|
+
- **Possui Texto**: ${Boolean(p.pdfText)}
|
|
1514
|
+
`.trim();
|
|
1515
|
+
instructions = `
|
|
1516
|
+
### Pr\xF3ximos Passos Obrigat\xF3rios:
|
|
1517
|
+
1. Extraia e analise o perfil estruturado (\`Profile\`) e o diagn\xF3stico (\`ProfileReview\`) com notas (0-100), gargalos de triagem de recrutadores e lacunas de stack.
|
|
1518
|
+
2. Envie o resultado de volta para o navegador chamando a tool \`submit_job_result\`:
|
|
1519
|
+
\`\`\`json
|
|
1520
|
+
{
|
|
1521
|
+
"jobId": "${job.id}",
|
|
1522
|
+
"result": {
|
|
1523
|
+
"profile": { ... },
|
|
1524
|
+
"review": { ... }
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
\`\`\`
|
|
1528
|
+
3. Ap\xF3s enviar o resultado, execute a tool \`watch_linkegringo\` novamente para aguardar a pr\xF3xima a\xE7\xE3o do usu\xE1rio (entrevista t\xE9cnica ou reescrita).
|
|
1529
|
+
`.trim();
|
|
1530
|
+
} else if (job.type === "generate_interview") {
|
|
1531
|
+
const p = job.payload || {};
|
|
1532
|
+
details = `
|
|
1533
|
+
- **Tipo de A\xE7\xE3o**: Gera\xE7\xE3o de Perguntas de Entrevista T\xE9cnica
|
|
1534
|
+
- **Candidato**: ${p.profile?.name || "N\xE3o informado"}
|
|
1535
|
+
- **Cargo Almejado**: ${p.objective?.primaryRole || "N\xE3o informado"}
|
|
1536
|
+
`.trim();
|
|
1537
|
+
instructions = `
|
|
1538
|
+
### Pr\xF3ximos Passos Obrigat\xF3rios:
|
|
1539
|
+
1. Gere as perguntas t\xE9cnicas focando nos gargalos e lacunas identificados.
|
|
1540
|
+
2. Envie o resultado chamando \`submit_job_result({ jobId: "${job.id}", result: { questions: [...] } })\`.
|
|
1541
|
+
3. Rearme a escuta chamando \`watch_linkegringo\` para aguardar as respostas do candidato.
|
|
1542
|
+
`.trim();
|
|
1543
|
+
} else if (job.type === "generate_rewritten_profile") {
|
|
1544
|
+
const p = job.payload || {};
|
|
1545
|
+
details = `
|
|
1546
|
+
- **Tipo de A\xE7\xE3o**: Reescrita Completa de Perfil com Bullets Google XYZ
|
|
1547
|
+
- **Candidato**: ${p.profile?.name || "N\xE3o informado"}
|
|
1548
|
+
- **Fatos Confirmados na Entrevista**: ${p.confirmedFacts?.length || 0}
|
|
1549
|
+
`.trim();
|
|
1550
|
+
instructions = `
|
|
1551
|
+
### Pr\xF3ximos Passos Obrigat\xF3rios:
|
|
1552
|
+
1. Reescreva o perfil aplicando a f\xF3rmula Google XYZ (Accomplished [X], measured by [Y], by doing [Z]) e Headline $\\le$ 160 caracteres.
|
|
1553
|
+
2. Envie o resultado chamando \`submit_job_result({ jobId: "${job.id}", result: { rewritten: { ... } } })\`.
|
|
1554
|
+
3. Rearme a escuta chamando \`watch_linkegringo\`.
|
|
1555
|
+
`.trim();
|
|
1556
|
+
}
|
|
1557
|
+
return {
|
|
1558
|
+
content: [
|
|
1559
|
+
{
|
|
1560
|
+
type: "text",
|
|
1561
|
+
text: `\u{1F680} **NOVO JOB RECEBIDO DO LINKEGRINGO**: \`${job.id}\`
|
|
1562
|
+
|
|
1563
|
+
${details}
|
|
1564
|
+
|
|
1565
|
+
${instructions}`
|
|
1566
|
+
}
|
|
1567
|
+
],
|
|
1568
|
+
structuredData: {
|
|
1569
|
+
jobFound: true,
|
|
1570
|
+
job
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
async function handleWatchLinkeGringo(input = {}) {
|
|
1575
|
+
const timeoutSeconds = Math.min(Math.max(input.timeoutSeconds ?? 60, 0.1), 300);
|
|
1576
|
+
const timeoutMs = timeoutSeconds * 1e3;
|
|
1577
|
+
const bridgeUrl = input.bridgeUrl || "http://127.0.0.1:5174";
|
|
1578
|
+
const immediateJob = await getRemoteOrLocalPendingJob();
|
|
1579
|
+
if (immediateJob) {
|
|
1580
|
+
bridgeJobStore.markProcessing(immediateJob.id);
|
|
1581
|
+
return formatJobResponse(immediateJob);
|
|
1582
|
+
}
|
|
1583
|
+
const job = await waitForNextJob(bridgeUrl, timeoutMs);
|
|
1584
|
+
if (!job) {
|
|
1585
|
+
return {
|
|
1586
|
+
content: [
|
|
1587
|
+
{
|
|
1588
|
+
type: "text",
|
|
1589
|
+
text: `\u{1F7E2} **Modo Watch do LinkeGringo Ativo** (Bridge porta 5174)
|
|
1590
|
+
|
|
1591
|
+
Nenhum job recebido nos \xFAltimos ${timeoutSeconds} segundos.
|
|
1592
|
+
A interface web est\xE1 conectada ao Bridge. Assim que o usu\xE1rio clicar em **Analisar com Agente MCP**, **Iniciar Entrevista** ou **Gerar Perfil Reescrito**, execute a tool \`watch_linkegringo\` novamente para receber e processar os dados!`
|
|
1593
|
+
}
|
|
1594
|
+
],
|
|
1595
|
+
structuredData: {
|
|
1596
|
+
status: "waiting",
|
|
1597
|
+
timeoutSeconds,
|
|
1598
|
+
pendingCount: bridgeJobStore.getPendingCount(),
|
|
1599
|
+
serverOnline: true
|
|
1600
|
+
}
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
bridgeJobStore.markProcessing(job.id);
|
|
1604
|
+
return formatJobResponse(job);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1399
1607
|
// src/server.ts
|
|
1400
1608
|
function createLinkeGringoMcpServer() {
|
|
1401
1609
|
const server = new McpServer({
|
|
@@ -1462,6 +1670,16 @@ function createLinkeGringoMcpServer() {
|
|
|
1462
1670
|
return await handleSubmitJobResult(args);
|
|
1463
1671
|
}
|
|
1464
1672
|
);
|
|
1673
|
+
server.registerTool(
|
|
1674
|
+
"watch_linkegringo",
|
|
1675
|
+
{
|
|
1676
|
+
description: "Inicia a escuta ativa por jobs enviados pela interface web do LinkeGringo (upload de perfil para diagn\xF3stico, entrevista t\xE9cnica, reescrita de perfil). Aguarda o job e o retorna imediatamente para processamento.",
|
|
1677
|
+
inputSchema: watchLinkeGringoInputSchema.shape
|
|
1678
|
+
},
|
|
1679
|
+
async (args) => {
|
|
1680
|
+
return await handleWatchLinkeGringo(args);
|
|
1681
|
+
}
|
|
1682
|
+
);
|
|
1465
1683
|
server.registerResource(
|
|
1466
1684
|
"guidelines",
|
|
1467
1685
|
"linkegringo://guidelines",
|
|
@@ -1679,7 +1897,7 @@ function runInstaller(args = process.argv) {
|
|
|
1679
1897
|
}
|
|
1680
1898
|
|
|
1681
1899
|
// src/bridge/server.ts
|
|
1682
|
-
import
|
|
1900
|
+
import http2 from "http";
|
|
1683
1901
|
function setCorsHeaders(res) {
|
|
1684
1902
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1685
1903
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
@@ -1713,7 +1931,7 @@ async function parseBody(req) {
|
|
|
1713
1931
|
}
|
|
1714
1932
|
function createBridgeHttpServer(options = {}) {
|
|
1715
1933
|
const activeSseClients = /* @__PURE__ */ new Map();
|
|
1716
|
-
const server =
|
|
1934
|
+
const server = http2.createServer(async (req, res) => {
|
|
1717
1935
|
setCorsHeaders(res);
|
|
1718
1936
|
if (req.method === "OPTIONS") {
|
|
1719
1937
|
res.writeHead(204);
|
|
@@ -1945,7 +2163,7 @@ async function stopBridgeServer() {
|
|
|
1945
2163
|
}
|
|
1946
2164
|
|
|
1947
2165
|
// src/bridge/watcher.ts
|
|
1948
|
-
import
|
|
2166
|
+
import http3 from "http";
|
|
1949
2167
|
function startBridgeWatcher(options = {}) {
|
|
1950
2168
|
const bridgeUrl = options.bridgeUrl || process.env.LINKEGRINGO_BRIDGE_URL || "http://127.0.0.1:5174";
|
|
1951
2169
|
let isClosed = false;
|
|
@@ -1956,7 +2174,7 @@ function startBridgeWatcher(options = {}) {
|
|
|
1956
2174
|
if (isClosed) return;
|
|
1957
2175
|
try {
|
|
1958
2176
|
const url = new URL("/api/jobs/stream", bridgeUrl);
|
|
1959
|
-
const req =
|
|
2177
|
+
const req = http3.get(
|
|
1960
2178
|
url.toString(),
|
|
1961
2179
|
{
|
|
1962
2180
|
headers: {
|
|
@@ -2136,6 +2354,7 @@ export {
|
|
|
2136
2354
|
handleGetPendingJob,
|
|
2137
2355
|
handleSimulateRecruiterSearch,
|
|
2138
2356
|
handleSubmitJobResult,
|
|
2357
|
+
handleWatchLinkeGringo,
|
|
2139
2358
|
installMcpServerConfig,
|
|
2140
2359
|
parseArgs,
|
|
2141
2360
|
runInstaller,
|
|
@@ -2143,5 +2362,7 @@ export {
|
|
|
2143
2362
|
startBridgeServer,
|
|
2144
2363
|
startBridgeWatcher,
|
|
2145
2364
|
stopBridgeServer,
|
|
2146
|
-
submitJobResultInputSchema
|
|
2365
|
+
submitJobResultInputSchema,
|
|
2366
|
+
waitForNextJob,
|
|
2367
|
+
watchLinkeGringoInputSchema
|
|
2147
2368
|
};
|