@pipeworx/mcp-aviationstack 0.1.2 → 0.1.3

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.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @pipeworx/aviationstack
2
2
 
3
- Aviationstack MCP — global flight tracking + airport / airline / route reference data. Free tier: 100 req/month.
3
+ Aviationstack MCP — global flight tracking + airport / airline / route reference data.
4
4
 
5
- Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 1683+ live data sources.
5
+ Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 1689+ live data sources. This is an independent, unofficial integration — not affiliated with, endorsed by, or published by the upstream provider.
6
6
 
7
7
  ## Tools
8
8
 
@@ -12,19 +12,29 @@ Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents
12
12
  - `cities(search?, iata_code?, country_iso2?, limit?)` — city → primary airport mapping
13
13
  - `countries(search?, country_iso2?, limit?)` — country reference
14
14
  - `routes(dep_iata?, arr_iata?, airline_iata?, flight_number?, limit?)` — scheduled routes
15
+ - `aviationstack_future_flights(iataCode, type, date, airline_iata?, airline_icao?, flight_number?, limit?, offset?)` — scheduled future flights at one airport for a specific date (`type`: `departure` or `arrival`). Backed by Aviationstack's `/flightsFuture` endpoint, which apilayer's 2026-10-06 notice opened only for the **next 7 days** — `date` must be `tomorrow..today+7` (UTC); the pack refuses out-of-window dates itself before calling upstream (fleet #2700).
15
16
 
16
17
  ## Auth
17
18
 
18
19
  - **Platform key:** gateway env `PLATFORM_AVIATIONSTACK_KEY`
19
20
  - **BYO:** `?_apiKey=<key>` after registering at https://aviationstack.com/signup/free
20
21
 
21
- ### Free-plan limits
22
+ ### Plan tier
22
23
 
23
- Only the `flights` endpoint works on the free tier. `airports`, `airlines`, `cities`, `countries`, `routes` return HTTP 403 `function_access_restricted`. Upgrade to Basic (~$50/mo) to unlock them. Free plan is HTTP-only (no https) — the pack already uses `http://`.
24
+ As of 2026-10-06 the shared platform key is on a plan that reaches `flights`, `airports`,
25
+ `airlines`, `cities`, `countries` and `routes` live (verified: `airports`/JFK and
26
+ `routes`/JFK→LAX both returned non-empty data — neither is free-tier-only any more; an
27
+ earlier version of this README, written 2026-08-28, said otherwise and was stale).
28
+ `aviationstack_future_flights` was added the same day the vendor emailed that they opened
29
+ `/flightsFuture` more broadly; whether our specific plan reaches *that* endpoint was not
30
+ independently confirmed before this tool shipped (the platform key's raw value is a
31
+ Cloudflare/Supabase secret, not available to a pack-authoring session) — if a live call
32
+ answers HTTP 403 `function_access_restricted`, that means the plan doesn't cover this
33
+ endpoint yet and is a money/plan decision, not a pack bug.
24
34
 
25
- The free plan is also capped at **100 requests per month**, and once that is spent every
26
- endpoint answers 429 — including the ones the tier would otherwise allow. As of 2026-08-28
27
- the shared key is in that state (`routes` and `airports`, zero successes in seven days).
35
+ The free plan (if the platform key ever reverts to it) is capped at **100 requests per
36
+ month**, and once that is spent every endpoint answers 429 — including ones the tier would
37
+ otherwise allow. Free plan is also HTTP-only (no https) — the pack already uses `http://`.
28
38
 
29
39
  **Reading a 429 here:** Aviationstack labels the monthly-allowance refusal with the code
30
40
  `rate_limit_reached` and only says "monthly" in the message text, so the code name argues
@@ -33,7 +43,8 @@ renews. The pack branches on the message, not the code. Fleet #577.
33
43
 
34
44
  ## Data source
35
45
 
36
- `http://api.aviationstack.com/v1/` — `access_key` query param.
46
+ `http://api.aviationstack.com/v1/` — `access_key` query param. `/flightsFuture` spec:
47
+ https://api.swaggerhub.com/apis/apilayer-863/AviationstackAPI/1.0.0/swagger.json
37
48
 
38
49
  ## Quick Start
39
50
 
@@ -79,7 +90,7 @@ directly, instead of just this one's:
79
90
  }
80
91
  ```
81
92
 
82
- Both URLs reach the same gateway and the same 1683+ data sources. The
93
+ Both URLs reach the same gateway and the same 1689+ data sources. The
83
94
  only difference is which pack's tools are listed **directly**; `ask_pipeworx`
84
95
  reaches all of them from either one.
85
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipeworx/mcp-aviationstack",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Aviationstack MCP — global flight + airport + airline data",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -26,7 +26,7 @@
26
26
  "@cloudflare/workers-types": "^4.20260405.1"
27
27
  },
28
28
  "pipeworx": {
29
- "sourceHash": "v1-17d9a3d7acdd3fb0a4f2e81daafc56c2e3068f281d2a5e064fece8a70284b339",
30
- "sourceCommit": "454041882179fae560c06e770b6b7a2f33970015"
29
+ "sourceHash": "v1-935ef0d23982e69db2037243fd0cba3475b438663a5b5a288b9f7e2189c770fe",
30
+ "sourceCommit": "211a6d307878cf994dd13cc72b7c2921b47759a7"
31
31
  }
32
32
  }
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.pipeworx-io/aviationstack",
4
4
  "title": "Aviationstack",
5
5
  "description": "Aviationstack MCP — global flight + airport + airline data",
6
- "version": "0.1.2",
6
+ "version": "0.1.3",
7
7
  "websiteUrl": "https://pipeworx.io/packs/aviationstack",
8
8
  "repository": {
9
9
  "url": "https://github.com/pipeworx-io/mcp-aviationstack",
package/src/index.ts CHANGED
@@ -777,6 +777,109 @@ const tools: McpToolExport['tools'] = [
777
777
  },
778
778
  },
779
779
  },
780
+ {
781
+ // apilayer emailed developer@mojibake.ai on 2026-10-06 ("What the new
782
+ // /flightsFuture range changes for your app"): they opened this endpoint
783
+ // to cover the next 7 days for itinerary/booking views. Vendor spec:
784
+ // https://api.swaggerhub.com/apis/apilayer-863/AviationstackAPI/1.0.0/swagger.json
785
+ // (paths./v1/flightsFuture — iataCode + type + date all required; date
786
+ // format YYYY-MM-DD). The 7-day window is the vendor's own notice, not a
787
+ // field the swagger spec encodes, so it is enforced here rather than left
788
+ // to a far-future call failing late and unexplained (fleet #2700).
789
+ name: 'aviationstack_future_flights',
790
+ description:
791
+ "Scheduled future flights at one airport, from Aviationstack's /flightsFuture endpoint — what flies a route on a day next week. Requires an IATA airport code, a direction (departure or arrival), and a date; returns each flight's number, airline, aircraft model, and scheduled terminal/gate/time. Aviationstack's booking-view window for this endpoint currently covers only the next 7 days (apilayer notice, 2026-10-06): date must be between tomorrow and 7 days from today — a date outside that window is refused before the call is made, not after a wasted round trip.",
792
+ inputSchema: {
793
+ type: 'object',
794
+ properties: {
795
+ iataCode: { type: 'string', description: '3-letter IATA airport code whose schedule to look up (e.g. "JFK")' },
796
+ type: {
797
+ type: 'string',
798
+ enum: ['departure', 'arrival'],
799
+ description: 'Whether to return flights scheduled to depart or arrive at iataCode',
800
+ },
801
+ date: {
802
+ type: 'string',
803
+ description:
804
+ 'Future date, format YYYY-MM-DD, must fall within tomorrow through 7 days from today (UTC) — Aviationstack\'s current flightsFuture window.',
805
+ },
806
+ airline_iata: { type: 'string', description: 'Filter by airline IATA code' },
807
+ airline_icao: { type: 'string', description: 'Filter by airline ICAO code' },
808
+ flight_number: { type: 'string', description: 'Filter by flight number' },
809
+ limit: { type: 'number', description: '1-100 (default 100)' },
810
+ offset: { type: 'number', description: '0-based offset' },
811
+ },
812
+ required: ['iataCode', 'type', 'date'],
813
+ },
814
+ outputSchema: {
815
+ type: 'object',
816
+ properties: {
817
+ pagination: {
818
+ type: 'object',
819
+ properties: {
820
+ limit: { type: 'number' },
821
+ offset: { type: 'number' },
822
+ count: { type: 'number' },
823
+ total: { type: 'number' },
824
+ },
825
+ },
826
+ data: {
827
+ type: 'array',
828
+ items: {
829
+ type: 'object',
830
+ properties: {
831
+ weekday: { type: ['string', 'null'], description: 'ISO weekday number 1-7 as a string' },
832
+ departure: {
833
+ type: 'object',
834
+ properties: {
835
+ iataCode: { type: ['string', 'null'] },
836
+ icaoCode: { type: ['string', 'null'] },
837
+ terminal: { type: ['string', 'null'] },
838
+ gate: { type: ['string', 'null'] },
839
+ scheduledTime: { type: ['string', 'null'], description: 'Local time, HH:MM' },
840
+ },
841
+ },
842
+ arrival: {
843
+ type: 'object',
844
+ properties: {
845
+ iataCode: { type: ['string', 'null'] },
846
+ icaoCode: { type: ['string', 'null'] },
847
+ terminal: { type: ['string', 'null'] },
848
+ gate: { type: ['string', 'null'] },
849
+ scheduledTime: { type: ['string', 'null'], description: 'Local time, HH:MM' },
850
+ },
851
+ },
852
+ aircraft: {
853
+ type: ['object', 'null'],
854
+ properties: {
855
+ modelCode: { type: ['string', 'null'] },
856
+ modelText: { type: ['string', 'null'] },
857
+ },
858
+ },
859
+ airline: {
860
+ type: 'object',
861
+ properties: {
862
+ name: { type: ['string', 'null'] },
863
+ iataCode: { type: ['string', 'null'] },
864
+ icaoCode: { type: ['string', 'null'] },
865
+ },
866
+ },
867
+ flight: {
868
+ type: 'object',
869
+ properties: {
870
+ number: { type: ['string', 'null'] },
871
+ iataNumber: { type: ['string', 'null'] },
872
+ icaoNumber: { type: ['string', 'null'] },
873
+ },
874
+ },
875
+ codeshared: { type: ['object', 'null'] },
876
+ },
877
+ },
878
+ },
879
+ data_as_of: { type: 'string', description: 'ISO timestamp when this response was fetched live from Aviationstack' },
880
+ },
881
+ },
882
+ },
780
883
  ];
781
884
 
782
885
  async function callTool(name: string, args: Record<string, unknown>): Promise<unknown> {
@@ -786,6 +889,11 @@ async function callTool(name: string, args: Record<string, unknown>): Promise<un
786
889
  'Aviationstack requires an API key. Contact the operator about platform credentials, or BYO via ?_apiKey=<key> after registering at https://aviationstack.com/signup/free.',
787
890
  );
788
891
  }
892
+
893
+ if (name === 'aviationstack_future_flights') {
894
+ return getFutureFlights(apiKey, args);
895
+ }
896
+
789
897
  const path = `/${name}`;
790
898
  const params = new URLSearchParams({ access_key: apiKey });
791
899
  for (const [k, v] of Object.entries(args)) {
@@ -795,6 +903,56 @@ async function callTool(name: string, args: Record<string, unknown>): Promise<un
795
903
  return avsGet(`${path}?${params}`);
796
904
  }
797
905
 
906
+ /**
907
+ * Validate the date is inside Aviationstack's current flightsFuture window
908
+ * (tomorrow through 7 days from today, UTC — apilayer notice, 2026-10-06)
909
+ * BEFORE spending a call on it. Refusing early and by name beats a vendor
910
+ * 200-with-empty-data or a late 4xx for a date the caller had no way to know
911
+ * was out of range.
912
+ */
913
+ function assertWithinFutureWindow(date: string): void {
914
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
915
+ throw new Error(`Aviationstack future_flights: date must be YYYY-MM-DD, got "${date}".`);
916
+ }
917
+ const requested = Date.parse(`${date}T00:00:00Z`);
918
+ if (Number.isNaN(requested)) {
919
+ throw new Error(`Aviationstack future_flights: "${date}" is not a valid calendar date.`);
920
+ }
921
+ const now = new Date();
922
+ const todayUtc = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
923
+ const DAY_MS = 86_400_000;
924
+ const minDate = todayUtc + DAY_MS; // tomorrow
925
+ const maxDate = todayUtc + 7 * DAY_MS; // today + 7
926
+ if (requested < minDate || requested > maxDate) {
927
+ const min = new Date(minDate).toISOString().slice(0, 10);
928
+ const max = new Date(maxDate).toISOString().slice(0, 10);
929
+ throw new Error(
930
+ `Aviationstack future_flights: "${date}" is outside the vendor's current flightsFuture window. Aviationstack opened this endpoint to only the next 7 days (apilayer notice, 2026-10-06) — valid range today is ${min} through ${max}. Pass a date inside that range.`,
931
+ );
932
+ }
933
+ }
934
+
935
+ async function getFutureFlights(apiKey: string, args: Record<string, unknown>): Promise<unknown> {
936
+ const iataCode = (args.iataCode as string | undefined)?.trim();
937
+ const type = args.type as string | undefined;
938
+ const date = (args.date as string | undefined)?.trim();
939
+ if (!iataCode) throw new Error('Aviationstack future_flights: iataCode is required (3-letter airport code, e.g. "JFK").');
940
+ if (type !== 'departure' && type !== 'arrival') {
941
+ throw new Error(`Aviationstack future_flights: type must be "departure" or "arrival", got "${type ?? ''}".`);
942
+ }
943
+ if (!date) throw new Error('Aviationstack future_flights: date is required (YYYY-MM-DD).');
944
+ assertWithinFutureWindow(date);
945
+
946
+ const params = new URLSearchParams({ access_key: apiKey, iataCode, type, date });
947
+ for (const k of ['airline_iata', 'airline_icao', 'flight_number', 'limit', 'offset'] as const) {
948
+ const v = args[k];
949
+ if (v === undefined || v === null || v === '') continue;
950
+ params.set(k, String(v));
951
+ }
952
+ const data = (await avsGet(`/flightsFuture?${params}`)) as Record<string, unknown>;
953
+ return { ...data, data_as_of: new Date().toISOString() };
954
+ }
955
+
798
956
  /**
799
957
  * Turn an Aviationstack limit refusal into a sentence that says which limit.
800
958
  *
package/src/server.ts CHANGED
@@ -9,7 +9,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprot
9
9
  import pack from './index.js';
10
10
 
11
11
  const server = new Server(
12
- { name: '@pipeworx/mcp-aviationstack', version: '0.1.2' },
12
+ { name: '@pipeworx/mcp-aviationstack', version: '0.1.3' },
13
13
  { capabilities: { tools: {} } },
14
14
  );
15
15