#
tokens: 45990/50000 6/825 files (page 31/43)
lines: off (toggle) GitHub
raw markdown copy
This is page 31 of 43. Use http://codebase.md/taurgis/sfcc-dev-mcp?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

--------------------------------------------------------------------------------
/docs/TopLevel/XML.md:
--------------------------------------------------------------------------------

```markdown
## Package: TopLevel

# Class XML

## Inheritance Hierarchy

- Object
  - XML

## Description

The XML object contains functions and properties for working with XML instances. The XML object implements the powerful XML-handling standards defined in the ECMA-357 specification (known as "E4X"). Use the toXMLString() method to return a string representation of the XML object regardless of whether the XML object has simple content or complex content. Do not create large XML objects in memory to avoid out-of-memory conditions. When dealing with XML streams use XMLStreamReader and XMLStreamWriter. The following example shows how: var id : String = "p42"; var pname : String = "a product"; // use E4X syntax var product : XML = <product id={id}> <name>{pname}</name> <shortdesc></shortdesc> </product>; product.shortdesc = "a fine product"; product.longdesc = "this is a fine product"; var xmlString = product.toXMLString(); fileWriter.write(xmlString); The code above will write the following to file: <product id="p42"> <name>a product</name> <shortdesc>a fine product</shortdesc> <longdesc>this is a fine product</longdesc> </product> Do not create large XML objects in memory to avoid out-of-memory conditions. When dealing with XML streams use XMLStreamReader and XMLStreamWriter.

## Properties

## Constructor Summary

XML() Creates a new XML object.

XML(value : Object) Creates a new XML object.

## Method Summary

### addNamespace

**Signature:** `addNamespace(ns : Object) : XML`

Adds a namespace to the set of in-scope namespaces for the XML object.

### appendChild

**Signature:** `appendChild(child : Object) : XML`

Appends the specified child to the end of the object's properties.

### attribute

**Signature:** `attribute(attributeName : String) : XMLList`

Returns the attribute associated with this XML object that is identified by the specified name.

### attributes

**Signature:** `attributes() : XMLList`

Returns an XMList of the attributes in this XML Object.

### child

**Signature:** `child(propertyName : Object) : XMLList`

Returns the children of the XML object based on the specified property name.

### childIndex

**Signature:** `childIndex() : Number`

Identifies the zero-based index of this XML object within the context of its parent, or -1 if this object has no parent.

### children

**Signature:** `children() : XMLList`

Returns an XMLList containing the children of this XML object, maintaing the sequence in which they appear.

### comments

**Signature:** `comments() : XMLList`

Returns the properties of the XML object that contain comments.

### contains

**Signature:** `contains(value : XML) : boolean`

Returns true if this XML object contains the specified XML object, false otherwise.

### copy

**Signature:** `copy() : XML`

Returns a copy of the this XML object including duplicate copies of the entire tree of nodes.

### defaultSettings

**Signature:** `static defaultSettings() : Object`

Returns a new Object with the following properties set to the default values: ignoreComments, ignoreProcessingInstructions, ignoreWhitespace, prettyIndent, and prettyPrinting.

### descendants

**Signature:** `descendants() : XMLList`

Returns all descendents of the XML object.

### descendants

**Signature:** `descendants(name : String) : XMLList`

Returns all descendents of the XML object that have the specified name parameter.

### elements

**Signature:** `elements() : XMLList`

Returns a list of all of the elements of the XML object.

### elements

**Signature:** `elements(name : Object) : XMLList`

Returns a list of the elements of the XML object using the specified name to constrain the list.

### hasComplexContent

**Signature:** `hasComplexContent() : boolean`

Returns a Boolean value indicating whether this XML object contains complex content.

### hasOwnProperty

**Signature:** `hasOwnProperty(prop : String) : boolean`

Returns a Boolean value indicating whether this object has the property specified by prop.

### hasSimpleContent

**Signature:** `hasSimpleContent() : boolean`

Returns a Boolean value indicating whether this XML object contains simple content.

### inScopeNamespaces

**Signature:** `inScopeNamespaces() : Array`

Returns an Array of Namespace objects representing the namespaces in scope for this XML object in the context of its parent.

### insertChildAfter

**Signature:** `insertChildAfter(child1 : Object, child2 : Object) : XML`

Inserts the specified child2 after the specified child1 in this XML object and returns this XML object.

### insertChildBefore

**Signature:** `insertChildBefore(child1 : Object, child2 : Object) : XML`

Inserts the specified child2 before the specified child1 in this XML object and returns this XML object.

### length

**Signature:** `length() : Number`

Returns a value of 1 for XML objects.

### localName

**Signature:** `localName() : Object`

Returns the local name portion of the qualified name of the XML object.

### name

**Signature:** `name() : Object`

Returns the qualified name for the XML object.

### namespace

**Signature:** `namespace() : Object`

Returns the namespace associated with the qualified name of this XML object.

### namespace

**Signature:** `namespace(prefix : String) : Object`

Returns the namespace that matches the specified prefix and that is in scope for the XML object.

### namespaceDeclarations

**Signature:** `namespaceDeclarations() : Array`

Returns an Array of namespace declarations associated with the XML Obnject in the context of its parent.

### nodeKind

**Signature:** `nodeKind() : String`

Returns the type of the XML object, such as text, comment, processing-instruction, or attribute.

### normalize

**Signature:** `normalize() : XML`

Merges adjacent text nodes and eliminates and eliminates empty text nodes for this XML object and all its descendents.

### parent

**Signature:** `parent() : Object`

Returns the parent of the XML object or null if the XML object does not have a parent.

### prependChild

**Signature:** `prependChild(value : Object) : XML`

Inserts the specified child into this XML object prior to its existing XML properties and then returns this XML object.

### processingInstructions

**Signature:** `processingInstructions() : XMLList`

Returns an XMLList containing all the children of this XML object that are processing-instructions.

### processingInstructions

**Signature:** `processingInstructions(name : String) : XMLList`

Returns an XMLList containing all the children of this XML object that are processing-instructions with the specified name.

### propertyIsEnumerable

**Signature:** `propertyIsEnumerable(property : String) : boolean`

Returns a Boolean indicating whether the specified property will be included in the set of properties iterated over when this XML object is used in a for..in statement.

### removeNamespace

**Signature:** `removeNamespace(ns : Namespace) : XML`

Removes the specified namespace from the in scope namespaces of this object and all its descendents, then returns a copy of this XML object.

### replace

**Signature:** `replace(propertyName : String, value : Object) : XML`

Replaces the XML properties of this XML object specified by propertyName with value and returns this updated XML object.

### setChildren

**Signature:** `setChildren(value : Object) : XML`

Replaces the XML properties of this XML object with a new set of XML properties from value.

### setLocalName

**Signature:** `setLocalName(name : String) : void`

Replaces the local name of this XML object with a string constructed from the specified name.

### setName

**Signature:** `setName(name : String) : void`

Replaces the name of this XML object with a QName or AttributeName constructed from the specified name.

### setNamespace

**Signature:** `setNamespace(ns : Namespace) : void`

Replaces the namespace associated with the name of this XML object with the specified namespace.

### setSettings

**Signature:** `static setSettings() : void`

Restores the default settings for the following XML properties: XML.ignoreComments = true XML.ignoreProcessingInstructions = true XML.ignoreWhitespace = true XML.prettyIndent = 2 XML.prettyPrinting = true

### setSettings

**Signature:** `static setSettings(settings : Object) : void`

Updates the collection of global XML properties: ignoreComments, ignoreProcessingInstructions, ignoreWhitespace, prettyPrinting, prettyIndent, and prettyPrinting.

### settings

**Signature:** `static settings() : Object`

Returns the collection of global XML properties: ignoreComments, ignoreProcessingInstructions, ignoreWhitespace, prettyPrinting, prettyIndent, and prettyPrinting.

### text

**Signature:** `text() : XMLList`

Returns an XMLList containing all XML properties of this XML object that represent XML text nodes.

### toString

**Signature:** `toString() : String`

Returns the String representation of the XML object.

### toXMLString

**Signature:** `toXMLString() : String`

Returns a XML-encoded String representation of the XML object, including tag and attributed delimiters.

### valueOf

**Signature:** `valueOf() : XML`

Returns this XML object.

## Constructor Detail

## Method Detail

## Method Details

### addNamespace

**Signature:** `addNamespace(ns : Object) : XML`

**Description:** Adds a namespace to the set of in-scope namespaces for the XML object. If the namespace already exists in the in-scope namespaces for the XML object, then the prefix of the existing namespace is set to undefined. If ns is a Namespace instance, it is used directly. However, if ns is a QName instance, the input parameter's URI is used to create a new namespace. If ns is not a Namespace or QName instance, ns is converted to a String and a namespace is created from the String.

**Parameters:**

- `ns`: the namespace to add to the XML object.

**Returns:**

a new XML object, with the namespace added.

---

### appendChild

**Signature:** `appendChild(child : Object) : XML`

**Description:** Appends the specified child to the end of the object's properties. child should be a XML object, an XMLList object or any other data type that will then be converted to a String.

**Parameters:**

- `child`: the object to append to this XML object.

**Returns:**

the XML object with the child appended.

---

### attribute

**Signature:** `attribute(attributeName : String) : XMLList`

**Description:** Returns the attribute associated with this XML object that is identified by the specified name.

**Parameters:**

- `attributeName`: the name of the attribute.

**Returns:**

the value of the attribute as either an XMLList or an empty XMLList

---

### attributes

**Signature:** `attributes() : XMLList`

**Description:** Returns an XMList of the attributes in this XML Object.

**Returns:**

an XMList of the attributes in this XML Object.

---

### child

**Signature:** `child(propertyName : Object) : XMLList`

**Description:** Returns the children of the XML object based on the specified property name.

**Parameters:**

- `propertyName`: the property name representing the children of this XML object.

**Returns:**

an XMLList of children that match the property name parameter.

---

### childIndex

**Signature:** `childIndex() : Number`

**Description:** Identifies the zero-based index of this XML object within the context of its parent, or -1 if this object has no parent.

**Returns:**

the index of this XML object in the context of its parent, or -1 if this object has no parent.

---

### children

**Signature:** `children() : XMLList`

**Description:** Returns an XMLList containing the children of this XML object, maintaing the sequence in which they appear.

**Returns:**

an XMLList containing the children of this XML object.

---

### comments

**Signature:** `comments() : XMLList`

**Description:** Returns the properties of the XML object that contain comments.

**Returns:**

properties of the XML object that contain comments.

---

### contains

**Signature:** `contains(value : XML) : boolean`

**Description:** Returns true if this XML object contains the specified XML object, false otherwise.

**Parameters:**

- `value`: the object to locate in this XML object.

**Returns:**

true if this XML object contains the specified XML object, false otherwise.

---

### copy

**Signature:** `copy() : XML`

**Description:** Returns a copy of the this XML object including duplicate copies of the entire tree of nodes. The copied XML object has no parent.

**Returns:**

the copy of the object.

---

### defaultSettings

**Signature:** `static defaultSettings() : Object`

**Description:** Returns a new Object with the following properties set to the default values: ignoreComments, ignoreProcessingInstructions, ignoreWhitespace, prettyIndent, and prettyPrinting. The default values are as follows: ignoreComments = true ignoreProcessingInstructions = true ignoreWhitespace = true prettyIndent = 2 prettyPrinting = true Be aware that this method does not apply the settings to an existing instance of an XML object. Instead, this method returns an Object containing the default settings.

**Returns:**

an Object with properties set to the default settings.

---

### descendants

**Signature:** `descendants() : XMLList`

**Description:** Returns all descendents of the XML object.

**Returns:**

a list of all descendents of the XML object.

---

### descendants

**Signature:** `descendants(name : String) : XMLList`

**Description:** Returns all descendents of the XML object that have the specified name parameter. To return all descendents, use * as the name parameter.

**Parameters:**

- `name`: the name of the element to match. To return all descendents, use * as the name parameter.

**Returns:**

a list of all descendents constrained by the name parameter.

---

### elements

**Signature:** `elements() : XMLList`

**Description:** Returns a list of all of the elements of the XML object.

---

### elements

**Signature:** `elements(name : Object) : XMLList`

**Description:** Returns a list of the elements of the XML object using the specified name to constrain the list. name can be a QName, String, or any other data type that will be converted to a string prior to performing the search for elements of that name. To list all objects use * for the value of name.

**Parameters:**

- `name`: the name of the elements to return or an * to return all elements.

**Returns:**

a list of the elements of the XML object using the specified name to constrain the list.

---

### hasComplexContent

**Signature:** `hasComplexContent() : boolean`

**Description:** Returns a Boolean value indicating whether this XML object contains complex content. An XML object is considered to contain complex content if it represents an XML element that has child elements. XML objects representing attributes, comments, processing instructions and text nodes do not have complex content. The existence of attributes, comments, processing instructions and text nodes within an XML object is not significant in determining if it has complex content.

**Returns:**

a Boolean value indicating whether this XML object contains complex content.

---

### hasOwnProperty

**Signature:** `hasOwnProperty(prop : String) : boolean`

**Description:** Returns a Boolean value indicating whether this object has the property specified by prop.

**Parameters:**

- `prop`: the property to locate.

**Returns:**

true if the property exists, false otherwise.

---

### hasSimpleContent

**Signature:** `hasSimpleContent() : boolean`

**Description:** Returns a Boolean value indicating whether this XML object contains simple content. An XML object is considered to contain simple content if it represents a text node, represents an attribute node or if it represents an XML element that has no child elements. XML objects representing comments and processing instructions do not have simple content. The existence of attributes, comments, processing instructions and text nodes within an XML object is not significant in determining if it has simple content.

**Returns:**

a Boolean value indicating whether this XML object contains simple content.

---

### inScopeNamespaces

**Signature:** `inScopeNamespaces() : Array`

**Description:** Returns an Array of Namespace objects representing the namespaces in scope for this XML object in the context of its parent. If the parent of this XML object is modified, the associated namespace declarations may change. The set of namespaces returned by this method may be a super set of the namespaces used by this value

**Returns:**

an Array of Namespace objects representing the namespaces in scope for this XML object in the context of its parent.

---

### insertChildAfter

**Signature:** `insertChildAfter(child1 : Object, child2 : Object) : XML`

**Description:** Inserts the specified child2 after the specified child1 in this XML object and returns this XML object. If child1 is null, inserts child2 before all children of this XML object. If child1 does not exist in this XML object, it returns without modifying this XML object.

**Parameters:**

- `child1`: the child after which child2 is inserted.
- `child2`: the child to insert into this XML object.

**Returns:**

the updated XML object.

---

### insertChildBefore

**Signature:** `insertChildBefore(child1 : Object, child2 : Object) : XML`

**Description:** Inserts the specified child2 before the specified child1 in this XML object and returns this XML object. If child1 is null, inserts child2 after all children of this XML object. If child1 does not exist in this XML object, it returns without modifying this XML object.

**Parameters:**

- `child1`: the child before which child2 is inserted.
- `child2`: the child to insert into this XML object.

**Returns:**

the updated XML object.

---

### length

**Signature:** `length() : Number`

**Description:** Returns a value of 1 for XML objects.

**Returns:**

the value of 1.

---

### localName

**Signature:** `localName() : Object`

**Description:** Returns the local name portion of the qualified name of the XML object.

**Returns:**

the local name as either a String or null.

---

### name

**Signature:** `name() : Object`

**Description:** Returns the qualified name for the XML object.

**Returns:**

the qualified name as either a QName or null.

---

### namespace

**Signature:** `namespace() : Object`

**Description:** Returns the namespace associated with the qualified name of this XML object.

**Returns:**

the namespace associated with the qualified name of this XML object.

---

### namespace

**Signature:** `namespace(prefix : String) : Object`

**Description:** Returns the namespace that matches the specified prefix and that is in scope for the XML object. if there is no such namespace, the method returns undefined.

**Parameters:**

- `prefix`: the prefix to use when attempting to locate a namespace.

**Returns:**

the namespace that matches the specified prefix and that is in scope for the XML object. If specified namespace does not exist, the method returns undefined.

---

### namespaceDeclarations

**Signature:** `namespaceDeclarations() : Array`

**Description:** Returns an Array of namespace declarations associated with the XML Obnject in the context of its parent.

**Returns:**

an Array of namespace declarations associated with the XML Obnject in the context of its parent.

---

### nodeKind

**Signature:** `nodeKind() : String`

**Description:** Returns the type of the XML object, such as text, comment, processing-instruction, or attribute.

**Returns:**

the type of the XML object.

---

### normalize

**Signature:** `normalize() : XML`

**Description:** Merges adjacent text nodes and eliminates and eliminates empty text nodes for this XML object and all its descendents.

**Returns:**

the normalized XML object.

---

### parent

**Signature:** `parent() : Object`

**Description:** Returns the parent of the XML object or null if the XML object does not have a parent.

**Returns:**

the parent of the XML object of null if the XML object does not have a parent.

---

### prependChild

**Signature:** `prependChild(value : Object) : XML`

**Description:** Inserts the specified child into this XML object prior to its existing XML properties and then returns this XML object.

**Parameters:**

- `value`: the child to prepend to this XML object.

**Returns:**

the XML object updated with the prepended child.

---

### processingInstructions

**Signature:** `processingInstructions() : XMLList`

**Description:** Returns an XMLList containing all the children of this XML object that are processing-instructions.

**Returns:**

an XMLList containing all the children of this XML object that are processing-instructions.

---

### processingInstructions

**Signature:** `processingInstructions(name : String) : XMLList`

**Description:** Returns an XMLList containing all the children of this XML object that are processing-instructions with the specified name. If you use * for the name, all processing-instructions are returned.

**Parameters:**

- `name`: the name representing the processing-instructions you want to retreive.

**Returns:**

an XMLList containing all the children of this XML object that are processing-instructions with the specified name.

---

### propertyIsEnumerable

**Signature:** `propertyIsEnumerable(property : String) : boolean`

**Description:** Returns a Boolean indicating whether the specified property will be included in the set of properties iterated over when this XML object is used in a for..in statement.

**Parameters:**

- `property`: the property to test.

**Returns:**

true when the property can be iterated in a for..in statement, false otherwise.

---

### removeNamespace

**Signature:** `removeNamespace(ns : Namespace) : XML`

**Description:** Removes the specified namespace from the in scope namespaces of this object and all its descendents, then returns a copy of this XML object. This method will not remove a namespace from an object when it is referenced by that object's QName or the ONames of that object's attributes.

**Parameters:**

- `ns`: the namespace to remove.

**Returns:**

a copy of this XML object with the namespace removed.

---

### replace

**Signature:** `replace(propertyName : String, value : Object) : XML`

**Description:** Replaces the XML properties of this XML object specified by propertyName with value and returns this updated XML object. If this XML object contains no properties that match propertyName, the replace method returns without modifying this XML object. The propertyName parameter may be a numeric property name, an unqualified name for a set of XML elements, a qualified name for a set of XML elements or the properties wildcard *. When the propertyName parameter is an unqualified name, it identifies XML elements in the default namespace. The value parameter may be an XML object, XMLList object or any value that may be converted to a String.

**Parameters:**

- `propertyName`: a numeric property name, an unqualified name for a set of XML elements, a qualified name for a set of XML elements or the properties wildcard *.
- `value`: an XML object, XMLList object or any value that may be converted to a String.

**Returns:**

the updated XML object.

---

### setChildren

**Signature:** `setChildren(value : Object) : XML`

**Description:** Replaces the XML properties of this XML object with a new set of XML properties from value.

**Parameters:**

- `value`: a single XML object or an XMLList.

**Returns:**

the updated XML object.

---

### setLocalName

**Signature:** `setLocalName(name : String) : void`

**Description:** Replaces the local name of this XML object with a string constructed from the specified name.

**Parameters:**

- `name`: the new local name.

---

### setName

**Signature:** `setName(name : String) : void`

**Description:** Replaces the name of this XML object with a QName or AttributeName constructed from the specified name.

**Parameters:**

- `name`: the new name of this XML object.

---

### setNamespace

**Signature:** `setNamespace(ns : Namespace) : void`

**Description:** Replaces the namespace associated with the name of this XML object with the specified namespace.

**Parameters:**

- `ns`: the namespace to associated with the name of thix XML object.

---

### setSettings

**Signature:** `static setSettings() : void`

**Description:** Restores the default settings for the following XML properties: XML.ignoreComments = true XML.ignoreProcessingInstructions = true XML.ignoreWhitespace = true XML.prettyIndent = 2 XML.prettyPrinting = true

---

### setSettings

**Signature:** `static setSettings(settings : Object) : void`

**Description:** Updates the collection of global XML properties: ignoreComments, ignoreProcessingInstructions, ignoreWhitespace, prettyPrinting, prettyIndent, and prettyPrinting.

**Parameters:**

- `settings`: an object with each of the following properties: ignoreComments, ignoreProcessingInstructions, ignoreWhitespace, prettyIndent, and prettyPrinting.

---

### settings

**Signature:** `static settings() : Object`

**Description:** Returns the collection of global XML properties: ignoreComments, ignoreProcessingInstructions, ignoreWhitespace, prettyPrinting, prettyIndent, and prettyPrinting.

**Returns:**

an object with each of the following properties: ignoreComments, ignoreProcessingInstructions, ignoreWhitespace, prettyIndent, and prettyPrinting.

---

### text

**Signature:** `text() : XMLList`

**Description:** Returns an XMLList containing all XML properties of this XML object that represent XML text nodes.

**Returns:**

an XMLList containing all XML properties of this XML object that represent XML text nodes.

---

### toString

**Signature:** `toString() : String`

**Description:** Returns the String representation of the XML object. If the object contains simple content, this method returns a String with tag, attributes, and namespace declarations removed. However, if the object contains complex content, this method returns an XML encoded String representing the entire XML object. If you want to return the entire XML object regardless of content complexity, use the toXMLString() method.

**Returns:**

the String representation of the XML object.

---

### toXMLString

**Signature:** `toXMLString() : String`

**Description:** Returns a XML-encoded String representation of the XML object, including tag and attributed delimiters.

**Returns:**

the string representation of the XML object.

---

### valueOf

**Signature:** `valueOf() : XML`

**Description:** Returns this XML object.

**Returns:**

this XML object.

---
```

--------------------------------------------------------------------------------
/tests/mcp/node/generate-cartridge-structure.docs-only.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Programmatic tests for generate_cartridge_structure tool
 * 
 * These tests provide comprehensive verification of cartridge generation functionality,
 * including directory structure validation, file content verification, cleanup operations,
 * and edge case handling. All tests use system temp directory for safe isolation.
 * 
 * Response format discovered via aegis query:
 * - Success: { content: [{ type: "text", text: "{\"success\": true, \"message\": \"...\", \"createdFiles\": [...], \"createdDirectories\": [...], \"skippedFiles\": []}" }] }
 * - Error: { content: [{ type: "text", text: "Error: ..." }], isError: true }
 */

import { test, describe, before, after, beforeEach, afterEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { connect } from 'mcp-aegis';
import { promises as fs } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { randomBytes } from 'node:crypto';

describe('generate_cartridge_structure Programmatic Tests', () => {
  let client;
  let testDirectories = []; // Track directories for cleanup

  before(async () => {
    client = await connect('./aegis.config.docs-only.json');
  });

  after(async () => {
    if (client?.connected) {
      await client.disconnect();
    }
  });

  beforeEach(() => {
    // CRITICAL: Clear all buffers to prevent test interference
    client.clearAllBuffers(); // Recommended - comprehensive protection
  });

  afterEach(async () => {
    // Clean up all test directories
    for (const dir of testDirectories) {
      try {
        await fs.rm(dir, { recursive: true, force: true });
      } catch (error) {
        // Ignore cleanup errors - directory might not exist
        console.warn(`Cleanup warning for ${dir}:`, error.message);
      }
    }
    testDirectories = [];
  });

  /**
   * Create a unique temporary directory for testing
   * @returns {string} Absolute path to temporary directory
   */
  function createTempTestDir() {
    const uniqueId = randomBytes(8).toString('hex');
    const testDir = join(tmpdir(), `mcp-cartridge-test-${uniqueId}`);
    testDirectories.push(testDir);
    return testDir;
  }

  /**
   * Check if a file exists and is readable
   * @param {string} filePath - Path to check
   * @returns {Promise<boolean>} True if file exists and is readable
   */
  async function fileExists(filePath) {
    try {
      await fs.access(filePath, fs.constants.F_OK | fs.constants.R_OK);
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Check if a directory exists and is readable
   * @param {string} dirPath - Path to check
   * @returns {Promise<boolean>} True if directory exists and is readable
   */
  async function directoryExists(dirPath) {
    try {
      const stat = await fs.stat(dirPath);
      return stat.isDirectory();
    } catch {
      return false;
    }
  }

  /**
   * Read and parse JSON response from tool execution
   * @param {object} result - MCP tool result
   * @returns {object} Parsed response object
   */
  function parseToolResponse(result) {
    assert.equal(result.isError, false, 'Tool should execute successfully');
    assert.ok(result.content, 'Result should have content');
    assert.equal(result.content.length, 1, 'Should have exactly one content item');
    assert.equal(result.content[0].type, 'text', 'Content should be text type');
    
    const responseText = result.content[0].text;
    return JSON.parse(responseText);
  }

  describe('Protocol Compliance', () => {
    test('should be properly connected to MCP server', async () => {
      assert.ok(client.connected, 'Client should be connected');
    });

    test('should have generate_cartridge_structure tool available', async () => {
      const tools = await client.listTools();
      const cartridgeTool = tools.find(tool => tool.name === 'generate_cartridge_structure');
      
      assert.ok(cartridgeTool, 'generate_cartridge_structure tool should be available');
      assert.equal(cartridgeTool.name, 'generate_cartridge_structure');
      assert.ok(cartridgeTool.description, 'Tool should have description');
      assert.ok(cartridgeTool.inputSchema, 'Tool should have input schema');
      assert.equal(cartridgeTool.inputSchema.type, 'object');
      assert.ok(cartridgeTool.inputSchema.properties.cartridgeName, 'Tool should have cartridgeName parameter');
      assert.ok(cartridgeTool.inputSchema.required.includes('cartridgeName'), 'cartridgeName should be required');
    });
  });

  describe('Full Project Setup Generation', () => {
    test('should create complete project structure with all required files', async () => {
      const testDir = createTempTestDir();
      const cartridgeName = 'test_full_project';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName,
        targetPath: testDir,
        fullProjectSetup: true
      });

      const response = parseToolResponse(result);
      
      // Verify response structure
      assert.equal(response.success, true, 'Operation should be successful');
      assert.ok(response.message.includes('Successfully created full project setup'), 'Message should indicate full project setup');
      assert.ok(response.message.includes(cartridgeName), 'Message should include cartridge name');
      assert.ok(response.message.includes(testDir), 'Message should include target path');
      assert.ok(Array.isArray(response.createdFiles), 'Should have createdFiles array');
      assert.ok(Array.isArray(response.createdDirectories), 'Should have createdDirectories array');
      assert.ok(Array.isArray(response.skippedFiles), 'Should have skippedFiles array');

      // Verify essential project files were created
      const expectedProjectFiles = [
        'package.json',
        'dw.json',
        'webpack.config.js',
        '.eslintrc.json',
        '.stylelintrc.json',
        '.eslintignore',
        '.gitignore'
      ];

      for (const file of expectedProjectFiles) {
        const filePath = join(testDir, file);
        assert.ok(await fileExists(filePath), `Project file ${file} should exist`);
        assert.ok(response.createdFiles.some(f => f.endsWith(file)), `${file} should be in createdFiles list`);
      }

      // Verify cartridge-specific files
      const cartridgeDir = join(testDir, 'cartridges', cartridgeName);
      const expectedCartridgeFiles = [
        '.project',
        join('cartridge', `${cartridgeName}.properties`)
      ];

      for (const file of expectedCartridgeFiles) {
        const filePath = join(cartridgeDir, file);
        assert.ok(await fileExists(filePath), `Cartridge file ${file} should exist`);
      }

      // Verify directory structure
      const expectedDirectories = [
        testDir,
        join(testDir, 'cartridges'),
        cartridgeDir,
        join(cartridgeDir, 'cartridge'),
        join(cartridgeDir, 'cartridge', 'controllers'),
        join(cartridgeDir, 'cartridge', 'models'),
        join(cartridgeDir, 'cartridge', 'templates'),
        join(cartridgeDir, 'cartridge', 'templates', 'default'),
        join(cartridgeDir, 'cartridge', 'templates', 'resources'),
        join(cartridgeDir, 'cartridge', 'client'),
        join(cartridgeDir, 'cartridge', 'client', 'default'),
        join(cartridgeDir, 'cartridge', 'client', 'default', 'js'),
        join(cartridgeDir, 'cartridge', 'client', 'default', 'scss')
      ];

      for (const dir of expectedDirectories) {
        assert.ok(await directoryExists(dir), `Directory ${dir} should exist`);
      }

      // Verify file contents - check package.json contains cartridge name
      const packageJsonPath = join(testDir, 'package.json');
      const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8');
      const packageJson = JSON.parse(packageJsonContent);
      assert.ok(packageJson.name.includes(cartridgeName), 'package.json should reference cartridge name');
    });

    test('should create cartridge with default fullProjectSetup when not specified', async () => {
      const testDir = createTempTestDir();
      const cartridgeName = 'test_default_full';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName,
        targetPath: testDir
        // fullProjectSetup not specified - should default to true
      });

      const response = parseToolResponse(result);
      
      assert.equal(response.success, true, 'Operation should be successful');
      assert.ok(response.message.includes('full project setup'), 'Should create full project setup by default');

      // Verify project files were created (indicates full project setup)
      const packageJsonPath = join(testDir, 'package.json');
      assert.ok(await fileExists(packageJsonPath), 'package.json should exist with default full project setup');
    });
  });

  describe('Cartridge-Only Generation', () => {
    test('should create cartridge structure without project files when fullProjectSetup is false', async () => {
      const testDir = createTempTestDir();
      const cartridgeName = 'test_cartridge_only';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName,
        targetPath: testDir,
        fullProjectSetup: false
      });

      const response = parseToolResponse(result);
      
      // Verify response structure
      assert.equal(response.success, true, 'Operation should be successful');
      assert.ok(response.message.includes('Successfully created cartridge'), 'Message should indicate cartridge creation');
      assert.ok(response.message.includes('existing project'), 'Message should indicate existing project context');
      assert.ok(response.message.includes(cartridgeName), 'Message should include cartridge name');

      // Verify cartridge files were created
      const cartridgeDir = join(testDir, 'cartridges', cartridgeName);
      const expectedCartridgeFiles = [
        join(cartridgeDir, '.project'),
        join(cartridgeDir, 'cartridge', `${cartridgeName}.properties`)
      ];

      for (const filePath of expectedCartridgeFiles) {
        assert.ok(await fileExists(filePath), `Cartridge file ${filePath} should exist`);
      }

      // Verify project files were NOT created
      const projectFiles = ['package.json', 'dw.json', 'webpack.config.js'];
      for (const file of projectFiles) {
        const filePath = join(testDir, file);
        assert.ok(!(await fileExists(filePath)), `Project file ${file} should NOT exist in cartridge-only mode`);
      }

      // Verify cartridge directory structure exists
      const expectedDirs = [
        join(testDir, 'cartridges'),
        cartridgeDir,
        join(cartridgeDir, 'cartridge'),
        join(cartridgeDir, 'cartridge', 'controllers'),
        join(cartridgeDir, 'cartridge', 'models'),
        join(cartridgeDir, 'cartridge', 'templates')
      ];

      for (const dir of expectedDirs) {
        assert.ok(await directoryExists(dir), `Cartridge directory ${dir} should exist`);
      }
    });
  });

  describe('Target Path Handling', () => {
    test('should create cartridge in current working directory when targetPath not specified', async () => {
      const cartridgeName = 'test_cwd_cartridge';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName
        // targetPath not specified - should use current working directory
      });

      const response = parseToolResponse(result);
      
      assert.equal(response.success, true, 'Operation should be successful');
      assert.ok(Array.isArray(response.createdFiles), 'Should have createdFiles array');
      assert.ok(response.createdFiles.length > 0, 'Should have created files');

      // Clean up files created in current directory
      const cwd = process.cwd();
      const createdPaths = [...response.createdFiles, ...response.createdDirectories];
      
      for (const path of createdPaths) {
        try {
          if (path.startsWith(cwd)) {
            const relativePath = path.substring(cwd.length + 1);
            if (relativePath.startsWith('cartridges/') || ['package.json', 'dw.json', 'webpack.config.js', '.eslintrc.json', '.stylelintrc.json', '.eslintignore', '.gitignore'].includes(relativePath)) {
              await fs.rm(path, { recursive: true, force: true });
            }
          }
        } catch {
          // Ignore cleanup errors
        }
      }
    });

    test('should handle absolute target paths correctly', async () => {
      const testDir = createTempTestDir();
      const cartridgeName = 'test_absolute_path';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName,
        targetPath: testDir,
        fullProjectSetup: false
      });

      const response = parseToolResponse(result);
      
      assert.equal(response.success, true, 'Operation should be successful');
      
      // Verify files were created in specified absolute path
      const cartridgeDir = join(testDir, 'cartridges', cartridgeName);
      assert.ok(await directoryExists(cartridgeDir), 'Cartridge should be created in specified absolute path');
      
      // Verify all created paths are under the specified target directory
      for (const createdPath of response.createdFiles) {
        assert.ok(createdPath.startsWith(testDir), `Created file ${createdPath} should be under target directory ${testDir}`);
      }
      
      for (const createdDir of response.createdDirectories) {
        assert.ok(createdDir.startsWith(testDir) || createdDir === testDir, `Created directory ${createdDir} should be under target directory ${testDir}`);
      }
    });
  });

  describe('Cartridge Name Validation', () => {
    test('should reject empty cartridge name', async () => {
      const testDir = createTempTestDir();

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName: '',
        targetPath: testDir
      });

      assert.equal(result.isError, true, 'Should return error for empty cartridge name');
      assert.ok(result.content[0].text.includes('Error'), 'Error message should be present');
      assert.ok(result.content[0].text.includes('valid identifier'), 'Should mention valid identifier requirement');
    });

    test('should reject missing cartridge name', async () => {
      const testDir = createTempTestDir();

      const result = await client.callTool('generate_cartridge_structure', {
        targetPath: testDir
        // cartridgeName missing
      });

      assert.equal(result.isError, true, 'Should return error for missing cartridge name');
      assert.ok(result.content[0].text.includes('Error'), 'Error message should be present');
    });

    test('should accept valid cartridge names with different formats', async () => {
      const testDir = createTempTestDir();
      const validNames = [
        'simple_cartridge',
        'plugin-example',
        'my_plugin_123',
        'cart123',
        'a_b_c'
      ];

      for (const cartridgeName of validNames) {
        const subTestDir = join(testDir, cartridgeName + '_test');
        testDirectories.push(subTestDir);

        const result = await client.callTool('generate_cartridge_structure', {
          cartridgeName,
          targetPath: subTestDir,
          fullProjectSetup: false
        });

        const response = parseToolResponse(result);
        assert.equal(response.success, true, `Cartridge name "${cartridgeName}" should be valid`);
        assert.ok(response.message.includes(cartridgeName), `Message should include cartridge name "${cartridgeName}"`);
      }
    });
  });

  describe('File System Integration', () => {
    test('should handle directory creation permissions correctly', async () => {
      const testDir = createTempTestDir();
      const cartridgeName = 'test_permissions';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName,
        targetPath: testDir,
        fullProjectSetup: false
      });

      const response = parseToolResponse(result);
      assert.equal(response.success, true, 'Should handle directory creation successfully');

      // Verify created directories have appropriate permissions (readable and writable)
      const cartridgeDir = join(testDir, 'cartridges', cartridgeName);
      const stat = await fs.stat(cartridgeDir);
      
      // Check that directory is readable and writable by owner
      const mode = stat.mode;
      const ownerRead = (mode & 0o400) !== 0;
      const ownerWrite = (mode & 0o200) !== 0;
      const ownerExecute = (mode & 0o100) !== 0;
      
      assert.ok(ownerRead, 'Directory should be readable by owner');
      assert.ok(ownerWrite, 'Directory should be writable by owner');
      assert.ok(ownerExecute, 'Directory should be executable by owner');
    });

    test('should create files with correct content structure', async () => {
      const testDir = createTempTestDir();
      const cartridgeName = 'test_file_content';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName,
        targetPath: testDir,
        fullProjectSetup: true
      });

      const response = parseToolResponse(result);
      assert.equal(response.success, true, 'Should create files successfully');

      // Check package.json structure
      const packageJsonPath = join(testDir, 'package.json');
      const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8');
      const packageJson = JSON.parse(packageJsonContent);
      
      assert.ok(packageJson.name, 'package.json should have name field');
      assert.ok(packageJson.version, 'package.json should have version field');
      assert.ok(packageJson.scripts, 'package.json should have scripts');
      assert.ok(packageJson.devDependencies, 'package.json should have devDependencies');

      // Check cartridge properties file
      const propertiesPath = join(testDir, 'cartridges', cartridgeName, 'cartridge', `${cartridgeName}.properties`);
      const propertiesContent = await fs.readFile(propertiesPath, 'utf-8');
      
      assert.ok(propertiesContent.includes('demandware.cartridges'), 'Properties file should contain demandware.cartridges');
      assert.ok(propertiesContent.includes(cartridgeName), 'Properties file should reference cartridge name');

      // Check .project file (Eclipse project file)
      const projectPath = join(testDir, 'cartridges', cartridgeName, '.project');
      const projectContent = await fs.readFile(projectPath, 'utf-8');
      
      assert.ok(projectContent.includes('<name>'), '.project file should contain project name tag');
      assert.ok(projectContent.includes(cartridgeName), '.project file should reference cartridge name');
    });

    test('should handle nested directory creation correctly', async () => {
      const testDir = createTempTestDir();
      const cartridgeName = 'test_nested_dirs';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName,
        targetPath: testDir,
        fullProjectSetup: false
      });

      const response = parseToolResponse(result);
      assert.equal(response.success, true, 'Should create nested directories successfully');

      // Verify deeply nested directories were created
      const deepPath = join(testDir, 'cartridges', cartridgeName, 'cartridge', 'client', 'default', 'scss');
      assert.ok(await directoryExists(deepPath), 'Deep nested directory structure should be created');

      // Verify specific key directories in the structure exist
      const keyDirectories = [
        join(testDir, 'cartridges', cartridgeName),
        join(testDir, 'cartridges', cartridgeName, 'cartridge'),
        join(testDir, 'cartridges', cartridgeName, 'cartridge', 'client'),
        join(testDir, 'cartridges', cartridgeName, 'cartridge', 'client', 'default'),
        join(testDir, 'cartridges', cartridgeName, 'cartridge', 'client', 'default', 'scss')
      ];
      
      for (const dir of keyDirectories) {
        assert.ok(await directoryExists(dir), `Key directory ${dir} should exist`);
      }
    });
  });

  describe('Edge Cases and Error Handling', () => {
    test('should handle special characters in target path', async () => {
      const specialDir = join(tmpdir(), 'mcp-test-special chars & symbols');
      testDirectories.push(specialDir);
      const cartridgeName = 'test_special_path';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName,
        targetPath: specialDir,
        fullProjectSetup: false
      });

      const response = parseToolResponse(result);
      assert.equal(response.success, true, 'Should handle special characters in path');
      
      const cartridgeDir = join(specialDir, 'cartridges', cartridgeName);
      assert.ok(await directoryExists(cartridgeDir), 'Should create cartridge in path with special characters');
    });

    test('should report created files and directories accurately', async () => {
      const testDir = createTempTestDir();
      const cartridgeName = 'test_file_reporting';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName,
        targetPath: testDir,
        fullProjectSetup: true
      });

      const response = parseToolResponse(result);
      assert.equal(response.success, true, 'Operation should be successful');

      // Verify all reported files actually exist
      for (const filePath of response.createdFiles) {
        assert.ok(await fileExists(filePath), `Reported file ${filePath} should actually exist`);
      }

      // Verify all reported directories actually exist
      for (const dirPath of response.createdDirectories) {
        assert.ok(await directoryExists(dirPath), `Reported directory ${dirPath} should actually exist`);
      }

      // Verify files list is comprehensive - check for essential files
      const essentialFiles = ['package.json', 'dw.json', '.gitignore'];
      for (const file of essentialFiles) {
        assert.ok(response.createdFiles.some(f => f.endsWith(file)), `Essential file ${file} should be in createdFiles list`);
      }

      // Verify no duplicate entries in created files/directories
      const uniqueFiles = new Set(response.createdFiles);
      const uniqueDirs = new Set(response.createdDirectories);
      
      assert.equal(response.createdFiles.length, uniqueFiles.size, 'createdFiles should not contain duplicates');
      assert.equal(response.createdDirectories.length, uniqueDirs.size, 'createdDirectories should not contain duplicates');
    });

    test('should handle very long cartridge names appropriately', async () => {
      const testDir = createTempTestDir();
      const longCartridgeName = 'very_long_cartridge_name_that_exceeds_normal_limits_but_should_still_work_properly_for_testing_purposes';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName: longCartridgeName,
        targetPath: testDir,
        fullProjectSetup: false
      });

      // The tool should either succeed or provide a clear error message about name length
      if (result.isError) {
        assert.ok(result.content[0].text.includes('Error'), 'Should provide clear error for long names');
      } else {
        const response = parseToolResponse(result);
        assert.equal(response.success, true, 'Should handle long cartridge names if accepted');
        
        const cartridgeDir = join(testDir, 'cartridges', longCartridgeName);
        assert.ok(await directoryExists(cartridgeDir), 'Should create directory with long name');
      }
    });
  });

  describe('Response Format Consistency', () => {
    test('should always return consistent JSON response structure', async () => {
      const testDir = createTempTestDir();
      const cartridgeName = 'test_response_format';

      const result = await client.callTool('generate_cartridge_structure', {
        cartridgeName,
        targetPath: testDir,
        fullProjectSetup: false
      });

      const response = parseToolResponse(result);
      
      // Verify required response fields
      assert.ok(typeof response.success === 'boolean', 'Response should have boolean success field');
      assert.ok(typeof response.message === 'string', 'Response should have string message field');
      assert.ok(Array.isArray(response.createdFiles), 'Response should have createdFiles array');
      assert.ok(Array.isArray(response.createdDirectories), 'Response should have createdDirectories array');
      assert.ok(Array.isArray(response.skippedFiles), 'Response should have skippedFiles array');

      // Verify field content types
      for (const file of response.createdFiles) {
        assert.ok(typeof file === 'string', 'All createdFiles entries should be strings');
        assert.ok(file.length > 0, 'All createdFiles entries should be non-empty');
      }

      for (const dir of response.createdDirectories) {
        assert.ok(typeof dir === 'string', 'All createdDirectories entries should be strings');
        assert.ok(dir.length > 0, 'All createdDirectories entries should be non-empty');
      }

      for (const skipped of response.skippedFiles) {
        assert.ok(typeof skipped === 'string', 'All skippedFiles entries should be strings');
      }
    });

    test('should provide meaningful success messages', async () => {
      const testDir = createTempTestDir();
      const cartridgeName = 'test_success_message';

      // Test full project setup message
      const fullResult = await client.callTool('generate_cartridge_structure', {
        cartridgeName: cartridgeName + '_full',
        targetPath: testDir + '_full',
        fullProjectSetup: true
      });

      const fullResponse = parseToolResponse(fullResult);
      assert.ok(fullResponse.message.includes('full project setup'), 'Full project message should mention full project setup');
      assert.ok(fullResponse.message.includes(cartridgeName + '_full'), 'Message should include cartridge name');

      // Test cartridge-only message
      const cartridgeResult = await client.callTool('generate_cartridge_structure', {
        cartridgeName: cartridgeName + '_only',
        targetPath: testDir + '_only',
        fullProjectSetup: false
      });

      const cartridgeResponse = parseToolResponse(cartridgeResult);
      assert.ok(cartridgeResponse.message.includes('existing project'), 'Cartridge-only message should mention existing project');
      assert.ok(cartridgeResponse.message.includes(cartridgeName + '_only'), 'Message should include cartridge name');

      // Add these additional test directories to cleanup list
      testDirectories.push(testDir + '_full', testDir + '_only');
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/mcp/node/search-system-object-attribute-definitions.full-mode.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
import { test, describe, before, after, beforeEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { connect } from 'mcp-aegis';

describe('search_system_object_attribute_definitions - Full Mode Programmatic Tests', () => {
  let client;

  before(async () => {
    client = await connect('./aegis.config.with-dw.json');
  });

  after(async () => {
    if (client?.connected) {
      await client.disconnect();
    }
  });

  beforeEach(() => {
    // Critical: Clear stderr to prevent test interference
    client.clearStderr();
  });

  // Helper functions for common validations
  function assertValidMCPResponse(result) {
    assert.ok(result.content, 'Should have content');
    assert.ok(Array.isArray(result.content), 'Content should be array');
    assert.equal(typeof result.isError, 'boolean', 'isError should be boolean');
  }

  function assertValidSearchResponse(result) {
    assertValidMCPResponse(result);
    assert.equal(result.isError, false, 'Should not be error');
    
    const responseText = result.content[0].text;
    const responseData = JSON.parse(responseText);
    
    assert.ok(responseData.hits !== undefined, 'Should have hits array');
    assert.ok(Array.isArray(responseData.hits), 'Hits should be array');
    assert.ok(typeof responseData.total === 'number', 'Should have total count');
    
    return responseData;
  }

  function assertValidAttributeDefinition(attribute) {
    assert.ok(typeof attribute.id === 'string', 'Attribute should have id');
    assert.ok(typeof attribute._type === 'string', 'Should have _type');
    assert.equal(attribute._type, 'object_attribute_definition', 'Should be object_attribute_definition type');
    assert.ok(typeof attribute._resource_state === 'string', 'Should have _resource_state');
    assert.ok(typeof attribute.link === 'string', 'Should have link');
    
    // Note: OCAPI search results only return basic info (id, link, _type, _resource_state)
    // Detailed fields like display_name, value_type, mandatory are only available 
    // when fetching individual attributes via the link
  }

  describe('Protocol Compliance and Tool Availability', () => {
    test('should be connected to MCP server', async () => {
      assert.ok(client.connected, 'Client should be connected');
    });

    test('should have search_system_object_attribute_definitions tool available', async () => {
      const tools = await client.listTools();
      const toolNames = tools.map(tool => tool.name);
      assert.ok(toolNames.includes('search_system_object_attribute_definitions'), 
        'Tool should be available in full mode');
    });

    test('should have proper tool schema definition', async () => {
      const tools = await client.listTools();
      const tool = tools.find(t => t.name === 'search_system_object_attribute_definitions');
      
      assert.ok(tool, 'Tool should exist');
      assert.ok(tool.description, 'Tool should have description');
      assert.ok(tool.inputSchema, 'Tool should have input schema');
      assert.equal(tool.inputSchema.type, 'object', 'Schema should be object type');
      
      // Validate required parameters
      const required = tool.inputSchema.required || [];
      assert.ok(required.includes('objectType'), 'objectType should be required');
      assert.ok(required.includes('searchRequest'), 'searchRequest should be required');
    });
  });

  describe('Dynamic Test Case Generation', () => {
    test('should generate and validate test cases for known object types', async () => {
      const knownObjectTypes = ['Product', 'Customer', 'Order', 'Category', 'Site'];
      
      for (const objectType of knownObjectTypes) {
        // Test with match_all_query for each object type
        const result = await client.callTool('search_system_object_attribute_definitions', {
          objectType: objectType,
          searchRequest: {
            query: { match_all_query: {} },
            count: 5
          }
        });
        
        const responseData = assertValidSearchResponse(result);
        
        // Validate each attribute in the response
        responseData.hits.forEach(attribute => {
          assertValidAttributeDefinition(attribute);
          
          // Business logic validation - verify attribute IDs are reasonable
          assert.ok(attribute.id.length > 0, 'Attribute ID should not be empty');
          assert.ok(!/\s/.test(attribute.id), 'Attribute ID should not contain spaces');
          
          // Note: Detailed validation of Product-specific fields would require
          // fetching individual attributes via their links, which is beyond
          // the scope of search functionality testing
        });
        
        assert.ok(responseData.total >= 0, `${objectType} should have non-negative attribute count`);
      }
    });

    test('should validate query type combinations dynamically', async () => {
      const queryTypes = [
        { match_all_query: {} },
        { 
          text_query: {
            fields: ['id', 'display_name', 'description'],
            search_phrase: 'name'
          }
        },
        {
          term_query: {
            fields: ['value_type'],
            operator: 'is',
            values: ['string']
          }
        }
      ];
      
      for (const query of queryTypes) {
        const result = await client.callTool('search_system_object_attribute_definitions', {
          objectType: 'Product',
          searchRequest: { query, count: 3 }
        });
        
        const responseData = assertValidSearchResponse(result);
        
        // Validate that query type affects results appropriately
        if (query.text_query && query.text_query.search_phrase === 'name') {
          // Should return attributes related to 'name'
          // Note: Not asserting as it depends on actual data and fuzzy matching
          responseData.hits.some(attr => 
            attr.id.toLowerCase().includes('name') ||
            Object.values(attr.display_name).some(name => 
              name.toLowerCase().includes('name')
            )
          );
        }
        
        if (query.term_query && query.term_query.values.includes('string')) {
          // Note: OCAPI search results don't include value_type in basic response
          // This would require fetching individual attributes to validate
          // For search testing, we focus on verifying the search request/response structure
          assert.ok(responseData.hits.length >= 0, 'Should return non-negative results');
        }
      }
    });
  });

  describe('Complex Query Combinations', () => {
    test('should handle bool_query with multiple clauses', async () => {
      const complexQuery = {
        bool_query: {
          must: [
            {
              term_query: {
                fields: ['mandatory'],
                operator: 'is',
                values: ['true']
              }
            },
            {
              term_query: {
                fields: ['searchable'],
                operator: 'is', 
                values: ['true']
              }
            }
          ],
          should: [
            {
              text_query: {
                fields: ['id'],
                search_phrase: 'custom'
              }
            }
          ],
          must_not: [
            {
              term_query: {
                fields: ['system'],
                operator: 'is',
                values: ['true']
              }
            }
          ]
        }
      };

      const result = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Product',
        searchRequest: {
          query: complexQuery,
          count: 10
        }
      });

      const responseData = assertValidSearchResponse(result);
      
      // Validate that results match the complex query criteria
      responseData.hits.forEach(attr => {
        // Note: OCAPI search results only return basic attribute info (id, link, _type)
        // Detailed validation of mandatory, searchable, system flags would require
        // fetching individual attributes via their links. For search API testing,
        // we focus on validating the search request/response structure and pagination.
        assertValidAttributeDefinition(attr);
        assert.ok(attr.id.length > 0, 'Attribute should have valid ID');
      });
    });

    test('should handle nested bool_query structures', async () => {
      const nestedQuery = {
        bool_query: {
          must: [
            {
              bool_query: {
                should: [
                  {
                    term_query: {
                      fields: ['value_type'],
                      operator: 'one_of',
                      values: ['string', 'text']
                    }
                  },
                  {
                    term_query: {
                      fields: ['value_type'],
                      operator: 'is',
                      values: ['enum-of-string']
                    }
                  }
                ]
              }
            }
          ]
        }
      };

      const result = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Customer',
        searchRequest: {
          query: nestedQuery,
          count: 5,
          sorts: [
            { field: 'id', sort_order: 'asc' }
          ]
        }
      });

      const responseData = assertValidSearchResponse(result);
      
      // Validate nested query results
      responseData.hits.forEach(attr => {
        // Note: OCAPI search results don't include value_type in basic response
        // For search API testing, we validate the structure and response format
        assertValidAttributeDefinition(attr);
        assert.ok(attr.id.length > 0, 'Should have valid attribute ID');
      });

      // Validate sorting is applied
      if (responseData.hits.length > 1) {
        for (let i = 1; i < responseData.hits.length; i++) {
          assert.ok(responseData.hits[i].id >= responseData.hits[i-1].id, 
            'Results should be sorted by id in ascending order');
        }
      }
    });
  });

  describe('Pagination and Large Dataset Handling', () => {
    test('should handle pagination correctly across multiple requests', async () => {
      const pageSize = 5;
      const maxPages = 3;
      const allResults = [];
      let totalCount = 0;

      for (let page = 0; page < maxPages; page++) {
        const result = await client.callTool('search_system_object_attribute_definitions', {
          objectType: 'Product',
          searchRequest: {
            query: { match_all_query: {} },
            count: pageSize,
            start: page * pageSize,
            sorts: [{ field: 'id', sort_order: 'asc' }]
          }
        });

        const responseData = assertValidSearchResponse(result);
        
        if (page === 0) {
          totalCount = responseData.total;
        } else {
          assert.equal(responseData.total, totalCount, 
            'Total count should be consistent across pages');
        }

        // Validate page results
        assert.ok(responseData.hits.length <= pageSize, 
          'Page should not exceed requested size');
        
        // Check for duplicates across pages
        responseData.hits.forEach(attr => {
          const isDuplicate = allResults.some(existing => existing.id === attr.id);
          assert.equal(isDuplicate, false, `Attribute ${attr.id} should not appear in multiple pages`);
          allResults.push(attr);
        });

        // Break if we've reached the end
        if (responseData.hits.length < pageSize || 
            allResults.length >= responseData.total) {
          break;
        }
      }

      // Validate overall pagination consistency
      assert.ok(allResults.length > 0, 'Should have retrieved some results');
      
      // Validate sorting consistency across pages
      if (allResults.length > 1) {
        for (let i = 1; i < allResults.length; i++) {
          assert.ok(allResults[i].id >= allResults[i-1].id, 
            'Results should maintain sort order across pages');
        }
      }
    });

    test('should handle large count requests appropriately', async () => {
      const largeCount = 200;
      
      const result = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Product',
        searchRequest: {
          query: { match_all_query: {} },
          count: largeCount
        }
      });

      const responseData = assertValidSearchResponse(result);
      
      // Validate that server handles large requests (may return fewer than requested)
      assert.ok(responseData.hits.length <= largeCount, 
        'Should not return more than requested');
      assert.ok(responseData.hits.length >= 0, 
        'Should return non-negative number of results');
      
      // Validate that all returned results are valid
      responseData.hits.forEach((attr, index) => {
        try {
          assertValidAttributeDefinition(attr);
        } catch (error) {
          throw new Error(`Invalid attribute at index ${index}: ${error.message}`);
        }
      });
    });
  });

  describe('Cross-Field Validation and Business Logic', () => {
    test('should validate attribute relationships and constraints', async () => {
      const result = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Product',
        searchRequest: {
          query: { match_all_query: {} },
          count: 50
        }
      });

      const responseData = assertValidSearchResponse(result);
      
      // Business logic validations - based on OCAPI search response structure
      responseData.hits.forEach(attr => {
        assertValidAttributeDefinition(attr);
        
        // Validate basic structure returned by OCAPI search
        assert.ok(attr.id.length > 0, 'Attribute ID should not be empty');
        assert.ok(attr.link.includes('/attribute_definitions/'), 'Link should be valid');
        assert.ok(attr._resource_state.length > 0, 'Should have resource state');
        
        // Note: Detailed attribute properties (value_type, mandatory, searchable, system, 
        // display_name) are not included in search results. They would need to be 
        // fetched individually via the attribute's link for detailed validation.
      });
    });

    test('should validate search result consistency across different sort orders', async () => {
      const sortFields = ['id']; // Only test 'id' since it's available in OCAPI search results
      const sortOrders = ['asc', 'desc'];
      const resultSets = new Map();

      for (const field of sortFields) {
        for (const order of sortOrders) {
          const result = await client.callTool('search_system_object_attribute_definitions', {
            objectType: 'Customer',
            searchRequest: {
              query: { match_all_query: {} },
              count: 10,
              sorts: [{ field, sort_order: order }]
            }
          });

          const responseData = assertValidSearchResponse(result);
          const key = `${field}_${order}`;
          resultSets.set(key, responseData);

          // Validate sort order is applied - OCAPI search results are sorted by ID
          if (responseData.hits.length > 1) {
            for (let i = 1; i < responseData.hits.length; i++) {
              const current = responseData.hits[i];
              const previous = responseData.hits[i-1];
              
              // For OCAPI search results, we can only sort by 'id' reliably
              // since other detailed fields are not included in search response
              if (field === 'id') {
                const currentValue = current.id;
                const previousValue = previous.id;

                if (order === 'asc') {
                  assert.ok(currentValue >= previousValue, 
                    `${field} should be in ascending order: ${previousValue} <= ${currentValue}`);
                } else {
                  assert.ok(currentValue <= previousValue, 
                    `${field} should be in descending order: ${previousValue} >= ${currentValue}`);
                }
              }
              // Note: Other sort fields (display_name, value_type) are not available 
              // in OCAPI search results, so we skip detailed validation for those
            }
          }
        }
      }

      // Validate that different sort orders return same total count
      const totalCounts = Array.from(resultSets.values()).map(data => data.total);
      const uniqueTotals = [...new Set(totalCounts)];
      assert.equal(uniqueTotals.length, 1, 
        'All sort variations should return same total count');
    });
  });

  describe('Error Recovery and Edge Cases', () => {
    test('should handle invalid object types gracefully', async () => {
      // Test non-existent object types (return empty results)
      const invalidObjectTypes = ['InvalidObject', 'NonExistent'];
      
      for (const objectType of invalidObjectTypes) {
        const result = await client.callTool('search_system_object_attribute_definitions', {
          objectType: objectType,
          searchRequest: {
            query: { match_all_query: {} },
            count: 5
          }
        });

        // OCAPI returns successful response with empty results for invalid object types
        const responseData = assertValidSearchResponse(result);
        assert.equal(responseData.total, 0, 
          `Should return 0 results for invalid object type: ${objectType}`);
        assert.equal(responseData.hits.length, 0, 
          `Should return empty hits array for invalid object type: ${objectType}`);
      }

      // Test empty object type (returns validation error)
      const emptyResult = await client.callTool('search_system_object_attribute_definitions', {
        objectType: '',
        searchRequest: {
          query: { match_all_query: {} },
          count: 5
        }
      });

      assert.equal(emptyResult.isError, true, 'Should return error for empty object type');
      const errorText = emptyResult.content[0].text;
      assert.ok(errorText.includes('objectType must be a non-empty string'), 
        'Error message should indicate objectType validation issue');
    });

    test('should handle malformed queries gracefully', async () => {
      const malformedQueries = [
        { invalid_query_type: {} },
        { text_query: { missing_required_fields: true } },
        { term_query: { fields: [], operator: 'invalid', values: [] } },
        { bool_query: { invalid_clause: [] } }
      ];

      for (const query of malformedQueries) {
        const result = await client.callTool('search_system_object_attribute_definitions', {
          objectType: 'Product',
          searchRequest: { query, count: 5 }
        });

        // Should handle malformed queries gracefully
        if (result.isError) {
          const errorText = result.content[0].text;
          assert.ok(errorText.length > 0, 'Error message should not be empty');
        } else {
          // If not an error, should return valid response structure
          assertValidSearchResponse(result);
        }
      }
    });

    test('should recover from network issues and continue working', async () => {
      // Test normal operation
      const normalResult = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Product',
        searchRequest: {
          query: { match_all_query: {} },
          count: 3
        }
      });

      assertValidSearchResponse(normalResult);
      
      // Test with missing query field - OCAPI provides default behavior (match_all_query)
      const missingQueryResult = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Product',
        searchRequest: {
          // Missing query field - OCAPI defaults to match_all_query
          count: 3
        }
      });

      // OCAPI provides graceful defaults rather than errors
      const missingQueryData = assertValidSearchResponse(missingQueryResult);
      assert.ok(missingQueryData.total >= 0, 'Should return valid results with default query');
      
      // Test that service continues to work normally after edge case
      const recoveryResult = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Customer',
        searchRequest: {
          query: { match_all_query: {} },
          count: 3
        }
      });

      assertValidSearchResponse(recoveryResult);
      assert.ok(recoveryResult.content[0].text.length > 0, 
        'Service should continue working normally');
    });
  });

  describe('Multi-Step Workflow Validation', () => {
    test('should support attribute discovery and detailed analysis workflow', async () => {
      // Step 1: Discover all attributes for an object type
      const discoveryResult = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Product',
        searchRequest: {
          query: { match_all_query: {} },
          count: 10,
          select: '(**)'
        }
      });

      const discoveryData = assertValidSearchResponse(discoveryResult);
      assert.ok(discoveryData.hits.length > 0, 'Should discover some attributes');

      // Step 2: Analyze specific attribute types found in step 1
      const valueTypes = [...new Set(discoveryData.hits.map(attr => attr.value_type))];
      
      for (const valueType of valueTypes.slice(0, 3)) { // Test first 3 types
        const typeAnalysisResult = await client.callTool('search_system_object_attribute_definitions', {
          objectType: 'Product',
          searchRequest: {
            query: {
              term_query: {
                fields: ['value_type'],
                operator: 'is',
                values: [valueType]
              }
            },
            count: 5
          }
        });

        const typeData = assertValidSearchResponse(typeAnalysisResult);
        
        // Validate that all results have the expected value type
        typeData.hits.forEach(attr => {
          // Note: OCAPI search results don't include value_type field
          // The query filtering happens server-side, so we validate structure instead
          assertValidAttributeDefinition(attr);
          assert.ok(attr.id.length > 0, `Should have valid attribute ID for type search: ${valueType}`);
        });
      }

      // Step 3: Analyze attribute relationships (mandatory vs optional)
      const mandatoryResult = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Product',
        searchRequest: {
          query: {
            term_query: {
              fields: ['mandatory'],
              operator: 'is',
              values: ['true']
            }
          },
          count: 10
        }
      });

      const mandatoryData = assertValidSearchResponse(mandatoryResult);
      
      mandatoryData.hits.forEach(attr => {
        // Note: OCAPI search results don't include mandatory field in basic response
        // The query filtering happens server-side, so we validate the response structure
        assertValidAttributeDefinition(attr);
        assert.ok(attr.id.length > 0, 'Should have valid attribute ID');
      });

      // Validate workflow consistency
      const totalMandatory = mandatoryData.total;
      assert.ok(totalMandatory >= 0, 'Should have non-negative mandatory attribute count');
      assert.ok(totalMandatory <= discoveryData.total, 
        'Mandatory attributes should be subset of all attributes');
    });

    test('should support complex search refinement workflow', async () => {
      // Step 1: Broad search
      const broadResult = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Customer',
        searchRequest: {
          query: {
            text_query: {
              fields: ['id', 'display_name'],
              search_phrase: 'address'
            }
          },
          count: 20
        }
      });

      const broadData = assertValidSearchResponse(broadResult);
      
      // Step 2: Refine to only searchable address-related attributes
      const refinedResult = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Customer',
        searchRequest: {
          query: {
            bool_query: {
              must: [
                {
                  text_query: {
                    fields: ['id', 'display_name'],
                    search_phrase: 'address'
                  }
                },
                {
                  term_query: {
                    fields: ['searchable'],
                    operator: 'is',
                    values: ['true']
                  }
                }
              ]
            }
          },
          count: 20
        }
      });

      const refinedData = assertValidSearchResponse(refinedResult);
      
      // Validate refinement logic
      assert.ok(refinedData.total <= broadData.total, 
        'Refined search should return same or fewer results');
      
      refinedData.hits.forEach(attr => {
        // Note: OCAPI search results don't include searchable field in basic response
        // The query filtering happens server-side, so we validate response structure
        assertValidAttributeDefinition(attr);
        assert.ok(attr.id.length > 0, 'Should have valid attribute ID');
        
        // Should contain address-related terms - validating with expression
        // Note: This validation works with attribute IDs which are available
        const containsAddressTerm = attr.id.toLowerCase().includes('address');
        // Expression evaluated for documentation purposes
        assert.ok(typeof containsAddressTerm === 'boolean', 'Should evaluate address term check');
      });

      // Step 3: Further refine to only custom (non-system) attributes
      const customResult = await client.callTool('search_system_object_attribute_definitions', {
        objectType: 'Customer',
        searchRequest: {
          query: {
            bool_query: {
              must: [
                {
                  text_query: {
                    fields: ['id', 'display_name'],
                    search_phrase: 'address'
                  }
                },
                {
                  term_query: {
                    fields: ['searchable'],
                    operator: 'is',
                    values: ['true']
                  }
                }
              ],
              must_not: [
                {
                  term_query: {
                    fields: ['system'],
                    operator: 'is',
                    values: ['true']
                  }
                }
              ]
            }
          },
          count: 20
        }
      });

      const customData = assertValidSearchResponse(customResult);
      
      // Validate final refinement
      assert.ok(customData.total <= refinedData.total, 
        'Custom search should return same or fewer results than refined search');
      
      customData.hits.forEach(attr => {
        // Note: OCAPI search results don't include searchable/system fields in basic response
        // The query filtering happens server-side, so we validate response structure
        assertValidAttributeDefinition(attr);
        assert.ok(attr.id.length > 0, 'Should have valid attribute ID');
      });
    });
  });
});
```

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

```javascript
/**
 * Programmatic tests for get_best_practice_guide tool
 * 
 * These tests provide advanced verification capabilities beyond YAML pattern matching,
 * including dynamic validation, comprehensive content analysis,
 * cross-guide relationship testing, and advanced error categorization for the SFCC
 * best practice guide retrieval functionality.
 * 
 * Response format discovered via aegis query:
 * - Success: { content: [{ type: "text", text: "{\"title\":\"...\",\"description\":\"...\",\"sections\":[...],\"content\":\"...\"}" }], isError: false }
 * - Error: { content: [{ type: "text", text: "Error: guideName must be a non-empty string" }], isError: true }
 * - Invalid guide: { content: [{ type: "text", text: "null" }], isError: false }
 * - JSON structure: title, description, sections array, content markdown
 */

import { test, describe, before, after, beforeEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { connect } from 'mcp-aegis';

/**
 * Content analysis utility for comprehensive guide validation
 */
class ContentAnalyzer {
  constructor() {
    this.contentPatterns = {
      security: [
        /CSRF|cross-site request forgery/i,
        /XSS|cross-site scripting/i,
        /authentication|authorization/i,
        /encryption|cryptography/i,
        /validation|sanitization/i
      ],
      performance: [
        /performance|optimization/i,
        /caching|cache/i,
        /memory|cpu/i,
        /scalability|throughput/i,
        /monitoring|metrics/i
      ],
      sfra: [
        /controller|middleware/i,
        /server\.get|server\.post/i,
        /SFRA|storefront reference architecture/i,
        /isml|template/i,
        /scss|sass/i,
        /model|view/i
      ],
      sfra_scss: [
        /@import\s+['"]~base\//i,
        /\$[a-z0-9_-]+\s*:/i,
        /@include|mixins?/i,
        /BEM|\b[a-z0-9]+__(?:[a-z0-9-]+)|\b[a-z0-9]+--[a-z0-9-]+/i,
        /prefers-reduced-motion|focus ring|wcag/i
      ],
      cartridge: [
        /cartridge path/i,
        /override mechanism/i,
        /app_storefront_base/i,
        /plugin_|app_custom_|int_|bm_/i,
        /deployment|upload/i
      ]
    };
  }

  analyzeGuide(guideData) {
    const analysis = {
      structuralIntegrity: this.checkStructuralIntegrity(guideData),
      contentDepth: this.assessContentDepth(guideData),
      codeExamples: this.extractCodeExamples(guideData),
      crossReferences: this.findCrossReferences(guideData),
      technicalAccuracy: this.validateTechnicalContent(guideData),
      readabilityMetrics: this.calculateReadabilityMetrics(guideData)
    };

    return analysis;
  }

  checkStructuralIntegrity(guideData) {
    const requiredFields = ['title', 'description', 'sections', 'content'];
    const missingFields = requiredFields.filter(field => !guideData[field]);
    
    return {
      isComplete: missingFields.length === 0,
      missingFields,
      sectionsCount: Array.isArray(guideData.sections) ? guideData.sections.length : 0,
      hasValidMarkdown: typeof guideData.content === 'string' && guideData.content.includes('#')
    };
  }

  assessContentDepth(guideData) {
    const content = guideData.content || '';
    const wordCount = content.split(/\s+/).length;
    const paragraphCount = content.split('\n\n').length;
    const headingCount = (content.match(/^#+\s/gm) || []).length;
    
    return {
      wordCount,
      paragraphCount,
      headingCount,
      averageWordsPerParagraph: wordCount / Math.max(paragraphCount, 1),
      depthScore: this.calculateDepthScore(wordCount, headingCount, paragraphCount)
    };
  }

  calculateDepthScore(wordCount, headingCount, paragraphCount) {
    // Scoring based on content richness
    let score = 0;
    if (wordCount > 2000) score += 3;
    else if (wordCount > 1000) score += 2;
    else if (wordCount > 500) score += 1;
    
    if (headingCount > 8) score += 2;
    else if (headingCount > 4) score += 1;
    
    if (paragraphCount > 15) score += 2;
    else if (paragraphCount > 8) score += 1;
    
    return Math.min(score, 5); // Max score of 5
  }

  extractCodeExamples(guideData) {
    const content = guideData.content || '';
    const codeBlocks = content.match(/```[\s\S]*?```/g) || [];
    const inlineCode = content.match(/`[^`\n]+`/g) || [];
    
    const languages = codeBlocks.map(block => {
      const match = block.match(/```(\w+)/);
      return match ? match[1] : 'unknown';
    });

    return {
      codeBlockCount: codeBlocks.length,
      inlineCodeCount: inlineCode.length,
      languages: [...new Set(languages)],
      hasJavaScript: languages.includes('javascript'),
      hasJSON: languages.includes('json'),
      hasBash: languages.includes('bash'),
      hasHTML: languages.includes('html') || languages.includes('isml')
    };
  }

  findCrossReferences(guideData) {
    const content = guideData.content || '';
    const mcpReferences = (content.match(/MCP server|MCP tool|`\w+_\w+`/g) || []).length;
    const sfccReferences = (content.match(/dw\.\w+|SFCC|Salesforce Commerce Cloud/g) || []).length;
    const externalReferences = (content.match(/https?:\/\/[^\s)]+/g) || []).length;
    
    return {
      mcpReferences,
      sfccReferences,
      externalReferences,
      totalReferences: mcpReferences + sfccReferences + externalReferences,
      hasIntegrationWorkflow: content.includes('MCP Integration Workflow')
    };
  }

  validateTechnicalContent(guideData) {
    const content = guideData.content || '';
    const guideName = this.inferGuideType(guideData);
    const expectedPatterns = this.contentPatterns[guideName] || [];
    
    const matchedPatterns = expectedPatterns.filter(pattern => pattern.test(content));

    let modernPattern = /dw\.crypto\.Cipher|server\.append|server\.prepend/i;
    if (guideName === 'sfra_scss') {
      modernPattern = /@include|prefers-reduced-motion|mixins?|@import\s+['"]~base\//i;
    }
    
    return {
      guideName,
      expectedPatterns: expectedPatterns.length,
      matchedPatterns: matchedPatterns.length,
      accuracyScore: expectedPatterns.length > 0 ? 
        (matchedPatterns.length / expectedPatterns.length) : 1,
      hasDeprecatedReferences: /WeakCipher|WeakMac|WeakMessageDigest/i.test(content),
      hasModernPractices: modernPattern.test(content)
    };
  }

  inferGuideType(guideData) {
    const title = (guideData.title || '').toLowerCase();
    if (title.includes('security')) return 'security';
    if (title.includes('performance')) return 'performance';
    if (title.includes('scss') || title.includes('styling')) return 'sfra_scss';
    if (title.includes('sfra') || title.includes('controller')) return 'sfra';
    if (title.includes('cartridge')) return 'cartridge';
    return 'general';
  }

  calculateReadabilityMetrics(guideData) {
    const content = guideData.content || '';
    const sentences = content.split(/[.!?]+/).filter(s => s.trim().length > 0);
    const words = content.split(/\s+/).filter(w => w.length > 0);
    
    const avgWordsPerSentence = words.length / Math.max(sentences.length, 1);
    const avgCharsPerWord = words.reduce((sum, word) => sum + word.length, 0) / Math.max(words.length, 1);
    
    return {
      sentenceCount: sentences.length,
      wordCount: words.length,
      avgWordsPerSentence,
      avgCharsPerWord,
      readabilityScore: this.calculateFleschScore(avgWordsPerSentence, avgCharsPerWord)
    };
  }

  calculateFleschScore(avgWordsPerSentence, avgCharsPerWord) {
    // Simplified Flesch Reading Ease approximation
    return Math.max(0, 206.835 - (1.015 * avgWordsPerSentence) - (84.6 * (avgCharsPerWord / 4.7)));
  }
}

/**
 * Error categorization utility for comprehensive error analysis
 */
class ErrorAnalyzer {
  static categorizeError(errorText) {
    const patterns = [
      { type: 'validation', keywords: ['required', 'invalid', 'missing', 'empty string'] },
      { type: 'not_found', keywords: ['not found', 'does not exist', 'null'] },
      { type: 'permission', keywords: ['permission', 'unauthorized', 'forbidden'] },
      { type: 'network', keywords: ['connection', 'timeout', 'unreachable'] },
      { type: 'parsing', keywords: ['parse', 'syntax', 'format'] }
    ];

    for (const pattern of patterns) {
      if (pattern.keywords.some(keyword => 
          errorText.toLowerCase().includes(keyword))) {
        return pattern.type;
      }
    }
    return 'unknown';
  }

  static assessErrorQuality(errorText) {
    return {
      isInformative: errorText.length > 20,
      hasContext: errorText.includes('guideName'),
      isActionable: /must|should|required|expected/i.test(errorText),
      category: this.categorizeError(errorText)
    };
  }
}

/**
 * Custom assertion helpers for MCP response validation
 */
function assertValidMCPResponse(result, expectError = false) {
  assert.ok(result.content, 'Should have content');
  assert.ok(Array.isArray(result.content), 'Content should be array');
  assert.equal(typeof result.isError, 'boolean', 'isError should be boolean');
  assert.equal(result.isError, expectError, `isError should be ${expectError}`);
}

function assertGuideContent(result, expectedGuideName) {
  assertValidMCPResponse(result);
  assert.equal(result.content[0].type, 'text');
  
  const text = result.content[0].text;
  if (text === 'null') {
    // Invalid guide name returns null
    return null;
  }
  
  const guideData = JSON.parse(text);
  assert.ok(guideData.title, 'Guide should have title');
  assert.ok(guideData.description, 'Guide should have description');
  assert.ok(Array.isArray(guideData.sections), 'Guide should have sections array');
  assert.ok(guideData.content, 'Guide should have content');
  
  if (expectedGuideName) {
    assert.ok(
      guideData.title.toLowerCase().includes(expectedGuideName.replace('_', ' ')) ||
      guideData.description.toLowerCase().includes(expectedGuideName.replace('_', ' ')),
      `Guide should be related to ${expectedGuideName}`
    );
  }
  
  return guideData;
}

function assertErrorResponse(result, expectedErrorType) {
  assertValidMCPResponse(result, true);
  assert.equal(result.content[0].type, 'text');
  
  const errorText = result.content[0].text;
  assert.ok(errorText.includes('Error:'), 'Should be error message');
  
  const errorAnalysis = ErrorAnalyzer.assessErrorQuality(errorText);
  assert.ok(errorAnalysis.isInformative, 'Error should be informative');
  
  if (expectedErrorType) {
    assert.equal(errorAnalysis.category, expectedErrorType, 
      `Error should be categorized as ${expectedErrorType}`);
  }
  
  return errorAnalysis;
}

describe('get_best_practice_guide Tool - Advanced Programmatic Tests', () => {
  let client;
  let contentAnalyzer;

  before(async () => {
    client = await connect('./aegis.config.docs-only.json');
    contentAnalyzer = new ContentAnalyzer();
  });

  after(async () => {
    if (client?.connected) {
      await client.disconnect();
    }
  });

  beforeEach(() => {
    // CRITICAL: Clear all buffers to prevent leaking between tests
    client.clearAllBuffers(); // Recommended - comprehensive protection
  });

  describe('Protocol Compliance and Basic Functionality', () => {
    test('should retrieve valid cartridge creation guide', async () => {
      const result = await client.callTool('get_best_practice_guide', {
        guideName: 'cartridge_creation'
      });

      const guideData = assertGuideContent(result, 'cartridge');
      assert.ok(guideData.title.includes('Cartridge'), 'Should be cartridge guide');
      assert.ok(guideData.content.includes('Core Principles'), 'Should have core principles');
    });

    test('should retrieve security best practices guide', async () => {
      const result = await client.callTool('get_best_practice_guide', {
        guideName: 'security'
      });

      const guideData = assertGuideContent(result, 'security');
      assert.ok(guideData.title.toLowerCase().includes('secure') || 
        guideData.title.toLowerCase().includes('security'), 'Should be security guide');
      assert.ok(guideData.content.includes('CSRF') || guideData.content.includes('XSS') || 
        guideData.content.includes('Security'), 'Should contain security concepts');
    });

    test('should retrieve SFRA client-side JavaScript guide', async () => {
      const result = await client.callTool('get_best_practice_guide', {
        guideName: 'sfra_client_side_js'
      });

      const guideData = assertGuideContent(result);
      assert.ok(guideData.title.toLowerCase().includes('client-side javascript') ||
        guideData.description.toLowerCase().includes('client-side javascript'),
        'Guide should reference client-side JavaScript in title or description');
      assert.ok(/ajax|assets\.js|debounce|event delegation/i.test(guideData.content),
        'Guide content should include client-side patterns like AJAX or assets.js');
    });

    test('should retrieve SFRA SCSS guide', async () => {
      const result = await client.callTool('get_best_practice_guide', {
        guideName: 'sfra_scss'
      });

      const guideData = assertGuideContent(result);
      assert.ok(guideData.title.toLowerCase().includes('scss') ||
        guideData.description.toLowerCase().includes('scss'),
        'Guide should reference SCSS in title or description');
      assert.ok(/@import|_variables\.scss|mixins|sass/i.test(guideData.content),
        'Guide content should highlight SCSS override patterns and mixins');
    });

    test('should handle invalid guide name gracefully', async () => {
      const result = await client.callTool('get_best_practice_guide', {
        guideName: 'nonexistent_guide'
      });

      assertValidMCPResponse(result);
      assert.equal(result.content[0].text, 'null', 'Should return null for invalid guide');
    });

    test('should validate required parameters', async () => {
      const result = await client.callTool('get_best_practice_guide', {});
      
      assertErrorResponse(result, 'validation');
    });

    test('should handle empty guide name', async () => {
      const result = await client.callTool('get_best_practice_guide', {
        guideName: ''
      });
      
      assertErrorResponse(result, 'validation');
    });
  });

  describe('Comprehensive Guide Validation', () => {
    const availableGuides = [
      'cartridge_creation',
      'security', 
      'performance',
      'sfra_controllers',
      'sfra_models',
      'sfra_client_side_js',
      'sfra_scss',
      'ocapi_hooks',
      'scapi_hooks',
      'scapi_custom_endpoint',
      'isml_templates',
      'job_framework',
      'localserviceregistry'
    ];

    test('should validate all available guides have proper structure', async () => {
      const guideAnalyses = new Map();

      for (const guideName of availableGuides) {
        const result = await client.callTool('get_best_practice_guide', {
          guideName
        });

        const guideData = assertGuideContent(result);
        const analysis = contentAnalyzer.analyzeGuide(guideData);
        guideAnalyses.set(guideName, analysis);

        // Structural integrity checks
        assert.ok(analysis.structuralIntegrity.isComplete, 
          `${guideName} should have complete structure`);
        assert.ok(analysis.structuralIntegrity.sectionsCount > 0, 
          `${guideName} should have sections`);
        assert.ok(analysis.structuralIntegrity.hasValidMarkdown, 
          `${guideName} should have valid markdown`);

        // Content depth validation
        assert.ok(analysis.contentDepth.wordCount > 500, 
          `${guideName} should have substantial content (>500 words)`);
        assert.ok(analysis.contentDepth.depthScore >= 2, 
          `${guideName} should have good content depth`);
      }

      // Analyze overall guide quality
      const averageDepthScore = Array.from(guideAnalyses.values())
        .reduce((sum, analysis) => sum + analysis.contentDepth.depthScore, 0) / 
        guideAnalyses.size;
      
      assert.ok(averageDepthScore >= 3, 
        `Average guide depth score should be >= 3, got ${averageDepthScore}`);
    });

    test('should validate technical accuracy across guide types', async () => {
      const techGuides = ['security', 'sfra_controllers', 'sfra_client_side_js', 'sfra_scss', 'cartridge_creation'];
      
      for (const guideName of techGuides) {
        const result = await client.callTool('get_best_practice_guide', {
          guideName
        });

        const guideData = assertGuideContent(result);
        const analysis = contentAnalyzer.analyzeGuide(guideData);

        // Technical accuracy validation
        assert.ok(analysis.technicalAccuracy.accuracyScore >= 0.7, 
          `${guideName} should have high technical accuracy`);
        
        // Modern practices validation
        if (guideName === 'security') {
          assert.ok(analysis.technicalAccuracy.hasModernPractices, 
            'Security guide should reference modern practices');
          assert.ok(!analysis.technicalAccuracy.hasDeprecatedReferences ||
            analysis.technicalAccuracy.hasModernPractices,
            'Security guide should prefer modern over deprecated practices');
        }

        // Code examples validation
        if (['sfra_controllers', 'sfra_client_side_js', 'sfra_scss', 'cartridge_creation'].includes(guideName)) {
          assert.ok(analysis.codeExamples.codeBlockCount > 0, 
            `${guideName} should have code examples`);

          if (guideName === 'sfra_scss') {
            const hasScssLanguage = analysis.codeExamples.languages.includes('scss');
            const mentionsScssPatterns = /@import|\$[a-zA-Z_-]+|mixins?/i.test(guideData.content);
            assert.ok(hasScssLanguage || mentionsScssPatterns,
              'SFRA SCSS guide should showcase SCSS override patterns');
          } else {
            assert.ok(analysis.codeExamples.hasJavaScript, 
              `${guideName} should have JavaScript examples`);
          }
        }
      }
    });

    test('should validate cross-references and integration patterns', async () => {
      const result = await client.callTool('get_best_practice_guide', {
        guideName: 'cartridge_creation'
      });

      const guideData = assertGuideContent(result);
      const analysis = contentAnalyzer.analyzeGuide(guideData);

      // Cross-reference validation
      assert.ok(analysis.crossReferences.mcpReferences > 0, 
        'Cartridge guide should reference MCP tools');
      assert.ok(analysis.crossReferences.hasIntegrationWorkflow, 
        'Cartridge guide should have MCP integration workflow');
      assert.ok(analysis.crossReferences.sfccReferences > 0, 
        'Cartridge guide should reference SFCC concepts');
    });
  });

  describe('Advanced Error Handling and Edge Cases', () => {
    test('should provide detailed error categorization', async () => {
      const errorScenarios = [
        { params: {}, expectedCategory: 'validation' },
        { params: { guideName: '' }, expectedCategory: 'validation' },
        { params: { guideName: null }, expectedCategory: 'validation' },
        { params: { guideName: 123 }, expectedCategory: 'validation' }
      ];

      for (const scenario of errorScenarios) {
        const result = await client.callTool('get_best_practice_guide', scenario.params);
        
        if (result.isError) {
          const errorAnalysis = assertErrorResponse(result, scenario.expectedCategory);
          assert.ok(errorAnalysis.isActionable, 
            'Error message should be actionable');
        } else {
          // Some invalid params might return null instead of error
          assert.equal(result.content[0].text, 'null', 
            'Invalid params should return null if not error');
        }
      }
    });

    test('should handle malformed requests gracefully', async () => {
      const malformedScenarios = [
        { guideName: 'a'.repeat(1000) }, // Very long guide name
        { guideName: 'guide\nwith\nnewlines' }, // Guide name with newlines
        { guideName: 'guide with spaces' }, // Guide name with spaces
        { extraParam: 'unexpected', guideName: 'security' } // Extra parameters
      ];

      for (const params of malformedScenarios) {
        const result = await client.callTool('get_best_practice_guide', params);
        
        // Should either return valid response or informative error
        if (result.isError) {
          assertErrorResponse(result);
        } else {
          // If it succeeds or returns null, that's also acceptable
          assertValidMCPResponse(result);
        }
      }
    });

    test('should maintain consistency across error conditions', async () => {
      const errorConditions = [
        { guideName: 'invalid1' },
        { guideName: 'invalid2' },
        { guideName: 'nonexistent' }
      ];

      const errorResponses = [];
      for (const params of errorConditions) {
        const result = await client.callTool('get_best_practice_guide', params);
        errorResponses.push(result);
      }

      // All invalid guide names should behave consistently
      const responseTypes = errorResponses.map(r => 
        r.isError ? 'error' : (r.content[0].text === 'null' ? 'null' : 'unexpected')
      );
      
      const uniqueResponseTypes = [...new Set(responseTypes)];
      assert.equal(uniqueResponseTypes.length, 1, 
        'All invalid guide names should have consistent response type');
    });
  });

  describe('Multi-Step Integration Workflows', () => {
    test('should support guide discovery to detailed retrieval workflow', async () => {
      // Step 1: Discover available guides
      const availableResult = await client.callTool('get_available_best_practice_guides', {});
      assertValidMCPResponse(availableResult);
      
      const availableGuides = JSON.parse(availableResult.content[0].text);
      assert.ok(Array.isArray(availableGuides), 'Should return guides array');
      assert.ok(availableGuides.length > 0, 'Should have available guides');

      // Step 2: Retrieve detailed guide for each discovered guide
      for (const guide of availableGuides.slice(0, 3)) { // Test first 3 for performance
        const detailResult = await client.callTool('get_best_practice_guide', {
          guideName: guide.name
        });

        const guideData = assertGuideContent(detailResult);
        
        // Validate consistency between discovery and detailed retrieval
        // Note: Descriptions may be slightly different between discovery and detailed view
        assert.ok(guideData.description.toLowerCase().includes('best practices') ||
          guideData.description.toLowerCase().includes('guide') ||
          guide.description.toLowerCase().includes('best practices'), 
          'Guide descriptions should be conceptually consistent');
      }
    });

    test('should support cross-guide relationship analysis', async () => {
      // Get related guides
      const cartridgeResult = await client.callTool('get_best_practice_guide', {
        guideName: 'cartridge_creation'
      });
      const securityResult = await client.callTool('get_best_practice_guide', {
        guideName: 'security'
      });
      const sfraResult = await client.callTool('get_best_practice_guide', {
        guideName: 'sfra_controllers'
      });

      const cartridgeGuide = assertGuideContent(cartridgeResult);
      const securityGuide = assertGuideContent(securityResult);
      const sfraGuide = assertGuideContent(sfraResult);

      // Analyze cross-references between guides
      const cartridgeAnalysis = contentAnalyzer.analyzeGuide(cartridgeGuide);
      const securityAnalysis = contentAnalyzer.analyzeGuide(securityGuide);
      const sfraAnalysis = contentAnalyzer.analyzeGuide(sfraGuide);

      // Validate that guides appropriately reference each other
      assert.ok(cartridgeAnalysis.crossReferences.mcpReferences > 0, 
        'Cartridge guide should reference other MCP tools');
      
      // Security guide should have practical implementation focus
      assert.ok(securityAnalysis.codeExamples.codeBlockCount > 0, 
        'Security guide should have code examples');
      
      // SFRA guide should reference server concepts
      assert.ok(sfraAnalysis.technicalAccuracy.hasModernPractices, 
        'SFRA guide should reference modern practices');
    });

    test('should validate comprehensive development workflow coverage', async () => {
      // Simulate a complete development workflow
      const workflowGuides = [
        'cartridge_creation',        // Project setup
        'sfra_controllers',          // Server-side implementation
        'sfra_client_side_js',       // Client-side enhancements
        'sfra_scss',                 // Styling and theming overrides
        'security',                  // Security review
        'performance'                // Optimization
      ];

      const workflowAnalysis = new Map();

      for (const guideName of workflowGuides) {
        const result = await client.callTool('get_best_practice_guide', {
          guideName
        });

        const guideData = assertGuideContent(result);
        const analysis = contentAnalyzer.analyzeGuide(guideData);
        workflowAnalysis.set(guideName, analysis);
      }

      // Validate workflow completeness
      const totalCodeExamples = Array.from(workflowAnalysis.values())
        .reduce((sum, analysis) => sum + analysis.codeExamples.codeBlockCount, 0);
      
      assert.ok(totalCodeExamples >= 10, 
        'Workflow guides should provide substantial code examples');

      const totalCrossReferences = Array.from(workflowAnalysis.values())
        .reduce((sum, analysis) => sum + analysis.crossReferences.totalReferences, 0);
      
      assert.ok(totalCrossReferences >= 20, 
        'Workflow guides should have extensive cross-references');

      // Validate that each workflow stage has appropriate depth
      workflowAnalysis.forEach((analysis, guideName) => {
        assert.ok(analysis.contentDepth.depthScore >= 3, 
          `${guideName} should have sufficient depth for workflow stage`);
      });
    });
  });

  describe('Content Quality and Accessibility', () => {
    test('should validate readability across all guides', async () => {
  const testGuides = ['cartridge_creation', 'security', 'sfra_controllers', 'sfra_client_side_js', 'sfra_scss'];
      
      for (const guideName of testGuides) {
        const result = await client.callTool('get_best_practice_guide', {
          guideName
        });

        const guideData = assertGuideContent(result);
        const analysis = contentAnalyzer.analyzeGuide(guideData);

        // Readability validation
        assert.ok(analysis.readabilityMetrics.avgWordsPerSentence < 25, 
          `${guideName} should have readable sentence length`);
        assert.ok(analysis.readabilityMetrics.readabilityScore > 30, 
          `${guideName} should have acceptable readability score`);
        
        // Content structure validation
        assert.ok(analysis.contentDepth.headingCount >= 5, 
          `${guideName} should have good content organization`);
        assert.ok(analysis.contentDepth.averageWordsPerParagraph < 200, 
          `${guideName} should have digestible paragraph sizes`);
      }
    });

    test('should ensure comprehensive coverage of technical topics', async () => {
      const technicalGuides = {
        'security': ['CSRF', 'XSS', 'authentication', 'encryption'],
        'performance': ['performance', 'optimization'], // Simplified expectations
        'sfra_controllers': ['server.get', 'middleware', 'ISML'],
        'sfra_client_side_js': ['ajax', 'assets.js', 'debounce', 'validation'],
        'sfra_scss': ['@import', '_variables.scss', 'mixins', 'scss']
      };

      for (const [guideName, expectedTopics] of Object.entries(technicalGuides)) {
        const result = await client.callTool('get_best_practice_guide', {
          guideName
        });

        const guideData = assertGuideContent(result);
        const content = guideData.content.toLowerCase();

        const coveredTopics = expectedTopics.filter(topic => 
          content.includes(topic.toLowerCase())
        );

        const coverageRatio = coveredTopics.length / expectedTopics.length;
        assert.ok(coverageRatio >= 0.7, 
          `${guideName} should cover at least 70% of expected topics, covered ${coverageRatio * 100}%`);
      }
    });
  });
});

```

--------------------------------------------------------------------------------
/.github/instructions/mcp-node-tests.instructions.md:
--------------------------------------------------------------------------------

```markdown
---
applyTo: "**/*.programmatic.test.js"
---
# MCP Aegis - Programmatic Testing Guide for AI Agents

**Target Audience**: AI coding assistants generating JavaScript/TypeScript programmatic t# Debugging and monitoring
const stderr = client.getStderr();      // Get captured stderr
client.clearStderr();                   // Clear stderr buffer (REQUIRED in beforeEach!)

**⚠️ CRITICAL**: Always include `client.clearStderr()` in your `beforeEach()` hook to prevent stderr from one test affecting the next test. This is a common source of test flakiness. files for Model Context Protocol servers.

## Overview

**Programmatic Testing** provides JavaScript/TypeScript API for complex MCP server testing scenarios that require dynamic validation logic, multi-step workflows, and integration with existing test suites. **For basic functional testing, the YAML-based testing approach (see `../yaml/AGENTS.md`) is more than sufficient and recommended.**

### When to Use Programmatic vs YAML Testing

**Use YAML Testing for:**
- ✅ Basic functional validation (tool discovery, parameter validation, response structure)
- ✅ Standard error handling scenarios
- ✅ Simple input/output verification
- ✅ CI/CD pipeline testing (more reliable across environments)
- ✅ Quick test development and maintenance

**Use Programmatic Testing for:**
- Complex business logic validation requiring code execution
- Multi-step workflows with state management
- Dynamic test case generation based on server configuration
- Integration with existing JavaScript/TypeScript test suites
- Advanced error recovery and resilience testing

### 📚 Key Resources
- **[Programmatic Testing Documentation](https://aegis.rhino-inquisitor.com/programmatic-testing.html)** - Complete guide
- **[API Reference](https://aegis.rhino-inquisitor.com/api-reference.html)** - All methods and properties
- **[Examples Directory](../../examples/)** - Real-world programmatic test files
- **[YAML Testing Guide](../yaml/AGENTS.md)** - Recommended for basic testing scenarios

## Quick Setup

### 1. Installation and Initialization
```bash
# Install in project
npm install --save-dev mcp-aegis
# OR 
npx mcp-aegis init
```

### 2. Configuration File
Always create `aegis.config.json` first:

```json
{
  "name": "My MCP Server",
  "command": "node",
  "args": ["./server.js"],
  "startupTimeout": 5000,
  "env": {
    "NODE_ENV": "test"
  }
}
```

### 3. Basic Programmatic Test Structure
File naming convention: `*.programmatic.test.js`

```javascript
import { test, describe, before, after, beforeEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { connect } from 'mcp-aegis';

describe('[SERVER_NAME] Programmatic Tests', () => {
  let client;

  before(async () => {
    client = await connect('./aegis.config.json');
  });

  after(async () => {
    if (client?.connected) {
      await client.disconnect();
    }
  });

  beforeEach(() => {
    // CRITICAL: Clear all buffers to prevent leaking into next tests
    client.clearAllBuffers(); // Recommended - comprehensive protection
    // OR: client.clearStderr(); // Minimum - stderr only
  });

  test('should list available tools', async () => {
    const tools = await client.listTools();
    assert.ok(Array.isArray(tools), 'Tools should be array');
    assert.ok(tools.length > 0, 'Should have at least one tool');
  });

  test('should execute tool successfully', async () => {
    const result = await client.callTool('[TOOL_NAME]', { param: 'value' });
    assert.ok(result.content, 'Should return content');
    assert.equal(result.isError, false, 'Should not be error');
  });
});
```

## Quick Debugging with Query Command

Before writing comprehensive programmatic tests, use the `query` command to rapidly test your server:

```bash
# List all available tools
aegis query --config aegis.config.json

# Test specific tool with arguments
aegis query read_file '{"path": "test.txt"}' --config aegis.config.json

# Get JSON output for inspection
aegis query calculator '{"operation": "add", "a": 5, "b": 3}' --config aegis.config.json --json
```

**Benefits for programmatic testing workflow**:
- **Rapid prototyping**: Verify server behavior before writing test code
- **API exploration**: Discover tool signatures and response formats
- **Debug assistance**: Inspect actual responses to design assertions
- **Development speed**: Test changes instantly without rebuilding test suite

**Integration with programmatic tests**:
```javascript
// Use query command findings to create targeted tests
test('should handle file reading as discovered via query', async () => {
  // Based on: aegis query read_file '{"path": "test.txt"}'
  const result = await client.callTool('read_file', { path: 'test.txt' });
  
  // Query command showed this response structure:
  assert.ok(result.content);
  assert.equal(result.content[0].type, 'text');
  assert.ok(result.content[0].text.includes('expected content'));
});
```

## API Reference

### Main Entry Points
```javascript
import { createClient, connect } from 'mcp-aegis';

// Option 1: Create client (not connected)
const client = await createClient('./aegis.config.json');
await client.connect();

// Option 2: Create and auto-connect
const connectedClient = await connect('./aegis.config.json');

// Option 3: Inline configuration
const client = await connect({
  name: 'My Server',
  command: 'node',
  args: ['./server.js'],
  cwd: './server-directory',
  startupTimeout: 5000
});
```

### Core Methods
```javascript
// Server lifecycle
await client.connect();                 // Start server + MCP handshake
await client.disconnect();              // Graceful shutdown
const isConnected = client.connected;   // Connection status

// Tool operations
const tools = await client.listTools();                    // Get available tools
const result = await client.callTool(name, arguments);     // Execute tool
const response = await client.sendMessage(jsonRpcMessage); // Raw JSON-RPC

// Debugging and monitoring
const stderr = client.getStderr();      // Get captured stderr
client.clearStderr();                   // Clear stderr buffer
```

### Error Handling
```javascript
try {
  const result = await client.callTool('nonexistent_tool', {});
} catch (error) {
  assert.ok(error.message.includes('Failed to call tool'));
  // Error details available in error object
}

// Check for execution errors (not exceptions)
const result = await client.callTool('tool_name', { invalid: 'param' });
if (result.isError) {
  assert.ok(result.content[0].text.includes('error message'));
}
```

## Critical: Preventing Test Interference

### Buffer Leaking Prevention
**The most common source of flaky programmatic tests is buffer leaking between tests.** When one test generates output (stderr, partial stdout messages) and doesn't clear it, subsequent tests may see the output from previous tests, causing unexpected failures.

#### Always Include beforeEach Hook
```javascript
beforeEach(() => {
  // RECOMMENDED: Clear all buffers to prevent any leaking
  client.clearAllBuffers();
  
  // OR minimum: Clear only stderr (less comprehensive)
  // client.clearStderr();
});
```

#### Buffer Bleeding Sources
- **Stderr buffer**: Error messages and debug output
- **Stdout buffer**: Partial JSON messages from previous requests  
- **Ready state**: Server readiness flag not reset
- **Pending reads**: Lingering message handlers

**Best Practice**: Use `client.clearAllBuffers()` instead of just `clearStderr()` for comprehensive protection.

#### Common Anti-Patterns to Avoid
```javascript
// ❌ WRONG - Missing beforeEach entirely
describe('My Tests', () => {
  let client;
  
  before(async () => {
    client = await connect('./config.json');
  });
  
  // Missing beforeEach - tests will leak buffers!
  
  test('first test', async () => {
    const result = await client.callTool('tool', {});
    // This test might generate stderr or leave stdout buffer data
  });
  
  test('second test', async () => {
    // This test might see output from first test!
    assert.equal(client.getStderr(), ''); // Will fail if first test had stderr
  });
});

// ✅ CORRECT - Include beforeEach with clearStderr
describe('My Tests', () => {
  let client;
  
  before(async () => {
    client = await connect('./config.json');
  });
  
  beforeEach(() => {
    client.clearAllBuffers(); // Prevents all buffer leaking between tests
  });
  
  test('first test', async () => {
    const result = await client.callTool('tool', {});
    // Any stderr is isolated to this test
  });
  
  test('second test', async () => {
    // Clean slate - no stderr from previous tests
    assert.equal(client.getStderr(), ''); // Will pass
  });
});
```

#### Debugging Stderr Issues
If you're experiencing flaky test failures related to unexpected stderr content:

1. **Add clearStderr() to beforeEach** - Most common fix
2. **Check test isolation** - Ensure each test starts with clean state  
3. **Debug stderr content** - Log `client.getStderr()` to see what's leaking
4. **Use afterEach cleanup** - Optional additional cleanup

```javascript
beforeEach(() => {
  client.clearStderr();
});

afterEach(() => {
  // Optional: Debug what stderr was generated
  const stderr = client.getStderr();
  if (stderr) {
    console.log('Test generated stderr:', stderr);
  }
});
```

## Testing Patterns

### 1. Tool Discovery and Validation
```javascript
describe('Tool Discovery', () => {
  test('should list all expected tools', async () => {
    const tools = await client.listTools();
    
    assert.equal(tools.length, 4, 'Should have 4 tools');
    
    const toolNames = tools.map(tool => tool.name);
    assert.ok(toolNames.includes('calculator'));
    assert.ok(toolNames.includes('text_processor'));
    assert.ok(toolNames.includes('data_validator'));
  });

  test('should validate tool schemas', async () => {
    const tools = await client.listTools();
    
    tools.forEach(tool => {
      assert.ok(tool.name, 'Tool should have name');
      assert.ok(tool.description, 'Tool should have description');
      assert.ok(tool.inputSchema, 'Tool should have input schema');
      assert.equal(tool.inputSchema.type, 'object');
    });
  });
});
```

### 2. Tool Execution Testing
```javascript
describe('Calculator Tool', () => {
  test('should perform basic arithmetic', async () => {
    const result = await client.callTool('calculator', {
      operation: 'add',
      a: 15,
      b: 27
    });

    assert.equal(result.isError, false);
    assert.equal(result.content.length, 1);
    assert.equal(result.content[0].type, 'text');
    assert.equal(result.content[0].text, 'Result: 42');
  });

  test('should handle division by zero', async () => {
    const result = await client.callTool('calculator', {
      operation: 'divide',
      a: 10,
      b: 0
    });

    assert.equal(result.isError, true);
    assert.ok(result.content[0].text.includes('division by zero'));
  });

  test('should validate required parameters', async () => {
    const result = await client.callTool('calculator', {
      operation: 'add'
      // Missing a and b parameters
    });

    assert.equal(result.isError, true);
    assert.ok(result.content[0].text.includes('required'));
  });
});
```

### 3. Multi-Step Workflows
```javascript
describe('Multi-Step Agent Workflows', () => {
  test('should support complex decision chains', async () => {
    // Step 1: Search for information
    const searchResult = await client.callTool('search_knowledge', {
      query: 'customer support best practices'
    });
    assert.equal(searchResult.isError, false);
    
    // Step 2: Analyze findings
    const analysisResult = await client.callTool('analyze_content', {
      content: searchResult.content[0].text,
      focus: 'actionable recommendations'
    });
    assert.equal(analysisResult.isError, false);
    
    // Step 3: Generate summary based on analysis
    const summaryResult = await client.callTool('generate_summary', {
      source_data: analysisResult.content[0].text,
      format: 'executive_summary'
    });
    assert.equal(summaryResult.isError, false);
    assert.ok(summaryResult.content[0].text.includes('Executive Summary'));
  });

  test('should maintain context across tool calls', async () => {
    // Initialize conversation context
    const initResult = await client.callTool('conversation_manager', {
      action: 'initialize',
      user_id: 'test_user_123'
    });
    assert.equal(initResult.isError, false);
    
    const sessionId = extractSessionId(initResult.content[0].text);
    
    // Make context-dependent call
    const contextResult = await client.callTool('conversation_manager', {
      action: 'recall',
      user_id: 'test_user_123',
      session_id: sessionId
    });
    assert.equal(contextResult.isError, false);
    assert.ok(contextResult.content[0].text.includes('session found'));
  });
});
```

### 4. Error Recovery and Resilience
```javascript
describe('Error Recovery', () => {
  test('should handle failures gracefully', async () => {
    // Test normal operation
    const normalResult = await client.callTool('external_api_call', {
      endpoint: 'users',
      action: 'list'
    });
    assert.equal(normalResult.isError, false);
    
    // Test failure scenario - should not throw
    const failureResult = await client.callTool('external_api_call', {
      endpoint: 'invalid_endpoint',
      action: 'list'
    });
    assert.equal(failureResult.isError, true);
    assert.ok(failureResult.content[0].text.includes('not found'));
    
    // Test recovery - should work again
    const recoveryResult = await client.callTool('external_api_call', {
      endpoint: 'users',
      action: 'list'
    });
    assert.equal(recoveryResult.isError, false);
  });

  test('should handle server restart scenarios', async () => {
    // Verify initial connection
    const tools = await client.listTools();
    assert.ok(tools.length > 0);
    
    // Simulate server issue by calling invalid method
    try {
      await client.sendMessage({
        jsonrpc: '2.0',
        id: 'test-1',
        method: 'invalid_method',
        params: {}
      });
    } catch (error) {
      // Expected error
    }
    
    // Verify connection still works
    const toolsAfter = await client.listTools();
    assert.ok(toolsAfter.length > 0);
  });
});
```

### ⚠️ Critical: Avoid Concurrent Requests

**Never use `Promise.all()` or concurrent requests** with MCP Aegis's programmatic API. MCP communication uses a single stdio process with shared message handlers and buffers. Concurrent requests can cause:

- **Buffer conflicts**: Multiple requests writing to the same stdout/stderr streams
- **Message handler interference**: JSON-RPC messages getting mixed or corrupted  
- **Race conditions**: Responses arriving out of order or getting lost
- **Unpredictable test failures**: Flaky tests that pass/fail randomly

```javascript
// ❌ NEVER DO THIS - Causes buffer/message handler conflicts
const promises = tools.map(tool => client.callTool(tool.name, {}));
const results = await Promise.all(promises); // WILL CAUSE ISSUES!

// ✅ ALWAYS DO THIS - Sequential requests work reliably
const results = [];
for (const tool of tools) {
  const result = await client.callTool(tool.name, {});
  results.push(result);
}
```

### 5. Sequential Request Testing
```javascript
describe('Sequential Request Testing', () => {
  test('should handle sequential requests correctly', async () => {
    const results = [];
    
    // Execute requests sequentially to avoid buffer/message handler conflicts
    for (let i = 0; i < 10; i++) {
      const result = await client.callTool('sequential_operation', { id: i });
      results.push(result);
    }
    
    results.forEach((result, i) => {
      assert.equal(result.isError, false, `Request ${i} should succeed`);
      assert.ok(result.content[0].text.includes(`id: ${i}`));
    });
  });

  test('should handle large payload processing', async () => {
    const largeText = 'x'.repeat(50000); // 50KB text
    
    const result = await client.callTool('text_processor', {
      text: largeText,
      operation: 'word_count'
    });
    
    assert.equal(result.isError, false);
    assert.ok(result.content[0].text.includes('50000'));
  });
});
```

### ⚠️ Performance Testing Not Recommended

**Performance testing is not recommended in programmatic tests** due to CI environment variability. CI environments have:
- Highly variable resource allocation and sharing
- Unpredictable I/O performance and network latency  
- JIT compilation delays and garbage collection interference
- Container and process initialization overhead

**Use dedicated performance testing tools instead:**
- Load testing frameworks (Artillery, k6, Apache JMeter)
- APM tools (New Relic, DataDog, AppDynamics)
- Custom monitoring with controlled environments
- Synthetic monitoring services

**If you must include timing validation**, use extremely lenient thresholds (5+ seconds) and focus on detecting major regressions rather than precise performance requirements.

### 6. Dynamic Validation Logic
```javascript
describe('Dynamic Validation', () => {
  test('should validate business rules dynamically', async () => {
    const tools = await client.listTools();
    
    // Dynamic validation based on actual tools
    const calculatorTool = tools.find(t => t.name === 'calculator');
    if (calculatorTool) {
      const supportedOps = calculatorTool.inputSchema.properties.operation.enum;
      
      for (const operation of supportedOps) {
        const result = await client.callTool('calculator', {
          operation,
          a: 10,
          b: 5
        });
        assert.equal(result.isError, false, `Operation ${operation} should work`);
      }
    }
  });

  test('should generate test cases from schema', async () => {
    const tools = await client.listTools();
    
    for (const tool of tools) {
      const schema = tool.inputSchema;
      const testCases = generateTestCases(schema);
      
      for (const testCase of testCases) {
        const result = await client.callTool(tool.name, testCase.input);
        
        if (testCase.shouldSucceed) {
          assert.equal(result.isError, false, 
            `${tool.name} should succeed with ${JSON.stringify(testCase.input)}`);
        } else {
          assert.equal(result.isError, true,
            `${tool.name} should fail with ${JSON.stringify(testCase.input)}`);
        }
      }
    }
  });
});

function generateTestCases(schema) {
  // Generate test cases based on JSON schema
  const testCases = [];
  
  // Valid case with all required properties
  const validCase = {};
  for (const [prop, propSchema] of Object.entries(schema.properties || {})) {
    if (propSchema.type === 'string') {
      validCase[prop] = 'test_value';
    } else if (propSchema.type === 'number') {
      validCase[prop] = 42;
    }
  }
  testCases.push({ input: validCase, shouldSucceed: true });
  
  // Invalid case missing required properties
  if (schema.required?.length > 0) {
    testCases.push({ input: {}, shouldSucceed: false });
  }
  
  return testCases;
}
```

## Advanced Testing Scenarios

### State Management Testing
```javascript
describe('State Management', () => {
  test('should maintain state across multiple calls', async () => {
    // Initialize state
    await client.callTool('state_manager', {
      action: 'set',
      key: 'test_key',
      value: 'test_value'
    });
    
    // Verify state persists
    const result = await client.callTool('state_manager', {
      action: 'get',
      key: 'test_key'
    });
    
    assert.equal(result.content[0].text, 'test_value');
  });
});
```

### Integration Testing with External Services
```javascript
describe('External Service Integration', () => {
  test('should handle API dependencies', async () => {
    // Mock external service responses if needed
    const result = await client.callTool('api_client', {
      endpoint: 'https://jsonplaceholder.typicode.com/posts/1',
      method: 'GET'
    });
    
    assert.equal(result.isError, false);
    const responseData = JSON.parse(result.content[0].text);
    assert.ok(responseData.id);
    assert.ok(responseData.title);
  });
});
```

### Custom Assertion Helpers
```javascript
// Helper functions for common assertions
function assertValidMCPResponse(result) {
  assert.ok(result.content, 'Should have content');
  assert.ok(Array.isArray(result.content), 'Content should be array');
  assert.equal(typeof result.isError, 'boolean', 'isError should be boolean');
}

function assertTextContent(result, expectedSubstring) {
  assertValidMCPResponse(result);
  assert.equal(result.content[0].type, 'text');
  assert.ok(result.content[0].text.includes(expectedSubstring));
}

function assertToolSchema(tool) {
  assert.ok(tool.name, 'Tool should have name');
  assert.ok(tool.description, 'Tool should have description');
  assert.ok(tool.inputSchema, 'Tool should have schema');
  assert.equal(tool.inputSchema.type, 'object');
}

// Usage
test('should return valid calculation result', async () => {
  const result = await client.callTool('calculator', { operation: 'add', a: 2, b: 3 });
  assertTextContent(result, 'Result: 5');
});
```

## Framework Integration

### Jest Integration
```javascript
// jest.config.js
module.exports = {
  testEnvironment: 'node',
  setupFilesAfterEnv: ['<rootDir>/test/setup.js'],
  testMatch: ['**/*.programmatic.test.js']
};

// test/setup.js
global.mcpClient = null;

beforeAll(async () => {
  const { connect } = require('mcp-aegis');
  global.mcpClient = await connect('./aegis.config.json');
});

afterAll(async () => {
  if (global.mcpClient) {
    await global.mcpClient.disconnect();
  }
});
```

### Mocha Integration
```javascript
// test/helpers/mcp-setup.js
const { connect } = require('mcp-aegis');

let client;

exports.mochaHooks = {
  beforeAll: async () => {
    client = await connect('./aegis.config.json');
  },
  afterAll: async () => {
    if (client) {
      await client.disconnect();
    }
  }
};

exports.getClient = () => client;
```

### TypeScript Support
```typescript
import { test, describe, before, after } from 'node:test';
import { strict as assert } from 'node:assert';
import { connect, MCPClient } from 'mcp-aegis';

describe('TypeScript MCP Tests', () => {
  let client: MCPClient;

  before(async () => {
    client = await connect('./aegis.config.json');
  });

  after(async () => {
    await client.disconnect();
  });

  test('should have typed responses', async () => {
    const tools = await client.listTools();
    
    // TypeScript provides full type safety
    assert.ok(Array.isArray(tools));
    tools.forEach(tool => {
      assert.ok(typeof tool.name === 'string');
      assert.ok(typeof tool.description === 'string');
      assert.ok(typeof tool.inputSchema === 'object');
    });
  });
});
```

## Best Practices for AI Agents

### 1. Test Structure Generation
```javascript
// Template for generating comprehensive test suites
function generateTestSuite(serverConfig, toolList) {
  return `
describe('${serverConfig.name} Programmatic Tests', () => {
  let client;

  before(async () => {
    client = await connect('${serverConfig.configPath}');
  });

  after(async () => {
    if (client?.connected) {
      await client.disconnect();
    }
  });

  beforeEach(() => {
    client.clearStderr();
  });

  describe('Protocol Compliance', () => {
    test('should complete MCP handshake', async () => {
      assert.ok(client.connected, 'Client should be connected');
    });

    test('should list available tools', async () => {
      const tools = await client.listTools();
      assert.ok(Array.isArray(tools));
      assert.equal(tools.length, ${toolList.length});
    });
  });

  ${toolList.map(tool => generateToolTests(tool)).join('\n\n')}
});`;
}

function generateToolTests(tool) {
  return `
  describe('${tool.name} Tool', () => {
    test('should execute successfully with valid parameters', async () => {
      const result = await client.callTool('${tool.name}', ${JSON.stringify(tool.validParams)});
      assert.equal(result.isError, false);
      assertValidMCPResponse(result);
    });

    test('should handle invalid parameters gracefully', async () => {
      const result = await client.callTool('${tool.name}', {});
      assert.equal(result.isError, true);
      assertTextContent(result, 'required');
    });
  });`;
}
```

### 2. Configuration Management
```javascript
// Detect server configuration from project structure
function detectServerConfig(projectPath) {
  const packageJson = readPackageJson(projectPath);
  
  return {
    name: packageJson.name || 'MCP Server',
    command: detectRuntime(packageJson.engines),
    args: [detectServerFile(projectPath)],
    cwd: projectPath,
    startupTimeout: 5000,
    env: {
      NODE_ENV: 'test',
      ...(packageJson.mcpConfig?.testEnv || {})
    }
  };
}

function detectRuntime(engines = {}) {
  if (engines.node) return 'node';
  if (engines.python) return 'python';
  return 'node'; // default
}
```

### 3. Error Pattern Recognition
```javascript
// Common error patterns to test for
const errorPatterns = [
  { type: 'validation', keywords: ['required', 'invalid', 'missing'] },
  { type: 'not_found', keywords: ['not found', 'does not exist'] },
  { type: 'permission', keywords: ['permission', 'unauthorized', 'forbidden'] },
  { type: 'network', keywords: ['connection', 'timeout', 'unreachable'] }
];

function categorizeError(errorText) {
  for (const pattern of errorPatterns) {
    if (pattern.keywords.some(keyword => 
        errorText.toLowerCase().includes(keyword))) {
      return pattern.type;
    }
  }
  return 'unknown';
}
```

### 4. Built-in Functional Monitoring
```javascript
// Focus on functional correctness rather than timing
class FunctionalMonitor {
  constructor() {
    this.results = new Map();
  }

  async validateTool(client, toolName, params) {
    const result = await client.callTool(toolName, params);
    
    const validation = {
      success: !result.isError,
      hasContent: result.content && result.content.length > 0,
      contentType: result.content?.[0]?.type,
      timestamp: new Date().toISOString()
    };
    
    if (!this.results.has(toolName)) {
      this.results.set(toolName, []);
    }
    this.results.get(toolName).push(validation);
    
    return { result, validation };
  }

  getReliabilityStats(toolName) {
    const validations = this.results.get(toolName) || [];
    const successful = validations.filter(v => v.success).length;
    
    return {
      totalCalls: validations.length,
      successRate: successful / validations.length,
      failureRate: (validations.length - successful) / validations.length,
      lastValidation: validations[validations.length - 1]?.timestamp
    };
  }
}
```

## Test Execution Commands

**CRITICAL**: Use the correct test commands for this SFCC Dev MCP project:

```bash
# Run individual MCP programmatic test (CORRECT for this project)
node --test tests/mcp/node/specific-test.programmatic.test.js

# Run all MCP programmatic tests (CORRECT for this project)  
npm run test:mcp:node

# Run all MCP tests (YAML + programmatic) (CORRECT for this project)
npm run test:mcp:all

# ❌ WRONG: Don't use npm test with individual files for MCP tests
# npm test -- tests/mcp/node/specific-test.programmatic.test.js  # This runs Jest!

# ❌ WRONG: Don't use Jest for MCP programmatic tests  
# npx jest --testMatch="**/*.programmatic.test.js"  # Jest doesn't handle MCP tests

# Watch mode for development (Node.js test runner)
node --test --watch tests/mcp/node/*.programmatic.test.js

# Jest is used for unit tests only (src/ directory)
jest base-handler.test.ts

# Complete test suite (Jest + MCP tests)
npm test
```

**Package.json Script Reference for this project:**
- `npm run test:mcp:node` → `node --test tests/mcp/node/*.programmatic.test.js`
- `npm run test:mcp:yaml` → Aegis YAML tests (docs-only mode)
- `npm run test:mcp:yaml:full` → Aegis YAML tests (full mode)
- `npm run test:mcp:all` → All MCP tests (YAML + programmatic)
- `npm test` → Jest unit tests + MCP tests

---

**Key Success Factors for Programmatic Testing:**
1. Always use proper lifecycle management (before/after hooks)
2. Clear stderr between tests for isolation
3. Use appropriate assertion libraries and helpers
4. Include comprehensive error handling tests
5. Test both success and failure scenarios
6. Implement performance monitoring for critical operations
7. Use TypeScript for better development experience
8. Integrate with existing testing frameworks and CI/CD pipelines

```

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

```typescript
import React from 'react';
import SEO from '../components/SEO';
import BreadcrumbSchema from '../components/BreadcrumbSchema';
import StructuredData from '../components/StructuredData';
import CodeBlock, { InlineCode } from '../components/CodeBlock';
import { H1, PageSubtitle, H2, H3 } from '../components/Typography';
import { SITE_DATES } from '../constants';

const DevelopmentPage: React.FC = () => {
    const developmentStructuredData = {
        "@context": "https://schema.org",
        "@type": "TechArticle",
        "headline": "Development Guide - SFCC Development MCP Server",
        "description": "Contributing to the SFCC Development MCP Server project. Learn the architecture, setup development environment, and contribute new features.",
        "author": {
            "@type": "Person",
            "name": "Thomas Theunen"
        },
        "publisher": {
            "@type": "Person",
            "name": "Thomas Theunen"
        },
        "datePublished": SITE_DATES.PUBLISHED,
        "dateModified": SITE_DATES.MODIFIED,
        "url": "https://sfcc-mcp-dev.rhino-inquisitor.com/development/",
        "about": [
          {
            "@type": "SoftwareApplication",
            "name": "SFCC Development MCP Server",
            "applicationCategory": "DeveloperApplication",
            "operatingSystem": "Node.js",
            "offers": {
              "@type": "Offer",
              "price": "0",
              "priceCurrency": "USD",
              "availability": "https://schema.org/InStock"
            }
          }
        ],
        "mainEntity": {
            "@type": "Guide",
            "name": "SFCC MCP Development Guide"
        }
    };

    return (
        <div className="max-w-6xl mx-auto px-6 py-12">
            <SEO 
                title="Development Guide"
                description="Contributing to the SFCC Development MCP Server project. Learn the architecture, setup development environment, and contribute new features."
                keywords="SFCC Development MCP Server, SFCC MCP development, TypeScript MCP server, Model Context Protocol development, SFCC tools development"
                canonical="/development/"
                ogType="article"
            />
            <BreadcrumbSchema items={[
                { name: "Home", url: "/" },
                { name: "Development", url: "/development/" }
            ]} />
            <StructuredData data={developmentStructuredData} />
            
            <H1 id="development-guide">👨‍💻 Development Guide</H1>
            <PageSubtitle>Contributing to the SFCC Development MCP Server project</PageSubtitle>

            <H2 id="getting-started">🚀 Getting Started</H2>
            
            <H3 id="prerequisites">Prerequisites</H3>
            <ul className="list-disc pl-6 space-y-1">
                <li><strong>Node.js</strong> 18 or higher</li>
                <li><strong>npm</strong> 8 or higher</li>
                <li><strong>Git</strong> for version control</li>
                <li><strong>TypeScript</strong> knowledge recommended</li>
            </ul>

            <H3 id="local-development-setup">Local Development Setup</H3>
            <CodeBlock language="bash" code={`
# Clone the repository
git clone https://github.com/taurgis/sfcc-dev-mcp.git
cd sfcc-dev-mcp

# Install dependencies
npm install

# Build TypeScript
npm run build

# Run tests
npm test

# Start in development mode
npm run dev -- --dw-json /Users/username/sfcc-project/dw.json
            `} />

            <H2 id="project-architecture">🏗️ Project Architecture</H2>
            
            <p className="text-[11px] text-gray-500 mb-4">Surface: <strong>36+ specialized tools</strong> spanning documentation, best practices, SFRA, cartridge generation, runtime logs, job logs, system & custom objects, site preferences, and code versions.</p>
            <H3 id="directory-structure">Directory Structure</H3>
            <CodeBlock language="text" code={`
sfcc-dev-mcp/
├── src/
│   ├── main.ts                    # CLI entry point
│   ├── index.ts                   # Package exports
│   ├── core/                      # Core MCP server & tool definitions
│   │   ├── server.ts              # MCP server (registers handlers, capability gating)
│   │   ├── tool-definitions.ts    # All tool schemas grouped by category
│   │   └── handlers/              # Modular tool handlers
│   │       ├── base-handler.ts
│   │       ├── docs-handler.ts
│   │       ├── best-practices-handler.ts
│   │       ├── sfra-handler.ts
│   │       ├── log-handler.ts
│   │       ├── job-log-handler.ts
│   │       ├── system-object-handler.ts
│   │       ├── code-version-handler.ts
│   │       └── cartridge-handler.ts
│   ├── clients/                   # API & domain clients (logic, not routing)
│   │   ├── base/                  # Shared HTTP + auth
│   │   │   ├── http-client.ts
│   │   │   ├── ocapi-auth-client.ts
│   │   │   └── oauth-token.ts
│   │   ├── logs/                  # Modular log system (composition)
│   │   │   ├── log-client.ts          # Orchestrator
│   │   │   ├── webdav-client-manager.ts # WebDAV auth + setup
│   │   │   ├── log-file-reader.ts     # Range / tail reads
│   │   │   ├── log-file-discovery.ts  # Listing & filtering
│   │   │   ├── log-processor.ts       # Parsing & normalization
│   │   │   ├── log-analyzer.ts        # Pattern & health analysis
│   │   │   ├── log-formatter.ts       # Output shaping
│   │   │   ├── log-constants.ts       # Central constants/config
│   │   │   └── log-types.ts           # Type definitions
│   │   ├── docs/                  # Modular documentation system
│   │   │   ├── documentation-scanner.ts
│   │   │   ├── class-content-parser.ts
│   │   │   ├── class-name-resolver.ts
│   │   │   ├── referenced-types-extractor.ts
│   │   │   └── index.ts
│   │   ├── docs-client.ts
│   │   ├── sfra-client.ts
│   │   ├── best-practices-client.ts
│   │   ├── cartridge-generation-client.ts
│   │   ├── ocapi/
│   │   │   ├── site-preferences-client.ts
│   │   │   └── system-objects-client.ts
│   │   ├── ocapi-client.ts
│   │   ├── log-client.ts              # Backwards compat wrapper
│   └── best-practices-client.ts   # (already listed above? keep once)
│   ├── services/                 # Dependency injection service layer
│   │   ├── file-system-service.ts
│   │   ├── path-service.ts
│   │   └── index.ts
│   ├── tool-configs/             # Tool grouping & category configs
│   │   ├── docs-tool-config.ts
│   │   ├── sfra-tool-config.ts
│   │   ├── best-practices-tool-config.ts
│   │   ├── log-tool-config.ts
│   │   ├── job-log-tool-config.ts
│   │   ├── system-object-tool-config.ts
│   │   ├── cartridge-tool-config.ts
│   │   └── code-version-tool-config.ts
│   ├── config/
│   │   ├── configuration-factory.ts   # Mode & capability resolution
│   │   └── dw-json-loader.ts          # Secure dw.json loading
│   ├── utils/
│   │   ├── cache.ts
│   │   ├── logger.ts
│   │   ├── path-resolver.ts
│   │   ├── query-builder.ts
│   │   ├── utils.ts
│   │   ├── validator.ts
│   └── types/
│       └── types.ts
├── tests/                        # Jest + MCP YAML + programmatic tests
│   ├── *.test.ts
│   ├── mcp/yaml/*.mcp.yml        # Declarative tool tests
│   ├── mcp/node/*.programmatic.test.js
│   └── servers/webdav/           # Mock WebDAV server fixtures
├── docs/                         # SFCC & best practices markdown sources
├── docs-site/                    # React + Vite documentation site
├── scripts/                      # Conversion & build scripts
└── ai-instructions/              # AI platform instruction sets
            `} />

            <H3 id="configuration-system">Configuration & Capability Gating (<InlineCode>src/config/</InlineCode>)</H3>
            <ul className="list-disc pl-6 space-y-1">
              <li><strong>configuration-factory.ts</strong>: Determines operating mode & derives capabilities (<InlineCode>canAccessLogs</InlineCode>, <InlineCode>canAccessJobLogs</InlineCode>, <InlineCode>canAccessOCAPI</InlineCode>, <InlineCode>canAccessSitePrefs</InlineCode>).</li>
              <li><strong>dw-json-loader.ts</strong>: Safe credential ingestion, prevents accidental misuse.</li>
              <li><strong>Capability Gating</strong>: No credentials → docs & best practice tools only; WebDAV creds → runtime + job logs; Data API creds → system & custom objects, site preferences, code versions.</li>
              <li><strong>Least Privilege</strong>: Tools requiring unavailable capabilities never registered.</li>
            </ul>

            <H3 id="adding-new-tools">Adding New Tools (Updated Flow)</H3>
            <ol className="list-decimal pl-6 space-y-2">
              <li><strong>Define schema</strong> in correct category array inside <InlineCode>tool-definitions.ts</InlineCode>.</li>
              <li><strong>Decide placement</strong> – extend existing handler or create new handler extending <InlineCode>BaseToolHandler</InlineCode>.</li>
              <li><strong>Implement logic</strong> inside a client/service (keep handler thin).</li>
              <li><strong>Register handler</strong> only if new category (update <InlineCode>registerHandlers()</InlineCode> in <InlineCode>server.ts</InlineCode>).</li>
              <li><strong>Discover response format</strong>: Run with <InlineCode>npx aegis query [tool]</InlineCode> BEFORE writing tests; capture real JSON shape.</li>
              <li><strong>Add tests</strong>: Jest unit + YAML (docs & full mode as applicable) + programmatic Node tests if complex.</li>
              <li><strong>Update docs</strong>: This page + README + copilot instructions when categories/counts change.</li>
            </ol>

            <H3 id="testing-strategy">Testing Strategy</H3>
            <p className="text-xs text-gray-500 mb-2">Programmatic MCP tests must use Node test runner (e.g. <InlineCode>node --test tests/mcp/node/your-test.programmatic.test.js</InlineCode>) — do NOT invoke via <InlineCode>npm test -- file</InlineCode> (Jest only).</p>
            <H3 id="unit-tests">Unit Tests</H3>
            <CodeBlock language="bash" code={`
# Run all tests
npm test

# Run specific test file
npm test base-http-client.test.ts

# Run with coverage
npm run test:coverage

# Watch mode for development
npm run test:watch
            `} />

            <H3 id="available-test-scripts">Available Test Scripts</H3>
            <CodeBlock language="bash" code={`
# Core testing scripts from package.json
npm test              # Run all tests with Jest
npm run test:watch    # Run tests in watch mode
npm run test:coverage # Run tests with coverage report

# Linting and code quality
npm run lint          # Check code style
npm run lint:fix      # Auto-fix linting issues
npm run lint:check    # Check with zero warnings
            `} />

            <H3 id="manual-testing">Manual Testing</H3>
            <CodeBlock language="bash" code={`
# Test with real SFCC instance (create your own test-dw.json)
npm run dev -- --dw-json /Users/username/sfcc-project/test-dw.json --debug

# Test documentation-only mode
npm run dev -- --debug
            `} />

            <H2 id="documentation-updates">📚 Documentation Updates</H2>
            
            <H3 id="updating-sfcc-documentation">Updating SFCC Documentation</H3>
            
            <p><strong>1. Add/Update Markdown Files</strong> in <InlineCode>docs/</InlineCode>:</p>
            <CodeBlock language="bash" code={`
# Add new class documentation
echo "# NewClass\\n\\nDescription..." > docs/dw_catalog/NewClass.md
            `} />

            <p><strong>2. Run Documentation Conversion:</strong></p>
            <CodeBlock language="bash" code={`
# Convert and process documentation (requires axios and cheerio)
npm run convert-docs

# Test with limited conversion
npm run convert-docs:test

# Limited conversion (5 files)
npm run convert-docs:limit
            `} />

            <p><strong>3. Test Documentation Tools:</strong></p>
            <CodeBlock language="bash" code={`
# Test documentation access with your changes
npm run dev -- --debug true
# Then use MCP client to test get_sfcc_class_info with "NewClass"
            `} />

            <H3 id="updating-github-pages">Updating Documentation Site</H3>
            <p>The documentation site (<InlineCode>docs-site/</InlineCode>) is a React + Vite app. Deployment is handled by GitHub Actions after changes are pushed to the default branch.</p>
            <ol className="list-decimal pl-6 space-y-1">
              <li><strong>Edit Content</strong>: Modify or add pages/components under <InlineCode>docs-site/</InlineCode>.</li>
              <li><strong>Local Preview</strong>:
                <CodeBlock language="bash" code={`cd docs-site
npm install
npm run dev  # Opens Vite dev server (default http://localhost:5173)
                `} />
              </li>
              <li><strong>Build (optional check)</strong>:
                <CodeBlock language="bash" code={`cd docs-site
npm run build  # Generates dist/ with static assets
                `} />
              </li>
              <li><strong>Push Changes</strong>: CI workflow publishes the built site to GitHub Pages.</li>
              <li><strong>Search Index / Sitemap</strong>: Automatically generated via build scripts (<InlineCode>generate:search-index</InlineCode>, <InlineCode>generate:sitemap</InlineCode>).</li>
            </ol>

            <H2 id="coding-standards">🎯 Coding Standards</H2>
            
            <H3 id="typescript-guidelines">TypeScript Guidelines</H3>
            <CodeBlock language="typescript" code={`
// Use explicit types
interface ToolParams {
  readonly query: string;
  readonly limit?: number;
}

// Use proper error handling
async function riskyOperation(): Promise<Result> {
  try {
    return await performOperation();
  } catch (error) {
    this.logger.error('Operation failed', { error: error.message });
    throw new OperationError('Failed to perform operation', error);
  }
}

// Use meaningful names
const searchProductsByCategory = (categoryId: string) => {
  // Implementation
};
            `} />

            <H3 id="code-organization">Code Organization</H3>
            <ul className="list-disc pl-6 space-y-1">
                <li><strong>Single Responsibility</strong>: Each class/function has one clear purpose</li>
                <li><strong>Dependency Injection</strong>: Use constructor injection for dependencies</li>
                <li><strong>Error Boundaries</strong>: Proper error handling at service boundaries</li>
                <li><strong>Logging</strong>: Comprehensive logging for debugging and monitoring</li>
            </ul>

            <H3 id="git-workflow">Git Workflow</H3>
            <CodeBlock language="bash" code={`
# Create feature branch from develop
git checkout develop
git pull origin develop
git checkout -b feature/new-tool-name

# Make atomic commits
git add src/clients/my-client.ts
git commit -m "feat: add my new tool implementation"

git add tests/my-client.test.ts  
git commit -m "test: add unit tests for my new tool"

git add docs-site/tools.md
git commit -m "docs: update tools documentation"

# Push and create PR to develop branch
git push origin feature/new-tool-name
            `} />

            <p><strong>Commit Message Convention:</strong></p>
            <ul className="list-disc pl-6 space-y-1">
                <li><InlineCode>feat:</InlineCode> - New features</li>
                <li><InlineCode>fix:</InlineCode> - Bug fixes</li>
                <li><InlineCode>docs:</InlineCode> - Documentation updates</li>
                <li><InlineCode>test:</InlineCode> - Test additions/modifications</li>
                <li><InlineCode>refactor:</InlineCode> - Code refactoring</li>
                <li><InlineCode>chore:</InlineCode> - Build process or auxiliary tool changes</li>
            </ul>

            <H2 id="testing-best-practices">🧪 Testing Best Practices</H2>
            
            <H3 id="test-structure">Test Structure</H3>
            <CodeBlock language="typescript" code={`
describe('FeatureName', () => {
  let client: MyClient;
  let mockHttpClient: jest.Mocked<HttpClient>;
  
  beforeEach(() => {
    mockHttpClient = createMockHttpClient();
    client = new MyClient(mockHttpClient, mockLogger);
  });
  
  describe('methodName', () => {
    it('should handle success case', async () => {
      // Arrange
      const input = { query: 'test' };
      const mockResponse = { data: 'mock response' };
      mockHttpClient.get.mockResolvedValue(mockResponse);
      
      // Act
      const result = await client.methodName(input);
      
      // Assert
      expect(result).toBeDefined();
      expect(mockHttpClient.get).toHaveBeenCalledWith('/expected/path');
    });
    
    it('should handle error case', async () => {
      // Arrange
      const input = { query: 'test' };
      mockHttpClient.get.mockRejectedValue(new Error('Network error'));
      
      // Act & Assert
      await expect(client.methodName(input)).rejects.toThrow('Network error');
    });
  });
});
            `} />

            <H3 id="mock-strategy">Mock Strategy</H3>
            <CodeBlock language="typescript" code={`
// Mock external dependencies using Jest
const mockLogger = {
  info: jest.fn(),
  error: jest.fn(),
  debug: jest.fn(),
  warn: jest.fn()
};

// Use factories for complex mocks
const createMockSFCCResponse = (overrides = {}) => ({
  statusCode: 200,
  headers: { 'content-type': 'application/json' },
  data: 'Mock response data',
  ...overrides
});
            `} />

            <H3 id="testing-files-available">Testing Coverage Overview</H3>
            <ul className="list-disc pl-6 space-y-1">
              <li><strong>Unit Clients</strong>: HTTP/auth, OCAPI subclients, docs, SFRA, best practices, cartridge generation.</li>
              <li><strong>Handlers</strong>: Each modular handler has focused tests (error shaping, capability filtering).</li>
              <li><strong>Log System</strong>: Discovery, reader, processor, analyzer, formatter modules.</li>
              <li><strong>Job Logs</strong>: Parsing & multi-level consolidation logic.</li>
              <li><strong>MCP Protocol Tests</strong>: YAML declarative + programmatic (Node) in <InlineCode>tests/mcp/</InlineCode>.</li>
              <li><strong>WebDAV Mock</strong>: Integration environment for log + job retrieval.</li>
            </ul>

            <H3 id="handler-architecture">Handler Architecture</H3>
            <ul className="list-disc pl-6 space-y-1">
              <li><strong>BaseToolHandler</strong>: Central timing, error normalization, logger integration.</li>
              <li><strong>Category Isolation</strong>: Each functional domain kept small & cohesive.</li>
              <li><strong>Extensibility</strong>: New feature area → new handler; minimal churn to existing code.</li>
              <li><strong>Testing Benefit</strong>: Handlers test orchestration; clients test domain logic.</li>
            </ul>

            <H3 id="services-di">Services & Dependency Injection</H3>
            <ul className="list-disc pl-6 space-y-1">
              <li><strong>FileSystemService</strong> & <strong>PathService</strong>: Abstract Node APIs for test isolation.</li>
              <li><strong>Client Composition</strong>: Pass services or mocks explicitly—no hidden globals.</li>
              <li><strong>Deterministic Tests</strong>: Avoids brittle fs/path mocking at module level.</li>
            </ul>

            <H3 id="log-architecture">Log & Job Log Architecture</H3>
            <ul className="list-disc pl-6 space-y-1">
              <li><strong>Reader</strong>: Range tail reads minimize bandwidth.</li>
              <li><strong>Processor</strong>: Normalizes raw lines → structured entries.</li>
              <li><strong>Analyzer</strong>: Pattern extraction, severity grouping, health scoring.</li>
              <li><strong>Formatter</strong>: Produces human-oriented summaries for MCP output.</li>
              <li><strong>Job Logs</strong>: Unified multi-level log files consolidated logically.</li>
            </ul>

            <H3 id="tool-configs">Tool Config Modules</H3>
            <p>Each <InlineCode>tool-configs/*.ts</InlineCode> file groups logically related tool definitions or export sets, enabling cleaner segregation and future dynamic registration strategies.</p>

            <H3 id="caching-performance">Caching & Performance</H3>
            <ul className="list-disc pl-6 space-y-1">
              <li><strong>cache.ts</strong>: In-memory response caching (documentation & static lookups).</li>
              <li><strong>log-cache.ts</strong>: Specialized transient caching for recently tailed segments.</li>
              <li><strong>Avoid Premature I/O</strong>: Lazy fetch patterns in log discovery & system objects.</li>
              <li><strong>Capability Filter</strong>: Reduces surface area → fewer accidental expensive calls.</li>
            </ul>

            <H2 id="release-process">🚀 Release Process</H2>
            
            <H3 id="version-management">Version Management</H3>
            <CodeBlock language="bash" code={`
# Update version
npm version patch  # 1.0.0 → 1.0.1
npm version minor  # 1.0.0 → 1.1.0  
npm version major  # 1.0.0 → 2.0.0

# Push tags
git push origin main --tags
            `} />

            <H3 id="release-checklist">Release Checklist</H3>
            <p><strong>1. Update Documentation</strong></p>
            <ul className="list-disc pl-6 space-y-1">
                <li>README.md tool counts & feature surface (36+ phrasing)</li>
                <li><InlineCode>ai-instructions/github-copilot/copilot-instructions.md</InlineCode> architecture updates</li>
                <li><InlineCode>.github/copilot-instructions.md</InlineCode> (sync architecture + counts)</li>
                <li>Configuration & Features pages updated if capability surface changed</li>
                <li>CHANGELOG.md entry (if present)</li>
            </ul>

            <p><strong>2. Testing</strong></p>
            <ul className="list-disc pl-6 space-y-1">
                <li>All unit tests pass (<InlineCode>npm test</InlineCode>)</li>
                <li>Linting passes (<InlineCode>npm run lint:check</InlineCode>)</li>
                <li>Manual testing with real SFCC instance</li>
                <li>Documentation-only mode validation</li>
                <li>Build succeeds (<InlineCode>npm run build</InlineCode>)</li>
            </ul>

            <p><strong>3. Build & Package</strong></p>
            <ul className="list-disc pl-6 space-y-1">
                <li>TypeScript compilation successful</li>
                <li>Package size reasonable</li>
                <li>Dependencies audit clean (<InlineCode>npm audit</InlineCode>)</li>
            </ul>

            <p><strong>4. Release</strong></p>
            <ul className="list-disc pl-6 space-y-1">
                <li>GitHub release with changelog</li>
                <li>npm publish (automated via <InlineCode>.github/workflows/publish.yml</InlineCode>)</li>
                <li>Documentation deployment (automated)</li>
            </ul>

            <H2 id="contributing-guidelines">🤝 Contributing Guidelines</H2>
            
            <H3 id="before-contributing">Before Contributing</H3>
            <ol className="list-decimal pl-6 space-y-1">
                <li><strong>Check Existing Issues</strong>: Search for existing issues or discussions</li>
                <li><strong>Discuss Large Changes</strong>: Open an issue for significant modifications</li>
                <li><strong>Follow Conventions</strong>: Adhere to established coding and commit patterns</li>
            </ol>

            <H3 id="pull-request-process">Pull Request Process</H3>
            <ol className="list-decimal pl-6 space-y-1">
                <li><strong>Fork & Branch</strong>: Create feature branch from <InlineCode>develop</InlineCode></li>
                <li><strong>Implement Changes</strong>: Follow coding standards and testing requirements</li>
                <li><strong>Update Documentation</strong>: Ensure documentation reflects changes</li>
                <li><strong>Test Thoroughly</strong>: All tests must pass (<InlineCode>npm test</InlineCode>, <InlineCode>npm run lint:check</InlineCode>)</li>
                <li><strong>Submit PR</strong>: Provide clear description and link to related issues</li>
            </ol>

            <H3 id="code-review">Code Review</H3>
            <ul className="list-disc pl-6 space-y-1">
                <li><strong>GitHub Actions</strong>: CI pipeline must pass (see <InlineCode>.github/workflows/ci.yml</InlineCode>)</li>
                <li><strong>Code Quality</strong>: ESLint and TypeScript checks must pass</li>
                <li><strong>Test Coverage</strong>: Maintain or improve test coverage</li>
                <li><strong>Documentation</strong>: Ensure user-facing changes are documented</li>
            </ul>

            <H2 id="performance-considerations">📊 Performance Considerations</H2>
            
            <H3 id="optimization-guidelines">Optimization Guidelines</H3>
            <ul className="list-disc pl-6 space-y-1">
                <li><strong>Caching Strategy</strong>: Implement intelligent caching for API responses</li>
                <li><strong>Rate Limiting</strong>: Respect SFCC API limits and implement backoff</li>
                <li><strong>Memory Management</strong>: Monitor memory usage, especially for large datasets</li>
                <li><strong>Asynchronous Operations</strong>: Use proper async/await patterns</li>
            </ul>

            <H3 id="monitoring">Monitoring</H3>
            <CodeBlock language="typescript" code={`
// Performance monitoring example
const startTime = Date.now();
try {
  const result = await performOperation();
  this.metrics.recordSuccess('operation_name', Date.now() - startTime);
  return result;
} catch (error) {
  this.metrics.recordError('operation_name', Date.now() - startTime);
  throw error;
}
            `} />

            <H2 id="security-considerations">🔒 Security Considerations</H2>
            
            <H3 id="credential-handling">Credential Handling</H3>
            <ul className="list-disc pl-6 space-y-1">
                <li><strong>No Hardcoding</strong>: Never commit credentials to repository</li>
                <li><strong>Secure Storage</strong>: Use appropriate credential storage mechanisms</li>
                <li><strong>Minimal Permissions</strong>: Request only necessary permissions</li>
                <li><strong>Rotation Support</strong>: Design for credential rotation</li>
            </ul>

            <H3 id="input-validation">Input Validation</H3>
            <CodeBlock language="typescript" code={`
// Validate all inputs using proper TypeScript types
import { ToolResponse, ValidationError } from '../types/types.js';

interface ToolParams {
  readonly query: string;
  readonly limit?: number;
}

function validateToolInput(input: unknown): ToolParams {
  if (!input || typeof input !== 'object') {
    throw new ValidationError('Input must be an object');
  }
  
  const { query, limit } = input as any;
  
  if (!query || typeof query !== 'string') {
    throw new ValidationError('Query is required and must be a string');
  }
  
  if (query.length > 1000) {
    throw new ValidationError('Query must be 1000 characters or less');
  }
  
  if (limit !== undefined && (typeof limit !== 'number' || limit < 1 || limit > 100)) {
    throw new ValidationError('Limit must be a number between 1 and 100');
  }
  
  return { query, limit };
}
            `} />

            <H2 id="next-steps">Next Steps</H2>
            <ul className="list-disc pl-6 space-y-2">
                <li>📝 <strong><a href="https://github.com/taurgis/sfcc-dev-mcp/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener noreferrer">Contributing Guidelines</a></strong> - Detailed contribution process</li>
                <li>🏗️ <strong><a href="https://github.com/taurgis/sfcc-dev-mcp/issues" target="_blank" rel="noopener noreferrer">Issues & Features</a></strong> - Report bugs or request features</li>
                <li>💬 <strong><a href="https://github.com/taurgis/sfcc-dev-mcp/discussions" target="_blank" rel="noopener noreferrer">Discussions</a></strong> - Community discussions and Q&A</li>
                <li>🚀 <strong><a href="https://github.com/taurgis/sfcc-dev-mcp/actions" target="_blank" rel="noopener noreferrer">GitHub Actions</a></strong> - View CI/CD pipeline status</li>
            </ul>
        </div>
    );
};

export default DevelopmentPage;
```
Page 31/43FirstPrevNextLast