mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-26 06:04:08 +00:00
fix(mcp): preserve confirmation compatibility across clients
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
parent
d700fc1fad
commit
d797bf249f
5 changed files with 9 additions and 15 deletions
|
|
@ -70,7 +70,6 @@ function resourceMetadata(c: Context<{ Bindings: Bindings }>) {
|
|||
const canonicalMetadataUrl = new URL(
|
||||
protectedResourceMetadataUrl(mcpResource),
|
||||
)
|
||||
// MCP clients also try the root well-known location as a compatibility fallback.
|
||||
const isMcpRootFallback =
|
||||
requestUrl.pathname === PROTECTED_RESOURCE_METADATA_PATH &&
|
||||
requestUrl.search === ""
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
export const PROTECTED_RESOURCE_METADATA_PATH =
|
||||
"/.well-known/oauth-protected-resource"
|
||||
|
||||
/**
|
||||
* Builds the protected-resource metadata URL advertised to OAuth clients.
|
||||
*
|
||||
* The canonical resource is the single source of truth so the advertised URL
|
||||
* and the metadata document cannot disagree. Forwarded headers are intentionally
|
||||
* excluded because they are request-controlled in some proxy deployments.
|
||||
*/
|
||||
// Discovery must use the configured resource, never request-controlled forwarding headers.
|
||||
export function protectedResourceMetadataUrl(resource: string): string {
|
||||
const resourceUrl = new URL(resource)
|
||||
const resourcePath = resourceUrl.pathname === "/" ? "" : resourceUrl.pathname
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import { forgetMemory } from "./forget-memory"
|
|||
import { addMemoryOutputSchema, type AddMemoryOutput } from "./output-schemas"
|
||||
import { textContent, type ToolDeps } from "./types"
|
||||
|
||||
// v5 metadata values are flat: string, number, boolean or string list.
|
||||
export const memoryMetadataSchema = z
|
||||
.record(
|
||||
z.string(),
|
||||
|
|
|
|||
|
|
@ -28,15 +28,18 @@ export interface ForgetMemoryArgs {
|
|||
containerTag?: string
|
||||
}
|
||||
|
||||
// Only 2026-era requests carry capabilities per request; stateless legacy requests never saw initialize, so they use the confirm flag instead.
|
||||
// Stateless legacy requests cannot receive elicitation, so they use explicit two-call confirmation.
|
||||
function canElicit(context: ServerContext | undefined): boolean {
|
||||
const envelope = context?.mcpReq?.envelope
|
||||
if (!envelope) return false
|
||||
const capabilities = Reflect.get(envelope, CLIENT_CAPABILITIES_META_KEY)
|
||||
if (!capabilities || typeof capabilities !== "object") return false
|
||||
const elicitation = Reflect.get(capabilities, "elicitation")
|
||||
return Boolean(
|
||||
capabilities &&
|
||||
typeof capabilities === "object" &&
|
||||
Reflect.get(capabilities, "elicitation"),
|
||||
elicitation &&
|
||||
typeof elicitation === "object" &&
|
||||
(Reflect.get(elicitation, "form") !== undefined ||
|
||||
Reflect.get(elicitation, "url") === undefined),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +63,6 @@ function askToConfirm(
|
|||
},
|
||||
}),
|
||||
},
|
||||
// Only compared against the server-resolved ID, so a tampered value can just trigger a re-ask.
|
||||
requestState: candidate.id,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ export const addMemoryOutputSchema = z.object({
|
|||
message: z.string(),
|
||||
id: z.string().optional(),
|
||||
status: z.string().optional(),
|
||||
memory: z.string().optional(),
|
||||
})
|
||||
|
||||
export type AddMemoryOutput = z.infer<typeof addMemoryOutputSchema>
|
||||
|
||||
export const forgetMemoryOutputSchema = addMemoryOutputSchema.extend({
|
||||
action: z.literal("forget"),
|
||||
memory: z.string().optional(),
|
||||
})
|
||||
|
||||
export const getDocumentOutputSchema = z.object({
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue