#
tokens: 45082/50000 6/825 files (page 34/61)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 34 of 61. Use http://codebase.md/taurgis/sfcc-dev-mcp?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .DS_Store
├── .github
│   ├── dependabot.yml
│   ├── instructions
│   │   ├── mcp-node-tests.instructions.md
│   │   └── mcp-yml-tests.instructions.md
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── documentation.yml
│   │   ├── feature_request.yml
│   │   └── question.yml
│   ├── PULL_REQUEST_TEMPLATE
│   │   ├── bug_fix.md
│   │   ├── documentation.md
│   │   └── new_tool.md
│   ├── pull_request_template.md
│   └── workflows
│       ├── ci.yml
│       ├── deploy-pages.yml
│       ├── publish.yml
│       └── update-docs.yml
├── .gitignore
├── .husky
│   └── pre-commit
├── aegis.config.docs-only.json
├── aegis.config.json
├── aegis.config.with-dw.json
├── AGENTS.md
├── ai-instructions
│   ├── claude-desktop
│   │   └── claude_custom_instructions.md
│   ├── cursor
│   │   └── .cursor
│   │       └── rules
│   │           ├── debugging-workflows.mdc
│   │           ├── hooks-development.mdc
│   │           ├── isml-templates.mdc
│   │           ├── job-framework.mdc
│   │           ├── performance-optimization.mdc
│   │           ├── scapi-endpoints.mdc
│   │           ├── security-patterns.mdc
│   │           ├── sfcc-development.mdc
│   │           ├── sfra-controllers.mdc
│   │           ├── sfra-models.mdc
│   │           ├── system-objects.mdc
│   │           └── testing-patterns.mdc
│   └── github-copilot
│       └── copilot-instructions.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── docs
│   ├── best-practices
│   │   ├── cartridge_creation.md
│   │   ├── isml_templates.md
│   │   ├── job_framework.md
│   │   ├── localserviceregistry.md
│   │   ├── ocapi_hooks.md
│   │   ├── performance.md
│   │   ├── scapi_custom_endpoint.md
│   │   ├── scapi_hooks.md
│   │   ├── security.md
│   │   ├── sfra_client_side_js.md
│   │   ├── sfra_controllers.md
│   │   ├── sfra_models.md
│   │   └── sfra_scss.md
│   ├── dw_campaign
│   │   ├── ABTest.md
│   │   ├── ABTestMgr.md
│   │   ├── ABTestSegment.md
│   │   ├── AmountDiscount.md
│   │   ├── ApproachingDiscount.md
│   │   ├── BonusChoiceDiscount.md
│   │   ├── BonusDiscount.md
│   │   ├── Campaign.md
│   │   ├── CampaignMgr.md
│   │   ├── CampaignStatusCodes.md
│   │   ├── Coupon.md
│   │   ├── CouponMgr.md
│   │   ├── CouponRedemption.md
│   │   ├── CouponStatusCodes.md
│   │   ├── Discount.md
│   │   ├── DiscountPlan.md
│   │   ├── FixedPriceDiscount.md
│   │   ├── FixedPriceShippingDiscount.md
│   │   ├── FreeDiscount.md
│   │   ├── FreeShippingDiscount.md
│   │   ├── PercentageDiscount.md
│   │   ├── PercentageOptionDiscount.md
│   │   ├── PriceBookPriceDiscount.md
│   │   ├── Promotion.md
│   │   ├── PromotionMgr.md
│   │   ├── PromotionPlan.md
│   │   ├── SlotContent.md
│   │   ├── SourceCodeGroup.md
│   │   ├── SourceCodeInfo.md
│   │   ├── SourceCodeStatusCodes.md
│   │   └── TotalFixedPriceDiscount.md
│   ├── dw_catalog
│   │   ├── Catalog.md
│   │   ├── CatalogMgr.md
│   │   ├── Category.md
│   │   ├── CategoryAssignment.md
│   │   ├── CategoryLink.md
│   │   ├── PriceBook.md
│   │   ├── PriceBookMgr.md
│   │   ├── Product.md
│   │   ├── ProductActiveData.md
│   │   ├── ProductAttributeModel.md
│   │   ├── ProductAvailabilityLevels.md
│   │   ├── ProductAvailabilityModel.md
│   │   ├── ProductInventoryList.md
│   │   ├── ProductInventoryMgr.md
│   │   ├── ProductInventoryRecord.md
│   │   ├── ProductLink.md
│   │   ├── ProductMgr.md
│   │   ├── ProductOption.md
│   │   ├── ProductOptionModel.md
│   │   ├── ProductOptionValue.md
│   │   ├── ProductPriceInfo.md
│   │   ├── ProductPriceModel.md
│   │   ├── ProductPriceTable.md
│   │   ├── ProductSearchHit.md
│   │   ├── ProductSearchModel.md
│   │   ├── ProductSearchRefinementDefinition.md
│   │   ├── ProductSearchRefinements.md
│   │   ├── ProductSearchRefinementValue.md
│   │   ├── ProductVariationAttribute.md
│   │   ├── ProductVariationAttributeValue.md
│   │   ├── ProductVariationModel.md
│   │   ├── Recommendation.md
│   │   ├── SearchModel.md
│   │   ├── SearchRefinementDefinition.md
│   │   ├── SearchRefinements.md
│   │   ├── SearchRefinementValue.md
│   │   ├── SortingOption.md
│   │   ├── SortingRule.md
│   │   ├── Store.md
│   │   ├── StoreGroup.md
│   │   ├── StoreInventoryFilter.md
│   │   ├── StoreInventoryFilterValue.md
│   │   ├── StoreMgr.md
│   │   ├── Variant.md
│   │   └── VariationGroup.md
│   ├── dw_content
│   │   ├── Content.md
│   │   ├── ContentMgr.md
│   │   ├── ContentSearchModel.md
│   │   ├── ContentSearchRefinementDefinition.md
│   │   ├── ContentSearchRefinements.md
│   │   ├── ContentSearchRefinementValue.md
│   │   ├── Folder.md
│   │   ├── Library.md
│   │   ├── MarkupText.md
│   │   └── MediaFile.md
│   ├── dw_crypto
│   │   ├── CertificateRef.md
│   │   ├── CertificateUtils.md
│   │   ├── Cipher.md
│   │   ├── Encoding.md
│   │   ├── JWE.md
│   │   ├── JWEHeader.md
│   │   ├── JWS.md
│   │   ├── JWSHeader.md
│   │   ├── KeyRef.md
│   │   ├── Mac.md
│   │   ├── MessageDigest.md
│   │   ├── SecureRandom.md
│   │   ├── Signature.md
│   │   ├── WeakCipher.md
│   │   ├── WeakMac.md
│   │   ├── WeakMessageDigest.md
│   │   ├── WeakSignature.md
│   │   └── X509Certificate.md
│   ├── dw_customer
│   │   ├── AddressBook.md
│   │   ├── AgentUserMgr.md
│   │   ├── AgentUserStatusCodes.md
│   │   ├── AuthenticationStatus.md
│   │   ├── Credentials.md
│   │   ├── Customer.md
│   │   ├── CustomerActiveData.md
│   │   ├── CustomerAddress.md
│   │   ├── CustomerCDPData.md
│   │   ├── CustomerContextMgr.md
│   │   ├── CustomerGroup.md
│   │   ├── CustomerList.md
│   │   ├── CustomerMgr.md
│   │   ├── CustomerPasswordConstraints.md
│   │   ├── CustomerPaymentInstrument.md
│   │   ├── CustomerStatusCodes.md
│   │   ├── EncryptedObject.md
│   │   ├── ExternalProfile.md
│   │   ├── OrderHistory.md
│   │   ├── ProductList.md
│   │   ├── ProductListItem.md
│   │   ├── ProductListItemPurchase.md
│   │   ├── ProductListMgr.md
│   │   ├── ProductListRegistrant.md
│   │   ├── Profile.md
│   │   └── Wallet.md
│   ├── dw_extensions.applepay
│   │   ├── ApplePayHookResult.md
│   │   └── ApplePayHooks.md
│   ├── dw_extensions.facebook
│   │   ├── FacebookFeedHooks.md
│   │   └── FacebookProduct.md
│   ├── dw_extensions.paymentrequest
│   │   ├── PaymentRequestHookResult.md
│   │   └── PaymentRequestHooks.md
│   ├── dw_extensions.payments
│   │   ├── SalesforceBancontactPaymentDetails.md
│   │   ├── SalesforceCardPaymentDetails.md
│   │   ├── SalesforceEpsPaymentDetails.md
│   │   ├── SalesforceIdealPaymentDetails.md
│   │   ├── SalesforceKlarnaPaymentDetails.md
│   │   ├── SalesforcePaymentDetails.md
│   │   ├── SalesforcePaymentIntent.md
│   │   ├── SalesforcePaymentMethod.md
│   │   ├── SalesforcePaymentRequest.md
│   │   ├── SalesforcePaymentsHooks.md
│   │   ├── SalesforcePaymentsMgr.md
│   │   ├── SalesforcePaymentsSiteConfiguration.md
│   │   ├── SalesforcePayPalOrder.md
│   │   ├── SalesforcePayPalOrderAddress.md
│   │   ├── SalesforcePayPalOrderPayer.md
│   │   ├── SalesforcePayPalPaymentDetails.md
│   │   ├── SalesforceSepaDebitPaymentDetails.md
│   │   └── SalesforceVenmoPaymentDetails.md
│   ├── dw_extensions.pinterest
│   │   ├── PinterestAvailability.md
│   │   ├── PinterestFeedHooks.md
│   │   ├── PinterestOrder.md
│   │   ├── PinterestOrderHooks.md
│   │   └── PinterestProduct.md
│   ├── dw_io
│   │   ├── CSVStreamReader.md
│   │   ├── CSVStreamWriter.md
│   │   ├── File.md
│   │   ├── FileReader.md
│   │   ├── FileWriter.md
│   │   ├── InputStream.md
│   │   ├── OutputStream.md
│   │   ├── PrintWriter.md
│   │   ├── RandomAccessFileReader.md
│   │   ├── Reader.md
│   │   ├── StringWriter.md
│   │   ├── Writer.md
│   │   ├── XMLIndentingStreamWriter.md
│   │   ├── XMLStreamConstants.md
│   │   ├── XMLStreamReader.md
│   │   └── XMLStreamWriter.md
│   ├── dw_job
│   │   ├── JobExecution.md
│   │   └── JobStepExecution.md
│   ├── dw_net
│   │   ├── FTPClient.md
│   │   ├── FTPFileInfo.md
│   │   ├── HTTPClient.md
│   │   ├── HTTPRequestPart.md
│   │   ├── Mail.md
│   │   ├── SFTPClient.md
│   │   ├── SFTPFileInfo.md
│   │   ├── WebDAVClient.md
│   │   └── WebDAVFileInfo.md
│   ├── dw_object
│   │   ├── ActiveData.md
│   │   ├── CustomAttributes.md
│   │   ├── CustomObject.md
│   │   ├── CustomObjectMgr.md
│   │   ├── Extensible.md
│   │   ├── ExtensibleObject.md
│   │   ├── Note.md
│   │   ├── ObjectAttributeDefinition.md
│   │   ├── ObjectAttributeGroup.md
│   │   ├── ObjectAttributeValueDefinition.md
│   │   ├── ObjectTypeDefinition.md
│   │   ├── PersistentObject.md
│   │   ├── SimpleExtensible.md
│   │   └── SystemObjectMgr.md
│   ├── dw_order
│   │   ├── AbstractItem.md
│   │   ├── AbstractItemCtnr.md
│   │   ├── Appeasement.md
│   │   ├── AppeasementItem.md
│   │   ├── Basket.md
│   │   ├── BasketMgr.md
│   │   ├── BonusDiscountLineItem.md
│   │   ├── CouponLineItem.md
│   │   ├── CreateAgentBasketLimitExceededException.md
│   │   ├── CreateBasketFromOrderException.md
│   │   ├── CreateCouponLineItemException.md
│   │   ├── CreateOrderException.md
│   │   ├── CreateTemporaryBasketLimitExceededException.md
│   │   ├── GiftCertificate.md
│   │   ├── GiftCertificateLineItem.md
│   │   ├── GiftCertificateMgr.md
│   │   ├── GiftCertificateStatusCodes.md
│   │   ├── Invoice.md
│   │   ├── InvoiceItem.md
│   │   ├── LineItem.md
│   │   ├── LineItemCtnr.md
│   │   ├── Order.md
│   │   ├── OrderAddress.md
│   │   ├── OrderItem.md
│   │   ├── OrderMgr.md
│   │   ├── OrderPaymentInstrument.md
│   │   ├── OrderProcessStatusCodes.md
│   │   ├── PaymentCard.md
│   │   ├── PaymentInstrument.md
│   │   ├── PaymentMethod.md
│   │   ├── PaymentMgr.md
│   │   ├── PaymentProcessor.md
│   │   ├── PaymentStatusCodes.md
│   │   ├── PaymentTransaction.md
│   │   ├── PriceAdjustment.md
│   │   ├── PriceAdjustmentLimitTypes.md
│   │   ├── ProductLineItem.md
│   │   ├── ProductShippingCost.md
│   │   ├── ProductShippingLineItem.md
│   │   ├── ProductShippingModel.md
│   │   ├── Return.md
│   │   ├── ReturnCase.md
│   │   ├── ReturnCaseItem.md
│   │   ├── ReturnItem.md
│   │   ├── Shipment.md
│   │   ├── ShipmentShippingCost.md
│   │   ├── ShipmentShippingModel.md
│   │   ├── ShippingLineItem.md
│   │   ├── ShippingLocation.md
│   │   ├── ShippingMethod.md
│   │   ├── ShippingMgr.md
│   │   ├── ShippingOrder.md
│   │   ├── ShippingOrderItem.md
│   │   ├── SumItem.md
│   │   ├── TaxGroup.md
│   │   ├── TaxItem.md
│   │   ├── TaxMgr.md
│   │   ├── TrackingInfo.md
│   │   └── TrackingRef.md
│   ├── dw_order.hooks
│   │   ├── CalculateHooks.md
│   │   ├── OrderHooks.md
│   │   ├── PaymentHooks.md
│   │   ├── ReturnHooks.md
│   │   └── ShippingOrderHooks.md
│   ├── dw_rpc
│   │   ├── SOAPUtil.md
│   │   ├── Stub.md
│   │   └── WebReference.md
│   ├── dw_suggest
│   │   ├── BrandSuggestions.md
│   │   ├── CategorySuggestions.md
│   │   ├── ContentSuggestions.md
│   │   ├── CustomSuggestions.md
│   │   ├── ProductSuggestions.md
│   │   ├── SearchPhraseSuggestions.md
│   │   ├── SuggestedCategory.md
│   │   ├── SuggestedContent.md
│   │   ├── SuggestedPhrase.md
│   │   ├── SuggestedProduct.md
│   │   ├── SuggestedTerm.md
│   │   ├── SuggestedTerms.md
│   │   ├── Suggestions.md
│   │   └── SuggestModel.md
│   ├── dw_svc
│   │   ├── FTPService.md
│   │   ├── FTPServiceDefinition.md
│   │   ├── HTTPFormService.md
│   │   ├── HTTPFormServiceDefinition.md
│   │   ├── HTTPService.md
│   │   ├── HTTPServiceDefinition.md
│   │   ├── LocalServiceRegistry.md
│   │   ├── Result.md
│   │   ├── Service.md
│   │   ├── ServiceCallback.md
│   │   ├── ServiceConfig.md
│   │   ├── ServiceCredential.md
│   │   ├── ServiceDefinition.md
│   │   ├── ServiceProfile.md
│   │   ├── ServiceRegistry.md
│   │   ├── SOAPService.md
│   │   └── SOAPServiceDefinition.md
│   ├── dw_system
│   │   ├── AgentUserStatusCodes.md
│   │   ├── Cache.md
│   │   ├── CacheMgr.md
│   │   ├── HookMgr.md
│   │   ├── InternalObject.md
│   │   ├── JobProcessMonitor.md
│   │   ├── Log.md
│   │   ├── Logger.md
│   │   ├── LogNDC.md
│   │   ├── OrganizationPreferences.md
│   │   ├── Pipeline.md
│   │   ├── PipelineDictionary.md
│   │   ├── RemoteInclude.md
│   │   ├── Request.md
│   │   ├── RequestHooks.md
│   │   ├── Response.md
│   │   ├── RESTErrorResponse.md
│   │   ├── RESTResponseMgr.md
│   │   ├── RESTSuccessResponse.md
│   │   ├── SearchStatus.md
│   │   ├── Session.md
│   │   ├── Site.md
│   │   ├── SitePreferences.md
│   │   ├── Status.md
│   │   ├── StatusItem.md
│   │   ├── System.md
│   │   └── Transaction.md
│   ├── dw_util
│   │   ├── ArrayList.md
│   │   ├── Assert.md
│   │   ├── BigInteger.md
│   │   ├── Bytes.md
│   │   ├── Calendar.md
│   │   ├── Collection.md
│   │   ├── Currency.md
│   │   ├── DateUtils.md
│   │   ├── Decimal.md
│   │   ├── FilteringCollection.md
│   │   ├── Geolocation.md
│   │   ├── HashMap.md
│   │   ├── HashSet.md
│   │   ├── Iterator.md
│   │   ├── LinkedHashMap.md
│   │   ├── LinkedHashSet.md
│   │   ├── List.md
│   │   ├── Locale.md
│   │   ├── Map.md
│   │   ├── MapEntry.md
│   │   ├── MappingKey.md
│   │   ├── MappingMgr.md
│   │   ├── PropertyComparator.md
│   │   ├── SecureEncoder.md
│   │   ├── SecureFilter.md
│   │   ├── SeekableIterator.md
│   │   ├── Set.md
│   │   ├── SortedMap.md
│   │   ├── SortedSet.md
│   │   ├── StringUtils.md
│   │   ├── Template.md
│   │   └── UUIDUtils.md
│   ├── dw_value
│   │   ├── EnumValue.md
│   │   ├── MimeEncodedText.md
│   │   ├── Money.md
│   │   └── Quantity.md
│   ├── dw_web
│   │   ├── ClickStream.md
│   │   ├── ClickStreamEntry.md
│   │   ├── Cookie.md
│   │   ├── Cookies.md
│   │   ├── CSRFProtection.md
│   │   ├── Form.md
│   │   ├── FormAction.md
│   │   ├── FormElement.md
│   │   ├── FormElementValidationResult.md
│   │   ├── FormField.md
│   │   ├── FormFieldOption.md
│   │   ├── FormFieldOptions.md
│   │   ├── FormGroup.md
│   │   ├── FormList.md
│   │   ├── FormListItem.md
│   │   ├── Forms.md
│   │   ├── HttpParameter.md
│   │   ├── HttpParameterMap.md
│   │   ├── LoopIterator.md
│   │   ├── PageMetaData.md
│   │   ├── PageMetaTag.md
│   │   ├── PagingModel.md
│   │   ├── Resource.md
│   │   ├── URL.md
│   │   ├── URLAction.md
│   │   ├── URLParameter.md
│   │   ├── URLRedirect.md
│   │   ├── URLRedirectMgr.md
│   │   └── URLUtils.md
│   ├── sfra
│   │   ├── account.md
│   │   ├── address.md
│   │   ├── billing.md
│   │   ├── cart.md
│   │   ├── categories.md
│   │   ├── content.md
│   │   ├── locale.md
│   │   ├── order.md
│   │   ├── payment.md
│   │   ├── price-default.md
│   │   ├── price-range.md
│   │   ├── price-tiered.md
│   │   ├── product-bundle.md
│   │   ├── product-full.md
│   │   ├── product-line-items.md
│   │   ├── product-search.md
│   │   ├── product-tile.md
│   │   ├── querystring.md
│   │   ├── render.md
│   │   ├── request.md
│   │   ├── response.md
│   │   ├── server.md
│   │   ├── shipping.md
│   │   ├── store.md
│   │   ├── stores.md
│   │   └── totals.md
│   └── TopLevel
│       ├── APIException.md
│       ├── arguments.md
│       ├── Array.md
│       ├── ArrayBuffer.md
│       ├── BigInt.md
│       ├── Boolean.md
│       ├── ConversionError.md
│       ├── DataView.md
│       ├── Date.md
│       ├── Error.md
│       ├── ES6Iterator.md
│       ├── EvalError.md
│       ├── Fault.md
│       ├── Float32Array.md
│       ├── Float64Array.md
│       ├── Function.md
│       ├── Generator.md
│       ├── global.md
│       ├── Int16Array.md
│       ├── Int32Array.md
│       ├── Int8Array.md
│       ├── InternalError.md
│       ├── IOError.md
│       ├── Iterable.md
│       ├── Iterator.md
│       ├── JSON.md
│       ├── Map.md
│       ├── Math.md
│       ├── Module.md
│       ├── Namespace.md
│       ├── Number.md
│       ├── Object.md
│       ├── QName.md
│       ├── RangeError.md
│       ├── ReferenceError.md
│       ├── RegExp.md
│       ├── Set.md
│       ├── StopIteration.md
│       ├── String.md
│       ├── Symbol.md
│       ├── SyntaxError.md
│       ├── SystemError.md
│       ├── TypeError.md
│       ├── Uint16Array.md
│       ├── Uint32Array.md
│       ├── Uint8Array.md
│       ├── Uint8ClampedArray.md
│       ├── URIError.md
│       ├── WeakMap.md
│       ├── WeakSet.md
│       ├── XML.md
│       ├── XMLList.md
│       └── XMLStreamError.md
├── docs-site
│   ├── .gitignore
│   ├── App.tsx
│   ├── components
│   │   ├── Badge.tsx
│   │   ├── BreadcrumbSchema.tsx
│   │   ├── CodeBlock.tsx
│   │   ├── Collapsible.tsx
│   │   ├── ConfigBuilder.tsx
│   │   ├── ConfigHero.tsx
│   │   ├── ConfigModeTabs.tsx
│   │   ├── icons.tsx
│   │   ├── Layout.tsx
│   │   ├── LightCodeContainer.tsx
│   │   ├── NewcomerCTA.tsx
│   │   ├── NextStepsStrip.tsx
│   │   ├── OnThisPage.tsx
│   │   ├── Search.tsx
│   │   ├── SEO.tsx
│   │   ├── Sidebar.tsx
│   │   ├── StructuredData.tsx
│   │   ├── ToolCard.tsx
│   │   ├── ToolFilters.tsx
│   │   ├── Typography.tsx
│   │   └── VersionBadge.tsx
│   ├── constants.tsx
│   ├── index.html
│   ├── main.tsx
│   ├── metadata.json
│   ├── package-lock.json
│   ├── package.json
│   ├── pages
│   │   ├── AIInterfacesPage.tsx
│   │   ├── ConfigurationPage.tsx
│   │   ├── DevelopmentPage.tsx
│   │   ├── ExamplesPage.tsx
│   │   ├── FeaturesPage.tsx
│   │   ├── HomePage.tsx
│   │   ├── SecurityPage.tsx
│   │   ├── ToolsPage.tsx
│   │   └── TroubleshootingPage.tsx
│   ├── postcss.config.js
│   ├── public
│   │   ├── .well-known
│   │   │   └── security.txt
│   │   ├── 404.html
│   │   ├── android-chrome-192x192.png
│   │   ├── android-chrome-512x512.png
│   │   ├── apple-touch-icon.png
│   │   ├── explain-product-pricing-methods-no-mcp.png
│   │   ├── explain-product-pricing-methods.png
│   │   ├── favicon-16x16.png
│   │   ├── favicon-32x32.png
│   │   ├── favicon.ico
│   │   ├── llms.txt
│   │   ├── robots.txt
│   │   ├── site.webmanifest
│   │   └── sitemap.xml
│   ├── README.md
│   ├── scripts
│   │   ├── generate-search-index.js
│   │   ├── generate-sitemap.js
│   │   └── search-dev.js
│   ├── src
│   │   └── styles
│   │       ├── input.css
│   │       └── prism-theme.css
│   ├── tailwind.config.js
│   ├── tsconfig.json
│   ├── types.ts
│   ├── utils
│   │   ├── search.ts
│   │   └── toolsData.ts
│   └── vite.config.ts
├── eslint.config.js
├── jest.config.js
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── scripts
│   └── convert-docs.js
├── SECURITY.md
├── server.json
├── src
│   ├── clients
│   │   ├── base
│   │   │   ├── http-client.ts
│   │   │   ├── oauth-token.ts
│   │   │   └── ocapi-auth-client.ts
│   │   ├── best-practices-client.ts
│   │   ├── cartridge-generation-client.ts
│   │   ├── docs
│   │   │   ├── class-content-parser.ts
│   │   │   ├── class-name-resolver.ts
│   │   │   ├── documentation-scanner.ts
│   │   │   ├── index.ts
│   │   │   └── referenced-types-extractor.ts
│   │   ├── docs-client.ts
│   │   ├── log-client.ts
│   │   ├── logs
│   │   │   ├── index.ts
│   │   │   ├── log-analyzer.ts
│   │   │   ├── log-client.ts
│   │   │   ├── log-constants.ts
│   │   │   ├── log-file-discovery.ts
│   │   │   ├── log-file-reader.ts
│   │   │   ├── log-formatter.ts
│   │   │   ├── log-processor.ts
│   │   │   ├── log-types.ts
│   │   │   └── webdav-client-manager.ts
│   │   ├── ocapi
│   │   │   ├── code-versions-client.ts
│   │   │   ├── site-preferences-client.ts
│   │   │   └── system-objects-client.ts
│   │   ├── ocapi-client.ts
│   │   └── sfra-client.ts
│   ├── config
│   │   ├── configuration-factory.ts
│   │   └── dw-json-loader.ts
│   ├── core
│   │   ├── handlers
│   │   │   ├── abstract-log-tool-handler.ts
│   │   │   ├── base-handler.ts
│   │   │   ├── best-practices-handler.ts
│   │   │   ├── cartridge-handler.ts
│   │   │   ├── client-factory.ts
│   │   │   ├── code-version-handler.ts
│   │   │   ├── docs-handler.ts
│   │   │   ├── job-log-handler.ts
│   │   │   ├── job-log-tool-config.ts
│   │   │   ├── log-handler.ts
│   │   │   ├── log-tool-config.ts
│   │   │   ├── sfra-handler.ts
│   │   │   ├── system-object-handler.ts
│   │   │   └── validation-helpers.ts
│   │   ├── server.ts
│   │   └── tool-definitions.ts
│   ├── index.ts
│   ├── main.ts
│   ├── services
│   │   ├── file-system-service.ts
│   │   ├── index.ts
│   │   └── path-service.ts
│   ├── tool-configs
│   │   ├── best-practices-tool-config.ts
│   │   ├── cartridge-tool-config.ts
│   │   ├── code-version-tool-config.ts
│   │   ├── docs-tool-config.ts
│   │   ├── job-log-tool-config.ts
│   │   ├── log-tool-config.ts
│   │   ├── sfra-tool-config.ts
│   │   └── system-object-tool-config.ts
│   ├── types
│   │   └── types.ts
│   └── utils
│       ├── cache.ts
│       ├── job-log-tool-config.ts
│       ├── job-log-utils.ts
│       ├── log-cache.ts
│       ├── log-tool-config.ts
│       ├── log-tool-constants.ts
│       ├── log-tool-utils.ts
│       ├── logger.ts
│       ├── ocapi-url-builder.ts
│       ├── path-resolver.ts
│       ├── query-builder.ts
│       ├── utils.ts
│       └── validator.ts
├── tests
│   ├── __mocks__
│   │   ├── docs-client.ts
│   │   ├── src
│   │   │   └── clients
│   │   │       └── base
│   │   │           └── http-client.js
│   │   └── webdav.js
│   ├── base-handler.test.ts
│   ├── base-http-client.test.ts
│   ├── best-practices-handler.test.ts
│   ├── cache.test.ts
│   ├── cartridge-handler.test.ts
│   ├── class-content-parser.test.ts
│   ├── class-name-resolver.test.ts
│   ├── client-factory.test.ts
│   ├── code-version-handler.test.ts
│   ├── code-versions-client.test.ts
│   ├── config.test.ts
│   ├── configuration-factory.test.ts
│   ├── docs-handler.test.ts
│   ├── documentation-scanner.test.ts
│   ├── file-system-service.test.ts
│   ├── job-log-handler.test.ts
│   ├── job-log-utils.test.ts
│   ├── log-client.test.ts
│   ├── log-handler.test.ts
│   ├── log-processor.test.ts
│   ├── logger.test.ts
│   ├── mcp
│   │   ├── AGENTS.md
│   │   ├── node
│   │   │   ├── activate-code-version-advanced.full-mode.programmatic.test.js
│   │   │   ├── code-versions.full-mode.programmatic.test.js
│   │   │   ├── generate-cartridge-structure.docs-only.programmatic.test.js
│   │   │   ├── get-available-best-practice-guides.docs-only.programmatic.test.js
│   │   │   ├── get-available-sfra-documents.programmatic.test.js
│   │   │   ├── get-best-practice-guide.docs-only.programmatic.test.js
│   │   │   ├── get-hook-reference.docs-only.programmatic.test.js
│   │   │   ├── get-job-execution-summary.full-mode.programmatic.test.js
│   │   │   ├── get-job-log-entries.full-mode.programmatic.test.js
│   │   │   ├── get-latest-debug.full-mode.programmatic.test.js
│   │   │   ├── get-latest-error.full-mode.programmatic.test.js
│   │   │   ├── get-latest-info.full-mode.programmatic.test.js
│   │   │   ├── get-latest-job-log-files.full-mode.programmatic.test.js
│   │   │   ├── get-latest-warn.full-mode.programmatic.test.js
│   │   │   ├── get-log-file-contents.full-mode.programmatic.test.js
│   │   │   ├── get-sfcc-class-documentation.docs-only.programmatic.test.js
│   │   │   ├── get-sfcc-class-info.docs-only.programmatic.test.js
│   │   │   ├── get-sfra-categories.docs-only.programmatic.test.js
│   │   │   ├── get-sfra-document.programmatic.test.js
│   │   │   ├── get-sfra-documents-by-category.docs-only.programmatic.test.js
│   │   │   ├── get-system-object-definition.full-mode.programmatic.test.js
│   │   │   ├── get-system-object-definitions.docs-only.programmatic.test.js
│   │   │   ├── get-system-object-definitions.full-mode.programmatic.test.js
│   │   │   ├── list-log-files.full-mode.programmatic.test.js
│   │   │   ├── list-sfcc-classes.docs-only.programmatic.test.js
│   │   │   ├── search-best-practices.docs-only.programmatic.test.js
│   │   │   ├── search-custom-object-attribute-definitions.full-mode.programmatic.test.js
│   │   │   ├── search-job-logs-by-name.full-mode.programmatic.test.js
│   │   │   ├── search-job-logs.full-mode.programmatic.test.js
│   │   │   ├── search-logs.full-mode.programmatic.test.js
│   │   │   ├── search-sfcc-classes.docs-only.programmatic.test.js
│   │   │   ├── search-sfcc-methods.docs-only.programmatic.test.js
│   │   │   ├── search-sfra-documentation.docs-only.programmatic.test.js
│   │   │   ├── search-site-preferences.full-mode.programmatic.test.js
│   │   │   ├── search-system-object-attribute-definitions.full-mode.programmatic.test.js
│   │   │   ├── search-system-object-attribute-groups.full-mode.programmatic.test.js
│   │   │   ├── summarize-logs.full-mode.programmatic.test.js
│   │   │   ├── tools.docs-only.programmatic.test.js
│   │   │   └── tools.full-mode.programmatic.test.js
│   │   ├── README.md
│   │   ├── test-fixtures
│   │   │   └── dw.json
│   │   └── yaml
│   │       ├── activate-code-version.docs-only.test.mcp.yml
│   │       ├── activate-code-version.full-mode.test.mcp.yml
│   │       ├── get_latest_error.test.mcp.yml
│   │       ├── get-available-best-practice-guides.docs-only.test.mcp.yml
│   │       ├── get-available-best-practice-guides.full-mode.test.mcp.yml
│   │       ├── get-available-sfra-documents.docs-only.test.mcp.yml
│   │       ├── get-available-sfra-documents.full-mode.test.mcp.yml
│   │       ├── get-best-practice-guide.docs-only.test.mcp.yml
│   │       ├── get-best-practice-guide.full-mode.test.mcp.yml
│   │       ├── get-code-versions.docs-only.test.mcp.yml
│   │       ├── get-code-versions.full-mode.test.mcp.yml
│   │       ├── get-hook-reference.docs-only.test.mcp.yml
│   │       ├── get-hook-reference.full-mode.test.mcp.yml
│   │       ├── get-job-execution-summary.full-mode.test.mcp.yml
│   │       ├── get-job-log-entries.full-mode.test.mcp.yml
│   │       ├── get-latest-debug.full-mode.test.mcp.yml
│   │       ├── get-latest-error.full-mode.test.mcp.yml
│   │       ├── get-latest-info.full-mode.test.mcp.yml
│   │       ├── get-latest-job-log-files.full-mode.test.mcp.yml
│   │       ├── get-latest-warn.full-mode.test.mcp.yml
│   │       ├── get-log-file-contents.full-mode.test.mcp.yml
│   │       ├── get-sfcc-class-documentation.docs-only.test.mcp.yml
│   │       ├── get-sfcc-class-documentation.full-mode.test.mcp.yml
│   │       ├── get-sfcc-class-info.docs-only.test.mcp.yml
│   │       ├── get-sfcc-class-info.full-mode.test.mcp.yml
│   │       ├── get-sfra-categories.docs-only.test.mcp.yml
│   │       ├── get-sfra-categories.full-mode.test.mcp.yml
│   │       ├── get-sfra-document.docs-only.test.mcp.yml
│   │       ├── get-sfra-document.full-mode.test.mcp.yml
│   │       ├── get-sfra-documents-by-category.docs-only.test.mcp.yml
│   │       ├── get-sfra-documents-by-category.full-mode.test.mcp.yml
│   │       ├── get-system-object-definition.docs-only.test.mcp.yml
│   │       ├── get-system-object-definition.full-mode.test.mcp.yml
│   │       ├── get-system-object-definitions.docs-only.test.mcp.yml
│   │       ├── get-system-object-definitions.full-mode.test.mcp.yml
│   │       ├── list-log-files.full-mode.test.mcp.yml
│   │       ├── list-sfcc-classes.docs-only.test.mcp.yml
│   │       ├── list-sfcc-classes.full-mode.test.mcp.yml
│   │       ├── search-best-practices.docs-only.test.mcp.yml
│   │       ├── search-best-practices.full-mode.test.mcp.yml
│   │       ├── search-custom-object-attribute-definitions.docs-only.test.mcp.yml
│   │       ├── search-custom-object-attribute-definitions.test.mcp.yml
│   │       ├── search-job-logs-by-name.full-mode.test.mcp.yml
│   │       ├── search-job-logs.full-mode.test.mcp.yml
│   │       ├── search-logs.full-mode.test.mcp.yml
│   │       ├── search-sfcc-classes.docs-only.test.mcp.yml
│   │       ├── search-sfcc-classes.full-mode.test.mcp.yml
│   │       ├── search-sfcc-methods.docs-only.test.mcp.yml
│   │       ├── search-sfcc-methods.full-mode.test.mcp.yml
│   │       ├── search-sfra-documentation.docs-only.test.mcp.yml
│   │       ├── search-sfra-documentation.full-mode.test.mcp.yml
│   │       ├── search-site-preferences.docs-only.test.mcp.yml
│   │       ├── search-site-preferences.full-mode.test.mcp.yml
│   │       ├── search-system-object-attribute-definitions.docs-only.test.mcp.yml
│   │       ├── search-system-object-attribute-definitions.full-mode.test.mcp.yml
│   │       ├── search-system-object-attribute-groups.docs-only.test.mcp.yml
│   │       ├── search-system-object-attribute-groups.full-mode.test.mcp.yml
│   │       ├── summarize-logs.full-mode.test.mcp.yml
│   │       ├── tools.docs-only.test.mcp.yml
│   │       └── tools.full-mode.test.mcp.yml
│   ├── oauth-token.test.ts
│   ├── ocapi-auth-client.test.ts
│   ├── ocapi-client.test.ts
│   ├── path-service.test.ts
│   ├── query-builder.test.ts
│   ├── referenced-types-extractor.test.ts
│   ├── servers
│   │   ├── sfcc-mock-server
│   │   │   ├── mock-data
│   │   │   │   └── ocapi
│   │   │   │       ├── code-versions.json
│   │   │   │       ├── custom-object-attributes-customapi.json
│   │   │   │       ├── custom-object-attributes-globalsettings.json
│   │   │   │       ├── custom-object-attributes-versionhistory.json
│   │   │   │       ├── site-preferences-ccv.json
│   │   │   │       ├── site-preferences-fastforward.json
│   │   │   │       ├── site-preferences-sfra.json
│   │   │   │       ├── site-preferences-storefront.json
│   │   │   │       ├── site-preferences-system.json
│   │   │   │       ├── system-object-attribute-groups-campaign.json
│   │   │   │       ├── system-object-attribute-groups-category.json
│   │   │   │       ├── system-object-attribute-groups-order.json
│   │   │   │       ├── system-object-attribute-groups-product.json
│   │   │   │       ├── system-object-attribute-groups-sitepreferences.json
│   │   │   │       ├── system-object-attributes-customeraddress.json
│   │   │   │       ├── system-object-attributes-product-expanded.json
│   │   │   │       ├── system-object-attributes-product.json
│   │   │   │       ├── system-object-definition-category.json
│   │   │   │       ├── system-object-definition-customer.json
│   │   │   │       ├── system-object-definition-customeraddress.json
│   │   │   │       ├── system-object-definition-order.json
│   │   │   │       ├── system-object-definition-product.json
│   │   │   │       ├── system-object-definitions-old.json
│   │   │   │       └── system-object-definitions.json
│   │   │   ├── package-lock.json
│   │   │   ├── package.json
│   │   │   ├── README.md
│   │   │   ├── scripts
│   │   │   │   └── setup-logs.js
│   │   │   ├── server.js
│   │   │   └── src
│   │   │       ├── app.js
│   │   │       ├── config
│   │   │       │   └── server-config.js
│   │   │       ├── middleware
│   │   │       │   ├── auth.js
│   │   │       │   ├── cors.js
│   │   │       │   └── logging.js
│   │   │       ├── routes
│   │   │       │   ├── ocapi
│   │   │       │   │   ├── code-versions-handler.js
│   │   │       │   │   ├── oauth-handler.js
│   │   │       │   │   ├── ocapi-error-utils.js
│   │   │       │   │   ├── ocapi-utils.js
│   │   │       │   │   ├── site-preferences-handler.js
│   │   │       │   │   └── system-objects-handler.js
│   │   │       │   ├── ocapi.js
│   │   │       │   └── webdav.js
│   │   │       └── utils
│   │   │           ├── mock-data-loader.js
│   │   │           └── webdav-xml.js
│   │   └── sfcc-mock-server-manager.ts
│   ├── sfcc-mock-server.test.ts
│   ├── site-preferences-client.test.ts
│   ├── system-objects-client.test.ts
│   ├── utils.test.ts
│   ├── validation-helpers.test.ts
│   └── validator.test.ts
├── tsconfig.json
└── tsconfig.test.json
```

# Files

--------------------------------------------------------------------------------
/tests/mcp/node/get-system-object-definition.full-mode.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * ==================================================================================
  3 |  * SFCC MCP Server - get_system_object_definition Tool Programmatic Tests (Full Mode)
  4 |  * Streamlined programmatic testing focused on tool functionality and critical workflows
  5 |  * 
  6 |  * These tests require SFCC credentials and use the mock server for realistic testing
  7 |  * Focus on complex business logic, multi-step workflows, and advanced error handling
  8 |  * that cannot be effectively tested in YAML format
  9 |  * 
 10 |  * Quick Test Commands:
 11 |  * node --test tests/mcp/node/get-system-object-definition.full-mode.programmatic.test.js
 12 |  * npm test -- --grep "get_system_object_definition"
 13 |  * ==================================================================================
 14 |  */
 15 | 
 16 | import { test, describe, before, after, beforeEach } from 'node:test';
 17 | import { strict as assert } from 'node:assert';
 18 | import { connect } from 'mcp-aegis';
 19 | 
 20 | describe('get_system_object_definition Tool - Full Mode Programmatic Tests', () => {
 21 |   let client;
 22 |   
 23 |   // Streamlined test data - focus on representative objects
 24 |   const coreObjectTypes = ['Product', 'Customer', 'Order'];
 25 |   const expectedSystemObjectFlags = {
 26 |     'Product': { content_object: true, queryable: false, read_only: false },
 27 |     'Customer': { content_object: false, queryable: true, read_only: false },
 28 |     'Order': { content_object: false, queryable: true, read_only: true }
 29 |   };
 30 | 
 31 |   before(async () => {
 32 |     client = await connect('./aegis.config.with-dw.json');
 33 |   });
 34 | 
 35 |   after(async () => {
 36 |     if (client?.connected) {
 37 |       await client.disconnect();
 38 |     }
 39 |   });
 40 | 
 41 |   beforeEach(() => {
 42 |     // CRITICAL: Clear all buffers to prevent test interference
 43 |     client.clearAllBuffers();
 44 |   });
 45 | 
 46 |   // ==================================================================================
 47 |   // CONSOLIDATED TOOL DISCOVERY AND FUNCTIONALITY
 48 |   // ==================================================================================
 49 | 
 50 |   describe('Tool Discovery and Core Functionality', () => {
 51 |     test('should discover tool with comprehensive schema and execute successfully', async () => {
 52 |       // Tool discovery with schema validation
 53 |       const tools = await client.listTools();
 54 |       const systemObjectTool = tools.find(tool => tool.name === 'get_system_object_definition');
 55 |       
 56 |       assert.ok(systemObjectTool, 'get_system_object_definition tool should be available in full mode');
 57 |       assert.ok(systemObjectTool.description.includes('system object'), 'Description should mention system objects');
 58 |       
 59 |       const schema = systemObjectTool.inputSchema;
 60 |       assert.equal(schema.type, 'object', 'Schema should be object type');
 61 |       assert.ok(schema.properties?.objectType, 'Schema should have objectType property');
 62 |       assert.equal(schema.properties.objectType.type, 'string', 'objectType should be string type');
 63 |       assert.ok(schema.required.includes('objectType'), 'objectType should be required');
 64 |       
 65 |       // Functional validation - execute tool successfully
 66 |       const result = await client.callTool('get_system_object_definition', { objectType: 'Product' });
 67 |       assert.equal(result.isError, false, 'Should execute successfully with valid parameters');
 68 |       assert.ok(result.content?.[0]?.text, 'Should return content');
 69 |       
 70 |       // Validate response structure
 71 |       const objectData = JSON.parse(result.content[0].text);
 72 |       assertValidObjectDefinition(objectData, 'Product');
 73 |     });
 74 | 
 75 |     test('should validate tool context and availability with other system tools', async () => {
 76 |       const tools = await client.listTools();
 77 |       const toolNames = tools.map(t => t.name);
 78 |       
 79 |       // Should have related system object tools
 80 |       assert.ok(toolNames.includes('get_system_object_definition'), 'Should have single object tool');
 81 |       assert.ok(toolNames.includes('get_system_object_definitions'), 'Should have multi-object tool');
 82 |       assert.ok(toolNames.includes('search_system_object_attribute_definitions'), 'Should have attribute search tool');
 83 |       
 84 |       // Should have substantial tool count in full mode
 85 |       assert.ok(tools.length >= 30, `Should have many tools in full mode, got ${tools.length}`);
 86 |     });
 87 |   });
 88 | 
 89 |   // ==================================================================================
 90 |   // STREAMLINED BUSINESS LOGIC VALIDATION
 91 |   // ==================================================================================
 92 | 
 93 |   describe('Business Logic and System Object Validation', () => {
 94 |     test('should validate core SFCC object characteristics and flag logic', async () => {
 95 |       const results = new Map();
 96 |       
 97 |       // Test core object types sequentially (streamlined from 5 to 3 objects)
 98 |       for (const objectType of coreObjectTypes) {
 99 |         const result = await client.callTool('get_system_object_definition', { objectType });
100 |         
101 |         assert.equal(result.isError, false, `${objectType} should be retrieved successfully`);
102 |         const objectData = JSON.parse(result.content[0].text);
103 |         results.set(objectType, objectData);
104 |         
105 |         // Use helper function for comprehensive validation
106 |         assertValidObjectDefinition(objectData, objectType);
107 |         
108 |         // Validate expected flags (resilient to mock data changes)
109 |         const expectedFlags = expectedSystemObjectFlags[objectType];
110 |         if (expectedFlags) {
111 |           assert.equal(objectData.content_object, expectedFlags.content_object,
112 |             `${objectType} content_object should match expected value`);
113 |           assert.equal(objectData.queryable, expectedFlags.queryable,
114 |             `${objectType} queryable should match expected value`);
115 |           assert.equal(objectData.read_only, expectedFlags.read_only,
116 |             `${objectType} read_only should match expected value`);
117 |         }
118 |       }
119 |       
120 |       // Cross-object business rule validation
121 |       const productData = results.get('Product');
122 |       const customerData = results.get('Customer');
123 |       const orderData = results.get('Order');
124 |       
125 |       // Validate business rules (resilient assertions)
126 |       assert.ok(productData.attribute_definition_count > 0, 'Product should have attributes');
127 |       assert.ok(customerData.attribute_definition_count > 0, 'Customer should have attributes');
128 |       assert.equal(orderData.read_only, true, 'Order should be read-only');
129 |       assert.equal(customerData.queryable, true, 'Customer should be queryable');
130 |     });
131 | 
132 |     test('should validate localization structure and metadata consistency', async () => {
133 |       const result = await client.callTool('get_system_object_definition', { objectType: 'Product' });
134 |       const objectData = JSON.parse(result.content[0].text);
135 |       
136 |       // Validate localization structure (flexible assertions)
137 |       validateLocalizationStructure(objectData.display_name, 'display_name');
138 |       assert.ok(objectData.description?.default, 'Should have default description');
139 |       
140 |       // Validate timestamps and metadata
141 |       assert.ok(objectData.creation_date, 'Should have creation date');
142 |       assert.ok(objectData.last_modified, 'Should have last modified date');
143 |       assert.ok(!isNaN(Date.parse(objectData.creation_date)), 'Creation date should be valid');
144 |       assert.ok(!isNaN(Date.parse(objectData.last_modified)), 'Last modified should be valid');
145 |     });
146 |   });
147 | 
148 |   // ==================================================================================
149 |   // ESSENTIAL WORKFLOW VALIDATION 
150 |   // ==================================================================================
151 | 
152 |   describe('Multi-Tool Integration Workflows', () => {
153 |     test('should support object discovery and analysis workflow', async () => {
154 |       // Step 1: Get all system object definitions
155 |       const allObjectsResult = await client.callTool('get_system_object_definitions', {});
156 |       assert.equal(allObjectsResult.isError, false, 'Should retrieve all object definitions');
157 |       
158 |       const allObjects = JSON.parse(allObjectsResult.content[0].text);
159 |       assert.ok(Array.isArray(allObjects.data), 'Should return array of objects');
160 |       assert.ok(allObjects.data.length > 0, 'Should have multiple object types');
161 |       
162 |       // Step 2: Analyze representative objects in detail
163 |       const analysisResults = [];
164 |       const testObjects = ['Product', 'Customer']; // Streamlined from 3 to 2 objects
165 |       
166 |       for (const objectType of testObjects) {
167 |         const detailResult = await client.callTool('get_system_object_definition', { objectType });
168 |         assert.equal(detailResult.isError, false, `Should get details for ${objectType}`);
169 |         
170 |         const detailData = JSON.parse(detailResult.content[0].text);
171 |         analysisResults.push({
172 |           objectType,
173 |           attributes: detailData.attribute_definition_count,
174 |           groups: detailData.attribute_group_count,
175 |           flags: {
176 |             content_object: detailData.content_object,
177 |             queryable: detailData.queryable,
178 |             read_only: detailData.read_only
179 |           }
180 |         });
181 |       }
182 |       
183 |       // Step 3: Validate workflow results
184 |       assert.equal(analysisResults.length, 2, 'Should analyze expected objects');
185 |       
186 |       // At least one should be queryable
187 |       const queryableCount = analysisResults.filter(r => r.flags.queryable).length;
188 |       assert.ok(queryableCount > 0, 'Should have at least one queryable object (Customer)');
189 |     });
190 | 
191 |     test('should support attribute discovery and cross-tool integration', async () => {
192 |       // Step 1: Get object definition for context
193 |       const productResult = await client.callTool('get_system_object_definition', { objectType: 'Product' });
194 |       assert.equal(productResult.isError, false, 'Should get Product definition successfully');
195 |       
196 |       // Step 2: Search for Product attributes using integration tool
197 |       const attributeSearchResult = await client.callTool('search_system_object_attribute_definitions', {
198 |         objectType: 'Product',
199 |         searchRequest: {
200 |           query: { match_all_query: {} },
201 |           count: 10  // Reduced from 20 to 10 for efficiency
202 |         }
203 |       });
204 |       
205 |       assert.equal(attributeSearchResult.isError, false, 'Should search attributes successfully');
206 |       const attributeData = JSON.parse(attributeSearchResult.content[0].text);
207 |       
208 |       // Step 3: Validate cross-tool consistency
209 |       assert.ok(attributeData.hits !== undefined, 'Should have search results structure');
210 |       assert.ok(attributeData.total >= 0, 'Should have reasonable total count');
211 |       
212 |       // Validate attribute structure if results exist
213 |       if (attributeData.hits.length > 0) {
214 |         const sampleAttribute = attributeData.hits[0];
215 |         assert.ok(sampleAttribute.id, 'Attribute should have ID');
216 |         assert.ok(sampleAttribute._type === 'object_attribute_definition', 'Should have correct type');
217 |       }
218 |     });
219 |   });
220 | 
221 |   // ==================================================================================
222 |   // COMPREHENSIVE ERROR HANDLING
223 |   // ==================================================================================
224 | 
225 |   describe('Advanced Error Handling and Edge Cases', () => {
226 |     test('should handle comprehensive parameter validation scenarios', async () => {
227 |       const invalidCases = [
228 |         { args: {}, description: 'missing objectType', shouldError: true },
229 |         { args: { objectType: null }, description: 'null objectType', shouldError: true },
230 |         { args: { objectType: 123 }, description: 'numeric objectType', shouldError: true },
231 |         { args: { objectType: '' }, description: 'empty objectType', shouldError: true },
232 |         { args: { objectType: 'NonExistentObject' }, description: 'unknown object', shouldError: false }, // Fallback response
233 |         { args: { objectType: 'Product123' }, description: 'object with numbers', shouldError: false }, // Fallback response
234 |       ];
235 |       
236 |       for (const testCase of invalidCases) {
237 |         const result = await client.callTool('get_system_object_definition', testCase.args);
238 |         
239 |         if (testCase.shouldError) {
240 |           assert.equal(result.isError, true, 
241 |             `Should return error for ${testCase.description}`);
242 |           assert.ok(result.content[0].text.includes('Error'), 
243 |             `Error message should contain 'Error' for ${testCase.description}`);
244 |         } else {
245 |           // Fallback responses are acceptable for unknown object types
246 |           if (result.isError) {
247 |             assert.ok(result.content[0].text.includes('Error'), 
248 |               `Should have error message for ${testCase.description}`);
249 |           } else {
250 |             const data = JSON.parse(result.content[0].text);
251 |             assert.ok(data.object_type, 'Should have fallback object type');
252 |           }
253 |         }
254 |       }
255 |     });
256 | 
257 |     test('should maintain performance and reliability under moderate load', async () => {
258 |       const testObjects = ['Product', 'Customer', 'Order'];
259 |       const results = [];
260 |       const startTime = Date.now();
261 |       
262 |       // Streamlined stress test (reduced from 10 to 5 iterations)
263 |       for (let i = 0; i < 5; i++) {
264 |         const objectType = testObjects[i % testObjects.length];
265 |         const iterationStart = Date.now();
266 |         
267 |         const result = await client.callTool('get_system_object_definition', { objectType });
268 |         const iterationTime = Date.now() - iterationStart;
269 |         
270 |         results.push({
271 |           iteration: i,
272 |           objectType,
273 |           success: !result.isError,
274 |           responseTime: iterationTime
275 |         });
276 |         
277 |         assert.equal(result.isError, false, 
278 |           `Iteration ${i} should succeed for ${objectType}`);
279 |       }
280 |       
281 |       const totalTime = Date.now() - startTime;
282 |       const successRate = results.filter(r => r.success).length / results.length;
283 |       
284 |       // Functional validation focused on reliability
285 |       assert.equal(successRate, 1.0, 'All stress test iterations should succeed');
286 |       assert.ok(totalTime < 20000, 'Load test should complete within 20 seconds');
287 |       
288 |       // Validate response consistency
289 |       const responseTimes = results.map(r => r.responseTime);
290 |       const avgResponseTime = responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length;
291 |       assert.ok(avgResponseTime < 3000, 'Average response time should be reasonable');
292 |     });
293 |   });
294 | 
295 |   // ==================================================================================
296 |   // FOCUSED INTEGRATION VALIDATION
297 |   // ==================================================================================
298 | 
299 |   describe('Integration and Context Validation', () => {
300 |     test('should validate mock server integration and SFCC API compliance', async () => {
301 |       const result = await client.callTool('get_system_object_definition', { objectType: 'Product' });
302 |       const data = JSON.parse(result.content[0].text);
303 |       
304 |       // Validate SFCC API compliance (essential checks only)
305 |       assert.ok(data._v && data._v.match(/^\d+\.\d+$/), 'Should have valid API version format');
306 |       assert.ok(data.link && data.link.includes('/dw/data/'), 'Should have SFCC-style API link');
307 |       
308 |       // Validate realistic data structure
309 |       assert.ok(!isNaN(Date.parse(data.creation_date)), 'Should have valid creation date');
310 |       assert.ok(!isNaN(Date.parse(data.last_modified)), 'Should have valid last modified date');
311 |       assert.ok(data.attribute_definition_count >= 0, 'Should have realistic attribute count');
312 |       assert.ok(data.attribute_group_count >= 0, 'Should have realistic group count');
313 |     });
314 | 
315 |     test('should validate cross-tool integration consistency', async () => {
316 |       // Single object definition
317 |       const singleResult = await client.callTool('get_system_object_definition', { objectType: 'Customer' });
318 |       const singleData = JSON.parse(singleResult.content[0].text);
319 |       
320 |       // Bulk object definitions (for context)
321 |       const allResult = await client.callTool('get_system_object_definitions', {});
322 |       const allData = JSON.parse(allResult.content[0].text);
323 |       
324 |       // Validate integration consistency
325 |       assert.equal(singleData.object_type, 'Customer', 'Should access Customer directly');
326 |       assert.ok(Array.isArray(allData.data), 'Bulk tool should return array');
327 |       
328 |       // Find Customer in bulk results if present (pagination may affect this)
329 |       const customerFromAll = allData.data.find(obj => obj.object_type === 'Customer');
330 |       if (customerFromAll) {
331 |         assert.equal(singleData.object_type, customerFromAll.object_type, 
332 |           'Object type should match between single and bulk operations');
333 |       }
334 |     });
335 |   });
336 | 
337 |   // ==================================================================================
338 |   // HELPER FUNCTIONS
339 |   // ==================================================================================
340 | 
341 |   function assertValidObjectDefinition(data, objectType) {
342 |     assert.equal(data._type, 'object_type_definition', 'Should have correct type');
343 |     assert.equal(data.object_type, objectType, 'Should match requested object type');
344 |     assert.ok(data.display_name, 'Should have display name');
345 |     assert.ok(data.description, 'Should have description');
346 |     assert.ok(typeof data.attribute_definition_count === 'number', 'Should have attribute count');
347 |     assert.ok(typeof data.attribute_group_count === 'number', 'Should have group count');
348 |     assert.ok(typeof data.content_object === 'boolean', 'Should have content_object flag');
349 |     assert.ok(typeof data.queryable === 'boolean', 'Should have queryable flag');
350 |     assert.ok(typeof data.read_only === 'boolean', 'Should have read_only flag');
351 |   }
352 | 
353 |   function validateLocalizationStructure(localizationObject, fieldName) {
354 |     assert.ok(localizationObject, `Should have ${fieldName}`);
355 |     assert.ok(typeof localizationObject === 'object', `${fieldName} should be object`);
356 |     assert.ok(localizationObject.default, `${fieldName} should have default value`);
357 |     
358 |     const locales = Object.keys(localizationObject);
359 |     assert.ok(locales.length >= 1, `${fieldName} should have at least default locale`);
360 |     
361 |     locales.forEach(locale => {
362 |       assert.ok(typeof localizationObject[locale] === 'string', 
363 |         `${fieldName}.${locale} should be string`);
364 |       assert.ok(localizationObject[locale].length > 0, 
365 |         `${fieldName}.${locale} should not be empty`);
366 |     });
367 |   }
368 | });
369 | 
370 | // ==================================================================================
371 | // OPTIMIZATION NOTES
372 | // ==================================================================================
373 | // This programmatic test suite has been optimized for focused, essential testing.
374 | // 
375 | // OPTIMIZATIONS APPLIED:
376 | // - Consolidated tool discovery tests (2 tests → 1 comprehensive test)
377 | // - Streamlined business logic validation (3 tests → 2 focused tests)  
378 | // - Simplified multi-step workflows (3 tests → 2 essential workflows)
379 | // - Reduced stress testing iterations (10 → 5 iterations)
380 | // - Consolidated error handling patterns (3 tests → 2 comprehensive tests)
381 | // - Focused integration testing (3 tests → 2 targeted tests)
382 | // - Removed mock-data-specific assertions for resilience
383 | // - Reduced object type testing (5 objects → 3 core objects)
384 | // 
385 | // TOTAL REDUCTION: 14 tests → 9 focused tests
386 | // FOCUS: Tool functionality, critical workflows, error handling
387 | // RESILIENCE: Tests adapt to mock data changes
388 | // EFFICIENCY: Reduced API calls while maintaining comprehensive coverage
389 | // ==================================================================================
```

--------------------------------------------------------------------------------
/docs/dw_customer/Profile.md:
--------------------------------------------------------------------------------

```markdown
   1 | ## Package: dw.customer
   2 | 
   3 | # Class Profile
   4 | 
   5 | ## Inheritance Hierarchy
   6 | 
   7 | - Object
   8 |   - dw.object.PersistentObject
   9 |   - dw.object.ExtensibleObject
  10 |     - dw.customer.EncryptedObject
  11 |       - dw.customer.Profile
  12 | 
  13 | ## Description
  14 | 
  15 | The class represents a customer profile. It also provides access to the customers address book and credentials. Note: this class handles sensitive security-related data. Pay special attention to PCI DSS v3. requirements 2, 4, and 12.
  16 | 
  17 | ## Properties
  18 | 
  19 | ### addressBook
  20 | 
  21 | **Type:** AddressBook (Read Only)
  22 | 
  23 | The customer's address book.
  24 | 
  25 | ### birthday
  26 | 
  27 | **Type:** Date
  28 | 
  29 | The customer's birthday as a date.
  30 | 
  31 | ### companyName
  32 | 
  33 | **Type:** String
  34 | 
  35 | The customer's company name.
  36 | 
  37 | ### credentials
  38 | 
  39 | **Type:** Credentials (Read Only)
  40 | 
  41 | The customer's credentials.
  42 | 
  43 | ### customer
  44 | 
  45 | **Type:** Customer (Read Only)
  46 | 
  47 | The customer object related to this profile.
  48 | 
  49 | ### customerNo
  50 | 
  51 | **Type:** String (Read Only)
  52 | 
  53 | The customer's number, which is a number used to identify the Customer.
  54 | 
  55 | ### email
  56 | 
  57 | **Type:** String
  58 | 
  59 | The customer's email address.
  60 | 
  61 | ### fax
  62 | 
  63 | **Type:** String
  64 | 
  65 | The fax number to use for the customer.
  66 |  The length is restricted to 32 characters.
  67 | 
  68 | ### female
  69 | 
  70 | **Type:** boolean (Read Only)
  71 | 
  72 | Indicates that the customer is female when set to true.
  73 | 
  74 | ### firstName
  75 | 
  76 | **Type:** String
  77 | 
  78 | The customer's first name.
  79 | 
  80 | ### gender
  81 | 
  82 | **Type:** EnumValue
  83 | 
  84 | The customer's gender.
  85 | 
  86 | ### jobTitle
  87 | 
  88 | **Type:** String
  89 | 
  90 | The customer's job title.
  91 | 
  92 | ### lastLoginTime
  93 | 
  94 | **Type:** Date (Read Only)
  95 | 
  96 | The last login time of the customer.
  97 | 
  98 | ### lastName
  99 | 
 100 | **Type:** String
 101 | 
 102 | The customer's last name.
 103 | 
 104 | ### lastVisitTime
 105 | 
 106 | **Type:** Date (Read Only)
 107 | 
 108 | The last visit time of the customer.
 109 | 
 110 | ### male
 111 | 
 112 | **Type:** boolean (Read Only)
 113 | 
 114 | Indicates that the customer is male when set to true.
 115 | 
 116 | ### nextBirthday
 117 | 
 118 | **Type:** Date (Read Only)
 119 | 
 120 | The upcoming customer's birthday as a date.
 121 |  If the customer already had birthday this year the method returns the birthday of the next year.
 122 |  Otherwise its birthday in this year.
 123 |  If the customer has not set a birthday this method returns null.
 124 | 
 125 | ### phoneBusiness
 126 | 
 127 | **Type:** String
 128 | 
 129 | The business phone number to use for the customer.
 130 | 
 131 | ### phoneHome
 132 | 
 133 | **Type:** String
 134 | 
 135 | The phone number to use for the customer.
 136 | 
 137 | ### phoneMobile
 138 | 
 139 | **Type:** String
 140 | 
 141 | The mobile phone number to use for the customer.
 142 | 
 143 | ### preferredLocale
 144 | 
 145 | **Type:** String
 146 | 
 147 | The customer's preferred locale.
 148 | 
 149 | ### previousLoginTime
 150 | 
 151 | **Type:** Date (Read Only)
 152 | 
 153 | The time the customer logged in prior to the current login.
 154 | 
 155 | ### previousVisitTime
 156 | 
 157 | **Type:** Date (Read Only)
 158 | 
 159 | The time the customer visited the store prior to the current visit.
 160 | 
 161 | ### salutation
 162 | 
 163 | **Type:** String
 164 | 
 165 | The salutation to use for the customer.
 166 | 
 167 | ### secondName
 168 | 
 169 | **Type:** String
 170 | 
 171 | The customer's second name.
 172 | 
 173 | ### suffix
 174 | 
 175 | **Type:** String
 176 | 
 177 | The customer's suffix, such as "Jr." or "Sr.".
 178 | 
 179 | ### taxID
 180 | 
 181 | **Type:** String
 182 | 
 183 | The tax ID value. The value is returned either plain
 184 |  text if the current context allows plain text access, or
 185 |  if it's not allowed, the ID value will be returned masked.
 186 |  The following criteria must be met in order to have plain text access:
 187 |  
 188 |  
 189 |  	the method call must happen in the context of a storefront request;
 190 |  	the current customer must be registered and authenticated;
 191 |  	it is the profile of the current customer;
 192 |   and the current protocol is HTTPS.
 193 | 
 194 | ### taxIDMasked
 195 | 
 196 | **Type:** String (Read Only)
 197 | 
 198 | The masked value of the tax ID.
 199 | 
 200 | ### taxIDType
 201 | 
 202 | **Type:** EnumValue
 203 | 
 204 | The tax ID type.
 205 | 
 206 | ### title
 207 | 
 208 | **Type:** String
 209 | 
 210 | The customer's title, such as "Mrs" or "Mr".
 211 | 
 212 | ### wallet
 213 | 
 214 | **Type:** Wallet (Read Only)
 215 | 
 216 | The wallet of this customer.
 217 | 
 218 | ## Constructor Summary
 219 | 
 220 | ## Method Summary
 221 | 
 222 | ### getAddressBook
 223 | 
 224 | **Signature:** `getAddressBook() : AddressBook`
 225 | 
 226 | Returns the customer's address book.
 227 | 
 228 | ### getBirthday
 229 | 
 230 | **Signature:** `getBirthday() : Date`
 231 | 
 232 | Returns the customer's birthday as a date.
 233 | 
 234 | ### getCompanyName
 235 | 
 236 | **Signature:** `getCompanyName() : String`
 237 | 
 238 | Returns the customer's company name.
 239 | 
 240 | ### getCredentials
 241 | 
 242 | **Signature:** `getCredentials() : Credentials`
 243 | 
 244 | Returns the customer's credentials.
 245 | 
 246 | ### getCustomer
 247 | 
 248 | **Signature:** `getCustomer() : Customer`
 249 | 
 250 | Returns the customer object related to this profile.
 251 | 
 252 | ### getCustomerNo
 253 | 
 254 | **Signature:** `getCustomerNo() : String`
 255 | 
 256 | Returns the customer's number, which is a number used to identify the Customer.
 257 | 
 258 | ### getEmail
 259 | 
 260 | **Signature:** `getEmail() : String`
 261 | 
 262 | Returns the customer's email address.
 263 | 
 264 | ### getFax
 265 | 
 266 | **Signature:** `getFax() : String`
 267 | 
 268 | Returns the fax number to use for the customer.
 269 | 
 270 | ### getFirstName
 271 | 
 272 | **Signature:** `getFirstName() : String`
 273 | 
 274 | Returns the customer's first name.
 275 | 
 276 | ### getGender
 277 | 
 278 | **Signature:** `getGender() : EnumValue`
 279 | 
 280 | Returns the customer's gender.
 281 | 
 282 | ### getJobTitle
 283 | 
 284 | **Signature:** `getJobTitle() : String`
 285 | 
 286 | Returns the customer's job title.
 287 | 
 288 | ### getLastLoginTime
 289 | 
 290 | **Signature:** `getLastLoginTime() : Date`
 291 | 
 292 | Returns the last login time of the customer.
 293 | 
 294 | ### getLastName
 295 | 
 296 | **Signature:** `getLastName() : String`
 297 | 
 298 | Returns the customer's last name.
 299 | 
 300 | ### getLastVisitTime
 301 | 
 302 | **Signature:** `getLastVisitTime() : Date`
 303 | 
 304 | Returns the last visit time of the customer.
 305 | 
 306 | ### getNextBirthday
 307 | 
 308 | **Signature:** `getNextBirthday() : Date`
 309 | 
 310 | Returns the upcoming customer's birthday as a date.
 311 | 
 312 | ### getPhoneBusiness
 313 | 
 314 | **Signature:** `getPhoneBusiness() : String`
 315 | 
 316 | Returns the business phone number to use for the customer.
 317 | 
 318 | ### getPhoneHome
 319 | 
 320 | **Signature:** `getPhoneHome() : String`
 321 | 
 322 | Returns the phone number to use for the customer.
 323 | 
 324 | ### getPhoneMobile
 325 | 
 326 | **Signature:** `getPhoneMobile() : String`
 327 | 
 328 | Returns the mobile phone number to use for the customer.
 329 | 
 330 | ### getPreferredLocale
 331 | 
 332 | **Signature:** `getPreferredLocale() : String`
 333 | 
 334 | Returns the customer's preferred locale.
 335 | 
 336 | ### getPreviousLoginTime
 337 | 
 338 | **Signature:** `getPreviousLoginTime() : Date`
 339 | 
 340 | Returns the time the customer logged in prior to the current login.
 341 | 
 342 | ### getPreviousVisitTime
 343 | 
 344 | **Signature:** `getPreviousVisitTime() : Date`
 345 | 
 346 | Returns the time the customer visited the store prior to the current visit.
 347 | 
 348 | ### getSalutation
 349 | 
 350 | **Signature:** `getSalutation() : String`
 351 | 
 352 | Returns the salutation to use for the customer.
 353 | 
 354 | ### getSecondName
 355 | 
 356 | **Signature:** `getSecondName() : String`
 357 | 
 358 | Returns the customer's second name.
 359 | 
 360 | ### getSuffix
 361 | 
 362 | **Signature:** `getSuffix() : String`
 363 | 
 364 | Returns the customer's suffix, such as "Jr." or "Sr.".
 365 | 
 366 | ### getTaxID
 367 | 
 368 | **Signature:** `getTaxID() : String`
 369 | 
 370 | Returns the tax ID value.
 371 | 
 372 | ### getTaxIDMasked
 373 | 
 374 | **Signature:** `getTaxIDMasked() : String`
 375 | 
 376 | Returns the masked value of the tax ID.
 377 | 
 378 | ### getTaxIDType
 379 | 
 380 | **Signature:** `getTaxIDType() : EnumValue`
 381 | 
 382 | Returns the tax ID type.
 383 | 
 384 | ### getTitle
 385 | 
 386 | **Signature:** `getTitle() : String`
 387 | 
 388 | Returns the customer's title, such as "Mrs" or "Mr".
 389 | 
 390 | ### getWallet
 391 | 
 392 | **Signature:** `getWallet() : Wallet`
 393 | 
 394 | Returns the wallet of this customer.
 395 | 
 396 | ### isFemale
 397 | 
 398 | **Signature:** `isFemale() : boolean`
 399 | 
 400 | Indicates that the customer is female when set to true.
 401 | 
 402 | ### isMale
 403 | 
 404 | **Signature:** `isMale() : boolean`
 405 | 
 406 | Indicates that the customer is male when set to true.
 407 | 
 408 | ### setBirthday
 409 | 
 410 | **Signature:** `setBirthday(aValue : Date) : void`
 411 | 
 412 | Sets the customer's birthday as a date.
 413 | 
 414 | ### setCompanyName
 415 | 
 416 | **Signature:** `setCompanyName(aValue : String) : void`
 417 | 
 418 | Sets the customer's company name.
 419 | 
 420 | ### setEmail
 421 | 
 422 | **Signature:** `setEmail(aValue : String) : void`
 423 | 
 424 | Sets the customer's email address.
 425 | 
 426 | ### setFax
 427 | 
 428 | **Signature:** `setFax(number : String) : void`
 429 | 
 430 | Sets the fax number to use for the customer.
 431 | 
 432 | ### setFirstName
 433 | 
 434 | **Signature:** `setFirstName(aValue : String) : void`
 435 | 
 436 | Sets the customer's first name.
 437 | 
 438 | ### setGender
 439 | 
 440 | **Signature:** `setGender(aValue : Number) : void`
 441 | 
 442 | Sets the customer's gender.
 443 | 
 444 | ### setJobTitle
 445 | 
 446 | **Signature:** `setJobTitle(aValue : String) : void`
 447 | 
 448 | Sets the customer's job title.
 449 | 
 450 | ### setLastName
 451 | 
 452 | **Signature:** `setLastName(aValue : String) : void`
 453 | 
 454 | Sets the customer's last name.
 455 | 
 456 | ### setPhoneBusiness
 457 | 
 458 | **Signature:** `setPhoneBusiness(number : String) : void`
 459 | 
 460 | Sets the business phone number to use for the customer.
 461 | 
 462 | ### setPhoneHome
 463 | 
 464 | **Signature:** `setPhoneHome(number : String) : void`
 465 | 
 466 | Sets the phone number to use for the customer.
 467 | 
 468 | ### setPhoneMobile
 469 | 
 470 | **Signature:** `setPhoneMobile(number : String) : void`
 471 | 
 472 | Sets the mobile phone number to use for the customer.
 473 | 
 474 | ### setPreferredLocale
 475 | 
 476 | **Signature:** `setPreferredLocale(aValue : String) : void`
 477 | 
 478 | Sets the customer's preferred locale.
 479 | 
 480 | ### setSaluation
 481 | 
 482 | **Signature:** `setSaluation(salutation : String) : void`
 483 | 
 484 | Sets the salutation to use for the customer.
 485 | 
 486 | ### setSalutation
 487 | 
 488 | **Signature:** `setSalutation(salutation : String) : void`
 489 | 
 490 | Sets the salutation to use for the customer.
 491 | 
 492 | ### setSecondName
 493 | 
 494 | **Signature:** `setSecondName(aValue : String) : void`
 495 | 
 496 | Sets the customer's second name.
 497 | 
 498 | ### setSuffix
 499 | 
 500 | **Signature:** `setSuffix(aValue : String) : void`
 501 | 
 502 | Sets the the customer's suffix.
 503 | 
 504 | ### setTaxID
 505 | 
 506 | **Signature:** `setTaxID(taxID : String) : void`
 507 | 
 508 | Sets the tax ID value.
 509 | 
 510 | ### setTaxIDType
 511 | 
 512 | **Signature:** `setTaxIDType(taxIdType : String) : void`
 513 | 
 514 | Sets the tax ID type.
 515 | 
 516 | ### setTitle
 517 | 
 518 | **Signature:** `setTitle(aValue : String) : void`
 519 | 
 520 | Sets the customer's title.
 521 | 
 522 | ## Method Detail
 523 | 
 524 | ## Method Details
 525 | 
 526 | ### getAddressBook
 527 | 
 528 | **Signature:** `getAddressBook() : AddressBook`
 529 | 
 530 | **Description:** Returns the customer's address book.
 531 | 
 532 | **Returns:**
 533 | 
 534 | the customer's address book.
 535 | 
 536 | ---
 537 | 
 538 | ### getBirthday
 539 | 
 540 | **Signature:** `getBirthday() : Date`
 541 | 
 542 | **Description:** Returns the customer's birthday as a date.
 543 | 
 544 | **Returns:**
 545 | 
 546 | the customer's birthday as a date.
 547 | 
 548 | ---
 549 | 
 550 | ### getCompanyName
 551 | 
 552 | **Signature:** `getCompanyName() : String`
 553 | 
 554 | **Description:** Returns the customer's company name.
 555 | 
 556 | **Returns:**
 557 | 
 558 | the customer's company name.
 559 | 
 560 | ---
 561 | 
 562 | ### getCredentials
 563 | 
 564 | **Signature:** `getCredentials() : Credentials`
 565 | 
 566 | **Description:** Returns the customer's credentials.
 567 | 
 568 | **Returns:**
 569 | 
 570 | the customer's credentials.
 571 | 
 572 | ---
 573 | 
 574 | ### getCustomer
 575 | 
 576 | **Signature:** `getCustomer() : Customer`
 577 | 
 578 | **Description:** Returns the customer object related to this profile.
 579 | 
 580 | **Returns:**
 581 | 
 582 | customer object related to profile.
 583 | 
 584 | ---
 585 | 
 586 | ### getCustomerNo
 587 | 
 588 | **Signature:** `getCustomerNo() : String`
 589 | 
 590 | **Description:** Returns the customer's number, which is a number used to identify the Customer.
 591 | 
 592 | **Returns:**
 593 | 
 594 | the customer's number.
 595 | 
 596 | ---
 597 | 
 598 | ### getEmail
 599 | 
 600 | **Signature:** `getEmail() : String`
 601 | 
 602 | **Description:** Returns the customer's email address.
 603 | 
 604 | **Returns:**
 605 | 
 606 | the customer's email address.
 607 | 
 608 | ---
 609 | 
 610 | ### getFax
 611 | 
 612 | **Signature:** `getFax() : String`
 613 | 
 614 | **Description:** Returns the fax number to use for the customer. The length is restricted to 32 characters.
 615 | 
 616 | **Returns:**
 617 | 
 618 | the fax mobile phone number to use for the customer.
 619 | 
 620 | ---
 621 | 
 622 | ### getFirstName
 623 | 
 624 | **Signature:** `getFirstName() : String`
 625 | 
 626 | **Description:** Returns the customer's first name.
 627 | 
 628 | **Returns:**
 629 | 
 630 | the customer's first name.
 631 | 
 632 | ---
 633 | 
 634 | ### getGender
 635 | 
 636 | **Signature:** `getGender() : EnumValue`
 637 | 
 638 | **Description:** Returns the customer's gender.
 639 | 
 640 | **Returns:**
 641 | 
 642 | the customer's gender.
 643 | 
 644 | ---
 645 | 
 646 | ### getJobTitle
 647 | 
 648 | **Signature:** `getJobTitle() : String`
 649 | 
 650 | **Description:** Returns the customer's job title.
 651 | 
 652 | **Returns:**
 653 | 
 654 | the customer's job title.
 655 | 
 656 | ---
 657 | 
 658 | ### getLastLoginTime
 659 | 
 660 | **Signature:** `getLastLoginTime() : Date`
 661 | 
 662 | **Description:** Returns the last login time of the customer.
 663 | 
 664 | **Returns:**
 665 | 
 666 | the time, when the customer was last logged in.
 667 | 
 668 | ---
 669 | 
 670 | ### getLastName
 671 | 
 672 | **Signature:** `getLastName() : String`
 673 | 
 674 | **Description:** Returns the customer's last name.
 675 | 
 676 | **Returns:**
 677 | 
 678 | the customer's last name.
 679 | 
 680 | ---
 681 | 
 682 | ### getLastVisitTime
 683 | 
 684 | **Signature:** `getLastVisitTime() : Date`
 685 | 
 686 | **Description:** Returns the last visit time of the customer.
 687 | 
 688 | **Returns:**
 689 | 
 690 | the time, when the customer has visited the storefront the last time (with enabled remember me functionality).
 691 | 
 692 | ---
 693 | 
 694 | ### getNextBirthday
 695 | 
 696 | **Signature:** `getNextBirthday() : Date`
 697 | 
 698 | **Description:** Returns the upcoming customer's birthday as a date. If the customer already had birthday this year the method returns the birthday of the next year. Otherwise its birthday in this year. If the customer has not set a birthday this method returns null.
 699 | 
 700 | **Returns:**
 701 | 
 702 | the customer's next birthday as a date.
 703 | 
 704 | ---
 705 | 
 706 | ### getPhoneBusiness
 707 | 
 708 | **Signature:** `getPhoneBusiness() : String`
 709 | 
 710 | **Description:** Returns the business phone number to use for the customer.
 711 | 
 712 | **Returns:**
 713 | 
 714 | the business phone number to use for the customer.
 715 | 
 716 | ---
 717 | 
 718 | ### getPhoneHome
 719 | 
 720 | **Signature:** `getPhoneHome() : String`
 721 | 
 722 | **Description:** Returns the phone number to use for the customer.
 723 | 
 724 | **Returns:**
 725 | 
 726 | the phone number to use for the customer.
 727 | 
 728 | ---
 729 | 
 730 | ### getPhoneMobile
 731 | 
 732 | **Signature:** `getPhoneMobile() : String`
 733 | 
 734 | **Description:** Returns the mobile phone number to use for the customer.
 735 | 
 736 | **Returns:**
 737 | 
 738 | the mobile phone number to use for the customer.
 739 | 
 740 | ---
 741 | 
 742 | ### getPreferredLocale
 743 | 
 744 | **Signature:** `getPreferredLocale() : String`
 745 | 
 746 | **Description:** Returns the customer's preferred locale.
 747 | 
 748 | **Returns:**
 749 | 
 750 | the customer's preferred locale.
 751 | 
 752 | ---
 753 | 
 754 | ### getPreviousLoginTime
 755 | 
 756 | **Signature:** `getPreviousLoginTime() : Date`
 757 | 
 758 | **Description:** Returns the time the customer logged in prior to the current login.
 759 | 
 760 | **Returns:**
 761 | 
 762 | the time the customer logged in prior to the current login.
 763 | 
 764 | ---
 765 | 
 766 | ### getPreviousVisitTime
 767 | 
 768 | **Signature:** `getPreviousVisitTime() : Date`
 769 | 
 770 | **Description:** Returns the time the customer visited the store prior to the current visit.
 771 | 
 772 | **Returns:**
 773 | 
 774 | the time the customer visited the store prior to the current visit.
 775 | 
 776 | ---
 777 | 
 778 | ### getSalutation
 779 | 
 780 | **Signature:** `getSalutation() : String`
 781 | 
 782 | **Description:** Returns the salutation to use for the customer.
 783 | 
 784 | **Returns:**
 785 | 
 786 | the salutation to use for the customer.
 787 | 
 788 | ---
 789 | 
 790 | ### getSecondName
 791 | 
 792 | **Signature:** `getSecondName() : String`
 793 | 
 794 | **Description:** Returns the customer's second name.
 795 | 
 796 | **Returns:**
 797 | 
 798 | the customer's second name.
 799 | 
 800 | ---
 801 | 
 802 | ### getSuffix
 803 | 
 804 | **Signature:** `getSuffix() : String`
 805 | 
 806 | **Description:** Returns the customer's suffix, such as "Jr." or "Sr.".
 807 | 
 808 | **Returns:**
 809 | 
 810 | the customer's suffix.
 811 | 
 812 | ---
 813 | 
 814 | ### getTaxID
 815 | 
 816 | **Signature:** `getTaxID() : String`
 817 | 
 818 | **Description:** Returns the tax ID value. The value is returned either plain text if the current context allows plain text access, or if it's not allowed, the ID value will be returned masked. The following criteria must be met in order to have plain text access: the method call must happen in the context of a storefront request; the current customer must be registered and authenticated; it is the profile of the current customer; and the current protocol is HTTPS.
 819 | 
 820 | **Returns:**
 821 | 
 822 | the tax ID value
 823 | 
 824 | ---
 825 | 
 826 | ### getTaxIDMasked
 827 | 
 828 | **Signature:** `getTaxIDMasked() : String`
 829 | 
 830 | **Description:** Returns the masked value of the tax ID.
 831 | 
 832 | **Returns:**
 833 | 
 834 | the masked value of the tax ID
 835 | 
 836 | ---
 837 | 
 838 | ### getTaxIDType
 839 | 
 840 | **Signature:** `getTaxIDType() : EnumValue`
 841 | 
 842 | **Description:** Returns the tax ID type.
 843 | 
 844 | **Returns:**
 845 | 
 846 | the tax ID type
 847 | 
 848 | ---
 849 | 
 850 | ### getTitle
 851 | 
 852 | **Signature:** `getTitle() : String`
 853 | 
 854 | **Description:** Returns the customer's title, such as "Mrs" or "Mr".
 855 | 
 856 | **Returns:**
 857 | 
 858 | the customer's title.
 859 | 
 860 | ---
 861 | 
 862 | ### getWallet
 863 | 
 864 | **Signature:** `getWallet() : Wallet`
 865 | 
 866 | **Description:** Returns the wallet of this customer.
 867 | 
 868 | **Returns:**
 869 | 
 870 | the wallet of this customer.
 871 | 
 872 | ---
 873 | 
 874 | ### isFemale
 875 | 
 876 | **Signature:** `isFemale() : boolean`
 877 | 
 878 | **Description:** Indicates that the customer is female when set to true.
 879 | 
 880 | **Returns:**
 881 | 
 882 | true if the customer is a female, false otherwise.
 883 | 
 884 | ---
 885 | 
 886 | ### isMale
 887 | 
 888 | **Signature:** `isMale() : boolean`
 889 | 
 890 | **Description:** Indicates that the customer is male when set to true.
 891 | 
 892 | **Returns:**
 893 | 
 894 | true if the customer is a male, false otherwise.
 895 | 
 896 | ---
 897 | 
 898 | ### setBirthday
 899 | 
 900 | **Signature:** `setBirthday(aValue : Date) : void`
 901 | 
 902 | **Description:** Sets the customer's birthday as a date.
 903 | 
 904 | **Parameters:**
 905 | 
 906 | - `aValue`: the customer's birthday as a date.
 907 | 
 908 | ---
 909 | 
 910 | ### setCompanyName
 911 | 
 912 | **Signature:** `setCompanyName(aValue : String) : void`
 913 | 
 914 | **Description:** Sets the customer's company name.
 915 | 
 916 | **Parameters:**
 917 | 
 918 | - `aValue`: the customer's company name.
 919 | 
 920 | ---
 921 | 
 922 | ### setEmail
 923 | 
 924 | **Signature:** `setEmail(aValue : String) : void`
 925 | 
 926 | **Description:** Sets the customer's email address.
 927 | 
 928 | **Parameters:**
 929 | 
 930 | - `aValue`: the customer's email address.
 931 | 
 932 | ---
 933 | 
 934 | ### setFax
 935 | 
 936 | **Signature:** `setFax(number : String) : void`
 937 | 
 938 | **Description:** Sets the fax number to use for the customer. The length is restricted to 32 characters.
 939 | 
 940 | **Parameters:**
 941 | 
 942 | - `number`: the fax number to use for the customer.
 943 | 
 944 | ---
 945 | 
 946 | ### setFirstName
 947 | 
 948 | **Signature:** `setFirstName(aValue : String) : void`
 949 | 
 950 | **Description:** Sets the customer's first name.
 951 | 
 952 | **Parameters:**
 953 | 
 954 | - `aValue`: the customer's first name.
 955 | 
 956 | ---
 957 | 
 958 | ### setGender
 959 | 
 960 | **Signature:** `setGender(aValue : Number) : void`
 961 | 
 962 | **Description:** Sets the customer's gender.
 963 | 
 964 | **Parameters:**
 965 | 
 966 | - `aValue`: the customer's gender.
 967 | 
 968 | ---
 969 | 
 970 | ### setJobTitle
 971 | 
 972 | **Signature:** `setJobTitle(aValue : String) : void`
 973 | 
 974 | **Description:** Sets the customer's job title.
 975 | 
 976 | **Parameters:**
 977 | 
 978 | - `aValue`: the customer's job title.
 979 | 
 980 | ---
 981 | 
 982 | ### setLastName
 983 | 
 984 | **Signature:** `setLastName(aValue : String) : void`
 985 | 
 986 | **Description:** Sets the customer's last name.
 987 | 
 988 | **Parameters:**
 989 | 
 990 | - `aValue`: the customer's last name.
 991 | 
 992 | ---
 993 | 
 994 | ### setPhoneBusiness
 995 | 
 996 | **Signature:** `setPhoneBusiness(number : String) : void`
 997 | 
 998 | **Description:** Sets the business phone number to use for the customer. The length is restricted to 32 characters.
 999 | 
1000 | **Parameters:**
1001 | 
1002 | - `number`: the business phone number to use for the customer.
1003 | 
1004 | ---
1005 | 
1006 | ### setPhoneHome
1007 | 
1008 | **Signature:** `setPhoneHome(number : String) : void`
1009 | 
1010 | **Description:** Sets the phone number to use for the customer. The length is restricted to 32 characters.
1011 | 
1012 | **Parameters:**
1013 | 
1014 | - `number`: the phone number to use for the customer.
1015 | 
1016 | ---
1017 | 
1018 | ### setPhoneMobile
1019 | 
1020 | **Signature:** `setPhoneMobile(number : String) : void`
1021 | 
1022 | **Description:** Sets the mobile phone number to use for the customer. The length is restricted to 32 characters.
1023 | 
1024 | **Parameters:**
1025 | 
1026 | - `number`: the mobile phone number to use for the customer.
1027 | 
1028 | ---
1029 | 
1030 | ### setPreferredLocale
1031 | 
1032 | **Signature:** `setPreferredLocale(aValue : String) : void`
1033 | 
1034 | **Description:** Sets the customer's preferred locale.
1035 | 
1036 | **Parameters:**
1037 | 
1038 | - `aValue`: the customer's preferred locale.
1039 | 
1040 | ---
1041 | 
1042 | ### setSaluation
1043 | 
1044 | **Signature:** `setSaluation(salutation : String) : void`
1045 | 
1046 | **Description:** Sets the salutation to use for the customer.
1047 | 
1048 | **Deprecated:**
1049 | 
1050 | Use setSalutation(String)
1051 | 
1052 | **Parameters:**
1053 | 
1054 | - `salutation`: the salutation to use for the customer.
1055 | 
1056 | ---
1057 | 
1058 | ### setSalutation
1059 | 
1060 | **Signature:** `setSalutation(salutation : String) : void`
1061 | 
1062 | **Description:** Sets the salutation to use for the customer.
1063 | 
1064 | **Parameters:**
1065 | 
1066 | - `salutation`: the salutation to use for the customer.
1067 | 
1068 | ---
1069 | 
1070 | ### setSecondName
1071 | 
1072 | **Signature:** `setSecondName(aValue : String) : void`
1073 | 
1074 | **Description:** Sets the customer's second name.
1075 | 
1076 | **Parameters:**
1077 | 
1078 | - `aValue`: the customer's second name.
1079 | 
1080 | ---
1081 | 
1082 | ### setSuffix
1083 | 
1084 | **Signature:** `setSuffix(aValue : String) : void`
1085 | 
1086 | **Description:** Sets the the customer's suffix.
1087 | 
1088 | **Parameters:**
1089 | 
1090 | - `aValue`: the customer's suffix.
1091 | 
1092 | ---
1093 | 
1094 | ### setTaxID
1095 | 
1096 | **Signature:** `setTaxID(taxID : String) : void`
1097 | 
1098 | **Description:** Sets the tax ID value. The value can be set if the current context allows write access. The current context allows write access if the currently logged in user owns this profile and the connection is secured.
1099 | 
1100 | **Parameters:**
1101 | 
1102 | - `taxID`: the tax ID value to set
1103 | 
1104 | ---
1105 | 
1106 | ### setTaxIDType
1107 | 
1108 | **Signature:** `setTaxIDType(taxIdType : String) : void`
1109 | 
1110 | **Description:** Sets the tax ID type.
1111 | 
1112 | **Parameters:**
1113 | 
1114 | - `taxIdType`: the tax ID type to set
1115 | 
1116 | ---
1117 | 
1118 | ### setTitle
1119 | 
1120 | **Signature:** `setTitle(aValue : String) : void`
1121 | 
1122 | **Description:** Sets the customer's title.
1123 | 
1124 | **Parameters:**
1125 | 
1126 | - `aValue`: the customer's title.
1127 | 
1128 | ---
```

--------------------------------------------------------------------------------
/docs-site/pages/FeaturesPage.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React from 'react';
  2 | import { NavLink } from 'react-router-dom';
  3 | import SEO from '../components/SEO';
  4 | import BreadcrumbSchema from '../components/BreadcrumbSchema';
  5 | import StructuredData from '../components/StructuredData';
  6 | import { H1, PageSubtitle, H2, H3 } from '../components/Typography';
  7 | import { Collapsible } from '../components/Collapsible';
  8 | import { SITE_DATES } from '../constants';
  9 | 
 10 | const badge = (label: string) => (
 11 |   <span className="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium mr-2 mb-2 bg-slate-100 text-slate-700 border border-slate-200">{label}</span>
 12 | );
 13 | 
 14 | const FeaturesPage: React.FC = () => {
 15 |   const featuresStructuredData = {
 16 |     "@context": "https://schema.org",
 17 |     "@type": "TechArticle",
 18 |     "headline": "Features & Capabilities - SFCC Development MCP Server",
 19 |   "description": "Comprehensive overview of SFCC Development MCP Server features: documentation access, real-time log & job log analysis, system & custom object exploration, site preferences, cartridge generation, best practices, and AI-powered development tools.",
 20 |     "author": {
 21 |       "@type": "Person",
 22 |       "name": "Thomas Theunen"
 23 |     },
 24 |     "publisher": {
 25 |       "@type": "Person",
 26 |       "name": "Thomas Theunen"
 27 |     },
 28 |     "datePublished": SITE_DATES.PUBLISHED,
 29 |     "dateModified": SITE_DATES.MODIFIED,
 30 |     "url": "https://sfcc-mcp-dev.rhino-inquisitor.com/features/",
 31 |     "about": [
 32 |       {
 33 |         "@type": "SoftwareApplication",
 34 |         "name": "SFCC Development MCP Server",
 35 |         "applicationCategory": "DeveloperApplication",
 36 |         "operatingSystem": "Node.js",
 37 |         "offers": {
 38 |           "@type": "Offer",
 39 |           "price": "0",
 40 |           "priceCurrency": "USD",
 41 |           "availability": "https://schema.org/InStock"
 42 |         }
 43 |       }
 44 |     ],
 45 |     "mainEntity": {
 46 |       "@type": "SoftwareApplication",
 47 |       "name": "SFCC Development MCP Server",
 48 |       "applicationCategory": "DeveloperApplication",
 49 |       "operatingSystem": "Node.js",
 50 |       "offers": {
 51 |         "@type": "Offer",
 52 |         "price": "0",
 53 |         "priceCurrency": "USD",
 54 |         "availability": "https://schema.org/InStock"
 55 |       },
 56 |       "featureList": [
 57 |         "SFCC API Documentation Access",
 58 |   "Real-time Log & Job Log Analysis", 
 59 |   "System & Custom Object Exploration",
 60 |         "Cartridge Generation",
 61 |         "Best Practices Guides",
 62 |         "AI Assistant Integration"
 63 |       ]
 64 |     }
 65 |   };
 66 | 
 67 |   return (
 68 |     <div className="max-w-6xl mx-auto px-6 py-12">
 69 |       <SEO 
 70 |         title="Features & Capabilities"
 71 |   description="Comprehensive overview of SFCC Development MCP Server features: documentation access, real-time log & job log analysis, system & custom object exploration, site preferences, cartridge generation, best practices, and AI-powered development tools."
 72 |         keywords="SFCC MCP features, Commerce Cloud development tools, SFCC documentation access, log analysis tools, system object tools, cartridge generation, SFCC best practices, AI development features"
 73 |         canonical="/features/"
 74 |         ogType="article"
 75 |       />
 76 |       <BreadcrumbSchema items={[
 77 |         { name: "Home", url: "/" },
 78 |         { name: "Features", url: "/features/" }
 79 |       ]} />
 80 |       <StructuredData structuredData={featuresStructuredData} />
 81 |       
 82 |       <header className="mb-14 text-center">
 83 |         <div className="inline-flex items-center gap-2 bg-gradient-to-r from-blue-600 to-purple-600 text-white px-4 py-2 rounded-full text-sm font-medium mb-6">
 84 |           <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clipRule="evenodd"/></svg>
 85 |           Feature Surface
 86 |         </div>
 87 |         <H1 id="features" className="text-5xl md:text-6xl font-extrabold bg-gradient-to-r from-gray-900 via-blue-900 to-purple-900 bg-clip-text text-transparent mb-6">Discover & Apply Faster</H1>
 88 |         <PageSubtitle className="text-xl md:text-2xl text-gray-600 max-w-4xl mx-auto leading-relaxed">
 89 |           Lean overview of every capability—expand only what you need. Optimized for AI-assisted flows and rapid onboarding.
 90 |         </PageSubtitle>
 91 | 
 92 |           </header>
 93 |                   <div className="flex flex-wrap gap-2 mt-2">
 94 |           {badge('Docs')}
 95 |           {badge('SFRA')}
 96 |           {badge('Best Practices')}
 97 |           {badge('Logs')}
 98 |           {badge('Jobs')}
 99 |     {badge('Objects')}
100 |           {badge('Code Versions')}
101 |           {badge('Security')}
102 |         </div>
103 |         <div className="mb-6 rounded-lg border border-blue-200 bg-blue-50 p-4 text-sm text-blue-800">
104 |           <strong className="block font-semibold text-blue-900">Getting started</strong>
105 |           <ul className="mt-2 list-disc pl-5 space-y-1">
106 |             <li>Generate a cartridge with <code>generate_cartridge_structure</code></li>
107 |             <li>Or ask your AI assistant: <em>"Show me dw.catalog.Product methods"</em></li>
108 |           </ul>
109 |           <p className="mt-3 m-0 text-blue-800">Ready for deeper insight? Expand the sections below.</p>
110 |         </div>
111 |     
112 | 
113 |       {/* DEEP DIVES */}
114 |   <section className="space-y-8" aria-label="Feature deep dives">
115 |         <Collapsible id="cartridge-generation" title="🚀 Cartridge Generation" defaultOpen>
116 |           <p className="text-sm">Create a production-ready SFCC cartridge (or full project) in seconds via <code>generate_cartridge_structure</code>.</p>
117 |           <ul className="list-disc pl-5 text-sm space-y-1">
118 |             <li>Controllers, models, ISML templates, static assets, config, lint, build</li>
119 |             <li>Full project mode adds package.json, Webpack, linting, scripts</li>
120 |             <li>Direct file creation in specified target path (no manual copy)</li>
121 |             <li>Consistent patterns for AI to reason about project shape</li>
122 |           </ul>
123 |           <div className="mt-4 text-xs text-slate-600">
124 |             <p className="font-semibold mb-1 text-slate-700">Try asking:</p>
125 |             <ul className="list-disc pl-4 space-y-1">
126 |               <li>"Generate a cartridge named <em>training_core</em> and show the created directories."</li>
127 |               <li>"What files does generate_cartridge_structure create in full project mode?"</li>
128 |               <li>"Add a controller skeleton to my generated cartridge for listing products."</li>
129 |             </ul>
130 |           </div>
131 |         </Collapsible>
132 | 
133 |     <Collapsible id="sfcc-best-practices-guides" title="📚 Best Practices, Guides & Hook References">
134 |       <p className="text-sm mb-2">Structured guidance across 13 core development domains plus searchable OCAPI / SCAPI hook reference tables with extension point signatures.</p>
135 |             <div className="grid sm:grid-cols-2 gap-4 text-sm">
136 |               <div>
137 |                 <h4 className="font-semibold mb-1">Guides</h4>
138 |                 <ul className="list-disc pl-4 space-y-1">
139 |                   <li>Cartridge creation & architecture</li>
140 |                   <li>ISML templates (security & performance)</li>
141 |                   <li>Job framework patterns</li>
142 |                   <li>LocalServiceRegistry integrations</li>
143 |                   <li>SFRA controllers & models</li>
144 |                   <li>SFRA client-side JavaScript architecture</li>
145 |                   <li>SFRA SCSS theming & override discipline</li>
146 |                   <li>Custom SCAPI endpoints</li>
147 |                 </ul>
148 |               </div>
149 |               <div>
150 |                 <h4 className="font-semibold mb-1">Also Covers</h4>
151 |                 <ul className="list-disc pl-4 space-y-1">
152 |                   <li>OCAPI & SCAPI hooks</li>
153 |                   <li>Security hardening (OWASP)</li>
154 |                   <li>Performance & scalability tactics</li>
155 |                   <li>Search across all guides</li>
156 |                   <li>Hook reference tables (OCAPI / SCAPI)</li>
157 |                 </ul>
158 |               </div>
159 |             </div>
160 |             <div className="mt-4 text-xs text-slate-600">
161 |               <p className="font-semibold mb-1 text-slate-700">Try asking:</p>
162 |               <ul className="list-disc pl-4 space-y-1">
163 |                 <li>"Show me security recommendations for ISML templates."</li>
164 |                 <li>"List OCAPI hook points for product import and explain when to use each."</li>
165 |                 <li>"Summarize performance best practices relevant to checkout."</li>
166 |               </ul>
167 |             </div>
168 |         </Collapsible>
169 | 
170 |         <Collapsible id="sfcc-documentation" title="🔍 SFCC API Documentation">
171 |           <p className="text-sm">Query the entire <code>dw.*</code> namespace surface area.</p>
172 |           <ul className="list-disc pl-5 text-sm space-y-1">
173 |             <li>Get class info (properties, methods, descriptions)</li>
174 |             <li>Search classes & methods with partial matching</li>
175 |             <li>List all classes for discovery</li>
176 |             <li>Retrieve raw markdown documentation</li>
177 |           </ul>
178 |           <div className="mt-4 text-xs text-slate-600">
179 |             <p className="font-semibold mb-1 text-slate-700">Try asking:</p>
180 |             <ul className="list-disc pl-4 space-y-1">
181 |               <li>"What methods does dw.catalog.Product have for pricing?"</li>
182 |               <li>"Search SFCC classes related to inventory."</li>
183 |               <li>"Show raw documentation for dw.system.Transaction."</li>
184 |             </ul>
185 |           </div>
186 |         </Collapsible>
187 | 
188 |         <Collapsible id="sfra-docs" title="🏗️ Enhanced SFRA Documentation">
189 |           <p className="text-sm">26+ documents with smart categorization (core, product, order, customer, pricing, store, other).</p>
190 |           <ul className="list-disc pl-5 text-sm space-y-1">
191 |             <li>Relevance-scored search with highlighted context</li>
192 |             <li>Category filtering and model coverage</li>
193 |             <li>Core classes: server, request, response, querystring, render</li>
194 |             <li>Extensive product/order/customer/pricing/store models</li>
195 |           </ul>
196 |           <div className="mt-4 text-xs text-slate-600">
197 |             <p className="font-semibold mb-1 text-slate-700">Try asking:</p>
198 |             <ul className="list-disc pl-4 space-y-1">
199 |               <li>"Search SFRA docs for middleware examples."</li>
200 |               <li>"Show differences between product-full and product-tile models."</li>
201 |               <li>"List order-related SFRA documents and summarize each."</li>
202 |             </ul>
203 |           </div>
204 |         </Collapsible>
205 | 
206 |         <Collapsible id="logs" title="📊 Log & Job Log Analysis">
207 |           <p className="text-sm mb-2">Real-time visibility into runtime behaviour plus deep job execution insight (multi-level logs in single files).</p>
208 |           <div className="grid sm:grid-cols-2 gap-4 text-sm">
209 |             <div>
210 |               <h4 className="font-semibold mb-1">Runtime Logs</h4>
211 |               <ul className="list-disc pl-4 space-y-1">
212 |                 <li>Latest error / warn / info / debug</li>
213 |                 <li>Pattern search & daily summarization</li>
214 |                 <li>Full file listing & tail reads</li>
215 |                 <li>Health & recurrence analysis</li>
216 |               </ul>
217 |             </div>
218 |             <div>
219 |               <h4 className="font-semibold mb-1">Job Logs</h4>
220 |               <ul className="list-disc pl-4 space-y-1">
221 |                 <li>Latest job log files</li>
222 |                 <li>Search by job name or pattern</li>
223 |                 <li>Execution summary (steps, timings)</li>
224 |                 <li>Unified multi-level parsing</li>
225 |               </ul>
226 |             </div>
227 |           </div>
228 |           <div className="mt-3 text-xs text-amber-800 bg-amber-50 border border-amber-200 rounded px-3 py-2">
229 |             <strong className="font-semibold">Requires full mode:</strong> WebDAV / log tools need sandbox <code>username</code> & <code>password</code> (basic auth) plus any required realm access.
230 |           </div>
231 |           <div className="mt-4 text-xs text-slate-600">
232 |             <p className="font-semibold mb-1 text-slate-700">Try asking:</p>
233 |             <ul className="list-disc pl-4 space-y-1">
234 |               <li>"Show the latest 10 error log entries containing 'OCAPI'."</li>
235 |               <li>"Summarize today’s log health and top recurring issues."</li>
236 |               <li>"Get the latest job execution summary for ProductFeedJob."</li>
237 |             </ul>
238 |           </div>
239 |         </Collapsible>
240 | 
241 |         <Collapsible id="system-objects" title="⚙️ System & Custom Objects / Data Model">
242 |           <p className="text-sm">Explore system & custom object schemas, attributes, groups and site preferences with advanced querying.</p>
243 |           <ul className="list-disc pl-5 text-sm space-y-1">
244 |             <li>List all system objects with metadata</li>
245 |             <li>Attribute & group searches (boolean, text, sort)</li>
246 |             <li>Custom object attribute discovery (targeted or match-all queries)</li>
247 |             <li>Site preference group & value exploration</li>
248 |           </ul>
249 |           <div className="mt-3 text-xs text-amber-800 bg-amber-50 border border-amber-200 rounded px-3 py-2">
250 |             <strong className="font-semibold">Requires full mode:</strong> OCAPI system object & preference tools need <code>client_id</code> and <code>client_secret</code> with proper scopes (Data API + Shop as applicable).
251 |           </div>
252 |           <div className="mt-4 text-xs text-slate-600">
253 |             <p className="font-semibold mb-1 text-slate-700">Try asking:</p>
254 |             <ul className="list-disc pl-4 space-y-1">
255 |               <li>"List custom attributes for Product containing 'brand'."</li>
256 |               <li>"Search site preferences in the SEO group for description fields."</li>
257 |               <li>"Show attribute definitions for custom object Global_String matching 'locale'."</li>
258 |             </ul>
259 |           </div>
260 |         </Collapsible>
261 | 
262 |         <Collapsible id="code-versions" title="🔄 Code Version Management">
263 |           <p className="text-sm">Manage deployment states directly via MCP.</p>
264 |           <ul className="list-disc pl-5 text-sm space-y-1">
265 |             <li>List available code versions</li>
266 |             <li>Activate version to resolve endpoint or job issues</li>
267 |             <li>AI can propose switches based on log diagnostics</li>
268 |           </ul>
269 |           <div className="mt-3 text-xs text-amber-800 bg-amber-50 border border-amber-200 rounded px-3 py-2">
270 |             <strong className="font-semibold">Requires full mode:</strong> Code version tools require OCAPI <code>client_id</code> and <code>client_secret</code> authorized for code/version management.
271 |           </div>
272 |           <div className="mt-4 text-xs text-slate-600">
273 |             <p className="font-semibold mb-1 text-slate-700">Try asking:</p>
274 |             <ul className="list-disc pl-4 space-y-1">
275 |               <li>"List code versions and highlight the active one."</li>
276 |               <li>"Activate code version <em>int_release_2025_09</em>."</li>
277 |               <li>"Which inactive code versions look safe to remove?"</li>
278 |             </ul>
279 |           </div>
280 |         </Collapsible>
281 | 
282 |         <Collapsible id="security-performance" title="🛡️ Security & Performance">
283 |           <div className="grid sm:grid-cols-2 gap-6 text-sm">
284 |             <div>
285 |               <h4 className="font-semibold mb-1">Security</h4>
286 |               <ul className="list-disc pl-4 space-y-1">
287 |                 <li>Credential isolation & never persisted plaintext</li>
288 |                 <li>Path traversal & input validation guards</li>
289 |                 <li>Scoped tool surface (principle of least privilege)</li>
290 |                 <li>Structured error sanitization</li>
291 |               </ul>
292 |             </div>
293 |             <div>
294 |               <h4 className="font-semibold mb-1">Performance</h4>
295 |               <ul className="list-disc pl-4 space-y-1">
296 |                 <li>Layered caching & deduplicated requests</li>
297 |                 <li>Chunked log tailing (range reads)</li>
298 |                 <li>Lazy loading of heavy docs</li>
299 |                 <li>Resource bounding for single-dev usage</li>
300 |               </ul>
301 |             </div>
302 |           </div>
303 |         </Collapsible>
304 | 
305 |         <Collapsible id="ai-integration" title="🤖 AI Integration Rationale">
306 |           <p className="text-sm">Designed so assistants like GitHub Copilot, Claude, and Cursor can produce higher-quality SFCC code with deterministic tool surfaces and enriched local context.</p>
307 |           <ul className="list-disc pl-5 text-sm space-y-1">
308 |             <li>Deterministic tool naming & argument shapes</li>
309 |             <li>High-signal, low-noise responses (agent friendly)</li>
310 |             <li>Semantic grouping reduces prompt tokens</li>
311 |             <li>Guides & raw docs unify knowledge surface</li>
312 |           </ul>
313 |         </Collapsible>
314 |       </section>
315 | 
316 |       {/* NEXT STEPS (aligned with home/config CTA style) */}
317 |       <section className="mt-20 mb-12 text-center" aria-labelledby="next-steps">
318 |         <H2 id="next-steps" className="text-3xl font-bold mb-4">🔗 Next Steps</H2>
319 |         <p className="text-sm md:text-base text-gray-600 max-w-2xl mx-auto mb-8">Pick a direction—inspect the precise tool surface first or jump straight into multi-step usage patterns.</p>
320 |         <div className="flex flex-col sm:flex-row gap-4 justify-center mb-10">
321 |           <NavLink 
322 |             to="/tools/" 
323 |             className="group bg-gradient-to-r from-blue-600 to-purple-600 text-white px-8 py-4 rounded-xl font-semibold text-lg shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 no-underline hover:no-underline focus:no-underline"
324 |           >
325 |             Browse Tools
326 |             <span className="ml-2 group-hover:translate-x-1 inline-block transition-transform">→</span>
327 |           </NavLink>
328 |           <NavLink 
329 |             to="/examples/" 
330 |             className="border-2 border-gray-300 text-gray-700 px-8 py-4 rounded-xl font-semibold text-lg hover:border-blue-500 hover:text-blue-600 transition-all duration-300 no-underline hover:no-underline focus:no-underline"
331 |           >
332 |             Examples & Use Cases
333 |           </NavLink>
334 |         </div>
335 |         <div className="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto mb-8 text-left">
336 |           <div className="rounded-xl border border-gray-200 bg-white p-5">
337 |             <h3 className="font-semibold text-sm mb-2">Need exact tool names?</h3>
338 |             <p className="text-xs text-gray-600">The tools catalog lists arguments & intent so you can craft precise, low-token prompts.</p>
339 |           </div>
340 |           <div className="rounded-xl border border-blue-200 bg-blue-50 p-5">
341 |             <h3 className="font-semibold text-sm mb-2 text-blue-800">Prefer guided flows?</h3>
342 |             <p className="text-xs text-blue-800">Example sequences show chained usage (docs → logs → versions) ready to reuse.</p>
343 |           </div>
344 |         </div>
345 |         <div className="mx-auto max-w-xl p-4 bg-blue-50 border border-blue-200 rounded-lg text-sm text-blue-800">
346 |           💡 <strong>Tip:</strong> Ask: <em>"Suggest a workflow to inspect a product attribute then trace related log errors using available tools."</em>
347 |         </div>
348 |       </section>
349 |     </div>
350 |   );
351 | };
352 | 
353 | export default FeaturesPage;
354 | 
```

--------------------------------------------------------------------------------
/tests/mcp/node/get-available-best-practice-guides.docs-only.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * Programmatic tests for get_available_best_practice_guides tool
  3 |  * 
  4 |  * These tests provide advanced verification capabilities beyond YAML pattern matching,
  5 |  * including dynamic validation, comprehensive content analysis,
  6 |  * and advanced error categorization for the SFCC best practice guides functionality.
  7 |  * 
  8 |  * Response format discovered via aegis query:
  9 |  * - Success: { content: [{ type: "text", text: "[{\"name\":\"guide_name\",\"title\":\"Guide Title\",\"description\":\"...\"}]" }], isError: false }
 10 |  * - Always successful: No isError field or error conditions in docs-only mode
 11 |  * - Ignores extra parameters: Gracefully handles unexpected parameters
 12 |  * - Comprehensive: Returns 13 best practice guides across SFCC development areas
 13 |  */
 14 | 
 15 | import { test, describe, before, after, beforeEach } from 'node:test';
 16 | import { strict as assert } from 'node:assert';
 17 | import { connect } from 'mcp-aegis';
 18 | 
 19 | /**
 20 |  * Content analysis utility for SFCC best practice guides validation
 21 |  */
 22 | class BestPracticeAnalyzer {
 23 |   constructor() {
 24 |     // Expected best practice guides based on project structure
 25 |     this.expectedGuides = [
 26 |       'cartridge_creation',
 27 |       'isml_templates', 
 28 |       'job_framework',
 29 |       'localserviceregistry',
 30 |       'ocapi_hooks',
 31 |       'scapi_hooks',
 32 |       'scapi_custom_endpoint',
 33 |       'sfra_controllers',
 34 |       'sfra_models',
 35 |       'sfra_client_side_js',
 36 |       'sfra_scss',
 37 |       'performance',
 38 |       'security'
 39 |     ];
 40 | 
 41 |     // Critical guides that must be present
 42 |     this.criticalGuides = [
 43 |       'cartridge_creation',
 44 |       'sfra_controllers',
 45 |       'ocapi_hooks',
 46 |       'scapi_hooks',
 47 |       'sfra_client_side_js',
 48 |       'sfra_scss',
 49 |       'security',
 50 |       'performance'
 51 |     ];
 52 | 
 53 |     // Guide categories for validation
 54 |     this.guideCategories = {
 55 |       core: ['cartridge_creation', 'isml_templates', 'job_framework'],
 56 |       sfra: ['sfra_controllers', 'sfra_models', 'sfra_client_side_js', 'sfra_scss'],
 57 |       api_hooks: ['ocapi_hooks', 'scapi_hooks', 'scapi_custom_endpoint'],
 58 |       services: ['localserviceregistry'],
 59 |       quality: ['performance', 'security']
 60 |     };
 61 | 
 62 |     // Required fields for each guide
 63 |     this.requiredFields = ['name', 'title', 'description'];
 64 |   }
 65 | 
 66 |   analyzeGuideList(guideArray) {
 67 |     const analysis = {
 68 |       totalGuides: guideArray.length,
 69 |       foundGuides: new Set(),
 70 |       missingGuides: [],
 71 |       missingCriticalGuides: [],
 72 |       foundCriticalGuides: [],
 73 |       invalidGuides: [],
 74 |       duplicates: [],
 75 |       guidesByCategory: {},
 76 |       validationErrors: []
 77 |     };
 78 | 
 79 |     // Initialize category counters
 80 |     Object.keys(this.guideCategories).forEach(category => {
 81 |       analysis.guidesByCategory[category] = [];
 82 |     });
 83 | 
 84 |     // Analyze each guide
 85 |     const seenGuides = new Set();
 86 |     guideArray.forEach((guide, index) => {
 87 |       // Validate guide structure
 88 |       const structureErrors = this.validateGuideStructure(guide, index);
 89 |       if (structureErrors.length > 0) {
 90 |         analysis.validationErrors.push(...structureErrors);
 91 |         analysis.invalidGuides.push(guide);
 92 |         return;
 93 |       }
 94 | 
 95 |       const guideName = guide.name;
 96 | 
 97 |       // Check for duplicates
 98 |       if (seenGuides.has(guideName)) {
 99 |         analysis.duplicates.push(guideName);
100 |         return;
101 |       }
102 |       seenGuides.add(guideName);
103 |       analysis.foundGuides.add(guideName);
104 | 
105 |       // Check critical guides
106 |       if (this.criticalGuides.includes(guideName)) {
107 |         analysis.foundCriticalGuides.push(guideName);
108 |       }
109 | 
110 |       // Categorize guide
111 |       let categorized = false;
112 |       Object.entries(this.guideCategories).forEach(([category, guides]) => {
113 |         if (guides.includes(guideName)) {
114 |           analysis.guidesByCategory[category].push(guideName);
115 |           categorized = true;
116 |         }
117 |       });
118 | 
119 |       if (!categorized) {
120 |         if (!analysis.guidesByCategory.other) {
121 |           analysis.guidesByCategory.other = [];
122 |         }
123 |         analysis.guidesByCategory.other.push(guideName);
124 |       }
125 |     });
126 | 
127 |     // Find missing guides
128 |     analysis.missingGuides = this.expectedGuides.filter(
129 |       guide => !analysis.foundGuides.has(guide)
130 |     );
131 | 
132 |     // Find missing critical guides
133 |     analysis.missingCriticalGuides = this.criticalGuides.filter(
134 |       guide => !analysis.foundCriticalGuides.includes(guide)
135 |     );
136 | 
137 |     return analysis;
138 |   }
139 | 
140 |   validateGuideStructure(guide, index) {
141 |     const errors = [];
142 | 
143 |     if (typeof guide !== 'object' || guide === null) {
144 |       errors.push(`Guide at index ${index} is not an object`);
145 |       return errors;
146 |     }
147 | 
148 |     // Check required fields
149 |     this.requiredFields.forEach(field => {
150 |       if (!Object.prototype.hasOwnProperty.call(guide, field)) {
151 |         errors.push(`Guide at index ${index} missing required field: ${field}`);
152 |       } else if (typeof guide[field] !== 'string') {
153 |         errors.push(`Guide at index ${index} field ${field} should be string, got ${typeof guide[field]}`);
154 |       } else if (guide[field].trim().length === 0) {
155 |         errors.push(`Guide at index ${index} field ${field} is empty`);
156 |       }
157 |     });
158 | 
159 |     // Validate name format (should be lowercase with underscores)
160 |     if (guide.name && !/^[a-z][a-z0-9_]*$/.test(guide.name)) {
161 |       errors.push(`Guide at index ${index} has invalid name format: ${guide.name}`);
162 |     }
163 | 
164 |     // Validate title format (should contain "Best Practices")
165 |     if (guide.title && !guide.title.includes('Best Practices')) {
166 |       errors.push(`Guide at index ${index} title should contain "Best Practices": ${guide.title}`);
167 |     }
168 | 
169 |     // Validate description length (should be meaningful)
170 |     if (guide.description && guide.description.length < 20) {
171 |       errors.push(`Guide at index ${index} description too short: ${guide.description.length} chars`);
172 |     }
173 | 
174 |     return errors;
175 |   }
176 | 
177 |   validateCompleteness(analysis) {
178 |     const issues = [];
179 | 
180 |     if (analysis.totalGuides < 13) {
181 |       issues.push(`Guide count too low: ${analysis.totalGuides} (expected 13+)`);
182 |     }
183 | 
184 |     if (analysis.missingGuides.length > 0) {
185 |       issues.push(`Missing guides: ${analysis.missingGuides.join(', ')}`);
186 |     }
187 | 
188 |     if (analysis.missingCriticalGuides.length > 0) {
189 |       issues.push(`Missing critical guides: ${analysis.missingCriticalGuides.join(', ')}`);
190 |     }
191 | 
192 |     if (analysis.duplicates.length > 0) {
193 |       issues.push(`Duplicate guides found: ${analysis.duplicates.length}`);
194 |     }
195 | 
196 |     if (analysis.invalidGuides.length > 0) {
197 |       issues.push(`Invalid guides found: ${analysis.invalidGuides.length}`);
198 |     }
199 | 
200 |     if (analysis.validationErrors.length > 0) {
201 |       issues.push(`Validation errors: ${analysis.validationErrors.length}`);
202 |     }
203 | 
204 |     return issues;
205 |   }
206 | }
207 | 
208 | /**
209 |  * Helper function to validate MCP response structure
210 |  */
211 | function assertValidMCPResponse(result) {
212 |   assert.ok(result.content, 'Response should have content');
213 |   assert.ok(Array.isArray(result.content), 'Content should be array');
214 |   assert.equal(typeof result.isError, 'boolean', 'isError should be boolean');
215 | }
216 | 
217 | describe('get_available_best_practice_guides Programmatic Tests', () => {
218 |   let client;
219 |   const bestPracticeAnalyzer = new BestPracticeAnalyzer();
220 | 
221 |   before(async () => {
222 |     client = await connect('./aegis.config.docs-only.json');
223 |   });
224 | 
225 |   after(async () => {
226 |     if (client?.connected) {
227 |       await client.disconnect();
228 |     }
229 |   });
230 | 
231 |   beforeEach(() => {
232 |     client.clearAllBuffers();
233 |   });
234 | 
235 |   describe('Protocol Compliance', () => {
236 |     test('should be properly connected to MCP server', async () => {
237 |       assert.ok(client.connected, 'Client should be connected');
238 |     });
239 | 
240 |     test('should have get_available_best_practice_guides tool available', async () => {
241 |       const tools = await client.listTools();
242 |       const guidesTool = tools.find(tool => tool.name === 'get_available_best_practice_guides');
243 |       
244 |       assert.ok(guidesTool, 'get_available_best_practice_guides tool should be available');
245 |       assert.equal(guidesTool.name, 'get_available_best_practice_guides');
246 |       assert.ok(guidesTool.description, 'Tool should have description');
247 |       assert.ok(guidesTool.inputSchema, 'Tool should have input schema');
248 |     });
249 | 
250 |     test('should have correct tool input schema', async () => {
251 |       const tools = await client.listTools();
252 |       const guidesTool = tools.find(tool => tool.name === 'get_available_best_practice_guides');
253 |       
254 |       assert.equal(guidesTool.inputSchema.type, 'object');
255 |       // get_available_best_practice_guides takes no required parameters
256 |       assert.ok(!guidesTool.inputSchema.required || guidesTool.inputSchema.required.length === 0);
257 |     });
258 |   });
259 | 
260 |   describe('Basic Functionality', () => {
261 |     test('should execute successfully with empty parameters', async () => {
262 |       const result = await client.callTool('get_available_best_practice_guides', {});
263 |       
264 |       assertValidMCPResponse(result);
265 |       assert.equal(result.isError, false, 'Should not return error');
266 |       assert.equal(result.content.length, 1, 'Should return single content item');
267 |       assert.equal(result.content[0].type, 'text', 'Content type should be text');
268 |     });
269 | 
270 |     test('should return valid JSON array in response', async () => {
271 |       const result = await client.callTool('get_available_best_practice_guides', {});
272 |       
273 |       assertValidMCPResponse(result);
274 |       const responseText = result.content[0].text;
275 |       
276 |       // Should be valid JSON
277 |       let guideArray;
278 |       assert.doesNotThrow(() => {
279 |         guideArray = JSON.parse(responseText);
280 |       }, 'Response should be valid JSON');
281 |       
282 |       assert.ok(Array.isArray(guideArray), 'Response should be JSON array');
283 |       assert.ok(guideArray.length > 0, 'Guide array should not be empty');
284 |     });
285 | 
286 |     test('should ignore additional parameters gracefully', async () => {
287 |       const result = await client.callTool('get_available_best_practice_guides', {
288 |         unexpectedParam: 'should be ignored',
289 |         anotherParam: 123,
290 |         objectParam: { nested: 'value' }
291 |       });
292 |       
293 |       assertValidMCPResponse(result);
294 |       assert.equal(result.isError, false, 'Should handle extra params gracefully');
295 |       
296 |       // Result should be identical to empty params call
297 |       const baselineResult = await client.callTool('get_available_best_practice_guides', {});
298 |       assert.equal(result.content[0].text, baselineResult.content[0].text, 
299 |         'Result should be identical regardless of extra params');
300 |     });
301 |   });
302 | 
303 |   describe('Content Quality and Completeness', () => {
304 |     test('should return comprehensive best practice guide coverage', async () => {
305 |       const result = await client.callTool('get_available_best_practice_guides', {});
306 |       const guideArray = JSON.parse(result.content[0].text);
307 |       const analysis = bestPracticeAnalyzer.analyzeGuideList(guideArray);
308 |       
309 |       // Validate completeness
310 |       const issues = bestPracticeAnalyzer.validateCompleteness(analysis);
311 |       assert.equal(issues.length, 0, `Content issues found: ${issues.join('; ')}`);
312 |       
313 |       // Verify substantial guide count
314 |       assert.ok(analysis.totalGuides >= 13, 
315 |         `Should have substantial guide count (got ${analysis.totalGuides})`);
316 |     });
317 | 
318 |     test('should include all critical best practice guides', async () => {
319 |       const result = await client.callTool('get_available_best_practice_guides', {});
320 |       const guideArray = JSON.parse(result.content[0].text);
321 |       const analysis = bestPracticeAnalyzer.analyzeGuideList(guideArray);
322 |       
323 |       bestPracticeAnalyzer.criticalGuides.forEach(guideName => {
324 |         assert.ok(analysis.foundGuides.has(guideName), 
325 |           `Missing critical guide: ${guideName}`);
326 |       });
327 |       
328 |       // All critical guides should be found
329 |       assert.equal(analysis.missingCriticalGuides.length, 0,
330 |         `Missing critical guides: ${analysis.missingCriticalGuides.join(', ')}`);
331 |     });
332 | 
333 |     test('should have proper guide structure for all guides', async () => {
334 |       const result = await client.callTool('get_available_best_practice_guides', {});
335 |       const guideArray = JSON.parse(result.content[0].text);
336 |       
337 |       guideArray.forEach((guide, index) => {
338 |         // Validate required fields
339 |         bestPracticeAnalyzer.requiredFields.forEach(field => {
340 |           assert.ok(Object.prototype.hasOwnProperty.call(guide, field), 
341 |             `Guide ${index} missing field: ${field}`);
342 |           assert.equal(typeof guide[field], 'string', 
343 |             `Guide ${index} field ${field} should be string`);
344 |           assert.ok(guide[field].trim().length > 0, 
345 |             `Guide ${index} field ${field} should not be empty`);
346 |         });
347 | 
348 |         // Validate name format
349 |         assert.ok(/^[a-z][a-z0-9_]*$/.test(guide.name), 
350 |           `Guide ${index} has invalid name format: ${guide.name}`);
351 | 
352 |         // Validate title contains "Best Practices"
353 |         assert.ok(guide.title.includes('Best Practices'), 
354 |           `Guide ${index} title should contain "Best Practices": ${guide.title}`);
355 | 
356 |         // Validate description length
357 |         assert.ok(guide.description.length >= 20, 
358 |           `Guide ${index} description too short: ${guide.description}`);
359 |       });
360 |     });
361 | 
362 |     test('should cover all major SFCC development areas', async () => {
363 |       const result = await client.callTool('get_available_best_practice_guides', {});
364 |       const guideArray = JSON.parse(result.content[0].text);
365 |       const analysis = bestPracticeAnalyzer.analyzeGuideList(guideArray);
366 |       
367 |       // Check each category has coverage
368 |       Object.entries(bestPracticeAnalyzer.guideCategories).forEach(([category, expectedGuides]) => {
369 |         const foundInCategory = analysis.guidesByCategory[category] || [];
370 |         assert.ok(foundInCategory.length > 0, 
371 |           `Category ${category} should have at least one guide`);
372 |         
373 |         // Check that most expected guides in category are present
374 |         const coverageRatio = foundInCategory.length / expectedGuides.length;
375 |         assert.ok(coverageRatio >= 0.5, 
376 |           `Category ${category} should have good coverage (${foundInCategory.length}/${expectedGuides.length})`);
377 |       });
378 |     });
379 | 
380 |     test('should not have duplicate or invalid guides', async () => {
381 |       const result = await client.callTool('get_available_best_practice_guides', {});
382 |       const guideArray = JSON.parse(result.content[0].text);
383 |       const analysis = bestPracticeAnalyzer.analyzeGuideList(guideArray);
384 |       
385 |       assert.equal(analysis.duplicates.length, 0, 
386 |         `Found duplicate guides: ${analysis.duplicates.join(', ')}`);
387 |       
388 |       assert.equal(analysis.invalidGuides.length, 0, 
389 |         `Found invalid guides: ${analysis.invalidGuides.length}`);
390 |       
391 |       assert.equal(analysis.validationErrors.length, 0, 
392 |         `Validation errors: ${analysis.validationErrors.join('; ')}`);
393 |     });
394 |   });
395 | 
396 |   describe('Content Validation', () => {
397 |     test('should include expected core guides', async () => {
398 |       const result = await client.callTool('get_available_best_practice_guides', {});
399 |       const guideArray = JSON.parse(result.content[0].text);
400 |       const guideNames = guideArray.map(guide => guide.name);
401 |       
402 |       // Check specific expected guides
403 |       const expectedCoreGuides = [
404 |         'cartridge_creation',
405 |         'sfra_controllers', 
406 |         'sfra_models',
407 |         'sfra_client_side_js',
408 |         'sfra_scss',
409 |         'ocapi_hooks',
410 |         'scapi_hooks',
411 |         'security',
412 |         'performance'
413 |       ];
414 |       
415 |       expectedCoreGuides.forEach(guideName => {
416 |         assert.ok(guideNames.includes(guideName), 
417 |           `Should include core guide: ${guideName}`);
418 |       });
419 |     });
420 | 
421 |     test('should have meaningful guide descriptions', async () => {
422 |       const result = await client.callTool('get_available_best_practice_guides', {});
423 |       const guideArray = JSON.parse(result.content[0].text);
424 |       
425 |       guideArray.forEach(guide => {
426 |         // Description should be substantial
427 |         assert.ok(guide.description.length >= 50, 
428 |           `Guide ${guide.name} description should be substantial: ${guide.description.length} chars`);
429 |         
430 |         // Description should be related to the guide name
431 |         const nameWords = guide.name.split('_');
432 |         const hasRelatedContent = nameWords.some(word => 
433 |           guide.description.toLowerCase().includes(word.toLowerCase())
434 |         );
435 |         assert.ok(hasRelatedContent || guide.description.toLowerCase().includes('sfcc') || 
436 |           guide.description.toLowerCase().includes('commerce'), 
437 |           `Guide ${guide.name} description should be related to guide name or SFCC`);
438 |       });
439 |     });
440 | 
441 |     test('should provide guides for different skill levels', async () => {
442 |       const result = await client.callTool('get_available_best_practice_guides', {});
443 |       const guideArray = JSON.parse(result.content[0].text);
444 |       
445 |       // Should have guides for beginners (cartridge creation)
446 |       const beginnerGuides = guideArray.filter(guide => 
447 |         guide.name.includes('cartridge') || 
448 |         guide.description.toLowerCase().includes('introduction') ||
449 |         guide.description.toLowerCase().includes('getting started')
450 |       );
451 |       
452 |       // Should have advanced guides (security, performance)
453 |       const advancedGuides = guideArray.filter(guide => 
454 |         guide.name.includes('security') || 
455 |         guide.name.includes('performance') ||
456 |         guide.description.toLowerCase().includes('advanced') ||
457 |         guide.description.toLowerCase().includes('optimization')
458 |       );
459 |       
460 |       assert.ok(beginnerGuides.length > 0, 'Should have guides for beginners');
461 |       assert.ok(advancedGuides.length > 0, 'Should have guides for advanced users');
462 |     });
463 |   });
464 | 
465 |   describe('Error Handling and Edge Cases', () => {
466 |     test('should handle malformed parameters gracefully', async () => {
467 |       // Test with various edge case parameters that are still valid objects
468 |       const edgeCases = [
469 |         {},  // Empty object (valid)
470 |         { malformed: 'data' },
471 |         { nested: { deeply: { malformed: 'data' } } },
472 |         { randomField: null },
473 |         { arrayField: [1, 2, 3] },
474 |         { booleanField: true }
475 |       ];
476 |       
477 |       for (const params of edgeCases) {
478 |         const result = await client.callTool('get_available_best_practice_guides', params);
479 |         
480 |         assertValidMCPResponse(result);
481 |         assert.equal(result.isError, false, 
482 |           `Should handle edge case gracefully: ${JSON.stringify(params)}`);
483 |         
484 |         // Result should still be valid
485 |         const guideArray = JSON.parse(result.content[0].text);
486 |         assert.ok(Array.isArray(guideArray), 'Should still return valid array');
487 |         assert.ok(guideArray.length > 0, 'Should still return guides');
488 |       }
489 |     });
490 | 
491 |     test('should maintain functionality under rapid consecutive calls', async () => {
492 |       const rapidCalls = 10;
493 |       const promises = Array.from({ length: rapidCalls }, () => 
494 |         client.callTool('get_available_best_practice_guides', {})
495 |       );
496 |       
497 |       const results = await Promise.all(promises);
498 |       
499 |       results.forEach((result, index) => {
500 |         assertValidMCPResponse(result);
501 |         assert.equal(result.isError, false, `Rapid call ${index} should succeed`);
502 |         
503 |         const guideArray = JSON.parse(result.content[0].text);
504 |         assert.ok(Array.isArray(guideArray), `Rapid call ${index} should return array`);
505 |         assert.ok(guideArray.length > 0, `Rapid call ${index} should return guides`);
506 |       });
507 |       
508 |       // All results should be identical
509 |       const firstResponse = results[0].content[0].text;
510 |       results.forEach((result, index) => {
511 |         assert.equal(result.content[0].text, firstResponse, 
512 |           `Rapid call ${index} result should be identical`);
513 |       });
514 |     });
515 |   });
516 | });
517 | 
```

--------------------------------------------------------------------------------
/docs/best-practices/sfra_scss.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## SFRA SCSS Best Practices (AI-Agent Optimized)
  2 | 
  3 | Purpose: Provide a concise, implementation‑ready reference for styling and theming Salesforce B2C Commerce SFRA storefronts using SCSS. Optimized for automated reasoning, safe overrides, upgrade resilience, and performant output.
  4 | 
  5 | ---
  6 | ## 1. Core Principles (Never Skip)
  7 | | Principle | Rule | Why It Exists | Action Pattern |
  8 | |-----------|------|---------------|----------------|
  9 | | Cartridge Overlay | Never edit `app_storefront_base` | Preserve upgrade path | Create mirror path in custom cartridge + import base first |
 10 | | Import First, Then Override | Always `@import '~base/...';` at top | Keeps base layout + utilities | File header snippet template |
 11 | | Single Source of Truth | Centralize tokens in `abstracts/_variables.scss` | Global theming + diffable changes | Import base → redefine only changed vars |
 12 | | Shallow Specificity | Max selector depth 2–3 | Predictable cascade | Use BEM instead of nested HTML chains |
 13 | | Mobile‑First | Base = smallest viewport | Less override churn | Add enhancements with `min-width` media mixin |
 14 | | Immutable Generated CSS | Never hand edit `/static/default/css` | Prevent drift | Recompile via build scripts only |
 15 | | Deterministic Build | Same inputs ⇒ same CSS | Debug + CI parity | Lock dependency versions |
 16 | 
 17 | Red flag triggers for re‑evaluation: deep nesting (>3), repeated hardcoded colors, `!important`, duplicated breakpoint literals, editing compiled CSS, or missing initial `@import`.
 18 | 
 19 | AI Guardrail (fast reject): If asked to "change base SCSS" directly, respond with an override strategy instead of editing base source.
 20 | 
 21 | ---
 22 | ## 2. Minimal Override Workflow (Algorithm)
 23 | 1. Inspect element in browser → capture compiled CSS filename (e.g. `product/detail.css`) & selector(s).
 24 | 2. Locate source in base: `app_storefront_base/cartridge/client/default/scss/<path>.scss`.
 25 | 3. Recreate identical relative path in custom cartridge under `cartridge/client/default/scss/`.
 26 | 4. Add header: `@import '~base/<path>';` (must be line 1; no preceding BOM/comment).
 27 | 5. Append scoped overrides (BEM, tokens, mixins only—no raw hex unless introducing new token).
 28 | 6. Run `npm run compile:scss` (or project alias) → deploy → verify cascade diff.
 29 | 7. If change is global (color, spacing, radius) prefer variable override in `_variables.scss` before component override.
 30 | 
 31 | Decision Tree:
 32 | | Goal | Location to Change First | Fallback If Not Available |
 33 | |------|--------------------------|---------------------------|
 34 | | Color / Font / Spacing shift | `_variables.scss` | Component partial override |
 35 | | Layout structure | ISML + classes | New component partial |
 36 | | Responsive tweak | Mixin breakpoints | Local media query (still via mixin) |
 37 | | Reusable visual pattern | New mixin | Placeholder + `@extend` |
 38 | | Plugin override | `vendors/` override partial | Scoped component patch |
 39 | 
 40 | Plugin Extension Nuances (Observed from `plugin_wishlists`):
 41 | * Keep plugin additions minimal: import base global first inside plugin `global.scss` (`@import "~base/global";`) then only plugin-specific partials.
 42 | * Do NOT re-import `variables` inside multiple plugin partials unless you need local compilation context—prefer central import in the entry file to avoid duplication.
 43 | * Avoid redefining spacing tokens mid-file (e.g. `$spacer` inside a feature file) unless intentionally creating a component-scoped token; document with a comment `// local token`.
 44 | * Consolidate repeated keyframes: If multiple alerts use identical fade animation, define it once in a shared partial (e.g. `components/_animations.scss`) instead of inline per component.
 45 | * When plugin needs base mixins (e.g. Bootstrap breakpoints) prefer `@import "~base/global";` rather than directly importing bootstrap again to reduce risk of version drift.
 46 | 
 47 | ---
 48 | ## 3. Canonical SCSS Directory (7‑1 Adapted)
 49 | ```
 50 | scss/
 51 |   abstracts/  (_variables.scss, _mixins.scss, _functions.scss)
 52 |   base/       (_reset.scss, _typography.scss, _base.scss)
 53 |   layout/     (_header.scss, _footer.scss, _navigation.scss, _grid.scss)
 54 |   components/ (_buttons.scss, _product-tile.scss, _carousel.scss, ...)
 55 |   pages/      (_home.scss, _checkout.scss, _pdp.scss, ...)
 56 |   vendors/    (_bootstrap-overrides.scss, _plugin-X.scss)
 57 |   global.scss  (imports only – no rules)
 58 | ```
 59 | 
 60 | Import Order (global.scss):
 61 | 1. abstracts (variables → mixins → functions)
 62 | 2. vendors
 63 | 3. base
 64 | 4. layout
 65 | 5. components
 66 | 6. pages
 67 | 
 68 | SFRA Reality Snapshot:
 69 | The core `global.scss` in `app_storefront_base` imports: `variables`, `skin/skinVariables`, bootstrap custom, overrides, utilities, icon libraries, then individual component partials. AI agents should treat this as the authoritative ordering when composing custom `global.scss` overlays: always import base `global` first in plugin/global contexts, then plugin additions.
 70 | 
 71 | Rule: No CSS selectors in `global.scss`; only `@import`.
 72 | 
 73 | ---
 74 | ## 4. Naming & Specificity Strategy (BEM + SFRA Conventions)
 75 | | Aspect | Rule | Example |
 76 | |--------|------|---------|
 77 | | Block | Semantic, domain concept | `.product-tile` |
 78 | | Element | `__` double underscore | `.product-tile__price` |
 79 | | Modifier | `--` double hyphen | `.product-tile--featured` |
 80 | | State (JS) | `is-` prefix (transient) | `.is-loading` |
 81 | | Utility | `u-` + purpose | `.u-hidden` |
 82 | 
 83 | Guidelines:
 84 | * Avoid HTML element chaining: prefer `.mini-cart__count` not `header .mini-cart span.count`.
 85 | * Never exceed 3 levels of specificity (block + element + modifier/state).
 86 | * Do not style IDs; reserve for scripting anchor only if necessary.
 87 | * Existing SFRA partials sometimes nest deeper (e.g. `.product-tile .tile-body .price .tiered .value`). When extending these, do NOT replicate entire nesting; extract just the block root with a modifier class you control (e.g. `.product-tile--compact`).
 88 | * For legacy classes not following BEM (e.g. `.wishlistTile`), retain original naming but annotate exception with justification comment to aid future refactor and silence lint selectively—in code: `/* stylelint-disable-line */`.
 89 | 
 90 | ---
 91 | ## 5. Token & Theme Management
 92 | Override pattern (`abstracts/_variables.scss`):
 93 | ```scss
 94 | @import '~base/variables'; // must stay first
 95 | // Theme – Colors
 96 | $primary: #0B5FFF; // Brand blue
 97 | $secondary: #00A676;
 98 | $body-bg: #F8F9FA;
 99 | $body-color: #212529;
100 | // Typography
101 | $font-family-sans-serif: 'Inter', 'Helvetica Neue', Arial, sans-serif;
102 | $font-family-serif: 'Merriweather', Georgia, serif;
103 | // Radii / Spacing
104 | $border-radius: 2px;
105 | $btn-padding-y: 0.5rem;
106 | $btn-padding-x: 1.125rem;
107 | ```
108 | Rules:
109 | * Only override variables you intentionally change.
110 | * Introduce new tokens with consistent naming (`$color-*`, `$space-*`, `$z-*`).
111 | * Never redefine variables in component partials—centralize.
112 | * If a plugin must create a small local scalar (e.g. `$spacer` for a one-off layout tweak), prefix with component context when possible (`$wishlist-spacer`) to avoid collision with global tokens.
113 | * Avoid re-importing base variables in each partial—imports are concatenated; repeated imports increase compile time and risk inconsistent overrides if order diverges.
114 | 
115 | ---
116 | ## 6. Reuse Mechanisms (Decision Matrix)
117 | | Need | Use | Why | Avoid If |
118 | |------|-----|-----|----------|
119 | | Static property bundle reused widely | `%placeholder` + `@extend` | Single output selector group | Needs argumentization |
120 | | Dynamic pattern w/ params | `@mixin` | Flexible + readable | Output duplication causes bloat |
121 | | Global theming value | `$variable` | Single update point | Represents multi-rule semantic block |
122 | | Conditional computed value | `@function` | Returns scalar for calc chains | Can be plain variable |
123 | 
124 | Responsive Mixin:
125 | ```scss
126 | $breakpoints: (
127 |   sm: 576px,
128 |   md: 768px,
129 |   lg: 992px,
130 |   xl: 1200px
131 | ) !default;
132 | 
133 | @mixin mq($bp) {
134 |   @if map-has-key($breakpoints, $bp) {
135 |     @media (min-width: map-get($breakpoints, $bp)) { @content; }
136 |   } @else { @warn 'Unknown breakpoint: #{$bp}'; }
137 | }
138 | ```
139 | 
140 | Usage:
141 | ```scss
142 | .product-tile { width: 100%; @include mq(md) { width: 50%; } }
143 | ```
144 | Bootstrap Breakpoints Interop:
145 | SFRA base uses Bootstrap's `media-breakpoint-*` mixins. When generating code referencing base breakpoints, prefer those existing mixins (`@include media-breakpoint-down(md) { ... }`) if the base already depends on Bootstrap, instead of introducing a parallel custom map to avoid divergence. Use the custom `$breakpoints` + `mq()` pattern only if project intentionally decouples from Bootstrap.
146 | 
147 | ---
148 | ## 7. Example Override (PDP Title)
149 | File: `product/detail.scss`
150 | ```scss
151 | @import '~base/product/detail'; // inherit base
152 | 
153 | .product-detail__name {
154 |   font-family: $font-family-serif;
155 |   font-size: 2.4rem;
156 |   color: $primary;
157 | }
158 | ```
159 | Compiled file resolved via `assets.addCss('/css/product/detail.css')` with cartridge path precedence—no ISML changes needed.
160 | AI Validation Step: After generation, assert snippet begins with base import and contains only new selectors or modified declarations—no duplication of entire base blocks.
161 | 
162 | ---
163 | ## 8. Component Patterns Library (Sample Snippets)
164 | Buttons (`components/_buttons.scss`):
165 | ```scss
166 | @import '~base/components/buttons';
167 | 
168 | .btn--pill { border-radius: 999rem; }
169 | .btn--outline-secondary {
170 |   color: $secondary; background: transparent; border: 1px solid $secondary;
171 |   &:hover { background: $secondary; color: #fff; }
172 | }
173 | ```
174 | 
175 | Product Tile (`components/_product-tile.scss`):
176 | ```scss
177 | @import '~base/components/product-tile';
178 | 
179 | .product-tile {
180 |   border: 1px solid transparent; transition: box-shadow .2s;
181 |   &:hover { box-shadow: 0 4px 12px rgba(0,0,0,.1); border-color: $gray-300; }
182 |   &__price--sale { color: $danger; font-weight: 600; }
183 | }
184 | 
185 | Refactor Tip: Instead of deeply nested overrides inside `.product-tile .tile-body .price .tiered .value`, target a purposeful modifier class (`.product-tile__price--tiered-value`) you add in ISML for long-term maintainability.
186 | ```
187 | 
188 | Checkout (`pages/_checkout.scss`):
189 | ```scss
190 | @import '~base/pages/checkout';
191 | .checkout-nav .nav-link { background:$gray-200; color:$gray-600;
192 |   &.active { background:$primary; color:#fff; }
193 | }
194 | ```
195 | 
196 | ---
197 | ## 9. Responsive Strategy
198 | | Rule | Implementation |
199 | |------|----------------|
200 | | Mobile‑first | Base styles = small viewport |
201 | | Progressive enhancement | `@include mq(md){...}` not `max-width` overrides |
202 | | Centralized breakpoints | `$breakpoints` map only |
203 | | Avoid pixel drift | Use tokens, not ad‑hoc numbers |
204 | 
205 | Anti‑Pattern: Desktop CSS first + mobile overrides via `@media (max-width)` – leads to override churn.
206 | Plugin Reality: Wishlist plugin currently mixes `media-breakpoint-up` and `media-breakpoint-down` usage; maintain consistency—choose mobile-first pattern (prefer `-up` for enhancements) and refactor legacy `-down` only when safe.
207 | 
208 | ---
209 | ## 10. Accessibility & Inclusive Styling
210 | | Concern | Checklist |
211 | |---------|-----------|
212 | | Color Contrast | Ensure WCAG AA (> 4.5:1 body, 3:1 large text). Token review before merge. |
213 | | Focus States | Visible focus ring; never remove outline without replacement. |
214 | | Reduced Motion | Gate large transitions with `@media (prefers-reduced-motion: reduce)` |
215 | | Hidden Content | Use utility `.u-visually-hidden` for screen-reader only text; avoid `display:none` for needed ARIA live content. |
216 | | Touch Targets | Minimum 44px height for interactive elements (buttons, filters). |
217 | 
218 | Utility Example:
219 | ```scss
220 | .u-visually-hidden { position:absolute!important; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
221 | ```
222 | Animation Respect:
223 | Provide a reduced-motion alternative when introducing keyframe animations (e.g. fading alerts). Centralize keyframes and wrap any large movement inside `@media (prefers-reduced-motion: no-preference)`; fallback: immediate end state.
224 | 
225 | ---
226 | ## 11. Performance Practices
227 | | Optimization | Action | Tooling |
228 | |--------------|--------|---------|
229 | | Critical CSS | Generate subset for key templates → inline via `store-skin` asset | `npm i critical` |
230 | | Prune Unused CSS | Integrate PurgeCSS scanning ISML & JS | PurgeCSS plugin |
231 | | Minification | Ensure prod build via `sgmf-scripts` produces minified bundle | CI check size |
232 | | Source Maps (dev only) | Enable for DX; disable prod | Sass compiler flag |
233 | | Font Strategy | Use WOFF2 + subsetting + `font-display: swap` | Font tooling |
234 | 
235 | Suggested CI Budget: Global CSS ≤ 250KB uncompressed (enterprise baseline) – fail build if regression > +15% week‑over‑week.
236 | Duplicate Import Scan: Add a CI grep step to detect repeated `@import "~base/variables";` inside plugin component partials—emit warning to consolidate.
237 | 
238 | ---
239 | ## 12. Linting & Quality Automation
240 | Install:
241 | ```
242 | npm i -D stylelint stylelint-config-standard-scss stylelint-declaration-strict-value
243 | ```
244 | Config (`.stylelintrc.json`):
245 | ```json
246 | {
247 |   "extends": "stylelint-config-standard-scss",
248 |   "plugins": ["stylelint-declaration-strict-value"],
249 |   "rules": {
250 |     "selector-class-pattern": ["^([a-z][a-z0-9]*)(__(?:[a-z0-9]+))?(--[a-z0-9-]+)?$", {"message": "Use BEM: block__element--modifier"}],
251 |     "max-nesting-depth": 2,
252 |     "declaration-no-important": true,
253 |     "scale-unlimited/declaration-strict-value": [["color", "background-color", "border-color", "font-size"], {"ignoreValues": ["inherit", "transparent", "currentColor"]}]
254 |   }
255 | }
256 | ```
257 | Script:
258 | ```json
259 | "scripts": { "lint:scss": "stylelint 'cartridges/**/*.scss' --cache" }
260 | ```
261 | Optional: pre‑commit hook with Husky + lint-staged.
262 | 
263 | AI Generation Rule Set:
264 | When asked to produce SCSS:
265 | 1. Always start with required base import (unless file is `abstracts/_variables.scss`).
266 | 2. Do not inline raw colors already expressible as existing tokens—prefer token reference.
267 | 3. Replace any suggested `!important` with a cascade adjustment strategy comment.
268 | 4. Provide at most one new local token; advise moving to global if reused >2 times.
269 | 5. Add a 2-line header comment with PURPOSE + SAFE REMOVE classification.
270 | 
271 | ---
272 | ## 13. Anti‑Patterns (Refactor Immediately)
273 | | Smell | Why Dangerous | Fix |
274 | |-------|---------------|-----|
275 | | Editing compiled CSS | Lost on next build | Modify SCSS source |
276 | | Missing base import | Breaks structural inheritance | Add `@import '~base/...';` line 1 |
277 | | Deep selector chains | Specificity lock-in | Convert to BEM blocks/elements |
278 | | Repeated hex codes | Inconsistent theming | Promote to variable |
279 | | `!important` usage | Overrides cascade discipline | Reduce specificity / re-order imports |
280 | | Inline media queries w/ literals | Hard to refactor | Use `mq()` mixin |
281 | | Component partial modifies unrelated block | Hidden coupling | Create new partial / utility |
282 | | Re-importing base variables in every plugin partial | Build bloat / risk inconsistent overrides | Import once at entry (e.g. plugin `global.scss`) |
283 | | Inline duplicated keyframes | Larger bundle & maintenance overhead | Centralize in shared `components/_animations.scss` |
284 | 
285 | ---
286 | ## 14. Troubleshooting Matrix
287 | | Symptom | Likely Cause | Diagnostic | Resolution |
288 | |---------|--------------|-----------|-----------|
289 | | Style not applied | Not in compiled CSS | Search compiled file for selector | Check import order / rebuild |
290 | | Base style lost | Missing base import | Inspect diff of partial | Add import + revert overrides |
291 | | Wrong theme color persisting | Variable shadowed locally | Grep for variable redefinition | Centralize in `_variables.scss` |
292 | | Breakpoint shift inconsistent | Hardcoded px values | `grep -R "992px"` | Replace with token map |
293 | | Layout jumps FOUC | Critical CSS missing | Lighthouse trace | Implement store-skin inline CSS |
294 | | Repeated variable override ignored | Later import overrides earlier | Trace import graph order | Consolidate variable overrides at earliest import point |
295 | 
296 | ---
297 | ## 15. Promptable Action Snippets (For AI Agents)
298 | | Intent | Prompt Template |
299 | |--------|-----------------|
300 | | Create component override | "Generate SFRA SCSS override for `<component>` (base path `<path>`). Include base import and BEM modifiers for states: loading, error." |
301 | | Introduce new theme color | "Add semantic token for highlight color with accessible contrast against `$body-bg` and update button hover style." |
302 | | Refactor deep selectors | "Refactor selector `<selector>` into BEM with max depth 2; preserve semantics." |
303 | | Add responsive variant | "Extend `.product-tile` to show 2 columns at md, 4 at lg via existing breakpoint mixin." |
304 | | Performance audit | "List top 10 heaviest SCSS partials by compiled size estimate and suggest consolidation." |
305 | | Plugin extension | "Generate wishlist plugin SCSS override importing '~base/global' then add toast animation using existing base token palette; avoid duplicating keyframes if fade already exists." |
306 | 
307 | ---
308 | ## 16. Migration Checklist (Before Deploy)
309 | [] All overrides start with base import
310 | [] No direct edits to `app_storefront_base`
311 | [] Variables overridden centrally only
312 | [] Lint passes (no warnings in CI)
313 | [] No `!important` (or justified + documented)
314 | [] CSS bundle size budget respected
315 | [] Critical CSS updated for homepage + PDP
316 | [] Source maps disabled for production
317 | [] New utilities documented (inline comment header)
318 | [] No duplicate keyframe blocks with identical declarations
319 | [] Plugin global includes base global exactly once
320 | 
321 | ---
322 | ## 17. Reference Quick Commands
323 | ```
324 | # Compile SCSS
325 | npm run compile:scss
326 | 
327 | # Lint SCSS
328 | npm run lint:scss
329 | 
330 | # Find hardcoded colors (non-variable)
331 | // in app_custom/cartridge/client/default/scss/abstracts/_variables.scss
332 | 
333 | # Estimate partial usage (appearance in compiled)
334 | @import '~base/variables';
335 | ```
336 | 
337 | CI Grep Helpers (Optional):
338 | ```
339 | grep -R "@import \"~base/variables\"" cartridges/plugin_* | sort
340 | grep -R "@keyframes fade" cartridges/ | wc -l
341 | ```
342 | 
343 | ---
344 | ## 18. Secure & Maintainable Patterns
345 | | Concern | Guidance |
346 | |---------|----------|
347 | | Multi-brand scaling | Abstract brand diffs to dedicated `_brand-<id>.scss` imported conditionally via build flag |
348 | | A/B testing CSS | Isolate experiment layer in `components/experiments/` with clear removal date comment |
349 | | Plugin overrides | Keep each plugin override in `vendors/_<plugin>-overrides.scss` with upstream version tag |
350 | | Future SFRA upgrade | Diff only variable + override partials; never fork base files wholesale |
351 | 
352 | Header Annotation Block (optional for generated partial):
353 | ```scss
354 | // PURPOSE: Override of base product tile for brand visual refinement
355 | // DEPENDS: ~base/components/product-tile
356 | // SAFE REMOVE: Yes (reverts to base visual style)
357 | ```
358 | Classification Legend:
359 | * SAFE REMOVE: Removing file reverts to acceptable base styling.
360 | * CONDITIONAL: Removal affects brand contract (document impact).
361 | * CRITICAL: Provides accessibility or functional styling (never remove without replacement).
362 | 
363 | ---
364 | ## 19. When NOT to Override
365 | | Scenario | Better Option |
366 | |----------|---------------|
367 | | Structural HTML limitation | Adjust ISML markup first |
368 | | Repeated spacing hacks | Introduce spacing scale utility classes |
369 | | Overriding grid internals | Use flexbox utilities / custom wrapper |
370 | | One-off promotional layout | Page partial (`pages/_promo-<slug>.scss`) + sunset note |
371 | | Behavior actually JS-driven (e.g. show/hide) | Adjust JS or data attributes; keep CSS purely presentational |
372 | 
373 | ---
374 | 
375 | ## 20. AI Generation Quick Checklist (Inline in Responses)
376 | Provide along with any generated SCSS so humans can validate rapidly:
377 | 1. Base Import Present? (Yes/No)
378 | 2. Variable Overrides? (List or None)
379 | 3. New Tokens Introduced? (List or None)
380 | 4. Max Nesting Depth (#)
381 | 5. Uses Existing Breakpoint Mixins? (Yes/No)
382 | 6. Any Raw Hex Values? (List or None + justification)
383 | 7. Keyframes Added? (Name or None; already exists?)
384 | 8. Accessibility Considerations? (Focus/contrast/motion)
385 | 9. Removable Classification (SAFE REMOVE / CONDITIONAL / CRITICAL)
386 | 10. Lint‑Sensitive Areas (Exceptions with reason)
387 | 
388 | If any answer violates a stated rule, propose an auto-correction diff, not just a warning.
389 | 
```

--------------------------------------------------------------------------------
/tests/mcp/node/get-latest-warn.full-mode.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | import { test, describe, before, after, beforeEach } from 'node:test';
  2 | import { strict as assert } from 'node:assert';
  3 | import { connect } from 'mcp-aegis';
  4 | 
  5 | describe('get_latest_warn - Full Mode Programmatic Tests', () => {
  6 |   let client;
  7 | 
  8 |   before(async () => {
  9 |     client = await connect('./aegis.config.with-dw.json');
 10 |   });
 11 | 
 12 |   after(async () => {
 13 |     if (client?.connected) {
 14 |       await client.disconnect();
 15 |     }
 16 |   });
 17 | 
 18 |   beforeEach(() => {
 19 |     // CRITICAL: Clear all buffers to prevent leaking into next tests
 20 |     client.clearAllBuffers(); // Recommended - comprehensive protection
 21 |   });
 22 | 
 23 |   // Helper functions for common validations
 24 |   function assertValidMCPResponse(result) {
 25 |     assert.ok(result.content, 'Should have content');
 26 |     assert.ok(Array.isArray(result.content), 'Content should be array');
 27 |     assert.equal(typeof result.isError, 'boolean', 'isError should be boolean');
 28 |   }
 29 | 
 30 |   function assertErrorResponse(result, expectedErrorText) {
 31 |     assertValidMCPResponse(result);
 32 |     assert.equal(result.isError, true, 'Should be an error response');
 33 |     assert.equal(result.content[0].type, 'text');
 34 |     assert.ok(result.content[0].text.includes(expectedErrorText),
 35 |       `Expected error text "${expectedErrorText}" in "${result.content[0].text}"`);
 36 |   }
 37 | 
 38 |   function assertSuccessResponse(result) {
 39 |     assertValidMCPResponse(result);
 40 |     assert.equal(result.isError, false, 'Should not be an error response');
 41 |     assert.equal(result.content[0].type, 'text');
 42 |   }
 43 | 
 44 |   function assertLogFormat(result, expectedLimit) {
 45 |     assertSuccessResponse(result);
 46 |     const text = result.content[0].text;
 47 |     
 48 |     // Should contain the expected limit message
 49 |     assert.ok(text.includes(`Latest ${expectedLimit} warn messages`),
 50 |       `Should mention "${expectedLimit}" warn messages`);
 51 |     
 52 |     // Should contain log file name pattern
 53 |     assert.ok(/warn-blade-\d{8}-\d{6}\.log/.test(text),
 54 |       'Should contain warn log file name pattern');
 55 |     
 56 |     // Should contain WARN level entries
 57 |     assert.ok(text.includes('WARN'), 'Should contain WARN level entries');
 58 |     
 59 |     // Should contain GMT timestamps
 60 |     assert.ok(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} GMT/.test(text),
 61 |       'Should contain GMT timestamp pattern');
 62 |   }
 63 | 
 64 |   // Helper function to get current date in YYYYMMDD format
 65 |   function getCurrentDateString() {
 66 |     const now = new Date();
 67 |     const year = now.getFullYear();
 68 |     const month = String(now.getMonth() + 1).padStart(2, '0');
 69 |     const day = String(now.getDate()).padStart(2, '0');
 70 |     return `${year}${month}${day}`;
 71 |   }
 72 | 
 73 |   // Core functionality tests - focused on programmatic strengths
 74 |   describe('Core Functionality', () => {
 75 |     test('should retrieve latest warn messages with default parameters', async () => {
 76 |       const result = await client.callTool('get_latest_warn', {});
 77 |       
 78 |       assertLogFormat(result, 10); // Default limit is 10
 79 |       
 80 |       // Should contain SFCC-specific patterns
 81 |       const text = result.content[0].text;
 82 |       assert.ok(/PipelineCallServlet|SystemJobThread/.test(text),
 83 |         'Should contain SFCC thread patterns');
 84 |       assert.ok(text.includes('Sites-'), 'Should contain Sites information');
 85 |     });
 86 | 
 87 |     test('should handle comprehensive parameter combinations', async () => {
 88 |       const paramCombinations = [
 89 |         { limit: 3 },
 90 |         { date: getCurrentDateString(), limit: 2 },
 91 |         { limit: 50 },
 92 |         {} // default parameters
 93 |       ];
 94 |       
 95 |       const results = [];
 96 |       
 97 |       // Execute calls sequentially (never use Promise.all with MCP!)
 98 |       for (const params of paramCombinations) {
 99 |         const result = await client.callTool('get_latest_warn', params);
100 |         results.push({ params, result });
101 |       }
102 |       
103 |       // All should have consistent structure
104 |       results.forEach(({ params, result }, index) => {
105 |         assertValidMCPResponse(result);
106 |         assert.equal(result.isError, false, `Call ${index} should not be error`);
107 |         assert.equal(result.content[0].type, 'text', `Call ${index} should have text content`);
108 |         
109 |         const expectedLimit = params.limit || 10;
110 |         assert.ok(result.content[0].text.includes(`Latest ${expectedLimit} warn messages`), 
111 |           `Call ${index} should contain 'Latest ${expectedLimit}' in response`);
112 |       });
113 |     });
114 |   });
115 | 
116 |   // Error handling and edge cases - key validation scenarios
117 |   describe('Error Handling and Edge Cases', () => {
118 |     test('should handle parameter validation errors correctly', async () => {
119 |       const errorCases = [
120 |         { params: { limit: '5' }, expectedError: 'Invalid limit \'5\' for get_latest_warn. Must be a valid number' },
121 |         { params: { limit: 0 }, expectedError: 'Invalid limit \'0\' for get_latest_warn. Must be between 1 and 1000' },
122 |         { params: { limit: -5 }, expectedError: 'Invalid limit \'-5\'' },
123 |         { params: { limit: 9999 }, expectedError: 'Invalid limit' }
124 |       ];
125 | 
126 |       for (const { params, expectedError } of errorCases) {
127 |         const result = await client.callTool('get_latest_warn', params);
128 |         assertErrorResponse(result, expectedError);
129 |       }
130 |     });
131 | 
132 |     test('should handle edge cases without breaking server state', async () => {
133 |       const edgeCases = [
134 |         { date: '' },        // Empty date
135 |         { date: '2024-01-01' }, // Invalid format
136 |         { date: '20251231' }, // Future date
137 |         { invalid: 'param' } // Invalid parameter name
138 |       ];
139 |       
140 |       // Test all edge cases sequentially
141 |       for (const testCase of edgeCases) {
142 |         const result = await client.callTool('get_latest_warn', testCase);
143 |         assertValidMCPResponse(result);
144 |         // Some may succeed with no data, some may have default behavior - but none should crash
145 |       }
146 |       
147 |       // Verify tool still works after edge cases
148 |       const finalResult = await client.callTool('get_latest_warn', { limit: 1 });
149 |       assertSuccessResponse(finalResult);
150 |     });
151 |   });
152 | 
153 |   // Advanced content analysis - leveraging programmatic strengths
154 |   describe('SFCC Content Analysis', () => {
155 |     test('should contain and analyze SFCC-specific warning patterns', async () => {
156 |       const result = await client.callTool('get_latest_warn', { limit: 5 });
157 |       
158 |       assertSuccessResponse(result);
159 |       const text = result.content[0].text;
160 |       
161 |       // SFCC-specific validation patterns for warnings
162 |       const sfccWarningPatterns = [
163 |         /Sites-\w+/,                    // Sites names
164 |         /PipelineCallServlet|SystemJobThread/, // Thread types
165 |         /\|\d+\|/,                      // Thread IDs
166 |         /custom \[\]/,                  // Custom category
167 |         /inventory low|Content asset|offline/i // Common warning types
168 |       ];
169 |       
170 |       const matchedPatterns = sfccWarningPatterns.filter(pattern => pattern.test(text));
171 |       assert.ok(matchedPatterns.length >= 2,
172 |         `Should match at least 2 SFCC warning patterns. Matched: ${matchedPatterns.length}`);
173 |       
174 |       // Analyze log structure elements
175 |       assert.ok(/warn-blade-.*\.log/.test(text), 'Should include log file name');
176 |       assert.ok(/\d{4}-\d{2}-\d{2}/.test(text), 'Should include timestamp');
177 |       assert.ok(/GMT/.test(text), 'Should include GMT timezone');
178 |       assert.ok(/WARN PipelineCallServlet/.test(text), 'Should include servlet context');
179 |       
180 |       // Count separators - should have appropriate separators for multiple entries
181 |       const separators = text.match(/---/g);
182 |       assert.ok(separators && separators.length >= 1, 
183 |         `Should have separators for multiple entries. Found: ${separators?.length || 0}`);
184 |     });
185 | 
186 |     test('should validate warning content quality and context', async () => {
187 |       const result = await client.callTool('get_latest_warn', { limit: 3 });
188 |       assertSuccessResponse(result);
189 |       
190 |       const text = result.content[0].text;
191 |       const lines = text.split('\n').filter(line => line.trim().length > 0);
192 |       
193 |       // Dynamic validation based on actual content
194 |       const warnLines = lines.filter(line => line.includes('WARN'));
195 |       assert.ok(warnLines.length > 0, 'Should have at least one WARN line');
196 |       
197 |       // Each warn line should have proper structure
198 |       warnLines.forEach((line, index) => {
199 |         assert.ok(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(line),
200 |           `Warn line ${index} should contain timestamp: ${line}`);
201 |       });
202 |       
203 |       // Warning-specific content analysis
204 |       const warningIndicators = [
205 |         'inventory low',
206 |         'Content asset',
207 |         'offline',
208 |         'deprecated'
209 |       ];
210 |       
211 |       const foundIndicators = warningIndicators.filter(indicator => 
212 |         text.toLowerCase().includes(indicator.toLowerCase())
213 |       );
214 |       assert.ok(foundIndicators.length > 0,
215 |         `Should contain warning indicators. Found: ${foundIndicators.join(', ')}`);
216 |     });
217 |   });
218 | 
219 |   // Multi-step workflows - where programmatic tests excel
220 |   describe('Multi-Step Workflows and State Management', () => {
221 |     test('should support comprehensive warning analysis workflow', async () => {
222 |       // Step 1: Get recent warnings with small limit
223 |       const recentWarnings = await client.callTool('get_latest_warn', { limit: 2 });
224 |       assertSuccessResponse(recentWarnings);
225 |       
226 |       // Step 2: Get more comprehensive warning list
227 |       const comprehensiveWarnings = await client.callTool('get_latest_warn', { limit: 10 });
228 |       assertSuccessResponse(comprehensiveWarnings);
229 |       
230 |       // Step 3: Get warnings for specific date
231 |       const dateSpecificWarnings = await client.callTool('get_latest_warn', { 
232 |         date: getCurrentDateString(),
233 |         limit: 5 
234 |       });
235 |       assertSuccessResponse(dateSpecificWarnings);
236 |       
237 |       // Cross-analysis: Verify pattern consistency across different scopes
238 |       const recentText = recentWarnings.content[0].text;
239 |       const comprehensiveText = comprehensiveWarnings.content[0].text;
240 |       const dateText = dateSpecificWarnings.content[0].text;
241 |       
242 |       [recentText, comprehensiveText, dateText].forEach((text, index) => {
243 |         assert.ok(text.includes('WARN'), `Analysis ${index} should contain WARN level`);
244 |         assert.ok(/Sites-/.test(text), `Analysis ${index} should contain Sites information`);
245 |         assert.ok(/PipelineCallServlet/.test(text), `Analysis ${index} should contain servlet context`);
246 |       });
247 |       
248 |       // Advanced: Verify scope scaling
249 |       const recentCount = (recentText.match(/WARN/g) || []).length;
250 |       const comprehensiveCount = (comprehensiveText.match(/WARN/g) || []).length;
251 |       assert.ok(comprehensiveCount >= recentCount, 
252 |         'Comprehensive analysis should include at least as many warnings as recent analysis');
253 |     });
254 | 
255 |     test('should maintain server state integrity across sequential operations', async () => {
256 |       const operationSequence = [
257 |         { params: { limit: 1 }, description: 'Single warning check' },
258 |         { params: { limit: 20 }, description: 'Large batch analysis' },
259 |         { params: { limit: 0 }, description: 'Error condition', expectError: true },
260 |         { params: { limit: 5 }, description: 'Recovery operation' },
261 |         { params: { date: getCurrentDateString(), limit: 3 }, description: 'Date-specific analysis' }
262 |       ];
263 |       
264 |       const results = [];
265 |       
266 |       // Execute operations sequentially to test state management
267 |       for (const operation of operationSequence) {
268 |         const result = await client.callTool('get_latest_warn', operation.params);
269 |         results.push({
270 |           operation: operation.description,
271 |           params: operation.params,
272 |           success: !result.isError,
273 |           expectedError: operation.expectError || false,
274 |           result
275 |         });
276 |       }
277 |       
278 |       // Analyze operation results for state integrity
279 |       results.forEach((opResult, index) => {
280 |         if (opResult.expectedError) {
281 |           assert.equal(opResult.success, false, 
282 |             `Operation ${index} (${opResult.operation}) should have failed as expected`);
283 |         } else {
284 |           assert.equal(opResult.success, true, 
285 |             `Operation ${index} (${opResult.operation}) should have succeeded`);
286 |           
287 |           // Verify state consistency for successful operations
288 |           const text = opResult.result.content[0].text;
289 |           const expectedLimit = opResult.params.limit || 10;
290 |           assert.ok(text.includes(`Latest ${expectedLimit} warn messages`),
291 |             `Operation ${index} should show correct limit: ${expectedLimit}`);
292 |         }
293 |       });
294 |       
295 |       // Final verification: Server should still be fully functional
296 |       const finalCheck = await client.callTool('get_latest_warn', { limit: 1 });
297 |       assertSuccessResponse(finalCheck);
298 |     });
299 |   });
300 | 
301 |   // Operational monitoring and resilience - complex business logic validation
302 |   describe('Operational Monitoring and Resilience', () => {
303 |     test('should provide comprehensive monitoring capabilities', async () => {
304 |       // Simulate different monitoring scenarios
305 |       const monitoringScenarios = [
306 |         { limit: 1, description: 'Latest warning check', alertLevel: 'immediate' },
307 |         { limit: 10, description: 'Recent warnings review', alertLevel: 'hourly' },
308 |         { limit: 50, description: 'Comprehensive warning analysis', alertLevel: 'daily' }
309 |       ];
310 |       
311 |       const monitoringResults = [];
312 |       
313 |       for (const scenario of monitoringScenarios) {
314 |         const result = await client.callTool('get_latest_warn', { limit: scenario.limit });
315 |         
316 |         const analysisResult = {
317 |           scenario: scenario.description,
318 |           alertLevel: scenario.alertLevel,
319 |           limit: scenario.limit,
320 |           success: !result.isError,
321 |           warningCount: 0,
322 |           hasWarnings: false,
323 |           sfccPatterns: 0
324 |         };
325 |         
326 |         if (!result.isError) {
327 |           const text = result.content[0].text;
328 |           analysisResult.hasWarnings = text.includes('WARN');
329 |           analysisResult.warningCount = (text.match(/WARN/g) || []).length;
330 |           
331 |           // Count SFCC-specific patterns for monitoring quality
332 |           const sfccPatterns = [/Sites-/, /PipelineCallServlet/, /custom \[\]/];
333 |           analysisResult.sfccPatterns = sfccPatterns.filter(pattern => pattern.test(text)).length;
334 |         }
335 |         
336 |         monitoringResults.push(analysisResult);
337 |       }
338 |       
339 |       // Validate monitoring effectiveness
340 |       monitoringResults.forEach(result => {
341 |         assert.ok(result.success, `${result.scenario} should succeed`);
342 |         assert.ok(result.hasWarnings, `${result.scenario} should contain warnings`);
343 |         assert.ok(result.sfccPatterns >= 1, `${result.scenario} should contain SFCC patterns`);
344 |       });
345 |       
346 |       // Verify monitoring scope increases with limit
347 |       assert.ok(monitoringResults[0].limit < monitoringResults[1].limit, 
348 |         'Monitoring scope should increase');
349 |       assert.ok(monitoringResults[1].limit < monitoringResults[2].limit, 
350 |         'Comprehensive analysis should have largest scope');
351 |     });
352 | 
353 |     test('should handle resilience scenarios and error recovery', async () => {
354 |       // Test normal operation baseline
355 |       const baselineResult = await client.callTool('get_latest_warn', { limit: 3 });
356 |       assertSuccessResponse(baselineResult);
357 |       
358 |       // Test various failure scenarios and recovery
359 |       const resilienceTests = [
360 |         { params: { limit: 0 }, shouldFail: true, description: 'Zero limit validation' },
361 |         { params: { limit: '1' }, shouldFail: true, description: 'Type validation' },
362 |         { params: { limit: 99999 }, shouldFail: true, description: 'Range validation' },
363 |         { params: { date: '' }, shouldFail: false, description: 'Empty date handling' },
364 |         { params: { invalid: 'param' }, shouldFail: false, description: 'Unknown parameter handling' }
365 |       ];
366 |       
367 |       let failureCount = 0;
368 |       let recoveryCount = 0;
369 |       
370 |       for (const resilienceTest of resilienceTests) {
371 |         const result = await client.callTool('get_latest_warn', resilienceTest.params);
372 |         
373 |         if (resilienceTest.shouldFail) {
374 |           assert.equal(result.isError, true, 
375 |             `${resilienceTest.description} should fail as expected`);
376 |           failureCount++;
377 |         } else {
378 |           assert.equal(result.isError, false, 
379 |             `${resilienceTest.description} should succeed`);
380 |         }
381 |         
382 |         // Test recovery after each scenario
383 |         const recoveryResult = await client.callTool('get_latest_warn', { limit: 1 });
384 |         assert.equal(recoveryResult.isError, false, 
385 |           `Recovery after ${resilienceTest.description} should work`);
386 |         recoveryCount++;
387 |       }
388 |       
389 |       // Verify resilience metrics
390 |       assert.ok(failureCount >= 3, 'Should have tested multiple failure scenarios');
391 |       assert.equal(recoveryCount, resilienceTests.length, 'All recovery tests should pass');
392 |       
393 |       // Final comprehensive recovery test
394 |       const finalResult = await client.callTool('get_latest_warn', { limit: 5 });
395 |       assertSuccessResponse(finalResult);
396 |       
397 |       const finalText = finalResult.content[0].text;
398 |       assert.ok(finalText.includes('Latest 5 warn messages'), 'Final recovery should work correctly');
399 |       assert.ok(finalText.includes('WARN'), 'Final recovery should contain valid warning data');
400 |     });
401 | 
402 |     test('should provide detailed data quality analysis across different parameters', async () => {
403 |       const qualityTests = [
404 |         { params: { limit: 2 }, minWarnings: 1, description: 'Small sample quality' },
405 |         { params: { limit: 10 }, minWarnings: 3, description: 'Standard sample quality' },
406 |         { params: { date: getCurrentDateString(), limit: 5 }, minWarnings: 1, description: 'Date-specific quality' }
407 |       ];
408 |       
409 |       const qualityResults = [];
410 |       
411 |       for (const qualityTest of qualityTests) {
412 |         const result = await client.callTool('get_latest_warn', qualityTest.params);
413 |         assertSuccessResponse(result);
414 |         
415 |         const text = result.content[0].text;
416 |         
417 |         // Quality metrics
418 |         const qualityMetrics = {
419 |           hasLogFileName: /warn-blade-.*\.log/.test(text),
420 |           hasTimestamp: /\d{4}-\d{2}-\d{2}/.test(text),
421 |           hasSiteInfo: /Sites-/.test(text),
422 |           hasWarnLevel: text.includes('WARN'),
423 |           hasGMTTimezone: text.includes('GMT'),
424 |           warningCount: (text.match(/WARN/g) || []).length,
425 |           separatorCount: (text.match(/---/g) || []).length
426 |         };
427 |         
428 |         qualityResults.push({
429 |           description: qualityTest.description,
430 |           params: qualityTest.params,
431 |           metrics: qualityMetrics,
432 |           passedChecks: Object.values(qualityMetrics).filter(Boolean).length
433 |         });
434 |         
435 |         // Validate quality requirements
436 |         assert.ok(qualityMetrics.hasLogFileName, `${qualityTest.description}: Should have log file name`);
437 |         assert.ok(qualityMetrics.hasTimestamp, `${qualityTest.description}: Should have timestamp`);
438 |         assert.ok(qualityMetrics.hasWarnLevel, `${qualityTest.description}: Should have WARN level`);
439 |         assert.ok(qualityMetrics.warningCount >= qualityTest.minWarnings, 
440 |           `${qualityTest.description}: Should have at least ${qualityTest.minWarnings} warnings`);
441 |       }
442 |       
443 |       // Cross-quality analysis
444 |       const avgQualityScore = qualityResults.reduce((sum, result) => sum + result.passedChecks, 0) / qualityResults.length;
445 |       assert.ok(avgQualityScore >= 6, `Average quality score should be high (${avgQualityScore})`);
446 |       
447 |       // Consistency check across different parameter sets
448 |       qualityResults.forEach(result => {
449 |         assert.ok(result.passedChecks >= 6, 
450 |           `${result.description} should pass most quality checks (${result.passedChecks})`);
451 |       });
452 |     });
453 |   });
454 | });
455 | 
```
Page 34/61FirstPrevNextLast