airbyte-agent-mcp 0.1.33__py3-none-any.whl → 0.1.60__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.
Files changed (31) hide show
  1. airbyte_agent_mcp/_vendored/connector_sdk/auth_strategies.py +2 -5
  2. airbyte_agent_mcp/_vendored/connector_sdk/auth_template.py +1 -1
  3. airbyte_agent_mcp/_vendored/connector_sdk/cloud_utils/client.py +26 -26
  4. airbyte_agent_mcp/_vendored/connector_sdk/connector_model_loader.py +11 -4
  5. airbyte_agent_mcp/_vendored/connector_sdk/constants.py +1 -1
  6. airbyte_agent_mcp/_vendored/connector_sdk/executor/hosted_executor.py +10 -11
  7. airbyte_agent_mcp/_vendored/connector_sdk/executor/local_executor.py +163 -34
  8. airbyte_agent_mcp/_vendored/connector_sdk/extensions.py +43 -5
  9. airbyte_agent_mcp/_vendored/connector_sdk/http/response.py +2 -0
  10. airbyte_agent_mcp/_vendored/connector_sdk/http_client.py +50 -43
  11. airbyte_agent_mcp/_vendored/connector_sdk/introspection.py +262 -0
  12. airbyte_agent_mcp/_vendored/connector_sdk/logging/logger.py +9 -9
  13. airbyte_agent_mcp/_vendored/connector_sdk/logging/types.py +10 -10
  14. airbyte_agent_mcp/_vendored/connector_sdk/observability/config.py +179 -0
  15. airbyte_agent_mcp/_vendored/connector_sdk/observability/models.py +6 -6
  16. airbyte_agent_mcp/_vendored/connector_sdk/observability/session.py +41 -32
  17. airbyte_agent_mcp/_vendored/connector_sdk/performance/metrics.py +3 -3
  18. airbyte_agent_mcp/_vendored/connector_sdk/schema/base.py +20 -18
  19. airbyte_agent_mcp/_vendored/connector_sdk/schema/components.py +59 -58
  20. airbyte_agent_mcp/_vendored/connector_sdk/schema/connector.py +22 -33
  21. airbyte_agent_mcp/_vendored/connector_sdk/schema/extensions.py +102 -9
  22. airbyte_agent_mcp/_vendored/connector_sdk/schema/operations.py +32 -32
  23. airbyte_agent_mcp/_vendored/connector_sdk/schema/security.py +44 -34
  24. airbyte_agent_mcp/_vendored/connector_sdk/secrets.py +2 -2
  25. airbyte_agent_mcp/_vendored/connector_sdk/telemetry/events.py +9 -8
  26. airbyte_agent_mcp/_vendored/connector_sdk/telemetry/tracker.py +9 -5
  27. airbyte_agent_mcp/_vendored/connector_sdk/types.py +7 -3
  28. airbyte_agent_mcp/server.py +34 -1
  29. {airbyte_agent_mcp-0.1.33.dist-info → airbyte_agent_mcp-0.1.60.dist-info}/METADATA +1 -1
  30. {airbyte_agent_mcp-0.1.33.dist-info → airbyte_agent_mcp-0.1.60.dist-info}/RECORD +31 -29
  31. {airbyte_agent_mcp-0.1.33.dist-info → airbyte_agent_mcp-0.1.60.dist-info}/WHEEL +0 -0
@@ -0,0 +1,179 @@
1
+ """Unified configuration for connector-sdk."""
2
+
3
+ import logging
4
+ import os
5
+ import tempfile
6
+ import uuid
7
+ from dataclasses import dataclass, field
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ import yaml
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # New config location
16
+ CONFIG_DIR = Path.home() / ".airbyte" / "connector-sdk"
17
+ CONFIG_PATH = CONFIG_DIR / "config.yaml"
18
+
19
+ # Legacy file locations (for migration)
20
+ LEGACY_USER_ID_PATH = Path.home() / ".airbyte" / "ai_sdk_user_id"
21
+ LEGACY_INTERNAL_MARKER_PATH = Path.home() / ".airbyte" / "internal_user"
22
+
23
+
24
+ @dataclass
25
+ class SDKConfig:
26
+ """Connector SDK configuration."""
27
+
28
+ user_id: str = field(default_factory=lambda: str(uuid.uuid4()))
29
+ is_internal_user: bool = False
30
+
31
+ def to_dict(self) -> dict[str, Any]:
32
+ """Convert to dictionary for YAML serialization."""
33
+ return {
34
+ "user_id": self.user_id,
35
+ "is_internal_user": self.is_internal_user,
36
+ }
37
+
38
+
39
+ def _delete_legacy_files() -> None:
40
+ """
41
+ Delete legacy config files after successful migration.
42
+
43
+ Removes:
44
+ - ~/.airbyte/ai_sdk_user_id
45
+ - ~/.airbyte/internal_user
46
+ """
47
+ for legacy_path in [LEGACY_USER_ID_PATH, LEGACY_INTERNAL_MARKER_PATH]:
48
+ try:
49
+ if legacy_path.exists():
50
+ legacy_path.unlink()
51
+ logger.debug(f"Deleted legacy config file: {legacy_path}")
52
+ except Exception as e:
53
+ logger.debug(f"Could not delete legacy file {legacy_path}: {e}")
54
+
55
+
56
+ def _migrate_legacy_config() -> SDKConfig | None:
57
+ """
58
+ Migrate from legacy file-based config to new YAML format.
59
+
60
+ Reads from:
61
+ - ~/.airbyte/ai_sdk_user_id (user_id)
62
+ - ~/.airbyte/internal_user (is_internal_user marker)
63
+
64
+ Returns SDKConfig if migration was successful, None otherwise.
65
+ """
66
+ user_id = None
67
+ is_internal = False
68
+
69
+ # Try to read legacy user_id
70
+ try:
71
+ if LEGACY_USER_ID_PATH.exists():
72
+ user_id = LEGACY_USER_ID_PATH.read_text().strip()
73
+ if not user_id:
74
+ user_id = None
75
+ except Exception:
76
+ pass
77
+
78
+ # Check legacy internal_user marker
79
+ try:
80
+ is_internal = LEGACY_INTERNAL_MARKER_PATH.exists()
81
+ except Exception:
82
+ pass
83
+
84
+ if user_id or is_internal:
85
+ return SDKConfig(
86
+ user_id=user_id or str(uuid.uuid4()),
87
+ is_internal_user=is_internal,
88
+ )
89
+
90
+ return None
91
+
92
+
93
+ def load_config() -> SDKConfig:
94
+ """
95
+ Load SDK configuration from config file.
96
+
97
+ Checks (in order):
98
+ 1. New config file at ~/.airbyte/connector-sdk/config.yaml
99
+ 2. Legacy files at ~/.airbyte/ai_sdk_user_id and ~/.airbyte/internal_user
100
+ 3. Creates new config with generated user_id if nothing exists
101
+
102
+ Environment variable AIRBYTE_INTERNAL_USER can override is_internal_user.
103
+
104
+ Returns:
105
+ SDKConfig with user_id and is_internal_user
106
+ """
107
+ config = None
108
+
109
+ # Try to load from new config file
110
+ try:
111
+ if CONFIG_PATH.exists():
112
+ content = CONFIG_PATH.read_text()
113
+ data = yaml.safe_load(content) or {}
114
+ config = SDKConfig(
115
+ user_id=data.get("user_id", str(uuid.uuid4())),
116
+ is_internal_user=data.get("is_internal_user", False),
117
+ )
118
+ # Always clean up legacy files if they exist (even if new config exists)
119
+ _delete_legacy_files()
120
+ except Exception as e:
121
+ logger.debug(f"Could not load config from {CONFIG_PATH}: {e}")
122
+
123
+ # Try to migrate from legacy files if new config doesn't exist
124
+ if config is None:
125
+ config = _migrate_legacy_config()
126
+ if config:
127
+ # Save migrated config to new location
128
+ try:
129
+ save_config(config)
130
+ logger.debug("Migrated legacy config to new location")
131
+ # Delete legacy files after successful migration
132
+ _delete_legacy_files()
133
+ except Exception as e:
134
+ logger.debug(f"Could not save migrated config: {e}")
135
+
136
+ # Create new config if nothing exists
137
+ if config is None:
138
+ config = SDKConfig()
139
+ try:
140
+ save_config(config)
141
+ except Exception as e:
142
+ logger.debug(f"Could not save new config: {e}")
143
+
144
+ # Environment variable override for is_internal_user
145
+ env_value = os.getenv("AIRBYTE_INTERNAL_USER", "").lower()
146
+ if env_value in ("true", "1", "yes"):
147
+ config.is_internal_user = True
148
+ elif env_value:
149
+ # Any other non-empty value (including "false", "0", "no") defaults to False
150
+ config.is_internal_user = False
151
+
152
+ return config
153
+
154
+
155
+ def save_config(config: SDKConfig) -> None:
156
+ """
157
+ Save SDK configuration to config file.
158
+
159
+ Creates the config directory if it doesn't exist.
160
+ Uses atomic writes to prevent corruption from concurrent access.
161
+
162
+ Args:
163
+ config: SDKConfig to save
164
+ """
165
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
166
+
167
+ # Use atomic write: write to temp file then rename (atomic on POSIX)
168
+ fd, temp_path = tempfile.mkstemp(dir=CONFIG_DIR, suffix=".tmp")
169
+ try:
170
+ with os.fdopen(fd, "w") as f:
171
+ yaml.dump(config.to_dict(), f, default_flow_style=False)
172
+ os.rename(temp_path, CONFIG_PATH)
173
+ except Exception:
174
+ # Clean up temp file on failure
175
+ try:
176
+ os.unlink(temp_path)
177
+ except OSError:
178
+ pass
179
+ raise
@@ -2,7 +2,7 @@
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from datetime import datetime
5
- from typing import Any, Dict, Optional
5
+ from typing import Any, Dict
6
6
 
7
7
 
8
8
  @dataclass
@@ -12,8 +12,8 @@ class OperationMetadata:
12
12
  entity: str
13
13
  action: str
14
14
  timestamp: datetime
15
- timing_ms: Optional[float] = None
16
- status_code: Optional[int] = None
17
- error_type: Optional[str] = None
18
- error_message: Optional[str] = None
19
- params: Optional[Dict[str, Any]] = None
15
+ timing_ms: float | None = None
16
+ status_code: int | None = None
17
+ error_type: str | None = None
18
+ error_message: str | None = None
19
+ params: Dict[str, Any] | None = None
@@ -3,49 +3,43 @@
3
3
  import logging
4
4
  import uuid
5
5
  from datetime import UTC, datetime
6
- from pathlib import Path
7
- from typing import Any, Dict, Optional
6
+ from typing import Any, Dict
7
+
8
+ from .config import SDKConfig, load_config
8
9
 
9
10
  logger = logging.getLogger(__name__)
10
11
 
12
+ # Cache the config at module level to avoid repeated reads
13
+ _cached_config: SDKConfig | None = None
14
+
15
+
16
+ def _get_config() -> SDKConfig:
17
+ """Get cached SDK config or load from file."""
18
+ global _cached_config
19
+ if _cached_config is None:
20
+ _cached_config = load_config()
21
+ return _cached_config
22
+
23
+
24
+ def _clear_config_cache() -> None:
25
+ """Clear the cached config. Used for testing."""
26
+ global _cached_config
27
+ _cached_config = None
28
+
11
29
 
12
30
  def get_persistent_user_id() -> str:
13
31
  """
14
- Get or create an anonymous user ID stored in the home directory.
32
+ Get the persistent anonymous user ID.
15
33
 
16
- The ID is stored in ~/.airbyte/ai_sdk_user_id and persists across all sessions.
17
- If the file doesn't exist, a new UUID is generated and saved.
34
+ Now reads from ~/.airbyte/connector-sdk/config.yaml
18
35
 
19
36
  Returns:
20
37
  An anonymous UUID string that uniquely identifies this user across sessions.
21
38
  """
22
- try:
23
- # Create .airbyte directory in home folder if it doesn't exist
24
- airbyte_dir = Path.home() / ".airbyte"
25
- airbyte_dir.mkdir(exist_ok=True)
26
-
27
- # Path to user ID file
28
- user_id_file = airbyte_dir / "ai_sdk_user_id"
29
-
30
- # Try to read existing user ID
31
- if user_id_file.exists():
32
- user_id = user_id_file.read_text().strip()
33
- if user_id: # Validate it's not empty
34
- return user_id
39
+ return _get_config().user_id
35
40
 
36
- # Generate new user ID if file doesn't exist or is empty
37
- user_id = str(uuid.uuid4())
38
- user_id_file.write_text(user_id)
39
- logger.debug(f"Generated new anonymous user ID: {user_id}")
40
41
 
41
- return user_id
42
- except Exception as e:
43
- # If we can't read/write the file, generate a session-only ID
44
- logger.debug(f"Could not access anonymous user ID file: {e}")
45
- return str(uuid.uuid4())
46
-
47
-
48
- def get_public_ip() -> Optional[str]:
42
+ def get_public_ip() -> str | None:
49
43
  """
50
44
  Fetch the public IP address of the user.
51
45
 
@@ -53,6 +47,8 @@ def get_public_ip() -> Optional[str]:
53
47
  Uses httpx for a robust HTTP request to a public IP service.
54
48
  """
55
49
  try:
50
+ # NOTE: Import here intentionally - this is a non-critical network call
51
+ # that may fail. Importing at module level would make httpx a hard dependency.
56
52
  import httpx
57
53
 
58
54
  # Use a short timeout to avoid blocking
@@ -65,15 +61,27 @@ def get_public_ip() -> Optional[str]:
65
61
  return None
66
62
 
67
63
 
64
+ def get_is_internal_user() -> bool:
65
+ """
66
+ Check if the current user is an internal Airbyte user.
67
+
68
+ Now reads from ~/.airbyte/connector-sdk/config.yaml
69
+ Environment variable AIRBYTE_INTERNAL_USER can override.
70
+
71
+ Returns False if not set or on any error.
72
+ """
73
+ return _get_config().is_internal_user
74
+
75
+
68
76
  class ObservabilitySession:
69
77
  """Shared session context for both logging and telemetry."""
70
78
 
71
79
  def __init__(
72
80
  self,
73
81
  connector_name: str,
74
- connector_version: Optional[str] = None,
82
+ connector_version: str | None = None,
75
83
  execution_context: str = "direct",
76
- session_id: Optional[str] = None,
84
+ session_id: str | None = None,
77
85
  ):
78
86
  self.session_id = session_id or str(uuid.uuid4())
79
87
  self.user_id = get_persistent_user_id()
@@ -84,6 +92,7 @@ class ObservabilitySession:
84
92
  self.operation_count = 0
85
93
  self.metadata: Dict[str, Any] = {}
86
94
  self.public_ip = get_public_ip()
95
+ self.is_internal_user = get_is_internal_user()
87
96
 
88
97
  def increment_operations(self):
89
98
  """Increment the operation counter."""
@@ -2,7 +2,7 @@
2
2
 
3
3
  import time
4
4
  from contextlib import asynccontextmanager
5
- from typing import Dict, Optional
5
+ from typing import Dict
6
6
 
7
7
 
8
8
  class PerformanceMonitor:
@@ -33,7 +33,7 @@ class PerformanceMonitor:
33
33
  metrics["min"] = min(metrics["min"], duration)
34
34
  metrics["max"] = max(metrics["max"], duration)
35
35
 
36
- def get_stats(self, metric_name: str) -> Optional[Dict[str, float]]:
36
+ def get_stats(self, metric_name: str) -> Dict[str, float] | None:
37
37
  """Get statistics for a metric.
38
38
 
39
39
  Args:
@@ -62,7 +62,7 @@ class PerformanceMonitor:
62
62
  """
63
63
  return {name: self.get_stats(name) for name in self._metrics.keys()}
64
64
 
65
- def reset(self, metric_name: Optional[str] = None):
65
+ def reset(self, metric_name: str | None = None):
66
66
  """Reset metrics.
67
67
 
68
68
  Args:
@@ -7,13 +7,13 @@ References:
7
7
  """
8
8
 
9
9
  from enum import StrEnum
10
- from typing import Dict, Optional
10
+ from typing import Dict
11
11
  from uuid import UUID
12
12
 
13
13
  from pydantic import BaseModel, ConfigDict, Field, field_validator
14
14
  from pydantic_core import Url
15
15
 
16
- from .extensions import RetryConfig
16
+ from .extensions import CacheConfig, RetryConfig
17
17
 
18
18
 
19
19
  class ExampleQuestions(BaseModel):
@@ -45,9 +45,9 @@ class Contact(BaseModel):
45
45
 
46
46
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
47
47
 
48
- name: Optional[str] = None
49
- url: Optional[str] = None
50
- email: Optional[str] = None
48
+ name: str | None = None
49
+ url: str | None = None
50
+ email: str | None = None
51
51
 
52
52
 
53
53
  class License(BaseModel):
@@ -60,7 +60,7 @@ class License(BaseModel):
60
60
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
61
61
 
62
62
  name: str
63
- url: Optional[str] = None
63
+ url: str | None = None
64
64
 
65
65
 
66
66
  class DocUrlType(StrEnum):
@@ -85,7 +85,7 @@ class DocUrl(BaseModel):
85
85
 
86
86
  url: str
87
87
  type: DocUrlType
88
- title: Optional[str] = None
88
+ title: str | None = None
89
89
 
90
90
  @field_validator("url")
91
91
  def validate_url(cls, v):
@@ -105,23 +105,25 @@ class Info(BaseModel):
105
105
  - x-airbyte-external-documentation-urls: List of external documentation URLs (Airbyte extension)
106
106
  - x-airbyte-retry-config: Retry configuration for transient errors (Airbyte extension)
107
107
  - x-airbyte-example-questions: Example questions for AI connector README (Airbyte extension)
108
+ - x-airbyte-cache: Cache configuration for field mapping between API and cache schemas (Airbyte extension)
108
109
  """
109
110
 
110
111
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
111
112
 
112
113
  title: str
113
114
  version: str
114
- description: Optional[str] = None
115
- terms_of_service: Optional[str] = Field(None, alias="termsOfService")
116
- contact: Optional[Contact] = None
117
- license: Optional[License] = None
115
+ description: str | None = None
116
+ terms_of_service: str | None = Field(None, alias="termsOfService")
117
+ contact: Contact | None = None
118
+ license: License | None = None
118
119
 
119
120
  # Airbyte extension
120
- x_airbyte_connector_name: Optional[str] = Field(None, alias="x-airbyte-connector-name")
121
- x_airbyte_connector_id: Optional[UUID] = Field(None, alias="x-airbyte-connector-id")
121
+ x_airbyte_connector_name: str | None = Field(None, alias="x-airbyte-connector-name")
122
+ x_airbyte_connector_id: UUID | None = Field(None, alias="x-airbyte-connector-id")
122
123
  x_airbyte_external_documentation_urls: list[DocUrl] = Field(..., alias="x-airbyte-external-documentation-urls")
123
- x_airbyte_retry_config: Optional[RetryConfig] = Field(None, alias="x-airbyte-retry-config")
124
- x_airbyte_example_questions: Optional[ExampleQuestions] = Field(None, alias="x-airbyte-example-questions")
124
+ x_airbyte_retry_config: RetryConfig | None = Field(None, alias="x-airbyte-retry-config")
125
+ x_airbyte_example_questions: ExampleQuestions | None = Field(None, alias="x-airbyte-example-questions")
126
+ x_airbyte_cache: CacheConfig | None = Field(None, alias="x-airbyte-cache")
125
127
 
126
128
 
127
129
  class ServerVariable(BaseModel):
@@ -133,9 +135,9 @@ class ServerVariable(BaseModel):
133
135
 
134
136
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
135
137
 
136
- enum: Optional[list[str]] = None
138
+ enum: list[str] | None = None
137
139
  default: str
138
- description: Optional[str] = None
140
+ description: str | None = None
139
141
 
140
142
 
141
143
  class Server(BaseModel):
@@ -148,7 +150,7 @@ class Server(BaseModel):
148
150
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
149
151
 
150
152
  url: str
151
- description: Optional[str] = None
153
+ description: str | None = None
152
154
  variables: Dict[str, ServerVariable] = Field(default_factory=dict)
153
155
 
154
156
  @field_validator("url")
@@ -7,7 +7,7 @@ References:
7
7
  - https://spec.openapis.org/oas/v3.1.0#parameter-object
8
8
  """
9
9
 
10
- from typing import Any, Dict, List, Literal, Optional, Union
10
+ from typing import Any, Dict, List, Literal, Union
11
11
 
12
12
  from pydantic import BaseModel, ConfigDict, Field
13
13
 
@@ -30,43 +30,44 @@ class Schema(BaseModel):
30
30
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
31
31
 
32
32
  # Core JSON Schema fields
33
- type: Optional[str] = None
34
- format: Optional[str] = None
35
- title: Optional[str] = None
36
- description: Optional[str] = None
37
- default: Optional[Any] = None
38
- example: Optional[Any] = None
33
+ type: str | None = None
34
+ format: str | None = None
35
+ title: str | None = None
36
+ description: str | None = None
37
+ default: Any | None = None
38
+ example: Any | None = None
39
39
 
40
40
  # Object properties
41
41
  properties: Dict[str, Any] = Field(default_factory=dict) # May contain $ref
42
42
  required: List[str] = Field(default_factory=list)
43
- additional_properties: Optional[Any] = Field(None, alias="additionalProperties")
43
+ additional_properties: Any | None = Field(None, alias="additionalProperties")
44
44
 
45
45
  # Array properties
46
- items: Optional[Any] = None # May be Schema or $ref
46
+ items: Any | None = None # May be Schema or $ref
47
47
 
48
48
  # Validation
49
- enum: Optional[List[Any]] = None
50
- min_length: Optional[int] = Field(None, alias="minLength")
51
- max_length: Optional[int] = Field(None, alias="maxLength")
52
- minimum: Optional[float] = None
53
- maximum: Optional[float] = None
54
- pattern: Optional[str] = None
49
+ enum: List[Any] | None = None
50
+ min_length: int | None = Field(None, alias="minLength")
51
+ max_length: int | None = Field(None, alias="maxLength")
52
+ minimum: float | None = None
53
+ maximum: float | None = None
54
+ pattern: str | None = None
55
55
 
56
56
  # Composition
57
- all_of: Optional[List[Any]] = Field(None, alias="allOf")
58
- any_of: Optional[List[Any]] = Field(None, alias="anyOf")
59
- one_of: Optional[List[Any]] = Field(None, alias="oneOf")
60
- not_: Optional[Any] = Field(None, alias="not")
57
+ all_of: List[Any] | None = Field(None, alias="allOf")
58
+ any_of: List[Any] | None = Field(None, alias="anyOf")
59
+ one_of: List[Any] | None = Field(None, alias="oneOf")
60
+ not_: Any | None = Field(None, alias="not")
61
61
 
62
62
  # Metadata
63
- nullable: Optional[bool] = Field(None, deprecated="Use type union with null instead (OpenAPI 3.1)")
64
- read_only: Optional[bool] = Field(None, alias="readOnly")
65
- write_only: Optional[bool] = Field(None, alias="writeOnly")
66
- deprecated: Optional[bool] = None
63
+ nullable: bool | None = Field(None, deprecated="Use type union with null instead (OpenAPI 3.1)")
64
+ read_only: bool | None = Field(None, alias="readOnly")
65
+ write_only: bool | None = Field(None, alias="writeOnly")
66
+ deprecated: bool | None = None
67
67
 
68
- # Airbyte extension
69
- x_airbyte_entity_name: Optional[str] = Field(None, alias="x-airbyte-entity-name")
68
+ # Airbyte extensions
69
+ x_airbyte_entity_name: str | None = Field(None, alias="x-airbyte-entity-name")
70
+ x_airbyte_stream_name: str | None = Field(None, alias="x-airbyte-stream-name")
70
71
 
71
72
 
72
73
  class Parameter(BaseModel):
@@ -80,19 +81,19 @@ class Parameter(BaseModel):
80
81
 
81
82
  name: str
82
83
  in_: Literal["query", "header", "path", "cookie"] = Field(alias="in")
83
- description: Optional[str] = None
84
- required: Optional[bool] = None
85
- deprecated: Optional[bool] = None
86
- allow_empty_value: Optional[bool] = Field(None, alias="allowEmptyValue")
84
+ description: str | None = None
85
+ required: bool | None = None
86
+ deprecated: bool | None = None
87
+ allow_empty_value: bool | None = Field(None, alias="allowEmptyValue")
87
88
 
88
89
  # Schema can be inline or reference
89
- schema_: Optional[Dict[str, Any]] = Field(None, alias="schema")
90
+ schema_: Dict[str, Any] | None = Field(None, alias="schema")
90
91
 
91
92
  # Style and examples
92
- style: Optional[str] = None
93
- explode: Optional[bool] = None
94
- example: Optional[Any] = None
95
- examples: Optional[Dict[str, Any]] = None
93
+ style: str | None = None
94
+ explode: bool | None = None
95
+ example: Any | None = None
96
+ examples: Dict[str, Any] | None = None
96
97
 
97
98
 
98
99
  class MediaType(BaseModel):
@@ -104,10 +105,10 @@ class MediaType(BaseModel):
104
105
 
105
106
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
106
107
 
107
- schema_: Optional[Dict[str, Any]] = Field(None, alias="schema")
108
- example: Optional[Any] = None
109
- examples: Optional[Dict[str, Any]] = None
110
- encoding: Optional[Dict[str, Any]] = None
108
+ schema_: Dict[str, Any] | None = Field(None, alias="schema")
109
+ example: Any | None = None
110
+ examples: Dict[str, Any] | None = None
111
+ encoding: Dict[str, Any] | None = None
111
112
 
112
113
 
113
114
  class GraphQLBodyConfig(BaseModel):
@@ -124,12 +125,12 @@ class GraphQLBodyConfig(BaseModel):
124
125
  ...,
125
126
  description="GraphQL query or mutation string with optional template placeholders (e.g., {{ variable }})",
126
127
  )
127
- variables: Optional[Dict[str, Any]] = Field(
128
+ variables: Dict[str, Any] | None = Field(
128
129
  None,
129
130
  description="Variables to substitute in the GraphQL query using template syntax (e.g., {{ param_name }})",
130
131
  )
131
- operationName: Optional[str] = Field(None, description="Operation name for queries with multiple operations")
132
- default_fields: Optional[Union[str, List[str]]] = Field(
132
+ operationName: str | None = Field(None, description="Operation name for queries with multiple operations")
133
+ default_fields: Union[str, List[str]] | None = Field(
133
134
  None,
134
135
  description="Default fields to select if not provided in request parameters. Can be a string or array of field names.",
135
136
  )
@@ -155,7 +156,7 @@ class PathOverrideConfig(BaseModel):
155
156
 
156
157
  path: str = Field(
157
158
  ...,
158
- description=("Actual HTTP path to use for requests (e.g., '/graphql'). " "Must start with '/'"),
159
+ description=("Actual HTTP path to use for requests (e.g., '/graphql'). Must start with '/'"),
159
160
  )
160
161
 
161
162
 
@@ -172,17 +173,17 @@ class RequestBody(BaseModel):
172
173
 
173
174
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
174
175
 
175
- description: Optional[str] = None
176
+ description: str | None = None
176
177
  content: Dict[str, MediaType] = Field(default_factory=dict)
177
- required: Optional[bool] = None
178
+ required: bool | None = None
178
179
 
179
180
  # Airbyte extensions for GraphQL support
180
181
  # See connector_sdk.extensions for AIRBYTE_BODY_TYPE constant
181
- x_airbyte_body_type: Optional[BodyTypeConfig] = Field(
182
+ x_airbyte_body_type: BodyTypeConfig | None = Field(
182
183
  None,
183
184
  alias="x-airbyte-body-type", # AIRBYTE_BODY_TYPE
184
185
  description=(
185
- "Body type and configuration. Contains 'type' field (e.g., 'graphql') " "and type-specific configuration (query, variables, etc.)."
186
+ "Body type and configuration. Contains 'type' field (e.g., 'graphql') and type-specific configuration (query, variables, etc.)."
186
187
  ),
187
188
  )
188
189
 
@@ -196,11 +197,11 @@ class Header(BaseModel):
196
197
 
197
198
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
198
199
 
199
- description: Optional[str] = None
200
- required: Optional[bool] = None
201
- deprecated: Optional[bool] = None
202
- schema_: Optional[Dict[str, Any]] = Field(None, alias="schema")
203
- example: Optional[Any] = None
200
+ description: str | None = None
201
+ required: bool | None = None
202
+ deprecated: bool | None = None
203
+ schema_: Dict[str, Any] | None = Field(None, alias="schema")
204
+ example: Any | None = None
204
205
 
205
206
 
206
207
  class Response(BaseModel):
@@ -213,9 +214,9 @@ class Response(BaseModel):
213
214
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
214
215
 
215
216
  description: str
216
- headers: Optional[Dict[str, Header]] = None
217
- content: Optional[Dict[str, MediaType]] = None
218
- links: Optional[Dict[str, Any]] = None
217
+ headers: Dict[str, Header] | None = None
218
+ content: Dict[str, MediaType] | None = None
219
+ links: Dict[str, Any] | None = None
219
220
 
220
221
 
221
222
  class Components(BaseModel):
@@ -230,9 +231,9 @@ class Components(BaseModel):
230
231
  schemas: Dict[str, Schema] = Field(default_factory=dict)
231
232
  responses: Dict[str, Response] = Field(default_factory=dict)
232
233
  parameters: Dict[str, Parameter] = Field(default_factory=dict)
233
- examples: Optional[Dict[str, Any]] = None
234
+ examples: Dict[str, Any] | None = None
234
235
  request_bodies: Dict[str, RequestBody] = Field(default_factory=dict, alias="requestBodies")
235
- headers: Optional[Dict[str, Header]] = None
236
+ headers: Dict[str, Header] | None = None
236
237
  security_schemes: Dict[str, SecurityScheme] = Field(default_factory=dict, alias="securitySchemes")
237
- links: Optional[Dict[str, Any]] = None
238
- callbacks: Optional[Dict[str, Any]] = None
238
+ links: Dict[str, Any] | None = None
239
+ callbacks: Dict[str, Any] | None = None