-
Notifications
You must be signed in to change notification settings - Fork 3
feat: add MCP tools and complete product feed spec #8
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: main
Are you sure you want to change the base?
Conversation
Add Model Context Protocol (MCP) tool primitives to enable merchants to sell on ChatGPT Apps without waiting for ACP approval. Also complete the OpenAI product feed specification with all 70+ fields. ## MCP Tools (acp-handler/mcp) - Export flat tool definitions with Zod schemas for 7 commerce operations - Provide handler factory that calls existing acp-handler endpoints - Tools: searchProducts, getProduct, createCheckout, updateCheckout, completeCheckout, cancelCheckout, getCheckout - Clean API: users compose tools with any MCP server framework - Fully customizable: override descriptions, add UI templates, custom handlers ## Product Feed Enhancements (acp-handler/feed) - Add missing fields from OpenAI spec: physical properties, availability, merchant info, performance signals, compliance, reviews, geo-tagging - Total coverage: 70+ fields across all categories - Includes unit pricing, pickup methods, 3D models, region-specific pricing/availability - Format improvements for enums and nested objects ## Benefits - Merchants can sell on ChatGPT TODAY via MCP apps - When ACP approved, add product feed for discovery - Both systems use same checkout logic (no duplication) - Low-level primitives allow maximum flexibility 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
packages/sdk/src/mcp/handlers.ts
Outdated
| updateCheckout: async (input: any): Promise<unknown> => { | ||
| const { session_id, ...body } = input; | ||
| return request(`/api/checkout/${session_id}`, { | ||
| method: "PATCH", |
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.
The updateCheckout handler uses HTTP method PATCH, but the ACP API expects POST for checkout updates. This will cause update operations to fail.
View Details
📝 Patch Details
diff --git a/packages/sdk/src/mcp/handlers.ts b/packages/sdk/src/mcp/handlers.ts
index f2b8b76..3e369e2 100644
--- a/packages/sdk/src/mcp/handlers.ts
+++ b/packages/sdk/src/mcp/handlers.ts
@@ -111,7 +111,7 @@ export function createHandlers(config: HandlerConfig) {
updateCheckout: async (input: any): Promise<unknown> => {
const { session_id, ...body } = input;
return request(`/api/checkout/${session_id}`, {
- method: "PATCH",
+ method: "POST",
body: JSON.stringify(body),
});
},
diff --git a/packages/sdk/src/mcp/tools.ts b/packages/sdk/src/mcp/tools.ts
index 7b1086f..8abded1 100644
--- a/packages/sdk/src/mcp/tools.ts
+++ b/packages/sdk/src/mcp/tools.ts
@@ -97,7 +97,7 @@ export const createCheckout: MCPToolDefinition = {
/**
* Update an existing checkout session
*
- * Maps to: PATCH /api/checkout/:id
+ * Maps to: POST /api/checkout/:id
*/
export const updateCheckout: MCPToolDefinition = {
description:
Analysis
HTTP method mismatch in updateCheckout handler causes 405 Method Not Allowed errors
What fails: The updateCheckout handler in packages/sdk/src/mcp/handlers.ts line 114 uses method: "PATCH" to call /api/checkout/:id, but the API route only exports { GET, POST } methods.
How to reproduce:
- Call the MCP
updateCheckouthandler with a session ID - Handler sends PATCH request to
/api/checkout/{session_id} - Next.js route handler (created by
createNextCatchAll) only exports GET and POST
Result: Next.js returns 405 Method Not Allowed per Next.js route handler documentation, causing all checkout update operations to fail.
Expected: Should use POST method, matching the API implementation in packages/sdk/src/next/index.ts (lines 66-72) which routes POST /:id requests to the update handler, and consistent with the example implementation in examples/chat-sdk/lib/ai/tools/update-checkout.ts line 75 which explicitly uses POST.
Fixed:
- Changed
method: "PATCH"tomethod: "POST"inpackages/sdk/src/mcp/handlers.tsline 114 - Updated comment from
PATCH /api/checkout/:idtoPOST /api/checkout/:idinpackages/sdk/src/mcp/tools.tsline 100
Changed from PATCH to POST to match the actual ACP handler implementation which uses POST for checkout updates.
Add smart payment handling in completeCheckout that returns a checkout URL when no payment token is provided (MCP context), while still supporting full payment processing when a delegated token is available (ACP context). Changes: - Add checkoutUrlPattern and getCheckoutUrl to HandlerConfig - Update completeCheckout handler to detect missing payment token - Return checkout_url for MCP, process payment for ACP - Update tool description and schema to document dual behavior - Add comprehensive examples showing checkout URL configuration This allows merchants to implement the ACP protocol once and use it for both full ACP (with payments) and MCP (checkout redirect). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
| createCheckout: async (input: any): Promise<unknown> => { | ||
| return request("/api/checkout", { | ||
| method: "POST", | ||
| body: JSON.stringify(input), | ||
| }); | ||
| }, | ||
|
|
||
| /** | ||
| * Update an existing checkout session | ||
| */ | ||
| updateCheckout: async (input: any): Promise<unknown> => { | ||
| const { session_id, ...body } = input; | ||
| return request(`/api/checkout/${session_id}`, { | ||
| method: "POST", | ||
| body: JSON.stringify(body), | ||
| }); |
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.
The MCP handlers send data directly to ACP endpoints without transforming the schema, causing validation failures. The MCP tool schemas use different field names than the ACP API expects (e.g., customer.email/name vs customer.billing_address/shipping_address, fulfillment.selected_option_id vs fulfillment.selected_id, payment.token vs payment.delegated_token).
View Details
📝 Patch Details
diff --git a/packages/sdk/src/mcp/handlers.ts b/packages/sdk/src/mcp/handlers.ts
index 7e77098..0f67057 100644
--- a/packages/sdk/src/mcp/handlers.ts
+++ b/packages/sdk/src/mcp/handlers.ts
@@ -40,6 +40,88 @@ export interface HandlerConfig {
getCheckoutUrl?: (sessionId: string) => string;
}
+/**
+ * Transform MCP data to ACP schema
+ * MCP has customer: { email, name } and fulfillment: { address }
+ * ACP has customer: { shipping_address: Address }
+ * We need to merge the customer info with the fulfillment address
+ */
+function transformToACP(input: {
+ customer?: { email?: string; name?: string };
+ fulfillment?: {
+ selected_option_id?: string;
+ address?: {
+ line1: string;
+ line2?: string;
+ city: string;
+ state?: string;
+ postal_code: string;
+ country: string;
+ };
+ };
+}): {
+ customer?: {
+ shipping_address?: {
+ name?: string;
+ email?: string;
+ line1: string;
+ line2?: string;
+ city: string;
+ region?: string;
+ postal_code: string;
+ country: string;
+ };
+ };
+ fulfillment?: { selected_id?: string };
+} {
+ const result: any = {};
+
+ // Build shipping address from customer info + fulfillment address
+ if (input.fulfillment?.address) {
+ result.customer = {
+ shipping_address: {
+ line1: input.fulfillment.address.line1,
+ ...(input.fulfillment.address.line2 && {
+ line2: input.fulfillment.address.line2,
+ }),
+ city: input.fulfillment.address.city,
+ ...(input.fulfillment.address.state && {
+ region: input.fulfillment.address.state,
+ }),
+ postal_code: input.fulfillment.address.postal_code,
+ country: input.fulfillment.address.country,
+ ...(input.customer?.name && { name: input.customer.name }),
+ ...(input.customer?.email && { email: input.customer.email }),
+ },
+ };
+ }
+
+ // Transform fulfillment selected_option_id to selected_id
+ if (input.fulfillment?.selected_option_id) {
+ result.fulfillment = {
+ selected_id: input.fulfillment.selected_option_id,
+ };
+ }
+
+ return result;
+}
+
+/**
+ * Transform MCP payment data to ACP schema
+ * MCP: { method, token } → ACP: { method, delegated_token }
+ */
+function transformPayment(mcpPayment: {
+ method?: string;
+ token?: string;
+}): { method?: string; delegated_token?: string } | undefined {
+ if (!mcpPayment) return undefined;
+
+ return {
+ ...(mcpPayment.method && { method: mcpPayment.method }),
+ ...(mcpPayment.token && { delegated_token: mcpPayment.token }),
+ };
+}
+
/**
* Create handlers that call your acp-handler API endpoints
*
@@ -121,9 +203,16 @@ export function createHandlers(config: HandlerConfig) {
* Create a new checkout session
*/
createCheckout: async (input: any): Promise<unknown> => {
+ // Transform MCP schema to ACP schema
+ const transformed = transformToACP(input);
+ const acpBody: any = {
+ items: input.items,
+ ...transformed,
+ };
+
return request("/api/checkout", {
method: "POST",
- body: JSON.stringify(input),
+ body: JSON.stringify(acpBody),
});
},
@@ -131,10 +220,18 @@ export function createHandlers(config: HandlerConfig) {
* Update an existing checkout session
*/
updateCheckout: async (input: any): Promise<unknown> => {
- const { session_id, ...body } = input;
+ const { session_id, ...mcpBody } = input;
+
+ // Transform MCP schema to ACP schema
+ const transformed = transformToACP(mcpBody);
+ const acpBody: any = {
+ ...(mcpBody.items && { items: mcpBody.items }),
+ ...transformed,
+ };
+
return request(`/api/checkout/${session_id}`, {
method: "POST",
- body: JSON.stringify(body),
+ body: JSON.stringify(acpBody),
});
},
@@ -173,9 +270,14 @@ export function createHandlers(config: HandlerConfig) {
// ACP context: payment token provided
// Process payment through ACP complete endpoint
+ // Transform MCP schema to ACP schema
+ const acpBody: any = {
+ ...(payment && { payment: transformPayment(payment) }),
+ };
+
return request(`/api/checkout/${session_id}/complete`, {
method: "POST",
- body: JSON.stringify({ customer, payment }),
+ body: JSON.stringify(acpBody),
});
},
Analysis
Schema mismatch between MCP handlers and ACP API prevents checkout completion
What fails: MCP handlers in packages/sdk/src/mcp/handlers.ts (createCheckout, updateCheckout, completeCheckout) send data in MCP tool schema format that gets silently stripped by ACP schema validation, preventing checkouts from becoming ready for payment.
How to reproduce:
- Use MCP createCheckout tool with customer and fulfillment data:
{
customer: { email: "[email protected]", name: "John Doe" },
fulfillment: {
selected_option_id: "express",
address: { line1: "123 Main", city: "Seattle", postal_code: "98101", country: "US" }
}
}- MCP handler sends this directly to ACP API at
/api/checkout - ACP validates with
CreateCheckoutSessionSchemawhich expectscustomer.shipping_addressandfulfillment.selected_id - Zod strips unknown fields (
email,name,selected_option_id,address) and passes empty objects
Result: ACP handler receives customer: {} and fulfillment: {}. The products.price() function checks !!customer?.shipping_address and !!fulfillment?.selected_id (see examples/chat-sdk/lib/store/products.ts:105-108), both evaluate to false, so checkout status remains "not_ready_for_payment" forever. Same issue with completeCheckout: payment.token gets stripped instead of being mapped to payment.delegated_token.
Expected: MCP handlers should transform data to match ACP schema before sending requests, mapping customer.email/name + fulfillment.address → customer.shipping_address, fulfillment.selected_option_id → fulfillment.selected_id, and payment.token → payment.delegated_token.
Fix: Added transformation functions transformToACP() and transformPayment() in handlers.ts that convert MCP schema to ACP schema before API requests.
Remove checkoutUrlPattern in favor of just getCheckoutUrl function. Cleaner API with one clear way to customize checkout URLs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Add Model Context Protocol (MCP) tool primitives to enable merchants to sell on ChatGPT Apps without waiting for ACP approval. Also complete the OpenAI product feed specification with all 70+ fields.
MCP Tools (acp-handler/mcp)
Product Feed Enhancements (acp-handler/feed)
Benefits
🤖 Generated with Claude Code
Co-Authored-By: Claude [email protected]