awslabs.timestream-for-influxdb-mcp-server 0.0.1__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,1239 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ """awslabs Timestream for InfluxDB MCP Server implementation."""
17
+
18
+ import boto3
19
+ import os
20
+ from influxdb_client.client.influxdb_client import InfluxDBClient
21
+ from influxdb_client.client.write.point import Point
22
+ from influxdb_client.client.write_api import ASYNCHRONOUS, SYNCHRONOUS
23
+ from influxdb_client.domain.write_precision import WritePrecision
24
+ from loguru import logger
25
+ from mcp.server.fastmcp import FastMCP
26
+ from pydantic import Field
27
+ from typing import Any, Dict, List, Optional
28
+ from urllib.parse import urlparse
29
+
30
+
31
+ # Define Field parameters as global variables to avoid duplication
32
+ # Common fields
33
+ REQUIRED_FIELD_DB_CLUSTER_ID = Field(
34
+ ..., description='Service-generated unique identifier of the DB cluster.'
35
+ )
36
+
37
+ REQUIRED_FIELD_DB_INSTANCE_NAME = Field(
38
+ ...,
39
+ description='The name that uniquely identifies the DB instance. '
40
+ 'This name will also be a prefix included in the endpoint. '
41
+ 'DB instance names must be unique per customer and per region.',
42
+ )
43
+ REQUIRED_FIELD_DB_INSTANCE_TYPE = Field(
44
+ ..., description='The Timestream for InfluxDB DB instance type to run InfluxDB on.'
45
+ )
46
+
47
+ OPTIONAL_FIELD_DB_INSTANCE_TYPE_CLUSTER_UPDATE = Field(
48
+ None, description='Update the DB cluster to use the specified DB instance Type.'
49
+ )
50
+
51
+ REQUIRED_FIELD_PASSWORD = Field(
52
+ ...,
53
+ description='The password of the initial admin user created in InfluxDB. '
54
+ 'This password will allow you to access the InfluxDB UI to perform various administrative task '
55
+ 'and also use the InfluxDB CLI to create an operator token.',
56
+ )
57
+ REQUIRED_FIELD_ALLOCATED_STORAGE_GB = Field(
58
+ ...,
59
+ description='The amount of storage to allocate for your DB storage type in GiB (gibibytes).',
60
+ )
61
+ OPTIONAL_FIELD_ALLOCATED_STORAGE_GB_OPTIONAL = Field(
62
+ None, description='The amount of storage to allocate for your DB storage type (in gibibytes).'
63
+ )
64
+ REQUIRED_FIELD_VPC_SECURITY_GROUP_IDS = Field(
65
+ ..., description='A list of VPC security group IDs to associate with the DB.'
66
+ )
67
+
68
+ REQUIRED_FIELD_VPC_SUBNET_IDS = Field(
69
+ ...,
70
+ description='A list of VPC subnet IDs to associate with the DB. '
71
+ 'Provide at least two VPC subnet IDs in different Availability Zones when deploying with a Multi-AZ standby.',
72
+ )
73
+
74
+ OPTIONAL_FIELD_PUBLICLY_ACCESSIBLE = Field(
75
+ True,
76
+ description='Configures the DB with a public IP to facilitate access from outside the VPC.',
77
+ )
78
+
79
+ OPTIONAL_FIELD_TOOL_WRITE_MODE = Field(
80
+ False,
81
+ description='Tool is run in write mode and will be able to perform any create/update/delete operations. '
82
+ 'Default is read-only mode (False)',
83
+ )
84
+
85
+ OPTIONAL_FIELD_USERNAME = Field(
86
+ None, description='The username of the initial admin user created in InfluxDB.'
87
+ )
88
+ OPTIONAL_FIELD_ORGANIZATION = Field(
89
+ None,
90
+ description='The name of the initial organization for the initial admin user in InfluxDB.'
91
+ 'An InfluxDB organization is a workspace for a group of users',
92
+ )
93
+ REQUIRED_FIELD_BUCKET = Field(..., description='The name of the initial InfluxDB bucket.')
94
+ OPTIONAL_FIELD_BUCKET = Field(None, description='The name of the initial InfluxDB bucket.')
95
+ OPTIONAL_FIELD_DB_STORAGE_TYPE = Field(
96
+ None,
97
+ description='The Timestream for InfluxDB DB storage type to read and write InfluxDB data.',
98
+ )
99
+ OPTIONAL_FIELD_DEPLOYMENT_TYPE_INSTANCE = Field(
100
+ None,
101
+ description='Specifies whether the DB instance will be deployed as a standalone instance or with a Multi-AZ standby for high availability.',
102
+ )
103
+ OPTIONAL_FIELD_NETWORK_TYPE = Field(
104
+ None,
105
+ description='Specifies whether the network type of the Timestream for InfluxDB cluster is IPv4 or DUAL.',
106
+ )
107
+
108
+ OPTIONAL_FIELD_PORT = Field(
109
+ None, description='The port number on which InfluxDB accepts connections. Default: 8086'
110
+ )
111
+ OPTIONAL_FIELD_PORT_UPDATE = Field(
112
+ None, description='Update the DB cluster to use the specified port.'
113
+ )
114
+
115
+ OPTIONAL_FIELD_FAILOVER_MODE = Field(
116
+ None,
117
+ description='Specifies the behavior of failure recovery when the primary node of the cluster fails.',
118
+ )
119
+ OPTIONAL_FIELD_FAILOVER_MODE_UPDATE = Field(
120
+ None, description="Update the DB cluster's failover behavior."
121
+ )
122
+
123
+ OPTIONAL_FIELD_TAGS = Field(None, description='A list of tags to assign to the DB.')
124
+ OPTIONAL_FIELD_TAGS_PARAM_GROUP = Field(
125
+ None, description='A list of key-value pairs to associate with the DB parameter group.'
126
+ )
127
+ OPTIONAL_FIELD_LOG_DELIVERY_CONFIGURATION = Field(
128
+ None, description='Configuration for sending InfluxDB engine logs to a specified S3 bucket.'
129
+ )
130
+ OPTIONAL_FIELD_LOG_DELIVERY_CONFIGURATION_UPDATE = Field(
131
+ None, description='The log delivery configuration to apply to the DB cluster.'
132
+ )
133
+
134
+ # Pagination fields
135
+ OPTIONAL_FIELD_NEXT_TOKEN = Field(
136
+ None,
137
+ description='The pagination token. To resume pagination, provide the next-token value as an argument of a subsequent API invocation.',
138
+ )
139
+
140
+ OPTIONAL_FIELD_MAX_RESULTS = Field(
141
+ None,
142
+ description='The maximum number of items to return in the output. If the total number of items available is more than the value specified, a nextToken is provided in the output.',
143
+ )
144
+
145
+ # Resource fields
146
+ REQUIRED_FIELD_RESOURCE_ARN = Field(
147
+ ..., description='The Amazon Resource Name (ARN) of the tagged resource.'
148
+ )
149
+ REQUIRED_FIELD_TAG_KEYS = Field(..., description='The keys used to identify the tags to remove.')
150
+ REQUIRED_FIELD_TAGS_RESOURCE = Field(..., description='A list of key-value pairs as tags.')
151
+
152
+ # DB Parameter Group fiels
153
+ REQUIRED_FIELD_PARAMETER_GROUP_ID = Field(..., description='The id of the DB parameter group.')
154
+ REQUIRED_FIELD_PARAM_GROUP_NAME = Field(
155
+ ...,
156
+ description='The name of the DB parameter group. The name must be unique per customer and per region.',
157
+ )
158
+ OPTIONAL_FIELD_PARAM_GROUP_DESCRIPTION = Field(
159
+ None, description='A description of the DB parameter group.'
160
+ )
161
+ OPTIONAL_FIELD_PARAMETERS = Field(
162
+ None, description='A list of the parameters that comprise the DB parameter group.'
163
+ )
164
+ OPTIONAL_FIELD_DB_PARAMETER_GROUP_ID = Field(
165
+ None, description='The id of the DB parameter group to assign to your DB.'
166
+ )
167
+ OPTIONAL_FIELD_DB_PARAMETER_GROUP_IDENTIFIER_UPDATE = Field(
168
+ None, description='Update the DB cluster to use the specified DB parameter group.'
169
+ )
170
+
171
+ # DB Instance fields
172
+ REQUIRED_FIELD_DB_INSTANCE_IDENTIFIER = Field(..., description='The id of the DB instance.')
173
+
174
+ # Status fields
175
+ REQUIRED_FIELD_STATUS = Field(
176
+ ..., description='The status to filter DB instances by (case-insensitive).'
177
+ )
178
+ REQUIRED_FIELD_STATUS_CLUSTER = Field(
179
+ ..., description='The status to filter DB clusters by (case-insensitive).'
180
+ )
181
+
182
+ # InfluxDB fields
183
+ REQUIRED_FIELD_URL = Field(..., description='The URL of the InfluxDB server.')
184
+ REQUIRED_FIELD_TOKEN = Field(..., description='The authentication token.')
185
+ REQUIRED_FIELD_BUCKET_INFLUX = Field(..., description='The destination bucket for writes.')
186
+ REQUIRED_FIELD_ORG = Field(..., description='The organization name.')
187
+ REQUIRED_FIELD_POINTS = Field(
188
+ ...,
189
+ description='List of data points to write. Each point should be a dictionary with measurement, tags, fields, and optional time.',
190
+ )
191
+ REQUIRED_FIELD_DATA_LINE_PROTOCOL = Field(
192
+ ..., description='Data in InfluxDB Line Protocol format.'
193
+ )
194
+ OPTIONAL_FIELD_WRITE_PRECISION = Field(
195
+ default='ns',
196
+ description='The precision for the unix timestamps within the body line-protocol. One of: ns, us, ms, s (default is ns).',
197
+ )
198
+ OPTIONAL_FIELD_SYNC_MODE = Field(
199
+ default='synchronous',
200
+ description="The synchronization mode, either 'synchronous' or 'asynchronous'.",
201
+ )
202
+ OPTIONAL_FIELD_VERIFY_SSL = Field(
203
+ True, description='Whether to verify SSL with https connections.'
204
+ )
205
+ REQUIRED_FIELD_QUERY = Field(..., description='The Flux query string.')
206
+
207
+ # Cluster name field
208
+ REQUIRED_FIELD_CLUSTER_NAME = Field(
209
+ ...,
210
+ description='The name that uniquely identifies the DB cluster when interacting with '
211
+ 'the Amazon Timestream for InfluxDB API and CLI commands. '
212
+ 'This name will also be a prefix included in the endpoint.',
213
+ )
214
+
215
+ mcp = FastMCP(
216
+ 'awslabs.timestream-for-influxdb-mcp-server',
217
+ instructions="""
218
+ This MCP server provides tools to interact with AWS Timestream for InfluxDB APIs.
219
+ It allows you to create and manage databases, users, and perform other operations
220
+ related to Timestream for InfluxDB service.
221
+ """,
222
+ dependencies=['loguru', 'boto3', 'influxdb-client'],
223
+ )
224
+
225
+
226
+ def get_timestream_influxdb_client():
227
+ """Get the AWS Timestream for InfluxDB client."""
228
+ aws_region: str = os.environ.get('AWS_REGION', 'us-east-1')
229
+ aws_profile = os.environ.get('AWS_PROFILE')
230
+ try:
231
+ if aws_profile:
232
+ logger.info(f'Using AWS profile for AWS Timestream Influx Client: {aws_profile}')
233
+ client = boto3.Session(profile_name=aws_profile, region_name=aws_region).client(
234
+ 'timestream-influxdb'
235
+ )
236
+ else:
237
+ client = boto3.Session(region_name=aws_region).client('timestream-influxdb')
238
+ except Exception as e:
239
+ logger.error(f'Error creating AWS Timestream for InfluxDB client: {str(e)}')
240
+ raise
241
+
242
+ return client
243
+
244
+
245
+ def get_influxdb_client(url, token, org=None, timeout=10000, verify_ssl: bool = True):
246
+ """Get an InfluxDB client.
247
+
248
+ Args:
249
+ url: The URL of the InfluxDB server e.g. https://<host-name>:8086.
250
+ token: The authentication token.
251
+ org: The organization name.
252
+ timeout: The timeout in milliseconds.
253
+ verify_ssl: whether to verify SSL with https connections
254
+
255
+ Returns:
256
+ An InfluxDB client.
257
+
258
+ Raises:
259
+ ValueError: If the URL does not use HTTPS protocol or is not properly formatted.
260
+ """
261
+ try:
262
+ parsed_url = urlparse(url)
263
+ url_scheme = parsed_url.scheme
264
+ if url_scheme != 'https' and url_scheme != 'http':
265
+ raise ValueError('URL must use HTTP(S) protocol')
266
+ except Exception as e:
267
+ logger.error(f'Error parsing URL: {str(e)}')
268
+ raise
269
+
270
+ if not token:
271
+ raise ValueError('Token must be provided')
272
+
273
+ # Ensure org is not None when passed to InfluxDBClient
274
+ org_param = org if org is not None else ''
275
+
276
+ return InfluxDBClient(
277
+ url=url, token=token, org=org_param, timeout=timeout, verify_ssl=verify_ssl
278
+ )
279
+
280
+
281
+ @mcp.tool(
282
+ name='CreateDbCluster', description='Create a new Timestream for InfluxDB database cluster.'
283
+ )
284
+ async def create_db_cluster(
285
+ name: str = REQUIRED_FIELD_CLUSTER_NAME,
286
+ db_instance_type: str = REQUIRED_FIELD_DB_INSTANCE_TYPE,
287
+ password: str = REQUIRED_FIELD_PASSWORD,
288
+ allocated_storage_gb: int = REQUIRED_FIELD_ALLOCATED_STORAGE_GB,
289
+ vpc_security_group_ids: List[str] = REQUIRED_FIELD_VPC_SECURITY_GROUP_IDS,
290
+ vpc_subnet_ids: List[str] = REQUIRED_FIELD_VPC_SUBNET_IDS,
291
+ publicly_accessible: bool = OPTIONAL_FIELD_PUBLICLY_ACCESSIBLE,
292
+ username: Optional[str] = OPTIONAL_FIELD_USERNAME,
293
+ organization: Optional[str] = OPTIONAL_FIELD_ORGANIZATION,
294
+ bucket: Optional[str] = OPTIONAL_FIELD_BUCKET,
295
+ db_storage_type: Optional[str] = OPTIONAL_FIELD_DB_STORAGE_TYPE,
296
+ deployment_type: Optional[str] = OPTIONAL_FIELD_DEPLOYMENT_TYPE_INSTANCE,
297
+ networkType: Optional[str] = OPTIONAL_FIELD_NETWORK_TYPE,
298
+ port: Optional[int] = OPTIONAL_FIELD_PORT,
299
+ db_parameter_group_identifier: Optional[str] = OPTIONAL_FIELD_DB_PARAMETER_GROUP_ID,
300
+ failover_mode: Optional[str] = OPTIONAL_FIELD_FAILOVER_MODE,
301
+ tags: Optional[Dict[str, str]] = OPTIONAL_FIELD_TAGS,
302
+ log_delivery_configuration: Optional[
303
+ Dict[str, Any]
304
+ ] = OPTIONAL_FIELD_LOG_DELIVERY_CONFIGURATION,
305
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
306
+ ) -> Dict[str, Any]:
307
+ """Create a new Timestream for InfluxDB database cluster.
308
+
309
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_CreateDbCluster.html
310
+
311
+ Returns:
312
+ Details of the created DB cluster.
313
+ """
314
+ if not tool_write_mode:
315
+ raise Exception(
316
+ 'CreateDbCluster tool invocation not allowed when tool-write-mode is set to False'
317
+ )
318
+
319
+ ts_influx_client = get_timestream_influxdb_client()
320
+
321
+ # Required parameters
322
+ params = {
323
+ 'name': name,
324
+ 'dbInstanceType': db_instance_type,
325
+ 'password': password,
326
+ 'vpcSecurityGroupIds': vpc_security_group_ids,
327
+ 'vpcSubnetIds': vpc_subnet_ids,
328
+ 'allocatedStorage': allocated_storage_gb,
329
+ 'publiclyAccessible': publicly_accessible,
330
+ }
331
+
332
+ # Add optional parameters if provided
333
+ if db_parameter_group_identifier:
334
+ params['dbParameterGroupIdentifier'] = db_parameter_group_identifier
335
+ if username:
336
+ params['username'] = username
337
+ if organization:
338
+ params['organization'] = organization
339
+ if bucket:
340
+ params['bucket'] = bucket
341
+ if port:
342
+ params['port'] = port
343
+ if db_storage_type:
344
+ params['dbStorageType'] = db_storage_type
345
+ if deployment_type:
346
+ params['deploymentType'] = deployment_type
347
+ if networkType:
348
+ params['networkType'] = networkType
349
+ if failover_mode:
350
+ params['failoverMode'] = failover_mode
351
+ if log_delivery_configuration:
352
+ params['logDeliveryConfiguration'] = str(log_delivery_configuration)
353
+
354
+ if tags:
355
+ tag_list = [{'Key': k, 'Value': v} for k, v in tags.items()]
356
+ params['tags'] = str(tag_list)
357
+
358
+ try:
359
+ response = ts_influx_client.create_db_cluster(**params)
360
+ return response
361
+ except Exception as e:
362
+ logger.error(f'Error creating DB cluster: {str(e)}')
363
+ raise e
364
+
365
+
366
+ @mcp.tool(
367
+ name='CreateDbInstance', description='Create a new Timestream for InfluxDB database instance'
368
+ )
369
+ async def create_db_instance(
370
+ db_instance_name: str = REQUIRED_FIELD_DB_INSTANCE_NAME,
371
+ db_instance_type: str = REQUIRED_FIELD_DB_INSTANCE_TYPE,
372
+ password: str = REQUIRED_FIELD_PASSWORD,
373
+ allocated_storage_gb: int = REQUIRED_FIELD_ALLOCATED_STORAGE_GB,
374
+ vpc_security_group_ids: List[str] = REQUIRED_FIELD_VPC_SECURITY_GROUP_IDS,
375
+ vpc_subnet_ids: List[str] = REQUIRED_FIELD_VPC_SUBNET_IDS,
376
+ publicly_accessible: bool = OPTIONAL_FIELD_PUBLICLY_ACCESSIBLE,
377
+ username: Optional[str] = OPTIONAL_FIELD_USERNAME,
378
+ organization: Optional[str] = OPTIONAL_FIELD_ORGANIZATION,
379
+ bucket: Optional[str] = OPTIONAL_FIELD_BUCKET,
380
+ db_storage_type: Optional[str] = OPTIONAL_FIELD_DB_STORAGE_TYPE,
381
+ deployment_type: Optional[str] = OPTIONAL_FIELD_DEPLOYMENT_TYPE_INSTANCE,
382
+ networkType: Optional[str] = OPTIONAL_FIELD_NETWORK_TYPE,
383
+ port: Optional[int] = OPTIONAL_FIELD_PORT,
384
+ db_parameter_group_id: Optional[str] = OPTIONAL_FIELD_DB_PARAMETER_GROUP_ID,
385
+ tags: Optional[Dict[str, str]] = OPTIONAL_FIELD_TAGS,
386
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
387
+ ) -> Dict[str, Any]:
388
+ """Create a new Timestream for InfluxDB database instance.
389
+
390
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_CreateDbInstance.html#tsinfluxdb-CreateDbInstance-request-dbStorageType
391
+
392
+ Returns:
393
+ Details of the created DB instance.
394
+ """
395
+ if not tool_write_mode:
396
+ raise Exception(
397
+ 'CreateDbInstance tool invocation not allowed when tool-write-mode is set to False'
398
+ )
399
+
400
+ ts_influx_client = get_timestream_influxdb_client()
401
+
402
+ # Required parameters
403
+ params = {
404
+ 'name': db_instance_name,
405
+ 'dbInstanceType': db_instance_type,
406
+ 'password': password,
407
+ 'vpcSecurityGroupIds': vpc_security_group_ids,
408
+ 'vpcSubnetIds': vpc_subnet_ids,
409
+ 'allocatedStorage': allocated_storage_gb,
410
+ 'publiclyAccessible': publicly_accessible,
411
+ }
412
+
413
+ # Add optional parameters if provided
414
+ if db_parameter_group_id:
415
+ params['dbParameterGroupIdentifier'] = db_parameter_group_id
416
+ if username:
417
+ params['username'] = username
418
+ if organization:
419
+ params['organization'] = organization
420
+ if bucket:
421
+ params['bucket'] = bucket
422
+ if port:
423
+ params['port'] = str(port)
424
+ if username:
425
+ params['username'] = username
426
+ if db_storage_type:
427
+ params['db_storage_type'] = db_storage_type
428
+ if deployment_type:
429
+ params['deployment_type'] = deployment_type
430
+ if networkType:
431
+ params['networkType'] = networkType
432
+
433
+ if tags:
434
+ tag_list = [{'Key': k, 'Value': v} for k, v in tags.items()]
435
+ params['tags'] = str(tag_list)
436
+
437
+ try:
438
+ response = ts_influx_client.create_db_instance(**params)
439
+ return response
440
+ except Exception as e:
441
+ logger.error(f'Error creating DB instance: {str(e)}')
442
+ raise e
443
+
444
+
445
+ @mcp.tool(
446
+ name='LsInstancesOfCluster',
447
+ description='List all Timestream for InfluxDB instances belonging to a specific DB cluster.',
448
+ )
449
+ async def list_db_instances_for_cluster(
450
+ db_cluster_id: str = REQUIRED_FIELD_DB_CLUSTER_ID,
451
+ next_token: Optional[str] = OPTIONAL_FIELD_NEXT_TOKEN,
452
+ max_results: Optional[int] = OPTIONAL_FIELD_MAX_RESULTS,
453
+ ) -> Dict[str, Any]:
454
+ """Returns a list of Timestream for InfluxDB DB instances belonging to a specific cluster.
455
+
456
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_ListDbInstancesForCluster.html
457
+
458
+ Returns:
459
+ A list of Timestream for InfluxDB instance summaries belonging to the cluster.
460
+ """
461
+ ts_influx_client = get_timestream_influxdb_client()
462
+
463
+ params = {'dbClusterId': db_cluster_id}
464
+
465
+ if next_token:
466
+ params['nextToken'] = next_token
467
+ if max_results:
468
+ params['maxResults'] = str(max_results)
469
+
470
+ try:
471
+ response = ts_influx_client.list_db_instances_for_cluster(**params)
472
+ return response
473
+ except Exception as e:
474
+ logger.error(f'Error listing DB instances for cluster: {str(e)}')
475
+ raise e
476
+
477
+
478
+ @mcp.tool(name='ListDbInstances', description='List all Timestream for InfluxDB DB instances')
479
+ async def list_db_instances(
480
+ next_token: Optional[str] = OPTIONAL_FIELD_NEXT_TOKEN,
481
+ max_results: Optional[int] = OPTIONAL_FIELD_MAX_RESULTS,
482
+ ) -> Dict[str, Any]:
483
+ """Returns a list of Timestream for InfluxDB DB instances.
484
+
485
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_ListDbInstances.html
486
+
487
+ Returns:
488
+ A list of Timestream for InfluxDB DB instance summaries.
489
+ """
490
+ ts_influx_client = get_timestream_influxdb_client()
491
+
492
+ params = {}
493
+ if next_token:
494
+ params['nextToken'] = next_token
495
+ if max_results:
496
+ params['maxResults'] = str(max_results)
497
+
498
+ try:
499
+ response = ts_influx_client.list_db_instances(**params)
500
+ return response
501
+ except Exception as e:
502
+ logger.error(f'Error listing DB instances: {str(e)}')
503
+ raise e
504
+
505
+
506
+ @mcp.tool(name='ListDbClusters', description='List all Timestream for InfluxDB DB clusters.')
507
+ async def list_db_clusters(
508
+ next_token: Optional[str] = OPTIONAL_FIELD_NEXT_TOKEN,
509
+ max_results: Optional[int] = OPTIONAL_FIELD_MAX_RESULTS,
510
+ ) -> Dict[str, Any]:
511
+ """Returns a list of Timestream for InfluxDB DB clusters.
512
+
513
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_ListDbClusters.html
514
+
515
+ Returns:
516
+ A list of Timestream for InfluxDB cluster summaries.
517
+ """
518
+ ts_influx_client = get_timestream_influxdb_client()
519
+
520
+ params = {}
521
+ if next_token:
522
+ params['nextToken'] = next_token
523
+ if max_results:
524
+ params['maxResults'] = str(max_results)
525
+
526
+ try:
527
+ response = ts_influx_client.list_db_clusters(**params)
528
+ return response
529
+ except Exception as e:
530
+ logger.error(f'Error listing DB clusters: {str(e)}')
531
+ raise e
532
+
533
+
534
+ @mcp.tool(
535
+ name='GetDbParameterGroup',
536
+ description='Get a Timestream for InfluxDB DB parameter group details for a db_parameter_group_id',
537
+ )
538
+ async def get_db_parameter_group(
539
+ identifier: str = REQUIRED_FIELD_PARAMETER_GROUP_ID,
540
+ ) -> Dict[str, Any]:
541
+ """Returns a Timestream for InfluxDB DB parameter group.
542
+
543
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_GetDbParameterGroup.html
544
+
545
+ Returns:
546
+ Details of the DB parameter group.
547
+ """
548
+ ts_influx_client = get_timestream_influxdb_client()
549
+
550
+ try:
551
+ response = ts_influx_client.get_db_parameter_group(identifier=identifier)
552
+ return response
553
+ except Exception as e:
554
+ logger.error(f'Error getting DB parameter group: {str(e)}')
555
+ raise e
556
+
557
+
558
+ @mcp.tool(
559
+ name='GetDbInstance',
560
+ description='Returns a Timestream for InfluxDB DB instance details by the instance-identifier',
561
+ )
562
+ async def get_db_instance(
563
+ identifier: str = REQUIRED_FIELD_DB_INSTANCE_IDENTIFIER,
564
+ ) -> Dict[str, Any]:
565
+ """Returns a Timestream for InfluxDB DB instance.
566
+
567
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_GetDbInstance.html
568
+
569
+ Returns:
570
+ Details of the DB instance.
571
+ """
572
+ ts_influx_client = get_timestream_influxdb_client()
573
+
574
+ try:
575
+ response = ts_influx_client.get_db_instance(identifier=identifier)
576
+ return response
577
+ except Exception as e:
578
+ logger.error(f'Error getting DB instance: {str(e)}')
579
+ raise e
580
+
581
+
582
+ @mcp.tool(
583
+ name='GetDbCluster',
584
+ description='Returns a Timestream for InfluxDB DB cluster details by the db_cluster_id',
585
+ )
586
+ async def get_db_cluster(
587
+ db_cluster_id: str = REQUIRED_FIELD_DB_CLUSTER_ID,
588
+ ) -> Dict[str, Any]:
589
+ """Retrieves information about a Timestream for InfluxDB cluster.
590
+
591
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_GetDbCluster.html
592
+
593
+ Returns:
594
+ Details of the DB cluster.
595
+ """
596
+ ts_influx_client = get_timestream_influxdb_client()
597
+
598
+ try:
599
+ response = ts_influx_client.get_db_cluster(dbClusterId=db_cluster_id)
600
+ return response
601
+ except Exception as e:
602
+ logger.error(f'Error getting DB cluster: {str(e)}')
603
+ raise e
604
+
605
+
606
+ @mcp.tool(
607
+ name='DeleteDbInstance',
608
+ description='Deletes a Timestream for InfluxDB DB instance by the instance-identifier',
609
+ )
610
+ async def delete_db_instance(
611
+ identifier: str = REQUIRED_FIELD_DB_INSTANCE_IDENTIFIER,
612
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
613
+ ) -> Dict[str, Any]:
614
+ """Deletes a Timestream for InfluxDB DB instance.
615
+
616
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_DeleteDbInstance.html
617
+
618
+ Returns:
619
+ Details of the deleted DB instance.
620
+ """
621
+ if not tool_write_mode:
622
+ raise Exception(
623
+ 'DeleteDbInstance tool invocation not allowed when tool-write-mode is set to False'
624
+ )
625
+
626
+ ts_influx_client = get_timestream_influxdb_client()
627
+
628
+ try:
629
+ response = ts_influx_client.delete_db_instance(identifier=identifier)
630
+ return response
631
+ except Exception as e:
632
+ logger.error(f'Error deleting DB instance: {str(e)}')
633
+ raise e
634
+
635
+
636
+ @mcp.tool(
637
+ name='DeleteDbCluster',
638
+ description='Deletes a Timestream for InfluxDB cluster by the db_cluster_id',
639
+ )
640
+ async def delete_db_cluster(
641
+ db_cluster_id: str = REQUIRED_FIELD_DB_CLUSTER_ID,
642
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
643
+ ) -> Dict[str, Any]:
644
+ """Deletes a Timestream for InfluxDB cluster.
645
+
646
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_DeleteDbCluster.html
647
+
648
+ Returns:
649
+ Details of the deleted DB cluster.
650
+ """
651
+ if not tool_write_mode:
652
+ raise Exception(
653
+ 'DeleteDbCluster tool invocation not allowed when tool-write-mode is set to False'
654
+ )
655
+
656
+ ts_influx_client = get_timestream_influxdb_client()
657
+
658
+ try:
659
+ response = ts_influx_client.delete_db_cluster(dbClusterId=db_cluster_id)
660
+ return response
661
+ except Exception as e:
662
+ logger.error(f'Error deleting DB cluster: {str(e)}')
663
+ raise e
664
+
665
+
666
+ @mcp.tool(
667
+ name='ListDbParamGroups', description='List all Timestream for InfluxDB DB parameter groups.'
668
+ )
669
+ async def list_db_parameter_groups(
670
+ next_token: Optional[str] = OPTIONAL_FIELD_NEXT_TOKEN,
671
+ max_results: Optional[int] = OPTIONAL_FIELD_MAX_RESULTS,
672
+ ) -> Dict[str, Any]:
673
+ """Returns a list of Timestream for InfluxDB DB parameter groups.
674
+
675
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_ListDbParameterGroups.html
676
+
677
+ Returns:
678
+ A list of Timestream for InfluxDB DB parameter group summaries.
679
+ """
680
+ ts_influx_client = get_timestream_influxdb_client()
681
+
682
+ params = {}
683
+ if next_token:
684
+ params['nextToken'] = next_token
685
+ if max_results:
686
+ params['maxResults'] = str(max_results)
687
+
688
+ try:
689
+ response = ts_influx_client.list_db_parameter_groups(**params)
690
+ return response
691
+ except Exception as e:
692
+ logger.error(f'Error listing DB parameter groups: {str(e)}')
693
+ raise e
694
+
695
+
696
+ @mcp.tool(name='ListTagsForResource', description='A list of tags applied to the resource.')
697
+ async def list_tags_for_resource(
698
+ resource_arn: str = REQUIRED_FIELD_RESOURCE_ARN,
699
+ ) -> Dict[str, Any]:
700
+ """A list of tags applied to the resource.
701
+
702
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_ListTagsForResource.html
703
+
704
+ Returns:
705
+ A list of tags used to categorize and track resources.
706
+ """
707
+ ts_influx_client = get_timestream_influxdb_client()
708
+
709
+ try:
710
+ response = ts_influx_client.list_tags_for_resource(resourceArn=resource_arn)
711
+ return response
712
+ except Exception as e:
713
+ logger.error(f'Error listing tags for resource: {str(e)}')
714
+ raise e
715
+
716
+
717
+ @mcp.tool(
718
+ name='TagResource',
719
+ description='Tags are composed of a Key/Value pairs. Apply them to Timestream for InfluxDB resource.',
720
+ )
721
+ async def tag_resource(
722
+ resource_arn: str = REQUIRED_FIELD_RESOURCE_ARN,
723
+ tags: Dict[str, str] = REQUIRED_FIELD_TAGS_RESOURCE,
724
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
725
+ ) -> Dict[str, Any]:
726
+ """Tags are composed of a Key/Value pairs. You can use tags to categorize and track your Timestream for InfluxDB resources.
727
+
728
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_TagResource.html
729
+
730
+ Returns:
731
+ Status of the tag operation.
732
+ """
733
+ if not tool_write_mode:
734
+ raise Exception(
735
+ 'TagResource tool invocation not allowed when tool-write-mode is set to False'
736
+ )
737
+
738
+ ts_influx_client = get_timestream_influxdb_client()
739
+
740
+ # Convert tags dictionary to list of Key/Value pairs
741
+ tag_list = [{'Key': k, 'Value': v} for k, v in tags.items()]
742
+
743
+ try:
744
+ response = ts_influx_client.tag_resource(resourceArn=resource_arn, tags=tag_list)
745
+ return response
746
+ except Exception as e:
747
+ logger.error(f'Error tagging resource: {str(e)}')
748
+ raise e
749
+
750
+
751
+ @mcp.tool(
752
+ name='UntagResource',
753
+ description='Removes the tags, identified by the keys, from the specified resource.',
754
+ )
755
+ async def untag_resource(
756
+ resource_arn: str = REQUIRED_FIELD_RESOURCE_ARN,
757
+ tag_keys: List[str] = REQUIRED_FIELD_TAG_KEYS,
758
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
759
+ ) -> Dict[str, Any]:
760
+ """Removes the tag from the specified resource.
761
+
762
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_UntagResource.html
763
+
764
+ Returns:
765
+ Status of the untag operation.
766
+ """
767
+ if not tool_write_mode:
768
+ raise Exception(
769
+ 'UntagResource tool invocation not allowed when tool-write-mode is set to False'
770
+ )
771
+
772
+ ts_influx_client = get_timestream_influxdb_client()
773
+
774
+ try:
775
+ response = ts_influx_client.untag_resource(resourceArn=resource_arn, tagKeys=tag_keys)
776
+ return response
777
+ except Exception as e:
778
+ logger.error(f'Error untagging resource: {str(e)}')
779
+ raise e
780
+
781
+
782
+ @mcp.tool(name='UpdateDbCluster', description='Updates a Timestream for InfluxDB cluster.')
783
+ async def update_db_cluster(
784
+ db_cluster_id: str = REQUIRED_FIELD_DB_CLUSTER_ID,
785
+ db_instance_type: Optional[str] = OPTIONAL_FIELD_DB_INSTANCE_TYPE_CLUSTER_UPDATE,
786
+ db_parameter_group_identifier: Optional[
787
+ str
788
+ ] = OPTIONAL_FIELD_DB_PARAMETER_GROUP_IDENTIFIER_UPDATE,
789
+ port: Optional[int] = OPTIONAL_FIELD_PORT_UPDATE,
790
+ failover_mode: Optional[str] = OPTIONAL_FIELD_FAILOVER_MODE_UPDATE,
791
+ log_delivery_configuration: Optional[
792
+ Dict[str, Any]
793
+ ] = OPTIONAL_FIELD_LOG_DELIVERY_CONFIGURATION_UPDATE,
794
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
795
+ ) -> Dict[str, Any]:
796
+ """Updates a Timestream for InfluxDB cluster.
797
+
798
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_UpdateDbCluster.html
799
+
800
+ Returns:
801
+ Details of the updated DB cluster.
802
+ """
803
+ if not tool_write_mode:
804
+ raise Exception(
805
+ 'UpdateDbCluster tool invocation not allowed when tool-write-mode is set to False'
806
+ )
807
+
808
+ ts_influx_client = get_timestream_influxdb_client()
809
+
810
+ # Required parameters
811
+ params = {'dbClusterId': db_cluster_id}
812
+
813
+ # Add optional parameters if provided
814
+ if db_instance_type:
815
+ params['dbInstanceType'] = db_instance_type
816
+ if db_parameter_group_identifier:
817
+ params['dbParameterGroupIdentifier'] = db_parameter_group_identifier
818
+ if port:
819
+ params['port'] = str(port)
820
+ if failover_mode:
821
+ params['failoverMode'] = failover_mode
822
+ if log_delivery_configuration:
823
+ params['logDeliveryConfiguration'] = str(log_delivery_configuration)
824
+
825
+ try:
826
+ response = ts_influx_client.update_db_cluster(**params)
827
+ return response
828
+ except Exception as e:
829
+ logger.error(f'Error updating DB cluster: {str(e)}')
830
+ raise e
831
+
832
+
833
+ @mcp.tool(name='UpdateDbInstance', description='Updates a Timestream for InfluxDB DB instance.')
834
+ async def update_db_instance(
835
+ identifier: str = REQUIRED_FIELD_DB_INSTANCE_IDENTIFIER,
836
+ db_instance_type: Optional[str] = OPTIONAL_FIELD_DB_INSTANCE_TYPE_CLUSTER_UPDATE,
837
+ db_parameter_group_identifier: Optional[str] = OPTIONAL_FIELD_DB_PARAMETER_GROUP_ID,
838
+ port: Optional[int] = OPTIONAL_FIELD_PORT,
839
+ allocated_storage_gb: Optional[int] = OPTIONAL_FIELD_ALLOCATED_STORAGE_GB_OPTIONAL,
840
+ db_storage_type: Optional[str] = OPTIONAL_FIELD_DB_STORAGE_TYPE,
841
+ deployment_type: Optional[str] = OPTIONAL_FIELD_DEPLOYMENT_TYPE_INSTANCE,
842
+ log_delivery_configuration: Optional[
843
+ Dict[str, Any]
844
+ ] = OPTIONAL_FIELD_LOG_DELIVERY_CONFIGURATION,
845
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
846
+ ) -> Dict[str, Any]:
847
+ """Updates a Timestream for InfluxDB DB instance.
848
+
849
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_UpdateDbInstance.html
850
+
851
+ Returns:
852
+ Details of the updated DB instance.
853
+ """
854
+ if not tool_write_mode:
855
+ raise Exception(
856
+ 'UpdateDbInstance tool invocation not allowed when tool-write-mode is set to False'
857
+ )
858
+
859
+ ts_influx_client = get_timestream_influxdb_client()
860
+
861
+ # Required parameters
862
+ params = {'identifier': identifier}
863
+
864
+ # Add optional parameters if provided
865
+ if db_instance_type:
866
+ params['dbInstanceType'] = db_instance_type
867
+ if db_parameter_group_identifier:
868
+ params['dbParameterGroupIdentifier'] = db_parameter_group_identifier
869
+ if port:
870
+ params['port'] = str(port)
871
+ if allocated_storage_gb:
872
+ params['allocatedStorage'] = str(allocated_storage_gb)
873
+ if db_storage_type:
874
+ params['dbStorageType'] = db_storage_type
875
+ if deployment_type:
876
+ params['deploymentType'] = deployment_type
877
+ if log_delivery_configuration:
878
+ params['logDeliveryConfiguration'] = str(log_delivery_configuration)
879
+
880
+ try:
881
+ response = ts_influx_client.update_db_instance(**params)
882
+ return response
883
+ except Exception as e:
884
+ logger.error(f'Error updating DB instance: {str(e)}')
885
+ raise e
886
+
887
+
888
+ @mcp.tool(
889
+ name='LsInstancesByStatus',
890
+ description='Returns a list of Timestream for InfluxDB DB instances filtered by status (case-insensitive).',
891
+ )
892
+ async def list_db_instances_by_status(
893
+ status: str = REQUIRED_FIELD_STATUS,
894
+ ) -> Dict[str, Any]:
895
+ """Returns a list of Timestream for InfluxDB DB instances filtered by status (case-insensitive).
896
+
897
+ This tool paginates through all DB instances and filters them by the provided status
898
+ in a case-insensitive manner.
899
+
900
+ Returns:
901
+ A list of Timestream for InfluxDB DB instance summaries matching the specified status.
902
+ """
903
+ ts_influx_client = get_timestream_influxdb_client()
904
+
905
+ # Convert status to lowercase for case-insensitive comparison
906
+ status_lower = status.lower()
907
+
908
+ # Initialize variables for pagination
909
+ next_token = None
910
+ filtered_instances = []
911
+
912
+ try:
913
+ # Paginate through all instances
914
+ while True:
915
+ # Prepare parameters for the API call
916
+ params = {}
917
+ if next_token:
918
+ params['nextToken'] = next_token
919
+
920
+ # Call the ListDbInstances API
921
+ response = ts_influx_client.list_db_instances(**params)
922
+
923
+ # Filter instances by status (case-insensitive)
924
+ if 'items' in response:
925
+ for instance in response['items']:
926
+ if (
927
+ 'status' in instance
928
+ and instance['status'] is not None
929
+ and instance['status'].lower() == status_lower
930
+ ):
931
+ filtered_instances.append(instance)
932
+
933
+ # Check if there are more results to fetch
934
+ if 'nextToken' in response and response['nextToken']:
935
+ next_token = response['nextToken']
936
+ else:
937
+ # No more results to fetch
938
+ break
939
+
940
+ # Prepare the response
941
+ result = {'items': filtered_instances, 'count': len(filtered_instances)}
942
+
943
+ return result
944
+ except Exception as e:
945
+ logger.error(f'Error listing DB instances by status: {str(e)}')
946
+ raise e
947
+
948
+
949
+ @mcp.tool(
950
+ name='ListClustersByStatus',
951
+ description='Returns a list of Timestream for InfluxDB DB clusters filtered by status (case-insensitive).',
952
+ )
953
+ async def list_db_clusters_by_status(
954
+ status: str = REQUIRED_FIELD_STATUS_CLUSTER,
955
+ ) -> Dict[str, Any]:
956
+ """Returns a list of Timestream for InfluxDB DB clusters filtered by status (case-insensitive).
957
+
958
+ This tool paginates through all DB clusters and filters them by the provided status
959
+ in a case-insensitive manner.
960
+
961
+ Returns:
962
+ A list of Timestream for InfluxDB DB cluster summaries matching the specified status.
963
+ """
964
+ ts_influx_client = get_timestream_influxdb_client()
965
+
966
+ # Convert status to lowercase for case-insensitive comparison
967
+ status_lower = status.lower()
968
+
969
+ # Initialize variables for pagination
970
+ next_token = None
971
+ filtered_clusters = []
972
+
973
+ try:
974
+ # Paginate through all clusters
975
+ while True:
976
+ # Prepare parameters for the API call
977
+ params = {}
978
+ if next_token:
979
+ params['nextToken'] = next_token
980
+
981
+ # Call the ListDbClusters API
982
+ response = ts_influx_client.list_db_clusters(**params)
983
+
984
+ # Filter clusters by status (case-insensitive)
985
+ if 'items' in response:
986
+ for cluster in response['items']:
987
+ if (
988
+ 'status' in cluster
989
+ and cluster['status'] is not None
990
+ and cluster['status'].lower() == status_lower
991
+ ):
992
+ filtered_clusters.append(cluster)
993
+
994
+ # Check if there are more results to fetch
995
+ if 'nextToken' in response and response['nextToken']:
996
+ next_token = response['nextToken']
997
+ else:
998
+ # No more results to fetch
999
+ break
1000
+
1001
+ # Prepare the response
1002
+ result = {'items': filtered_clusters, 'count': len(filtered_clusters)}
1003
+
1004
+ return result
1005
+ except Exception as e:
1006
+ logger.error(f'Error listing DB clusters by status: {str(e)}')
1007
+ raise e
1008
+
1009
+
1010
+ @mcp.tool(
1011
+ name='CreateDbParamGroup',
1012
+ description='Creates a new Timestream for InfluxDB DB parameter group to associate with DB instances.',
1013
+ )
1014
+ async def create_db_parameter_group(
1015
+ name: str = REQUIRED_FIELD_PARAM_GROUP_NAME,
1016
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
1017
+ description: Optional[str] = OPTIONAL_FIELD_PARAM_GROUP_DESCRIPTION,
1018
+ parameters: Optional[Dict[str, Any]] = OPTIONAL_FIELD_PARAMETERS,
1019
+ tags: Optional[Dict[str, str]] = OPTIONAL_FIELD_TAGS,
1020
+ ) -> Dict[str, Any]:
1021
+ """Creates a new Timestream for InfluxDB DB parameter group to associate with DB instances.
1022
+
1023
+ API reference: https://docs.aws.amazon.com/ts-influxdb/latest/ts-influxdb-api/API_CreateDbParameterGroup.html
1024
+
1025
+ Returns:
1026
+ Details of the created DB parameter group.
1027
+ """
1028
+ if not tool_write_mode:
1029
+ raise Exception(
1030
+ 'CreateDbParamGroup tool invocation not allowed when tool-write-mode is set to False'
1031
+ )
1032
+
1033
+ ts_influx_client = get_timestream_influxdb_client()
1034
+
1035
+ # Required parameters
1036
+ params = {'name': name}
1037
+
1038
+ # Add optional parameters if provided
1039
+ if description:
1040
+ params['description'] = description
1041
+ if parameters:
1042
+ params['parameters'] = str(parameters)
1043
+ if tags:
1044
+ tag_list = [{'Key': k, 'Value': v} for k, v in tags.items()]
1045
+ params['tags'] = str(tag_list)
1046
+
1047
+ try:
1048
+ response = ts_influx_client.create_db_parameter_group(**params)
1049
+ return response
1050
+ except Exception as e:
1051
+ logger.error(f'Error creating DB parameter group: {str(e)}')
1052
+ raise e
1053
+
1054
+
1055
+ @mcp.tool(name='InfluxDBWritePoints', description='Write data points to InfluxDB endpoint.')
1056
+ async def influxdb_write_points(
1057
+ url: str = REQUIRED_FIELD_URL,
1058
+ token: str = REQUIRED_FIELD_TOKEN,
1059
+ bucket: str = REQUIRED_FIELD_BUCKET,
1060
+ org: str = REQUIRED_FIELD_ORG,
1061
+ points: List[Dict[str, Any]] = REQUIRED_FIELD_POINTS,
1062
+ time_precision: str = OPTIONAL_FIELD_WRITE_PRECISION,
1063
+ sync_mode: Optional[str] = OPTIONAL_FIELD_SYNC_MODE,
1064
+ verify_ssl: bool = OPTIONAL_FIELD_VERIFY_SSL,
1065
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
1066
+ ) -> Dict[str, Any]:
1067
+ """Write data points to InfluxDB.
1068
+
1069
+ Example of points:
1070
+ [
1071
+ {
1072
+ "measurement": "my_measurement",
1073
+ "tags": {"location": "Prague"},
1074
+ "fields": {"temperature": 25.3}
1075
+ "time": "2025-06-06T19:00:00Z"
1076
+ }
1077
+ ]
1078
+
1079
+ Returns:
1080
+ Status of the write operation.
1081
+ """
1082
+ if not tool_write_mode:
1083
+ raise Exception(
1084
+ 'InfluxDBWritePoints tool invocation not allowed when tool-write-mode is set to False'
1085
+ )
1086
+
1087
+ try:
1088
+ client = get_influxdb_client(url, token, org, verify_ssl)
1089
+
1090
+ # Set write mode
1091
+ if sync_mode and sync_mode.lower() == 'synchronous':
1092
+ write_api = client.write_api(write_options=SYNCHRONOUS)
1093
+ else:
1094
+ write_api = client.write_api(write_options=ASYNCHRONOUS)
1095
+
1096
+ # Convert dictionary points to Point objects
1097
+ influx_points = []
1098
+ for p in points:
1099
+ point = Point(p['measurement'])
1100
+
1101
+ # Add tags
1102
+ if 'tags' in p:
1103
+ for tag_key, tag_value in p['tags'].items():
1104
+ point = point.tag(tag_key, tag_value)
1105
+
1106
+ # Add fields
1107
+ if 'fields' in p:
1108
+ for field_key, field_value in p['fields'].items():
1109
+ point = point.field(field_key, field_value)
1110
+
1111
+ # Add time if provided
1112
+ if 'time' in p:
1113
+ point = point.time(p['time'])
1114
+
1115
+ influx_points.append(point)
1116
+
1117
+ # Write points
1118
+ write_api.write(
1119
+ bucket=bucket,
1120
+ org=org,
1121
+ record=influx_points,
1122
+ write_precision=getattr(WritePrecision, time_precision.upper()),
1123
+ verify_ssl=verify_ssl,
1124
+ )
1125
+
1126
+ # Close client
1127
+ client.close()
1128
+
1129
+ return {
1130
+ 'status': 'success',
1131
+ 'message': f'Successfully wrote {len(points)} points to InfluxDB',
1132
+ }
1133
+ except Exception as e:
1134
+ logger.error(f'Error writing points to InfluxDB: {str(e)}')
1135
+ return {'status': 'error', 'message': str(e)}
1136
+
1137
+
1138
+ @mcp.tool(name='InfluxDBWriteLP', description='Write data in Line Protocol format to InfluxDB.')
1139
+ async def influxdb_write_line_protocol(
1140
+ url: str = REQUIRED_FIELD_URL,
1141
+ token: str = REQUIRED_FIELD_TOKEN,
1142
+ bucket: str = REQUIRED_FIELD_BUCKET,
1143
+ org: str = REQUIRED_FIELD_ORG,
1144
+ data_line_protocol: str = REQUIRED_FIELD_DATA_LINE_PROTOCOL,
1145
+ time_precision: str = OPTIONAL_FIELD_WRITE_PRECISION,
1146
+ sync_mode: str = OPTIONAL_FIELD_SYNC_MODE,
1147
+ verify_ssl: bool = OPTIONAL_FIELD_VERIFY_SSL,
1148
+ tool_write_mode: bool = OPTIONAL_FIELD_TOOL_WRITE_MODE,
1149
+ ) -> Dict[str, Any]:
1150
+ """Write data in Line Protocol format to InfluxDB.
1151
+
1152
+ Returns:
1153
+ Status of the write operation.
1154
+ """
1155
+ if not tool_write_mode:
1156
+ raise Exception(
1157
+ 'InfluxDBWriteLineProtocol tool invocation not allowed when tool-write-mode is set to False'
1158
+ )
1159
+
1160
+ try:
1161
+ client = get_influxdb_client(url, token, org)
1162
+
1163
+ # Set write mode
1164
+ if sync_mode and sync_mode.lower() == 'synchronous':
1165
+ write_api = client.write_api(write_options=SYNCHRONOUS)
1166
+ else:
1167
+ write_api = client.write_api(write_options=ASYNCHRONOUS)
1168
+
1169
+ # Write line protocol
1170
+ write_api.write(
1171
+ bucket=bucket,
1172
+ org=org,
1173
+ record=data_line_protocol,
1174
+ write_precision=getattr(WritePrecision, time_precision.upper()),
1175
+ verify_ssl=verify_ssl,
1176
+ )
1177
+
1178
+ # Close client
1179
+ client.close()
1180
+
1181
+ return {
1182
+ 'status': 'success',
1183
+ 'message': 'Successfully wrote line protocol data to InfluxDB',
1184
+ }
1185
+ except Exception as e:
1186
+ logger.error(f'Error writing line protocol to InfluxDB: {str(e)}')
1187
+ return {'status': 'error', 'message': str(e)}
1188
+
1189
+
1190
+ @mcp.tool(name='InfluxDBQuery', description='Query data from InfluxDB using Flux query language.')
1191
+ async def influxdb_query(
1192
+ url: str = REQUIRED_FIELD_URL,
1193
+ token: str = REQUIRED_FIELD_TOKEN,
1194
+ org: str = REQUIRED_FIELD_ORG,
1195
+ query: str = REQUIRED_FIELD_QUERY,
1196
+ verify_ssl: bool = OPTIONAL_FIELD_VERIFY_SSL,
1197
+ ) -> Dict[str, Any]:
1198
+ """Query data from InfluxDB using Flux query language.
1199
+
1200
+ Returns:
1201
+ Query results in the specified format.
1202
+ """
1203
+ try:
1204
+ client = get_influxdb_client(url, token, org, verify_ssl)
1205
+ query_api = client.query_api()
1206
+
1207
+ # Return as JSON
1208
+ tables = query_api.query(org=org, query=query)
1209
+
1210
+ # Process the tables into a more usable format
1211
+ result = []
1212
+ for table in tables:
1213
+ for record in table.records:
1214
+ result.append(
1215
+ {
1216
+ 'measurement': record.get_measurement(),
1217
+ 'field': record.get_field(),
1218
+ 'value': record.get_value(),
1219
+ 'time': record.get_time().isoformat() if record.get_time() else None,
1220
+ 'tags': record.values.get('tags', {}),
1221
+ }
1222
+ )
1223
+
1224
+ client.close()
1225
+ return {'status': 'success', 'result': result, 'format': 'json'}
1226
+
1227
+ except Exception as e:
1228
+ logger.error(f'Error querying InfluxDB: {str(e)}')
1229
+ return {'status': 'error', 'message': str(e)}
1230
+
1231
+
1232
+ def main():
1233
+ """Main entry point for the MCP server application."""
1234
+ logger.info('Starting Timestream for InfluxDB MCP Server')
1235
+ mcp.run()
1236
+
1237
+
1238
+ if __name__ == '__main__':
1239
+ main()