2424
2525import asyncio
2626import json
27- import subprocess
28- import sys
2927import webbrowser
3028from typing import Any
3129from urllib .parse import urlparse
@@ -56,15 +54,19 @@ async def handle_elicitation(
5654 )
5755
5856
57+ ALLOWED_SCHEMES = {"http" , "https" }
58+
59+
5960async def handle_url_elicitation (
6061 params : types .ElicitRequestParams ,
6162) -> types .ElicitResult :
6263 """Handle URL mode elicitation - show security warning and optionally open browser.
6364
6465 This function demonstrates the security-conscious approach to URL elicitation:
65- 1. Display the full URL and domain for user inspection
66- 2. Show the server's reason for requesting this interaction
67- 3. Require explicit user consent before opening any URL
66+ 1. Validate the URL scheme before prompting the user
67+ 2. Display the full URL and domain for user inspection
68+ 3. Show the server's reason for requesting this interaction
69+ 4. Require explicit user consent before opening any URL
6870 """
6971 # Extract URL parameters - these are available on URL mode requests
7072 url = getattr (params , "url" , None )
@@ -75,6 +77,12 @@ async def handle_url_elicitation(
7577 print ("Error: No URL provided in elicitation request" )
7678 return types .ElicitResult (action = "cancel" )
7779
80+ # Reject dangerous URL schemes before prompting the user
81+ parsed = urlparse (str (url ))
82+ if parsed .scheme .lower () not in ALLOWED_SCHEMES :
83+ print (f"\n Rejecting URL with disallowed scheme '{ parsed .scheme } ': { url } " )
84+ return types .ElicitResult (action = "decline" )
85+
7886 # Extract domain for security display
7987 domain = extract_domain (url )
8088
@@ -105,7 +113,11 @@ async def handle_url_elicitation(
105113
106114 # Open the browser
107115 print (f"\n Opening browser to: { url } " )
108- open_browser (url )
116+ try :
117+ webbrowser .open (url )
118+ except Exception as e :
119+ print (f"Failed to open browser: { e } " )
120+ print (f"Please manually open: { url } " )
109121
110122 print ("Waiting for you to complete the interaction in your browser..." )
111123 print ("(The server will continue once you've finished)" )
@@ -121,20 +133,6 @@ def extract_domain(url: str) -> str:
121133 return "unknown"
122134
123135
124- def open_browser (url : str ) -> None :
125- """Open URL in the default browser."""
126- try :
127- if sys .platform == "darwin" :
128- subprocess .run (["open" , url ], check = False )
129- elif sys .platform == "win32" :
130- subprocess .run (["start" , url ], shell = True , check = False )
131- else :
132- webbrowser .open (url )
133- except Exception as e :
134- print (f"Failed to open browser: { e } " )
135- print (f"Please manually open: { url } " )
136-
137-
138136async def call_tool_with_error_handling (
139137 session : ClientSession ,
140138 tool_name : str ,
0 commit comments