Repository navigation
fix(mcp-apps): reconcile the show/defer contract — render results, remove show_ui #2774
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
d0ecf75
09a7a78
e2207ac
8b2363d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,7 @@ import { | |
| } from "@primer/octicons-react"; | ||
| import { AppProvider } from "../../components/AppProvider"; | ||
| import { useMcpApp } from "../../hooks/useMcpApp"; | ||
| import { completedToolResult } from "../../lib/toolResult"; | ||
| import { MarkdownEditor } from "../../components/MarkdownEditor"; | ||
|
|
||
| interface PRResult { | ||
|
|
@@ -267,14 +268,23 @@ function EditPRApp() { | |
| const [reviewersLoading, setReviewersLoading] = useState(false); | ||
| const [reviewersFilter, setReviewersFilter] = useState(""); | ||
|
|
||
| const { app, error: appError, toolInput, callTool, hostContext, setModelContext, openLink } = useMcpApp({ | ||
| const { app, error: appError, toolInput, toolResult, callTool, hostContext, setModelContext, openLink } = useMcpApp({ | ||
| appName: "github-mcp-server-edit-pull-request", | ||
| }); | ||
|
|
||
| const owner = (toolInput?.owner as string) || ""; | ||
| const repo = (toolInput?.repo as string) || ""; | ||
| const pullNumber = asNumber(toolInput?.pullNumber); | ||
|
|
||
| // When the server updated the PR up-front instead of deferring to this form, | ||
| // the host still renders this View and delivers the updated PR via | ||
| // tool-result. Render that completed result as success so we never show an | ||
| // edit form for changes already applied. The deferral sentinel | ||
| // (awaiting_user_submission) returns null here, keeping the form for the | ||
| // normal deferred flow. See github/copilot-mcp-core#1864. | ||
| const resultPR = useMemo(() => completedToolResult<PRResult>(toolResult), [toolResult]); | ||
| const shownPR = successPR ?? resultPR; | ||
|
|
||
|
Comment on lines
+279
to
+287
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch — fixed in 09a7a78. Rather than key the parsed result per app, I scoped it centrally in |
||
| useEffect(() => { | ||
| setTitle(""); | ||
| setBody(""); | ||
|
|
@@ -495,10 +505,10 @@ function EditPRApp() { | |
| } | ||
| }, [title, body, owner, repo, pullNumber, baseBranch, initialValues, selectedReviewers, prState, isDraft, maintainerCanModify, callTool, setModelContext]); | ||
|
|
||
| if (successPR) { | ||
| if (shownPR) { | ||
| return ( | ||
| <AppProvider hostContext={hostContext}> | ||
| <SuccessView pr={successPR} owner={owner} repo={repo} submittedTitle={submittedTitle} openLink={openLink} /> | ||
| <SuccessView pr={shownPR} owner={owner} repo={repo} submittedTitle={submittedTitle} openLink={openLink} /> | ||
| </AppProvider> | ||
| ); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; | ||
|
|
||
| /** | ||
| * Returns the parsed JSON payload of a *completed* tool result, or `null` when | ||
| * the result is absent, an error, or the deferral sentinel (structured | ||
| * `status` of `"awaiting_user_submission"`, set by the server's | ||
| * `NewToolResultAwaitingFormSubmission`). | ||
| * | ||
| * Form-backed write Views (create_pull_request, issue_write, | ||
| * update_pull_request) use this to tell apart "the server deferred and is | ||
| * waiting for my form submission" from "the server already executed". Without | ||
| * it, a View renders its input form off in-app state alone and will show e.g. a | ||
| * "Create pull request" form for a PR that was already created up-front (when | ||
| * the agent passed `show_ui=false` or parameters the form can't represent). | ||
| * | ||
| * See github/copilot-mcp-core#1864 for the full show/defer state machine. | ||
| */ | ||
| export function completedToolResult<T = Record<string, unknown>>( | ||
| result: CallToolResult | null, | ||
| ): T | null { | ||
| if (!result || result.isError) return null; | ||
|
|
||
| const status = (result.structuredContent as { status?: string } | undefined) | ||
| ?.status; | ||
| if (status === "awaiting_user_submission") return null; | ||
|
|
||
| const textContent = result.content?.find((c) => c.type === "text"); | ||
| if (!textContent || textContent.type !== "text" || !textContent.text) { | ||
| return null; | ||
| } | ||
|
|
||
| try { | ||
| return JSON.parse(textContent.text) as T; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.