Skip to content

Commit 3f9e68f

Browse files
authored
Merge remote-tracking branch 'origin/main' into mcp-ui-apps-advanced
# Conflicts: # ui/src/apps/issue-write/App.tsx # ui/src/apps/pr-write/App.tsx
2 parents 064250e + 2a5d38a commit 3f9e68f

3 files changed

Lines changed: 30 additions & 4 deletions

File tree

ui/src/apps/issue-write/App.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,15 @@ function SuccessView({
8383
submittedTitle,
8484
submittedLabels,
8585
isUpdate,
86+
openLink,
8687
}: {
8788
issue: IssueResult;
8889
owner: string;
8990
repo: string;
9091
submittedTitle: string;
9192
submittedLabels: LabelItem[];
9293
isUpdate: boolean;
94+
openLink: (url: string) => Promise<void>;
9395
}) {
9496
const issueUrl = issue.html_url || issue.url || issue.URL || "#";
9597

@@ -138,6 +140,14 @@ function SuccessView({
138140
href={issueUrl}
139141
target="_blank"
140142
rel="noopener noreferrer"
143+
onClick={(e) => {
144+
// MCP Apps run in a sandboxed iframe where a plain anchor may be
145+
// blocked, so route the click through the host's open-link
146+
// capability (falls back to window.open).
147+
e.preventDefault();
148+
if (issueUrl === "#") return;
149+
void openLink(issueUrl);
150+
}}
141151
style={{
142152
fontWeight: 600,
143153
fontSize: "14px",
@@ -216,7 +226,7 @@ function CreateIssueApp() {
216226
const [repoSearchLoading, setRepoSearchLoading] = useState(false);
217227
const [repoFilter, setRepoFilter] = useState("");
218228

219-
const { app, error: appError, toolInput, callTool, hostContext, setModelContext } = useMcpApp({
229+
const { app, error: appError, toolInput, callTool, hostContext, setModelContext, openLink } = useMcpApp({
220230
appName: "github-mcp-server-issue-write",
221231
});
222232

@@ -764,6 +774,7 @@ function CreateIssueApp() {
764774
submittedTitle={title}
765775
submittedLabels={selectedLabels}
766776
isUpdate={isUpdateMode}
777+
openLink={openLink}
767778
/>
768779
);
769780
}

ui/src/apps/pr-write/App.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ function SuccessView({
5252
owner,
5353
repo,
5454
submittedTitle,
55+
openLink,
5556
}: {
5657
pr: PRResult;
5758
owner: string;
5859
repo: string;
5960
submittedTitle: string;
61+
openLink: (url: string) => Promise<void>;
6062
}) {
6163
const prUrl = pr.html_url || pr.url || pr.URL || "#";
6264

@@ -105,6 +107,14 @@ function SuccessView({
105107
href={prUrl}
106108
target="_blank"
107109
rel="noopener noreferrer"
110+
onClick={(e) => {
111+
// MCP Apps run in a sandboxed iframe where a plain anchor may be
112+
// blocked, so route the click through the host's open-link
113+
// capability (falls back to window.open).
114+
e.preventDefault();
115+
if (prUrl === "#") return;
116+
void openLink(prUrl);
117+
}}
108118
style={{
109119
fontWeight: 600,
110120
fontSize: "14px",
@@ -157,7 +167,7 @@ function CreatePRApp() {
157167
const [repoSearchLoading, setRepoSearchLoading] = useState(false);
158168
const [repoFilter, setRepoFilter] = useState("");
159169

160-
const { app, error: appError, toolInput, callTool, hostContext, setModelContext } = useMcpApp({
170+
const { app, error: appError, toolInput, callTool, hostContext, setModelContext, openLink } = useMcpApp({
161171
appName: "github-mcp-server-create-pull-request",
162172
});
163173

@@ -352,7 +362,7 @@ function CreatePRApp() {
352362
if (successPR) {
353363
return (
354364
<AppProvider hostContext={hostContext}>
355-
<SuccessView pr={successPR} owner={owner} repo={repo} submittedTitle={submittedTitle} />
365+
<SuccessView pr={successPR} owner={owner} repo={repo} submittedTitle={submittedTitle} openLink={openLink} />
356366
</AppProvider>
357367
);
358368
}

ui/src/hooks/useMcpApp.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,12 @@ export function useMcpApp({
106106
window.open(url, "_blank", "noopener,noreferrer");
107107
return;
108108
}
109-
await app.openLink({ url });
109+
const result = await app.openLink({ url });
110+
// The host may deny the request (e.g. blocked domain or user cancelled).
111+
// Fall back to a direct window.open so the link still works.
112+
if (result?.isError) {
113+
window.open(url, "_blank", "noopener,noreferrer");
114+
}
110115
},
111116
[app]
112117
);

0 commit comments

Comments
 (0)