mcp-emtrafesa 1.1.1 → 1.1.2

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/README.es.md CHANGED
@@ -68,7 +68,7 @@ bun run index.ts
68
68
  | `get-arrival-terminal` | Obtiene terminales de destino para un origen | `departureTerminalId` |
69
69
  | `get-departure-schedules` | Obtiene horarios entre dos terminales | `departureTerminalId`, `arrivalTerminalId`, `date?` |
70
70
  | `get-latest-purchased-tickets` | Busca tus boletos comprados | `DNI`, `email` |
71
- | `download-ticket-pdf` | Descarga tu boleto como archivo PDF | `ticketCode` |
71
+ | `get-ticket-pdf` | Descarga tu boleto como archivo PDF | `ticketCode` |
72
72
  | `get-frequently-asked-questions` | Obtiene preguntas frecuentes del servicio | Ninguno |
73
73
 
74
74
  ---
@@ -103,7 +103,7 @@ const tickets = await client.callTool("get-latest-purchased-tickets", {
103
103
  ### Descargar tu boleto en PDF
104
104
 
105
105
  ```typescript
106
- const pdf = await client.callTool("download-ticket-pdf", {
106
+ const pdf = await client.callTool("get-ticket-pdf", {
107
107
  ticketCode: "BP01-123456",
108
108
  });
109
109
  // Devuelve un PDF codificado en base64 que puedes guardar o mostrar
package/README.md CHANGED
@@ -68,7 +68,7 @@ bun run index.ts
68
68
  | `get-arrival-terminal` | Get destination terminals for a given origin | `departureTerminalId` |
69
69
  | `get-departure-schedules` | Get schedules between two terminals | `departureTerminalId`, `arrivalTerminalId`, `date?` |
70
70
  | `get-latest-purchased-tickets` | Search your purchased tickets | `DNI`, `email` |
71
- | `download-ticket-pdf` | Download your ticket as a PDF file | `ticketCode` |
71
+ | `get-ticket-pdf` | Download your ticket as a PDF file | `ticketCode` |
72
72
  | `get-frequently-asked-questions` | Get FAQs about the service | None |
73
73
 
74
74
  ---
@@ -103,7 +103,7 @@ const tickets = await client.callTool("get-latest-purchased-tickets", {
103
103
  ### Download your ticket as PDF
104
104
 
105
105
  ```typescript
106
- const pdf = await client.callTool("download-ticket-pdf", {
106
+ const pdf = await client.callTool("get-ticket-pdf", {
107
107
  ticketCode: "BP01-123456",
108
108
  });
109
109
  // Returns a base64-encoded PDF that can be saved or displayed
package/dist/src/index.js CHANGED
@@ -31289,6 +31289,11 @@ var require_mime_type = __commonJS((exports, module) => {
31289
31289
  };
31290
31290
  });
31291
31291
 
31292
+ // src/index.ts
31293
+ import { readFileSync } from "node:fs";
31294
+ import { join } from "node:path";
31295
+ import { fileURLToPath } from "node:url";
31296
+
31292
31297
  // node_modules/zod/v3/external.js
31293
31298
  var exports_external = {};
31294
31299
  __export(exports_external, {
@@ -57486,170 +57491,124 @@ var api3 = {
57486
57491
  }
57487
57492
  };
57488
57493
 
57489
- // src/internal/emtrafesa/services.ts
57490
- async function getTerminals() {
57491
- const req = await fetch("https://emtrafesa.pe/Home/GetSucursales", {
57492
- headers: api3.headers
57493
- });
57494
- const res = await req.json();
57495
- return res;
57496
- }
57497
- async function getFrequentlyAskedQuestions() {
57498
- const req = await fetch("https://emtrafesa.pe/Home/GetPreguntasFrecuentes", {
57499
- headers: api3.headers
57500
- });
57501
- const res = await req.json();
57502
- return res;
57503
- }
57504
- async function getArrivalTerminalsByDepartureTerminal({
57505
- departureTerminalId
57506
- }) {
57507
- const req = await fetch(`https://emtrafesa.pe/Home/GetSucursalesDestino?origen=${departureTerminalId}`, {
57508
- headers: api3.headers
57509
- });
57510
- const res = await req.json();
57511
- return res;
57512
- }
57513
- async function getDepartureSchedules({
57514
- departureTerminalId,
57515
- arrivalTerminalId,
57516
- date: date4
57517
- }) {
57518
- const formattedDate = date4 ? date4 : new Intl.DateTimeFormat("es-PE", {
57519
- timeZone: "America/Lima",
57520
- day: "2-digit",
57521
- month: "2-digit",
57522
- year: "numeric"
57523
- }).format(new Date);
57524
- const req = await fetch(`https://emtrafesa.pe/Home/GetItinerario`, {
57525
- method: "POST",
57526
- headers: {
57527
- ...api3.headers,
57528
- "Content-Type": "application/json"
57529
- },
57530
- body: JSON.stringify({
57531
- embarque_sucursal_id: departureTerminalId,
57532
- desembarque_sucursal_id: arrivalTerminalId,
57533
- embarque_fecha: formattedDate
57534
- })
57535
- });
57536
- const res = await req.json();
57537
- return res;
57538
- }
57539
- async function getLatestPurchaseTickets({
57540
- DNI,
57541
- email: email2
57542
- }) {
57543
- const body = new URLSearchParams;
57544
- body.append("Dni", DNI);
57545
- body.append("Correo", email2);
57546
- const req = await fetch(`https://emtrafesa.pe/Consulta/PostConsulta`, {
57547
- method: "POST",
57548
- headers: {
57549
- ...api3.headers,
57550
- "Content-Type": "application/x-www-form-urlencoded"
57551
- },
57552
- body: body.toString()
57553
- });
57554
- const res = await req.text();
57555
- const $2 = load(res);
57556
- const tickets = $2(".card-body").map((_, card) => {
57557
- const $card = $2(card);
57558
- const dateTimeRaw = $card.find("h5").first().text().trim();
57559
- const seats = $card.find(".text-muted.small span").toArray().map((el) => $2(el).text().trim()).filter((txt) => /^\d+$/.test(txt));
57560
- const ticketsCodes = $card.find("p.text-truncate").text().trim().split("|").map((code) => code.trim());
57561
- const price = $card.find("h4").first().text().trim();
57562
- const operation = $card.find("h6.text-success").text().replace(/[^0-9]/g, "").trim();
57563
- const [origin, destination] = $card.find("button.btn-sm").toArray().map((btn) => $2(btn).text().trim());
57564
- return {
57565
- dateTime: dateTimeRaw,
57566
- seats,
57567
- ticketsCodes,
57568
- price,
57569
- operationNumber: operation,
57570
- origin,
57571
- destination
57572
- };
57573
- }).get();
57574
- return tickets;
57575
- }
57576
- async function downloadTicketPDF({
57577
- tiketCode
57578
- }) {
57579
- const req = await fetch(`https://www.emtrafesa.pe/Home/ComprobanteDescarga?Boletos=3,BP01,${tiketCode}`, {
57580
- headers: api3.headers
57581
- });
57582
- if (!req.ok) {
57583
- throw new Error(`Failed to download ticket PDF: ${req.statusText}`);
57494
+ // src/infrastructure/http/emtrafesa-http.repository.ts
57495
+ class EmtrafesaHttpRepository {
57496
+ async getTerminals() {
57497
+ const req = await fetch("https://emtrafesa.pe/Home/GetSucursales", {
57498
+ headers: api3.headers
57499
+ });
57500
+ return await req.json();
57501
+ }
57502
+ async getFrequentlyAskedQuestions() {
57503
+ const req = await fetch("https://emtrafesa.pe/Home/GetPreguntasFrecuentes", {
57504
+ headers: api3.headers
57505
+ });
57506
+ return await req.json();
57507
+ }
57508
+ async getArrivalTerminalsByDepartureTerminal(params) {
57509
+ const req = await fetch(`https://emtrafesa.pe/Home/GetSucursalesDestino?origen=${params.departureTerminalId}`, { headers: api3.headers });
57510
+ return await req.json();
57511
+ }
57512
+ async getDepartureSchedules(params) {
57513
+ const formattedDate = params.date || new Intl.DateTimeFormat("es-PE", {
57514
+ timeZone: "America/Lima",
57515
+ day: "2-digit",
57516
+ month: "2-digit",
57517
+ year: "numeric"
57518
+ }).format(new Date);
57519
+ const req = await fetch("https://emtrafesa.pe/Home/GetItinerario", {
57520
+ method: "POST",
57521
+ headers: { ...api3.headers, "Content-Type": "application/json" },
57522
+ body: JSON.stringify({
57523
+ embarque_sucursal_id: params.departureTerminalId,
57524
+ desembarque_sucursal_id: params.arrivalTerminalId,
57525
+ embarque_fecha: formattedDate
57526
+ })
57527
+ });
57528
+ return await req.json();
57529
+ }
57530
+ async getLatestPurchaseTickets(params) {
57531
+ const body = new URLSearchParams;
57532
+ body.append("Dni", params.DNI);
57533
+ body.append("Correo", params.email);
57534
+ const req = await fetch("https://emtrafesa.pe/Consulta/PostConsulta", {
57535
+ method: "POST",
57536
+ headers: {
57537
+ ...api3.headers,
57538
+ "Content-Type": "application/x-www-form-urlencoded"
57539
+ },
57540
+ body: body.toString()
57541
+ });
57542
+ const html3 = await req.text();
57543
+ const $2 = load(html3);
57544
+ return $2(".card-body").map((_, card) => {
57545
+ const $card = $2(card);
57546
+ return {
57547
+ dateTime: $card.find("h5").first().text().trim(),
57548
+ seats: $card.find(".text-muted.small span").toArray().map((el) => $2(el).text().trim()).filter((txt) => /^\d+$/.test(txt)),
57549
+ ticketsCodes: $card.find("p.text-truncate").text().trim().split("|").map((code) => code.trim()),
57550
+ price: $card.find("h4").first().text().trim(),
57551
+ operationNumber: $card.find("h6.text-success").text().replace(/[^0-9]/g, "").trim(),
57552
+ origin: $card.find("button.btn-sm").first().text().trim(),
57553
+ destination: $card.find("button.btn-sm").last().text().trim()
57554
+ };
57555
+ }).get();
57556
+ }
57557
+ async downloadTicketPDF(params) {
57558
+ const req = await fetch(`https://www.emtrafesa.pe/Home/ComprobanteDescarga?Boletos=3,BP01,${params.ticketCode}`, { headers: api3.headers });
57559
+ if (!req.ok) {
57560
+ throw new Error(`Failed to download ticket PDF: ${req.statusText}`);
57561
+ }
57562
+ const arrayBuffer = await req.arrayBuffer();
57563
+ return Buffer.from(arrayBuffer);
57584
57564
  }
57585
- const arrayBuffer = await req.arrayBuffer();
57586
- return Buffer.from(arrayBuffer);
57587
57565
  }
57588
57566
 
57589
- // src/lib/utils.ts
57567
+ // src/shared/utils.ts
57590
57568
  function bufferToBase64(buffer) {
57591
57569
  return buffer.toString("base64");
57592
57570
  }
57593
- function isPdfBuffer(buffer) {
57594
- if (buffer.length < 4)
57595
- return false;
57596
- const pdfHeader = buffer.subarray(0, 4).toString("ascii");
57597
- return pdfHeader === "%PDF";
57598
- }
57599
57571
 
57600
- // src/common/tools.ts
57601
- async function registerTools(server) {
57572
+ // src/infrastructure/mcp/tools.ts
57573
+ function errorResponse(error2) {
57574
+ return {
57575
+ content: [
57576
+ {
57577
+ type: "text",
57578
+ text: error2 instanceof Error ? error2.message : "unknown error"
57579
+ }
57580
+ ]
57581
+ };
57582
+ }
57583
+ function registerTools(server, repository) {
57602
57584
  server.tool("get-terminals", "Get terminals throughout the country", async () => {
57603
57585
  try {
57604
- const terminals = await getTerminals();
57605
- return {
57606
- content: [{ type: "text", text: JSON.stringify(terminals) }]
57607
- };
57586
+ const terminals = await repository.getTerminals();
57587
+ return { content: [{ type: "text", text: JSON.stringify(terminals) }] };
57608
57588
  } catch (error2) {
57609
- return {
57610
- content: [
57611
- {
57612
- type: "text",
57613
- text: JSON.stringify(error2 instanceof Error ? error2.message : "unknow error")
57614
- }
57615
- ]
57616
- };
57589
+ return errorResponse(error2);
57617
57590
  }
57618
57591
  });
57619
57592
  server.tool("get-frequently-asked-questions", "Provides frequently asked questions about terminals, tickets, types of people, etc.", async () => {
57620
57593
  try {
57621
- const faq = await getFrequentlyAskedQuestions();
57622
- return {
57623
- content: [{ type: "text", text: JSON.stringify(faq) }]
57624
- };
57594
+ const faq = await repository.getFrequentlyAskedQuestions();
57595
+ return { content: [{ type: "text", text: JSON.stringify(faq) }] };
57625
57596
  } catch (error2) {
57626
- return {
57627
- content: [
57628
- {
57629
- type: "text",
57630
- text: JSON.stringify(error2 instanceof Error ? error2.message : "unknow error")
57631
- }
57632
- ]
57633
- };
57597
+ return errorResponse(error2);
57634
57598
  }
57635
57599
  });
57636
57600
  server.tool("get-arrival-terminal", "Get arrival terminal for a departure terminal.", {
57637
57601
  departureTerminalId: exports_external.string().describe("Departure terminal id (origin)")
57638
57602
  }, async ({ departureTerminalId }) => {
57639
57603
  try {
57640
- const arrivalTerminals = await getArrivalTerminalsByDepartureTerminal({ departureTerminalId });
57604
+ const arrivalTerminals = await repository.getArrivalTerminalsByDepartureTerminal({
57605
+ departureTerminalId
57606
+ });
57641
57607
  return {
57642
57608
  content: [{ type: "text", text: JSON.stringify(arrivalTerminals) }]
57643
57609
  };
57644
57610
  } catch (error2) {
57645
- return {
57646
- content: [
57647
- {
57648
- type: "text",
57649
- text: JSON.stringify(error2 instanceof Error ? error2.message : "unknown error")
57650
- }
57651
- ]
57652
- };
57611
+ return errorResponse(error2);
57653
57612
  }
57654
57613
  });
57655
57614
  server.tool("get-departure-schedules", "Get departure schedules for a specific departure terminal.", {
@@ -57658,23 +57617,14 @@ async function registerTools(server) {
57658
57617
  date: exports_external.string().optional().describe("Date in the format DD/MM/YYYY")
57659
57618
  }, async ({ departureTerminalId, arrivalTerminalId, date: date4 }) => {
57660
57619
  try {
57661
- const schedules = await getDepartureSchedules({
57620
+ const schedules = await repository.getDepartureSchedules({
57662
57621
  departureTerminalId,
57663
57622
  arrivalTerminalId,
57664
57623
  date: date4
57665
57624
  });
57666
- return {
57667
- content: [{ type: "text", text: JSON.stringify(schedules) }]
57668
- };
57625
+ return { content: [{ type: "text", text: JSON.stringify(schedules) }] };
57669
57626
  } catch (error2) {
57670
- return {
57671
- content: [
57672
- {
57673
- type: "text",
57674
- text: JSON.stringify(error2 instanceof Error ? error2.message : "unknown error")
57675
- }
57676
- ]
57677
- };
57627
+ return errorResponse(error2);
57678
57628
  }
57679
57629
  });
57680
57630
  server.tool("get-latest-purchased-tickets", "Get the latest purchased tickets for a specific departure terminal.", {
@@ -57682,61 +57632,49 @@ async function registerTools(server) {
57682
57632
  email: exports_external.string().email().describe("Email of the user")
57683
57633
  }, async ({ DNI, email: email2 }) => {
57684
57634
  try {
57685
- const tickets = await getLatestPurchaseTickets({ DNI, email: email2 });
57686
- return {
57687
- content: [{ type: "text", text: JSON.stringify(tickets) }]
57688
- };
57635
+ const tickets = await repository.getLatestPurchaseTickets({
57636
+ DNI,
57637
+ email: email2
57638
+ });
57639
+ return { content: [{ type: "text", text: JSON.stringify(tickets) }] };
57689
57640
  } catch (error2) {
57690
- return {
57691
- content: [
57692
- {
57693
- type: "text",
57694
- text: JSON.stringify(error2 instanceof Error ? error2.message : "unknown error")
57695
- }
57696
- ]
57697
- };
57641
+ return errorResponse(error2);
57698
57642
  }
57699
57643
  });
57700
- server.tool("download-ticket-pdf", "Download a PDF of a ticket by its code.", {
57701
- tiketCode: exports_external.string().describe("Ticket code")
57702
- }, async ({ tiketCode }) => {
57644
+ server.tool("get-ticket-pdf", "Download and view a ticket PDF by its code. Returns a data URL that can be opened in browser.", {
57645
+ ticketCode: exports_external.string().describe("Ticket code")
57646
+ }, async ({ ticketCode }) => {
57703
57647
  try {
57704
- const pdfBuffer = await downloadTicketPDF({ tiketCode });
57705
- if (!isPdfBuffer(pdfBuffer)) {
57706
- throw new Error("Received invalid PDF data from server");
57707
- }
57648
+ const pdfBuffer = await repository.downloadTicketPDF({ ticketCode });
57708
57649
  const base64Data = bufferToBase64(pdfBuffer);
57709
57650
  return {
57710
57651
  content: [
57711
57652
  {
57712
57653
  type: "resource",
57713
57654
  resource: {
57714
- uri: `data:application/pdf;base64,${base64Data}`,
57715
- blob: base64Data,
57716
- mimeType: "application/pdf"
57655
+ uri: `ticket://${ticketCode}/document.pdf`,
57656
+ name: `Ticket ${ticketCode}`,
57657
+ mimeType: "application/pdf",
57658
+ blob: base64Data
57717
57659
  }
57718
57660
  }
57719
57661
  ]
57720
57662
  };
57721
57663
  } catch (error2) {
57722
- return {
57723
- content: [
57724
- {
57725
- type: "text",
57726
- text: JSON.stringify(error2 instanceof Error ? error2.message : "unknown error")
57727
- }
57728
- ]
57729
- };
57664
+ return errorResponse(error2);
57730
57665
  }
57731
57666
  });
57732
57667
  }
57733
57668
 
57734
57669
  // src/index.ts
57670
+ var __dirname2 = fileURLToPath(new URL(".", import.meta.url));
57671
+ var packageJson = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
57735
57672
  var server = new McpServer({
57736
- name: "mcp-emtrafesa",
57737
- version: "1.0.1"
57673
+ name: packageJson.name,
57674
+ version: packageJson.version
57738
57675
  });
57739
- registerTools(server);
57676
+ var repository = new EmtrafesaHttpRepository;
57677
+ registerTools(server, repository);
57740
57678
  async function main() {
57741
57679
  const transport = new StdioServerTransport;
57742
57680
  await server.connect(transport);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-emtrafesa",
3
3
  "description": "A Model Context Protocol (MCP) server for accessing Emtrafesa bus transportation services in Peru",
4
- "version": "1.1.1",
4
+ "version": "1.1.2",
5
5
  "module": "src/index.ts",
6
6
  "main": "dist/src/index.js",
7
7
  "types": "dist/src/index.d.ts",
@@ -38,7 +38,7 @@
38
38
  "dist/src/*.d.ts"
39
39
  ],
40
40
  "scripts": {
41
- "build": "bun build --target=node ./src/index.ts --outfile=dist/src/index.js && bun run build:declaration",
41
+ "build": "bun build --target=node ./src/index.ts --outdir=dist/src && bun run build:declaration",
42
42
  "build:declaration": "tsc --emitDeclarationOnly --project tsconfig.types.json",
43
43
  "format": "bunx biome check --write",
44
44
  "prepare": "husky",