diff --git a/README.md b/README.md
index 3a59549ae4..9cf5e12fe2 100644
--- a/README.md
+++ b/README.md
@@ -260,7 +260,7 @@ Connect systems with messaging, workflows, and location services.
|-------------|-------------|---------|
| [Amazon SNS / SQS MCP Server](src/amazon-sns-sqs-mcp-server) | Event-driven messaging and queue management | [](https://kiro.dev/launch/mcp/add?name=awslabs.amazon-sns-sqs-mcp-server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.amazon-sns-sqs-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22AWS_PROFILE%22%3A%22your-aws-profile%22%2C%22AWS_REGION%22%3A%22us-east-1%22%7D%7D)
[](https://cursor.com/en/install-mcp?name=awslabs.amazon-sns-sqs-mcp-server&config=eyJjb21tYW5kIjoidXZ4IGF3c2xhYnMuYW1hem9uLXNucy1zcXMtbWNwLXNlcnZlckBsYXRlc3QiLCJlbnYiOnsiQVdTX1BST0ZJTEUiOiJ5b3VyLWF3cy1wcm9maWxlIiwiQVdTX1JFR0lPTiI6InVzLWVhc3QtMSJ9fQ%3D%3D)
[](https://insiders.vscode.dev/redirect/mcp/install?name=Amazon%20SNS%2FSQS%20MCP%20Server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.amazon-sns-sqs-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22AWS_PROFILE%22%3A%22your-aws-profile%22%2C%22AWS_REGION%22%3A%22us-east-1%22%7D%7D) |
| [Amazon MQ MCP Server](src/amazon-mq-mcp-server) | Message broker management for RabbitMQ and ActiveMQ | [](https://kiro.dev/launch/mcp/add?name=awslabs.amazon-mq-mcp-server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.amazon-mq-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22AWS_PROFILE%22%3A%22your-aws-profile%22%2C%22AWS_REGION%22%3A%22us-east-1%22%2C%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%7D)
[](https://cursor.com/en/install-mcp?name=awslabs.amazon-mq-mcp-server&config=eyJjb21tYW5kIjoidXZ4IGF3c2xhYnMuYW1hem9uLW1xLW1jcC1zZXJ2ZXJAbGF0ZXN0IiwiZW52Ijp7IkFXU19QUk9GSUxFIjoieW91ci1hd3MtcHJvZmlsZSIsIkFXU19SRUdJT04iOiJ1cy1lYXN0LTEiLCJGQVNUTUNQX0xPR19MRVZFTCI6IkVSUk9SIn0sImRpc2FibGVkIjpmYWxzZSwiYXV0b0FwcHJvdmUiOltdfQ%3D%3D)
[](https://insiders.vscode.dev/redirect/mcp/install?name=Amazon%20MQ%20MCP%20Server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.amazon-mq-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22AWS_PROFILE%22%3A%22your-aws-profile%22%2C%22AWS_REGION%22%3A%22us-east-1%22%2C%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D) |
-| [AWS MSK MCP Server](src/aws-msk-mcp-server) | Managed Kafka cluster operations and streaming | [](https://kiro.dev/launch/mcp/add?name=awslabs.aws-msk-mcp-server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.aws-msk-mcp-server%40latest%22%2C%22--allow-writes%22%5D%2C%22env%22%3A%7B%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%7D)
[](https://cursor.com/en/install-mcp?name=awslabs.aws-msk-mcp-server&config=JTdCJTIyY29tbWFuZCUyMiUzQSUyMnV2eCUyMGF3c2xhYnMuYXdzLW1zay1tY3Atc2VydmVyJTQwbGF0ZXN0JTIwLS1hbGxvdy13cml0ZXMlMjIlMkMlMjJlbnYlMjIlM0ElN0IlMjJGQVNUTUNQX0xPR19MRVZFTCUyMiUzQSUyMkVSUk9SJTIyJTdEJTJDJTIyZGlzYWJsZWQlMjIlM0FmYWxzZSUyQyUyMmF1dG9BcHByb3ZlJTIyJTNBJTVCJTVEJTdE)
[](https://insiders.vscode.dev/redirect/mcp/install?name=AWS%20MSK%20MCP%20Server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.aws-msk-mcp-server%40latest%22%2C%22--allow-writes%22%5D%2C%22env%22%3A%7B%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D) |
+| [AWS MSK MCP Server](src/aws-msk-mcp-server) | Managed Kafka cluster operations and streaming (adds topic viewing: list, describe, partitions) | [](https://kiro.dev/launch/mcp/add?name=awslabs.aws-msk-mcp-server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.aws-msk-mcp-server%40latest%22%2C%22--allow-writes%22%5D%2C%22env%22%3A%7B%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%7D)
[](https://cursor.com/en/install-mcp?name=awslabs.aws-msk-mcp-server&config=JTdCJTIyY29tbWFuZCUyMiUzQSUyMnV2eCUyMGF3c2xhYnMuYXdzLW1zay1tY3Atc2VydmVyJTQwbGF0ZXN0JTIwLS1hbGxvdy13cml0ZXMlMjIlMkMlMjJlbnYlMjIlM0ElN0IlMjJGQVNUTUNQX0xPR19MRVZFTCUyMiUzQSUyMkVSUk9SJTIyJTdEJTJDJTIyZGlzYWJsZWQlMjIlM0FmYWxzZSUyQyUyMmF1dG9BcHByb3ZlJTIyJTNBJTVCJTVEJTdE)
[](https://insiders.vscode.dev/redirect/mcp/install?name=AWS%20MSK%20MCP%20Server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.aws-msk-mcp-server%40latest%22%2C%22--allow-writes%22%5D%2C%22env%22%3A%7B%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D) |
| [AWS Step Functions Tool MCP Server](src/stepfunctions-tool-mcp-server) | Execute complex workflows and business processes | [](https://kiro.dev/launch/mcp/add?name=awslabs.stepfunctions-tool-mcp-server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.stepfunctions-tool-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22AWS_PROFILE%22%3A%22your-aws-profile%22%2C%22AWS_REGION%22%3A%22us-east-1%22%2C%22STATE_MACHINE_PREFIX%22%3A%22your-state-machine-prefix%22%2C%22STATE_MACHINE_LIST%22%3A%22your-first-state-machine%2C%20your-second-state-machine%22%2C%22STATE_MACHINE_TAG_KEY%22%3A%22your-tag-key%22%2C%22STATE_MACHINE_TAG_VALUE%22%3A%22your-tag-value%22%2C%22STATE_MACHINE_INPUT_SCHEMA_ARN_TAG_KEY%22%3A%22your-state-machine-tag-for-input-schema%22%7D%7D)
[](https://cursor.com/en/install-mcp?name=awslabs.stepfunctions-tool-mcp-server&config=eyJjb21tYW5kIjoidXZ4IGF3c2xhYnMuc3RlcGZ1bmN0aW9ucy10b29sLW1jcC1zZXJ2ZXJAbGF0ZXN0IiwiZW52Ijp7IkFXU19QUk9GSUxFIjoieW91ci1hd3MtcHJvZmlsZSIsIkFXU19SRUdJT04iOiJ1cy1lYXN0LTEiLCJTVEFURV9NQUNISU5FX1BSRUZJWCI6InlvdXItc3RhdGUtbWFjaGluZS1wcmVmaXgiLCJTVEFURV9NQUNISU5FX0xJU1QiOiJ5b3VyLWZpcnN0LXN0YXRlLW1hY2hpbmUsIHlvdXItc2Vjb25kLXN0YXRlLW1hY2hpbmUiLCJTVEFURV9NQUNISU5FX1RBR19LRVkiOiJ5b3VyLXRhZy1rZXkiLCJTVEFURV9NQUNISU5FX1RBR19WQUxVRSI6InlvdXItdGFnLXZhbHVlIiwiU1RBVEVfTUFDSElORV9JTlBVVF9TQ0hFTUFfQVJOX1RBR19LRVkiOiJ5b3VyLXN0YXRlLW1hY2hpbmUtdGFnLWZvci1pbnB1dC1zY2hlbWEifX0%3D)
[](https://insiders.vscode.dev/redirect/mcp/install?name=Step%20Functions%20Tool%20MCP%20Server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.stepfunctions-tool-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22AWS_PROFILE%22%3A%22your-aws-profile%22%2C%22AWS_REGION%22%3A%22us-east-1%22%2C%22STATE_MACHINE_PREFIX%22%3A%22your-state-machine-prefix%22%2C%22STATE_MACHINE_LIST%22%3A%22your-first-state-machine%2C%20your-second-state-machine%22%2C%22STATE_MACHINE_TAG_KEY%22%3A%22your-tag-key%22%2C%22STATE_MACHINE_TAG_VALUE%22%3A%22your-tag-value%22%2C%22STATE_MACHINE_INPUT_SCHEMA_ARN_TAG_KEY%22%3A%22your-state-machine-tag-for-input-schema%22%7D%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D) |
| [Amazon Location Service MCP Server](src/aws-location-mcp-server) | Place search, geocoding, and route optimization | [](https://kiro.dev/launch/mcp/add?name=awslabs.aws-location-mcp-server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.aws-location-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22AWS_PROFILE%22%3A%22your-aws-profile%22%2C%22AWS_REGION%22%3A%22us-east-1%22%2C%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%7D)
[](https://cursor.com/en/install-mcp?name=awslabs.aws-location-mcp-server&config=eyJjb21tYW5kIjoidXZ4IGF3c2xhYnMuYXdzLWxvY2F0aW9uLW1jcC1zZXJ2ZXJAbGF0ZXN0IiwiZW52Ijp7IkFXU19QUk9GSUxFIjoieW91ci1hd3MtcHJvZmlsZSIsIkFXU19SRUdJT04iOiJ1cy1lYXN0LTEiLCJGQVNUTUNQX0xPR19MRVZFTCI6IkVSUk9SIn0sImRpc2FibGVkIjpmYWxzZSwiYXV0b0FwcHJvdmUiOltdfQ%3D%3D)
[](https://insiders.vscode.dev/redirect/mcp/install?name=AWS%20Location%20MCP%20Server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.aws-location-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22AWS_PROFILE%22%3A%22your-aws-profile%22%2C%22AWS_REGION%22%3A%22us-east-1%22%2C%22FASTMCP_LOG_LEVEL%22%3A%22ERROR%22%7D%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D) |
| [OpenAPI MCP Server](src/openapi-mcp-server) | Dynamic API integration through OpenAPI specifications | [](https://kiro.dev/launch/mcp/add?name=awslabs.openapi-mcp-server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.openapi-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22API_NAME%22%3A%22your-api-name%22%2C%22API_BASE_URL%22%3A%22https%3A//api.example.com%22%2C%22API_SPEC_URL%22%3A%22https%3A//api.example.com/openapi.json%22%2C%22LOG_LEVEL%22%3A%22ERROR%22%2C%22ENABLE_PROMETHEUS%22%3A%22false%22%2C%22ENABLE_OPERATION_PROMPTS%22%3A%22true%22%2C%22UVICORN_TIMEOUT_GRACEFUL_SHUTDOWN%22%3A%225.0%22%2C%22UVICORN_GRACEFUL_SHUTDOWN%22%3A%22true%22%7D%7D)
[](https://cursor.com/en/install-mcp?name=awslabs.openapi-mcp-server&config=eyJjb21tYW5kIjoidXZ4IGF3c2xhYnMub3BlbmFwaS1tY3Atc2VydmVyQGxhdGVzdCIsImVudiI6eyJBUElfTkFNRSI6InlvdXItYXBpLW5hbWUiLCJBUElfQkFTRV9VUkwiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbSIsIkFQSV9TUEVDX1VSTCI6Imh0dHBzOi8vYXBpLmV4YW1wbGUuY29tL29wZW5hcGkuanNvbiIsIkxPR19MRVZFTCI6IkVSUk9SIiwiRU5BQkxFX1BST01FVEhFVVMiOiJmYWxzZSIsIkVOQUJMRV9PUEVSQVRJT05fUFJPTVBUUyI6InRydWUiLCJVVklDT1JOX1RJTUVPVVRfR1JBQ0VGVUxfU0hVVERPV04iOiI1LjAiLCJVVklDT1JOX0dSQUNFRlVMX1NIVVRET1dOIjoidHJ1ZSJ9LCJkaXNhYmxlZCI6ZmFsc2UsImF1dG9BcHByb3ZlIjpbXX0%3D)
[](https://insiders.vscode.dev/redirect/mcp/install?name=OpenAPI%20MCP%20Server&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22awslabs.openapi-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22API_NAME%22%3A%22your-api-name%22%2C%22API_BASE_URL%22%3A%22https%3A%2F%2Fapi.example.com%22%2C%22API_SPEC_URL%22%3A%22https%3A%2F%2Fapi.example.com%2Fopenapi.json%22%2C%22LOG_LEVEL%22%3A%22ERROR%22%2C%22ENABLE_PROMETHEUS%22%3A%22false%22%2C%22ENABLE_OPERATION_PROMPTS%22%3A%22true%22%2C%22UVICORN_TIMEOUT_GRACEFUL_SHUTDOWN%22%3A%225.0%22%2C%22UVICORN_GRACEFUL_SHUTDOWN%22%3A%22true%22%7D%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D) |
diff --git a/src/aws-msk-mcp-server/README.md b/src/aws-msk-mcp-server/README.md
index d28abdcb70..5fe5712d79 100644
--- a/src/aws-msk-mcp-server/README.md
+++ b/src/aws-msk-mcp-server/README.md
@@ -60,6 +60,42 @@ The AWS MSK MCP Server provides a set of tools for interacting with Amazon MSK t
- **get_cluster_telemetry**: Retrieves telemetry data for MSK clusters
- **get_cluster_best_practices**: Gets best practices and recommendations for MSK clusters
+### Topic Operations
+
+- **get_topic_info**: View MSK topics and partition metadata (supports actions `list`, `describe`, `partitions`).
+ - action=`list`: lists topics in a cluster (supports pagination using `max_results` and `next_token`).
+ - action=`describe`: returns topic-level metadata; requires `topic_name`.
+ - action=`partitions`: returns partition-level metadata for a topic; requires `topic_name`.
+
+Example MCP call payloads (JSON):
+
+action = list:
+```json
+{"tool":"get_topic_info","args":{"action":"list","cluster_arn":"arn:aws:kafka:us-east-1:123456789012:cluster/my-cluster/abcdef","max_results":20}}
+```
+
+action = describe:
+```json
+{"tool":"get_topic_info","args":{"action":"describe","cluster_arn":"arn:aws:kafka:us-east-1:123456789012:cluster/my-cluster/abcdef","topic_name":"my-topic"}}
+```
+
+action = partitions:
+```json
+{"tool":"get_topic_info","args":{"action":"partitions","cluster_arn":"arn:aws:kafka:us-east-1:123456789012:cluster/my-cluster/abcdef","topic_name":"my-topic","max_results":100}}
+```
+
+Quick Python example (helper functions in the package):
+
+```py
+from awslabs.aws_msk_mcp_server.tools.read_topic import list_topics, describe_topic, describe_topic_partitions
+# list topics
+result = list_topics(cluster_arn, client, max_results=20)
+# describe topic
+result = describe_topic(cluster_arn, "my-topic", client)
+# partitions
+result = describe_topic_partitions(cluster_arn, "my-topic", client, max_results=100)
+```
+
## Usage
This MCP server can be used by AI assistants to help users manage their Amazon MSK resources. It provides structured access to MSK APIs, making it easier for AI to understand and interact with MSK clusters.
diff --git a/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/__init__.py b/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/__init__.py
new file mode 100644
index 0000000000..88803bbe11
--- /dev/null
+++ b/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/__init__.py
@@ -0,0 +1,62 @@
+# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Topic Information API Module
+
+Provides topic-related tools for the MSK MCP server.
+"""
+
+import boto3
+from botocore.config import Config
+from awslabs.aws_msk_mcp_server import __version__
+try:
+ from mcp.server.fastmcp import FastMCP
+except Exception: # pragma: no cover - allow imports in lightweight test envs
+ FastMCP = None
+from pydantic import Field
+
+from .list_topics import list_topics
+from .describe_topic import describe_topic
+from .describe_topic_partitions import describe_topic_partitions
+
+
+def register_module(mcp: FastMCP) -> None:
+ @mcp.tool(name='get_topic_info', description='Gets topic information for a cluster')
+ def get_topic_info(
+ region: str = Field(..., description='AWS region'),
+ cluster_arn: str = Field(..., description='The ARN of the cluster'),
+ action: str = Field('list', description="Action: 'list' | 'describe' | 'partitions'"),
+ topic_name: str = Field(None, description='Topic name (required for describe/partitions)'),
+ kwargs: dict = Field({}, description='Additional args for pagination'),
+ ):
+ client = boto3.client(
+ 'kafka',
+ region_name=region,
+ config=Config(user_agent_extra=f'awslabs/mcp/aws-msk-mcp-server/{__version__}'),
+ )
+
+ if action == 'list':
+ max_results = kwargs.get('max_results', 10)
+ next_token = kwargs.get('next_token', None)
+ return list_topics(cluster_arn, client, max_results, next_token)
+ if action == 'describe':
+ if not topic_name:
+ raise ValueError('topic_name is required for describe action')
+ return describe_topic(cluster_arn, topic_name, client)
+ if action == 'partitions':
+ if not topic_name:
+ raise ValueError('topic_name is required for partitions action')
+ max_results = kwargs.get('max_results', 10)
+ next_token = kwargs.get('next_token', None)
+ return describe_topic_partitions(cluster_arn, topic_name, client, max_results, next_token)
diff --git a/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/describe_topic.py b/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/describe_topic.py
new file mode 100644
index 0000000000..f16106a211
--- /dev/null
+++ b/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/describe_topic.py
@@ -0,0 +1,39 @@
+# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Function to describe a topic in an MSK cluster.
+
+Maps to AWS CLI command: aws kafka describe-topic.
+"""
+
+
+def describe_topic(cluster_arn, topic_name, client):
+ """Returns metadata/configuration for a topic.
+
+ Args:
+ cluster_arn (str): The ARN of the cluster
+ topic_name (str): The name of the topic to describe
+ client (boto3.client): Boto3 client for Kafka. Must be provided by get_topic_info.
+
+ Returns:
+ dict: Topic description
+ """
+ if client is None:
+ raise ValueError(
+ 'Client must be provided. This function should only be called from get_topic_info.'
+ )
+
+ response = client.describe_topic(ClusterArn=cluster_arn, TopicName=topic_name)
+
+ return response
diff --git a/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/describe_topic_partitions.py b/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/describe_topic_partitions.py
new file mode 100644
index 0000000000..7dc6abcacb
--- /dev/null
+++ b/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/describe_topic_partitions.py
@@ -0,0 +1,46 @@
+# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Function to describe topic partitions in an MSK cluster.
+
+Maps to AWS CLI command: aws kafka describe-topic-partitions.
+"""
+
+
+def describe_topic_partitions(cluster_arn, topic_name, client, max_results=10, next_token=None):
+ """Returns partition-level information for a topic.
+
+ Args:
+ cluster_arn (str): The ARN of the cluster
+ topic_name (str): The name of the topic
+ client (boto3.client): Boto3 client for Kafka. Must be provided by get_topic_info.
+ max_results (int): Maximum number of partitions to return
+ next_token (str): Pagination token
+
+ Returns:
+ dict: Partition description
+ """
+ if client is None:
+ raise ValueError(
+ 'Client must be provided. This function should only be called from get_topic_info.'
+ )
+
+ params = {'ClusterArn': cluster_arn, 'TopicName': topic_name, 'MaxResults': max_results}
+
+ if next_token:
+ params['NextToken'] = next_token
+
+ response = client.describe_topic_partitions(**params)
+
+ return response
diff --git a/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/list_topics.py b/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/list_topics.py
new file mode 100644
index 0000000000..88c4a909fe
--- /dev/null
+++ b/src/aws-msk-mcp-server/awslabs/aws_msk_mcp_server/tools/read_topic/list_topics.py
@@ -0,0 +1,45 @@
+# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Function to list topics of an MSK cluster.
+
+Maps to AWS CLI command: aws kafka list-topics.
+"""
+
+
+def list_topics(cluster_arn, client, max_results=10, next_token=None):
+ """Returns a list of topics in the cluster.
+
+ Args:
+ cluster_arn (str): The ARN of the cluster to list topics for
+ client (boto3.client): Boto3 client for Kafka. Must be provided by get_topic_info.
+ max_results (int): Maximum number of topics to return
+ next_token (str): Token for pagination
+
+ Returns:
+ dict: List of topics
+ """
+ if client is None:
+ raise ValueError(
+ 'Client must be provided. This function should only be called from get_topic_info.'
+ )
+
+ params = {'ClusterArn': cluster_arn, 'MaxResults': max_results}
+
+ if next_token:
+ params['NextToken'] = next_token
+
+ response = client.list_topics(**params)
+
+ return response
diff --git a/src/aws-msk-mcp-server/tests/test_describe_topic.py b/src/aws-msk-mcp-server/tests/test_describe_topic.py
new file mode 100644
index 0000000000..ac8b4e0967
--- /dev/null
+++ b/src/aws-msk-mcp-server/tests/test_describe_topic.py
@@ -0,0 +1,51 @@
+"""Tests for the describe_topic module."""
+
+import pytest
+from awslabs.aws_msk_mcp_server.tools.read_topic.describe_topic import describe_topic
+from botocore.exceptions import ClientError
+from unittest.mock import MagicMock
+
+
+class TestDescribeTopic:
+ def test_describe_topic_basic(self):
+ mock_client = MagicMock()
+ expected_response = {
+ 'TopicInfo': {
+ 'TopicName': 'topic-a',
+ 'TopicArn': 'arn:topic:a',
+ 'Partitions': 3,
+ 'Configs': {'retention.ms': '604800000'},
+ }
+ }
+ mock_client.describe_topic.return_value = expected_response
+
+ cluster_arn = 'arn:aws:kafka:us-east-1:123456789012:cluster/test-cluster/abcdef'
+ topic_name = 'topic-a'
+ result = describe_topic(cluster_arn, topic_name, mock_client)
+
+ mock_client.describe_topic.assert_called_once_with(ClusterArn=cluster_arn, TopicName=topic_name)
+ assert result == expected_response
+
+ def test_describe_topic_error(self):
+ mock_client = MagicMock()
+ mock_client.describe_topic.side_effect = ClientError(
+ {'Error': {'Code': 'ResourceNotFoundException', 'Message': 'Topic not found'}},
+ 'DescribeTopic',
+ )
+
+ cluster_arn = 'arn:aws:kafka:us-east-1:123456789012:cluster/test-cluster/abcdef'
+ topic_name = 'missing-topic'
+ with pytest.raises(ClientError) as excinfo:
+ describe_topic(cluster_arn, topic_name, mock_client)
+
+ assert 'ResourceNotFoundException' in str(excinfo.value)
+ assert 'Topic not found' in str(excinfo.value)
+ mock_client.describe_topic.assert_called_once_with(ClusterArn=cluster_arn, TopicName=topic_name)
+
+ def test_describe_topic_missing_client(self):
+ cluster_arn = 'arn:aws:kafka:us-east-1:123456789012:cluster/test-cluster/abcdef'
+ topic_name = 'topic-a'
+ with pytest.raises(ValueError) as excinfo:
+ describe_topic(cluster_arn, topic_name, None)
+
+ assert 'Client must be provided' in str(excinfo.value)
diff --git a/src/aws-msk-mcp-server/tests/test_describe_topic_partitions.py b/src/aws-msk-mcp-server/tests/test_describe_topic_partitions.py
new file mode 100644
index 0000000000..f17ba22893
--- /dev/null
+++ b/src/aws-msk-mcp-server/tests/test_describe_topic_partitions.py
@@ -0,0 +1,69 @@
+# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for the describe_topic_partitions module."""
+
+import pytest
+from awslabs.aws_msk_mcp_server.tools.read_topic.describe_topic_partitions import (
+ describe_topic_partitions,
+)
+from botocore.exceptions import ClientError
+from unittest.mock import MagicMock
+
+
+class TestDescribeTopicPartitions:
+ def test_describe_topic_partitions_basic(self):
+ mock_client = MagicMock()
+ expected_response = {
+ 'PartitionInfoList': [
+ {'Partition': 0, 'Leader': 1, 'Replicas': [1, 2]},
+ {'Partition': 1, 'Leader': 2, 'Replicas': [1, 2]},
+ ]
+ }
+ mock_client.describe_topic_partitions.return_value = expected_response
+
+ cluster_arn = 'arn:aws:kafka:us-east-1:123456789012:cluster/test-cluster/abcdef'
+ topic_name = 'topic-a'
+ result = describe_topic_partitions(cluster_arn, topic_name, mock_client)
+
+ mock_client.describe_topic_partitions.assert_called_once_with(
+ ClusterArn=cluster_arn, TopicName=topic_name, MaxResults=10
+ )
+ assert result == expected_response
+
+ def test_describe_topic_partitions_error(self):
+ mock_client = MagicMock()
+ mock_client.describe_topic_partitions.side_effect = ClientError(
+ {'Error': {'Code': 'ResourceNotFoundException', 'Message': 'Topic not found'}},
+ 'DescribeTopicPartitions',
+ )
+
+ cluster_arn = 'arn:aws:kafka:us-east-1:123456789012:cluster/test-cluster/abcdef'
+ topic_name = 'missing-topic'
+ with pytest.raises(ClientError) as excinfo:
+ describe_topic_partitions(cluster_arn, topic_name, mock_client)
+
+ assert 'ResourceNotFoundException' in str(excinfo.value)
+ assert 'Topic not found' in str(excinfo.value)
+ mock_client.describe_topic_partitions.assert_called_once_with(
+ ClusterArn=cluster_arn, TopicName=topic_name, MaxResults=10
+ )
+
+ def test_describe_topic_partitions_missing_client(self):
+ cluster_arn = 'arn:aws:kafka:us-east-1:123456789012:cluster/test-cluster/abcdef'
+ topic_name = 'topic-a'
+ with pytest.raises(ValueError) as excinfo:
+ describe_topic_partitions(cluster_arn, topic_name, None)
+
+ assert 'Client must be provided' in str(excinfo.value)
diff --git a/src/aws-msk-mcp-server/tests/test_list_topics.py b/src/aws-msk-mcp-server/tests/test_list_topics.py
new file mode 100644
index 0000000000..33b3767f44
--- /dev/null
+++ b/src/aws-msk-mcp-server/tests/test_list_topics.py
@@ -0,0 +1,61 @@
+# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for the list_topics module."""
+
+import pytest
+from awslabs.aws_msk_mcp_server.tools.read_topic.list_topics import list_topics
+from botocore.exceptions import ClientError
+from unittest.mock import MagicMock
+
+
+class TestListTopics:
+ def test_list_topics_basic(self):
+ mock_client = MagicMock()
+ expected_response = {
+ 'TopicInfoList': [
+ {'TopicName': 'topic-a', 'TopicArn': 'arn:topic:a'},
+ {'TopicName': 'topic-b', 'TopicArn': 'arn:topic:b'},
+ ]
+ }
+ mock_client.list_topics.return_value = expected_response
+
+ cluster_arn = 'arn:aws:kafka:us-east-1:123456789012:cluster/test-cluster/abcdef'
+ result = list_topics(cluster_arn, mock_client)
+
+ mock_client.list_topics.assert_called_once_with(ClusterArn=cluster_arn, MaxResults=10)
+ assert result == expected_response
+ assert len(result['TopicInfoList']) == 2
+
+ def test_list_topics_error(self):
+ mock_client = MagicMock()
+ mock_client.list_topics.side_effect = ClientError(
+ {'Error': {'Code': 'ResourceNotFoundException', 'Message': 'Cluster not found'}},
+ 'ListTopics',
+ )
+
+ cluster_arn = 'arn:aws:kafka:us-east-1:123456789012:cluster/test-cluster/abcdef'
+ with pytest.raises(ClientError) as excinfo:
+ list_topics(cluster_arn, mock_client)
+
+ assert 'ResourceNotFoundException' in str(excinfo.value)
+ assert 'Cluster not found' in str(excinfo.value)
+ mock_client.list_topics.assert_called_once_with(ClusterArn=cluster_arn, MaxResults=10)
+
+ def test_list_topics_missing_client(self):
+ cluster_arn = 'arn:aws:kafka:us-east-1:123456789012:cluster/test-cluster/abcdef'
+ with pytest.raises(ValueError) as excinfo:
+ list_topics(cluster_arn, None)
+
+ assert 'Client must be provided' in str(excinfo.value)