aviationstack-mcp-server 0.1.0__py3-none-any.whl
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.
- aviationstack_mcp_server/__init__.py +0 -0
- aviationstack_mcp_server/__main__.py +15 -0
- aviationstack_mcp_server/client/__init__.py +9 -0
- aviationstack_mcp_server/client/aviationstack.py +138 -0
- aviationstack_mcp_server/client/factory.py +30 -0
- aviationstack_mcp_server/client/http.py +292 -0
- aviationstack_mcp_server/config/__init__.py +3 -0
- aviationstack_mcp_server/config/settings.py +162 -0
- aviationstack_mcp_server/errors/__init__.py +25 -0
- aviationstack_mcp_server/errors/exceptions.py +56 -0
- aviationstack_mcp_server/logging_config.py +52 -0
- aviationstack_mcp_server/mcp/__init__.py +3 -0
- aviationstack_mcp_server/mcp/dependencies.py +64 -0
- aviationstack_mcp_server/mcp/errors.py +118 -0
- aviationstack_mcp_server/mcp/prompts/__init__.py +0 -0
- aviationstack_mcp_server/mcp/prompts/aviation.py +98 -0
- aviationstack_mcp_server/mcp/resources/__init__.py +0 -0
- aviationstack_mcp_server/mcp/resources/documentation.py +165 -0
- aviationstack_mcp_server/mcp/resources/metadata.py +46 -0
- aviationstack_mcp_server/mcp/server.py +164 -0
- aviationstack_mcp_server/mcp/tools/__init__.py +0 -0
- aviationstack_mcp_server/mcp/tools/aircraft.py +64 -0
- aviationstack_mcp_server/mcp/tools/airlines.py +43 -0
- aviationstack_mcp_server/mcp/tools/airports.py +43 -0
- aviationstack_mcp_server/mcp/tools/flights.py +125 -0
- aviationstack_mcp_server/mcp/tools/reference.py +129 -0
- aviationstack_mcp_server/models/__init__.py +81 -0
- aviationstack_mcp_server/models/aircraft.py +43 -0
- aviationstack_mcp_server/models/airline.py +29 -0
- aviationstack_mcp_server/models/airport.py +33 -0
- aviationstack_mcp_server/models/common.py +20 -0
- aviationstack_mcp_server/models/flight.py +125 -0
- aviationstack_mcp_server/models/location.py +50 -0
- aviationstack_mcp_server/models/queries.py +284 -0
- aviationstack_mcp_server/models/route.py +24 -0
- aviationstack_mcp_server/models/tax.py +20 -0
- aviationstack_mcp_server/observability.py +27 -0
- aviationstack_mcp_server/security.py +62 -0
- aviationstack_mcp_server/services/__init__.py +13 -0
- aviationstack_mcp_server/services/aircraft_service.py +156 -0
- aviationstack_mcp_server/services/airline_service.py +89 -0
- aviationstack_mcp_server/services/airport_service.py +116 -0
- aviationstack_mcp_server/services/flight_service.py +366 -0
- aviationstack_mcp_server/services/reference_service.py +315 -0
- aviationstack_mcp_server/utils/__init__.py +0 -0
- aviationstack_mcp_server-0.1.0.dist-info/METADATA +57 -0
- aviationstack_mcp_server-0.1.0.dist-info/RECORD +50 -0
- aviationstack_mcp_server-0.1.0.dist-info/WHEEL +4 -0
- aviationstack_mcp_server-0.1.0.dist-info/entry_points.txt +3 -0
- aviationstack_mcp_server-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from collections.abc import AsyncIterator, Callable
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
|
|
5
|
+
from mcp.server import MCPServer
|
|
6
|
+
|
|
7
|
+
from aviationstack_mcp_server.client.aviationstack import AviationstackClient
|
|
8
|
+
from aviationstack_mcp_server.client.factory import create_aviationstack_client
|
|
9
|
+
from aviationstack_mcp_server.config import get_settings
|
|
10
|
+
from aviationstack_mcp_server.mcp.dependencies import (
|
|
11
|
+
AppContext,
|
|
12
|
+
build_app_context,
|
|
13
|
+
)
|
|
14
|
+
from aviationstack_mcp_server.mcp.prompts.aviation import (
|
|
15
|
+
register_aviation_prompts,
|
|
16
|
+
)
|
|
17
|
+
from aviationstack_mcp_server.mcp.resources.documentation import (
|
|
18
|
+
register_documentation_resources,
|
|
19
|
+
)
|
|
20
|
+
from aviationstack_mcp_server.mcp.resources.metadata import (
|
|
21
|
+
register_metadata_resources,
|
|
22
|
+
)
|
|
23
|
+
from aviationstack_mcp_server.mcp.tools.aircraft import register_aircraft_tools
|
|
24
|
+
from aviationstack_mcp_server.mcp.tools.airlines import register_airline_tools
|
|
25
|
+
from aviationstack_mcp_server.mcp.tools.airports import register_airport_tools
|
|
26
|
+
from aviationstack_mcp_server.mcp.tools.flights import register_flight_tools
|
|
27
|
+
from aviationstack_mcp_server.mcp.tools.reference import register_reference_tools
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
ClientFactory = Callable[[], AviationstackClient]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def default_client_factory() -> AviationstackClient:
|
|
35
|
+
"""Create the production Aviationstack client from application settings."""
|
|
36
|
+
|
|
37
|
+
settings = get_settings()
|
|
38
|
+
return create_aviationstack_client(settings)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def create_lifespan(
|
|
42
|
+
client_factory: ClientFactory = default_client_factory,
|
|
43
|
+
):
|
|
44
|
+
"""Create the application lifespan with an injectable client factory."""
|
|
45
|
+
|
|
46
|
+
@asynccontextmanager
|
|
47
|
+
async def lifespan(
|
|
48
|
+
server: MCPServer[AppContext],
|
|
49
|
+
) -> AsyncIterator[AppContext]:
|
|
50
|
+
logger.info("Starting Aviationstack MCP application lifespan")
|
|
51
|
+
client = client_factory()
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
logger.info("Aviationstack API client initialized")
|
|
55
|
+
|
|
56
|
+
app_context = build_app_context(client)
|
|
57
|
+
logger.info("Aviationstack application context ready")
|
|
58
|
+
|
|
59
|
+
yield app_context
|
|
60
|
+
finally:
|
|
61
|
+
logger.info("Shutting down Aviationstack MCP application lifespan")
|
|
62
|
+
try:
|
|
63
|
+
await client.close()
|
|
64
|
+
except Exception:
|
|
65
|
+
logger.exception("Failed to close Aviationstack API client")
|
|
66
|
+
raise
|
|
67
|
+
else:
|
|
68
|
+
logger.info("Aviationstack API client closed")
|
|
69
|
+
|
|
70
|
+
return lifespan
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def create_server(
|
|
74
|
+
client_factory: ClientFactory = default_client_factory,
|
|
75
|
+
) -> MCPServer[AppContext]:
|
|
76
|
+
"""Create and configure the Aviationstack MCP server."""
|
|
77
|
+
|
|
78
|
+
logger.debug("Creating Aviationstack MCP server")
|
|
79
|
+
server = MCPServer(
|
|
80
|
+
"aviationstack-mcp",
|
|
81
|
+
lifespan=create_lifespan(client_factory),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Tools
|
|
85
|
+
register_flight_tools(server)
|
|
86
|
+
register_airport_tools(server)
|
|
87
|
+
register_airline_tools(server)
|
|
88
|
+
register_aircraft_tools(server)
|
|
89
|
+
register_reference_tools(server)
|
|
90
|
+
|
|
91
|
+
# Prompts
|
|
92
|
+
register_aviation_prompts(server)
|
|
93
|
+
|
|
94
|
+
# Resources
|
|
95
|
+
register_metadata_resources(server)
|
|
96
|
+
register_documentation_resources(server)
|
|
97
|
+
|
|
98
|
+
logger.info("Aviationstack MCP server configured")
|
|
99
|
+
return server
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# from collections.abc import AsyncIterator
|
|
103
|
+
# from contextlib import asynccontextmanager
|
|
104
|
+
|
|
105
|
+
# from mcp.server import MCPServer
|
|
106
|
+
|
|
107
|
+
# from aviationstack_mcp_server.client.factory import create_aviationstack_client
|
|
108
|
+
# from aviationstack_mcp_server.config import get_settings
|
|
109
|
+
# from aviationstack_mcp_server.mcp.dependencies import AppContext, build_app_context
|
|
110
|
+
# from aviationstack_mcp_server.mcp.prompts.aviation import (
|
|
111
|
+
# register_aviation_prompts,
|
|
112
|
+
# )
|
|
113
|
+
# from aviationstack_mcp_server.mcp.resources.documentation import (
|
|
114
|
+
# register_documentation_resources,
|
|
115
|
+
# )
|
|
116
|
+
# from aviationstack_mcp_server.mcp.resources.metadata import (
|
|
117
|
+
# register_metadata_resources,
|
|
118
|
+
# )
|
|
119
|
+
# from aviationstack_mcp_server.mcp.tools.aircraft import register_aircraft_tools
|
|
120
|
+
# from aviationstack_mcp_server.mcp.tools.airlines import register_airline_tools
|
|
121
|
+
# from aviationstack_mcp_server.mcp.tools.airports import register_airport_tools
|
|
122
|
+
# from aviationstack_mcp_server.mcp.tools.flights import register_flight_tools
|
|
123
|
+
# from aviationstack_mcp_server.mcp.tools.reference import register_reference_tools
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# @asynccontextmanager
|
|
127
|
+
# async def app_lifespan(
|
|
128
|
+
# server: MCPServer[AppContext],
|
|
129
|
+
# ) -> AsyncIterator[AppContext]:
|
|
130
|
+
# """Initialize and clean up application-wide dependencies."""
|
|
131
|
+
|
|
132
|
+
# settings = get_settings()
|
|
133
|
+
# client = create_aviationstack_client(settings)
|
|
134
|
+
|
|
135
|
+
# try:
|
|
136
|
+
# app_context = build_app_context(client)
|
|
137
|
+
|
|
138
|
+
# yield app_context
|
|
139
|
+
|
|
140
|
+
# finally:
|
|
141
|
+
# await client.close()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# def create_server() -> MCPServer[AppContext]:
|
|
145
|
+
# """Create and configure the Aviationstack MCP server."""
|
|
146
|
+
|
|
147
|
+
# server = MCPServer(
|
|
148
|
+
# "aviationstack-mcp",
|
|
149
|
+
# lifespan=app_lifespan,
|
|
150
|
+
# )
|
|
151
|
+
|
|
152
|
+
# register_flight_tools(server)
|
|
153
|
+
# register_airport_tools(server)
|
|
154
|
+
# register_airline_tools(server)
|
|
155
|
+
# register_aircraft_tools(server)
|
|
156
|
+
# register_reference_tools(server)
|
|
157
|
+
|
|
158
|
+
# register_aviation_prompts(server)
|
|
159
|
+
|
|
160
|
+
# # Resources
|
|
161
|
+
# register_metadata_resources(server)
|
|
162
|
+
# register_documentation_resources(server)
|
|
163
|
+
|
|
164
|
+
# return server
|
|
File without changes
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from mcp.server.mcpserver import Context
|
|
4
|
+
|
|
5
|
+
from aviationstack_mcp_server.mcp.dependencies import AppContext
|
|
6
|
+
from aviationstack_mcp_server.mcp.errors import mcp_error_boundary
|
|
7
|
+
from aviationstack_mcp_server.models.queries import RecordLimitQuery
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def register_aircraft_tools(server) -> None:
|
|
13
|
+
"""Register aircraft-related MCP tools."""
|
|
14
|
+
|
|
15
|
+
@server.tool(
|
|
16
|
+
name="list_aircraft_types",
|
|
17
|
+
title="List Aircraft Types",
|
|
18
|
+
description=(
|
|
19
|
+
"List aircraft type records available from Aviationstack. "
|
|
20
|
+
"Use the limit parameter to control the number of records."
|
|
21
|
+
),
|
|
22
|
+
)
|
|
23
|
+
@mcp_error_boundary
|
|
24
|
+
async def list_aircraft_types(
|
|
25
|
+
query: RecordLimitQuery,
|
|
26
|
+
ctx: Context[AppContext],
|
|
27
|
+
):
|
|
28
|
+
"""List aircraft type records."""
|
|
29
|
+
|
|
30
|
+
app = ctx.request_context.lifespan_context
|
|
31
|
+
logger.info("MCP tool called: list_aircraft_types limit=%s", query.limit)
|
|
32
|
+
records = await app.services.aircraft.list_aircraft_types(query)
|
|
33
|
+
logger.debug(
|
|
34
|
+
"MCP tool completed: list_aircraft_types returned=%s",
|
|
35
|
+
len(records),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return records
|
|
39
|
+
|
|
40
|
+
@server.tool(
|
|
41
|
+
name="list_airplanes",
|
|
42
|
+
title="List Airplanes",
|
|
43
|
+
description=(
|
|
44
|
+
"List individual airplane records available from "
|
|
45
|
+
"Aviationstack. Use the limit parameter to control "
|
|
46
|
+
"the number of records."
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
@mcp_error_boundary
|
|
50
|
+
async def list_airplanes(
|
|
51
|
+
query: RecordLimitQuery,
|
|
52
|
+
ctx: Context[AppContext],
|
|
53
|
+
):
|
|
54
|
+
"""List individual airplane records."""
|
|
55
|
+
|
|
56
|
+
app = ctx.request_context.lifespan_context
|
|
57
|
+
logger.info("MCP tool called: list_airplanes limit=%s", query.limit)
|
|
58
|
+
records = await app.services.aircraft.list_airplanes(query)
|
|
59
|
+
logger.debug(
|
|
60
|
+
"MCP tool completed: list_airplanes returned=%s",
|
|
61
|
+
len(records),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return records
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from mcp.server.mcpserver import Context
|
|
4
|
+
|
|
5
|
+
from aviationstack_mcp_server.mcp.dependencies import AppContext
|
|
6
|
+
from aviationstack_mcp_server.mcp.errors import mcp_error_boundary
|
|
7
|
+
from aviationstack_mcp_server.models.queries import AirlineSearchQuery
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def register_airline_tools(server) -> None:
|
|
13
|
+
"""Register airline-related MCP tools."""
|
|
14
|
+
|
|
15
|
+
@server.tool(
|
|
16
|
+
name="search_airlines",
|
|
17
|
+
title="Search Airlines",
|
|
18
|
+
description=(
|
|
19
|
+
"Search airline records using airline name, IATA code, "
|
|
20
|
+
"ICAO code, or other supported airline search criteria."
|
|
21
|
+
),
|
|
22
|
+
)
|
|
23
|
+
@mcp_error_boundary
|
|
24
|
+
async def search_airlines(
|
|
25
|
+
query: AirlineSearchQuery,
|
|
26
|
+
ctx: Context[AppContext],
|
|
27
|
+
):
|
|
28
|
+
"""Search airline records."""
|
|
29
|
+
|
|
30
|
+
app = ctx.request_context.lifespan_context
|
|
31
|
+
logger.info(
|
|
32
|
+
"MCP tool called: search_airlines limit=%s offset=%s search_provided=%s",
|
|
33
|
+
query.limit,
|
|
34
|
+
query.offset,
|
|
35
|
+
query.search is not None,
|
|
36
|
+
)
|
|
37
|
+
records = await app.services.airline.search_airlines(query)
|
|
38
|
+
logger.debug(
|
|
39
|
+
"MCP tool completed: search_airlines returned=%s",
|
|
40
|
+
len(records),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return records
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from mcp.server.mcpserver import Context
|
|
4
|
+
|
|
5
|
+
from aviationstack_mcp_server.mcp.dependencies import AppContext
|
|
6
|
+
from aviationstack_mcp_server.mcp.errors import mcp_error_boundary
|
|
7
|
+
from aviationstack_mcp_server.models.queries import AirportSearchQuery
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def register_airport_tools(server) -> None:
|
|
13
|
+
"""Register airport-related MCP tools."""
|
|
14
|
+
|
|
15
|
+
@server.tool(
|
|
16
|
+
name="search_airports",
|
|
17
|
+
title="Search Airports",
|
|
18
|
+
description=(
|
|
19
|
+
"Search airports by airport name, IATA code, ICAO code, "
|
|
20
|
+
"city, or other supported airport search criteria."
|
|
21
|
+
),
|
|
22
|
+
)
|
|
23
|
+
@mcp_error_boundary
|
|
24
|
+
async def search_airports(
|
|
25
|
+
query: AirportSearchQuery,
|
|
26
|
+
ctx: Context[AppContext],
|
|
27
|
+
):
|
|
28
|
+
"""Search airport records."""
|
|
29
|
+
|
|
30
|
+
app = ctx.request_context.lifespan_context
|
|
31
|
+
logger.info(
|
|
32
|
+
"MCP tool called: search_airports limit=%s offset=%s search_provided=%s",
|
|
33
|
+
query.limit,
|
|
34
|
+
query.offset,
|
|
35
|
+
query.search is not None,
|
|
36
|
+
)
|
|
37
|
+
records = await app.services.airport.search_airports(query)
|
|
38
|
+
logger.debug(
|
|
39
|
+
"MCP tool completed: search_airports returned=%s",
|
|
40
|
+
len(records),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return records
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from mcp.server.mcpserver import Context
|
|
4
|
+
|
|
5
|
+
from aviationstack_mcp_server.mcp.dependencies import AppContext
|
|
6
|
+
from aviationstack_mcp_server.mcp.errors import mcp_error_boundary
|
|
7
|
+
from aviationstack_mcp_server.models.queries import (
|
|
8
|
+
AirportScheduleQuery,
|
|
9
|
+
FlightSearchQuery,
|
|
10
|
+
HistoricalFlightQuery,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def register_flight_tools(
|
|
17
|
+
server,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Register flight-related MCP tools."""
|
|
20
|
+
|
|
21
|
+
@server.tool(
|
|
22
|
+
name="search_flights",
|
|
23
|
+
title="Search Flights",
|
|
24
|
+
description=(
|
|
25
|
+
"Search flights using airline, flight number, departure, "
|
|
26
|
+
"arrival, date, and pagination filters."
|
|
27
|
+
),
|
|
28
|
+
)
|
|
29
|
+
@mcp_error_boundary
|
|
30
|
+
async def search_flights(
|
|
31
|
+
query: FlightSearchQuery,
|
|
32
|
+
ctx: Context[AppContext],
|
|
33
|
+
):
|
|
34
|
+
"""Search scheduled flight records."""
|
|
35
|
+
|
|
36
|
+
app = ctx.request_context.lifespan_context
|
|
37
|
+
logger.info(
|
|
38
|
+
"MCP tool called: search_flights limit=%s filters=%s",
|
|
39
|
+
query.limit,
|
|
40
|
+
[
|
|
41
|
+
field
|
|
42
|
+
for field in (
|
|
43
|
+
"airline_name",
|
|
44
|
+
"airline_iata",
|
|
45
|
+
"flight_iata",
|
|
46
|
+
"flight_icao",
|
|
47
|
+
"departure_iata",
|
|
48
|
+
"arrival_iata",
|
|
49
|
+
)
|
|
50
|
+
if getattr(query, field) is not None
|
|
51
|
+
],
|
|
52
|
+
)
|
|
53
|
+
records = await app.services.flight.search_flights(query)
|
|
54
|
+
logger.debug(
|
|
55
|
+
"MCP tool completed: search_flights returned=%s",
|
|
56
|
+
len(records),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return records
|
|
60
|
+
|
|
61
|
+
@server.tool(
|
|
62
|
+
name="search_historical_flights",
|
|
63
|
+
title="Search Historical Flights",
|
|
64
|
+
description=(
|
|
65
|
+
"Search historical flight records for a specific date "
|
|
66
|
+
"using optional airline, flight number, departure, "
|
|
67
|
+
"arrival, and pagination filters."
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
@mcp_error_boundary
|
|
71
|
+
async def search_historical_flights(
|
|
72
|
+
query: HistoricalFlightQuery,
|
|
73
|
+
ctx: Context[AppContext],
|
|
74
|
+
):
|
|
75
|
+
"""Search historical flight records."""
|
|
76
|
+
|
|
77
|
+
app = ctx.request_context.lifespan_context
|
|
78
|
+
logger.info(
|
|
79
|
+
"MCP tool called: search_historical_flights date=%s limit=%s filters=%s",
|
|
80
|
+
query.flight_date,
|
|
81
|
+
query.limit,
|
|
82
|
+
[
|
|
83
|
+
field
|
|
84
|
+
for field in ("airline_iata", "departure_iata", "arrival_iata")
|
|
85
|
+
if getattr(query, field) is not None
|
|
86
|
+
],
|
|
87
|
+
)
|
|
88
|
+
records = await app.services.flight.get_historical_flights(query)
|
|
89
|
+
logger.debug(
|
|
90
|
+
"MCP tool completed: search_historical_flights returned=%s",
|
|
91
|
+
len(records),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return records
|
|
95
|
+
|
|
96
|
+
@server.tool(
|
|
97
|
+
name="get_flight_schedule",
|
|
98
|
+
title="Get Flight Schedule",
|
|
99
|
+
description=(
|
|
100
|
+
"Retrieve airport arrival or departure schedules using "
|
|
101
|
+
"an airport IATA code and schedule filters."
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
@mcp_error_boundary
|
|
105
|
+
async def get_flight_schedule(
|
|
106
|
+
query: AirportScheduleQuery,
|
|
107
|
+
ctx: Context[AppContext],
|
|
108
|
+
):
|
|
109
|
+
"""Retrieve airport flight schedules."""
|
|
110
|
+
|
|
111
|
+
app = ctx.request_context.lifespan_context
|
|
112
|
+
logger.info(
|
|
113
|
+
"MCP tool called: get_flight_schedule airport=%s type=%s airline_filter=%s limit=%s",
|
|
114
|
+
query.airport_iata,
|
|
115
|
+
query.schedule_type.value,
|
|
116
|
+
query.airline_name is not None,
|
|
117
|
+
query.limit,
|
|
118
|
+
)
|
|
119
|
+
records = await app.services.flight.get_airport_schedule(query)
|
|
120
|
+
logger.debug(
|
|
121
|
+
"MCP tool completed: get_flight_schedule returned=%s",
|
|
122
|
+
len(records),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return records
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from mcp.server.mcpserver import Context
|
|
4
|
+
|
|
5
|
+
from aviationstack_mcp_server.mcp.dependencies import AppContext
|
|
6
|
+
from aviationstack_mcp_server.mcp.errors import mcp_error_boundary
|
|
7
|
+
from aviationstack_mcp_server.models.queries import (
|
|
8
|
+
RecordLimitQuery,
|
|
9
|
+
RouteSearchQuery,
|
|
10
|
+
TaxSearchQuery,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def register_reference_tools(server) -> None:
|
|
17
|
+
"""Register reference-data MCP tools."""
|
|
18
|
+
|
|
19
|
+
@server.tool(
|
|
20
|
+
name="list_countries",
|
|
21
|
+
title="List Countries",
|
|
22
|
+
description=(
|
|
23
|
+
"List country records available from Aviationstack. "
|
|
24
|
+
"Use the limit parameter to control the number of records."
|
|
25
|
+
),
|
|
26
|
+
)
|
|
27
|
+
@mcp_error_boundary
|
|
28
|
+
async def list_countries(
|
|
29
|
+
query: RecordLimitQuery,
|
|
30
|
+
ctx: Context[AppContext],
|
|
31
|
+
):
|
|
32
|
+
"""List country reference records."""
|
|
33
|
+
|
|
34
|
+
app = ctx.request_context.lifespan_context
|
|
35
|
+
logger.info("MCP tool called: list_countries limit=%s", query.limit)
|
|
36
|
+
records = await app.services.reference.list_countries(query)
|
|
37
|
+
logger.debug(
|
|
38
|
+
"MCP tool completed: list_countries returned=%s",
|
|
39
|
+
len(records),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return records
|
|
43
|
+
|
|
44
|
+
@server.tool(
|
|
45
|
+
name="list_cities",
|
|
46
|
+
title="List Cities",
|
|
47
|
+
description=(
|
|
48
|
+
"List city records available from Aviationstack. "
|
|
49
|
+
"Use the limit parameter to control the number of records."
|
|
50
|
+
),
|
|
51
|
+
)
|
|
52
|
+
@mcp_error_boundary
|
|
53
|
+
async def list_cities(
|
|
54
|
+
query: RecordLimitQuery,
|
|
55
|
+
ctx: Context[AppContext],
|
|
56
|
+
):
|
|
57
|
+
"""List city reference records."""
|
|
58
|
+
|
|
59
|
+
app = ctx.request_context.lifespan_context
|
|
60
|
+
logger.info("MCP tool called: list_cities limit=%s", query.limit)
|
|
61
|
+
records = await app.services.reference.list_cities(query)
|
|
62
|
+
logger.debug(
|
|
63
|
+
"MCP tool completed: list_cities returned=%s",
|
|
64
|
+
len(records),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return records
|
|
68
|
+
|
|
69
|
+
@server.tool(
|
|
70
|
+
name="search_routes",
|
|
71
|
+
title="Search Routes",
|
|
72
|
+
description=(
|
|
73
|
+
"Search airline route records using supported route "
|
|
74
|
+
"filters such as airline, departure airport, arrival "
|
|
75
|
+
"airport, and pagination."
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
@mcp_error_boundary
|
|
79
|
+
async def search_routes(
|
|
80
|
+
query: RouteSearchQuery,
|
|
81
|
+
ctx: Context[AppContext],
|
|
82
|
+
):
|
|
83
|
+
"""Search route records."""
|
|
84
|
+
|
|
85
|
+
app = ctx.request_context.lifespan_context
|
|
86
|
+
logger.info(
|
|
87
|
+
"MCP tool called: search_routes limit=%s offset=%s filters=%s",
|
|
88
|
+
query.limit,
|
|
89
|
+
query.offset,
|
|
90
|
+
[
|
|
91
|
+
field
|
|
92
|
+
for field in ("airline_iata", "departure_iata", "arrival_iata")
|
|
93
|
+
if getattr(query, field) is not None
|
|
94
|
+
],
|
|
95
|
+
)
|
|
96
|
+
records = await app.services.reference.search_routes(query)
|
|
97
|
+
logger.debug(
|
|
98
|
+
"MCP tool completed: search_routes returned=%s",
|
|
99
|
+
len(records),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return records
|
|
103
|
+
|
|
104
|
+
@server.tool(
|
|
105
|
+
name="search_taxes",
|
|
106
|
+
title="Search Taxes",
|
|
107
|
+
description=("Search aviation tax records using supported tax filters and pagination."),
|
|
108
|
+
)
|
|
109
|
+
@mcp_error_boundary
|
|
110
|
+
async def search_taxes(
|
|
111
|
+
query: TaxSearchQuery,
|
|
112
|
+
ctx: Context[AppContext],
|
|
113
|
+
):
|
|
114
|
+
"""Search tax records."""
|
|
115
|
+
|
|
116
|
+
app = ctx.request_context.lifespan_context
|
|
117
|
+
logger.info(
|
|
118
|
+
"MCP tool called: search_taxes limit=%s offset=%s search_provided=%s",
|
|
119
|
+
query.limit,
|
|
120
|
+
query.offset,
|
|
121
|
+
query.search is not None,
|
|
122
|
+
)
|
|
123
|
+
records = await app.services.reference.search_taxes(query)
|
|
124
|
+
logger.debug(
|
|
125
|
+
"MCP tool completed: search_taxes returned=%s",
|
|
126
|
+
len(records),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return records
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from .aircraft import (
|
|
2
|
+
AircraftType,
|
|
3
|
+
AircraftTypeResponse,
|
|
4
|
+
Airplane,
|
|
5
|
+
AirplaneResponse,
|
|
6
|
+
)
|
|
7
|
+
from .airline import Airline, AirlineResponse
|
|
8
|
+
from .airport import Airport, AirportResponse
|
|
9
|
+
from .common import AviationstackModel, AviationstackResponse
|
|
10
|
+
from .flight import (
|
|
11
|
+
AirlineReference,
|
|
12
|
+
AirportReference,
|
|
13
|
+
Flight,
|
|
14
|
+
FlightAircraftReference,
|
|
15
|
+
FlightIdentifier,
|
|
16
|
+
FlightResponse,
|
|
17
|
+
ScheduleFlight,
|
|
18
|
+
ScheduleFlightIdentifier,
|
|
19
|
+
ScheduleResponse,
|
|
20
|
+
ScheduleTimeReference,
|
|
21
|
+
)
|
|
22
|
+
from .location import City, CityResponse, Country, CountryResponse
|
|
23
|
+
from .queries import (
|
|
24
|
+
AirlineSearchQuery,
|
|
25
|
+
AirportScheduleQuery,
|
|
26
|
+
AirportSearchQuery,
|
|
27
|
+
FlightSearchQuery,
|
|
28
|
+
FutureScheduleQuery,
|
|
29
|
+
HistoricalFlightQuery,
|
|
30
|
+
PaginationQuery,
|
|
31
|
+
QueryModel,
|
|
32
|
+
RecordLimitQuery,
|
|
33
|
+
RouteSearchQuery,
|
|
34
|
+
ScheduleType,
|
|
35
|
+
TaxSearchQuery,
|
|
36
|
+
)
|
|
37
|
+
from .route import Route, RouteResponse
|
|
38
|
+
from .tax import Tax, TaxResponse
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"AircraftType",
|
|
42
|
+
"AircraftTypeResponse",
|
|
43
|
+
"Airline",
|
|
44
|
+
"AirlineReference",
|
|
45
|
+
"AirlineResponse",
|
|
46
|
+
"Airport",
|
|
47
|
+
"AirportReference",
|
|
48
|
+
"AirportResponse",
|
|
49
|
+
"AviationstackModel",
|
|
50
|
+
"AviationstackResponse",
|
|
51
|
+
"Airplane",
|
|
52
|
+
"AirplaneResponse",
|
|
53
|
+
"City",
|
|
54
|
+
"CityResponse",
|
|
55
|
+
"Country",
|
|
56
|
+
"CountryResponse",
|
|
57
|
+
"Flight",
|
|
58
|
+
"FlightAircraftReference",
|
|
59
|
+
"FlightIdentifier",
|
|
60
|
+
"FlightResponse",
|
|
61
|
+
"Route",
|
|
62
|
+
"RouteResponse",
|
|
63
|
+
"ScheduleFlight",
|
|
64
|
+
"ScheduleFlightIdentifier",
|
|
65
|
+
"ScheduleResponse",
|
|
66
|
+
"ScheduleTimeReference",
|
|
67
|
+
"Tax",
|
|
68
|
+
"TaxResponse",
|
|
69
|
+
"AirlineSearchQuery",
|
|
70
|
+
"AirportScheduleQuery",
|
|
71
|
+
"AirportSearchQuery",
|
|
72
|
+
"FlightSearchQuery",
|
|
73
|
+
"FutureScheduleQuery",
|
|
74
|
+
"HistoricalFlightQuery",
|
|
75
|
+
"PaginationQuery",
|
|
76
|
+
"QueryModel",
|
|
77
|
+
"RecordLimitQuery",
|
|
78
|
+
"RouteSearchQuery",
|
|
79
|
+
"ScheduleType",
|
|
80
|
+
"TaxSearchQuery",
|
|
81
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from aviationstack_mcp_server.models.common import AviationstackModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AircraftType(AviationstackModel):
|
|
9
|
+
"""Aircraft type reference data."""
|
|
10
|
+
|
|
11
|
+
aircraft_name: str | None = None
|
|
12
|
+
iata_code: str | None = None
|
|
13
|
+
icao_code: str | None = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AircraftTypeResponse(AviationstackModel):
|
|
17
|
+
"""Response containing aircraft types."""
|
|
18
|
+
|
|
19
|
+
pagination: dict | None = None
|
|
20
|
+
data: list[AircraftType] = Field(default_factory=list)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Airplane(AviationstackModel):
|
|
24
|
+
"""Detailed airplane information."""
|
|
25
|
+
|
|
26
|
+
production_line: str | None = None
|
|
27
|
+
plane_owner: str | None = None
|
|
28
|
+
plane_age: int | float | None = None
|
|
29
|
+
model_name: str | None = None
|
|
30
|
+
model_code: str | None = None
|
|
31
|
+
plane_series: str | None = None
|
|
32
|
+
registration_number: str | None = None
|
|
33
|
+
engines_type: str | None = None
|
|
34
|
+
engines_count: int | None = None
|
|
35
|
+
delivery_date: str | None = None
|
|
36
|
+
first_flight_date: str | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AirplaneResponse(AviationstackModel):
|
|
40
|
+
"""Response containing airplane records."""
|
|
41
|
+
|
|
42
|
+
pagination: dict | None = None
|
|
43
|
+
data: list[Airplane] = Field(default_factory=list)
|