Skip to content

Conversation

@zhan9san
Copy link
Contributor

Issue number:
#31

Summary

Changes

Please provide a summary of what's being changed

User experience

Please share what the user experience looks like before and after this change

Retrieve cross-account cloudwatch logs

Checklist

If your change doesn't seem to apply, please leave them unchecked.

  • I have reviewed the contributing guidelines
  • I have performed a self-review of this change
  • Changes have been tested
  • Changes are documented
Is this a breaking change? No **RFC issue number**:

Checklist:

  • Migration process documented
  • Implement warnings (if it can live side by side)

Acknowledgment

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the project license.

@zhan9san
Copy link
Contributor Author

Hi @adiadd

Thanks for your effort in this great project.

Would you please help review this PR?

Copy link
Contributor

@adiadd adiadd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really solid work overall! I love the mixin approach - it's clean and reusable. The automatic credential refresh with the 5-minute buffer is a nice touch too. This is going to be super useful for cross-account log analysis.

Most of my comments are about defensive programming and consistency. Once we clean up the error handling and imports, this should be good to go!

Would also love a quick working video showcasing the updated MCP server and functionality!

sts_client = source_session.client("sts")
assume_role_params = {
"RoleArn": self.role_arn,
"RoleSessionName": f"CloudWatchLogs-{int(time.time())}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor thing, but I noticed we're only using timestamp for the session name. In high-frequency scenarios (like multiple MCP servers running), we might get collisions. What do you think about making it a bit more unique?

Maybe something like:

import uuid
"RoleSessionName": f"CloudWatchLogs-{int(time.time())}-{uuid.uuid4().hex[:8]}",

Not a blocker, but could save us some headaches down the road!

Comment on lines +98 to +101
try:
response = sts_client.assume_role(**assume_role_params)
except Exception as e:
raise Exception(f"Failed to assume role {self.role_arn}: {str(e)}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really like the overall approach here, but I'm a bit worried about the generic exception handling. This could potentially expose sensitive AWS error details to end users, and we might miss important specific error cases.

Would you mind updating this to catch specific AWS exceptions? Something like:

from botocore.exceptions import ClientError

try:
    response = sts_client.assume_role(**assume_role_params)
except ClientError as e:
    error_code = e.response['Error']['Code']
    if error_code == 'AccessDenied':
        raise Exception(f"Access denied when assuming role {self.role_arn}")
    elif error_code == 'InvalidUserType':
        raise Exception(f"Invalid user type for role assumption")
    else:
        raise Exception(f"Failed to assume role: {error_code}")

This way we can give users more helpful error messages while keeping things secure.

Comment on lines +6 to +8
import boto3
import time
from datetime import datetime, timedelta
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the other code review comment, we'll likely need to import ClientError. Could you add:

from botocore.exceptions import ClientError

Thanks!

Comment on lines +34 to +35
self._assumed_session = None
self._credentials_expiry = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since MCP servers can handle concurrent requests, I'm wondering if we should consider some synchronization here? The session state could potentially get mixed up if multiple threads are assuming roles simultaneously.

Might be worth thinking about a simple lock/check around the credential refresh logic. What's your take on this?

Comment on lines +12 to +16
import sys
import os

sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from aws_credentials_mixin import AWSCredentialsMixin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works, but modifying sys.path at runtime can make the code a bit fragile and harder to test. Since we're doing this in multiple files, what do you think about either:

  1. Moving the mixin to a proper package location, or
  2. Using relative imports like from ..aws_credentials_mixin import AWSCredentialsMixin

It would make the imports cleaner and more maintainable. Same with the src/cw-mcp-server/tools/correlation_tools.py and src/cw-mcp-server/tools/search_tools.py as well

if __name__ == "__main__":
# Run the MCP server
mcp.run()
mcp.run(transport="streamable-http")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious if this works as intended with the addition of this, as this is an issue we have open (#29) - just want to make sure this doesn't break existing setups. Maybe worth a quick note in the PR description + any update to docs

@zhan9san
Copy link
Contributor Author

@adiadd

Thanks for your review.

I happened to find these tickets. What do you think?

boto/botocore#761
boto/boto3#443
boto/boto3#3143
boto/boto3#3253

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants