traia-iatp 0.1.29__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.

Potentially problematic release.


This version of traia-iatp might be problematic. Click here for more details.

Files changed (107) hide show
  1. traia_iatp/README.md +368 -0
  2. traia_iatp/__init__.py +54 -0
  3. traia_iatp/cli/__init__.py +5 -0
  4. traia_iatp/cli/main.py +483 -0
  5. traia_iatp/client/__init__.py +10 -0
  6. traia_iatp/client/a2a_client.py +274 -0
  7. traia_iatp/client/crewai_a2a_tools.py +335 -0
  8. traia_iatp/client/d402_a2a_client.py +293 -0
  9. traia_iatp/client/grpc_a2a_tools.py +349 -0
  10. traia_iatp/client/root_path_a2a_client.py +1 -0
  11. traia_iatp/contracts/__init__.py +12 -0
  12. traia_iatp/contracts/iatp_contracts_config.py +263 -0
  13. traia_iatp/contracts/wallet_creator.py +255 -0
  14. traia_iatp/core/__init__.py +43 -0
  15. traia_iatp/core/models.py +172 -0
  16. traia_iatp/d402/__init__.py +55 -0
  17. traia_iatp/d402/chains.py +102 -0
  18. traia_iatp/d402/client.py +150 -0
  19. traia_iatp/d402/clients/__init__.py +7 -0
  20. traia_iatp/d402/clients/base.py +218 -0
  21. traia_iatp/d402/clients/httpx.py +219 -0
  22. traia_iatp/d402/common.py +114 -0
  23. traia_iatp/d402/encoding.py +28 -0
  24. traia_iatp/d402/examples/client_example.py +197 -0
  25. traia_iatp/d402/examples/server_example.py +171 -0
  26. traia_iatp/d402/facilitator.py +453 -0
  27. traia_iatp/d402/fastapi_middleware/__init__.py +6 -0
  28. traia_iatp/d402/fastapi_middleware/middleware.py +225 -0
  29. traia_iatp/d402/fastmcp_middleware.py +147 -0
  30. traia_iatp/d402/mcp_middleware.py +434 -0
  31. traia_iatp/d402/middleware.py +193 -0
  32. traia_iatp/d402/models.py +116 -0
  33. traia_iatp/d402/networks.py +98 -0
  34. traia_iatp/d402/path.py +43 -0
  35. traia_iatp/d402/payment_introspection.py +104 -0
  36. traia_iatp/d402/payment_signing.py +178 -0
  37. traia_iatp/d402/paywall.py +119 -0
  38. traia_iatp/d402/starlette_middleware.py +326 -0
  39. traia_iatp/d402/template.py +1 -0
  40. traia_iatp/d402/types.py +300 -0
  41. traia_iatp/mcp/__init__.py +18 -0
  42. traia_iatp/mcp/client.py +201 -0
  43. traia_iatp/mcp/d402_mcp_tool_adapter.py +361 -0
  44. traia_iatp/mcp/mcp_agent_template.py +481 -0
  45. traia_iatp/mcp/templates/Dockerfile.j2 +80 -0
  46. traia_iatp/mcp/templates/README.md.j2 +310 -0
  47. traia_iatp/mcp/templates/cursor-rules.md.j2 +520 -0
  48. traia_iatp/mcp/templates/deployment_params.json.j2 +20 -0
  49. traia_iatp/mcp/templates/docker-compose.yml.j2 +32 -0
  50. traia_iatp/mcp/templates/dockerignore.j2 +47 -0
  51. traia_iatp/mcp/templates/env.example.j2 +57 -0
  52. traia_iatp/mcp/templates/gitignore.j2 +77 -0
  53. traia_iatp/mcp/templates/mcp_health_check.py.j2 +150 -0
  54. traia_iatp/mcp/templates/pyproject.toml.j2 +32 -0
  55. traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
  56. traia_iatp/mcp/templates/run_local_docker.sh.j2 +390 -0
  57. traia_iatp/mcp/templates/server.py.j2 +175 -0
  58. traia_iatp/mcp/traia_mcp_adapter.py +543 -0
  59. traia_iatp/preview_diagrams.html +181 -0
  60. traia_iatp/registry/__init__.py +26 -0
  61. traia_iatp/registry/atlas_search_indexes.json +280 -0
  62. traia_iatp/registry/embeddings.py +298 -0
  63. traia_iatp/registry/iatp_search_api.py +846 -0
  64. traia_iatp/registry/mongodb_registry.py +771 -0
  65. traia_iatp/registry/readmes/ATLAS_SEARCH_INDEXES.md +252 -0
  66. traia_iatp/registry/readmes/ATLAS_SEARCH_SETUP.md +134 -0
  67. traia_iatp/registry/readmes/AUTHENTICATION_UPDATE.md +124 -0
  68. traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +172 -0
  69. traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +257 -0
  70. traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +208 -0
  71. traia_iatp/registry/readmes/README.md +251 -0
  72. traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +191 -0
  73. traia_iatp/scripts/__init__.py +2 -0
  74. traia_iatp/scripts/create_wallet.py +244 -0
  75. traia_iatp/server/__init__.py +15 -0
  76. traia_iatp/server/a2a_server.py +219 -0
  77. traia_iatp/server/example_template_usage.py +72 -0
  78. traia_iatp/server/iatp_server_agent_generator.py +237 -0
  79. traia_iatp/server/iatp_server_template_generator.py +235 -0
  80. traia_iatp/server/templates/.dockerignore.j2 +48 -0
  81. traia_iatp/server/templates/Dockerfile.j2 +49 -0
  82. traia_iatp/server/templates/README.md +137 -0
  83. traia_iatp/server/templates/README.md.j2 +425 -0
  84. traia_iatp/server/templates/__init__.py +1 -0
  85. traia_iatp/server/templates/__main__.py.j2 +565 -0
  86. traia_iatp/server/templates/agent.py.j2 +94 -0
  87. traia_iatp/server/templates/agent_config.json.j2 +22 -0
  88. traia_iatp/server/templates/agent_executor.py.j2 +279 -0
  89. traia_iatp/server/templates/docker-compose.yml.j2 +23 -0
  90. traia_iatp/server/templates/env.example.j2 +84 -0
  91. traia_iatp/server/templates/gitignore.j2 +78 -0
  92. traia_iatp/server/templates/grpc_server.py.j2 +218 -0
  93. traia_iatp/server/templates/pyproject.toml.j2 +78 -0
  94. traia_iatp/server/templates/run_local_docker.sh.j2 +103 -0
  95. traia_iatp/server/templates/server.py.j2 +243 -0
  96. traia_iatp/special_agencies/__init__.py +4 -0
  97. traia_iatp/special_agencies/registry_search_agency.py +392 -0
  98. traia_iatp/utils/__init__.py +10 -0
  99. traia_iatp/utils/docker_utils.py +251 -0
  100. traia_iatp/utils/general.py +64 -0
  101. traia_iatp/utils/iatp_utils.py +126 -0
  102. traia_iatp-0.1.29.dist-info/METADATA +423 -0
  103. traia_iatp-0.1.29.dist-info/RECORD +107 -0
  104. traia_iatp-0.1.29.dist-info/WHEEL +5 -0
  105. traia_iatp-0.1.29.dist-info/entry_points.txt +2 -0
  106. traia_iatp-0.1.29.dist-info/licenses/LICENSE +21 -0
  107. traia_iatp-0.1.29.dist-info/top_level.txt +1 -0
@@ -0,0 +1,543 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Traia MCP Adapter
4
+
5
+ A custom MCP adapter that extends CrewAI's MCPServerAdapter to support
6
+ passing headers (like Authorization) and x402 payment protocol to streamable-http MCP servers.
7
+
8
+ This adapter transparently handles:
9
+ - Authenticated connections (via headers)
10
+ - Payment-based connections (via x402/d402 protocol)
11
+ - Standard connections (no auth or payment)
12
+ """
13
+
14
+ import logging
15
+ from typing import Dict, Any, List, Optional, Union
16
+ import httpx
17
+ from functools import wraps
18
+
19
+ from crewai_tools import MCPServerAdapter
20
+ from crewai.tools import BaseTool
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Try to import d402 payment hooks from IATP
25
+ try:
26
+ from eth_account import Account
27
+ from traia_iatp.d402.clients.httpx import d402_payment_hooks
28
+ D402_AVAILABLE = True
29
+ except ImportError:
30
+ D402_AVAILABLE = False
31
+ logger.warning("d402 payment hooks not available. Ensure traia_iatp.d402 is installed.")
32
+
33
+
34
+ class TraiaMCPAdapter(MCPServerAdapter):
35
+ """
36
+ MCP adapter that supports custom headers for streamable-http transport.
37
+
38
+ This adapter extracts headers from server_params and injects them into
39
+ all HTTP requests made to the MCP server. It works transparently for
40
+ both authenticated and non-authenticated connections.
41
+
42
+ Example:
43
+ ```python
44
+ # With authentication headers
45
+ server_params = {
46
+ "url": "https://mcp.example.com/mcp",
47
+ "transport": "streamable-http",
48
+ "headers": {"Authorization": "Bearer YOUR_API_KEY"}
49
+ }
50
+
51
+ # Without headers (standard connection)
52
+ server_params = {
53
+ "url": "https://mcp.example.com/mcp",
54
+ "transport": "streamable-http"
55
+ }
56
+
57
+ with TraiaMCPAdapter(server_params) as tools:
58
+ # Use tools with or without authentication
59
+ agent = Agent(tools=tools)
60
+ ```
61
+ """
62
+
63
+ def __init__(
64
+ self,
65
+ server_params: Union[Dict[str, Any], Any],
66
+ *tool_names: str
67
+ ):
68
+ """
69
+ Initialize the adapter with optional header and x402 payment support.
70
+
71
+ Args:
72
+ server_params: Server configuration. For streamable-http, can include:
73
+ - url: The MCP server URL
74
+ - transport: "streamable-http"
75
+ - headers: Optional dict of headers to include in requests
76
+ - x402_account: Optional eth_account.Account for x402 payments
77
+ - x402_max_value: Optional max payment value
78
+ *tool_names: Optional tool names to filter
79
+ """
80
+ # Handle dict params
81
+ if isinstance(server_params, dict):
82
+ # Extract headers and d402 config if present (don't modify original)
83
+ server_params_copy = server_params.copy()
84
+ self._auth_headers = server_params_copy.pop("headers", {})
85
+ self._d402_account = server_params_copy.pop("d402_account", None)
86
+ self._d402_max_value = server_params_copy.pop("d402_max_value", None)
87
+
88
+ # IMPORTANT: Also remove any x402_* parameters that shouldn't go to MCP library
89
+ # The MCP library's streamablehttp_client() doesn't accept these
90
+ server_params_copy.pop("x402_account", None)
91
+ server_params_copy.pop("x402_max_value", None)
92
+
93
+ # Determine connection mode
94
+ if self._d402_account and D402_AVAILABLE:
95
+ logger.info("💳 TraiaMCPAdapter: d402 payment protocol enabled")
96
+ logger.info(f" Client account: {self._d402_account.address}")
97
+ logger.info(f" Max value: {self._d402_max_value}")
98
+ logger.info(" Applying httpx monkey-patch for d402 hooks...")
99
+ # Apply d402 payment hooks patch
100
+ self._apply_d402_patch()
101
+ logger.info(" ✅ Monkey-patch applied - ALL new httpx clients will have d402 hooks")
102
+ elif self._auth_headers:
103
+ logger.info(f"TraiaMCPAdapter: Headers configured: {list(self._auth_headers.keys())}")
104
+ # Apply header injection patch
105
+ self._apply_httpx_patch()
106
+
107
+ # Pass clean params to parent - this will create the MCP client
108
+ logger.info(" Creating parent MCPServerAdapter...")
109
+ super().__init__(server_params_copy, *tool_names)
110
+ logger.info(" ✅ Parent MCPServerAdapter created")
111
+ else:
112
+ # Non-dict params, no headers or payment possible
113
+ self._auth_headers = {}
114
+ self._d402_account = None
115
+ self._d402_max_value = None
116
+ super().__init__(server_params, *tool_names)
117
+
118
+ def _apply_httpx_patch(self):
119
+ """Monkey-patch httpx.AsyncClient to inject headers."""
120
+ original_init = httpx.AsyncClient.__init__
121
+ auth_headers = self._auth_headers
122
+
123
+ @wraps(original_init)
124
+ def patched_init(client_self, *args, **kwargs):
125
+ # Get existing headers
126
+ existing_headers = kwargs.get('headers', {})
127
+ if isinstance(existing_headers, dict):
128
+ # Merge our headers
129
+ merged_headers = {**auth_headers, **existing_headers}
130
+ kwargs['headers'] = merged_headers
131
+ logger.debug(f"Injected headers into httpx.AsyncClient: {list(auth_headers.keys())}")
132
+
133
+ # Also ensure headers are preserved on redirects
134
+ if 'follow_redirects' not in kwargs:
135
+ kwargs['follow_redirects'] = True
136
+
137
+ # Call original init
138
+ original_init(client_self, *args, **kwargs)
139
+
140
+ # Apply the patch
141
+ httpx.AsyncClient.__init__ = patched_init
142
+ self._original_httpx_init = original_init
143
+ logger.debug("Applied httpx.AsyncClient monkey patch for headers")
144
+
145
+ def _apply_d402_patch(self):
146
+ """Monkey-patch httpx.AsyncClient to add d402 payment hooks."""
147
+ if not D402_AVAILABLE:
148
+ raise ImportError("d402 payment hooks not available. Ensure traia_iatp.d402 is installed.")
149
+
150
+ original_init = httpx.AsyncClient.__init__
151
+ d402_account = self._d402_account # Keep variable name for compatibility
152
+ d402_max_value = self._d402_max_value
153
+
154
+ @wraps(original_init)
155
+ def patched_init(client_self, *args, **kwargs):
156
+ # Log client creation
157
+ import traceback
158
+ creation_stack = ''.join(traceback.format_stack()[-4:-1])
159
+ logger.info(f"🔨 httpx.AsyncClient.__init__ called")
160
+ logger.debug(f" Creation stack: {creation_stack}")
161
+
162
+ # Call original init first
163
+ original_init(client_self, *args, **kwargs)
164
+
165
+ # Add d402 payment hooks to the client
166
+ if d402_account:
167
+ try:
168
+ hooks = d402_payment_hooks(d402_account, max_value=d402_max_value)
169
+ # Merge with existing hooks if any
170
+ if hasattr(client_self, 'event_hooks') and client_self.event_hooks:
171
+ # Merge hooks
172
+ for event_type, handlers in hooks.items():
173
+ if event_type in client_self.event_hooks:
174
+ client_self.event_hooks[event_type].extend(handlers)
175
+ else:
176
+ client_self.event_hooks[event_type] = handlers
177
+ else:
178
+ client_self.event_hooks = hooks
179
+
180
+ logger.info(f"✅ Applied d402 payment hooks to httpx.AsyncClient")
181
+ logger.info(f" Account: {d402_account.address[:10]}...")
182
+ logger.info(f" Hooks: {list(hooks.keys())}")
183
+ except Exception as e:
184
+ logger.error(f"❌ Failed to apply d402 hooks: {e}")
185
+ import traceback
186
+ logger.error(traceback.format_exc())
187
+ raise
188
+
189
+ # Apply the patch
190
+ httpx.AsyncClient.__init__ = patched_init
191
+ self._original_httpx_init = original_init
192
+ logger.debug("Applied httpx.AsyncClient monkey patch for d402 payments")
193
+
194
+ def __exit__(self, exc_type, exc_val, exc_tb):
195
+ """Exit context manager and restore original httpx if patched."""
196
+ # Restore original httpx.AsyncClient.__init__ if we patched it
197
+ if hasattr(self, '_original_httpx_init'):
198
+ httpx.AsyncClient.__init__ = self._original_httpx_init
199
+ logger.debug("Restored original httpx.AsyncClient")
200
+
201
+ # Call parent exit
202
+ return super().__exit__(exc_type, exc_val, exc_tb)
203
+
204
+ def __enter__(self) -> List[BaseTool]:
205
+ """Enter context manager."""
206
+ if self._d402_account:
207
+ logger.debug(f"TraiaMCPAdapter: Using d402 payment connection")
208
+ elif self._auth_headers:
209
+ logger.debug(f"TraiaMCPAdapter: Using authenticated connection")
210
+
211
+ return super().__enter__()
212
+
213
+
214
+ def create_mcp_adapter(
215
+ url: str,
216
+ headers: Optional[Dict[str, str]] = None,
217
+ transport: str = "streamable-http",
218
+ tool_names: Optional[List[str]] = None,
219
+ x402_account: Optional[Any] = None,
220
+ x402_max_value: Optional[int] = None
221
+ ) -> TraiaMCPAdapter:
222
+ """
223
+ Create a Traia MCP adapter with optional headers and/or d402 payment support.
224
+
225
+ Args:
226
+ url: MCP server URL
227
+ headers: Optional dictionary of headers to include in requests
228
+ transport: Transport type (default: streamable-http)
229
+ tool_names: Optional list of tool names to filter
230
+ x402_account: Optional eth_account.Account for d402 payment protocol
231
+ x402_max_value: Optional maximum payment value in base units.
232
+ Typically, each MCP server uses one primary token, so this limit
233
+ applies to all endpoints using that token. Set based on the token's
234
+ base units (e.g., USDC with 6 decimals: $1.00 = 1_000_000).
235
+
236
+ Returns:
237
+ TraiaMCPAdapter configured with headers and/or d402 payments
238
+
239
+ Example:
240
+ ```python
241
+ # With headers (authenticated)
242
+ adapter = create_mcp_adapter(
243
+ url="https://news-mcp.example.com/mcp",
244
+ headers={"Authorization": "Bearer YOUR_API_KEY"}
245
+ )
246
+
247
+ # With d402 payments
248
+ from eth_account import Account
249
+ account = Account.from_key("0x...")
250
+ adapter = create_mcp_adapter(
251
+ url="https://news-mcp.example.com/mcp",
252
+ x402_account=account,
253
+ x402_max_value=1_000_000 # $1.00 in USDC (6 decimals)
254
+ )
255
+
256
+ # Without headers or payment (standard)
257
+ adapter = create_mcp_adapter(
258
+ url="https://news-mcp.example.com/mcp"
259
+ )
260
+
261
+ with adapter as tools:
262
+ agent = Agent(tools=tools)
263
+ ```
264
+ """
265
+ server_params = {
266
+ "url": url,
267
+ "transport": transport
268
+ }
269
+
270
+ if headers:
271
+ server_params["headers"] = headers
272
+
273
+ if x402_account:
274
+ server_params["d402_account"] = x402_account # Use d402_ internally
275
+ if x402_max_value is not None:
276
+ server_params["d402_max_value"] = x402_max_value # Use d402_ internally
277
+
278
+ if tool_names:
279
+ return TraiaMCPAdapter(server_params, *tool_names)
280
+ else:
281
+ return TraiaMCPAdapter(server_params)
282
+
283
+
284
+ def create_mcp_adapter_with_auth(
285
+ url: str,
286
+ api_key: str,
287
+ auth_header: str = "Authorization",
288
+ auth_prefix: str = "Bearer",
289
+ transport: str = "streamable-http",
290
+ tool_names: Optional[List[str]] = None
291
+ ) -> TraiaMCPAdapter:
292
+ """
293
+ Create a Traia MCP adapter with authentication.
294
+
295
+ Args:
296
+ url: MCP server URL
297
+ api_key: API key for authentication
298
+ auth_header: Header name for auth (default: Authorization)
299
+ auth_prefix: Auth scheme prefix (default: Bearer)
300
+ transport: Transport type (default: streamable-http)
301
+ tool_names: Optional list of tool names to filter
302
+
303
+ Returns:
304
+ TraiaMCPAdapter configured with auth headers
305
+
306
+ Example:
307
+ ```python
308
+ adapter = create_mcp_adapter_with_auth(
309
+ url="https://news-mcp.example.com/mcp",
310
+ api_key="your-api-key"
311
+ )
312
+
313
+ with adapter as tools:
314
+ # Tools will include Authorization header in all requests
315
+ agent = Agent(tools=tools)
316
+ ```
317
+ """
318
+ headers = {}
319
+ if auth_prefix:
320
+ headers[auth_header] = f"{auth_prefix} {api_key}"
321
+ else:
322
+ headers[auth_header] = api_key
323
+
324
+ return create_mcp_adapter(url, headers, transport, tool_names)
325
+
326
+
327
+ def create_mcp_adapter_with_x402(
328
+ url: str,
329
+ account: Any,
330
+ max_value: Optional[int] = None,
331
+ transport: str = "streamable-http",
332
+ tool_names: Optional[List[str]] = None
333
+ ) -> TraiaMCPAdapter:
334
+ """
335
+ Create a Traia MCP adapter with d402 payment protocol support.
336
+
337
+ Note: Function name uses "x402" for backward compatibility, but it uses
338
+ the d402 implementation from traia_iatp.d402.
339
+
340
+ Args:
341
+ url: MCP server URL
342
+ account: eth_account.Account instance for signing payments
343
+ max_value: Optional maximum allowed payment amount in base units.
344
+ Typically, each MCP server uses one primary token, so this limit
345
+ applies to all endpoints using that token. Set based on the token's
346
+ base units (e.g., USDC with 6 decimals: $1.00 = 1_000_000).
347
+ transport: Transport type (default: streamable-http)
348
+ tool_names: Optional list of tool names to filter
349
+
350
+ Returns:
351
+ TraiaMCPAdapter configured with d402 payment hooks
352
+
353
+ Example:
354
+ ```python
355
+ from eth_account import Account
356
+
357
+ account = Account.from_key("0x...")
358
+ adapter = create_mcp_adapter_with_x402(
359
+ url="https://news-mcp.example.com/mcp",
360
+ account=account,
361
+ max_value=1_000_000 # $1.00 in USDC (6 decimals) - adjust for your server's token
362
+ )
363
+
364
+ with adapter as tools:
365
+ # Tools will automatically handle 402 Payment Required responses
366
+ agent = Agent(tools=tools)
367
+ ```
368
+ """
369
+ if not D402_AVAILABLE:
370
+ raise ImportError(
371
+ "d402 payment hooks not available. Ensure traia_iatp.d402 is installed."
372
+ )
373
+
374
+ return create_mcp_adapter(
375
+ url=url,
376
+ transport=transport,
377
+ tool_names=tool_names,
378
+ x402_account=account, # Function param uses x402 for backward compat
379
+ x402_max_value=max_value # Will be converted to d402 internally
380
+ )
381
+
382
+
383
+ # Backwards compatibility aliases
384
+ HeaderMCPAdapter = TraiaMCPAdapter
385
+ create_mcp_adapter_with_headers = create_mcp_adapter
386
+
387
+
388
+ # Usage examples
389
+ USAGE_GUIDE = """
390
+ TraiaMCPAdapter Usage Guide
391
+ ==========================
392
+
393
+ The TraiaMCPAdapter seamlessly handles both authenticated and non-authenticated
394
+ MCP connections. It extracts headers from server_params and injects them into
395
+ all HTTP requests when using streamable-http transport.
396
+
397
+ Basic Usage
398
+ -----------
399
+ ```python
400
+ from traia_iatp.mcp import TraiaMCPAdapter
401
+
402
+ # Standard connection (no authentication)
403
+ server_params = {
404
+ "url": "http://localhost:8000/mcp",
405
+ "transport": "streamable-http"
406
+ }
407
+
408
+ # Authenticated connection
409
+ server_params = {
410
+ "url": "http://localhost:8000/mcp",
411
+ "transport": "streamable-http",
412
+ "headers": {
413
+ "Authorization": "Bearer YOUR_API_KEY"
414
+ }
415
+ }
416
+
417
+ # Use the same way regardless of authentication
418
+ with TraiaMCPAdapter(server_params) as tools:
419
+ agent = Agent(
420
+ role="My Agent",
421
+ tools=tools
422
+ )
423
+ ```
424
+
425
+ Using Helper Functions
426
+ ----------------------
427
+ ```python
428
+ from traia_iatp.mcp import create_mcp_adapter_with_auth
429
+
430
+ # Create authenticated adapter
431
+ adapter = create_mcp_adapter_with_auth(
432
+ url="http://localhost:8000/mcp",
433
+ api_key="your-api-key"
434
+ )
435
+
436
+ with adapter as tools:
437
+ # Use authenticated tools
438
+ pass
439
+ ```
440
+
441
+ Multiple Headers
442
+ ----------------
443
+ ```python
444
+ adapter = create_mcp_adapter(
445
+ url="http://localhost:8000/mcp",
446
+ headers={
447
+ "Authorization": "Bearer YOUR_API_KEY",
448
+ "X-API-Version": "v1",
449
+ "X-Client-ID": "my-client"
450
+ }
451
+ )
452
+ ```
453
+
454
+ With Tool Filtering
455
+ -------------------
456
+ ```python
457
+ adapter = create_mcp_adapter_with_auth(
458
+ url="http://localhost:8000/mcp",
459
+ api_key="your-api-key",
460
+ tool_names=["search_news", "get_api_info"]
461
+ )
462
+ ```
463
+
464
+ Server-Side Authentication
465
+ --------------------------
466
+ For MCP servers that require authentication, implement FastMCP middleware:
467
+
468
+ ```python
469
+ from fastmcp import FastMCP, Context
470
+ from fastmcp.server.middleware import Middleware, MiddlewareContext
471
+ from fastmcp.server.dependencies import get_http_request, get_context
472
+ from starlette.requests import Request
473
+
474
+ class AuthMiddleware(Middleware):
475
+ async def on_request(self, context: MiddlewareContext, call_next):
476
+ try:
477
+ # Access the raw HTTP request
478
+ request: Request = get_http_request()
479
+
480
+ # Extract bearer token from Authorization header
481
+ auth = request.headers.get("Authorization", "")
482
+ token = auth[7:].strip() if auth.lower().startswith("bearer ") else None
483
+
484
+ if not token:
485
+ # Check X-API-KEY header as alternative
486
+ token = request.headers.get("X-API-KEY", "")
487
+
488
+ if token:
489
+ # Store the API key in the context state
490
+ if hasattr(context, 'state'):
491
+ context.state.api_key = token
492
+ else:
493
+ # Try to store it in the request state as fallback
494
+ request.state.api_key = token
495
+ except Exception as e:
496
+ logger.debug(f"Could not extract API key from request: {e}")
497
+
498
+ return await call_next(context)
499
+
500
+ mcp = FastMCP("My Server", middleware=[AuthMiddleware()])
501
+
502
+ def get_session_api_key(context: Context) -> Optional[str]:
503
+ '''Get the API key for the current session.'''
504
+ try:
505
+ # Try to get the API key from the context state
506
+ if hasattr(context, 'state') and hasattr(context.state, 'api_key'):
507
+ return context.state.api_key
508
+
509
+ # Fallback: try to get it from the current HTTP request
510
+ try:
511
+ request: Request = get_http_request()
512
+ if hasattr(request.state, 'api_key'):
513
+ return request.state.api_key
514
+ except Exception:
515
+ pass
516
+
517
+ # If we're in a tool context, try to get the context using the dependency
518
+ try:
519
+ ctx = get_context()
520
+ if hasattr(ctx, 'state') and hasattr(ctx.state, 'api_key'):
521
+ return ctx.state.api_key
522
+ except Exception:
523
+ pass
524
+ except Exception as e:
525
+ logger.debug(f"Could not retrieve API key from context: {e}")
526
+
527
+ return None
528
+ ```
529
+
530
+ Note: Headers are only applicable for streamable-http transport.
531
+ For stdio or SSE transports, authentication must be handled differently.
532
+ """
533
+
534
+
535
+ __all__ = [
536
+ 'TraiaMCPAdapter',
537
+ 'HeaderMCPAdapter', # Alias for backward compatibility
538
+ 'create_mcp_adapter',
539
+ 'create_mcp_adapter_with_auth',
540
+ 'create_mcp_adapter_with_x402',
541
+ 'create_mcp_adapter_with_headers', # Alias
542
+ 'USAGE_GUIDE'
543
+ ]