@flightbyte/mcp 0.0.0-stage → 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FlightByte
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,347 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <a href="https://flightbyte.dev">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="assets/logo-dark.svg">
5
+ <img src="assets/logo-light.svg" alt="FlightByte" width="340">
6
+ </picture>
7
+ </a>
8
+ </p>
2
9
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
10
+ <h3 align="center">The FlightByte MCP server</h3>
11
+
12
+ <p align="center">
13
+ Live flight status and flight webhooks as tools for your AI assistant.<br>
14
+ Look flights up, watch them with signed webhooks, debug deliveries and search the docs<br>
15
+ from Claude, Cursor, VS Code or any MCP client.
16
+ </p>
17
+
18
+ <p align="center">
19
+ <a href="https://www.npmjs.com/package/@flightbyte/mcp"><img src="https://img.shields.io/npm/v/@flightbyte/mcp?style=flat-square&color=EF6A1A&label=npm" alt="npm version"></a>
20
+ <a href="https://mcp.flightbyte.dev/healthz"><img src="https://img.shields.io/website?url=https%3A%2F%2Fmcp.flightbyte.dev%2Fhealthz&style=flat-square&label=hosted&up_message=online&up_color=EF6A1A" alt="Hosted server status"></a>
21
+ <img src="https://img.shields.io/badge/MCP-2025--11--25_%7C_2026--07--28-121212?style=flat-square" alt="MCP protocol versions">
22
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-121212?style=flat-square" alt="MIT license"></a>
23
+ </p>
24
+
25
+ <p align="center">
26
+ <a href="https://flightbyte.dev/docs/ai"><b>Docs</b></a> ·
27
+ <a href="https://app.flightbyte.dev/signup"><b>Get a free key</b></a> ·
28
+ <a href="https://flightbyte.dev/docs/reference"><b>API reference</b></a> ·
29
+ <a href="https://t.me/flightbyte"><b>Support</b></a>
30
+ </p>
31
+
32
+ <p align="center">
33
+ <a href="https://cursor.com/en/install-mcp?name=flightbyte&config=eyJ1cmwiOiJodHRwczovL21jcC5mbGlnaHRieXRlLmRldi9tY3AiLCJoZWFkZXJzIjp7IkF1dGhvcml6YXRpb24iOiJCZWFyZXIgWU9VUl9GTElHSFRCWVRFX0tFWSJ9fQ%3D%3D"><img src="https://cursor.com/deeplink/mcp-install-dark.svg" alt="Add to Cursor" height="32"></a>
34
+ &nbsp;
35
+ <a href="https://vscode.dev/redirect/mcp/install?name=flightbyte&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22flightbyte_key%22%2C%22description%22%3A%22FlightByte%20API%20key%22%2C%22password%22%3Atrue%7D%5D&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fmcp.flightbyte.dev%2Fmcp%22%2C%22headers%22%3A%7B%22Authorization%22%3A%22Bearer%20%24%7Binput%3Aflightbyte_key%7D%22%7D%7D"><img src="https://img.shields.io/badge/VS_Code-Install_server-0098FF?style=for-the-badge&logo=visualstudiocode&logoColor=white" alt="Install in VS Code" height="32"></a>
36
+ </p>
37
+
38
+ ---
39
+
40
+ ## Quick start
41
+
42
+ 1. Get a free API key at [app.flightbyte.dev/signup](https://app.flightbyte.dev/signup).
43
+ 2. Connect the server. Use the hosted one, or run it locally:
44
+
45
+ | | Address | Key |
46
+ | --- | --- | --- |
47
+ | **Hosted** | `https://mcp.flightbyte.dev/mcp` | `Authorization: Bearer <key>` header |
48
+ | **Local** | `npx -y @flightbyte/mcp` | `FLIGHTBYTE_KEY` environment variable |
49
+
50
+ In Claude Code, for example:
51
+
52
+ ```bash
53
+ claude mcp add --transport http flightbyte https://mcp.flightbyte.dev/mcp \
54
+ --header "Authorization: Bearer $FLIGHTBYTE_KEY"
55
+ ```
56
+
57
+ Every other client is under [Install](#install).
58
+ 3. Ask: *"Is TK1848 on time today?"*
59
+
60
+ The docs tools work without a key, so an assistant can read the documentation before you have signed up.
61
+
62
+ ## What you can ask
63
+
64
+ | You ask | What happens |
65
+ | --- | --- |
66
+ | *"Is TK1848 on time today? Which gate?"* | `get_flight` returns the live snapshot: phase, gate, terminal, delay, and the best known departure and arrival times. |
67
+ | *"TK1 from Istanbul tomorrow: when does it leave?"* | TK1 flies two legs that day. The assistant passes `origin: "IST"` and gets the right one instead of a guess. |
68
+ | *"Watch PC1001 on Friday and post every change to https://api.example.com/hooks/flights."* | `create_subscription` starts the watch and hands you the signing secret, shown once. |
69
+ | *"Our webhook stopped firing. Why?"* | `get_subscription` and `list_deliveries` show the status and the HTTP code and error of every recent attempt. |
70
+ | *"Add a FlightByte webhook receiver with signature verification to this Express app."* | `search_docs` and `get_doc_page` pull the exact steps from the docs, so the code matches the API rather than a guess. |
71
+
72
+ ## Tools
73
+
74
+ **Docs**: no key needed.
75
+
76
+ | Tool | What it does |
77
+ | --- | --- |
78
+ | `search_docs` | Keyword search over the whole documentation: docs, API reference, guides and glossary. Returns titles, links and snippets. |
79
+ | `get_doc_page` | Any docs page as Markdown, from a link or a path like `/docs/webhooks`. |
80
+
81
+ **Flights**: read-only.
82
+
83
+ | Tool | What it does |
84
+ | --- | --- |
85
+ | `get_flight` | Live status of one flight leg: phase, gates, terminals, baggage belt, scheduled, estimated and actual times, delay, aircraft, codeshares, and every leg the number flies that day.<br><sub>`GET /v1/flights/{carrier}/{flightNumber}/{date}`</sub> |
86
+ | `search_flights` | The same lookup with query parameters.<br><sub>`GET /v1/flights/search`</sub> |
87
+ | `get_flight_timeline` | The airline's own change history for a leg, newest first.<br><sub>`GET /v1/flights/{carrier}/{flightNumber}/{date}/timeline`</sub> |
88
+
89
+ **Subscriptions**
90
+
91
+ | Tool | What it does | Access |
92
+ | --- | --- | --- |
93
+ | `create_subscription` | Watch a flight and get a signed webhook for every change. Counts as a watched flight on your plan.<br><sub>`POST /v1/subscriptions`</sub> | writes |
94
+ | `list_subscriptions` | Your subscriptions, filtered by status, carrier, number or date.<br><sub>`GET /v1/subscriptions`</sub> | read-only |
95
+ | `get_subscription` | One subscription with its status and delivery counters.<br><sub>`GET /v1/subscriptions/{id}`</sub> | read-only |
96
+ | `update_subscription` | Change the webhook URL, event filter, delay threshold, headers or metadata.<br><sub>`PATCH /v1/subscriptions/{id}`</sub> | writes |
97
+ | `pause_subscription` | Stop polling and deliveries without losing anything.<br><sub>`POST /v1/subscriptions/{id}/pause`</sub> | writes |
98
+ | `resume_subscription` | Resume a paused or auto-suspended subscription.<br><sub>`POST /v1/subscriptions/{id}/resume`</sub> | writes |
99
+ | `delete_subscription` | Delete a subscription for good.<br><sub>`DELETE /v1/subscriptions/{id}`</sub> | **destructive** |
100
+ | `send_test_event` | Send a signed test webhook to the subscription's URL.<br><sub>`POST /v1/subscriptions/{id}/test`</sub> | writes |
101
+ | `list_deliveries` | Recent delivery attempts with HTTP status and errors.<br><sub>`GET /v1/subscriptions/{id}/deliveries`</sub> | read-only |
102
+ | `list_events` | Recent events with the exact payloads that were sent.<br><sub>`GET /v1/subscriptions/{id}/events`</sub> | read-only |
103
+
104
+ Parameters carry the API's own names (`carrier`, `flightNumber`, `date`, `origin`, `webhookUrl`, `Idempotency-Key`, …), and the test suite checks every one against the [OpenAPI document](https://flightbyte.dev/openapi.json). Results are the API's JSON, as text and as structured content.
105
+
106
+ ## Built for assistants
107
+
108
+ - **Your key stays yours.** The local server reads `FLIGHTBYTE_KEY`. The hosted server reads the key from each request's header, uses it for that request only, and never stores or logs it.
109
+ - **Secrets stay out of the chat.** There are no tools to reveal or rotate a webhook signing secret, because either would put it into the model's context and from there into transcripts. `create_subscription` returns the secret once, as the API does, and tells the assistant to hand it to you. Reveal or rotate secrets in the [dashboard](https://app.flightbyte.dev).
110
+ - **Clients know what is safe.** Read-only tools are marked read-only and `delete_subscription` is marked destructive, so clients can ask before running it. `create_subscription` tells the model it creates a billable watched flight and should only run when you ask for one.
111
+ - **Errors say what to do next.** A flight number that flies twice comes back with its legs and "pass `origin`". A `429` comes back with how long to wait. A `402 watched_flight_limit` says to stop, when the allowance resets, and where to upgrade.
112
+ - **Docs fetches stay on flightbyte.dev.** `get_doc_page` refuses every other host.
113
+
114
+ ## Install
115
+
116
+ Replace `YOUR_FLIGHTBYTE_KEY` with your key. Every client below can use the hosted server; the local one needs Node.js 20 or later.
117
+
118
+ <details open>
119
+ <summary><b>Claude Code</b></summary>
120
+
121
+ <br>
122
+
123
+ Hosted:
124
+
125
+ ```bash
126
+ claude mcp add --transport http flightbyte https://mcp.flightbyte.dev/mcp \
127
+ --header "Authorization: Bearer YOUR_FLIGHTBYTE_KEY"
128
+ ```
129
+
130
+ Local:
131
+
132
+ ```bash
133
+ claude mcp add flightbyte --env FLIGHTBYTE_KEY=YOUR_FLIGHTBYTE_KEY \
134
+ -- npx -y @flightbyte/mcp
135
+ ```
136
+
137
+ Add `--scope user` to have it in every project. To share it with a team through a committed `.mcp.json`, keep the key in each person's environment:
138
+
139
+ ```json
140
+ {
141
+ "mcpServers": {
142
+ "flightbyte": {
143
+ "type": "http",
144
+ "url": "https://mcp.flightbyte.dev/mcp",
145
+ "headers": { "Authorization": "Bearer ${FLIGHTBYTE_KEY}" }
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ </details>
152
+
153
+ <details>
154
+ <summary><b>Cursor</b></summary>
155
+
156
+ <br>
157
+
158
+ Click **Add to Cursor** at the top for the hosted server, or [this link for the local one](https://cursor.com/en/install-mcp?name=flightbyte&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBmbGlnaHRieXRlL21jcCJdLCJlbnYiOnsiRkxJR0hUQllURV9LRVkiOiJZT1VSX0ZMSUdIVEJZVEVfS0VZIn19), then replace `YOUR_FLIGHTBYTE_KEY` in the installed entry. Or add it to `~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project):
159
+
160
+ ```json
161
+ {
162
+ "mcpServers": {
163
+ "flightbyte": {
164
+ "url": "https://mcp.flightbyte.dev/mcp",
165
+ "headers": { "Authorization": "Bearer YOUR_FLIGHTBYTE_KEY" }
166
+ }
167
+ }
168
+ }
169
+ ```
170
+
171
+ Local:
172
+
173
+ ```json
174
+ {
175
+ "mcpServers": {
176
+ "flightbyte": {
177
+ "command": "npx",
178
+ "args": ["-y", "@flightbyte/mcp"],
179
+ "env": { "FLIGHTBYTE_KEY": "YOUR_FLIGHTBYTE_KEY" }
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ `"Bearer ${env:FLIGHTBYTE_KEY}"` also works, if Cursor was started from a shell that has the variable.
186
+
187
+ </details>
188
+
189
+ <details>
190
+ <summary><b>VS Code</b></summary>
191
+
192
+ <br>
193
+
194
+ Click **Install in VS Code** at the top: VS Code asks for the key and stores it securely. Or add it to `.vscode/mcp.json`:
195
+
196
+ ```json
197
+ {
198
+ "inputs": [
199
+ { "type": "promptString", "id": "flightbyte_key", "description": "FlightByte API key", "password": true }
200
+ ],
201
+ "servers": {
202
+ "flightbyte": {
203
+ "type": "http",
204
+ "url": "https://mcp.flightbyte.dev/mcp",
205
+ "headers": { "Authorization": "Bearer ${input:flightbyte_key}" }
206
+ }
207
+ }
208
+ }
209
+ ```
210
+
211
+ Local, from a terminal:
212
+
213
+ ```bash
214
+ code --add-mcp '{"name":"flightbyte","type":"stdio","command":"npx","args":["-y","@flightbyte/mcp"],"env":{"FLIGHTBYTE_KEY":"YOUR_FLIGHTBYTE_KEY"}}'
215
+ ```
216
+
217
+ </details>
218
+
219
+ <details>
220
+ <summary><b>Claude Desktop</b></summary>
221
+
222
+ <br>
223
+
224
+ Add the local server to `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`), then quit and reopen Claude:
225
+
226
+ ```json
227
+ {
228
+ "mcpServers": {
229
+ "flightbyte": {
230
+ "command": "npx",
231
+ "args": ["-y", "@flightbyte/mcp"],
232
+ "env": { "FLIGHTBYTE_KEY": "YOUR_FLIGHTBYTE_KEY" }
233
+ }
234
+ }
235
+ }
236
+ ```
237
+
238
+ The hosted server can also be added under **Settings → Connectors → Add custom connector** with the URL `https://mcp.flightbyte.dev/mcp`. Sending the key needs the connector's **Request headers** option (`authorization: Bearer YOUR_FLIGHTBYTE_KEY`), which is in beta and not on every account; without it only the docs tools work, so the local server is the reliable choice here.
239
+
240
+ </details>
241
+
242
+ <details>
243
+ <summary><b>Any other client</b></summary>
244
+
245
+ <br>
246
+
247
+ - **Streamable HTTP:** `https://mcp.flightbyte.dev/mcp` with the header `Authorization: Bearer YOUR_FLIGHTBYTE_KEY` (or `x-api-key: YOUR_FLIGHTBYTE_KEY`).
248
+ - **stdio:** command `npx`, arguments `-y @flightbyte/mcp`, environment `FLIGHTBYTE_KEY=YOUR_FLIGHTBYTE_KEY`.
249
+
250
+ Both speak MCP 2026-07-28 and the 2025 revisions (2024-11-05 to 2025-11-25); the client's first message picks one.
251
+
252
+ </details>
253
+
254
+ ## Configuration
255
+
256
+ | Variable | Used by | Default | Meaning |
257
+ | --- | --- | --- | --- |
258
+ | `FLIGHTBYTE_KEY` | local | none | Your API key. Without it the API tools explain how to set one and the docs tools still work. The hosted server ignores it and reads the key from each request. |
259
+ | `FLIGHTBYTE_API_URL` | both | `https://api.flightbyte.dev/v1` | API base URL, e.g. `http://localhost:3000/v1` for a local API. |
260
+ | `FLIGHTBYTE_DOCS_URL` | both | `https://flightbyte.dev` | Docs site that serves `/llms-full.txt` and `<page>.md`. |
261
+ | `PORT` | HTTP | `8080` | Listening port. |
262
+ | `HOST` | HTTP | `127.0.0.1` (`0.0.0.0` in Docker) | Listening address. |
263
+ | `RATE_LIMIT_PER_MINUTE` | HTTP | `120` | Requests to `/mcp` per client IP per minute; `0` turns it off. |
264
+ | `TRUST_PROXY` | HTTP | `0` (`1` in Docker) | How many reverse proxies' `X-Forwarded-For` entries to trust when finding the client IP. |
265
+ | `ALLOWED_HOSTS` | HTTP | loopback names on a loopback `HOST`, otherwise any | Comma-separated `Host` values accepted on `/mcp`, against DNS rebinding. |
266
+
267
+ ## Self-hosting
268
+
269
+ The hosted server is this repository's `Dockerfile`: a stateless Streamable HTTP server with no database and no secrets of its own.
270
+
271
+ ```bash
272
+ docker build -t flightbyte-mcp .
273
+ docker run -p 8080:8080 flightbyte-mcp
274
+ ```
275
+
276
+ It serves `POST /mcp` and `GET /healthz`. Each request gets a fresh server bound to the key in its headers; responses are plain JSON with no sessions, so it scales to any number of replicas. Requests in the 2026-07-28 format are served by the SDK's per-request handler, and 2025-era requests by a JSON-mode Streamable HTTP transport. Deployment notes for Dokploy are in [DEPLOY.md](DEPLOY.md).
277
+
278
+ ## Development
279
+
280
+ Running the server needs Node.js 20 or later; developing it needs 22.12 or later.
281
+
282
+ ```bash
283
+ npm install
284
+ npm run build # TypeScript → dist/
285
+ npm test # builds, then runs everything against a local mock of the API and docs
286
+ npm run typecheck
287
+ npm run lint
288
+ ```
289
+
290
+ The tests never touch the real API and need no key. They cover every tool's request mapping, the error mapping, docs parsing and ranking, and both transports end to end: the built `dist/stdio.js` and `dist/http.js` are spawned and driven by MCP clients on protocol 2025-11-25 and 2026-07-28, including one built on the v1 SDK. A drift check fails when a tool's parameter names, locations, required fields or enums stop matching `spec/openapi.json`, a copy of the [public OpenAPI document](https://flightbyte.dev/openapi.json). When the API changes:
291
+
292
+ ```bash
293
+ npm run sync:openapi
294
+ npm test
295
+ ```
296
+
297
+ Try it by hand with the MCP Inspector, or against a local API:
298
+
299
+ ```bash
300
+ npx @modelcontextprotocol/inspector node dist/stdio.js
301
+ ```
302
+
303
+ ```bash
304
+ FLIGHTBYTE_API_URL=http://localhost:3000/v1 FLIGHTBYTE_KEY=your-local-key node dist/stdio.js
305
+ ```
306
+
307
+ <details>
308
+ <summary><b>Project layout</b></summary>
309
+
310
+ ```text
311
+ src/
312
+ stdio.ts bin entry: stdio transport, key from FLIGHTBYTE_KEY
313
+ http.ts HTTP entry: POST /mcp, GET /healthz, env config
314
+ http-app.ts request handling, protocol routing, key extraction, per-IP rate limit
315
+ server.ts createServer(): registers every tool on an McpServer
316
+ tools/api-tools.ts the 13 API tools, one definition per OpenAPI operation
317
+ tools/docs-tools.ts search_docs and get_doc_page
318
+ docs.ts llms-full.txt parsing, ranking, caching, page fetches
319
+ api.ts fetch wrapper for the REST API
320
+ errors.ts error envelope → tool error with next steps
321
+ spec/openapi.json copy of the public OpenAPI document (npm run sync:openapi)
322
+ server.json MCP Registry entry: the npm package and the hosted remote
323
+ ```
324
+
325
+ </details>
326
+
327
+ <details>
328
+ <summary><b>Releasing</b></summary>
329
+
330
+ 1. Bump `version` in `package.json` and in both places in `server.json`; a test fails if they differ.
331
+ 2. `npm publish`. It runs typecheck, lint and tests first, and publishes publicly under the `@flightbyte` scope.
332
+ 3. Publish `server.json` to the [MCP Registry](https://registry.modelcontextprotocol.io): `mcp-publisher login dns --domain flightbyte.dev …`, then `mcp-publisher publish`.
333
+ 4. Redeploy the Docker image behind `mcp.flightbyte.dev`.
334
+
335
+ </details>
336
+
337
+ ## License
338
+
339
+ [MIT](LICENSE)
340
+
341
+ <br>
342
+
343
+ <p align="center">
344
+ <a href="https://flightbyte.dev"><img src="assets/icon.svg" alt="FlightByte" width="36"></a>
345
+ <br>
346
+ <sub><b>FlightByte</b> · Flight intelligence, byte by byte · <a href="https://flightbyte.dev">flightbyte.dev</a></sub>
347
+ </p>
package/dist/api.js ADDED
@@ -0,0 +1,80 @@
1
+ import { USER_AGENT } from './config.js';
2
+ /** A thin client over the public FlightByte REST API. It never throws for HTTP or network failures. */
3
+ export class ApiClient {
4
+ baseUrl;
5
+ hasKey;
6
+ #apiKey;
7
+ #timeoutMs;
8
+ #fetch;
9
+ constructor(options) {
10
+ this.baseUrl = options.baseUrl.replace(/\/+$/, '');
11
+ this.#apiKey = options.apiKey?.trim() || undefined;
12
+ this.hasKey = this.#apiKey !== undefined;
13
+ this.#timeoutMs = options.timeoutMs ?? 30_000;
14
+ this.#fetch = options.fetch ?? fetch;
15
+ }
16
+ url(path, query) {
17
+ const url = new URL(this.baseUrl + path);
18
+ for (const [name, value] of Object.entries(query ?? {})) {
19
+ if (value !== undefined)
20
+ url.searchParams.set(name, value);
21
+ }
22
+ return url.toString();
23
+ }
24
+ async request(req) {
25
+ const url = this.url(req.path, req.query);
26
+ const headers = {
27
+ accept: 'application/json',
28
+ 'user-agent': USER_AGENT,
29
+ };
30
+ if (this.#apiKey)
31
+ headers['x-api-key'] = this.#apiKey;
32
+ for (const [name, value] of Object.entries(req.headers ?? {})) {
33
+ if (value !== undefined)
34
+ headers[name] = value;
35
+ }
36
+ let body;
37
+ if (req.body !== undefined) {
38
+ headers['content-type'] = 'application/json';
39
+ body = JSON.stringify(req.body);
40
+ }
41
+ let res;
42
+ try {
43
+ res = await this.#fetch(url, {
44
+ method: req.method,
45
+ headers,
46
+ body,
47
+ signal: AbortSignal.timeout(this.#timeoutMs),
48
+ });
49
+ }
50
+ catch (err) {
51
+ const message = err instanceof Error && (err.name === 'TimeoutError' || err.name === 'AbortError')
52
+ ? `no response within ${Math.ceil(this.#timeoutMs / 1000)}s`
53
+ : describeFetchError(err);
54
+ return { kind: 'network_error', url, message };
55
+ }
56
+ const text = await res.text().catch(() => '');
57
+ const data = parseJson(text);
58
+ if (res.ok)
59
+ return { kind: 'ok', status: res.status, data, headers: res.headers };
60
+ return { kind: 'http_error', status: res.status, body: data, text, headers: res.headers };
61
+ }
62
+ }
63
+ function parseJson(text) {
64
+ if (text === '')
65
+ return undefined;
66
+ try {
67
+ return JSON.parse(text);
68
+ }
69
+ catch {
70
+ return undefined;
71
+ }
72
+ }
73
+ function describeFetchError(err) {
74
+ if (!(err instanceof Error))
75
+ return String(err);
76
+ const cause = err.cause;
77
+ if (cause instanceof Error && cause.message)
78
+ return `${err.message} (${cause.message})`;
79
+ return err.message;
80
+ }
package/dist/config.js ADDED
@@ -0,0 +1,21 @@
1
+ import { createRequire } from 'node:module';
2
+ const require = createRequire(import.meta.url);
3
+ const pkg = require('../package.json');
4
+ export const VERSION = pkg.version;
5
+ export const USER_AGENT = `flightbyte-mcp/${VERSION}`;
6
+ export const DEFAULT_API_URL = 'https://api.flightbyte.dev/v1';
7
+ export const DEFAULT_DOCS_URL = 'https://flightbyte.dev';
8
+ /** Where to get a key, and how to hand one to this server. Shown whenever a key is missing or rejected. */
9
+ export const SIGNUP_URL = 'https://app.flightbyte.dev/signup';
10
+ export const KEYS_URL = 'https://app.flightbyte.dev/keys';
11
+ export const PLAN_URL = 'https://app.flightbyte.dev/plan';
12
+ export const REMOTE_URL = 'https://mcp.flightbyte.dev/mcp';
13
+ function trimSlash(url) {
14
+ return url.replace(/\/+$/, '');
15
+ }
16
+ export function loadConfig(env = process.env) {
17
+ return {
18
+ apiUrl: trimSlash(env.FLIGHTBYTE_API_URL?.trim() || DEFAULT_API_URL),
19
+ docsUrl: trimSlash(env.FLIGHTBYTE_DOCS_URL?.trim() || DEFAULT_DOCS_URL),
20
+ };
21
+ }