mcp-travelcode 1.0.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.
Files changed (70) hide show
  1. package/README.md +133 -0
  2. package/build/auth/cli-auth.d.ts +16 -0
  3. package/build/auth/cli-auth.js +281 -0
  4. package/build/auth/token-store.d.ts +39 -0
  5. package/build/auth/token-store.js +113 -0
  6. package/build/client/api-client.d.ts +42 -0
  7. package/build/client/api-client.js +235 -0
  8. package/build/client/types.d.ts +420 -0
  9. package/build/client/types.js +3 -0
  10. package/build/config.d.ts +9 -0
  11. package/build/config.js +30 -0
  12. package/build/formatters/aerodatabox-formatter.d.ts +6 -0
  13. package/build/formatters/aerodatabox-formatter.js +246 -0
  14. package/build/formatters/airline-formatter.d.ts +3 -0
  15. package/build/formatters/airline-formatter.js +12 -0
  16. package/build/formatters/airport-formatter.d.ts +4 -0
  17. package/build/formatters/airport-formatter.js +28 -0
  18. package/build/formatters/flight-formatter.d.ts +13 -0
  19. package/build/formatters/flight-formatter.js +100 -0
  20. package/build/formatters/hotel-formatter.d.ts +4 -0
  21. package/build/formatters/hotel-formatter.js +55 -0
  22. package/build/formatters/order-formatter.d.ts +8 -0
  23. package/build/formatters/order-formatter.js +115 -0
  24. package/build/http-server.d.ts +16 -0
  25. package/build/http-server.js +164 -0
  26. package/build/index.d.ts +3 -0
  27. package/build/index.js +16 -0
  28. package/build/polling/flight-poller.d.ts +11 -0
  29. package/build/polling/flight-poller.js +60 -0
  30. package/build/server.d.ts +4 -0
  31. package/build/server.js +54 -0
  32. package/build/tools/cancel-order.d.ts +9 -0
  33. package/build/tools/cancel-order.js +24 -0
  34. package/build/tools/check-order-cancellation.d.ts +8 -0
  35. package/build/tools/check-order-cancellation.js +22 -0
  36. package/build/tools/check-order-modification.d.ts +8 -0
  37. package/build/tools/check-order-modification.js +22 -0
  38. package/build/tools/create-order.d.ts +75 -0
  39. package/build/tools/create-order.js +52 -0
  40. package/build/tools/get-airport-delay-stats.d.ts +9 -0
  41. package/build/tools/get-airport-delay-stats.js +31 -0
  42. package/build/tools/get-airport-flights.d.ts +13 -0
  43. package/build/tools/get-airport-flights.js +78 -0
  44. package/build/tools/get-airport.d.ts +8 -0
  45. package/build/tools/get-airport.js +25 -0
  46. package/build/tools/get-flight-delay-stats.d.ts +8 -0
  47. package/build/tools/get-flight-delay-stats.js +28 -0
  48. package/build/tools/get-flight-results.d.ts +16 -0
  49. package/build/tools/get-flight-results.js +68 -0
  50. package/build/tools/get-flight-status.d.ts +11 -0
  51. package/build/tools/get-flight-status.js +42 -0
  52. package/build/tools/get-hotel-location.d.ts +8 -0
  53. package/build/tools/get-hotel-location.js +27 -0
  54. package/build/tools/get-order.d.ts +8 -0
  55. package/build/tools/get-order.js +22 -0
  56. package/build/tools/list-orders.d.ts +16 -0
  57. package/build/tools/list-orders.js +46 -0
  58. package/build/tools/modify-order.d.ts +10 -0
  59. package/build/tools/modify-order.js +33 -0
  60. package/build/tools/search-airlines.d.ts +9 -0
  61. package/build/tools/search-airlines.js +29 -0
  62. package/build/tools/search-airports.d.ts +9 -0
  63. package/build/tools/search-airports.js +30 -0
  64. package/build/tools/search-flights.d.ts +17 -0
  65. package/build/tools/search-flights.js +82 -0
  66. package/build/tools/search-hotel-locations.d.ts +9 -0
  67. package/build/tools/search-hotel-locations.js +23 -0
  68. package/build/tools/search-hotels.d.ts +46 -0
  69. package/build/tools/search-hotels.js +106 -0
  70. package/package.json +60 -0
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * HTTP entry point for the TravelCode MCP Server.
4
+ *
5
+ * Supports OAuth 2.1 via MCP spec:
6
+ * - Serves Protected Resource Metadata (RFC 9728) at /.well-known/oauth-protected-resource
7
+ * - Returns 401 with WWW-Authenticate header when Bearer token is missing
8
+ * - Creates per-session McpServer instances using the user's OAuth token
9
+ *
10
+ * The Authorization Server is TravelCode's own OAuth server.
11
+ * Tokens are opaque and validated by TravelCode API on each request.
12
+ *
13
+ * Stdio transport (src/index.ts) remains unchanged for backward compatibility.
14
+ */
15
+ import express from "express";
16
+ import { randomUUID } from "node:crypto";
17
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
18
+ import { createServer } from "./server.js";
19
+ // --- Configuration ---
20
+ const PORT = parseInt(process.env.PORT || "3000", 10);
21
+ const API_BASE_URL = (process.env.TRAVELCODE_API_BASE_URL || "https://api.travel-code.com/v1").replace(/\/+$/, "");
22
+ const OAUTH_ISSUER = process.env.OAUTH_ISSUER || "https://travel-code.com";
23
+ // Resource URI — the public URL of this MCP server.
24
+ // In production, set to the actual public URL (e.g. https://mcp.travel-code.com).
25
+ // Locally defaults to http://localhost:PORT.
26
+ const RESOURCE_URI = process.env.RESOURCE_URI || `http://localhost:${PORT}`;
27
+ const POLL_INTERVAL_MS = parseInt(process.env.TRAVELCODE_POLL_INTERVAL_MS || "2000", 10);
28
+ const POLL_TIMEOUT_MS = parseInt(process.env.TRAVELCODE_POLL_TIMEOUT_MS || "90000", 10);
29
+ const SCOPES_SUPPORTED = [
30
+ "flights:search",
31
+ "flights:status",
32
+ "flights:stats",
33
+ "airports:read",
34
+ "airlines:read",
35
+ "orders:read",
36
+ "orders:write",
37
+ ];
38
+ const sessions = new Map();
39
+ // Cleanup stale sessions every 30 minutes
40
+ const SESSION_TTL_MS = 2 * 60 * 60 * 1000; // 2 hours
41
+ setInterval(() => {
42
+ const now = Date.now();
43
+ for (const [id, session] of sessions) {
44
+ if (now - session.createdAt > SESSION_TTL_MS) {
45
+ session.transport.close().catch(() => { });
46
+ session.server.close().catch(() => { });
47
+ sessions.delete(id);
48
+ }
49
+ }
50
+ }, 30 * 60 * 1000);
51
+ // --- Express app ---
52
+ const app = express();
53
+ app.use(express.json());
54
+ // CORS — needed for browser-based MCP clients
55
+ app.use((_req, res, next) => {
56
+ res.setHeader("Access-Control-Allow-Origin", "*");
57
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
58
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Mcp-Session-Id");
59
+ res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id");
60
+ next();
61
+ });
62
+ app.options("*", (_req, res) => {
63
+ res.sendStatus(204);
64
+ });
65
+ // --- Protected Resource Metadata (RFC 9728) ---
66
+ app.get("/.well-known/oauth-protected-resource", (_req, res) => {
67
+ res.json({
68
+ resource: RESOURCE_URI,
69
+ authorization_servers: [OAUTH_ISSUER],
70
+ scopes_supported: SCOPES_SUPPORTED,
71
+ bearer_methods_supported: ["header"],
72
+ resource_name: "TravelCode MCP Server",
73
+ });
74
+ });
75
+ // --- Health check ---
76
+ app.get("/health", (_req, res) => {
77
+ res.json({
78
+ status: "ok",
79
+ transport: "streamable-http",
80
+ sessions: sessions.size,
81
+ });
82
+ });
83
+ // --- Helper: send 401 ---
84
+ function send401(res) {
85
+ const metadataUrl = `${RESOURCE_URI}/.well-known/oauth-protected-resource`;
86
+ res.status(401)
87
+ .set("WWW-Authenticate", `Bearer resource_metadata="${metadataUrl}"`)
88
+ .json({ error: "unauthorized", error_description: "Bearer token required" });
89
+ }
90
+ // --- MCP endpoint ---
91
+ app.all("/mcp", async (req, res) => {
92
+ // 1. Extract Bearer token
93
+ const authHeader = req.headers.authorization;
94
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
95
+ send401(res);
96
+ return;
97
+ }
98
+ const token = authHeader.substring(7).trim();
99
+ if (!token) {
100
+ send401(res);
101
+ return;
102
+ }
103
+ // 2. Route to existing session or create new
104
+ const sessionId = req.headers["mcp-session-id"];
105
+ if (sessionId) {
106
+ const session = sessions.get(sessionId);
107
+ if (!session) {
108
+ res.status(404).json({
109
+ jsonrpc: "2.0",
110
+ error: { code: -32000, message: "Session not found. Please start a new session." },
111
+ });
112
+ return;
113
+ }
114
+ await session.transport.handleRequest(req, res, req.body);
115
+ return;
116
+ }
117
+ // 3. New session — only via POST (initialization)
118
+ if (req.method !== "POST") {
119
+ res.status(400).json({
120
+ jsonrpc: "2.0",
121
+ error: { code: -32000, message: "Missing Mcp-Session-Id header. Initialize session with POST first." },
122
+ });
123
+ return;
124
+ }
125
+ // Create per-session config with user's OAuth token
126
+ const config = {
127
+ apiBaseUrl: API_BASE_URL,
128
+ apiToken: token,
129
+ pollIntervalMs: POLL_INTERVAL_MS,
130
+ pollTimeoutMs: POLL_TIMEOUT_MS,
131
+ };
132
+ const server = createServer(config);
133
+ const transport = new StreamableHTTPServerTransport({
134
+ sessionIdGenerator: () => randomUUID(),
135
+ });
136
+ transport.onclose = () => {
137
+ const sid = transport.sessionId;
138
+ if (sid) {
139
+ sessions.delete(sid);
140
+ }
141
+ };
142
+ await server.connect(transport);
143
+ // Store session
144
+ const sid = transport.sessionId;
145
+ if (sid) {
146
+ sessions.set(sid, {
147
+ transport,
148
+ server,
149
+ createdAt: Date.now(),
150
+ });
151
+ }
152
+ // Handle the initialization request
153
+ await transport.handleRequest(req, res, req.body);
154
+ });
155
+ // --- Start ---
156
+ app.listen(PORT, () => {
157
+ console.log(`TravelCode MCP Server (HTTP) listening on port ${PORT}`);
158
+ console.log(`MCP endpoint: ${RESOURCE_URI}/mcp`);
159
+ console.log(`Resource metadata: ${RESOURCE_URI}/.well-known/oauth-protected-resource`);
160
+ console.log(`OAuth issuer: ${OAUTH_ISSUER}`);
161
+ console.log(`API base URL: ${API_BASE_URL}`);
162
+ console.log(`Scopes: ${SCOPES_SUPPORTED.join(", ")}`);
163
+ });
164
+ //# sourceMappingURL=http-server.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
package/build/index.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { loadConfig } from "./config.js";
4
+ import { createServer } from "./server.js";
5
+ async function main() {
6
+ const config = await loadConfig();
7
+ const server = createServer(config);
8
+ const transport = new StdioServerTransport();
9
+ await server.connect(transport);
10
+ console.error("TravelCode MCP Server running on stdio");
11
+ }
12
+ main().catch((error) => {
13
+ console.error("Fatal error:", error.message);
14
+ process.exit(1);
15
+ });
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,11 @@
1
+ import { TravelCodeApiClient } from "../client/api-client.js";
2
+ import { TravelCodeConfig } from "../config.js";
3
+ import { FlightSearchRequest, FlightSearchResultsResponse } from "../client/types.js";
4
+ export interface FlightSearchResult {
5
+ cacheId: string;
6
+ completed: boolean;
7
+ response: FlightSearchResultsResponse;
8
+ }
9
+ export type ProgressCallback = (progress: number, total: number, message: string) => Promise<void>;
10
+ export declare function executeFlightSearch(client: TravelCodeApiClient, searchParams: FlightSearchRequest, config: TravelCodeConfig, onProgress?: ProgressCallback): Promise<FlightSearchResult>;
11
+ //# sourceMappingURL=flight-poller.d.ts.map
@@ -0,0 +1,60 @@
1
+ function sleep(ms) {
2
+ return new Promise((resolve) => setTimeout(resolve, ms));
3
+ }
4
+ export async function executeFlightSearch(client, searchParams, config, onProgress) {
5
+ // Step 1: Create the search
6
+ await onProgress?.(0, 100, "Submitting search request...");
7
+ const createResponse = await client.post("/search/flights", searchParams);
8
+ const cacheId = createResponse.cacheId;
9
+ if (!cacheId) {
10
+ throw new Error("No cacheId returned from flight search creation");
11
+ }
12
+ // Step 2: Poll for results
13
+ const startTime = Date.now();
14
+ let interval = config.pollIntervalMs;
15
+ const maxInterval = Math.max(config.pollIntervalMs * 2.5, 5000);
16
+ let pollAttempt = 0;
17
+ await onProgress?.(10, 100, "Waiting for airline responses...");
18
+ while (true) {
19
+ await sleep(interval);
20
+ pollAttempt++;
21
+ const elapsed = Date.now() - startTime;
22
+ // Progress: 10-90% mapped to elapsed/timeout ratio
23
+ const progressPct = Math.min(10 + Math.round((elapsed / config.pollTimeoutMs) * 80), 90);
24
+ if (elapsed >= config.pollTimeoutMs) {
25
+ await onProgress?.(90, 100, "Timeout — fetching partial results...");
26
+ const partialResults = await client.get(`/search/flights/${encodeURIComponent(cacheId)}`, {
27
+ limit: 25,
28
+ offset: 0,
29
+ sort: "price",
30
+ sortOrder: "ASC",
31
+ embedded: "airlines,dictionaries",
32
+ });
33
+ return {
34
+ cacheId,
35
+ completed: false,
36
+ response: partialResults,
37
+ };
38
+ }
39
+ const results = await client.get(`/search/flights/${encodeURIComponent(cacheId)}`, {
40
+ limit: 25,
41
+ offset: 0,
42
+ sort: "price",
43
+ sortOrder: "ASC",
44
+ embedded: "airlines,dictionaries",
45
+ });
46
+ const foundCount = results.total ?? 0;
47
+ await onProgress?.(progressPct, 100, `Polling airlines... ${foundCount} flights found so far (attempt ${pollAttempt})`);
48
+ if (results.completed || results.status === "completed") {
49
+ await onProgress?.(100, 100, `Search complete — ${foundCount} flights found`);
50
+ return {
51
+ cacheId,
52
+ completed: true,
53
+ response: results,
54
+ };
55
+ }
56
+ // Exponential backoff
57
+ interval = Math.min(interval * 1.5, maxInterval);
58
+ }
59
+ }
60
+ //# sourceMappingURL=flight-poller.js.map
@@ -0,0 +1,4 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { TravelCodeConfig } from "./config.js";
3
+ export declare function createServer(config: TravelCodeConfig): McpServer;
4
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1,54 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { TravelCodeApiClient } from "./client/api-client.js";
3
+ import { registerSearchAirports } from "./tools/search-airports.js";
4
+ import { registerGetAirport } from "./tools/get-airport.js";
5
+ import { registerSearchAirlines } from "./tools/search-airlines.js";
6
+ import { registerSearchFlights } from "./tools/search-flights.js";
7
+ import { registerGetFlightResults } from "./tools/get-flight-results.js";
8
+ import { registerGetFlightStatus } from "./tools/get-flight-status.js";
9
+ import { registerGetAirportFlights } from "./tools/get-airport-flights.js";
10
+ import { registerGetFlightDelayStats } from "./tools/get-flight-delay-stats.js";
11
+ import { registerGetAirportDelayStats } from "./tools/get-airport-delay-stats.js";
12
+ import { registerListOrders } from "./tools/list-orders.js";
13
+ import { registerGetOrder } from "./tools/get-order.js";
14
+ import { registerCreateOrder } from "./tools/create-order.js";
15
+ import { registerCheckOrderCancellation } from "./tools/check-order-cancellation.js";
16
+ import { registerCancelOrder } from "./tools/cancel-order.js";
17
+ import { registerCheckOrderModification } from "./tools/check-order-modification.js";
18
+ import { registerModifyOrder } from "./tools/modify-order.js";
19
+ import { registerSearchHotelLocations } from "./tools/search-hotel-locations.js";
20
+ import { registerGetHotelLocation } from "./tools/get-hotel-location.js";
21
+ import { registerSearchHotels } from "./tools/search-hotels.js";
22
+ export function createServer(config) {
23
+ const server = new McpServer({
24
+ name: "TravelCode",
25
+ version: "1.0.0",
26
+ });
27
+ const client = new TravelCodeApiClient(config);
28
+ // Reference data tools
29
+ registerSearchAirports(server, client);
30
+ registerGetAirport(server, client);
31
+ registerSearchAirlines(server, client);
32
+ // Flight search tools
33
+ registerSearchFlights(server, client, config);
34
+ registerGetFlightResults(server, client);
35
+ // Flight statistics tools (AeroDataBox)
36
+ registerGetFlightStatus(server, client);
37
+ registerGetAirportFlights(server, client);
38
+ registerGetFlightDelayStats(server, client);
39
+ registerGetAirportDelayStats(server, client);
40
+ // Order management tools
41
+ registerListOrders(server, client);
42
+ registerGetOrder(server, client);
43
+ registerCreateOrder(server, client);
44
+ registerCheckOrderCancellation(server, client);
45
+ registerCancelOrder(server, client);
46
+ registerCheckOrderModification(server, client);
47
+ registerModifyOrder(server, client);
48
+ // Hotel search tools
49
+ registerSearchHotelLocations(server, client);
50
+ registerGetHotelLocation(server, client);
51
+ registerSearchHotels(server, client);
52
+ return server;
53
+ }
54
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { TravelCodeApiClient } from "../client/api-client.js";
4
+ export declare const cancelOrderSchema: {
5
+ order_id: z.ZodNumber;
6
+ reason: z.ZodOptional<z.ZodString>;
7
+ };
8
+ export declare function registerCancelOrder(server: McpServer, client: TravelCodeApiClient): void;
9
+ //# sourceMappingURL=cancel-order.d.ts.map
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ import { formatCancelResult } from "../formatters/order-formatter.js";
3
+ export const cancelOrderSchema = {
4
+ order_id: z.number().int().describe("Order ID to cancel"),
5
+ reason: z.string().optional().describe("Reason for cancellation"),
6
+ };
7
+ export function registerCancelOrder(server, client) {
8
+ server.tool("cancel_order", "Cancel an order. Cancellation is asynchronous — use get_order to poll for final status. It is recommended to call check_order_cancellation first to verify conditions. Idempotent: calling on an already cancelled order returns current status.", cancelOrderSchema, async ({ order_id, reason }) => {
9
+ try {
10
+ const body = reason ? { reason } : undefined;
11
+ const data = await client.post(`/orders/${order_id}/cancel`, body);
12
+ return {
13
+ content: [{ type: "text", text: formatCancelResult(data) }],
14
+ };
15
+ }
16
+ catch (error) {
17
+ return {
18
+ content: [{ type: "text", text: `Error cancelling order ${order_id}: ${error.message}` }],
19
+ isError: true,
20
+ };
21
+ }
22
+ });
23
+ }
24
+ //# sourceMappingURL=cancel-order.js.map
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { TravelCodeApiClient } from "../client/api-client.js";
4
+ export declare const checkOrderCancellationSchema: {
5
+ order_id: z.ZodNumber;
6
+ };
7
+ export declare function registerCheckOrderCancellation(server: McpServer, client: TravelCodeApiClient): void;
8
+ //# sourceMappingURL=check-order-cancellation.d.ts.map
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ import { formatCancelCheck } from "../formatters/order-formatter.js";
3
+ export const checkOrderCancellationSchema = {
4
+ order_id: z.number().int().describe("Order ID to check cancellation for"),
5
+ };
6
+ export function registerCheckOrderCancellation(server, client) {
7
+ server.tool("check_order_cancellation", "Check if an order can be cancelled and what the refund conditions are (penalty, deadline, estimated refund amount). Always call this before cancel_order.", checkOrderCancellationSchema, async ({ order_id }) => {
8
+ try {
9
+ const data = await client.get(`/orders/${order_id}/cancel/check`);
10
+ return {
11
+ content: [{ type: "text", text: formatCancelCheck(data, order_id) }],
12
+ };
13
+ }
14
+ catch (error) {
15
+ return {
16
+ content: [{ type: "text", text: `Error checking cancellation for order ${order_id}: ${error.message}` }],
17
+ isError: true,
18
+ };
19
+ }
20
+ });
21
+ }
22
+ //# sourceMappingURL=check-order-cancellation.js.map
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { TravelCodeApiClient } from "../client/api-client.js";
4
+ export declare const checkOrderModificationSchema: {
5
+ order_id: z.ZodNumber;
6
+ };
7
+ export declare function registerCheckOrderModification(server: McpServer, client: TravelCodeApiClient): void;
8
+ //# sourceMappingURL=check-order-modification.d.ts.map
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ import { formatModifyCheck } from "../formatters/order-formatter.js";
3
+ export const checkOrderModificationSchema = {
4
+ order_id: z.number().int().describe("Order ID to check modification for"),
5
+ };
6
+ export function registerCheckOrderModification(server, client) {
7
+ server.tool("check_order_modification", "Check if an order can be modified and what changes are allowed (contact, passport, rebook, baggage). Returns the list of services with their allowed change types. Always call this before modify_order.", checkOrderModificationSchema, async ({ order_id }) => {
8
+ try {
9
+ const data = await client.get(`/orders/${order_id}/modify/check`);
10
+ return {
11
+ content: [{ type: "text", text: formatModifyCheck(data, order_id) }],
12
+ };
13
+ }
14
+ catch (error) {
15
+ return {
16
+ content: [{ type: "text", text: `Error checking modification for order ${order_id}: ${error.message}` }],
17
+ isError: true,
18
+ };
19
+ }
20
+ });
21
+ }
22
+ //# sourceMappingURL=check-order-modification.js.map
@@ -0,0 +1,75 @@
1
+ import { z } from "zod";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { TravelCodeApiClient } from "../client/api-client.js";
4
+ export declare const createOrderSchema: {
5
+ cache_id: z.ZodString;
6
+ offer_id: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
7
+ passengers: z.ZodArray<z.ZodObject<{
8
+ type: z.ZodOptional<z.ZodEnum<["adult", "child", "infant"]>>;
9
+ firstName: z.ZodString;
10
+ lastName: z.ZodString;
11
+ dateOfBirth: z.ZodString;
12
+ gender: z.ZodEnum<["M", "F"]>;
13
+ document: z.ZodOptional<z.ZodObject<{
14
+ type: z.ZodDefault<z.ZodEnum<["PASSPORT", "ID_CARD"]>>;
15
+ number: z.ZodString;
16
+ expiryDate: z.ZodOptional<z.ZodString>;
17
+ nationality: z.ZodOptional<z.ZodString>;
18
+ }, "strip", z.ZodTypeAny, {
19
+ number: string;
20
+ type: "PASSPORT" | "ID_CARD";
21
+ expiryDate?: string | undefined;
22
+ nationality?: string | undefined;
23
+ }, {
24
+ number: string;
25
+ type?: "PASSPORT" | "ID_CARD" | undefined;
26
+ expiryDate?: string | undefined;
27
+ nationality?: string | undefined;
28
+ }>>;
29
+ contacts: z.ZodOptional<z.ZodObject<{
30
+ email: z.ZodOptional<z.ZodString>;
31
+ phone: z.ZodOptional<z.ZodString>;
32
+ }, "strip", z.ZodTypeAny, {
33
+ email?: string | undefined;
34
+ phone?: string | undefined;
35
+ }, {
36
+ email?: string | undefined;
37
+ phone?: string | undefined;
38
+ }>>;
39
+ }, "strip", z.ZodTypeAny, {
40
+ firstName: string;
41
+ lastName: string;
42
+ dateOfBirth: string;
43
+ gender: "M" | "F";
44
+ type?: "adult" | "child" | "infant" | undefined;
45
+ document?: {
46
+ number: string;
47
+ type: "PASSPORT" | "ID_CARD";
48
+ expiryDate?: string | undefined;
49
+ nationality?: string | undefined;
50
+ } | undefined;
51
+ contacts?: {
52
+ email?: string | undefined;
53
+ phone?: string | undefined;
54
+ } | undefined;
55
+ }, {
56
+ firstName: string;
57
+ lastName: string;
58
+ dateOfBirth: string;
59
+ gender: "M" | "F";
60
+ type?: "adult" | "child" | "infant" | undefined;
61
+ document?: {
62
+ number: string;
63
+ type?: "PASSPORT" | "ID_CARD" | undefined;
64
+ expiryDate?: string | undefined;
65
+ nationality?: string | undefined;
66
+ } | undefined;
67
+ contacts?: {
68
+ email?: string | undefined;
69
+ phone?: string | undefined;
70
+ } | undefined;
71
+ }>, "many">;
72
+ idempotency_key: z.ZodOptional<z.ZodString>;
73
+ };
74
+ export declare function registerCreateOrder(server: McpServer, client: TravelCodeApiClient): void;
75
+ //# sourceMappingURL=create-order.d.ts.map
@@ -0,0 +1,52 @@
1
+ import { z } from "zod";
2
+ import { formatOrderDetail } from "../formatters/order-formatter.js";
3
+ const passengerDocumentSchema = z.object({
4
+ type: z.enum(["PASSPORT", "ID_CARD"]).default("PASSPORT").describe("Document type"),
5
+ number: z.string().describe("Document number"),
6
+ expiryDate: z.string().optional().describe("Expiry date (DD.MM.YYYY)"),
7
+ nationality: z.string().optional().describe("Country code ISO 2 (e.g. BY, RU, PL)"),
8
+ });
9
+ const passengerContactsSchema = z.object({
10
+ email: z.string().optional().describe("Email address"),
11
+ phone: z.string().optional().describe("Phone number with country code (e.g. +375291234567)"),
12
+ });
13
+ const passengerSchema = z.object({
14
+ type: z.enum(["adult", "child", "infant"]).optional().describe("Passenger type. If omitted, calculated from date of birth"),
15
+ firstName: z.string().describe("First name (Latin characters)"),
16
+ lastName: z.string().describe("Last name (Latin characters)"),
17
+ dateOfBirth: z.string().describe("Date of birth (DD.MM.YYYY)"),
18
+ gender: z.enum(["M", "F"]).describe("Gender"),
19
+ document: passengerDocumentSchema.optional().describe("Travel document details"),
20
+ contacts: passengerContactsSchema.optional().describe("Contact information (recommended for first passenger)"),
21
+ });
22
+ export const createOrderSchema = {
23
+ cache_id: z.string().describe("Cache ID from a previous search_flights result"),
24
+ offer_id: z.union([z.number(), z.string()]).describe("Offer index from the search results items array, or a specific orderId"),
25
+ passengers: z.array(passengerSchema).min(1).describe("Array of passenger details"),
26
+ idempotency_key: z.string().optional().describe("UUID for duplicate protection (recommended)"),
27
+ };
28
+ export function registerCreateOrder(server, client) {
29
+ server.tool("create_order", "Book a flight by creating an order from a search result. Requires a cache_id from search_flights, an offer_id (index in search results), and passenger details. The typical flow: search_flights → pick offer → create_order.", createOrderSchema, async ({ cache_id, offer_id, passengers, idempotency_key }) => {
30
+ try {
31
+ const extraHeaders = {};
32
+ if (idempotency_key) {
33
+ extraHeaders["Idempotency-Key"] = idempotency_key;
34
+ }
35
+ const order = await client.post("/orders", {
36
+ cacheId: cache_id,
37
+ offerId: offer_id,
38
+ passengers,
39
+ }, extraHeaders);
40
+ return {
41
+ content: [{ type: "text", text: formatOrderDetail(order) }],
42
+ };
43
+ }
44
+ catch (error) {
45
+ return {
46
+ content: [{ type: "text", text: `Error creating order: ${error.message}` }],
47
+ isError: true,
48
+ };
49
+ }
50
+ });
51
+ }
52
+ //# sourceMappingURL=create-order.js.map
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { TravelCodeApiClient } from "../client/api-client.js";
4
+ export declare const getAirportDelayStatsSchema: {
5
+ airport_code: z.ZodString;
6
+ date: z.ZodString;
7
+ };
8
+ export declare function registerGetAirportDelayStats(server: McpServer, client: TravelCodeApiClient): void;
9
+ //# sourceMappingURL=get-airport-delay-stats.d.ts.map
@@ -0,0 +1,31 @@
1
+ import { z } from "zod";
2
+ import { formatAirportDelayStats } from "../formatters/aerodatabox-formatter.js";
3
+ export const getAirportDelayStatsSchema = {
4
+ airport_code: z
5
+ .string()
6
+ .describe("IATA airport code (e.g. 'JFK', 'LHR', 'WAW')"),
7
+ date: z
8
+ .string()
9
+ .describe("Date in YYYY-MM-DD format (e.g. '2026-02-15')"),
10
+ };
11
+ export function registerGetAirportDelayStats(server, client) {
12
+ server.tool("get_airport_delay_stats", "Get current delay statistics for an airport on a specific date. Shows average departure/arrival delays and cancellation rates. Use this during weather events or disruptions to assess the situation at an airport.", getAirportDelayStatsSchema, async ({ airport_code, date }) => {
13
+ try {
14
+ const code = airport_code.toUpperCase();
15
+ const stats = await client.getAerodatabox(`/airports/iata/${encodeURIComponent(code)}/delays/${date}`);
16
+ return {
17
+ content: [{ type: "text", text: formatAirportDelayStats(stats, code, date) }],
18
+ };
19
+ }
20
+ catch (error) {
21
+ return {
22
+ content: [{
23
+ type: "text",
24
+ text: `Error getting delay stats for airport ${airport_code}: ${error.message}`,
25
+ }],
26
+ isError: true,
27
+ };
28
+ }
29
+ });
30
+ }
31
+ //# sourceMappingURL=get-airport-delay-stats.js.map
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { TravelCodeApiClient } from "../client/api-client.js";
4
+ export declare const getAirportFlightsSchema: {
5
+ airport_code: z.ZodString;
6
+ direction: z.ZodDefault<z.ZodEnum<["departure", "arrival"]>>;
7
+ from_time: z.ZodString;
8
+ to_time: z.ZodString;
9
+ include_codeshares: z.ZodDefault<z.ZodBoolean>;
10
+ include_cargo: z.ZodDefault<z.ZodBoolean>;
11
+ };
12
+ export declare function registerGetAirportFlights(server: McpServer, client: TravelCodeApiClient): void;
13
+ //# sourceMappingURL=get-airport-flights.d.ts.map