smartapi-mcp 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.
@@ -0,0 +1,34 @@
1
+ """
2
+ SmartAPI MCP Server Package
3
+
4
+ Create MCP servers for one or multiple APIs registered in SmartAPI registry.
5
+ """
6
+
7
+ __version__ = "0.1.0"
8
+ __author__ = "BioThings Team"
9
+ __email__ = "help@biothings.io"
10
+
11
+ # Optional imports for when dependencies are available
12
+ try:
13
+ from .server import get_mcp_server, get_merged_mcp_server, merge_mcp_servers
14
+ from .smartapi import (
15
+ PREDEFINED_API_SETS,
16
+ get_base_server_url,
17
+ get_predefined_api_set,
18
+ get_smartapi_ids,
19
+ load_api_spec,
20
+ )
21
+
22
+ __all__ = [
23
+ "PREDEFINED_API_SETS",
24
+ "get_base_server_url",
25
+ "get_mcp_server",
26
+ "get_merged_mcp_server",
27
+ "get_predefined_api_set",
28
+ "get_smartapi_ids",
29
+ "load_api_spec",
30
+ "merge_mcp_servers",
31
+ ]
32
+ except ImportError:
33
+ # Dependencies not available, only export version info
34
+ __all__ = []
@@ -0,0 +1,10 @@
1
+ """
2
+ Main entry point for smartapi-mcp when run as a module.
3
+
4
+ This allows the package to be run with `python -m smartapi_mcp`.
5
+ """
6
+
7
+ from .cli import main
8
+
9
+ if __name__ == "__main__":
10
+ main()
@@ -0,0 +1,392 @@
1
+ # The following code contains modified functions from awslabs/openapi-mcp-server
2
+ # implementation.
3
+ # The original file name is `server.py`, the following changes were made for better
4
+ # reuse in smartapi-mcp:
5
+ # * Modified `create_mcp_server` function to be async and awaitable, avoid to use
6
+ # asyncio.run
7
+ # * Extracted `get_all_counts` function out of `main` function, so it can be used
8
+ # in other modules
9
+ #
10
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
11
+ #
12
+ # Licensed under the Apache License, Version 2.0 (the "License");
13
+ # you may not use this file except in compliance with the License.
14
+ # You may obtain a copy of the License at
15
+ #
16
+ # http://www.apache.org/licenses/LICENSE-2.0
17
+ #
18
+ # Unless required by applicable law or agreed to in writing, software
19
+ # distributed under the License is distributed on an "AS IS" BASIS,
20
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ # See the License for the specific language governing permissions and
22
+ # limitations under the License.
23
+ """awslabs openapi MCP Server implementation."""
24
+
25
+ import re
26
+ import sys
27
+ from typing import Any
28
+
29
+ import httpx
30
+
31
+ # Import from our modules - use direct imports from sub-modules for better patching in tests
32
+ from awslabs.openapi_mcp_server import logger
33
+ from awslabs.openapi_mcp_server.api.config import Config
34
+ from awslabs.openapi_mcp_server.prompts import MCPPromptManager
35
+ from awslabs.openapi_mcp_server.utils.http_client import (
36
+ HttpClientFactory,
37
+ make_request_with_retry,
38
+ )
39
+ from awslabs.openapi_mcp_server.utils.metrics_provider import metrics
40
+ from awslabs.openapi_mcp_server.utils.openapi import load_openapi_spec
41
+ from awslabs.openapi_mcp_server.utils.openapi_validator import validate_openapi_spec
42
+ from fastmcp import FastMCP
43
+ from fastmcp.server.openapi import FastMCPOpenAPI, RouteMap, RouteType
44
+
45
+
46
+ async def create_mcp_server(config: Config) -> FastMCP:
47
+ """Create and configure the FastMCP server.
48
+
49
+ Args:
50
+ config: Server configuration
51
+
52
+ Returns:
53
+ FastMCP: The configured FastMCP server
54
+
55
+ """
56
+ # Log environment information
57
+ logger.debug("Environment information:")
58
+ logger.debug(f"Python version: {sys.version}")
59
+ try:
60
+ logger.debug(f"HTTPX version: {httpx.__version__}")
61
+ except AttributeError:
62
+ logger.debug("HTTPX version: unknown")
63
+
64
+ logger.info("Creating FastMCP server")
65
+
66
+ # Create the FastMCP server
67
+ server = FastMCP(
68
+ "awslabs.openapi-mcp-server",
69
+ instructions="This server acts as a bridge between OpenAPI specifications and LLMs, allowing models to have a better understanding of available API capabilities without requiring manual tool definitions.",
70
+ dependencies=[
71
+ "pydantic",
72
+ "loguru",
73
+ "httpx",
74
+ ],
75
+ )
76
+
77
+ try:
78
+ # Load OpenAPI spec
79
+ if not config.api_spec_url and not config.api_spec_path:
80
+ logger.error("No API spec URL or path provided")
81
+ raise ValueError("Either api_spec_url or api_spec_path must be provided")
82
+
83
+ logger.debug(
84
+ f"Loading OpenAPI spec from URL: {config.api_spec_url} or path: {config.api_spec_path}"
85
+ )
86
+ openapi_spec = load_openapi_spec(
87
+ url=config.api_spec_url, path=config.api_spec_path
88
+ )
89
+
90
+ # Validate the OpenAPI spec
91
+ if not validate_openapi_spec(openapi_spec):
92
+ logger.warning(
93
+ "OpenAPI specification validation failed, but continuing anyway"
94
+ )
95
+
96
+ # Create a client for the API
97
+ if not config.api_base_url:
98
+ logger.error("No API base URL provided")
99
+ raise ValueError("API base URL must be provided")
100
+
101
+ # Configure authentication using the auth factory
102
+ from awslabs.openapi_mcp_server.auth import (
103
+ get_auth_provider,
104
+ is_auth_type_available,
105
+ )
106
+
107
+ # Import and register the specific auth provider
108
+ from awslabs.openapi_mcp_server.auth.register import register_provider_by_type
109
+
110
+ # Register only the provider we need
111
+ if config.auth_type and config.auth_type != "none":
112
+ logger.debug(
113
+ f"Registering authentication provider for type: {config.auth_type}"
114
+ )
115
+ register_provider_by_type(config.auth_type)
116
+ else:
117
+ logger.debug("No authentication type specified, using none")
118
+
119
+ # Check if the requested auth type is available
120
+ if config.auth_type != "none" and not is_auth_type_available(config.auth_type):
121
+ logger.warning(
122
+ f"Authentication type {config.auth_type} is not available. Falling back to none."
123
+ )
124
+ config.auth_type = "none"
125
+
126
+ # Get the auth provider
127
+ auth_provider = get_auth_provider(config)
128
+
129
+ # Get authentication components
130
+ auth_headers = auth_provider.get_auth_headers()
131
+ # Get auth params (not used directly but may be needed in the future)
132
+ _ = auth_provider.get_auth_params()
133
+ auth_cookies = auth_provider.get_auth_cookies()
134
+ httpx_auth = auth_provider.get_httpx_auth()
135
+
136
+ # Helper function to handle authentication configuration errors
137
+ def handle_auth_error(auth_type, error_message):
138
+ """Handle authentication configuration errors.
139
+
140
+ Args:
141
+ auth_type: The authentication type
142
+ error_message: The error message to log
143
+
144
+ """
145
+ logger.error(
146
+ f"Authentication provider {auth_provider.provider_name} is not properly configured"
147
+ )
148
+ logger.error(error_message)
149
+ logger.error(
150
+ "Server shutting down due to authentication configuration error."
151
+ )
152
+ sys.exit(1)
153
+
154
+ # Check if the provider is properly configured
155
+ if not auth_provider.is_configured() and config.auth_type != "none":
156
+ if config.auth_type == "bearer":
157
+ handle_auth_error(
158
+ "bearer",
159
+ "Bearer authentication requires a valid token. Please provide a token using --auth-token command line argument or AUTH_TOKEN environment variable.",
160
+ )
161
+ elif config.auth_type == "basic":
162
+ handle_auth_error(
163
+ "basic",
164
+ "Basic authentication requires both username and password. Please provide them using --auth-username and --auth-password command line arguments or AUTH_USERNAME and AUTH_PASSWORD environment variables.",
165
+ )
166
+ elif config.auth_type == "api_key":
167
+ handle_auth_error(
168
+ "api_key",
169
+ "API Key authentication requires a valid API key. Please provide it using --auth-api-key command line argument or AUTH_API_KEY environment variable.",
170
+ )
171
+ elif config.auth_type == "cognito":
172
+ handle_auth_error(
173
+ "cognito",
174
+ "Cognito authentication requires client ID, username, and password. Please provide them using --auth-cognito-client-id, --auth-cognito-username, and --auth-cognito-password command line arguments or corresponding environment variables.",
175
+ )
176
+ else:
177
+ logger.warning(
178
+ "Continuing with incomplete authentication configuration. This may cause API requests to fail."
179
+ )
180
+
181
+ # Log authentication info
182
+ if config.auth_type != "none":
183
+ logger.info(f"Using {auth_provider.provider_name} authentication")
184
+
185
+ # Create the HTTP client with authentication and connection pooling
186
+ client = HttpClientFactory.create_client(
187
+ base_url=config.api_base_url,
188
+ headers=auth_headers,
189
+ auth=httpx_auth,
190
+ cookies=auth_cookies,
191
+ )
192
+ logger.info(f"Created HTTP client for API base URL: {config.api_base_url}")
193
+
194
+ custom_mappings = []
195
+
196
+ # Identify GET operations with query parameters in the OpenAPI spec
197
+ for path, path_item in openapi_spec.get("paths", {}).items():
198
+ for method, operation in path_item.items():
199
+ if method.lower() == "get":
200
+ parameters = operation.get("parameters", [])
201
+ query_params = [p for p in parameters if p.get("in") == "query"]
202
+ if query_params:
203
+ # Create a specific mapping for this path to ensure it's treated as a TOOL
204
+ custom_mappings.append(
205
+ RouteMap(
206
+ methods=["GET"],
207
+ pattern=f"^{re.escape(path)}$",
208
+ route_type=RouteType.TOOL,
209
+ )
210
+ )
211
+
212
+ # Create the FastMCP server with custom route mappings
213
+ logger.info("Creating FastMCP server with OpenAPI specification")
214
+ # Update API name from OpenAPI spec title if available
215
+ if openapi_spec and isinstance(openapi_spec, dict) and "info" in openapi_spec:
216
+ if "title" in openapi_spec["info"] and openapi_spec["info"]["title"]:
217
+ config.api_name = openapi_spec["info"]["title"]
218
+ logger.info(
219
+ f"Updated API name from OpenAPI spec title: {config.api_name}"
220
+ )
221
+ server = FastMCPOpenAPI(
222
+ openapi_spec=openapi_spec,
223
+ client=client,
224
+ name=config.api_name or "OpenAPI MCP Server",
225
+ route_maps=custom_mappings, # Custom mappings take precedence over default mappings
226
+ )
227
+
228
+ # Log route information at debug level
229
+ if logger.level == "DEBUG":
230
+ # Use getattr with default value to safely access attributes
231
+ openapi_router = getattr(server, "_openapi_router", None)
232
+ if openapi_router is not None:
233
+ routes = getattr(openapi_router, "_routes", [])
234
+ logger.debug(f"Server has {len(routes)} routes")
235
+
236
+ # Log details of each route
237
+ for i, route in enumerate(routes):
238
+ path = getattr(route, "path", "unknown")
239
+ method = getattr(route, "method", "unknown")
240
+ route_type = getattr(route, "route_type", "unknown")
241
+ logger.debug(f"Route {i}: {method} {path} - Type: {route_type}")
242
+
243
+ logger.info(f"Successfully configured API: {config.api_name}")
244
+
245
+ # Generate MCP-compliant prompts
246
+ try:
247
+ logger.info(f"Generating MCP prompts for API: {config.api_name}")
248
+ # Create prompt manager
249
+ prompt_manager = MCPPromptManager()
250
+
251
+ # Generate prompts
252
+ # asyncio.run(prompt_manager.generate_prompts(server, config.api_name, openapi_spec))
253
+ await prompt_manager.generate_prompts(server, config.api_name, openapi_spec)
254
+
255
+ # Register resource handler
256
+ prompt_manager.register_api_resource_handler(
257
+ server, config.api_name, client
258
+ )
259
+
260
+ except Exception as e:
261
+ logger.warning(f"Failed to generate operation-specific prompts: {e}")
262
+ import traceback
263
+
264
+ logger.warning(f"Traceback: {traceback.format_exc()}")
265
+
266
+ # Register health check tool
267
+ async def health_check() -> dict[str, Any]:
268
+ """Check the health of the server and API.
269
+
270
+ Returns:
271
+ Dict[str, Any]: Health check results
272
+
273
+ """
274
+ api_health = True
275
+ api_message = "API is reachable"
276
+
277
+ # Try to make a simple request to the API
278
+ try:
279
+ # Use the retry-enabled request function
280
+ response = await make_request_with_retry(
281
+ client=client, method="GET", url="/", max_retries=2, retry_delay=0.5
282
+ )
283
+ status_code = response.status_code
284
+ if status_code >= 400:
285
+ api_health = False
286
+ api_message = f"API returned status code {status_code}"
287
+ except Exception as e:
288
+ api_health = False
289
+ api_message = f"Error connecting to API: {e!s}"
290
+
291
+ # Get metrics summary
292
+ summary = metrics.get_summary()
293
+
294
+ return {
295
+ "server": {
296
+ "status": "healthy",
297
+ "version": config.version,
298
+ "uptime": "N/A", # Would require tracking start time
299
+ },
300
+ "api": {
301
+ "name": config.api_name,
302
+ "status": "healthy" if api_health else "unhealthy",
303
+ "message": api_message,
304
+ "base_url": config.api_base_url,
305
+ },
306
+ "metrics": summary,
307
+ }
308
+
309
+ except Exception as e:
310
+ logger.error(f"Error setting up API: {e}")
311
+ logger.error("Server shutting down due to API setup error.")
312
+ import traceback
313
+
314
+ logger.error(f"Traceback: {traceback.format_exc()}")
315
+ sys.exit(1)
316
+
317
+ # Move the logging here, after the server is fully initialized
318
+ # Get the actual tools from the server's internal structure
319
+ tool_count = 0
320
+ tool_names = []
321
+ tools = None
322
+
323
+ # Try different ways to access tools based on FastMCP implementation
324
+ if hasattr(server, "get_tools"):
325
+ try:
326
+ # Use asyncio to run the async method in a synchronous context
327
+ # tools = asyncio.run(server.list_tools()) # type: ignore
328
+ tools = await server.get_tools()
329
+ tool_count = len(tools)
330
+ tool_names = list(tools)
331
+
332
+ # DEBUG - Log detailed information about each tool
333
+ logger.debug(f"Found {tool_count} tools via get_tools()")
334
+ for i, tool in enumerate(tools.values()):
335
+ tool_name = getattr(tool, "name", "unknown")
336
+ tool_desc = getattr(tool, "description", "no description")
337
+ logger.debug(f"Tool {i}: {tool_name} - {tool_desc}")
338
+
339
+ # Check if the tool has a schema
340
+ if hasattr(tool, "parameters"):
341
+ params = getattr(tool, "parameters", {})
342
+ if "properties" in params:
343
+ properties = params.get("properties", {})
344
+ logger.debug(f" Parameters: {list(properties.keys())}")
345
+ except Exception as e:
346
+ logger.warning(f"Failed to list tools: {e}")
347
+ import traceback
348
+
349
+ logger.debug(f"Tool listing error traceback: {traceback.format_exc()}")
350
+
351
+ # Log the prompt count
352
+ prompt_count = (
353
+ len(server._prompt_manager._prompts)
354
+ if hasattr(server, "_prompt_manager")
355
+ and hasattr(server._prompt_manager, "_prompts")
356
+ else 0
357
+ )
358
+
359
+ # Log details of registered components
360
+ if tool_count > 0:
361
+ logger.info(f"Registered tools: {tool_names}")
362
+
363
+ if (
364
+ prompt_count > 0
365
+ and hasattr(server, "_prompt_manager")
366
+ and hasattr(server._prompt_manager, "_prompts")
367
+ ):
368
+ prompt_names = list(server._prompt_manager._prompts.keys())
369
+ logger.info(f"Registered prompts: {prompt_names}")
370
+
371
+ return server
372
+
373
+
374
+ # Get counts of prompts, tools, resources, and resource templates
375
+ async def get_all_counts(server):
376
+ prompts = await server.get_prompts()
377
+ tools = await server.get_tools()
378
+ resources = await server.get_resources()
379
+
380
+ # Get resource templates if available
381
+ resource_templates = []
382
+ if hasattr(server, "get_resource_templates"):
383
+ try:
384
+ resource_templates = await server.get_resource_templates()
385
+ except AttributeError as e:
386
+ # This is expected if the method exists but is not implemented
387
+ logger.debug(f"get_resource_templates exists but not implemented: {e}")
388
+ except Exception as e:
389
+ # Log other unexpected errors
390
+ logger.warning(f"Error retrieving resource templates: {e}")
391
+
392
+ return len(prompts), len(tools), len(resources), len(resource_templates)
smartapi_mcp/cli.py ADDED
@@ -0,0 +1,139 @@
1
+ """
2
+ Command Line Interface for SmartAPI MCP Server
3
+
4
+ Provides CLI commands for running and managing the SmartAPI MCP server.
5
+ """
6
+
7
+ import argparse
8
+ import asyncio
9
+ import sys
10
+ import traceback
11
+
12
+ from awslabs.openapi_mcp_server import logger
13
+ from awslabs.openapi_mcp_server.server import setup_signal_handlers
14
+
15
+ from .awslabs_server import get_all_counts
16
+ from .config import load_config
17
+ from .server import get_merged_mcp_server
18
+
19
+
20
+ def main():
21
+ parser = argparse.ArgumentParser(
22
+ description="Create MCP tools based on multiple registered SmartAPI APIs."
23
+ )
24
+ parser.add_argument(
25
+ "--api_set",
26
+ help=(
27
+ "the set of predefined SmartAPI APIs to include, e.g. 'biothings_core' "
28
+ "or 'biothings'."
29
+ ),
30
+ )
31
+ parser.add_argument(
32
+ "--smartapi_id",
33
+ help="Pass a single SmartAPI (id) to create a MCP server.",
34
+ )
35
+ parser.add_argument(
36
+ "--smartapi_ids",
37
+ help="Pass a list of SmartAPIs (comma-separated ids) to create a MCP server.",
38
+ )
39
+ parser.add_argument(
40
+ "--smartapi_q",
41
+ help="Pass a query string for a list of SmartAPIs to create a MCP server.",
42
+ )
43
+ parser.add_argument(
44
+ "--smartapi_exclude_ids",
45
+ help=(
46
+ "Exclude a list of SmartAPIs (comma-separated ids) to create a MCP server."
47
+ ),
48
+ )
49
+ parser.add_argument(
50
+ "--host",
51
+ help="The host address for the MCP server in HTTP mode. Default is localhost.",
52
+ )
53
+ parser.add_argument(
54
+ "--port",
55
+ type=int,
56
+ default=8000,
57
+ help="The http port for the MCP server in HTTP mode. Default is 8000.",
58
+ )
59
+ parser.add_argument(
60
+ "--transport",
61
+ help="The transport mode for the MCP server, either stdio (default) or http.",
62
+ )
63
+ parser.add_argument(
64
+ "--server_name",
65
+ help='The name of the MCP server, default is "smartapi_mcp".',
66
+ )
67
+ parser.add_argument(
68
+ "--log-level",
69
+ choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
70
+ default="INFO",
71
+ help="Set logging level",
72
+ )
73
+
74
+ args = parser.parse_args()
75
+
76
+ # Set up logging with loguru at specified level
77
+ logger.remove()
78
+ logger.add(lambda msg: print(msg, end=""), level=args.log_level)
79
+ logger.info(f"Starting server with logging level: {args.log_level}")
80
+
81
+ # Load configuration
82
+ logger.debug("Loading configuration from arguments and environment")
83
+ config = load_config(args)
84
+ logger.debug("Configuration loaded.")
85
+
86
+ merged_server = asyncio.run(
87
+ get_merged_mcp_server(
88
+ smartapi_q=config.smartapi_q,
89
+ smartapi_id=config.smartapi_id,
90
+ smartapi_ids=config.smartapi_ids,
91
+ smartapi_exclude_ids=config.smartapi_exclude_ids,
92
+ api_set=config.smartapi_api_set,
93
+ server_name=config.server_name,
94
+ )
95
+ )
96
+
97
+ # Set up signal handlers
98
+ setup_signal_handlers()
99
+
100
+ try:
101
+ prompt_count, tool_count, resource_count, resource_template_count = asyncio.run(
102
+ get_all_counts(merged_server)
103
+ )
104
+
105
+ # Log all counts in a single statement
106
+ logger.info(
107
+ f"Server components: {prompt_count} prompts, {tool_count} tools, "
108
+ f"{resource_count} resources, {resource_template_count} resource templates"
109
+ )
110
+
111
+ # Check if we have at least one tool or resource
112
+ if tool_count == 0 and resource_count == 0:
113
+ logger.warning(
114
+ (
115
+ "No tools or resources were registered. This might "
116
+ "indicate an issue "
117
+ "with the API specification or authentication."
118
+ ),
119
+ )
120
+ except Exception as e:
121
+ logger.error(f"Error counting tools and resources: {e}")
122
+ logger.error("Server shutting down due to error in tool/resource registration.")
123
+ logger.error(f"Traceback: {traceback.format_exc()}")
124
+ sys.exit(1)
125
+
126
+ if config.transport in ["http", "sse"]:
127
+ # Run server with http transport only
128
+ logger.info(f"Running server with {config.transport} transport")
129
+ merged_server.run(
130
+ transport=config.transport, host=config.host, port=config.port
131
+ )
132
+ return
133
+ # Otherwise run server with stdio transport by default
134
+ logger.info("Running server with stdio transport")
135
+ merged_server.run()
136
+
137
+
138
+ if __name__ == "__main__":
139
+ main()
smartapi_mcp/config.py ADDED
@@ -0,0 +1,84 @@
1
+ import os
2
+ from dataclasses import fields
3
+ from typing import Any
4
+
5
+ from awslabs.openapi_mcp_server import logger
6
+ from awslabs.openapi_mcp_server.api import config as _config
7
+
8
+
9
+ class Config(_config.Config):
10
+ """Subclass of Config to add extra configuration options"""
11
+
12
+ smartapi_id: str = ""
13
+ smartapi_ids: list[str] | None = None
14
+ smartapi_exclude_ids: list[str] | None = None
15
+ smartapi_q: str = ""
16
+ smartapi_api_set: str = ""
17
+ server_name: str = "smartapi-mcp"
18
+
19
+
20
+ def load_config(args: Any = None) -> Config:
21
+ config = Config()
22
+ _cfg = _config.load_config(args)
23
+ for field in fields(_cfg):
24
+ setattr(config, field.name, getattr(_cfg, field.name))
25
+
26
+ # define the following SmartAPI-specific environment variables
27
+ env_vars = {
28
+ "SMARTAPI_ID": (lambda v: setattr(config, "smartapi_id", v)),
29
+ "SMARTAPI_IDS": (lambda v: setattr(config, "smartapi_ids", v)),
30
+ "SMARTAPI_EXCLUDE_IDS": (lambda v: setattr(config, "smartapi_exclude_ids", v)),
31
+ "SMARTAPI_Q": (lambda v: setattr(config, "smartapi_q", v)),
32
+ "SMARTAPI_API_SET": (lambda v: setattr(config, "smartapi_api_set", v)),
33
+ "SERVER_NAME": (lambda v: setattr(config, "server_name", v)),
34
+ }
35
+
36
+ # Load environment variables
37
+ env_loaded = {}
38
+ for key, setter in env_vars.items():
39
+ if key in os.environ:
40
+ env_value = os.environ[key]
41
+ setter(env_value)
42
+ env_loaded[key] = env_value
43
+
44
+ if env_loaded:
45
+ logger.debug(
46
+ f"Loaded {len(env_loaded)} SmartAPI-specific environment variables: "
47
+ f"{', '.join(env_loaded.keys())}"
48
+ )
49
+
50
+ # Load from arguments
51
+ if args:
52
+ if hasattr(args, "smartapi_id") and args.smartapi_id:
53
+ logger.debug(f"Setting SmartAPI id from arguments: {args.smartapi_id}")
54
+ config.smartapi_id = args.smartapi_id
55
+ if hasattr(args, "smartapi_ids") and args.smartapi_id:
56
+ logger.debug(f"Setting SmartAPI ids from arguments: {args.smartapi_ids}")
57
+ config.smartapi_ids = args.smartapi_ids
58
+ if hasattr(args, "smartapi_exclude_ids") and args.smartapi_id:
59
+ logger.debug(
60
+ "Setting excluded SmartAPI ids from arguments: {}",
61
+ args.smartapi_exclude_ids,
62
+ )
63
+ config.smartapi_exclude_ids = args.smartapi_exclude_ids
64
+ if hasattr(args, "smartapi_q") and args.smartapi_q:
65
+ logger.debug(f"Setting SmartAPI query from arguments: {args.smartapi_q}")
66
+ config.smartapi_q = args.smartapi_q
67
+ if hasattr(args, "api_set") and args.api_set:
68
+ logger.debug(
69
+ "Setting predefined SmartAPI API set from arguments: {}",
70
+ args.api_set,
71
+ )
72
+ config.smartapi_api_set = args.api_set
73
+ if hasattr(args, "server_name") and args.server_name:
74
+ logger.debug(f"Setting MCP Server name from arguments: {args.server_name}")
75
+ config.server_name = args.server_name
76
+ if hasattr(args, "transport") and args.transport:
77
+ logger.debug(
78
+ f"Setting MCP Server transport mode from arguments: {args.transport}"
79
+ )
80
+ config.transport = args.transport
81
+ # Log final configuration details
82
+ logger.info("SmartAPI Configuration loaded")
83
+
84
+ return config
smartapi_mcp/py.typed ADDED
@@ -0,0 +1 @@
1
+ # Marker file for PEP 561 - indicates this package supports type hints