-
Notifications
You must be signed in to change notification settings - Fork 2.4k
fix(mcp): return graph view message from fetch-graph-data #1195
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
base: graphite-base/1195
Are you sure you want to change the base?
Changes from all commits
276d354
f52da0f
521db2b
9d80b75
ddfc4cd
35fb9af
1f74da1
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 |
|---|---|---|
| @@ -1,21 +1,30 @@ | ||
| import type { ToolDeps } from "./types" | ||
|
|
||
| export type ContainerTagValidation = "exists" | "missing" | "unavailable" | ||
|
|
||
| /** | ||
| * Checks whether a container tag actually exists for this user. | ||
| * Fast path is the cached tag list from init; on a miss we re-fetch once so | ||
| * tags created after the session started are not falsely rejected. | ||
| */ | ||
| export async function containerTagExists( | ||
| export async function validateContainerTag( | ||
| deps: ToolDeps, | ||
| containerTag: string, | ||
| ): Promise<boolean> { | ||
| if (deps.cachedContainerTags().includes(containerTag)) return true | ||
| await deps.refreshContainerTags() | ||
| ): Promise<ContainerTagValidation> { | ||
| if (deps.cachedContainerTags().includes(containerTag)) return "exists" | ||
| const refreshed = await deps.refreshContainerTags() | ||
| if (!refreshed) return "unavailable" | ||
| return deps.cachedContainerTags().includes(containerTag) | ||
| ? "exists" | ||
| : "missing" | ||
| } | ||
|
|
||
| export function unknownContainerTagError(containerTag: string): Error { | ||
| return new Error( | ||
| `Container tag '${containerTag}' does not exist. Use listSpaces to see the available container tags.`, | ||
| ) | ||
| } | ||
|
|
||
| export function containerTagValidationUnavailableError(): Error { | ||
| return new Error("Could not verify workspace. Please try again.") | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,6 +14,11 @@ function safeLog( | |||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| function toolResultText(result: CallToolResult): string | null { | ||||||||||||||||||||||||||||||||||||||||||||||||
| const item = result.content?.[0] | ||||||||||||||||||||||||||||||||||||||||||||||||
| return item?.type === "text" ? item.text : null | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| type ViewState = | ||||||||||||||||||||||||||||||||||||||||||||||||
| | { kind: "loading" } | ||||||||||||||||||||||||||||||||||||||||||||||||
| | { kind: "view"; message: ViewMessage } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -54,8 +59,20 @@ export function useViewState(): { | |||||||||||||||||||||||||||||||||||||||||||||||
| setState({ kind: "loading" }) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| app.ontoolresult = (result: CallToolResult) => { | ||||||||||||||||||||||||||||||||||||||||||||||||
| if (result.isError) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| const text = toolResultText(result) ?? "Tool returned an error" | ||||||||||||||||||||||||||||||||||||||||||||||||
| safeLog("error", `[host] ontoolresult: tool error: ${text}`) | ||||||||||||||||||||||||||||||||||||||||||||||||
| setState({ kind: "error", message: text }) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+62
to
+67
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. When The early return means the structured error view with its Fix: Check for structured error content first before falling back to text: if (result.isError) {
const sc = (result as { structuredContent?: unknown }).structuredContent
if (sc && typeof sc === "object" && "view" in sc && sc.view === "error") {
// Use structured error content if available
if (isViewMessage(sc)) {
safeLog("info", `[host] ontoolresult: structured error view`)
setState({ kind: "view", message: sc })
return
}
}
const text = toolResultText(result) ?? "Tool returned an error"
safeLog("error", `[host] ontoolresult: tool error: ${text}`)
setState({ kind: "error", message: text })
return
}
Suggested change
Spotted by Graphite |
||||||||||||||||||||||||||||||||||||||||||||||||
| const sc = (result as { structuredContent?: unknown }).structuredContent | ||||||||||||||||||||||||||||||||||||||||||||||||
| if (!sc || typeof sc !== "object") { | ||||||||||||||||||||||||||||||||||||||||||||||||
| const text = toolResultText(result) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if (text) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| safeLog("warning", `[host] ontoolresult: text-only result: ${text}`) | ||||||||||||||||||||||||||||||||||||||||||||||||
| setState({ kind: "error", message: text }) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| safeLog("warning", "[host] ontoolresult: no structuredContent") | ||||||||||||||||||||||||||||||||||||||||||||||||
| setState({ kind: "raw", structuredContent: sc }) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
structuredContent: scdrops the existingpaginationobject even thoughfetch-graph-datastill acceptspageandlimit. Existing paginated callers losecurrentPage,limit, andtotalPages, andapps/mcp/e2e/graph.test.tsstill expectsres.structuredContent.pagination.limitto be5. Please keep theview: "graph"widget shape, but preserve pagination metadata instructuredContentor update the contract and all affected tests/callers together.