from line 1
1# Opening external links from MCP Apps
1# Open external links from MCP Apps
22
3> How Claude handles ui/open-link requests, and how directory connectors can allowlist destinations to skip the confirmation modal
3> Declare allowed link destinations so ui/open-link requests from your directory connector's MCP App open without Claude's confirmation modal.
44
5When your MCP App sends a `ui/open-link` request, Claude shows an "Open external link" confirmation modal before navigating. This protects users from being silently redirected by an embedded app.
5When your MCP App sends a `ui/open-link` request, Claude shows an **Open external link** confirmation modal before navigating. The modal protects users from being silently redirected by an embedded app.
66
7Directory connectors can declare a set of trusted destinations that open immediately without the modal. Custom connectors and locally configured servers always show the modal.
7If your connector is published in the [Connectors Directory](/docs/connectors/directory), you can declare a set of trusted destinations that open immediately without the modal. Custom connectors and locally configured servers always show it. To skip the modal for a directory connector, [allowlist your destinations](#allowlist-link-destinations) and send each request after a [real user gesture](#user-activation-requirement), then [design your app](#design-for-the-modal) for the cases where the modal still appears.
88
9## Default behavior
9## Default link behavior
1010
11A `ui/open-link` request displays a confirmation modal showing the destination URL. The link opens in a new tab when the user confirms; the request resolves as cancelled if they dismiss the modal.
11A `ui/open-link` request displays a confirmation modal showing the destination URL. If the user confirms, the link opens in a new tab. If they dismiss the modal, the request resolves as cancelled.
1212
13## Allowlisting link destinations
13## Allowlist link destinations
1414
15If your connector is published in the [Connectors Directory](/docs/connectors/directory), you can declare destinations that skip the modal. Provide them in the **Allowed link URIs** field when you [submit](/docs/connectors/building/submission) or update your directory listing.
15A directory connector declares the destinations that skip the modal in its directory listing. Provide them in the **Allowed link URIs** field when you [submit](/docs/connectors/building/submission#allowed-link-uris) or update your listing.
1616
17Each entry must be one of two shapes:
17Each entry must be an HTTPS origin or a custom URI scheme:
1818
1919| Entry shape | Example | Matches |
2020| ----------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
21| HTTPS origin | `https://docs.example.com` | Any `https://` URL whose hostname is exactly `docs.example.com` (case-insensitive). Subdomains do not match implicitly; list each one you need. Port is not compared. |
21| HTTPS origin | `https://docs.example.com` | Any `https://` URL whose hostname is exactly `docs.example.com`, case-insensitive. Subdomains don't match implicitly, so list each one you need. Port isn't compared. |
2222| Custom URI scheme | `example-app` or `example-app:` | Any URL with the scheme `example-app:`, typically a deep link into your native mobile or desktop app. |
2323
24Entries that do not fit one of these shapes are ignored. This includes bare hostnames such as `example.com`, `http://` origins, and malformed values.
24Entries that don't fit one of these shapes are ignored. This includes bare hostnames such as `example.com`, `http://` origins, and malformed values.
2525
26### Example
26### Allowlist example
2727
2828Given the following allowlist:
2929
from line 41
4141
4242These destinations still show the confirmation modal:
4343
44* `https://blog.example.com` (subdomain not listed)
45* `http://example.com` (not HTTPS)
46* `https://example.com.attacker.net` (different hostname)
44* `https://blog.example.com`, because the subdomain isn't listed
45* `http://example.com`, because it isn't HTTPS
46* `https://example.com.attacker.net`, because the hostname is different
4747
4848### Restrictions on custom schemes
4949
from line 51
5151
5252## User-activation requirement
5353
54The modal is bypassed only when the `ui/open-link` request follows a real user gesture in your app, such as a button click.
54Claude bypasses the modal only when the `ui/open-link` request follows a real user gesture in your app, such as a button click.
5555
56If your app sends `ui/open-link` without a preceding gesture (programmatically, on a timer, or after the browser's activation window has expired), the modal is shown so the user's confirmation click supplies the gesture the browser requires to open a new tab.
56If your app sends `ui/open-link` without a preceding gesture, for example programmatically, on a timer, or after the browser's activation window has expired, Claude shows the modal so the user's confirmation click supplies the gesture the browser requires to open a new tab.
5757
5858<Note>
59 A bypassed `ui/open-link` request resolves successfully once the open is attempted; it does not indicate whether the browser actually opened the tab. Do not treat the response as confirmation that the user reached the destination.
59 A bypassed `ui/open-link` request resolves successfully once the open is attempted. It doesn't indicate whether the browser actually opened the tab, so don't treat the response as confirmation that the user reached the destination.
6060</Note>
6161
6262## Design for the modal
6363
64Even with an allowlist configured, your app should remain usable when the modal appears:
64Even with an allowlist configured, the modal still appears in some cases, so your app should remain usable when it does:
6565
66* Custom and local connectors always show the modal. Your app may run outside the directory during development or in self-hosted deployments.
67* Destinations not on your allowlist, or added since your last published directory update, show the modal.
68* Requests without user activation show the modal.
66* Custom and local connectors always show the modal, and your app may run outside the directory during development or in self-hosted deployments
67* Destinations not on your allowlist, or added since your last published directory update, show the modal
68* Requests without user activation show the modal
6969
7070Provide enough context in your UI that the destination URL shown in the modal is recognizable to the user.
71
72## Next steps
73
74* [Submit a connector](/docs/connectors/building/submission#allowed-link-uris): where the **Allowed link URIs** field fits in your directory submission
75* [Design guidelines](/docs/connectors/building/mcp-apps/design-guidelines#interaction-patterns): which interactions belong in your app and which belong in chat
76* [Troubleshoot MCP Apps](/docs/connectors/building/mcp-apps/troubleshooting): developer tools for inspecting your app's requests
7177
No line in this hunk matches that.