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,56 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AviationstackError(Exception):
|
|
5
|
+
"""Base exception for all Aviationstack application errors."""
|
|
6
|
+
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
message: str,
|
|
10
|
+
*,
|
|
11
|
+
status_code: int | None = None,
|
|
12
|
+
error_code: str | None = None,
|
|
13
|
+
) -> None:
|
|
14
|
+
super().__init__(message)
|
|
15
|
+
self.message = message
|
|
16
|
+
self.status_code = status_code
|
|
17
|
+
self.error_code = error_code
|
|
18
|
+
|
|
19
|
+
def __str__(self) -> str:
|
|
20
|
+
return self.message
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AviationstackConfigurationError(AviationstackError):
|
|
24
|
+
"""Raised when the Aviationstack client is incorrectly configured."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AviationstackRequestError(AviationstackError):
|
|
28
|
+
"""Raised when an HTTP request to Aviationstack fails."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AviationstackAuthenticationError(AviationstackRequestError):
|
|
32
|
+
"""Raised when Aviationstack rejects the API credentials."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AviationstackAuthorizationError(AviationstackRequestError):
|
|
36
|
+
"""Raised when the configured plan does not permit an operation."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AviationstackRateLimitError(AviationstackRequestError):
|
|
40
|
+
"""Raised when the Aviationstack API rate limit is exceeded."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AviationstackNotFoundError(AviationstackRequestError):
|
|
44
|
+
"""Raised when the requested Aviationstack resource is unavailable."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AviationstackServerError(AviationstackRequestError):
|
|
48
|
+
"""Raised when Aviationstack returns a server-side error."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AviationstackTimeoutError(AviationstackRequestError):
|
|
52
|
+
"""Raised when an Aviationstack request times out."""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class AviationstackAPIError(AviationstackRequestError):
|
|
56
|
+
"""Raised when Aviationstack returns an application-level API error."""
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Final
|
|
5
|
+
|
|
6
|
+
from aviationstack_mcp_server.observability import get_correlation_id
|
|
7
|
+
|
|
8
|
+
DEFAULT_LOG_FORMAT: Final = (
|
|
9
|
+
"%(asctime)s | %(levelname)s | correlation_id=%(correlation_id)s | %(name)s | %(message)s"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
LOGGER_NAME: Final = "aviationstack_mcp_server"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CorrelationIdFilter(logging.Filter):
|
|
16
|
+
"""Add the current correlation ID to every log record."""
|
|
17
|
+
|
|
18
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
19
|
+
record.correlation_id = get_correlation_id() or "-"
|
|
20
|
+
return True
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def configure_logging(level: str) -> None:
|
|
24
|
+
"""Configure application-wide logging.
|
|
25
|
+
|
|
26
|
+
This function is intentionally idempotent so it can safely be called
|
|
27
|
+
during application startup without creating duplicate handlers.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
numeric_level = getattr(logging, level.upper(), None)
|
|
31
|
+
|
|
32
|
+
if not isinstance(numeric_level, int):
|
|
33
|
+
raise ValueError(f"Invalid log level: {level}")
|
|
34
|
+
|
|
35
|
+
root_logger = logging.getLogger()
|
|
36
|
+
|
|
37
|
+
root_logger.setLevel(numeric_level)
|
|
38
|
+
|
|
39
|
+
if not root_logger.handlers:
|
|
40
|
+
new_handler = logging.StreamHandler()
|
|
41
|
+
new_handler.setLevel(numeric_level)
|
|
42
|
+
new_handler.addFilter(CorrelationIdFilter())
|
|
43
|
+
|
|
44
|
+
formatter = logging.Formatter(DEFAULT_LOG_FORMAT)
|
|
45
|
+
new_handler.setFormatter(formatter)
|
|
46
|
+
|
|
47
|
+
root_logger.addHandler(new_handler)
|
|
48
|
+
else:
|
|
49
|
+
for handler in root_logger.handlers:
|
|
50
|
+
handler.setLevel(numeric_level)
|
|
51
|
+
|
|
52
|
+
logging.getLogger(LOGGER_NAME).setLevel(numeric_level)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
from aviationstack_mcp_server.client.aviationstack import AviationstackClient
|
|
5
|
+
from aviationstack_mcp_server.services.aircraft_service import AircraftService
|
|
6
|
+
from aviationstack_mcp_server.services.airline_service import AirlineService
|
|
7
|
+
from aviationstack_mcp_server.services.airport_service import AirportService
|
|
8
|
+
from aviationstack_mcp_server.services.flight_service import FlightService
|
|
9
|
+
from aviationstack_mcp_server.services.reference_service import ReferenceDataService
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True, slots=True)
|
|
15
|
+
class ServiceContainer:
|
|
16
|
+
"""Application-level collection of domain services."""
|
|
17
|
+
|
|
18
|
+
flight: FlightService
|
|
19
|
+
airport: AirportService
|
|
20
|
+
airline: AirlineService
|
|
21
|
+
aircraft: AircraftService
|
|
22
|
+
reference: ReferenceDataService
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True, slots=True)
|
|
26
|
+
class AppContext:
|
|
27
|
+
"""Application dependencies shared across MCP requests."""
|
|
28
|
+
|
|
29
|
+
client: AviationstackClient
|
|
30
|
+
services: ServiceContainer
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build_services(
|
|
34
|
+
client: AviationstackClient,
|
|
35
|
+
) -> ServiceContainer:
|
|
36
|
+
"""Build all domain services using a shared API client."""
|
|
37
|
+
|
|
38
|
+
services = ServiceContainer(
|
|
39
|
+
flight=FlightService(client),
|
|
40
|
+
airport=AirportService(client),
|
|
41
|
+
airline=AirlineService(client),
|
|
42
|
+
aircraft=AircraftService(client),
|
|
43
|
+
reference=ReferenceDataService(client),
|
|
44
|
+
)
|
|
45
|
+
logger.debug(
|
|
46
|
+
"Application services built: names=%s",
|
|
47
|
+
("flight", "airport", "airline", "aircraft", "reference"),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return services
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def build_app_context(
|
|
54
|
+
client: AviationstackClient,
|
|
55
|
+
) -> AppContext:
|
|
56
|
+
"""Build the application dependency container."""
|
|
57
|
+
|
|
58
|
+
services = build_services(client)
|
|
59
|
+
logger.debug("Application dependency context built")
|
|
60
|
+
|
|
61
|
+
return AppContext(
|
|
62
|
+
client=client,
|
|
63
|
+
services=services,
|
|
64
|
+
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Awaitable, Callable
|
|
5
|
+
from functools import wraps
|
|
6
|
+
from time import perf_counter
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from mcp.types import CallToolResult, TextContent
|
|
10
|
+
|
|
11
|
+
from aviationstack_mcp_server.errors import (
|
|
12
|
+
AviationstackAPIError,
|
|
13
|
+
AviationstackAuthenticationError,
|
|
14
|
+
AviationstackAuthorizationError,
|
|
15
|
+
AviationstackConfigurationError,
|
|
16
|
+
AviationstackError,
|
|
17
|
+
AviationstackNotFoundError,
|
|
18
|
+
AviationstackRateLimitError,
|
|
19
|
+
AviationstackRequestError,
|
|
20
|
+
AviationstackServerError,
|
|
21
|
+
AviationstackTimeoutError,
|
|
22
|
+
)
|
|
23
|
+
from aviationstack_mcp_server.observability import (
|
|
24
|
+
generate_correlation_id,
|
|
25
|
+
set_correlation_id,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def translate_aviationstack_error(error: AviationstackError) -> str:
|
|
32
|
+
"""Convert an internal exception into a safe MCP-facing message."""
|
|
33
|
+
|
|
34
|
+
if isinstance(error, AviationstackConfigurationError):
|
|
35
|
+
return "Server configuration is invalid."
|
|
36
|
+
|
|
37
|
+
if isinstance(error, AviationstackAuthenticationError):
|
|
38
|
+
return "Aviationstack authentication failed."
|
|
39
|
+
|
|
40
|
+
if isinstance(error, AviationstackAuthorizationError):
|
|
41
|
+
return "Aviationstack authorization failed."
|
|
42
|
+
|
|
43
|
+
if isinstance(error, AviationstackRateLimitError):
|
|
44
|
+
return "Aviationstack rate limit exceeded."
|
|
45
|
+
|
|
46
|
+
if isinstance(error, AviationstackNotFoundError):
|
|
47
|
+
return "The requested Aviationstack resource was not found."
|
|
48
|
+
|
|
49
|
+
if isinstance(error, AviationstackTimeoutError):
|
|
50
|
+
return "The Aviationstack request timed out."
|
|
51
|
+
|
|
52
|
+
if isinstance(error, AviationstackServerError):
|
|
53
|
+
return "The Aviationstack service is temporarily unavailable."
|
|
54
|
+
|
|
55
|
+
if isinstance(error, AviationstackAPIError):
|
|
56
|
+
return "The Aviationstack API rejected the request."
|
|
57
|
+
|
|
58
|
+
if isinstance(error, AviationstackRequestError):
|
|
59
|
+
return "Unable to reach the Aviationstack API."
|
|
60
|
+
|
|
61
|
+
return "An Aviationstack error occurred."
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def mcp_error_message(error: AviationstackError) -> str:
|
|
65
|
+
"""Return the safe MCP-facing message for an Aviationstack error."""
|
|
66
|
+
|
|
67
|
+
return translate_aviationstack_error(error)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def mcp_error_boundary[T](
|
|
71
|
+
func: Callable[..., Awaitable[T]],
|
|
72
|
+
) -> Callable[..., Awaitable[T | CallToolResult]]:
|
|
73
|
+
"""Convert tool failures into safe MCP-facing errors."""
|
|
74
|
+
|
|
75
|
+
@wraps(func)
|
|
76
|
+
async def wrapper(*args: Any, **kwargs: Any) -> T | CallToolResult:
|
|
77
|
+
tool_name = func.__name__
|
|
78
|
+
correlation = generate_correlation_id()
|
|
79
|
+
set_correlation_id(correlation)
|
|
80
|
+
started_at = perf_counter()
|
|
81
|
+
logger.info("MCP tool started tool=%s", tool_name)
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
result = await func(*args, **kwargs)
|
|
85
|
+
duration_ms = (perf_counter() - started_at) * 1000
|
|
86
|
+
logger.info(
|
|
87
|
+
"MCP tool completed tool=%s duration_ms=%.2f",
|
|
88
|
+
tool_name,
|
|
89
|
+
duration_ms,
|
|
90
|
+
)
|
|
91
|
+
return result
|
|
92
|
+
except AviationstackError as exc:
|
|
93
|
+
duration_ms = (perf_counter() - started_at) * 1000
|
|
94
|
+
logger.error(
|
|
95
|
+
"MCP tool failed tool=%s duration_ms=%.2f error_type=%s",
|
|
96
|
+
tool_name,
|
|
97
|
+
duration_ms,
|
|
98
|
+
type(exc).__name__,
|
|
99
|
+
)
|
|
100
|
+
return CallToolResult(
|
|
101
|
+
content=[TextContent(text=translate_aviationstack_error(exc))],
|
|
102
|
+
is_error=True,
|
|
103
|
+
)
|
|
104
|
+
except Exception:
|
|
105
|
+
duration_ms = (perf_counter() - started_at) * 1000
|
|
106
|
+
logger.exception(
|
|
107
|
+
"Unexpected MCP tool failure tool=%s duration_ms=%.2f",
|
|
108
|
+
tool_name,
|
|
109
|
+
duration_ms,
|
|
110
|
+
)
|
|
111
|
+
return CallToolResult(
|
|
112
|
+
content=[TextContent(text="Internal server error.")],
|
|
113
|
+
is_error=True,
|
|
114
|
+
)
|
|
115
|
+
finally:
|
|
116
|
+
set_correlation_id("")
|
|
117
|
+
|
|
118
|
+
return wrapper
|
|
File without changes
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from mcp.server.mcpserver import MCPServer
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register_aviation_prompts(server: MCPServer) -> None:
|
|
9
|
+
"""Register Aviationstack MCP prompts."""
|
|
10
|
+
|
|
11
|
+
logger.debug("Registering Aviationstack MCP prompts")
|
|
12
|
+
|
|
13
|
+
@server.prompt(
|
|
14
|
+
name="plan_flight_search",
|
|
15
|
+
title="Plan Flight Search",
|
|
16
|
+
description=(
|
|
17
|
+
"Help formulate a flight search using available Aviationstack flight filters."
|
|
18
|
+
),
|
|
19
|
+
)
|
|
20
|
+
def plan_flight_search(
|
|
21
|
+
request: str,
|
|
22
|
+
) -> str:
|
|
23
|
+
"""Generate guidance for searching flights."""
|
|
24
|
+
|
|
25
|
+
prompt = (
|
|
26
|
+
"Plan a flight search using the Aviationstack MCP server.\n\n"
|
|
27
|
+
f"User request:\n{request}\n\n"
|
|
28
|
+
"Determine the relevant flight filters such as airline, "
|
|
29
|
+
"flight number, departure airport, arrival airport, "
|
|
30
|
+
"and date. Then use the search_flights tool with the "
|
|
31
|
+
"appropriate structured query."
|
|
32
|
+
)
|
|
33
|
+
logger.debug(
|
|
34
|
+
"Generated prompt: plan_flight_search request_length=%s prompt_length=%s",
|
|
35
|
+
len(request),
|
|
36
|
+
len(prompt),
|
|
37
|
+
)
|
|
38
|
+
return prompt
|
|
39
|
+
|
|
40
|
+
@server.prompt(
|
|
41
|
+
name="plan_schedule_search",
|
|
42
|
+
title="Plan Schedule Search",
|
|
43
|
+
description=("Help formulate an airport arrival or departure schedule search."),
|
|
44
|
+
)
|
|
45
|
+
def plan_schedule_search(
|
|
46
|
+
airport: str,
|
|
47
|
+
schedule_type: str = "departure",
|
|
48
|
+
) -> str:
|
|
49
|
+
"""Generate guidance for an airport schedule search."""
|
|
50
|
+
|
|
51
|
+
prompt = (
|
|
52
|
+
"Plan an Aviationstack airport schedule lookup.\n\n"
|
|
53
|
+
f"Airport: {airport}\n"
|
|
54
|
+
f"Schedule type: {schedule_type}\n\n"
|
|
55
|
+
"Use the get_flight_schedule tool. Ensure the airport "
|
|
56
|
+
"identifier and schedule type are mapped to the "
|
|
57
|
+
"corresponding structured query fields. Return the "
|
|
58
|
+
"schedule information relevant to the user's request."
|
|
59
|
+
)
|
|
60
|
+
logger.debug(
|
|
61
|
+
"Generated prompt: plan_schedule_search airport=%s schedule_type=%s prompt_length=%s",
|
|
62
|
+
airport,
|
|
63
|
+
schedule_type,
|
|
64
|
+
len(prompt),
|
|
65
|
+
)
|
|
66
|
+
return prompt
|
|
67
|
+
|
|
68
|
+
@server.prompt(
|
|
69
|
+
name="plan_reference_data_search",
|
|
70
|
+
title="Plan Reference Data Search",
|
|
71
|
+
description=(
|
|
72
|
+
"Help determine which Aviationstack reference-data tool should be used for a lookup."
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
def plan_reference_data_search(
|
|
76
|
+
request: str,
|
|
77
|
+
) -> str:
|
|
78
|
+
"""Generate guidance for reference-data lookup."""
|
|
79
|
+
|
|
80
|
+
prompt = (
|
|
81
|
+
"Plan a reference-data lookup using the "
|
|
82
|
+
"Aviationstack MCP server.\n\n"
|
|
83
|
+
f"User request:\n{request}\n\n"
|
|
84
|
+
"Choose the appropriate reference-data capability:\n"
|
|
85
|
+
"- list_countries for country reference data\n"
|
|
86
|
+
"- list_cities for city reference data\n"
|
|
87
|
+
"- search_routes for airline route data\n"
|
|
88
|
+
"- search_taxes for aviation tax data\n"
|
|
89
|
+
"- list_aircraft_types for aircraft type data\n"
|
|
90
|
+
"- list_airplanes for individual airplane records\n\n"
|
|
91
|
+
"Use the selected tool with its structured query."
|
|
92
|
+
)
|
|
93
|
+
logger.debug(
|
|
94
|
+
"Generated prompt: plan_reference_data_search request_length=%s prompt_length=%s",
|
|
95
|
+
len(request),
|
|
96
|
+
len(prompt),
|
|
97
|
+
)
|
|
98
|
+
return prompt
|
|
File without changes
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from mcp.server.mcpserver import MCPServer
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register_documentation_resources(server: MCPServer) -> None:
|
|
9
|
+
"""Register MCP documentation resources."""
|
|
10
|
+
|
|
11
|
+
logger.debug("Registering Aviationstack documentation resources")
|
|
12
|
+
|
|
13
|
+
@server.resource(
|
|
14
|
+
"aviationstack://metadata/endpoints",
|
|
15
|
+
name="aviationstack_endpoints",
|
|
16
|
+
title="Aviationstack API Endpoints",
|
|
17
|
+
description=(
|
|
18
|
+
"Public documentation of the Aviationstack API domains used by this MCP server."
|
|
19
|
+
),
|
|
20
|
+
mime_type="application/json",
|
|
21
|
+
)
|
|
22
|
+
def aviationstack_endpoints() -> dict[str, object]:
|
|
23
|
+
"""Return public endpoint documentation."""
|
|
24
|
+
|
|
25
|
+
logger.debug("Reading documentation resource: aviationstack://metadata/endpoints")
|
|
26
|
+
return {
|
|
27
|
+
"flights": {
|
|
28
|
+
"endpoint": "/flights",
|
|
29
|
+
"operations": [
|
|
30
|
+
"search_flights",
|
|
31
|
+
"search_historical_flights",
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
"schedule": {
|
|
35
|
+
"endpoint": "/timetable",
|
|
36
|
+
"operations": [
|
|
37
|
+
"get_flight_schedule",
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
"future_schedule": {
|
|
41
|
+
"endpoint": "/flightsFuture",
|
|
42
|
+
"operations": [
|
|
43
|
+
"get_flight_schedule",
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
"aircraft_types": {
|
|
47
|
+
"endpoint": "/aircraft_types",
|
|
48
|
+
"operations": [
|
|
49
|
+
"list_aircraft_types",
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
"airplanes": {
|
|
53
|
+
"endpoint": "/airplanes",
|
|
54
|
+
"operations": [
|
|
55
|
+
"list_airplanes",
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
"countries": {
|
|
59
|
+
"endpoint": "/countries",
|
|
60
|
+
"operations": [
|
|
61
|
+
"list_countries",
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
"cities": {
|
|
65
|
+
"endpoint": "/cities",
|
|
66
|
+
"operations": [
|
|
67
|
+
"list_cities",
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
"airports": {
|
|
71
|
+
"endpoint": "/airports",
|
|
72
|
+
"operations": [
|
|
73
|
+
"search_airports",
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
"airlines": {
|
|
77
|
+
"endpoint": "/airlines",
|
|
78
|
+
"operations": [
|
|
79
|
+
"search_airlines",
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
"routes": {
|
|
83
|
+
"endpoint": "/routes",
|
|
84
|
+
"operations": [
|
|
85
|
+
"search_routes",
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
"taxes": {
|
|
89
|
+
"endpoint": "/taxes",
|
|
90
|
+
"operations": [
|
|
91
|
+
"search_taxes",
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@server.resource(
|
|
97
|
+
"aviationstack://documentation/tools",
|
|
98
|
+
name="tool_documentation",
|
|
99
|
+
title="Aviationstack MCP Tools",
|
|
100
|
+
description=("Documentation of the tools exposed by the Aviationstack MCP server."),
|
|
101
|
+
mime_type="application/json",
|
|
102
|
+
)
|
|
103
|
+
def tool_documentation() -> dict[str, object]:
|
|
104
|
+
"""Return public MCP tool documentation."""
|
|
105
|
+
|
|
106
|
+
logger.debug("Reading documentation resource: aviationstack://documentation/tools")
|
|
107
|
+
return {
|
|
108
|
+
"tools": [
|
|
109
|
+
{
|
|
110
|
+
"name": "search_flights",
|
|
111
|
+
"domain": "flights",
|
|
112
|
+
"purpose": "Search flight records.",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "search_historical_flights",
|
|
116
|
+
"domain": "flights",
|
|
117
|
+
"purpose": "Search historical flight records.",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"name": "get_flight_schedule",
|
|
121
|
+
"domain": "flights",
|
|
122
|
+
"purpose": "Retrieve airport flight schedules.",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "search_airports",
|
|
126
|
+
"domain": "airports",
|
|
127
|
+
"purpose": "Search airport records.",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"name": "search_airlines",
|
|
131
|
+
"domain": "airlines",
|
|
132
|
+
"purpose": "Search airline records.",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "list_aircraft_types",
|
|
136
|
+
"domain": "aircraft",
|
|
137
|
+
"purpose": "List aircraft type records.",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"name": "list_airplanes",
|
|
141
|
+
"domain": "aircraft",
|
|
142
|
+
"purpose": "List airplane records.",
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"name": "list_countries",
|
|
146
|
+
"domain": "reference_data",
|
|
147
|
+
"purpose": "List country records.",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"name": "list_cities",
|
|
151
|
+
"domain": "reference_data",
|
|
152
|
+
"purpose": "List city records.",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"name": "search_routes",
|
|
156
|
+
"domain": "reference_data",
|
|
157
|
+
"purpose": "Search route records.",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"name": "search_taxes",
|
|
161
|
+
"domain": "reference_data",
|
|
162
|
+
"purpose": "Search aviation tax records.",
|
|
163
|
+
},
|
|
164
|
+
]
|
|
165
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from mcp.server.mcpserver import MCPServer
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register_metadata_resources(server: MCPServer) -> None:
|
|
9
|
+
"""Register server metadata resources."""
|
|
10
|
+
|
|
11
|
+
logger.debug("Registering Aviationstack server metadata resource")
|
|
12
|
+
|
|
13
|
+
@server.resource(
|
|
14
|
+
"aviationstack://metadata/server",
|
|
15
|
+
name="server_metadata",
|
|
16
|
+
title="Aviationstack MCP Server Metadata",
|
|
17
|
+
description=(
|
|
18
|
+
"Metadata describing the Aviationstack MCP server, "
|
|
19
|
+
"its capabilities, and supported MCP primitives."
|
|
20
|
+
),
|
|
21
|
+
mime_type="application/json",
|
|
22
|
+
)
|
|
23
|
+
def server_metadata() -> dict[str, object]:
|
|
24
|
+
"""Return public server metadata."""
|
|
25
|
+
|
|
26
|
+
logger.debug("Reading metadata resource: aviationstack://metadata/server")
|
|
27
|
+
return {
|
|
28
|
+
"name": "aviationstack-mcp",
|
|
29
|
+
"project": "aviationstack_mcp_server",
|
|
30
|
+
"description": (
|
|
31
|
+
"MCP server providing structured access to "
|
|
32
|
+
"Aviationstack flight and aviation reference data."
|
|
33
|
+
),
|
|
34
|
+
"capabilities": {
|
|
35
|
+
"tools": True,
|
|
36
|
+
"prompts": True,
|
|
37
|
+
"resources": True,
|
|
38
|
+
},
|
|
39
|
+
"domains": [
|
|
40
|
+
"flights",
|
|
41
|
+
"airports",
|
|
42
|
+
"airlines",
|
|
43
|
+
"aircraft",
|
|
44
|
+
"reference_data",
|
|
45
|
+
],
|
|
46
|
+
}
|