77
88if TYPE_CHECKING :
99 from sse_starlette .sse import EventSourceResponse
10+ from starlette .datastructures import URL
1011 from starlette .requests import Request
1112 from starlette .responses import JSONResponse , Response
1213
1516else :
1617 try :
1718 from sse_starlette .sse import EventSourceResponse
19+ from starlette .datastructures import URL
1820 from starlette .requests import Request
1921 from starlette .responses import JSONResponse , Response
2022
@@ -119,7 +121,8 @@ async def handle_get_agent_card(self, request: Request) -> JSONResponse:
119121 A JSONResponse containing the agent card data.
120122 """
121123 # The public agent card is a direct serialization of the agent_card
122- # provided at initialization.
124+ # provided at initialization except for the RPC URL.
125+ self ._modify_rpc_url (self .agent_card , request )
123126 return JSONResponse (
124127 self .agent_card .model_dump (mode = 'json' , exclude_none = True )
125128 )
@@ -145,9 +148,65 @@ async def handle_authenticated_agent_card(
145148 message = 'Authenticated card not supported'
146149 )
147150 )
151+ self ._modify_rpc_url (self .agent_card , request )
148152 return JSONResponse (
149153 self .agent_card .model_dump (mode = 'json' , exclude_none = True )
150154 )
155+
156+ def _modify_rpc_url (self , agent_card : AgentCard , request : Request ):
157+ """Modifies Agent's RPC URL based on the AgentCard request.
158+
159+ Args:
160+ agent_card (AgentCard): Original AgentCard
161+ request (Request): AgentCard request
162+ """
163+ rpc_url = URL (agent_card .url )
164+ rpc_path = rpc_url .path
165+ port = None
166+ if "X-Forwarded-Host" in request .headers :
167+ host = request .headers ["X-Forwarded-Host" ]
168+ else :
169+ host = request .url .hostname
170+ port = request .url .port
171+
172+ if "X-Forwarded-Proto" in request .headers :
173+ scheme = request .headers ["X-Forwarded-Proto" ]
174+ port = None
175+ else :
176+ scheme = request .url .scheme
177+ if not scheme :
178+ scheme = "http"
179+ if ":" in host : # type: ignore
180+ comps = host .rsplit (":" , 1 ) # type: ignore
181+ host = comps [0 ]
182+ port = comps [1 ]
183+
184+ # Handle URL maps,
185+ # e.g. "agents/my-agent/.well-known/agent-card.json"
186+ if "X-Forwarded-Path" in request .headers :
187+ forwarded_path = request .headers ["X-Forwarded-Path" ].strip ()
188+ if (
189+ forwarded_path and
190+ request .url .path != forwarded_path
191+ and forwarded_path .endswith (request .url .path )
192+ ):
193+ # "agents/my-agent" for "agents/my-agent/.well-known/agent-card.json"
194+ extra_path = forwarded_path [:- len (request .url .path )]
195+ new_path = extra_path + rpc_path
196+ # If original path was just "/",
197+ # we remove trailing "/" in the the extended one
198+ if len (new_path ) > 1 and rpc_path == "/" :
199+ new_path = new_path .rstrip ("/" )
200+ rpc_path = new_path
201+
202+ agent_card .url = str (
203+ rpc_url .replace (
204+ hostname = host ,
205+ port = port ,
206+ scheme = scheme ,
207+ path = rpc_path
208+ )
209+ )
151210
152211 def routes (self ) -> dict [tuple [str , str ], Callable [[Request ], Any ]]:
153212 """Constructs a dictionary of API routes and their corresponding handlers.
0 commit comments