@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 CHANGED
@@ -467,4 +467,38 @@ declare function handleSubmitJobResult(input: SubmitJobResultInput): Promise<{
467
467
  };
468
468
  }>;
469
469
 
470
- 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 WatcherOptions, auditProfileInputSchema, bridgeJobStore, completeRemoteOrLocalJob, convertToXyzBulletInputSchema, createBridgeHttpServer, createLinkeGringoMcpServer, detectSparseExperiences, failRemoteOrLocalJob, formatGoogleXyzBullet, generateHeadlineInputSchema, getExperienceBulletCount, getMcpConfigsForSystem, getPendingJobInputSchema, getRemoteOrLocalPendingJob, handleAuditProfile, handleConvertToXyzBullet, handleGenerateHeadline, handleGetPendingJob, handleSimulateRecruiterSearch, handleSubmitJobResult, installMcpServerConfig, parseArgs, runInstaller, simulateRecruiterSearchInputSchema, startBridgeServer, startBridgeWatcher, stopBridgeServer, submitJobResultInputSchema };
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 http from "http";
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 = http.createServer(async (req, res) => {
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 http2 from "http";
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 = http2.get(
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linkegringo/mcp",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Servidor MCP oficial do LinkeGringo para auditoria e otimização de perfis para o mercado internacional",
5
5
  "type": "module",
6
6
  "bin": {