Skip to content

Conversation

@wzxxing
Copy link
Contributor

@wzxxing wzxxing commented Dec 3, 2025

Summary

This PR refactors the MCP proxy initialization logic to improve error handling and code organization. The main changes extract proxy logic into a dedicated module and implement initialization as middleware.

The way error message is surfaced is by sending JSONRPCError back to the client. Then wait for client to close the stdin stream and terminate the proxy process. This is the stdio transport specification.

Changes

Please provide a summary of what's being changed

  • New proxy.py module: Extracted proxy connection logic from server.py for better separation of concerns
  • Initialize middleware: Moved initialization logic into initialize_middleware.py to ensure errors are properly surfaced for all MCP clients
  • Improved error visibility: Initialization errors now consistently displayed across different MCP client implementations
  • Enhanced server naming: Added endpoint URL to MCP proxy server name for better identification
  • Fixed disconnect order: Clients now disconnect in reverse order to prevent potential issues
  • Test coverage: Added comprehensive unit tests for the new proxy module

User experience

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

Customer is able to see error message in Kiro CLI.

Screenshot 2025-12-02 at 15 32 29

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? (Y/N)

  • Yes
  • No

Please add details about how this change was tested.

  • Did integration tests succeed?
  • If the feature is a new use case, is it necessary to add a new integration test case?

Acknowledgment

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

async def __aexit__(self, exc_type, exc_val, exc_tb):
"""The MCP Proxy for AWS project is a proxy from stdio to http (sigv4).
We want the client to remain connected in the until the stdio connection is closed.
Copy link
Member

Choose a reason for hiding this comment

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

typo

else:
raise http_error

async def __aexit__(self, exc_type, exc_val, exc_tb):
Copy link
Member

Choose a reason for hiding this comment

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

override?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not necessary imo, __aexit__ is async context manager protocol method, not a method specific to the parent class.

There is no equivalent of the streamble-http DELETE concept in stdio to terminate a session.
Hence the connection will be terminated only at program exit.
"""
# return await super().__aexit__(exc_type, exc_val, exc_tb)
Copy link
Member

Choose a reason for hiding this comment

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

is this by intention?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will remove the commented line.

self._client_factory.set_init_params(context.message)
return await call_next(context)
except Exception:
logger.exception('Initialize failed in middleware.')
Copy link
Member

Choose a reason for hiding this comment

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

do we want to log the exact error?

Copy link
Contributor Author

@wzxxing wzxxing Dec 3, 2025

Choose a reason for hiding this comment

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

exception logs the latest error automatically

@wzxxing wzxxing marked this pull request as ready for review December 3, 2025 11:18
@wzxxing wzxxing requested a review from a team as a code owner December 3, 2025 11:18
@wzxxing wzxxing requested review from anasstahr and jinet December 3, 2025 11:18
@wzxxing wzxxing enabled auto-merge (squash) December 3, 2025 11:18
awsjjzhou
awsjjzhou previously approved these changes Dec 3, 2025
body = await response.aread()
jsonrpc_msg = JSONRPCMessage.model_validate_json(body).root
except Exception:
logger.debug('HTTP error is not a valid MCP message.')
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to log the exception here?

"""Initialize a client factory with transport."""
self._transport = transport
self._client: AWSMCPProxyClient | None = None
self._clients: list[AWSMCPProxyClient] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

When would we have >1 client?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So this is part of the fix by @arnewouters, when client is in a disconnect state, we will create a new client and push to the list.

Copy link
Contributor

@arnewouters arnewouters Dec 3, 2025

Choose a reason for hiding this comment

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

I don't think we need to keep track of this list, can't we immediately disconnect the old client and replace it with the new one in get_client?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

f42625b

Screenshot 2025-12-03 at 15 44 46

With the latest commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The old client might be used by a concurrent request.

async def run_proxy(args) -> None:
"""Set up the server in MCP mode."""
logger.info('Setting up server in MCP mode')
logger.info('Setting up mcp proxy server to %s', args.endpoint)
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you are already editing this, can you also log the current version of the proxy, should help with debugging when logs are shared.

arnewouters
arnewouters previously approved these changes Dec 3, 2025
@wzxxing wzxxing merged commit aa601be into aws:main Dec 4, 2025
3 checks passed
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.

4 participants