-
Notifications
You must be signed in to change notification settings - Fork 0
Add initial implementation for VTEX MCP app #31
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
WalkthroughThis PR adds a comprehensive VTEX e-commerce integration package to the monorepo. It includes a typed VTEX API client, data transformation utilities, tool factories for product search and cart management, Zod-based schemas for validation, configuration files, tests, and documentation. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🚀 Preview Deployments Ready!Deployed from commit: |
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.
Actionable comments posted: 8
♻️ Duplicate comments (1)
vtex/server/tools/cart.ts (1)
24-30: Duplicate ofgetStatefrom products.ts.As noted in the products.ts review, this should be extracted to a shared module.
🧹 Nitpick comments (10)
vtex/tsconfig.json (2)
13-13: Consider enablingverbatimModuleSyntaxfor better ESM compliance.Setting
verbatimModuleSyntax: falsecan lead to issues with import/export handling. The recommended practice is to set this totruefor modern ESM projects to ensure type-only imports are properly distinguished from value imports.Apply this diff if there are no specific reasons to disable it:
- "verbatimModuleSyntax": false, + "verbatimModuleSyntax": true,
16-16: Verify ifallowJsis needed.The
allowJsoption is enabled, but the codebase appears to use TypeScript exclusively. Unless there are JavaScript files that need to be included, this option can be removed for stricter type safety.vtex/wrangler.toml (1)
15-15: Consider hosting the icon asset.The icon URL points to Wikimedia Commons (
https://upload.wikimedia.org/wikipedia/commons/a/a9/VTEX_Logo.svg), which could change or become unavailable. Consider hosting the icon in a more reliable location (e.g., in your own assets or CDN) for better long-term stability.vtex/server/tools/products.ts (2)
24-32: DuplicategetStatefunction - extract to shared utility.This
getStatefunction is duplicated incart.ts(lines 24-30). Extract it to a shared module (e.g.,../lib/utils.tsor./helpers.ts) to follow DRY principles.// In a new file: ../lib/state.ts import type { Env } from "../main.ts"; export function getState(env: Env) { const state = env.DECO_REQUEST_CONTEXT?.state; if (!state?.account) { throw new Error("VTEX account not configured. Please configure the MCP with your VTEX account."); } return state; }
43-70: Consider adding error handling for API failures.The
executefunction doesn't wrap the client calls in try-catch. Network failures or VTEX API errors will propagate as unhandled exceptions. Depending on howcreatePrivateToolhandles errors, you may want to add explicit error handling with user-friendly messages.vtex/server/lib/vtex-client.ts (3)
1-116: Client configuration: unusedsalesChanneland formatting check
ClientConfig.salesChannelis never used insidecreateClient, which is mildly confusing given sales channel affects catalog/checkout behavior. Either remove it from the config/state mapping or thread it through to relevant calls (typically as anscquery param on cart mutations and possibly search) so the configured trade policy actually takes effect.(community.vtex.com)Also, CI is failing with
oxfmt --checkon this file; please runoxfmtto reformat it before merging.
160-170:getProductBySlugshould distinguish 404 from other HTTP errorsRight now any non‑OK status returns
null, so upstream code cannot tell “product not found” apart from server errors, auth issues, or misconfiguration:if (!response.ok) { return null; }Consider only returning
nullfor 404 and throwing for other error codes to avoid silently hiding infrastructure/API problems:- const response = await fetch(url, { headers }); - if (!response.ok) { - return null; - } + const response = await fetch(url, { headers }); + if (response.status === 404) { + return null; + } + if (!response.ok) { + throw new Error(`VTEX getProductBySlug failed: ${response.status}`); + }
215-260: Cart mutation endpoints align with Checkout, consider optional query params and call serialization
addToCartandupdateCartItemscorrectly POST to:
.../orderForm/{orderFormId}/itemswith anorderItemsarray containingid,seller,quantity.(developers.vtex.com).../orderForm/{orderFormId}/items/updatewith anorderItemsarray containingindex,quantity.(developers.vtex.com)This matches the documented body shapes and is a good minimal implementation.
Two optional improvements you may want later:
- Thread
salesChannelinto these URLs as?sc=...for multi‑trade‑policy setups, instead of assuming the default sales channel.(community.vtex.com)- Expose
allowedOutdatedDataas a configurable query param to let callers opt in to performance vs. freshness trade‑offs when updating/adding items.(developers.vtex.com)Also note that VTEX recommends not performing data‑modifying Checkout calls in parallel; callers of this client should serialize cart mutations to avoid race conditions.(plataformas-digitais.apidog.io)
vtex/server/tools/schemas.ts (2)
1-72: Common product/cart schemas are sensible; minor tightening possible
productSchema,cartItemSchema, andcartSchemalook coherent with the “standard format” you’re targeting and provide good structure for validation and tool metadata.If you later need stricter typing around variants, you could replace
hasVariant: z.array(z.any())with a dedicated variant schema, but it’s fine to keep it loose for now. Also, remember to runoxfmthere as well to fix the formatting check failure.
77-110: UseproductSchemainstead ofz.anyin product tool outputs
searchProductsOutputSchema.productsandgetProductOutputSchema.productcurrently allow any shape, even though you already have a detailedproductSchema. Tightening these will give you better runtime guarantees and clearer tool documentation:export const searchProductsOutputSchema = z.object({ - products: z.array(z.any()).describe("Array of products in standard format"), + products: z.array(productSchema).describe("Array of products in standard format"), total: z.number().describe("Total number of products matching the query"), page: z.number().describe("Current page number"), pageSize: z.number().describe("Number of products per page"), }); export const getProductOutputSchema = z.object({ - product: z.any().nullable().describe("Product in standard format, or null if not found"), + product: productSchema.nullable().describe("Product in standard format, or null if not found"), });This should be a drop‑in change as long as your
toProducttransform already outputs the same structure.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (15)
package.json(1 hunks)vtex/README.md(1 hunks)vtex/package.json(1 hunks)vtex/server/lib/client.test.ts(1 hunks)vtex/server/lib/transform.ts(1 hunks)vtex/server/lib/vtex-client.ts(1 hunks)vtex/server/main.ts(1 hunks)vtex/server/tools/cart.ts(1 hunks)vtex/server/tools/index.ts(1 hunks)vtex/server/tools/products.ts(1 hunks)vtex/server/tools/schemas.ts(1 hunks)vtex/shared/deco.gen.ts(1 hunks)vtex/tsconfig.json(1 hunks)vtex/vite.config.ts(1 hunks)vtex/wrangler.toml(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
vtex/server/tools/index.ts (2)
vtex/server/tools/products.ts (1)
productTools(123-127)vtex/server/tools/cart.ts (1)
cartTools(99-103)
vtex/vite.config.ts (1)
shared/deco-vite-plugin.ts (1)
deco(89-146)
vtex/server/tools/cart.ts (4)
vtex/server/main.ts (1)
Env(29-34)vtex/shared/deco.gen.ts (1)
Env(37-42)vtex/server/tools/schemas.ts (4)
getCartInputSchema(130-132)cartOutputSchema(126-128)addToCartInputSchema(134-141)updateCartInputSchema(143-149)vtex/server/lib/vtex-client.ts (1)
getClientFromState(267-274)
vtex/shared/deco.gen.ts (1)
vtex/server/main.ts (1)
Env(29-34)
vtex/server/lib/vtex-client.ts (1)
vtex/shared/deco.gen.ts (1)
State(35-35)
🪛 GitHub Actions: Checks
vtex/server/lib/client.test.ts
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
vtex/server/main.ts
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
vtex/server/tools/index.ts
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
vtex/vite.config.ts
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
vtex/server/tools/products.ts
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
vtex/server/lib/transform.ts
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
vtex/server/tools/cart.ts
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
vtex/shared/deco.gen.ts
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
vtex/server/lib/vtex-client.ts
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
vtex/server/tools/schemas.ts
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
🔇 Additional comments (26)
package.json (1)
38-39: LGTM! Workspace addition looks correct.The addition of the "vtex" workspace to the monorepo is properly formatted and follows the existing pattern.
vtex/vite.config.ts (1)
8-29: Configuration looks good for Cloudflare Workers deployment.The Vite configuration is properly set up with:
- Cloudflare plugin for Workers support
- Deco plugin integration
- Proper environment variable definitions for Workers context
- Appropriate caching strategy
vtex/shared/deco.gen.ts (1)
8-33: Schema structure is well-designed.The StateSchema provides appropriate VTEX configuration with:
- Required account field (correctly not having a default)
- Sensible defaults for environment, salesChannel, locale, and currency
- Clear descriptions for each field
- Proper Zod validation
vtex/wrangler.toml (2)
4-4: Verify the compatibility date.The compatibility date is set to
2025-06-17. Please confirm this is the intended date for Cloudflare Workers compatibility flags.
1-19: Configuration structure looks good.The Wrangler configuration is properly set up with:
- Correct main entry point
- Node.js compatibility enabled
- Deco workspace integration configured
- Clear integration metadata
- Observability enabled
vtex/server/lib/client.test.ts (3)
9-11: Verify the use of a real VTEX account in tests.The tests use a real VTEX account (
brmotorolanew) for integration testing. Please confirm:
- This is intentional and the account is suitable for testing
- The account will remain accessible for CI/CD environments
- Consider documenting the test account setup in the README or test documentation
13-77: VTEX client tests are well-structured.The test suite comprehensively covers:
- Product search with and without query
- Product retrieval by slug
- Handling of invalid slugs
- Suggestions API
The tests validate both structure and behavior appropriately.
79-197: Transform utility tests are comprehensive.The tests effectively validate:
- SKU selection logic (preferring available SKUs)
- Complete product transformation to schema.org format
- All key fields including pricing, images, variants, and metadata
The test data is realistic and covers important edge cases.
vtex/server/main.ts (1)
36-63: Server configuration is well-structured.The runtime configuration is properly set up with:
- Combined tool sets (user tools + VTEX tools)
- Comprehensive Env typing with proper extensions
- StateSchema for installation-time configuration
- Appropriate fetch fallback for asset serving
The OAuth scopes array is empty (line 42), which appears intentional since VTEX APIs are accessed via account credentials in the state rather than OAuth tokens.
vtex/README.md (1)
1-121: Excellent documentation.The README is comprehensive and well-structured, covering:
- Clear feature descriptions for all tools
- Configuration requirements with helpful defaults table
- Complete development workflow
- API reference with TypeScript interfaces for key data structures
This provides users with everything needed to understand, configure, and use the VTEX MCP integration.
vtex/server/tools/index.ts (1)
1-24: Clean module aggregation pattern.The file provides a well-organized central export point that maintains domain separation while enabling easy imports. The structure is clean and follows good practices.
Minor note: The docstring (line 11) mentions
userToolsfrom@decocms/mcps-shared, but this isn't actually used in the file. Consider removing or updating this reference to avoid confusion.vtex/package.json (2)
22-25: Dependencies look appropriate for the VTEX integration.The runtime dependency and zod for validation are well-chosen. The use of caret versioning for zod allows patch/minor updates while @decocms/runtime is pinned for stability.
34-34: Review comment is incorrect — Vite 7.2.0 is a valid, current stable release.As of November 2025, Vite v7.2.4 is the latest stable release, making v7.2.0 a standard, in-range version. The concern that 7.2.0 is "ahead of typical stable releases (5.x-6.x range)" reflects outdated information; Vite 7.x is the current stable major version. Pinning an exact version (7.2.0 without caret) is a valid practice for reproducibility and does not indicate an error.
vtex/server/lib/transform.ts (3)
118-126: Handle edge case when no sellers exist.If
sku.sellersis empty, bothfindand[0]access will returnundefined, causing optional chaining to work but potentially yielding unexpected defaults (price=0, seller="1"). Consider adding explicit validation or logging for this edge case.
176-194: Offer structure only includes a single seller.The
offersarray is hardcoded to contain only one offer (the selected seller). If multiple sellers exist with different prices/availability, this information is lost. This may be intentional for simplicity, but worth confirming.
209-228: toCart transformation looks correct.The cart transformation properly converts cents to major currency units, handles optional imageUrl with fallback, and extracts subtotal from totalizers. The empty coupons array is appropriate for initial implementation.
vtex/server/tools/products.ts (1)
122-127: Tool exports follow a clean factory pattern.The productTools array export is well-organized and consistent with the cart tools pattern.
vtex/server/tools/cart.ts (4)
35-50: Get cart tool implementation is correct.The tool properly handles optional
orderFormId- creating a new cart when not provided or fetching existing one. The transformation viatoCartensures consistent output format.
55-73: Add-to-cart implementation looks good.The tool correctly passes items array to the client and transforms the result. The schema (from snippets) properly validates the required fields.
78-96: Update cart tool correctly handles item removal.The description clearly documents that setting quantity to 0 removes an item. The implementation delegates to the client's
updateCartItemsmethod appropriately.
98-103: Consistent tool export pattern.The cartTools array follows the same factory pattern as productTools, ensuring consistency across the codebase.
vtex/server/lib/vtex-client.ts (3)
121-155:searchProductsimplementation LGTMQuery parameter handling, pagination defaults, and facets path construction look reasonable and align with how the Intelligent Search product search endpoint is typically used. I don’t see functional issues here.
175-189:getSuggestionsimplementation looks correctThe endpoint, query construction, and error handling are consistent with VTEX Intelligent Search’s suggestions API (locale + query, throwing on non‑OK). No changes needed here.
267-273:getClientFromStatewiring is straightforwardThe mapping from
StatetoClientConfigis direct and consistent with the client’s expectations. AssumingStateSchemadefinesaccount,environment,salesChannel, andlocale, this helper is good to go. Just remember to update it if you later remove or repurposesalesChannelfromClientConfigper the earlier comment.vtex/server/tools/schemas.ts (2)
111-121: Suggestion schemas match VTEX suggestions; ensure transform mapssearches→suggestionsThe output schema:
suggestions: z.array(z.object({ term: z.string(), count: z.number() }))matches the shape returned by VTEX Intelligent Search’s “Get list of suggested terms” endpoint (
searches: [{ term, count }, ...]) once you rename the top‑level property.(developers.vtex.com)Just double‑check that your tool layer converts the VTEX response
{ searches: [...] }into{ suggestions: [...] }before validation so it aligns with this schema.
126-149: Cart tool schemas align with Checkout API and client expectations
getCartInputSchema.orderFormIdbeing optional matchesgetOrderForm(orderFormId?: string)in the client.addToCartInputSchema.itemsmatches the Checkout “Add cart items” request body (orderItemswithid,seller,quantity), and thequantity >= 1constraint is consistent with VTEX docs.(developers.vtex.com)updateCartInputSchema.items(index >= 0,quantity >= 0) lines up with the “Update cart items” endpoint, wherequantity = 0removes an item.(developers.vtex.com)These schemas look well aligned with the VTEX client and Checkout behavior.
| /** | ||
| * VTEX Client Tests | ||
| */ | ||
|
|
||
| import { describe, test, expect, beforeAll } from "bun:test"; | ||
| import { createClient, type VTEXProduct, type ProductSearchResult } from "./vtex-client.ts"; | ||
| import { toProduct, pickSku, type Product } from "./transform.ts"; | ||
|
|
||
| const TEST_ACCOUNT = "brmotorolanew"; | ||
| const TEST_ENVIRONMENT = "vtexcommercestable"; | ||
| const CURRENCY = "BRL"; | ||
|
|
||
| describe("VTEX Client", () => { | ||
| const client = createClient({ | ||
| account: TEST_ACCOUNT, | ||
| environment: TEST_ENVIRONMENT, | ||
| salesChannel: "1", | ||
| locale: "pt-BR", | ||
| }); | ||
|
|
||
| const baseUrl = `https://${TEST_ACCOUNT}.${TEST_ENVIRONMENT}.com.br`; | ||
|
|
||
| test("searchProducts returns products", async () => { | ||
| const result = await client.searchProducts({ | ||
| query: "", | ||
| count: 5, | ||
| }); | ||
|
|
||
| expect(result).toBeDefined(); | ||
| expect(result.products).toBeArray(); | ||
| expect(result.products.length).toBeGreaterThan(0); | ||
| expect(result.recordsFiltered).toBeGreaterThan(0); | ||
|
|
||
| // Check product structure | ||
| const product = result.products[0]; | ||
| expect(product.productId).toBeDefined(); | ||
| expect(product.productName).toBeDefined(); | ||
| expect(product.linkText).toBeDefined(); | ||
| expect(product.items).toBeArray(); | ||
| }); | ||
|
|
||
| test("searchProducts with query returns results", async () => { | ||
| const result = await client.searchProducts({ | ||
| query: "motorola", | ||
| count: 5, | ||
| }); | ||
|
|
||
| // Query might return 0 results depending on API state, just check structure | ||
| expect(result.products).toBeArray(); | ||
| expect(typeof result.recordsFiltered).toBe("number"); | ||
| }); | ||
|
|
||
| test("getProductBySlug returns a product", async () => { | ||
| // First get a product from search to get a valid slug | ||
| const searchResult = await client.searchProducts({ count: 1 }); | ||
| const slug = searchResult.products[0].linkText; | ||
|
|
||
| const product = await client.getProductBySlug(slug); | ||
|
|
||
| expect(product).toBeDefined(); | ||
| expect(product?.productId).toBeDefined(); | ||
| expect(product?.productName).toBeDefined(); | ||
| expect(product?.items).toBeArray(); | ||
| }); | ||
|
|
||
| test("getProductBySlug returns null for invalid slug", async () => { | ||
| const product = await client.getProductBySlug("this-product-does-not-exist-12345"); | ||
| expect(product).toBeNull(); | ||
| }); | ||
|
|
||
| test("getSuggestions returns suggestions", async () => { | ||
| const result = await client.getSuggestions("smart"); | ||
|
|
||
| expect(result).toBeDefined(); | ||
| expect(result.searches).toBeArray(); | ||
| }); | ||
| }); | ||
|
|
||
| describe("VTEX Transform", () => { | ||
| const baseUrl = `https://${TEST_ACCOUNT}.${TEST_ENVIRONMENT}.com.br`; | ||
|
|
||
| test("pickSku returns available SKU", () => { | ||
| const items = [ | ||
| { | ||
| itemId: "123", | ||
| name: "SKU 1", | ||
| nameComplete: "SKU 1 Complete", | ||
| images: [], | ||
| sellers: [ | ||
| { | ||
| sellerId: "1", | ||
| sellerName: "Main Seller", | ||
| commertialOffer: { | ||
| Price: 10000, | ||
| ListPrice: 12000, | ||
| AvailableQuantity: 0, | ||
| PriceWithoutDiscount: 12000, | ||
| Installments: [], | ||
| }, | ||
| }, | ||
| ], | ||
| variations: [], | ||
| }, | ||
| { | ||
| itemId: "456", | ||
| name: "SKU 2", | ||
| nameComplete: "SKU 2 Complete", | ||
| images: [], | ||
| sellers: [ | ||
| { | ||
| sellerId: "1", | ||
| sellerName: "Main Seller", | ||
| commertialOffer: { | ||
| Price: 10000, | ||
| ListPrice: 12000, | ||
| AvailableQuantity: 5, | ||
| PriceWithoutDiscount: 12000, | ||
| Installments: [], | ||
| }, | ||
| }, | ||
| ], | ||
| variations: [], | ||
| }, | ||
| ]; | ||
|
|
||
| const sku = pickSku(items); | ||
| expect(sku.itemId).toBe("456"); // Should pick the available one | ||
| }); | ||
|
|
||
| test("toProduct transforms VTEX product to schema.org format", () => { | ||
| const vtexProduct: VTEXProduct = { | ||
| productId: "123", | ||
| productName: "Test Product", | ||
| brand: "Test Brand", | ||
| brandId: 1, | ||
| description: "Test description", | ||
| linkText: "test-product", | ||
| link: "/test-product/p", | ||
| categories: ["/Category/"], | ||
| categoryId: "1", | ||
| items: [ | ||
| { | ||
| itemId: "456", | ||
| name: "Test SKU", | ||
| nameComplete: "Test SKU Complete", | ||
| images: [ | ||
| { | ||
| imageUrl: "https://example.com/image.jpg", | ||
| imageText: "Image", | ||
| imageLabel: "main", | ||
| }, | ||
| ], | ||
| sellers: [ | ||
| { | ||
| sellerId: "1", | ||
| sellerName: "Main Seller", | ||
| commertialOffer: { | ||
| Price: 10000, | ||
| ListPrice: 12000, | ||
| AvailableQuantity: 5, | ||
| PriceWithoutDiscount: 12000, | ||
| Installments: [ | ||
| { | ||
| Value: 10000, | ||
| NumberOfInstallments: 1, | ||
| PaymentSystemName: "Visa", | ||
| }, | ||
| ], | ||
| }, | ||
| }, | ||
| ], | ||
| variations: [ | ||
| { name: "Color", values: ["Red"] }, | ||
| ], | ||
| }, | ||
| ], | ||
| origin: "intelligent-search", | ||
| }; | ||
|
|
||
| const sku = pickSku(vtexProduct.items); | ||
| const product = toProduct(vtexProduct, sku, baseUrl, CURRENCY); | ||
|
|
||
| expect(product["@type"]).toBe("Product"); | ||
| expect(product.productID).toBe("123"); | ||
| expect(product.name).toBe("Test Product"); | ||
| expect(product.description).toBe("Test description"); | ||
| expect(product.url).toContain("/test-product/p"); | ||
| expect(product.brand?.name).toBe("Test Brand"); | ||
| expect(product.sku).toBe("456"); | ||
| expect(product.offers.lowPrice).toBe(100); // 10000/100 | ||
| expect(product.offers.highPrice).toBe(120); // 12000/100 | ||
| expect(product.offers.priceCurrency).toBe(CURRENCY); | ||
| expect(product.offers.offers[0].availability).toBe("https://schema.org/InStock"); | ||
| expect(product.image.length).toBeGreaterThan(0); | ||
| expect(product.additionalProperty?.length).toBeGreaterThan(0); | ||
| expect(product.isVariantOf?.hasVariant.length).toBeGreaterThan(0); | ||
| }); | ||
| }); |
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.
Fix formatting issues.
The pipeline indicates formatting issues. Please run oxfmt to fix the formatting.
Run this command to fix formatting:
#!/bin/bash
# Format the test file
oxfmt vtex/server/lib/client.test.ts🧰 Tools
🪛 GitHub Actions: Checks
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
🤖 Prompt for AI Agents
In vtex/server/lib/client.test.ts lines 1-198 the file has formatting issues
reported by the pipeline; run the project formatter (oxfmt) against this file to
apply the standard code style and commit the changes (e.g., run `oxfmt
vtex/server/lib/client.test.ts` in the repo root), then re-run tests/CI and
include the formatted file in your PR.
| * Simplified from apps/vtex/utils/transform.ts | ||
| */ | ||
|
|
||
| import type { VTEXProduct, VTEXItem, OrderForm } from "./client.ts"; |
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.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# List all files in the vtex/server/lib directory to verify the actual filename
ls -la vtex/server/lib/ | grep -i clientRepository: decocms/mcps
Length of output: 183
Correct the import path from "./client.ts" to "./vtex-client.ts".
The import on line 7 references "./client.ts", but the actual file in vtex/server/lib/ is named vtex-client.ts. This import path will cause a module resolution failure.
🤖 Prompt for AI Agents
In vtex/server/lib/transform.ts around line 7 the import currently references
"./client.ts" but the actual module file is named "vtex-client.ts"; update the
import path to "./vtex-client.ts" so the correct module is resolved and exported
types VTEXProduct, VTEXItem, OrderForm are imported from the existing file.
| export function pickSku(items: VTEXItem[]): VTEXItem { | ||
| const available = items.find( | ||
| (item) => item.sellers.some((s) => s.commertialOffer.AvailableQuantity > 0) | ||
| ); | ||
| return available || items[0]; | ||
| } |
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.
Add guard for empty items array.
pickSku will return undefined if the items array is empty, which could cause downstream errors in toProduct. Consider adding a guard or throwing a descriptive error.
export function pickSku(items: VTEXItem[]): VTEXItem {
+ if (!items.length) {
+ throw new Error("Product has no SKU items");
+ }
const available = items.find(
(item) => item.sellers.some((s) => s.commertialOffer.AvailableQuantity > 0)
);
return available || items[0];
}🤖 Prompt for AI Agents
In vtex/server/lib/transform.ts around lines 102 to 107, pickSku currently
assumes items has at least one element and may return undefined for an empty
array; add an explicit guard at the top of the function to handle an empty items
array (either throw a descriptive Error like "pickSku: items array is empty" or
return a well-defined fallback) and ensure the function's return type and
callers (e.g., toProduct) are updated/checked to handle the thrown error or
fallback value accordingly.
| async getOrderForm(orderFormId?: string): Promise<OrderForm> { | ||
| const url = orderFormId | ||
| ? `${baseUrl}/api/checkout/pub/orderForm/${orderFormId}` | ||
| : `${baseUrl}/api/checkout/pub/orderForm`; | ||
|
|
||
| const response = await fetch(url, { | ||
| method: "POST", | ||
| headers, | ||
| body: JSON.stringify({}), | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`VTEX cart failed: ${response.status}`); | ||
| } | ||
|
|
||
| return response.json(); | ||
| }, |
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.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, let's find and examine the vtex-client.ts file
find . -name "vtex-client.ts" -type fRepository: decocms/mcps
Length of output: 88
🏁 Script executed:
#!/bin/bash
# Search for any usage of getOrderForm in the codebase
rg "getOrderForm" -n -C 3Repository: decocms/mcps
Length of output: 998
🏁 Script executed:
#!/bin/bash
# Also check for any comments or documentation about the HTTP method used
rg "orderForm" -n -C 2 --type=ts --type=jsRepository: decocms/mcps
Length of output: 5328
🌐 Web query:
VTEX Checkout API GET orderForm endpoint documentation
💡 Result:
Key VTEX Checkout GET orderForm endpoints and docs:
-
Get current (or create) cart — GET /api/checkout/pub/orderForm
- Create new cart: add ?forceNewCart=true
- Returns full orderForm (includes orderFormId). See docs. [1]
-
Get cart by ID — GET /api/checkout/pub/orderForm/{orderFormId}
- Optional query: refreshOutdatedData
- Use when you already have an orderFormId. [2]
-
OrderForm fields / schema (items, clientProfileData, shippingData, totalizers, etc.) — overview of orderForm structure and field meanings. [3]
Notes:
- Personal data may be masked unless the correct CheckoutOrderFormOwnership cookie / auth is present; some endpoints require authentication depending on customer context. [1][2]
Sources:
[1] VTEX — Get current or create a new cart (Checkout API).
[2] VTEX — Get cart information by ID (Checkout API).
[3] VTEX — orderForm fields / orderForm overview.
🌐 Web query:
VTEX API POST vs GET /checkout/pub/orderForm method specification
💡 Result:
Short answer — use GET to read/create the cart; there is no generic POST that replaces it.
Key points
- GET /api/checkout/pub/orderForm
- Returns the current orderForm (cart) for the session or, with ?forceNewCart=true, creates a new empty cart and returns its orderFormId. [1][2]
- There is no documented single POST /api/checkout/pub/orderForm to replace or create/modify the full orderForm. Instead you modify the cart with the specific Checkout POST/PUT endpoints (add/change items, attachments like shippingData, simulation, place order, etc.). Data-changing endpoints are POST/PATCH/PUT/DELETE on resources such as /items, /attachments/shippingData, /orderForms/simulation, /orderForms/place (see Checkout API overview and guides). Also note Checkout mutating requests must not be performed in parallel (enqueue them). [2][3][4]
Sources
- VTEX OrderForm overview (fields & semantics). [1]
- VTEX Checkout API — Get current or create cart; overview of checkout endpoints and recommendations about mutating requests. [2][3]
- Examples / Postman collection for cart item and simulation endpoints (shows POST/PUT usage for specific cart operations). [4]
getOrderForm incorrectly uses POST instead of GET—VTEX Checkout API requires GET
The VTEX Checkout API documentation confirms that cart retrieval and creation use GET:
GET /api/checkout/pub/orderForm— returns current cart or creates a new one (optionally?forceNewCart=true)GET /api/checkout/pub/orderForm/{orderFormId}— retrieves cart by ID (optionally?refreshOutdatedData=true)
There is no POST endpoint on these paths. Sending POST with an empty body will fail with 404/405 or undefined behavior.
Switch to GET and optionally include refreshOutdatedData=true when retrieving an existing cart:
/**
* Get or create order form (cart)
*/
async getOrderForm(orderFormId?: string): Promise<OrderForm> {
const url = orderFormId
- ? `${baseUrl}/api/checkout/pub/orderForm/${orderFormId}`
- : `${baseUrl}/api/checkout/pub/orderForm`;
-
- const response = await fetch(url, {
- method: "POST",
- headers,
- body: JSON.stringify({}),
- });
-
+ ? `${baseUrl}/api/checkout/pub/orderForm/${orderFormId}?refreshOutdatedData=true`
+ : `${baseUrl}/api/checkout/pub/orderForm`;
+
+ const response = await fetch(url, {
+ headers,
+ });
+
if (!response.ok) {
throw new Error(`VTEX cart failed: ${response.status}`);
}
-
+
return response.json();
},🤖 Prompt for AI Agents
In vtex/server/lib/vtex-client.ts around lines 194 to 210, the getOrderForm
implementation incorrectly uses POST; change it to use GET, remove the empty
request body, and include the proper query parameters: when orderFormId is
provided append ?refreshOutdatedData=true (or allow passing that flag) and when
no orderFormId consider supporting ?forceNewCart=true; keep the existing
headers, issue a GET fetch to the constructed URL, check response.ok and throw
as before, and return response.json().
| /** | ||
| * VTEX MCP Server | ||
| * | ||
| * This is the main entry point for the VTEX MCP server. | ||
| * It provides tools for interacting with VTEX commerce APIs including: | ||
| * - Product search and retrieval | ||
| * - Shopping cart management | ||
| * - Search suggestions | ||
| */ | ||
| import { DefaultEnv, withRuntime } from "@decocms/runtime"; | ||
| import { | ||
| type Env as DecoEnv, | ||
| StateSchema, | ||
| } from "../shared/deco.gen.ts"; | ||
|
|
||
| import { vtexTools } from "./tools/index.ts"; | ||
| import { userTools } from "@decocms/mcps-shared/tools/user"; | ||
|
|
||
| // Combine VTEX tools with shared user tools | ||
| const tools = [...userTools, ...vtexTools]; | ||
|
|
||
| /** | ||
| * This Env type is the main context object that is passed to | ||
| * all of your Application. | ||
| * | ||
| * It includes all of the generated types from your | ||
| * Deco bindings, along with the default ones. | ||
| */ | ||
| export type Env = DefaultEnv & | ||
| DecoEnv & { | ||
| ASSETS: { | ||
| fetch: (request: Request, init?: RequestInit) => Promise<Response>; | ||
| }; | ||
| }; | ||
|
|
||
| const runtime = withRuntime<Env, typeof StateSchema>({ | ||
| oauth: { | ||
| /** | ||
| * These scopes define the asking permissions of your | ||
| * app when a user is installing it. | ||
| */ | ||
| scopes: [], | ||
| /** | ||
| * The state schema of your Application defines what | ||
| * your installed App state will look like. When a user | ||
| * is installing your App, they will have to fill in | ||
| * a form with the fields defined in the state schema. | ||
| * | ||
| * For VTEX, users need to provide: | ||
| * - account: VTEX account name | ||
| * - environment: VTEX environment (default: vtexcommercestable) | ||
| * - salesChannel: Sales channel ID (default: 1) | ||
| * - locale: Default locale (default: pt-BR) | ||
| * - currency: Currency code (default: BRL) | ||
| */ | ||
| state: StateSchema, | ||
| }, | ||
| tools, | ||
| /** | ||
| * Fallback directly to assets for all requests that do not match a tool or auth. | ||
| */ | ||
| fetch: (req, env) => env.ASSETS.fetch(req), | ||
| }); | ||
|
|
||
| export default runtime; |
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.
Fix formatting issues.
The pipeline indicates formatting issues. Please run oxfmt to fix the formatting.
Run this command to fix formatting:
#!/bin/bash
# Format the main server file
oxfmt vtex/server/main.ts🧰 Tools
🪛 GitHub Actions: Checks
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
🤖 Prompt for AI Agents
vtex/server/main.ts lines 1-65: the file has formatting issues flagged by CI;
run the project formatter (oxfmt) on this file or apply the formatter's changes
to fix whitespace/indentation/comment spacing and ensure imports/exports are
properly aligned; run `oxfmt vtex/server/main.ts` (or your repo's format script)
and commit the resulting formatted file.
| const baseUrl = `https://${state.account}.vtexcommercestable.com.br`; | ||
| const currency = state.currency || "BRL"; |
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.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Step 1: Check the environment usage in vtex-client.ts
rg -n 'environment' vtex/server/lib/vtex-client.ts -A2 -B2Repository: decocms/mcps
Length of output: 665
🏁 Script executed:
#!/bin/bash
# Step 2: Read products.ts around lines 56-57 for context
cat -n vtex/server/tools/products.ts | head -70 | tail -40Repository: decocms/mcps
Length of output: 1609
🏁 Script executed:
#!/bin/bash
# Step 3: Check for other hardcoded vtexcommercestable patterns in products.ts
rg -n 'vtexcommercestable' vtex/server/tools/products.tsRepository: decocms/mcps
Length of output: 213
🏁 Script executed:
#!/bin/bash
# Step 4: Verify state.environment is properly available in the State type
rg -n 'environment' vtex/server/tools/products.ts -A1 -B1Repository: decocms/mcps
Length of output: 127
🏁 Script executed:
#!/bin/bash
# Step 5: Find and read the State interface definition
rg -n 'interface State|type State' vtex/server/tools/products.ts -A10Repository: decocms/mcps
Length of output: 38
🏁 Script executed:
#!/bin/bash
# Step 6: Read context around line 92
cat -n vtex/server/tools/products.ts | sed -n '80,105p'Repository: decocms/mcps
Length of output: 985
🏁 Script executed:
#!/bin/bash
# Step 7: Check imports and State definition in products.ts
head -20 vtex/server/tools/products.tsRepository: decocms/mcps
Length of output: 667
🏁 Script executed:
#!/bin/bash
# Step 8: Verify State type has environment field by checking vtex-client.ts
rg -n 'export.*State|interface.*State|type.*State' vtex/server/lib/vtex-client.ts -A5Repository: decocms/mcps
Length of output: 569
Perfect! I now have all the information needed to verify the review comment. Let me confirm the findings:
Verification Results:
- ✅ State.environment EXISTS and is AVAILABLE - Imported from "../../shared/deco.gen.ts" and used by
getClientFromState() - ✅ Pattern is DEMONSTRATED in vtex-client.ts - Line 110 shows:
https://${config.account}.${config.environment}.com.br - ✅ The issue is CONFIRMED - Line 56 hardcodes
.vtexcommercestable.com.brinstead of usingstate.environment - ✅ Issue occurs in MULTIPLE PLACES - Found at both line 56 AND line 92 in products.ts
- ✅ Inconsistency is REAL - Both functions use
getClientFromState(state)which internally usesstate.environment, but then construct baseUrl with hardcoded domain
The review comment is accurate and identifies a real bug. The hardcoded domain pattern would produce incorrect URLs if the environment differs from "vtexcommercestable".
Hardcoded domain pattern should use state.environment instead of "vtexcommercestable".
Both createSearchProductsTool (line 56) and createGetSuggestionsTool (line 92) hardcode .vtexcommercestable.com.br, while the client obtained from getClientFromState(state) dynamically uses state.environment. This inconsistency will produce incorrect product URLs when the environment differs. Follow the pattern in vtex-client.ts line 110: use https://${state.account}.${state.environment}.com.br.
🤖 Prompt for AI Agents
vtex/server/tools/products.ts lines 56 and 92: both functions build baseUrl with
a hardcoded ".vtexcommercestable.com.br" which contradicts
getClientFromState(state) that relies on state.environment; change the baseUrl
construction to use the state's environment (e.g.,
`https://${state.account}.${state.environment}.com.br`) in both
createSearchProductsTool and createGetSuggestionsTool so URLs follow the same
pattern as vtex-client.ts line 110.
| /** | ||
| * Auto-generated file. Do not edit manually. | ||
| * Run `deco gen` to regenerate. | ||
| */ | ||
|
|
||
| import { z } from "zod"; | ||
|
|
||
| export const StateSchema = z.object({ | ||
| /** | ||
| * @description VTEX Account Name (e.g., "mystore") | ||
| */ | ||
| account: z.string().describe("VTEX Account Name"), | ||
|
|
||
| /** | ||
| * @description VTEX Environment (e.g., "vtexcommercestable") | ||
| */ | ||
| environment: z.string().default("vtexcommercestable").describe("VTEX Environment"), | ||
|
|
||
| /** | ||
| * @description Sales Channel ID | ||
| */ | ||
| salesChannel: z.string().default("1").describe("Sales Channel ID"), | ||
|
|
||
| /** | ||
| * @description Default locale (e.g., "pt-BR") | ||
| */ | ||
| locale: z.string().default("pt-BR").describe("Default Locale"), | ||
|
|
||
| /** | ||
| * @description Default currency code (e.g., "BRL") | ||
| */ | ||
| currency: z.string().default("BRL").describe("Currency Code"), | ||
| }); | ||
|
|
||
| export type State = z.infer<typeof StateSchema>; | ||
|
|
||
| export interface Env { | ||
| SELF: unknown; | ||
| DECO_REQUEST_CONTEXT?: { | ||
| state?: State; | ||
| }; | ||
| } |
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.
Fix formatting issues in auto-generated file.
The pipeline indicates formatting issues. Since this is an auto-generated file (lines 1-4), consider updating the generation tool to produce properly formatted output, or add a post-generation formatting step.
Run this command to fix formatting:
#!/bin/bash
# Format the auto-generated file
oxfmt vtex/shared/deco.gen.ts🧰 Tools
🪛 GitHub Actions: Checks
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
🤖 Prompt for AI Agents
In vtex/shared/deco.gen.ts around lines 1 to 42 the auto-generated file has
formatting issues; update the generator to emit properly formatted output or add
a post-generation formatting step that runs the project formatter (e.g., run
oxfmt on vtex/shared/deco.gen.ts as part of the generation script or CI) and
commit the reformatted file so the pipeline passes.
| import { defineConfig } from "vite"; | ||
| import { cloudflare } from "@cloudflare/vite-plugin"; | ||
| import deco from "@decocms/mcps-shared/vite-plugin"; | ||
|
|
||
| const VITE_SERVER_ENVIRONMENT_NAME = "server"; | ||
|
|
||
| // https://vitejs.dev/config/ | ||
| export default defineConfig({ | ||
| plugins: [ | ||
| cloudflare({ | ||
| configPath: "wrangler.toml", | ||
| viteEnvironment: { | ||
| name: VITE_SERVER_ENVIRONMENT_NAME, | ||
| }, | ||
| }), | ||
| deco(), | ||
| ], | ||
|
|
||
| define: { | ||
| // Ensure proper module definitions for Cloudflare Workers context | ||
| "process.env.NODE_ENV": JSON.stringify( | ||
| process.env.NODE_ENV || "development", | ||
| ), | ||
| global: "globalThis", | ||
| }, | ||
|
|
||
| // Clear cache more aggressively | ||
| cacheDir: "node_modules/.vite", | ||
| }); |
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.
Fix formatting issues.
The pipeline indicates formatting issues detected by oxfmt. Please run oxfmt to fix the formatting.
Run this command to fix formatting:
#!/bin/bash
# Format the file with oxfmt
oxfmt vtex/vite.config.ts🧰 Tools
🪛 GitHub Actions: Checks
[error] 1-1: Formatting issues found by oxfmt --check. Run 'oxfmt' to fix.
🤖 Prompt for AI Agents
In vtex/vite.config.ts around lines 1 to 29, the file fails the project's oxfmt
formatting checks; run the formatter and save the file to correct whitespace and
style. Execute the project's formatter (oxfmt) on this file (e.g., run `oxfmt
vtex/vite.config.ts`) and commit the resulting changes so the file matches the
repository's formatting conventions.
Summary by CodeRabbit
Release Notes
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.