├── .env.example
├── .gitignore
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── src
├── config.ts
├── index.ts
├── test.ts
├── types.ts
└── types
│ ├── global.d.ts
│ └── modelcontextprotocol-sdk.d.ts
└── tsconfig.json
/.env.example:
--------------------------------------------------------------------------------
1 | # Flightradar24 API Configuration
2 | FR24_API_KEY=your-api-key-here
3 | # Flightradar24 API URL, shouldn't need to change this
4 | FR24_API_URL=https://fr24api.flightradar24.com
5 | # Server Configuration
6 | PORT=3000
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | dist/
3 | node_modules/
4 | build/
5 | *.log
6 | .env
7 | .env.local
8 | .env.*.local
9 |
10 | # Keep example file
11 | !.env.example
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 sunsetcoder
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flightradar24 MCP Server 🛩️
2 |
3 | A Claude Desktop MCP server that helps you track flights in real-time using Flightradar24 data. Perfect for aviation enthusiasts, travel planners, or anyone curious about flights overhead!
4 |
5 | ## What Can This Do? ✨
6 |
7 | - 🔍 Track any flight in real-time
8 | - ⏰ Get arrival and departure times for specific flights
9 | - 🌉 View the status of flights at an airport
10 | - 🚨 Monitor emergency flights
11 |
12 |
13 |
14 | ## Setup Guide 🚀
15 |
16 | ### 1. Prerequisites
17 | - [Claude Desktop](https://claude.ai/desktop) installed on your computer
18 | - A Flightradar24 API key (get one from [Flightradar24's website](https://www.flightradar24.com/premium))*
19 |
20 | ### 2. Installation
21 |
22 | 1. Clone this repository somewhere on your computer:
23 | ```bash
24 | git clone https://github.com/sunsetcoder/flightradar24-mcp-server.git
25 | ```
26 |
27 | 2. Install dependencies & build the project:
28 | ```bash
29 | cd flightradar24-mcp-server
30 | npm install
31 | npm run build
32 | ```
33 |
34 | ### 3. Integration with Claude Desktop
35 |
36 | 1. Open your Claude Desktop configuration file:
37 | ```
38 | # On Mac:
39 | ~/Library/Application Support/Claude/claude_desktop_config.json
40 |
41 | # On Windows:
42 | %APPDATA%/Claude/claude_desktop_config.json
43 | ```
44 |
45 | 2. Add the following to the `mcpServers` object in your config:
46 | ```json
47 | {
48 | "mcpServers": {
49 | "flightradar24-server": {
50 | "command": "node",
51 | "args": [
52 | "/Users///flightradar24-mcp-server/dist/index.js"
53 | ],
54 | "env": {
55 | "FR24_API_KEY": "your_api_key_here",
56 | "FR24_API_URL": "https://fr24api.flightradar24.com"
57 | }
58 | }
59 | }
60 | }
61 | ```
62 |
63 | 3. Important Steps:
64 | - Replace `/FULL/PATH/TO/flightradar24-mcp-server` with the actual full path to where you cloned the repository
65 | - Add your Flightradar24 API key in the `env` section
66 | - Make sure to use forward slashes (`/`) in the path, even on Windows
67 |
68 | 4. Restart Claude Desktop for the changes to take effect
69 |
70 | ## Environment Setup
71 |
72 | 1. Copy `.env.example` to `.env`:
73 | ```bash
74 | cp .env.example .env
75 | ```
76 |
77 | 2. Update the `.env` file with your actual Flightradar24 API key:
78 | ```env
79 | FR24_API_KEY=your_actual_api_key_here
80 | ```
81 |
82 | Note: Never commit your actual API key to version control. The `.env` file is ignored by git for security reasons.
83 |
84 | ## Let's Try It Out! 🎮
85 |
86 | Once the server is configured, you can ask Claude questions like:
87 |
88 | 1. "What's the ETA for United Airlines flight UA123?"
89 | 2. "Show me all flights currently at SFO"
90 | 3. "Are there any emergency flights in the area?"
91 | 4. "Show me all international flights arriving at SFO in the next 2 hours"
92 | 5. "How many commercial flights are currently over the Pacific Ocean?"
93 | 6. "Identify any flights that have declared an emergency in the California region"
94 |
95 | Example conversation with Claude:
96 | ```
97 | You: What's the status of flight UA123?
98 | Claude: Let me check that for you...
99 | [Claude will use the MCP server to fetch real-time flight information]
100 | ```
101 |
102 | ## Common Questions & Troubleshooting 🤔
103 |
104 | ### "Claude can't connect to the server"
105 | - Check if the path in `claude_desktop_config.json` is correct
106 | - Make sure you're using the full absolute path
107 | - Verify your API key is correct
108 | - Try restarting Claude Desktop
109 |
110 | ### "The server isn't responding"
111 | - Make sure your Flightradar24 API key is valid
112 | - Check if the API URL is correct
113 | - Look for any error messages in server logs
114 |
115 | ### FlightRadar API Access
116 | - Note: Using Flightradar24's API requires a [subscription](https://fr24api.flightradar24.com/subscriptions-and-credits)
117 |
118 | ## Need More Help? 🆘
119 |
120 | 1. Make sure Claude Desktop is properly installed
121 | 2. Verify your Flightradar24 API key is active
122 | 3. Check the path in your configuration file is correct
123 | 4. Look for error messages in MCP server logs
124 |
125 | ## License 📄
126 |
127 | MIT
128 |
129 | ---
130 |
131 | Made with ❤️ for aviation enthusiasts
132 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flightradar24-mcp-server",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "flightradar24-mcp-server",
9 | "version": "1.0.0",
10 | "license": "MIT",
11 | "dependencies": {
12 | "@modelcontextprotocol/sdk": "latest",
13 | "axios": "^1.6.0",
14 | "dotenv": "^16.4.7",
15 | "zod": "^3.22.4"
16 | },
17 | "devDependencies": {
18 | "@types/express": "^4.17.21",
19 | "@types/node": "^20.10.4",
20 | "tsc-watch": "^6.0.4",
21 | "typescript": "^5.3.3"
22 | }
23 | },
24 | "node_modules/@modelcontextprotocol/sdk": {
25 | "version": "1.0.3",
26 | "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.0.3.tgz",
27 | "integrity": "sha512-2as3cX/VJ0YBHGmdv3GFyTpoM8q2gqE98zh3Vf1NwnsSY0h3mvoO07MUzfygCKkWsFjcZm4otIiqD6Xh7kiSBQ==",
28 | "dependencies": {
29 | "content-type": "^1.0.5",
30 | "raw-body": "^3.0.0",
31 | "zod": "^3.23.8"
32 | }
33 | },
34 | "node_modules/@types/body-parser": {
35 | "version": "1.19.5",
36 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
37 | "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
38 | "dev": true,
39 | "dependencies": {
40 | "@types/connect": "*",
41 | "@types/node": "*"
42 | }
43 | },
44 | "node_modules/@types/connect": {
45 | "version": "3.4.38",
46 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
47 | "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
48 | "dev": true,
49 | "dependencies": {
50 | "@types/node": "*"
51 | }
52 | },
53 | "node_modules/@types/express": {
54 | "version": "4.17.21",
55 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
56 | "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
57 | "dev": true,
58 | "dependencies": {
59 | "@types/body-parser": "*",
60 | "@types/express-serve-static-core": "^4.17.33",
61 | "@types/qs": "*",
62 | "@types/serve-static": "*"
63 | }
64 | },
65 | "node_modules/@types/express-serve-static-core": {
66 | "version": "4.19.6",
67 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz",
68 | "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==",
69 | "dev": true,
70 | "dependencies": {
71 | "@types/node": "*",
72 | "@types/qs": "*",
73 | "@types/range-parser": "*",
74 | "@types/send": "*"
75 | }
76 | },
77 | "node_modules/@types/http-errors": {
78 | "version": "2.0.4",
79 | "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
80 | "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
81 | "dev": true
82 | },
83 | "node_modules/@types/mime": {
84 | "version": "1.3.5",
85 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
86 | "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
87 | "dev": true
88 | },
89 | "node_modules/@types/node": {
90 | "version": "20.17.9",
91 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz",
92 | "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==",
93 | "dev": true,
94 | "license": "MIT",
95 | "dependencies": {
96 | "undici-types": "~6.19.2"
97 | }
98 | },
99 | "node_modules/@types/qs": {
100 | "version": "6.9.17",
101 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz",
102 | "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==",
103 | "dev": true
104 | },
105 | "node_modules/@types/range-parser": {
106 | "version": "1.2.7",
107 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
108 | "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
109 | "dev": true
110 | },
111 | "node_modules/@types/send": {
112 | "version": "0.17.4",
113 | "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
114 | "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
115 | "dev": true,
116 | "dependencies": {
117 | "@types/mime": "^1",
118 | "@types/node": "*"
119 | }
120 | },
121 | "node_modules/@types/serve-static": {
122 | "version": "1.15.7",
123 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
124 | "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
125 | "dev": true,
126 | "dependencies": {
127 | "@types/http-errors": "*",
128 | "@types/node": "*",
129 | "@types/send": "*"
130 | }
131 | },
132 | "node_modules/asynckit": {
133 | "version": "0.4.0",
134 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
135 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
136 | "license": "MIT"
137 | },
138 | "node_modules/axios": {
139 | "version": "1.7.9",
140 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
141 | "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
142 | "license": "MIT",
143 | "dependencies": {
144 | "follow-redirects": "^1.15.6",
145 | "form-data": "^4.0.0",
146 | "proxy-from-env": "^1.1.0"
147 | }
148 | },
149 | "node_modules/bytes": {
150 | "version": "3.1.2",
151 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
152 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
153 | "license": "MIT",
154 | "engines": {
155 | "node": ">= 0.8"
156 | }
157 | },
158 | "node_modules/combined-stream": {
159 | "version": "1.0.8",
160 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
161 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
162 | "license": "MIT",
163 | "dependencies": {
164 | "delayed-stream": "~1.0.0"
165 | },
166 | "engines": {
167 | "node": ">= 0.8"
168 | }
169 | },
170 | "node_modules/content-type": {
171 | "version": "1.0.5",
172 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
173 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
174 | "license": "MIT",
175 | "engines": {
176 | "node": ">= 0.6"
177 | }
178 | },
179 | "node_modules/cross-spawn": {
180 | "version": "7.0.6",
181 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
182 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
183 | "dev": true,
184 | "dependencies": {
185 | "path-key": "^3.1.0",
186 | "shebang-command": "^2.0.0",
187 | "which": "^2.0.1"
188 | },
189 | "engines": {
190 | "node": ">= 8"
191 | }
192 | },
193 | "node_modules/delayed-stream": {
194 | "version": "1.0.0",
195 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
196 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
197 | "license": "MIT",
198 | "engines": {
199 | "node": ">=0.4.0"
200 | }
201 | },
202 | "node_modules/depd": {
203 | "version": "2.0.0",
204 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
205 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
206 | "license": "MIT",
207 | "engines": {
208 | "node": ">= 0.8"
209 | }
210 | },
211 | "node_modules/dotenv": {
212 | "version": "16.4.7",
213 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
214 | "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
215 | "engines": {
216 | "node": ">=12"
217 | },
218 | "funding": {
219 | "url": "https://dotenvx.com"
220 | }
221 | },
222 | "node_modules/duplexer": {
223 | "version": "0.1.2",
224 | "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
225 | "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
226 | "dev": true
227 | },
228 | "node_modules/event-stream": {
229 | "version": "3.3.4",
230 | "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
231 | "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==",
232 | "dev": true,
233 | "dependencies": {
234 | "duplexer": "~0.1.1",
235 | "from": "~0",
236 | "map-stream": "~0.1.0",
237 | "pause-stream": "0.0.11",
238 | "split": "0.3",
239 | "stream-combiner": "~0.0.4",
240 | "through": "~2.3.1"
241 | }
242 | },
243 | "node_modules/follow-redirects": {
244 | "version": "1.15.9",
245 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
246 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
247 | "funding": [
248 | {
249 | "type": "individual",
250 | "url": "https://github.com/sponsors/RubenVerborgh"
251 | }
252 | ],
253 | "license": "MIT",
254 | "engines": {
255 | "node": ">=4.0"
256 | },
257 | "peerDependenciesMeta": {
258 | "debug": {
259 | "optional": true
260 | }
261 | }
262 | },
263 | "node_modules/form-data": {
264 | "version": "4.0.1",
265 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
266 | "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
267 | "license": "MIT",
268 | "dependencies": {
269 | "asynckit": "^0.4.0",
270 | "combined-stream": "^1.0.8",
271 | "mime-types": "^2.1.12"
272 | },
273 | "engines": {
274 | "node": ">= 6"
275 | }
276 | },
277 | "node_modules/from": {
278 | "version": "0.1.7",
279 | "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
280 | "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==",
281 | "dev": true
282 | },
283 | "node_modules/http-errors": {
284 | "version": "2.0.0",
285 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
286 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
287 | "license": "MIT",
288 | "dependencies": {
289 | "depd": "2.0.0",
290 | "inherits": "2.0.4",
291 | "setprototypeof": "1.2.0",
292 | "statuses": "2.0.1",
293 | "toidentifier": "1.0.1"
294 | },
295 | "engines": {
296 | "node": ">= 0.8"
297 | }
298 | },
299 | "node_modules/iconv-lite": {
300 | "version": "0.6.3",
301 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
302 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
303 | "dependencies": {
304 | "safer-buffer": ">= 2.1.2 < 3.0.0"
305 | },
306 | "engines": {
307 | "node": ">=0.10.0"
308 | }
309 | },
310 | "node_modules/inherits": {
311 | "version": "2.0.4",
312 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
313 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
314 | "license": "ISC"
315 | },
316 | "node_modules/isexe": {
317 | "version": "2.0.0",
318 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
319 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
320 | "dev": true
321 | },
322 | "node_modules/map-stream": {
323 | "version": "0.1.0",
324 | "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
325 | "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==",
326 | "dev": true
327 | },
328 | "node_modules/mime-db": {
329 | "version": "1.52.0",
330 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
331 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
332 | "license": "MIT",
333 | "engines": {
334 | "node": ">= 0.6"
335 | }
336 | },
337 | "node_modules/mime-types": {
338 | "version": "2.1.35",
339 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
340 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
341 | "license": "MIT",
342 | "dependencies": {
343 | "mime-db": "1.52.0"
344 | },
345 | "engines": {
346 | "node": ">= 0.6"
347 | }
348 | },
349 | "node_modules/node-cleanup": {
350 | "version": "2.1.2",
351 | "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz",
352 | "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==",
353 | "dev": true
354 | },
355 | "node_modules/path-key": {
356 | "version": "3.1.1",
357 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
358 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
359 | "dev": true,
360 | "engines": {
361 | "node": ">=8"
362 | }
363 | },
364 | "node_modules/pause-stream": {
365 | "version": "0.0.11",
366 | "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
367 | "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==",
368 | "dev": true,
369 | "dependencies": {
370 | "through": "~2.3"
371 | }
372 | },
373 | "node_modules/proxy-from-env": {
374 | "version": "1.1.0",
375 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
376 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
377 | "license": "MIT"
378 | },
379 | "node_modules/ps-tree": {
380 | "version": "1.2.0",
381 | "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
382 | "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==",
383 | "dev": true,
384 | "dependencies": {
385 | "event-stream": "=3.3.4"
386 | },
387 | "bin": {
388 | "ps-tree": "bin/ps-tree.js"
389 | },
390 | "engines": {
391 | "node": ">= 0.10"
392 | }
393 | },
394 | "node_modules/raw-body": {
395 | "version": "3.0.0",
396 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
397 | "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
398 | "dependencies": {
399 | "bytes": "3.1.2",
400 | "http-errors": "2.0.0",
401 | "iconv-lite": "0.6.3",
402 | "unpipe": "1.0.0"
403 | },
404 | "engines": {
405 | "node": ">= 0.8"
406 | }
407 | },
408 | "node_modules/safer-buffer": {
409 | "version": "2.1.2",
410 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
411 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
412 | "license": "MIT"
413 | },
414 | "node_modules/setprototypeof": {
415 | "version": "1.2.0",
416 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
417 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
418 | "license": "ISC"
419 | },
420 | "node_modules/shebang-command": {
421 | "version": "2.0.0",
422 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
423 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
424 | "dev": true,
425 | "dependencies": {
426 | "shebang-regex": "^3.0.0"
427 | },
428 | "engines": {
429 | "node": ">=8"
430 | }
431 | },
432 | "node_modules/shebang-regex": {
433 | "version": "3.0.0",
434 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
435 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
436 | "dev": true,
437 | "engines": {
438 | "node": ">=8"
439 | }
440 | },
441 | "node_modules/split": {
442 | "version": "0.3.3",
443 | "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
444 | "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==",
445 | "dev": true,
446 | "dependencies": {
447 | "through": "2"
448 | },
449 | "engines": {
450 | "node": "*"
451 | }
452 | },
453 | "node_modules/statuses": {
454 | "version": "2.0.1",
455 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
456 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
457 | "license": "MIT",
458 | "engines": {
459 | "node": ">= 0.8"
460 | }
461 | },
462 | "node_modules/stream-combiner": {
463 | "version": "0.0.4",
464 | "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
465 | "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==",
466 | "dev": true,
467 | "dependencies": {
468 | "duplexer": "~0.1.1"
469 | }
470 | },
471 | "node_modules/string-argv": {
472 | "version": "0.3.2",
473 | "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
474 | "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
475 | "dev": true,
476 | "engines": {
477 | "node": ">=0.6.19"
478 | }
479 | },
480 | "node_modules/through": {
481 | "version": "2.3.8",
482 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
483 | "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
484 | "dev": true
485 | },
486 | "node_modules/toidentifier": {
487 | "version": "1.0.1",
488 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
489 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
490 | "license": "MIT",
491 | "engines": {
492 | "node": ">=0.6"
493 | }
494 | },
495 | "node_modules/tsc-watch": {
496 | "version": "6.2.1",
497 | "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-6.2.1.tgz",
498 | "integrity": "sha512-GLwdz5Dy9K3sVm3RzgkLcyDpl5cvU9HEcE1A3gf5rqEwlUe7gDLxNCgcuNEw3zoKOiegMo3LnbF1t6HLqxhrSA==",
499 | "dev": true,
500 | "dependencies": {
501 | "cross-spawn": "^7.0.3",
502 | "node-cleanup": "^2.1.2",
503 | "ps-tree": "^1.2.0",
504 | "string-argv": "^0.3.1"
505 | },
506 | "bin": {
507 | "tsc-watch": "dist/lib/tsc-watch.js"
508 | },
509 | "engines": {
510 | "node": ">=12.12.0"
511 | },
512 | "peerDependencies": {
513 | "typescript": "*"
514 | }
515 | },
516 | "node_modules/typescript": {
517 | "version": "5.7.2",
518 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
519 | "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
520 | "dev": true,
521 | "license": "Apache-2.0",
522 | "bin": {
523 | "tsc": "bin/tsc",
524 | "tsserver": "bin/tsserver"
525 | },
526 | "engines": {
527 | "node": ">=14.17"
528 | }
529 | },
530 | "node_modules/undici-types": {
531 | "version": "6.19.8",
532 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
533 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
534 | "dev": true,
535 | "license": "MIT"
536 | },
537 | "node_modules/unpipe": {
538 | "version": "1.0.0",
539 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
540 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
541 | "license": "MIT",
542 | "engines": {
543 | "node": ">= 0.8"
544 | }
545 | },
546 | "node_modules/which": {
547 | "version": "2.0.2",
548 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
549 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
550 | "dev": true,
551 | "dependencies": {
552 | "isexe": "^2.0.0"
553 | },
554 | "bin": {
555 | "node-which": "bin/node-which"
556 | },
557 | "engines": {
558 | "node": ">= 8"
559 | }
560 | },
561 | "node_modules/zod": {
562 | "version": "3.24.1",
563 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
564 | "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
565 | "funding": {
566 | "url": "https://github.com/sponsors/colinhacks"
567 | }
568 | }
569 | }
570 | }
571 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flightradar24-mcp-server",
3 | "version": "1.0.0",
4 | "description": "Model Context Protocol server for FlightRadar24 API",
5 | "main": "build/index.js",
6 | "type": "module",
7 | "scripts": {
8 | "build": "tsc",
9 | "start": "node dist/index.js",
10 | "dev": "ts-node src/index.ts",
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "keywords": [
14 | "flightradar24",
15 | "api",
16 | "mcp",
17 | "flight-tracking"
18 | ],
19 | "author": "",
20 | "license": "MIT",
21 | "dependencies": {
22 | "@modelcontextprotocol/sdk": "latest",
23 | "axios": "^1.6.0",
24 | "dotenv": "^16.4.7",
25 | "zod": "^3.22.4"
26 | },
27 | "devDependencies": {
28 | "@types/express": "^4.17.21",
29 | "@types/node": "^20.10.4",
30 | "tsc-watch": "^6.0.4",
31 | "typescript": "^5.3.3"
32 | }
33 | }
--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod'
2 | import dotenv from 'dotenv'
3 |
4 | // Load environment variables
5 | dotenv.config()
6 |
7 | /**
8 | * Environment variable schema validation
9 | */
10 | const envSchema = z.object({
11 | FR24_API_KEY: z.string({
12 | required_error: "FR24_API_KEY is required in environment variables",
13 | invalid_type_error: "FR24_API_KEY must be a string"
14 | }).min(1, { message: "FR24_API_KEY cannot be empty" }),
15 | FR24_API_URL: z.string({
16 | required_error: "FR24_API_URL is required in environment variables",
17 | invalid_type_error: "FR24_API_URL must be a string"
18 | }).url({ message: "FR24_API_URL must be a valid URL" })
19 | })
20 |
21 | /**
22 | * Validate and extract environment variables
23 | * @throws {Error} If validation fails
24 | */
25 | export const env = envSchema.parse(process.env)
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4 | import {
5 | ListToolsRequestSchema,
6 | CallToolRequestSchema,
7 | ErrorCode,
8 | McpError,
9 | ListToolsRequest,
10 | CallToolRequest
11 | } from '@modelcontextprotocol/sdk/types.js';
12 | import axios from 'axios';
13 | import {
14 | FlightTrackingParams,
15 | FlightPositionsResponse,
16 | FlightETAResponse,
17 | isValidFlightTrackingParams
18 | } from './types.js';
19 | import { env } from './config.js';
20 |
21 | // API client configuration
22 | const fr24Client = axios.create({
23 | baseURL: env.FR24_API_URL,
24 | headers: {
25 | 'Accept': 'application/json',
26 | 'Accept-Version': 'v1',
27 | 'Authorization': `Bearer ${env.FR24_API_KEY}`
28 | }
29 | });
30 |
31 | /**
32 | * MCP Server implementation for Flightradar24 API
33 | * Provides tools for tracking flights and monitoring air traffic
34 | */
35 | class FlightTrackingServer {
36 | private server: Server;
37 |
38 | constructor() {
39 | this.server = new Server(
40 | { name: 'flightradar24-server', version: '1.0.0' },
41 | {
42 | capabilities: {
43 | tools: {},
44 | jsonrpc: true // Add this to enable JSON-RPC support
45 | }
46 | }
47 | );
48 |
49 | this.setupHandlers();
50 | this.setupErrorHandling();
51 | }
52 |
53 | private setupErrorHandling(): void {
54 | this.server.onerror = (error) => {
55 | console.error('[MCP Error]', error);
56 | };
57 |
58 | process.on('SIGINT', async () => {
59 | await this.server.close();
60 | process.exit(0);
61 | });
62 | }
63 |
64 | private setupHandlers(): void {
65 | this.setupToolHandlers();
66 | }
67 |
68 | /**
69 | * Configure available tools and their handlers
70 | */
71 | private setupToolHandlers(): void {
72 | this.server.setRequestHandler(ListToolsRequestSchema, async (request: ListToolsRequest) => ({
73 | tools: [
74 | {
75 | name: 'get_flight_positions',
76 | description: 'Get real-time flight positions with various filtering options',
77 | inputSchema: {
78 | type: 'object',
79 | properties: {
80 | airports: { type: 'string', description: 'Comma-separated list of airport ICAO codes' },
81 | bounds: { type: 'string', description: 'Geographical bounds (lat1,lon1,lat2,lon2)' },
82 | categories: { type: 'string', description: 'Aircraft categories (P,C,J)' },
83 | limit: { type: 'number', description: 'Maximum number of results' }
84 | }
85 | }
86 | },
87 | {
88 | name: 'get_flight_eta',
89 | description: 'Get estimated arrival time for a specific flight',
90 | inputSchema: {
91 | type: 'object',
92 | properties: {
93 | flightNumber: {
94 | type: 'string',
95 | description: 'Flight number (e.g., UA123)',
96 | pattern: '^[A-Z0-9]{2,8}$'
97 | }
98 | },
99 | required: ['flightNumber']
100 | }
101 | }
102 | ]
103 | }));
104 |
105 | this.server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {
106 | try {
107 | const toolName = request.params.name;
108 | const toolArgs = request.params.arguments;
109 |
110 | switch (toolName) {
111 | case 'get_flight_positions': {
112 | const params: FlightTrackingParams = {
113 | ...toolArgs,
114 | limit: toolArgs?.limit ? parseInt(toolArgs.limit as string) : undefined
115 | };
116 |
117 | if (!isValidFlightTrackingParams(params)) {
118 | throw new McpError(
119 | ErrorCode.InvalidParams,
120 | 'Invalid or missing query parameters. At least one valid parameter is required.'
121 | );
122 | }
123 |
124 | const response = await fr24Client.get(
125 | '/api/live/flight-positions/light',
126 | { params }
127 | );
128 |
129 | return {
130 | content: [{
131 | type: "text",
132 | text: JSON.stringify(response.data, null, 2)
133 | }]
134 | };
135 | }
136 |
137 | case 'get_flight_eta': {
138 | const { flightNumber } = toolArgs || {};
139 | const flightNumberStr = String(flightNumber || '');
140 |
141 | if (!flightNumberStr || !/^[A-Z0-9]{2,8}$/.test(flightNumberStr)) {
142 | throw new McpError(
143 | ErrorCode.InvalidParams,
144 | 'Invalid flight number format'
145 | );
146 | }
147 |
148 | const response = await fr24Client.get(
149 | '/api/flights/detail',
150 | {
151 | params: {
152 | flight: flightNumberStr,
153 | format: 'eta'
154 | }
155 | }
156 | );
157 |
158 | return {
159 | content: [{
160 | type: "text",
161 | text: JSON.stringify(response.data, null, 2)
162 | }]
163 | };
164 | }
165 |
166 | default:
167 | throw new McpError(
168 | ErrorCode.MethodNotFound,
169 | `Unknown tool: ${toolName}`
170 | );
171 | }
172 | } catch (error) {
173 | if (axios.isAxiosError(error)) {
174 | return {
175 | isError: true,
176 | content: [{
177 | type: "text",
178 | text: `Flightradar24 API error: ${error.response?.data?.message || error.message || 'Unknown error'}`
179 | }]
180 | };
181 | }
182 | throw error;
183 | }
184 | });
185 | }
186 |
187 | /**
188 | * Start the MCP server using stdio transport
189 | */
190 | async run(): Promise {
191 | const transport = new StdioServerTransport();
192 | await this.server.connect(transport);
193 | }
194 | }
195 |
196 | // Create and start the server
197 | const server = new FlightTrackingServer();
198 | server.run().catch(console.error);
199 | //console.log(env.FR24_API_KEY)
200 | //console.log(env.FR24_API_URL)
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // Test requests
2 | const requests = [
3 | // List tools request
4 | {
5 | jsonrpc: "2.0",
6 | id: "1",
7 | method: "list_tools",
8 | params: {}
9 | },
10 |
11 | // Test flight positions request
12 | {
13 | jsonrpc: "2.0",
14 | id: "2",
15 | method: "call_tool",
16 | params: {
17 | name: "get_flight_positions",
18 | arguments: {
19 | airports: "KJFK,KLAX",
20 | limit: "5"
21 | }
22 | }
23 | },
24 |
25 | // Test flight ETA request
26 | {
27 | jsonrpc: "2.0",
28 | id: "3",
29 | method: "call_tool",
30 | params: {
31 | name: "get_flight_eta",
32 | arguments: {
33 | flightNumber: "UA123"
34 | }
35 | }
36 | }
37 | ];
38 |
39 | // Print each request with a delay
40 | requests.forEach((request, index) => {
41 | console.log(JSON.stringify(request));
42 | });
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface Coordinate {
2 | lat: number;
3 | lng: number;
4 | }
5 |
6 | export interface ApiResponse {
7 | contents: {
8 | uri: string;
9 | mimeType: string;
10 | text: string;
11 | }[];
12 | }
13 |
14 | /**
15 | * Flightradar24 API Types
16 | */
17 |
18 | export interface FlightPosition {
19 | fr24_id: string;
20 | hex: string;
21 | callsign: string;
22 | lat: number;
23 | lon: number;
24 | track: number;
25 | alt: number;
26 | gspeed: number;
27 | vspeed: number;
28 | squawk: string;
29 | timestamp: string;
30 | source: 'ADSB' | 'MLAT' | 'ESTIMATED';
31 | }
32 |
33 | export interface FlightPositionsResponse {
34 | data: FlightPosition[];
35 | }
36 |
37 | export interface FlightETA {
38 | flight_number: string;
39 | departure: {
40 | airport: string;
41 | scheduled: string;
42 | actual: string;
43 | delay?: number;
44 | };
45 | arrival: {
46 | airport: string;
47 | scheduled: string;
48 | estimated: string;
49 | delay?: number;
50 | };
51 | status: 'scheduled' | 'active' | 'landed' | 'cancelled';
52 | }
53 |
54 | export interface FlightETAResponse {
55 | data: FlightETA;
56 | }
57 |
58 | export interface FlightTrackingParams {
59 | bounds?: string;
60 | flights?: string;
61 | callsigns?: string;
62 | registrations?: string;
63 | painted_as?: string;
64 | operating_as?: string;
65 | airports?: string;
66 | routes?: string;
67 | aircraft?: string;
68 | altitude_ranges?: string;
69 | squawks?: string;
70 | categories?: string;
71 | data_sources?: string;
72 | airspaces?: string;
73 | limit?: number;
74 | }
75 |
76 | /**
77 | * Validates flight tracking parameters
78 | */
79 | export function isValidFlightTrackingParams(params: unknown): params is FlightTrackingParams {
80 | if (!params || typeof params !== 'object') {
81 | return false;
82 | }
83 |
84 | const typedParams = params as Record;
85 |
86 | // Check that at least one valid parameter is provided
87 | const validParams = [
88 | 'bounds', 'flights', 'callsigns', 'registrations', 'painted_as',
89 | 'operating_as', 'airports', 'routes', 'aircraft', 'altitude_ranges',
90 | 'squawks', 'categories', 'data_sources', 'airspaces', 'limit'
91 | ];
92 |
93 | const hasValidParam = validParams.some(param => {
94 | const value = typedParams[param];
95 | if (param === 'limit') {
96 | return typeof value === 'number' && value > 0;
97 | }
98 | return typeof value === 'string' && value.length > 0;
99 | });
100 |
101 | return hasValidParam;
102 | }
103 |
104 | export enum FlightCategory {
105 | PASSENGER = 'P',
106 | CARGO = 'C',
107 | MILITARY_AND_GOVERNMENT = 'M',
108 | BUSINESS_JETS = 'J',
109 | GENERAL_AVIATION = 'T',
110 | HELICOPTERS = 'H',
111 | LIGHTER_THAN_AIR = 'B',
112 | GLIDERS = 'G',
113 | DRONES = 'D',
114 | GROUND_VEHICLES = 'V',
115 | OTHER = 'O',
116 | NON_CATEGORIZED = 'N'
117 | }
118 |
119 | export interface ApiError {
120 | code: string;
121 | message: string;
122 | details?: any;
123 | }
--------------------------------------------------------------------------------
/src/types/global.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace NodeJS {
2 | interface ProcessEnv {
3 | FR24_API_KEY: string;
4 | FR24_API_URL: string;
5 | }
6 | }
--------------------------------------------------------------------------------
/src/types/modelcontextprotocol-sdk.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@modelcontextprotocol/sdk' {
2 | export class MCPServer {
3 | constructor(config: { apiKey: string });
4 | }
5 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "module": "Node16",
5 | "moduleResolution": "Node16",
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "skipLibCheck": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "resolveJsonModule": true
13 | },
14 | "include": [
15 | "src/**/*"
16 | ],
17 | "exclude": [
18 | "node_modules"
19 | ]
20 | }
--------------------------------------------------------------------------------