#
tokens: 48673/50000 9/825 files (page 23/43)
lines: off (toggle) GitHub
raw markdown copy
This is page 23 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

--------------------------------------------------------------------------------
/tests/mcp/node/get-sfra-categories.docs-only.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('SFCC Dev MCP - get_sfra_categories Tool (docs-only mode)', () => {
  let client;

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

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

  beforeEach(() => {
    // Clear all buffers to prevent test interference
    client.clearAllBuffers();
  });

  describe('Basic Functionality', () => {
    test('should be available in tool list', async () => {
      const tools = await client.listTools();
      const toolNames = tools.map(tool => tool.name);
      assert.ok(toolNames.includes('get_sfra_categories'), 'get_sfra_categories should be available');
    });

    test('should have proper tool schema', async () => {
      const tools = await client.listTools();
      const tool = tools.find(t => t.name === 'get_sfra_categories');
      
      assert.ok(tool, 'Tool should exist');
      assert.equal(tool.name, 'get_sfra_categories');
      assert.ok(tool.description, 'Tool should have description');
      assert.ok(tool.description.includes('SFRA document categories'), 'Description should mention SFRA categories');
      assert.ok(tool.inputSchema, 'Tool should have input schema');
      assert.equal(tool.inputSchema.type, 'object');
    });

    test('should return valid MCP response structure', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      assert.equal(result.isError, false, 'Should not be an error');
      assert.ok(result.content, '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');
      assert.ok(result.content[0].text, 'Should have text content');
    });
  });

  describe('Response Content Validation', () => {
    test('should return valid JSON array', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const responseText = result.content[0].text;
      assert.doesNotThrow(() => {
        const parsed = JSON.parse(responseText);
        assert.ok(Array.isArray(parsed), 'Response should be a JSON array');
      }, 'Response should be valid JSON');
    });

    test('should return exactly 7 categories', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      assert.equal(categories.length, 7, 'Should have exactly 7 categories');
    });

    test('should include all expected category names', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const categoryNames = categories.map(cat => cat.category).sort();
      const expectedNames = ['core', 'customer', 'order', 'other', 'pricing', 'product', 'store'];
      
      assert.deepEqual(categoryNames, expectedNames, 'Should have all expected category names');
    });

    test('should have proper structure for each category', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      categories.forEach((category, index) => {
        assert.ok(category.category, `Category ${index} should have category field`);
        assert.ok(typeof category.category === 'string', `Category ${index} name should be string`);
        assert.ok(typeof category.count === 'number', `Category ${index} count should be number`);
        assert.ok(category.count > 0, `Category ${index} count should be positive`);
        assert.ok(category.description, `Category ${index} should have description`);
        assert.ok(typeof category.description === 'string', `Category ${index} description should be string`);
        assert.ok(category.description.length > 10, `Category ${index} description should be meaningful`);
      });
    });
  });

  describe('Specific Category Validation', () => {
    test('should have core category with expected properties', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const coreCategory = categories.find(cat => cat.category === 'core');
      
      assert.ok(coreCategory, 'Should have core category');
      assert.equal(coreCategory.count, 5, 'Core category should have 5 documents');
      assert.ok(coreCategory.description.includes('Core SFRA classes'), 'Core description should mention SFRA classes');
      assert.ok(coreCategory.description.includes('Server'), 'Core description should mention Server');
      assert.ok(coreCategory.description.includes('Request'), 'Core description should mention Request');
      assert.ok(coreCategory.description.includes('Response'), 'Core description should mention Response');
    });

    test('should have customer category with expected properties', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const customerCategory = categories.find(cat => cat.category === 'customer');
      
      assert.ok(customerCategory, 'Should have customer category');
      assert.equal(customerCategory.count, 2, 'Customer category should have 2 documents');
      assert.ok(customerCategory.description.includes('Customer account'), 'Customer description should mention account');
      assert.ok(customerCategory.description.includes('address'), 'Customer description should mention address');
    });

    test('should have order category with expected properties', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const orderCategory = categories.find(cat => cat.category === 'order');
      
      assert.ok(orderCategory, 'Should have order category');
      assert.equal(orderCategory.count, 6, 'Order category should have 6 documents');
      assert.ok(orderCategory.description.includes('Order'), 'Order description should mention Order');
      assert.ok(orderCategory.description.includes('cart'), 'Order description should mention cart');
      assert.ok(orderCategory.description.includes('billing'), 'Order description should mention billing');
      assert.ok(orderCategory.description.includes('shipping'), 'Order description should mention shipping');
    });

    test('should have product category with expected properties', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const productCategory = categories.find(cat => cat.category === 'product');
      
      assert.ok(productCategory, 'Should have product category');
      assert.equal(productCategory.count, 5, 'Product category should have 5 documents');
      assert.ok(productCategory.description.includes('Product-related'), 'Product description should mention product-related');
      assert.ok(productCategory.description.includes('models'), 'Product description should mention models');
    });

    test('should have pricing category with expected properties', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const pricingCategory = categories.find(cat => cat.category === 'pricing');
      
      assert.ok(pricingCategory, 'Should have pricing category');
      assert.equal(pricingCategory.count, 3, 'Pricing category should have 3 documents');
      assert.ok(pricingCategory.description.includes('Pricing'), 'Pricing description should mention Pricing');
      assert.ok(pricingCategory.description.includes('discount'), 'Pricing description should mention discount');
    });

    test('should have store category with expected properties', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const storeCategory = categories.find(cat => cat.category === 'store');
      
      assert.ok(storeCategory, 'Should have store category');
      assert.equal(storeCategory.count, 2, 'Store category should have 2 documents');
      assert.ok(storeCategory.description.includes('Store'), 'Store description should mention Store');
      assert.ok(storeCategory.description.includes('location'), 'Store description should mention location');
    });

    test('should have other category with expected properties', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const otherCategory = categories.find(cat => cat.category === 'other');
      
      assert.ok(otherCategory, 'Should have other category');
      assert.equal(otherCategory.count, 3, 'Other category should have 3 documents');
      assert.ok(otherCategory.description.includes('Other'), 'Other description should mention Other');
      assert.ok(otherCategory.description.includes('models'), 'Other description should mention models');
      assert.ok(otherCategory.description.includes('utilities'), 'Other description should mention utilities');
    });
  });

  describe('Total Count Validation', () => {
    test('should have total of 26 documents across all categories', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const totalCount = categories.reduce((sum, cat) => sum + cat.count, 0);
      
      assert.equal(totalCount, 26, 'Total documents across all categories should be 26');
    });

    test('should have expected count distribution', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const countMap = {};
      categories.forEach(cat => {
        countMap[cat.category] = cat.count;
      });

      assert.equal(countMap.core, 5, 'Core should have 5 documents');
      assert.equal(countMap.customer, 2, 'Customer should have 2 documents');
      assert.equal(countMap.order, 6, 'Order should have 6 documents');
      assert.equal(countMap.product, 5, 'Product should have 5 documents');
      assert.equal(countMap.pricing, 3, 'Pricing should have 3 documents');
      assert.equal(countMap.store, 2, 'Store should have 2 documents');
      assert.equal(countMap.other, 3, 'Other should have 3 documents');
    });
  });

  describe('Error Handling and Edge Cases', () => {
    test('should handle empty parameters', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      assert.equal(result.isError, false, 'Should handle empty parameters without error');
      const categories = JSON.parse(result.content[0].text);
      assert.equal(categories.length, 7, 'Should return all categories with empty parameters');
    });

    test('should ignore invalid parameters', async () => {
      const result = await client.callTool('get_sfra_categories', {
        invalid: 'param',
        another: 'value',
        numeric: 123
      });
      
      assert.equal(result.isError, false, 'Should ignore invalid parameters without error');
      const categories = JSON.parse(result.content[0].text);
      assert.equal(categories.length, 7, 'Should return all categories despite invalid parameters');
    });

    test('should handle special parameter values', async () => {
      const result = await client.callTool('get_sfra_categories', {
        null_value: null,
        empty_string: '',
        zero: 0,
        boolean: false
      });
      
      assert.equal(result.isError, false, 'Should handle special parameter values without error');
      const categories = JSON.parse(result.content[0].text);
      assert.equal(categories.length, 7, 'Should return all categories with special parameter values');
    });
  });


  describe('Content Quality and Consistency', () => {
    test('should have consistent JSON formatting', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const responseText = result.content[0].text;
      
      // Should be properly formatted JSON
      assert.ok(responseText.startsWith('['), 'Response should start with array bracket');
      assert.ok(responseText.endsWith(']'), 'Response should end with array bracket');
      assert.ok(responseText.includes('{'), 'Response should contain objects');
      assert.ok(responseText.includes('}'), 'Response should contain complete objects');
      
      // Should have proper field formatting
      assert.ok(responseText.includes('"category":'), 'Should have category fields');
      assert.ok(responseText.includes('"count":'), 'Should have count fields');
      assert.ok(responseText.includes('"description":'), 'Should have description fields');
    });

    test('should have alphabetically sorted categories', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const categoryNames = categories.map(cat => cat.category);
      const sortedNames = [...categoryNames].sort();
      
      assert.deepEqual(categoryNames, sortedNames, 'Categories should be alphabetically sorted');
    });

    test('should have meaningful descriptions for all categories', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      
      categories.forEach(category => {
        assert.ok(category.description.length >= 20, `Description for ${category.category} should be substantial`);
        assert.ok(category.description.includes('models') || category.description.includes('classes') || category.description.includes('utilities'), 
          `Description for ${category.category} should mention relevant concepts`);
        assert.ok(/^[A-Z]/.test(category.description), `Description for ${category.category} should start with capital letter`);
        assert.ok(!category.description.endsWith('.'), `Description for ${category.category} should not end with period`);
      });
    });

    test('should have realistic count values', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      
      categories.forEach(category => {
        assert.ok(category.count >= 1, `Count for ${category.category} should be at least 1`);
        assert.ok(category.count <= 10, `Count for ${category.category} should be reasonable (≤10)`);
        assert.ok(Number.isInteger(category.count), `Count for ${category.category} should be an integer`);
      });
    });
  });

  describe('Integration Validation', () => {
    test('should provide categories useful for get_sfra_documents_by_category tool', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      const categoryNames = categories.map(cat => cat.category);
      
      // These categories should be usable with get_sfra_documents_by_category
      const expectedCategoriesForFiltering = ['core', 'product', 'order', 'customer', 'pricing', 'store', 'other'];
      expectedCategoriesForFiltering.forEach(expectedCategory => {
        assert.ok(categoryNames.includes(expectedCategory), 
          `Should include ${expectedCategory} category for document filtering`);
      });
    });

    test('should have categories that align with SFRA functional areas', async () => {
      const result = await client.callTool('get_sfra_categories', {});
      
      const categories = JSON.parse(result.content[0].text);
      
      // Check that categories align with known SFRA functional areas
      const coreCategory = categories.find(cat => cat.category === 'core');
      const productCategory = categories.find(cat => cat.category === 'product');
      const orderCategory = categories.find(cat => cat.category === 'order');
      
      assert.ok(coreCategory && coreCategory.count > 0, 'Should have core SFRA functionality');
      assert.ok(productCategory && productCategory.count > 0, 'Should have product functionality');
      assert.ok(orderCategory && orderCategory.count > 0, 'Should have order/cart functionality');
    });
  });

  describe('Robustness Testing', () => {
    test('should handle rapid successive calls', async () => {
      // Note: We execute sequentially to avoid MCP buffer conflicts
      for (let i = 0; i < 3; i++) {
        const result = await client.callTool('get_sfra_categories', {});
        assert.equal(result.isError, false, `Rapid call ${i + 1} should succeed`);
        
        const categories = JSON.parse(result.content[0].text);
        assert.equal(categories.length, 7, `Rapid call ${i + 1} should return correct number of categories`);
      }
    });

    test('should maintain state independence', async () => {
      // First call
      const result1 = await client.callTool('get_sfra_categories', {});
      const categories1 = JSON.parse(result1.content[0].text);
      
      // Call with parameters (should ignore them)
      const result2 = await client.callTool('get_sfra_categories', { someParam: 'value' });
      const categories2 = JSON.parse(result2.content[0].text);
      
      // Third call without parameters
      const result3 = await client.callTool('get_sfra_categories', {});
      const categories3 = JSON.parse(result3.content[0].text);
      
      // All should be identical
      assert.deepEqual(categories1, categories2, 'Results should be identical regardless of parameters');
      assert.deepEqual(categories1, categories3, 'Results should be consistent across calls');
    });
  });
});

```

--------------------------------------------------------------------------------
/docs/dw_net/FTPClient.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.net

# Class FTPClient

## Inheritance Hierarchy

- Object
  - dw.net.FTPClient

## Description

The FTPClient class supports the FTP commands CD, GET, PUT, DEL, MKDIR, RENAME, and LIST. The FTP connection is established using passive transfer mode (PASV). The transfer of files can be text or binary. Note: when this class is used with sensitive data, be careful in persisting sensitive information to disk. An example usage is as follows: var ftp : FTPClient = new dw.net.FTPClient(); ftp.connect("my.ftp-server.com", "username", "password"); var data : String = ftp.get("simple.txt"); ftp.disconnect(); The default connection timeout depends on the script context timeout and will be set to a maximum of 30 seconds (default script context timeout is 10 seconds within storefront requests and 15 minutes within jobs). IMPORTANT NOTE: Before you can make an outbound FTP connection, the FTP server IP address must be enabled for outbound traffic at the Commerce Cloud Digital firewall for your POD. Please file a support request to request a new firewall rule.

## Constants

### DEFAULT_GET_FILE_SIZE

**Type:** Number = 5242880

The default size for get() returning a File is 5MB

### DEFAULT_GET_STRING_SIZE

**Type:** Number = 2097152

The default size for get() returning a String is 2MB

### MAX_GET_FILE_SIZE

**Type:** Number = 209715200

The maximum size for get() returning a File is forty times the default size for getting a file. The largest file allowed is 200MB.

### MAX_GET_STRING_SIZE

**Type:** Number = 10485760

The maximum size for get() returning a String is five times the default size for getting a String. The largest String allowed is 10MB.

## Properties

### connected

**Type:** boolean (Read Only)

Identifies if the FTP client is currently connected to the FTP server.

### replyCode

**Type:** Number (Read Only)

The reply code from the last FTP action.

### replyMessage

**Type:** String (Read Only)

The string message from the last FTP action.

### timeout

**Type:** Number

The timeout for this client, in milliseconds.

## Constructor Summary

FTPClient() Constructs the FTPClient instance.

## Method Summary

### cd

**Signature:** `cd(path : String) : boolean`

Changes the current directory on the remote server to the given path.

### connect

**Signature:** `connect(host : String) : boolean`

Connects and logs on to an FTP Server as "anonymous" and returns a boolean indicating success or failure.

### connect

**Signature:** `connect(host : String, user : String, password : String) : boolean`

Connects and logs on to an FTP server and returns a boolean indicating success or failure.

### connect

**Signature:** `connect(host : String, port : Number) : boolean`

Connects and logs on to an FTP Server as "anonymous" and returns a boolean indicating success or failure.

### connect

**Signature:** `connect(host : String, port : Number, user : String, password : String) : boolean`

Connects and logs on to an FTP server and returns a boolean indicating success or failure.

### del

**Signature:** `del(path : String) : boolean`

Deletes the remote file on the server identified by the path parameter.

### disconnect

**Signature:** `disconnect() : void`

The method first logs the current user out from the server and then disconnects from the server.

### get

**Signature:** `get(path : String) : String`

Reads the content of a remote file and returns it as a string using "ISO-8859-1" encoding to read it.

### get

**Signature:** `get(path : String, encoding : String) : String`

Reads the content of a remote file and returns it as string using the passed encoding.

### get

**Signature:** `get(path : String, maxGetSize : Number) : String`

Reads the content of a remote file and returns it as a string using "ISO-8859-1" encoding to read it.

### get

**Signature:** `get(path : String, encoding : String, maxGetSize : Number) : String`

Reads the content of a remote file and returns it as a string using the specified encoding.

### get

**Signature:** `get(path : String, encoding : String, file : File) : boolean`

Reads the content of a remote file and creates a local copy in the given file using the passed string encoding to read the file content and using the system standard encoding "UTF-8" to write the file.

### get

**Signature:** `get(path : String, encoding : String, file : File, maxGetSize : Number) : boolean`

Reads the content of a remote file and creates a local copy in the given file using the passed string encoding to read the file content and using the system standard encoding "UTF-8" to write the file.

### getBinary

**Signature:** `getBinary(path : String, file : File) : boolean`

Reads the content of a remote file and creates a local copy in the given file.

### getBinary

**Signature:** `getBinary(path : String, file : File, maxGetSize : Number) : boolean`

Reads the content of a remote file and creates a local copy in the given file.

### getConnected

**Signature:** `getConnected() : boolean`

Identifies if the FTP client is currently connected to the FTP server.

### getReplyCode

**Signature:** `getReplyCode() : Number`

Returns the reply code from the last FTP action.

### getReplyMessage

**Signature:** `getReplyMessage() : String`

Returns the string message from the last FTP action.

### getTimeout

**Signature:** `getTimeout() : Number`

Returns the timeout for this client, in milliseconds.

### list

**Signature:** `list() : FTPFileInfo[]`

Returns a list of FTPFileInfo objects containing information about the files in the current directory.

### list

**Signature:** `list(path : String) : FTPFileInfo[]`

Returns a list of FTPFileInfo objects containing information about the files in the remote directory defined by the given path.

### mkdir

**Signature:** `mkdir(path : String) : boolean`

Creates a directory

### put

**Signature:** `put(path : String, content : String) : boolean`

Puts the specified content to the specified full path using "ISO-8859-1" encoding.

### put

**Signature:** `put(path : String, content : String, encoding : String) : boolean`

Put the given content to a file on the given full path on the FTP server.

### putBinary

**Signature:** `putBinary(path : String, file : File) : boolean`

Put the content of the given file into a file on the remote FTP server with the given full path.

### removeDirectory

**Signature:** `removeDirectory(path : String) : boolean`

Deletes the remote directory on the server identified by the path parameter.

### rename

**Signature:** `rename(from : String, to : String) : boolean`

Renames an existing file.

### setTimeout

**Signature:** `setTimeout(timeoutMillis : Number) : void`

Sets the timeout for connections made with the FTP client to the given number of milliseconds.

## Constructor Detail

## Method Detail

## Method Details

### cd

**Signature:** `cd(path : String) : boolean`

**Description:** Changes the current directory on the remote server to the given path.

**Parameters:**

- `path`: the new current directory

**Returns:**

true if the directory change was okay

---

### connect

**Signature:** `connect(host : String) : boolean`

**Description:** Connects and logs on to an FTP Server as "anonymous" and returns a boolean indicating success or failure.

**Parameters:**

- `host`: Name of the FTP sever

**Returns:**

true when connection is successful, false otherwise.

---

### connect

**Signature:** `connect(host : String, user : String, password : String) : boolean`

**Description:** Connects and logs on to an FTP server and returns a boolean indicating success or failure.

**Parameters:**

- `host`: Name of the FTP sever
- `user`: Username for the login
- `password`: Password for the login

**Returns:**

true when connection is successful, false otherwise.

---

### connect

**Signature:** `connect(host : String, port : Number) : boolean`

**Description:** Connects and logs on to an FTP Server as "anonymous" and returns a boolean indicating success or failure.

**Parameters:**

- `host`: Name of the FTP sever
- `port`: Port for FTP server

**Returns:**

true when connection is successful, false otherwise.

---

### connect

**Signature:** `connect(host : String, port : Number, user : String, password : String) : boolean`

**Description:** Connects and logs on to an FTP server and returns a boolean indicating success or failure.

**Parameters:**

- `host`: Name of the FTP sever
- `port`: Port for FTP server
- `user`: Username for the login
- `password`: Password for the login

**Returns:**

true when connection is successful, false otherwise.

---

### del

**Signature:** `del(path : String) : boolean`

**Description:** Deletes the remote file on the server identified by the path parameter.

**Parameters:**

- `path`: the path to the file.

**Returns:**

true if the file was successfully deleted, false otherwise.

---

### disconnect

**Signature:** `disconnect() : void`

**Description:** The method first logs the current user out from the server and then disconnects from the server.

---

### get

**Signature:** `get(path : String) : String`

**Description:** Reads the content of a remote file and returns it as a string using "ISO-8859-1" encoding to read it. Read at most MAX_GET_STRING_SIZE bytes.

**Parameters:**

- `path`: remote path of the file to be read.

**Returns:**

the contents of the file or null if an error occurred while reading the file.

---

### get

**Signature:** `get(path : String, encoding : String) : String`

**Description:** Reads the content of a remote file and returns it as string using the passed encoding. Read at most MAX_GET_STRING_SIZE characters.

**Parameters:**

- `path`: remote path of the file to be read.
- `encoding`: an ISO 8859 character encoding labeled as a string, e.g. "ISO-8859-1"

**Returns:**

the contents of the file or null if an error occurred while reading the file.

---

### get

**Signature:** `get(path : String, maxGetSize : Number) : String`

**Description:** Reads the content of a remote file and returns it as a string using "ISO-8859-1" encoding to read it. Read at most maxGetSize characters.

**Deprecated:**

The maxGetSize attribute is not supported anymore. Use the method get(String) instead.

**Parameters:**

- `path`: remote path of the file to be read.
- `maxGetSize`: the maximum bytes fetched from the remote file.

**Returns:**

the contents of the file or null if an error occurred while reading the file.

---

### get

**Signature:** `get(path : String, encoding : String, maxGetSize : Number) : String`

**Description:** Reads the content of a remote file and returns it as a string using the specified encoding. Returns at most maxGetSize characters.

**Deprecated:**

The maxGetSize attribute is not supported anymore. Use the method get(String, String) instead.

**Parameters:**

- `path`: remote path of the file to be read.
- `encoding`: the encoding to use.
- `maxGetSize`: the maximum bytes fetched from the remote file.

**Returns:**

the contents of the file or null if an error occurred while reading the file.

---

### get

**Signature:** `get(path : String, encoding : String, file : File) : boolean`

**Description:** Reads the content of a remote file and creates a local copy in the given file using the passed string encoding to read the file content and using the system standard encoding "UTF-8" to write the file. Copies at most MAX_GET_FILE_SIZE bytes.

**Parameters:**

- `path`: remote path of the file to be read.
- `encoding`: the encoding to use.
- `file`: the local file name

**Returns:**

true if remote file is fetched and copied into local file.

---

### get

**Signature:** `get(path : String, encoding : String, file : File, maxGetSize : Number) : boolean`

**Description:** Reads the content of a remote file and creates a local copy in the given file using the passed string encoding to read the file content and using the system standard encoding "UTF-8" to write the file. Copies at most maxGetSize bytes.

**Deprecated:**

The maxGetSize attribute is not supported anymore. Use the method get(String, String, File) instead.

**Parameters:**

- `path`: remote path of the file to be read.
- `encoding`: the encoding to use.
- `file`: the local file name
- `maxGetSize`: the maximum number of bytes to fetch

**Returns:**

true if remote file is fetched and copied into local file.

---

### getBinary

**Signature:** `getBinary(path : String, file : File) : boolean`

**Description:** Reads the content of a remote file and creates a local copy in the given file. Copies at most MAX_GET_FILE_SIZE bytes. The FTP transfer is done in Binary mode.

**Parameters:**

- `path`: remote path of the file to be read.
- `file`: the local file name

**Returns:**

true if remote file is fetched and copied into local file.

---

### getBinary

**Signature:** `getBinary(path : String, file : File, maxGetSize : Number) : boolean`

**Description:** Reads the content of a remote file and creates a local copy in the given file. Copies at most maxGetSize bytes. The FTP transfer is done in Binary mode.

**Deprecated:**

The maxGetSize attribute is not supported anymore. Use the method getBinary(String, File) instead.

**Parameters:**

- `path`: remote path of the file to be read.
- `file`: the local file name
- `maxGetSize`: the maximum number of bytes to fetch

**Returns:**

true if remote file is fetched and copied into local file.

---

### getConnected

**Signature:** `getConnected() : boolean`

**Description:** Identifies if the FTP client is currently connected to the FTP server.

**Returns:**

true if the client is currently connected.

---

### getReplyCode

**Signature:** `getReplyCode() : Number`

**Description:** Returns the reply code from the last FTP action.

**Returns:**

the reply code from the last FTP action.

---

### getReplyMessage

**Signature:** `getReplyMessage() : String`

**Description:** Returns the string message from the last FTP action.

**Returns:**

the string message from the last FTP action.

---

### getTimeout

**Signature:** `getTimeout() : Number`

**Description:** Returns the timeout for this client, in milliseconds.

**Returns:**

the timeout in milliseconds

---

### list

**Signature:** `list() : FTPFileInfo[]`

**Description:** Returns a list of FTPFileInfo objects containing information about the files in the current directory.

**Returns:**

list of objects with remote file information.

---

### list

**Signature:** `list(path : String) : FTPFileInfo[]`

**Description:** Returns a list of FTPFileInfo objects containing information about the files in the remote directory defined by the given path.

**Parameters:**

- `path`: the remote path from which the file info is listed.

**Returns:**

list of objects with remote file information.

---

### mkdir

**Signature:** `mkdir(path : String) : boolean`

**Description:** Creates a directory

**Parameters:**

- `path`: the path to the directory to create.

**Returns:**

true if the directory was successfully created, false otherwise.

---

### put

**Signature:** `put(path : String, content : String) : boolean`

**Description:** Puts the specified content to the specified full path using "ISO-8859-1" encoding. The full path must include the path and the file name. If the content of a local file is to be uploaded, please use method putBinary(String, File) instead.

**Parameters:**

- `path`: full path on the remote FTP server where the file will be stored.
- `content`: the content to put.

**Returns:**

true or false indicating success or failure.

---

### put

**Signature:** `put(path : String, content : String, encoding : String) : boolean`

**Description:** Put the given content to a file on the given full path on the FTP server. The full path must include the path and the file name. The transformation from String into binary data is done via the encoding provided with the method call. If the content of a local file is to be uploaded, please use method putBinary(String, File) instead.

**Parameters:**

- `path`: the full path on the remote FTP server where the file will be stored.
- `content`: the content to put.
- `encoding`: the encoding to use.

**Returns:**

true or false indicating success or failure.

---

### putBinary

**Signature:** `putBinary(path : String, file : File) : boolean`

**Description:** Put the content of the given file into a file on the remote FTP server with the given full path. The full path must include the path and the file name.

**Parameters:**

- `path`: the full path on the remote FTP server where the file will be stored.
- `file`: the file on the local system, which content is send to the remote FTP server.

**Returns:**

true or false indicating success or failure.

---

### removeDirectory

**Signature:** `removeDirectory(path : String) : boolean`

**Description:** Deletes the remote directory on the server identified by the path parameter. In order to delete the directory successfully the directory needs to be empty, otherwise the removeDirectory() method will return false.

**Parameters:**

- `path`: the path to the directory.

**Returns:**

true if the directory was successfully deleted, false otherwise.

---

### rename

**Signature:** `rename(from : String, to : String) : boolean`

**Description:** Renames an existing file.

**Parameters:**

- `from`: the file that will be renamed.
- `to`: the name of the new file.

**Returns:**

true if the file was successfully renamed, false otherwise.

---

### setTimeout

**Signature:** `setTimeout(timeoutMillis : Number) : void`

**Description:** Sets the timeout for connections made with the FTP client to the given number of milliseconds. If the given timeout is less than or equal to zero, the timeout is set to the same value as the script context timeout but will only be set to a maximum of 30 seconds. The maximum and default timeout depend on the script context timeout. The maximum timeout is set to a maximum of 2 minutes. The default timeout for a new client is set to a maximum of 30 seconds. This method can be called at any time, and will affect the next connection made with this client. It is not possible to set the timeout for an open connection.

**Parameters:**

- `timeoutMillis`: timeout, in milliseconds, up to a maximum of 2 minutes.

---
```

--------------------------------------------------------------------------------
/tests/mcp/node/get-sfcc-class-documentation.docs-only.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('get_sfcc_class_documentation Tool 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
  });

  describe('Valid Class Documentation Retrieval', () => {
    test('should retrieve documentation for dw.catalog.Product', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.catalog.Product'
      });

      assert.equal(result.isError, false, 'Should have isError: false on success');
      assert.ok(result.content, '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 of type text');
      
      const documentation = result.content[0].text;
      assert.ok(documentation.includes('## Package: dw.catalog'), 'Should include package information');
      assert.ok(documentation.includes('# Class Product'), 'Should include class name');
      assert.ok(documentation.includes('## Inheritance Hierarchy'), 'Should include inheritance hierarchy');
      assert.ok(documentation.includes('## Description'), 'Should include description section');
      assert.ok(documentation.includes('## Properties'), 'Should include properties section');
      assert.ok(documentation.includes('## Method Summary'), 'Should include method summary');
    });

    test('should retrieve documentation for dw.system.Site', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.system.Site'
      });

      assert.equal(result.isError, false, 'Should have isError: false on success');
      assert.ok(result.content, '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 of type text');
      
      const documentation = result.content[0].text;
      assert.ok(documentation.includes('dw.system'), 'Should include correct package');
      assert.ok(documentation.includes('Site'), 'Should include class name');
    });

    test('should retrieve documentation for dw.order.Order', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.order.Order'
      });

      assert.equal(result.isError, false, 'Should have isError: false on success');
      assert.ok(result.content, '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 of type text');
      
      const documentation = result.content[0].text;
      assert.ok(documentation.includes('dw.order'), 'Should include correct package');
      assert.ok(documentation.includes('Order'), 'Should include class name');
    });

    test('should handle class names without package prefix', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'Product'
      });

      assert.equal(result.isError, false, 'Should have isError: false on success');
      assert.ok(result.content, '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 of type text');
      
      const documentation = result.content[0].text;
      assert.ok(documentation.includes('Product'), 'Should include class name');
    });
  });

  describe('Documentation Content Structure Validation', () => {
    test('should include all expected sections for a complex class', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.catalog.Product'
      });

      assert.equal(result.isError, false, 'Should have isError: false on success');
      const documentation = result.content[0].text;

      // Check for main structural elements
      assert.ok(documentation.includes('## Package:'), 'Should include package section');
      assert.ok(documentation.includes('# Class'), 'Should include class header');
      assert.ok(documentation.includes('## Inheritance Hierarchy'), 'Should include inheritance');
      assert.ok(documentation.includes('## Description'), 'Should include description');
      assert.ok(documentation.includes('## Properties'), 'Should include properties');
      assert.ok(documentation.includes('## Method Summary'), 'Should include method summary');
      assert.ok(documentation.includes('## Method Detail'), 'Should include method details');

      // Check for specific property examples
      assert.ok(documentation.includes('### ID'), 'Should include ID property');
      assert.ok(documentation.includes('### name'), 'Should include name property');
      assert.ok(documentation.includes('### available'), 'Should include available property');

      // Check for method examples
      assert.ok(documentation.includes('getID()'), 'Should include getID method');
      assert.ok(documentation.includes('getName()'), 'Should include getName method');
      assert.ok(documentation.includes('isAvailable()'), 'Should include isAvailable method');

      // Check for type information
      assert.ok(documentation.includes('**Type:**'), 'Should include type information');
      assert.ok(documentation.includes('**Signature:**'), 'Should include method signatures');
      assert.ok(documentation.includes('**Returns:**'), 'Should include return information');
    });

    test('should include proper markdown formatting', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.catalog.Product'
      });

      assert.equal(result.isError, false, 'Should have isError: false on success');
      const documentation = result.content[0].text;

      // Check markdown formatting
      assert.ok(documentation.includes('##'), 'Should use markdown headers');
      assert.ok(documentation.includes('###'), 'Should use markdown subheaders');
      assert.ok(documentation.includes('**'), 'Should use markdown bold formatting');
      assert.ok(documentation.includes('`'), 'Should use markdown code formatting');
      assert.ok(documentation.includes('\\n'), 'Should have escaped newlines in the documentation string');
    });

    test('should include deprecation warnings when present', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.catalog.Product'
      });

      assert.equal(result.isError, false, 'Should have isError: false on success');
      const documentation = result.content[0].text;

      // Product class has deprecated methods, should include deprecation info
      if (documentation.includes('**Deprecated:**')) {
        assert.ok(documentation.includes('**Deprecated:**'), 'Should mark deprecated items');
      }
    });
  });

  describe('Error Handling', () => {
    test('should handle non-existent class name gracefully', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'NonExistentClass'
      });

      assert.equal(result.isError, true, 'Should be marked as error');
      assert.ok(result.content, 'Should have content even for errors');
      assert.equal(result.content.length, 1, 'Should have exactly one content item');
      assert.equal(result.content[0].type, 'text', 'Content should be of type text');
      assert.ok(result.content[0].text.includes('not found'), 'Should indicate class not found');
      assert.ok(result.content[0].text.includes('NonExistentClass'), 'Should include the requested class name');
    });

    test('should handle invalid package.class format', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'invalid.package.InvalidClass'
      });

      assert.equal(result.isError, true, 'Should be marked as error');
      assert.ok(result.content, 'Should have content even for errors');
      assert.equal(result.content.length, 1, 'Should have exactly one content item');
      assert.equal(result.content[0].type, 'text', 'Content should be of type text');
      assert.ok(result.content[0].text.includes('not found'), 'Should indicate class not found');
    });

    test('should handle empty class name', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: ''
      });

      assert.equal(result.isError, true, 'Should be marked as error');
      assert.ok(result.content, 'Should have content even for errors');
      assert.equal(result.content.length, 1, 'Should have exactly one content item');
      assert.equal(result.content[0].type, 'text', 'Content should be of type text');
      assert.ok(result.content[0].text.includes('non-empty string'), 'Should indicate className must be non-empty');
    });

    test('should handle missing className parameter', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {});

      assert.equal(result.isError, true, 'Should be marked as error');
      assert.ok(result.content, 'Should have content even for errors');
      assert.equal(result.content.length, 1, 'Should have exactly one content item');
      assert.equal(result.content[0].type, 'text', 'Content should be of type text');
      assert.ok(result.content[0].text.includes('non-empty string'), 'Should indicate className is required');
    });

    test('should handle null className parameter', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: null
      });

      assert.equal(result.isError, true, 'Should be marked as error');
      assert.ok(result.content, 'Should have content even for errors');
      assert.equal(result.content.length, 1, 'Should have exactly one content item');
      assert.equal(result.content[0].type, 'text', 'Content should be of type text');
      assert.ok(result.content[0].text.includes('non-empty string'), 'Should indicate className must be non-empty string');
    });

    test('should handle whitespace-only className', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: '   '
      });

      assert.equal(result.isError, true, 'Should be marked as error');
      assert.ok(result.content, 'Should have content even for errors');
      assert.equal(result.content.length, 1, 'Should have exactly one content item');
      assert.equal(result.content[0].type, 'text', 'Content should be of type text');
      assert.ok(result.content[0].text.includes('non-empty string'), 'Should indicate className must be non-empty');
    });
  });


  describe('Edge Cases and Special Characters', () => {
    test('should handle class names with special characters gracefully', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.catalog.Product$Special'
      });

      // Should handle gracefully, either find documentation or return proper error
      assert.ok(result.content, '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 of type text');
      // Either success (isError undefined) or proper error (isError true)
      assert.ok(result.isError === false || result.isError === true, 'Should have proper isError flag');
    });

    test('should handle very long class names', async () => {
      const longClassName = 'dw.catalog.' + 'A'.repeat(100);
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: longClassName
      });

      assert.equal(result.isError, true, 'Should be marked as error for non-existent long class');
      assert.ok(result.content, '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 of type text');
    });

    test('should handle case sensitivity correctly', async () => {
      // Test different case variations
      const variations = [
        'dw.catalog.product',  // lowercase
        'DW.CATALOG.PRODUCT',  // uppercase
        'dw.Catalog.Product'   // mixed case
      ];

      for (const className of variations) {
        const result = await client.callTool('get_sfcc_class_documentation', {
          className
        });

        // Should handle gracefully, likely return error for incorrect case
        assert.ok(result.content, `Should have content for ${className}`);
        assert.equal(result.content.length, 1, `Should have exactly one content item for ${className}`);
        assert.equal(result.content[0].type, 'text', `Content should be of type text for ${className}`);
        // Either success (isError undefined) or error (isError true)
        assert.ok(result.isError === false || result.isError === true, 
          `Should have proper isError flag for ${className}`);
      }
    });
  });

  describe('Documentation Quality and Completeness', () => {
    test('should provide comprehensive documentation for core classes', async () => {
      const coreClasses = [
        'dw.catalog.Product',
        'dw.order.Order',
        'dw.customer.Customer',
        'dw.system.Site'
      ];

      for (const className of coreClasses) {
        const result = await client.callTool('get_sfcc_class_documentation', {
          className
        });

        assert.equal(result.isError, false, `Should not have isError property on success for ${className}`);
        
        const documentation = result.content[0].text;
        assert.ok(documentation.length > 1000, `Documentation for ${className} should be comprehensive`);
        assert.ok(documentation.includes('## Description'), `Should include description for ${className}`);
        assert.ok(documentation.includes('## Properties') || documentation.includes('## Method Summary'), 
          `Should include properties or methods for ${className}`);
      }
    });

    test('should include method signatures and return types', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.catalog.Product'
      });

      assert.equal(result.isError, false, 'Should have isError: false on success');
      
      const documentation = result.content[0].text;
      assert.ok(documentation.includes('**Signature:**'), 'Should include method signatures');
      assert.ok(documentation.includes('**Returns:**'), 'Should include return type information');
      assert.ok(documentation.includes('**Parameters:**'), 'Should include parameter information');
      assert.ok(documentation.includes('**Description:**'), 'Should include method descriptions');
    });

    test('should include inheritance information for classes with hierarchy', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.catalog.Product'
      });

      assert.equal(result.isError, false, 'Should have isError: false on success');
      
      const documentation = result.content[0].text;
      assert.ok(documentation.includes('## Inheritance Hierarchy'), 'Should include inheritance hierarchy');
      assert.ok(documentation.includes('Object'), 'Should show Object as base class');
      assert.ok(documentation.includes('PersistentObject') || documentation.includes('ExtensibleObject'), 
        'Should show intermediate classes in hierarchy');
    });
  });

  describe('Tool Response Format Consistency', () => {
    test('should always return consistent response structure for success', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.catalog.Product'
      });

      // Validate response structure
      assert.ok(result, 'Should return a result object');
      assert.ok(result.content, 'Should have content property');
      assert.ok(Array.isArray(result.content), 'Content should be an array');
      assert.equal(result.content.length, 1, 'Should have exactly one content item');
      assert.equal(result.content[0].type, 'text', 'Content type should be text');
      assert.ok(typeof result.content[0].text === 'string', 'Content text should be string');
      assert.equal(result.isError, false, 'isError should be false for success');
    });

    test('should always return consistent response structure for errors', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'NonExistentClass'
      });

      // Validate response structure
      assert.ok(result, 'Should return a result object');
      assert.ok(result.content, 'Should have content property');
      assert.ok(Array.isArray(result.content), 'Content should be an array');
      assert.equal(result.content.length, 1, 'Should have exactly one content item');
      assert.equal(result.content[0].type, 'text', 'Content type should be text');
      assert.ok(typeof result.content[0].text === 'string', 'Content text should be string');
      assert.equal(result.isError, true, 'isError should be true for errors');
    });

    test('should include isError: false property when successful', async () => {
      const result = await client.callTool('get_sfcc_class_documentation', {
        className: 'dw.catalog.Product'
      });

      // isError should now always be included for consistency
      assert.equal(result.isError, false, 'isError should be false for successful operations');
      assert.ok(Object.prototype.hasOwnProperty.call(result, 'isError'), 'Should have isError property for all responses');
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/mcp/yaml/get-available-sfra-documents.full-mode.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
description: "Docs-only: get_available_sfra_documents tool tests"
config: ./aegis.config.with-dw.json

# We first list tools to ensure the tool exists in full-mode mode, then call it.
tests:
  - it: "should have get_available_sfra_documents in tools list"
    request:
      jsonrpc: "2.0"
      id: "list-sfra-docs-1"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "list-sfra-docs-1"
        result:
          tools:
            match:arrayElements:
              match:partial:
                name: "match:type:string"
          match:extractField: "tools.*.name"
          value: "match:arrayContains:get_available_sfra_documents"

  - it: "should return an array JSON string of sfra documents in text content"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-1"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-1"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:regex:\\[[\\s\\S]*querystring[\\s\\S]*server[\\s\\S]*cart[\\s\\S]*stores[\\s\\S]*\\]" # array JSON includes expected doc names
      stderr: toBeEmpty

  - it: "should include core category documents (querystring, server, request, response, render)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-2"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-2"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:querystring"
      stderr: toBeEmpty

  - it: "should contain product and store model documents (product-full, product-tile, store, stores)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-3"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-3"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:product-full"
      stderr: toBeEmpty

  - it: "should not return error when called without arguments (empty object)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-4"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-4"
        result:
          isError: false
          content: "match:type:array"
      stderr: toBeEmpty

  - it: "should include pricing documents (price-default, price-range, price-tiered)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-5"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-5"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:price-default"
      stderr: toBeEmpty

  - it: "should tolerate extraneous empty arguments object (idempotent behavior)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-6"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-6"
        result:
          match:partial:
            isError: false
      stderr: toBeEmpty

  - it: "should respond within acceptable performance threshold"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-7"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-7"
        result:
          match:partial:
            isError: false
      performance:
        maxResponseTime: "800ms" # docs listing should be fast but allow CI variance
      stderr: toBeEmpty

  - it: "should include multiple distinct categories (core, order, product, pricing, store)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-8"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-8"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:(core|order|product|pricing|store)"
      stderr: toBeEmpty

  - it: "should expose at least 18 documents (count via regex on JSON array)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                # Require at least 18 occurrences of the JSON key "name" (non-greedy across intervening content)
                # NOTE: The prior insanely large quantifier still matched because the engine satisfied the pattern once; quantifier applies to group occurrences.
                # This pattern enforces a realistic lower bound of 18.
                text: "match:regex:(?:\\\"name\\\"[\\s\\S]*?){18,}"
      stderr: toBeEmpty

  # Individual presence tests replacing aggregated lookahead test for better failure diagnostics
  # Core documents
  - it: "should include doc name: server"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-server"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-server"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:server"
      stderr: toBeEmpty

  - it: "should include doc name: request"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-request"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-request"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:request"
      stderr: toBeEmpty

  - it: "should include doc name: response"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-response"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-response"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:response"
      stderr: toBeEmpty

  - it: "should include doc name: querystring"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-querystring"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-querystring"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:querystring"
      stderr: toBeEmpty

  - it: "should include doc name: render"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-render"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-render"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:render"
      stderr: toBeEmpty

  # Functional / model documents
  - it: "should include doc name: cart"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-cart"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-cart"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:cart"
      stderr: toBeEmpty

  - it: "should include doc name: product-full"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-product-full"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-product-full"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:product-full"
      stderr: toBeEmpty

  - it: "should include doc name: product-tile"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-product-tile"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-product-tile"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:product-tile"
      stderr: toBeEmpty

  # Pricing documents
  - it: "should include doc name: price-default"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-price-default"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-price-default"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:price-default"
      stderr: toBeEmpty

  - it: "should include doc name: price-range"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-price-range"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-price-range"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:price-range"
      stderr: toBeEmpty

  - it: "should include doc name: price-tiered"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-price-tiered"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-price-tiered"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:price-tiered"
      stderr: toBeEmpty

  # Store documents
  - it: "should include doc name: store"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-store"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-store"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:store"
      stderr: toBeEmpty

  - it: "should include doc name: stores"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-stores"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-stores"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:stores"
      stderr: toBeEmpty

  # Customer/account related documents
  - it: "should include doc name: account"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-account"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-account"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:account"
      stderr: toBeEmpty

  - it: "should include doc name: billing"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-billing"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-billing"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:billing"
      stderr: toBeEmpty

  - it: "should include doc name: shipping"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-shipping"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-shipping"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:shipping"
      stderr: toBeEmpty

  - it: "should include doc name: address"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-address"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-address"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:address"
      stderr: toBeEmpty

  - it: "should include doc name: locale"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-locale"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-locale"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:locale"
      stderr: toBeEmpty

  - it: "should return JSON-RPC method not found error for invalid method name"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-error-1"
      method: "tools/call_WRONG" # invalid base method to trigger JSON-RPC error
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-error-1"
        error:
          code: "match:type:number"
          message: "match:contains:Method"

  - it: "should include required keys in each document object at least once (name,title,category,filename)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-ext-1"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-ext-1"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                # Newline-safe pattern ensuring all keys appear at least once in any order across pretty-printed JSON
                text: "match:regex:[\\s\\S]*\"name\"[\\s\\S]*\"title\"[\\s\\S]*\"category\"[\\s\\S]*\"filename\"[\\s\\S]*"
      stderr: toBeEmpty

  - it: "should list multiple product model documents (at least 3 occurrences of 'product-')"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-ext-2"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-ext-2"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                # Use a broad regex ensuring at least three product- tokens appear anywhere
                text: "match:regex:(?:product-)[\\s\\S]*(?:product-)[\\s\\S]*(?:product-)"
      stderr: toBeEmpty

  - it: "should have filenames ending with .md for multiple entries"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-ext-4"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-ext-4"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:\"filename\"\\s*:\\s*\"[a-z0-9\\-]+\\.md\""
      stderr: toBeEmpty

  - it: "should respond faster than previous performance spec (tighten to 600ms)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-ext-5"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-ext-5"
        result:
          match:partial:
            isError: false
      performance:
        maxResponseTime: "600ms"
      stderr: toBeEmpty

  - it: "should ignore unknown extraneous parameter without failing"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-ext-6"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments:
          bogus: true
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-ext-6"
        result:
          match:partial:
            isError: false
      stderr: toBeEmpty



```

--------------------------------------------------------------------------------
/tests/mcp/yaml/get-available-sfra-documents.docs-only.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
description: "Docs-only: get_available_sfra_documents tool tests"
config: ./aegis.config.docs-only.json

# We first list tools to ensure the tool exists in docs-only mode, then call it.
tests:
  - it: "should have get_available_sfra_documents in tools list"
    request:
      jsonrpc: "2.0"
      id: "list-sfra-docs-1"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "list-sfra-docs-1"
        result:
          tools:
            match:arrayElements:
              match:partial:
                name: "match:type:string"
          match:extractField: "tools.*.name"
          value: "match:arrayContains:get_available_sfra_documents"

  - it: "should return an array JSON string of sfra documents in text content"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-1"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-1"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:regex:\\[[\\s\\S]*querystring[\\s\\S]*server[\\s\\S]*cart[\\s\\S]*stores[\\s\\S]*\\]" # array JSON includes expected doc names
      stderr: toBeEmpty

  - it: "should include core category documents (querystring, server, request, response, render)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-2"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-2"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:querystring"
      stderr: toBeEmpty

  - it: "should contain product and store model documents (product-full, product-tile, store, stores)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-3"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-3"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:product-full"
      stderr: toBeEmpty

  - it: "should not return error when called without arguments (empty object)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-4"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-4"
        result:
          isError: false
          content: "match:type:array"
      stderr: toBeEmpty

  - it: "should include pricing documents (price-default, price-range, price-tiered)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-5"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-5"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:price-default"
      stderr: toBeEmpty

  - it: "should tolerate extraneous empty arguments object (idempotent behavior)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-6"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-6"
        result:
          match:partial:
            isError: false
      stderr: toBeEmpty

  - it: "should respond within acceptable performance threshold"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-7"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-7"
        result:
          match:partial:
            isError: false
      performance:
        maxResponseTime: "800ms" # docs listing should be fast but allow CI variance
      stderr: toBeEmpty

  - it: "should include multiple distinct categories (core, order, product, pricing, store)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-8"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-8"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:(core|order|product|pricing|store)"
      stderr: toBeEmpty

  - it: "should expose at least 18 documents (count via regex on JSON array)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                # Require at least 18 occurrences of the JSON key "name" (non-greedy across intervening content)
                # NOTE: The prior insanely large quantifier still matched because the engine satisfied the pattern once; quantifier applies to group occurrences.
                # This pattern enforces a realistic lower bound of 18.
                text: "match:regex:(?:\\\"name\\\"[\\s\\S]*?){18,}"
      stderr: toBeEmpty

  # Individual presence tests replacing aggregated lookahead test for better failure diagnostics
  # Core documents
  - it: "should include doc name: server"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-server"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-server"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:server"
      stderr: toBeEmpty

  - it: "should include doc name: request"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-request"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-request"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:request"
      stderr: toBeEmpty

  - it: "should include doc name: response"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-response"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-response"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:response"
      stderr: toBeEmpty

  - it: "should include doc name: querystring"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-querystring"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-querystring"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:querystring"
      stderr: toBeEmpty

  - it: "should include doc name: render"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-render"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-render"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:render"
      stderr: toBeEmpty

  # Functional / model documents
  - it: "should include doc name: cart"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-cart"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-cart"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:cart"
      stderr: toBeEmpty

  - it: "should include doc name: product-full"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-product-full"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-product-full"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:product-full"
      stderr: toBeEmpty

  - it: "should include doc name: product-tile"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-product-tile"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-product-tile"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:product-tile"
      stderr: toBeEmpty

  # Pricing documents
  - it: "should include doc name: price-default"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-price-default"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-price-default"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:price-default"
      stderr: toBeEmpty

  - it: "should include doc name: price-range"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-price-range"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-price-range"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:price-range"
      stderr: toBeEmpty

  - it: "should include doc name: price-tiered"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-price-tiered"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-price-tiered"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:price-tiered"
      stderr: toBeEmpty

  # Store documents
  - it: "should include doc name: store"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-store"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-store"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:store"
      stderr: toBeEmpty

  - it: "should include doc name: stores"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-stores"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-stores"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:stores"
      stderr: toBeEmpty

  # Customer/account related documents
  - it: "should include doc name: account"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-account"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-account"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:account"
      stderr: toBeEmpty

  - it: "should include doc name: billing"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-billing"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-billing"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:billing"
      stderr: toBeEmpty

  - it: "should include doc name: shipping"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-shipping"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-shipping"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:shipping"
      stderr: toBeEmpty

  - it: "should include doc name: address"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-address"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-address"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:address"
      stderr: toBeEmpty

  - it: "should include doc name: locale"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-9b-locale"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-9b-locale"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:locale"
      stderr: toBeEmpty

  - it: "should return JSON-RPC method not found error for invalid method name"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-error-1"
      method: "tools/call_WRONG" # invalid base method to trigger JSON-RPC error
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-error-1"
        error:
          code: "match:type:number"
          message: "match:contains:Method"

  - it: "should include required keys in each document object at least once (name,title,category,filename)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-ext-1"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-ext-1"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                # Newline-safe pattern ensuring all keys appear at least once in any order across pretty-printed JSON
                text: "match:regex:[\\s\\S]*\"name\"[\\s\\S]*\"title\"[\\s\\S]*\"category\"[\\s\\S]*\"filename\"[\\s\\S]*"
      stderr: toBeEmpty

  - it: "should list multiple product model documents (at least 3 occurrences of 'product-')"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-ext-2"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-ext-2"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                # Use a broad regex ensuring at least three product- tokens appear anywhere
                text: "match:regex:(?:product-)[\\s\\S]*(?:product-)[\\s\\S]*(?:product-)"
      stderr: toBeEmpty

  - it: "should have filenames ending with .md for multiple entries"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-ext-4"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-ext-4"
        result:
          isError: false
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:\"filename\"\\s*:\\s*\"[a-z0-9\\-]+\\.md\""
      stderr: toBeEmpty

  - it: "should respond faster than previous performance spec (tighten to 600ms)"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-ext-5"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-ext-5"
        result:
          match:partial:
            isError: false
      performance:
        maxResponseTime: "600ms"
      stderr: toBeEmpty

  - it: "should ignore unknown extraneous parameter without failing"
    request:
      jsonrpc: "2.0"
      id: "sfra-docs-ext-6"
      method: "tools/call"
      params:
        name: "get_available_sfra_documents"
        arguments:
          bogus: true
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-docs-ext-6"
        result:
          match:partial:
            isError: false
      stderr: toBeEmpty



```

--------------------------------------------------------------------------------
/src/clients/sfra-client.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * SFCC SFRA Documentation Client
 *
 * Provides access to SFRA (Storefront Reference Architecture) documentation including
 * core classes like Server, Request, Response, QueryString, render module, and comprehensive
 * model documentation for account, cart, products, pricing, billing, shipping, and more.
 */

import * as fs from 'fs/promises';
import * as path from 'path';
import { PathResolver } from '../utils/path-resolver.js';
import { CacheManager } from '../utils/cache.js';
import { Logger } from '../utils/logger.js';

export interface SFRADocument {
  title: string;
  description: string;
  sections: string[];
  content: string;
  type: 'class' | 'module' | 'model';
  category: 'core' | 'product' | 'order' | 'customer' | 'pricing' | 'store' | 'other';
  properties?: string[];
  methods?: string[];
  filename: string;
  lastModified?: Date;
}

export interface SFRADocumentSummary {
  name: string;
  title: string;
  description: string;
  type: string;
  category: string;
  filename: string;
}

// Document categorization rules
const CATEGORY_MAPPINGS: Record<string, string> = {
  // Core SFRA classes and modules
  'server': 'core',
  'request': 'core',
  'response': 'core',
  'querystring': 'core',
  'render': 'core',

  // Product-related models
  'product-full': 'product',
  'product-bundle': 'product',
  'product-tile': 'product',
  'product-search': 'product',
  'product-line-items': 'product',

  // Pricing models
  'price-default': 'pricing',
  'price-range': 'pricing',
  'price-tiered': 'pricing',

  // Order and cart models
  'cart': 'order',
  'order': 'order',
  'billing': 'order',
  'shipping': 'order',
  'payment': 'order',
  'totals': 'order',

  // Customer models
  'account': 'customer',
  'address': 'customer',

  // Store models
  'store': 'store',
  'stores': 'store',

  // Other models
  'categories': 'other',
  'content': 'other',
  'locale': 'other',
};

/**
 * Enhanced client for accessing SFRA documentation with dynamic discovery
 */
export class SFRAClient {
  private cache: CacheManager;
  private docsPath: string;
  private documentsCache: Map<string, SFRADocument> = new Map();
  private lastScanTime: number = 0;
  private static readonly SCAN_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
  private logger: Logger;

  constructor() {
    this.cache = new CacheManager();
    this.docsPath = PathResolver.getSFRADocsPath();
    this.logger = Logger.getChildLogger('SFRAClient');
  }

  /**
   * Dynamically discover all available SFRA documentation files
   */
  async getAvailableDocuments(): Promise<SFRADocumentSummary[]> {
    const cacheKey = 'sfra:available-documents-v2';
    const cached = this.cache.getSearchResults(cacheKey);

    // Check if we need to rescan the filesystem
    const now = Date.now();
    if (cached && (now - this.lastScanTime) < SFRAClient.SCAN_CACHE_TTL) {
      return cached;
    }

    try {
      const files = await fs.readdir(this.docsPath);
      const mdFiles = files.filter(file =>
        file.endsWith('.md') &&
        file !== 'README.md' &&
        !file.startsWith('.'),
      );

      const documents: SFRADocumentSummary[] = [];

      for (const filename of mdFiles) {
        try {
          const documentName = path.basename(filename, '.md');
          const document = await this.getSFRADocumentMetadata(documentName);

          if (document) {
            documents.push({
              name: documentName,
              title: document.title,
              description: document.description,
              type: document.type,
              category: document.category,
              filename: document.filename,
            });
          }
        } catch (error) {
          this.logger.error(`Error processing SFRA document ${filename}:`, error);
          // Continue processing other files
        }
      }

      // Sort documents by category and then by name
      documents.sort((a, b) => {
        if (a.category !== b.category) {
          // Prioritize core documents
          if (a.category === 'core') {return -1;}
          if (b.category === 'core') {return 1;}
          return a.category.localeCompare(b.category);
        }
        return a.name.localeCompare(b.name);
      });

      this.cache.setSearchResults(cacheKey, documents);
      this.lastScanTime = now;

      return documents;
    } catch (error) {
      this.logger.error('Error scanning SFRA documents directory:', error);
      return [];
    }
  }

  /**
   * Get lightweight metadata for a document without loading full content
   */
  private async getSFRADocumentMetadata(documentName: string): Promise<SFRADocument | null> {
    // Normalize document name for consistent caching and lookup
    const normalizedDocumentName = documentName.toLowerCase();

    // Check if we already have this document cached
    if (this.documentsCache.has(normalizedDocumentName)) {
      return this.documentsCache.get(normalizedDocumentName)!;
    }

    try {
      const filePath = await this.validateAndConstructPath(documentName);
      const stats = await fs.stat(filePath);

      // Check if we have a cached version that's still valid
      const cacheKey = `sfra:metadata:${normalizedDocumentName}`;
      const cached = this.cache.getFileContent(cacheKey);
      if (cached) {
        const cachedData = JSON.parse(cached);
        if (cachedData.lastModified && new Date(cachedData.lastModified) >= stats.mtime) {
          return cachedData;
        }
      }

      // Read only the first part of the file to extract metadata
      const content = await fs.readFile(filePath, 'utf-8');
      const lines = content.split('\n');

      // Extract title
      const titleLine = lines.find(line => line.startsWith('#'));
      const title = titleLine?.replace(/^#+\s*/, '').trim() ?? this.formatDocumentName(normalizedDocumentName);

      // Determine type based on title and content
      const type = this.determineDocumentType(title, content);

      // Determine category - use normalized name for consistent mapping
      const category = (CATEGORY_MAPPINGS[normalizedDocumentName] || 'other') as SFRADocument['category'];

      // Extract description (first substantial paragraph after title)
      const description = this.extractDescription(lines, title);

      // Extract sections (## headers)
      const sections = lines
        .filter(line => line.startsWith('##'))
        .map(line => line.replace(/^##\s*/, '').trim())
        .filter(section => section.length > 0);

      const document: SFRADocument = {
        title,
        description,
        sections,
        content, // Keep full content for now, optimize later if needed
        type,
        category,
        filename: `${normalizedDocumentName}.md`,
        lastModified: stats.mtime,
        ...(type === 'class' || type === 'model' ? {
          properties: this.extractProperties(lines),
          methods: this.extractMethods(lines),
        } : {}),
      };

      // Cache the metadata using normalized name
      this.cache.setFileContent(cacheKey, JSON.stringify(document));
      this.documentsCache.set(normalizedDocumentName, document);

      return document;
    } catch (error) {
      this.logger.error(`Error loading SFRA document metadata ${normalizedDocumentName}:`, error);
      return null;
    }
  }

  /**
   * Get a specific SFRA document with full content
   */
  async getSFRADocument(documentName: string): Promise<SFRADocument | null> {
    // Normalize document name for consistent lookup
    const normalizedDocumentName = documentName.toLowerCase();

    // First try to get from metadata cache
    const metadata = await this.getSFRADocumentMetadata(documentName);
    if (!metadata) {
      return null;
    }

    // If the content is already loaded, return it
    if (metadata.content?.trim()) {
      return metadata;
    }

    // Otherwise, load the full content
    try {
      const filePath = await this.validateAndConstructPath(documentName);
      const content = await fs.readFile(filePath, 'utf-8');

      const fullDocument: SFRADocument = {
        ...metadata,
        content,
      };

      // Update cache using normalized name
      this.documentsCache.set(normalizedDocumentName, fullDocument);
      return fullDocument;
    } catch (error) {
      this.logger.error(`Error loading full SFRA document ${normalizedDocumentName}:`, error);
      return metadata; // Return metadata even if content loading failed
    }
  }

  /**
   * Enhanced search across all SFRA documentation with better categorization
   */
  async searchSFRADocumentation(query: string): Promise<Array<{
    document: string;
    title: string;
    category: string;
    type: string;
    relevanceScore: number;
    matches: Array<{section: string; content: string; lineNumber: number}>;
  }>> {
    const cacheKey = `sfra:search:${query.toLowerCase()}`;
    const cached = this.cache.getSearchResults(cacheKey);
    if (cached) {return cached;}

    const documents = await this.getAvailableDocuments();
    const results = [];
    const queryLower = query.toLowerCase();
    const queryWords = queryLower.split(/\s+/).filter(word => word.length > 1);

    for (const doc of documents) {
      const documentContent = await this.getSFRADocument(doc.name);
      if (!documentContent) {continue;}

      const matches = [];
      const lines = documentContent.content.split('\n');
      let currentSection = '';
      let relevanceScore = 0;

      // Calculate relevance score based on title and description matches
      if (doc.title.toLowerCase().includes(queryLower)) {
        relevanceScore += 10;
      }
      if (doc.description.toLowerCase().includes(queryLower)) {
        relevanceScore += 5;
      }

      // Search through content
      for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        const lineLower = line.toLowerCase();

        if (line.startsWith('##')) {
          currentSection = line.replace(/^##\s*/, '').trim();
        }

        // Check for query matches
        let matchFound = false;
        let lineRelevance = 0;

        if (lineLower.includes(queryLower)) {
          matchFound = true;
          lineRelevance += 3;
        } else {
          // Check for partial matches with query words
          const wordMatches = queryWords.filter(word => lineLower.includes(word));
          if (wordMatches.length > 0) {
            matchFound = true;
            lineRelevance += wordMatches.length;
          }
        }

        if (matchFound) {
          // Get context around the match
          const contextStart = Math.max(0, i - 2);
          const contextEnd = Math.min(lines.length, i + 3);
          const context = lines.slice(contextStart, contextEnd)
            .map((contextLine, idx) => {
              const actualLineNumber = contextStart + idx;
              return actualLineNumber === i ? `>>> ${contextLine}` : contextLine;
            })
            .join('\n');

          matches.push({
            section: currentSection || 'Introduction',
            content: context,
            lineNumber: i + 1,
          });

          relevanceScore += lineRelevance;
        }
      }

      if (matches.length > 0) {
        results.push({
          document: doc.name,
          title: doc.title,
          category: doc.category,
          type: doc.type,
          relevanceScore,
          matches,
        });
      }
    }

    // Sort by relevance score (highest first)
    results.sort((a, b) => b.relevanceScore - a.relevanceScore);

    this.cache.setSearchResults(cacheKey, results);
    return results;
  }

  /**
   * Get documents by category
   */
  async getDocumentsByCategory(category: string): Promise<SFRADocumentSummary[]> {
    const allDocuments = await this.getAvailableDocuments();
    return allDocuments.filter(doc => doc.category === category);
  }

  /**
   * Get all available categories
   */
  async getAvailableCategories(): Promise<Array<{category: string; count: number; description: string}>> {
    const documents = await this.getAvailableDocuments();
    const categoryMap = new Map<string, number>();

    documents.forEach(doc => {
      categoryMap.set(doc.category, (categoryMap.get(doc.category) ?? 0) + 1);
    });

    const categoryDescriptions = {
      'core': 'Core SFRA classes and modules (Server, Request, Response, QueryString, render)',
      'product': 'Product-related models and functionality',
      'order': 'Order, cart, billing, shipping, and payment models',
      'customer': 'Customer account and address models',
      'pricing': 'Pricing and discount models',
      'store': 'Store and location models',
      'other': 'Other models and utilities',
    };

    return Array.from(categoryMap.entries()).map(([category, count]) => ({
      category,
      count,
      description: categoryDescriptions[category as keyof typeof categoryDescriptions] || 'Other documentation',
    }));
  }

  /**
   * Enhanced path validation and construction
   */
  private async validateAndConstructPath(documentName: string): Promise<string> {
    if (!documentName || typeof documentName !== 'string') {
      throw new Error('Invalid document name: must be a non-empty string');
    }

    if (documentName.includes('\0') || documentName.includes('\x00')) {
      throw new Error('Invalid document name: contains null bytes');
    }

    if (documentName.includes('..') || documentName.includes('/') || documentName.includes('\\')) {
      throw new Error('Invalid document name: contains path traversal sequences');
    }

    if (!/^[a-zA-Z0-9_-]+$/.test(documentName)) {
      throw new Error('Invalid document name: contains invalid characters');
    }

    // Normalize document name to lowercase for case-insensitive lookup
    const normalizedDocumentName = documentName.toLowerCase();
    const filePath = path.join(this.docsPath, `${normalizedDocumentName}.md`);
    const resolvedPath = path.resolve(filePath);
    const resolvedDocsPath = path.resolve(this.docsPath);

    if (!resolvedPath.startsWith(resolvedDocsPath)) {
      throw new Error('Invalid document name: path outside allowed directory');
    }

    if (!resolvedPath.toLowerCase().endsWith('.md')) {
      throw new Error('Invalid document name: must reference a markdown file');
    }

    return resolvedPath;
  }

  /**
   * Determine document type from title and content
   */
  private determineDocumentType(title: string, content: string): 'class' | 'module' | 'model' {
    const titleLower = title.toLowerCase();
    const contentLower = content.toLowerCase();

    if (titleLower.includes('class ')) {
      return 'class';
    }

    if (titleLower.includes('module ')) {
      return 'module';
    }

    if (titleLower.includes('model') || contentLower.includes('model') ||
        contentLower.includes('constructor') || contentLower.includes('properties')) {
      return 'model';
    }

    return 'model'; // Default for most SFRA docs
  }

  /**
   * Extract description from document lines
   */
  private extractDescription(lines: string[], title: string): string {
    const titleIndex = lines.findIndex(line => line.trim() === `# ${title}` || line.startsWith('#'));
    if (titleIndex === -1) {
      return 'No description available';
    }

    // Look for overview section first
    const overviewIndex = lines.findIndex((line, index) =>
      index > titleIndex && line.toLowerCase().includes('## overview'),
    );

    if (overviewIndex !== -1) {
      // Get content under Overview section
      let descriptionEnd = lines.findIndex((line, index) =>
        index > overviewIndex + 1 && line.startsWith('##'),
      );

      if (descriptionEnd === -1) {
        descriptionEnd = Math.min(lines.length, overviewIndex + 10);
      }

      const overviewContent = lines.slice(overviewIndex + 1, descriptionEnd)
        .filter(line => line.trim() && !line.startsWith('#'))
        .join(' ')
        .trim();

      if (overviewContent) {
        return overviewContent.substring(0, 300) + (overviewContent.length > 300 ? '...' : '');
      }
    }

    // Fallback to first paragraph after title
    let descriptionStart = titleIndex + 1;
    while (descriptionStart < lines.length && !lines[descriptionStart].trim()) {
      descriptionStart++;
    }

    const descriptionEnd = lines.findIndex((line, index) =>
      index > descriptionStart && (line.startsWith('#') || line.trim() === ''));

    const description = lines
      .slice(descriptionStart, descriptionEnd > -1 ? descriptionEnd : descriptionStart + 3)
      .filter(line => line.trim() && !line.startsWith('#'))
      .join(' ')
      .trim();

    return description || 'No description available';
  }

  /**
   * Extract properties from document content
   */
  private extractProperties(lines: string[]): string[] {
    const properties: string[] = [];
    let inPropertiesSection = false;

    for (const line of lines) {
      if (line.toLowerCase().includes('## properties') ||
          line.toLowerCase().includes('## property')) {
        inPropertiesSection = true;
        continue;
      }

      if (inPropertiesSection && line.startsWith('#') && !line.includes('properties')) {
        break;
      }

      if (inPropertiesSection && line.startsWith('### ')) {
        const property = line.replace('### ', '').trim();
        if (!properties.includes(property)) {
          properties.push(property);
        }
      }
    }

    return properties;
  }

  /**
   * Extract methods from document content
   */
  private extractMethods(lines: string[]): string[] {
    const methods: string[] = [];
    let inMethodSection = false;

    for (const line of lines) {
      if (line.toLowerCase().includes('## method') ||
          line.toLowerCase().includes('## function')) {
        inMethodSection = true;
        continue;
      }

      if (inMethodSection && line.startsWith('#') &&
          !line.toLowerCase().includes('method') &&
          !line.toLowerCase().includes('function')) {
        break;
      }

      if (inMethodSection && line.startsWith('### ')) {
        const method = line.replace('### ', '').trim();
        if (!methods.includes(method)) {
          methods.push(method);
        }
      }
    }

    return methods;
  }

  /**
   * Format document name for display
   */
  private formatDocumentName(documentName: string): string {
    return documentName
      .split('-')
      .map(word => word.charAt(0).toUpperCase() + word.slice(1))
      .join(' ');
  }

  /**
   * Clear all caches
   */
  clearCache(): void {
    this.cache.clearAll();
    this.documentsCache.clear();
    this.lastScanTime = 0;
  }
}

```

--------------------------------------------------------------------------------
/tests/mcp/node/get-job-log-entries.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('get_job_log_entries - Full Mode Programmatic Tests', () => {
  let client;
  let discoveredJobNames = [];

  before(async () => {
    client = await connect('./aegis.config.with-dw.json');
    
    // Discover available job names for advanced testing
    await discoverJobNames();
  });

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

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

  // 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 parseResponseText(text) {
    // The response may come wrapped in quotes, so parse if needed
    return text.startsWith('"') && text.endsWith('"') 
      ? JSON.parse(text) 
      : text;
  }

  function assertTextContent(result, expectedSubstring) {
    assertValidMCPResponse(result);
    assert.equal(result.content[0].type, 'text');
    const actualText = parseResponseText(result.content[0].text);
    assert.ok(actualText.includes(expectedSubstring),
      `Expected "${expectedSubstring}" in "${actualText}"`);
  }

  function assertSuccessResponse(result) {
    assertValidMCPResponse(result);
    assert.equal(result.isError, false, 'Should not be an error response');
    assert.equal(result.content[0].type, 'text');
  }

  function assertErrorResponse(result, expectedErrorText) {
    assertValidMCPResponse(result);
    assert.equal(result.isError, true, 'Should be an error response');
    assert.equal(result.content[0].type, 'text');
    if (expectedErrorText) {
      assertTextContent(result, expectedErrorText);
    }
  }

  function assertJobLogEntriesFormat(result, expectedLimit, expectedLevel = 'all levels', jobName = null) {
    assertSuccessResponse(result);
    const text = parseResponseText(result.content[0].text);
    
    // Determine expected header pattern
    let expectedHeader;
    if (jobName) {
      expectedHeader = `Latest ${expectedLimit} ${expectedLevel} messages from job: ${jobName}:`;
    } else {
      expectedHeader = `Latest ${expectedLimit} ${expectedLevel} messages from latest jobs:`;
    }
    
    // Check if it's an empty result
    if (text.trim() === expectedHeader.trim() || text.includes('No job logs found')) {
      // Valid empty result case
      return { entryCount: 0, jobNames: [] };
    }
    
    // Should start with the expected header
    assert.ok(text.includes(expectedHeader),
      `Should start with "${expectedHeader}"`);
    
    // Extract job log entries
    const entries = extractJobLogEntries(text);
    
    // Validate each entry has proper structure
    const jobNames = new Set();
    for (const entry of entries) {
      // Each entry should have job name in brackets
      assert.ok(/^\[[\w]+\]/.test(entry.trim()),
        `Entry should start with job name in brackets: "${entry.substring(0, 50)}..."`);
      
      // Should contain timestamp in GMT format
      assert.ok(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} GMT/.test(entry),
        `Entry should contain GMT timestamp: "${entry.substring(0, 100)}..."`);
      
      // Should contain log level (unless filtered to specific level)
      if (expectedLevel === 'all levels') {
        assert.ok(/(ERROR|WARN|INFO|DEBUG)/.test(entry),
          `Entry should contain log level: "${entry.substring(0, 100)}..."`);
      } else {
        const levelToCheck = expectedLevel.replace(' levels', '').replace(' messages', '');
        assert.ok(entry.includes(levelToCheck.toUpperCase()),
          `Entry should contain ${levelToCheck.toUpperCase()} level: "${entry.substring(0, 100)}..."`);
      }
      
      // Should contain SystemJobThread pattern
      assert.ok(/SystemJobThread/.test(entry),
        `Entry should contain SystemJobThread pattern: "${entry.substring(0, 100)}..."`);
      
      // Extract job name for validation
      const jobNameMatch = entry.match(/^\[(\w+)\]/);
      if (jobNameMatch) {
        jobNames.add(jobNameMatch[1]);
      }
    }
    
    // If job name filter is specified, all entries should be from that job
    if (jobName) {
      for (const extractedJobName of jobNames) {
        assert.equal(extractedJobName, jobName,
          `All entries should be from job "${jobName}", found "${extractedJobName}"`);
      }
    }
    
    // Number of entries should not exceed limit
    assert.ok(entries.length <= expectedLimit,
      `Number of entries ${entries.length} should not exceed limit ${expectedLimit}`);
    
    return { entryCount: entries.length, jobNames: Array.from(jobNames) };
  }

  function extractJobLogEntries(text) {
    // Split by the separator and filter out header and empty lines
    const parts = text.split('---').map(part => part.trim()).filter(part => 
      part && !part.includes('Latest') && !part.includes('messages from'));
    return parts;
  }

  function assertJobExecutionPatterns(result) {
    assertSuccessResponse(result);
    const text = parseResponseText(result.content[0].text);
    
    // Should contain job execution patterns
    const patterns = [
      /Execution of job finished with status/,
      /Step \[[\w]+\] completed successfully/,
      /Executing step \[[\w]+\] for/,
      /Executing job \[[\w]+\]\[[\d]+\]/
    ];
    
    const foundPatterns = patterns.filter(pattern => pattern.test(text));
    assert.ok(foundPatterns.length > 0,
      `Should contain at least one job execution pattern in: "${text.substring(0, 200)}..."`);
  }

  async function discoverJobNames() {
    try {
      const result = await client.callTool('get_job_log_entries', { limit: 20 });
      if (!result.isError) {
        const text = parseResponseText(result.content[0].text);
        
        // Look for job names that appear at the start of log lines
        // Pattern: [JobName] [timestamp] LEVEL SystemJobThread...
        const logLinePattern = /\n\[(\w+)\] \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} GMT\] \w+ SystemJobThread/g;
        const jobNameMatches = [];
        let match;
        
        while ((match = logLinePattern.exec(text)) !== null) {
          jobNameMatches.push(match[1]);
        }
        
        if (jobNameMatches.length > 0) {
          // Filter out obvious non-job names and get unique values
          const validJobNames = jobNameMatches
            .filter(name => 
              name.length > 2 && // Must be more than 2 characters
              !['OK', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'Organization'].includes(name) && // Exclude status words
              !/Step$/.test(name) // Exclude step names ending with "Step"
            );
          discoveredJobNames = [...new Set(validJobNames)];
        }
        
        // Fallback: if no job names found, use known job names from mock data
        if (discoveredJobNames.length === 0) {
          discoveredJobNames = ['ProcessOrders', 'ImportCatalog'];
        }
      }
    } catch (error) {
      // Discovery is optional, continue with tests using fallback
      discoveredJobNames = ['ProcessOrders', 'ImportCatalog'];
      console.warn('Job name discovery failed, using fallback:', error.message);
    }
  }

  // Core functionality and parameter validation tests
  describe('Core Functionality', () => {
    test('should retrieve job log entries with default parameters', async () => {
      const result = await client.callTool('get_job_log_entries', {});
      
      assertJobLogEntriesFormat(result, 50); // Default limit is 50
      assertJobExecutionPatterns(result);
      
      // Should contain SFCC job-specific patterns
      const text = parseResponseText(result.content[0].text);
      assert.ok(/SystemJobThread/.test(text),
        'Should contain SystemJobThread patterns');
    });

    test('should respect limit parameter boundaries', async () => {
      // Test small limit
      const smallResult = await client.callTool('get_job_log_entries', { limit: 1 });
      const smallAnalysis = assertJobLogEntriesFormat(smallResult, 1);
      assert.ok(smallAnalysis.entryCount <= 1, 'Should respect small limit');

      // Test reasonable limit
      const mediumResult = await client.callTool('get_job_log_entries', { limit: 10 });
      const mediumAnalysis = assertJobLogEntriesFormat(mediumResult, 10);
      assert.ok(mediumAnalysis.entryCount <= 10, 'Should respect medium limit');
    });

    test('should filter by log levels correctly', async () => {
      // Test representative log levels (not all - YAML tests cover others)
      const testCases = [
        { level: 'error', expected: 'error' },
        { level: 'info', expected: 'info' },
        { level: 'all', expected: 'all levels' }
      ];

      for (const testCase of testCases) {
        const result = await client.callTool('get_job_log_entries', { 
          level: testCase.level, 
          limit: 3 
        });
        
        assertJobLogEntriesFormat(result, 3, testCase.expected);
        assertTextContent(result, `Latest 3 ${testCase.expected} messages from latest jobs:`);
      }
    });

    test('should filter by job name when specified', async () => {
      const result = await client.callTool('get_job_log_entries', { 
        jobName: 'ProcessOrders',
        limit: 3 
      });
      
      assertJobLogEntriesFormat(result, 3, 'all levels', 'ProcessOrders');
      assertTextContent(result, 'Latest 3 all levels messages from job: ProcessOrders:');
    });

    test('should combine parameters correctly', async () => {
      const result = await client.callTool('get_job_log_entries', { 
        level: 'info',
        limit: 5,
        jobName: 'ImportCatalog' 
      });
      
      assertJobLogEntriesFormat(result, 5, 'info', 'ImportCatalog');
      assertTextContent(result, 'Latest 5 info messages from job: ImportCatalog:');
    });
  });

  // Content structure and format validation tests
  describe('Content Validation', () => {
    test('should maintain consistent job log entry structure', async () => {
      const result = await client.callTool('get_job_log_entries', { limit: 10 });
      
      assertSuccessResponse(result);
      
      const text = parseResponseText(result.content[0].text);
      const entries = extractJobLogEntries(text);
      
      // Each entry should follow consistent structure (test first 3 entries)
      for (const entry of entries.slice(0, 3)) {
        // Should start with job name in brackets
        assert.ok(/^\[[\w]+\]/.test(entry.trim()),
          `Entry should start with job name: "${entry.substring(0, 100)}..."`);
        
        // Should contain timestamp
        assert.ok(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} GMT/.test(entry),
          `Entry should contain timestamp: "${entry.substring(0, 100)}..."`);
        
        // Should contain log level
        assert.ok(/(ERROR|WARN|INFO|DEBUG)/.test(entry),
          `Entry should contain log level: "${entry.substring(0, 100)}..."`);
        
        // Should contain SystemJobThread with ID pattern
        assert.ok(/SystemJobThread\|\d+/.test(entry),
          `Entry should contain SystemJobThread with ID: "${entry.substring(0, 100)}..."`);
      }
    });

    test('should include proper job execution details', async () => {
      const result = await client.callTool('get_job_log_entries', { limit: 10 });
      
      assertJobExecutionPatterns(result);
      
      const text = parseResponseText(result.content[0].text);
      
      // Should contain organization references
      assert.ok(/\[Organization\]/.test(text),
        'Should contain Organization references');
    });
  });

  // Error handling and edge cases
  describe('Error Handling', () => {
    test('should handle invalid limit values', async () => {
      // Test zero limit
      const zeroResult = await client.callTool('get_job_log_entries', { limit: 0 });
      assertErrorResponse(zeroResult, 'Invalid limit');
      assertTextContent(zeroResult, 'Must be between 1 and 1000');

      // Test negative limit  
      const negativeResult = await client.callTool('get_job_log_entries', { limit: -5 });
      assertErrorResponse(negativeResult, 'Invalid limit');

      // Test extremely large limit
      const largeResult = await client.callTool('get_job_log_entries', { limit: 10000 });
      assertErrorResponse(largeResult, 'Invalid limit');
      assertTextContent(largeResult, 'Must be between 1 and 1000');
    });

    test('should handle invalid log level gracefully', async () => {
      const result = await client.callTool('get_job_log_entries', { 
        level: 'invalid',
        limit: 5 
      });
      
      assertErrorResponse(result, 'Error');
    });

    test('should handle nonexistent job name gracefully', async () => {
      const result = await client.callTool('get_job_log_entries', { 
        jobName: 'NonExistentJob',
        limit: 5 
      });
      
      assertSuccessResponse(result);
      assertTextContent(result, 'No job logs found for job name: NonExistentJob');
    });

    test('should handle edge case job names', async () => {
      // Empty job name should be treated as no filter
      const emptyResult = await client.callTool('get_job_log_entries', { 
        jobName: '',
        limit: 3 
      });
      assertSuccessResponse(emptyResult);
      assertTextContent(emptyResult, 'Latest 3 all levels messages from latest jobs:');

      // Special characters should be handled gracefully
      const specialResult = await client.callTool('get_job_log_entries', { 
        jobName: 'Job@#$%',
        limit: 3 
      });
      assertSuccessResponse(specialResult);
      assert.ok(specialResult.content[0].text.includes('Job@#$%') || 
                specialResult.content[0].text.includes('No job logs found'),
        'Should handle special characters gracefully');
    });
  });

  // Advanced workflows using discovered job names
  describe('Dynamic Job Discovery Workflows', () => {
    test('should discover and analyze job patterns dynamically', async () => {
      if (discoveredJobNames.length === 0) {
        console.warn('No job names discovered, skipping dynamic tests');
        return;
      }

      // Test with discovered job names (limit to first 2 for efficiency)
      for (const jobName of discoveredJobNames.slice(0, 2)) {
        const result = await client.callTool('get_job_log_entries', { 
          jobName,
          limit: 5 
        });
        
        if (!result.isError) {
          assertJobLogEntriesFormat(result, 5, 'all levels', jobName);
          assertTextContent(result, `Latest 5 all levels messages from job: ${jobName}:`);
        }
      }
    });

    test('should support progressive filtering workflow', async () => {
      // Step 1: Start with broad search to discover available content
      const broadResult = await client.callTool('get_job_log_entries', { limit: 20 });
      assertSuccessResponse(broadResult);
      
      // Step 2: Focus on error level to identify problem areas
      const errorResult = await client.callTool('get_job_log_entries', { 
        level: 'error',
        limit: 5 
      });
      
      assertValidMCPResponse(errorResult);
      
      // Step 3: If errors found and job names discovered, drill down
      if (!errorResult.isError && discoveredJobNames.length > 0) {
        const jobName = discoveredJobNames[0];
        const specificErrorResult = await client.callTool('get_job_log_entries', { 
          jobName,
          level: 'error',
          limit: 3 
        });
        
        assertValidMCPResponse(specificErrorResult);
        if (!specificErrorResult.isError) {
          assertJobLogEntriesFormat(specificErrorResult, 3, 'error', jobName);
        }
      }
    });

    test('should handle parameter combinations reliably across discovered jobs', async () => {
      if (discoveredJobNames.length === 0) {
        console.warn('No job names discovered, using fallback for parameter testing');
        discoveredJobNames = ['ProcessOrders']; // Use fallback for this test
      }

      const testJobName = discoveredJobNames[0];
      const paramCombinations = [
        { limit: 1 },
        { limit: 3, level: 'info' },
        { limit: 2, jobName: testJobName },
        { limit: 2, level: 'error', jobName: testJobName }
      ];
      
      for (const params of paramCombinations) {
        const result = await client.callTool('get_job_log_entries', params);
        
        assertValidMCPResponse(result);
        
        if (!result.isError) {
          assertSuccessResponse(result);
          const text = parseResponseText(result.content[0].text);
          
          // Should contain expected limit in header
          assert.ok(text.includes(`Latest ${params.limit}`),
            `Should contain limit ${params.limit} in response`);
          
          // Should contain expected level if specified
          if (params.level) {
            assert.ok(text.includes(`${params.level} messages`),
              `Should contain level ${params.level} in response`);
          }
          
          // Should contain job name if specified
          if (params.jobName) {
            assert.ok(text.includes(`from job: ${params.jobName}`),
              `Should contain job name ${params.jobName} in response`);
          }
        }
      }
    });
  });

  // Reliability and consistency testing
  describe('Functional Reliability', () => {
    test('should maintain consistent response structure across multiple calls', async () => {
      const calls = [];
      
      // Make multiple sequential calls to test consistency
      for (let i = 0; i < 3; i++) { // Reduced from 5 to 3 for efficiency
        const result = await client.callTool('get_job_log_entries', { limit: 2 });
        calls.push(result);
      }
      
      // All calls should have consistent structure
      for (const result of calls) {
        assertValidMCPResponse(result);
        assert.equal(result.content[0].type, 'text');
        assert.equal(typeof result.isError, 'boolean');
      }
      
      // All successful calls should have similar content structure
      const successfulCalls = calls.filter(call => !call.isError);
      for (const result of successfulCalls) {
        const text = parseResponseText(result.content[0].text);
        assert.ok(text.includes('Latest 2 all levels messages'),
          'Should contain consistent header format');
      }
    });
  });
});

```

--------------------------------------------------------------------------------
/src/clients/logs/log-client.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Main log client - lightweight orchestrator that composes specialized modules
 */

import { Logger } from '../../utils/logger.js';
import { getCurrentDate, normalizeFilePath } from '../../utils/utils.js';
import { WebDAVClientManager } from './webdav-client-manager.js';
import { LogFileReader } from './log-file-reader.js';
import { LogFileDiscovery } from './log-file-discovery.js';
import { LogProcessor } from './log-processor.js';
import { LogAnalyzer } from './log-analyzer.js';
import { LogFormatter } from './log-formatter.js';
import { LOG_CONSTANTS, LOG_MESSAGES, JOB_LOG_CONSTANTS } from './log-constants.js';
import type {
  LogLevel,
  LogSearchOptions,
  WebDAVClientConfig,
} from './log-types.js';

// Support for backward compatibility with SFCCConfig
interface SFCCConfig {
  hostname?: string;
  username?: string;
  password?: string;
  clientId?: string;
  clientSecret?: string;
}

export class SFCCLogClient {
  private logger: Logger;
  private webdavManager: WebDAVClientManager;
  private fileReader: LogFileReader;
  private fileDiscovery: LogFileDiscovery;
  private processor: LogProcessor;
  private analyzer: LogAnalyzer;

  constructor(config: SFCCConfig | WebDAVClientConfig, logger?: Logger) {
    this.logger = logger ?? Logger.getChildLogger('LogClient');
    this.webdavManager = new WebDAVClientManager(this.logger);

    // Convert SFCCConfig to WebDAVClientConfig for backward compatibility
    const webdavConfig: WebDAVClientConfig = {
      hostname: config.hostname!,
      username: config.username,
      password: config.password,
      clientId: config.clientId,
      clientSecret: config.clientSecret,
    };

    // Setup WebDAV client and initialize modules
    const webdavClient = this.webdavManager.setupClient(webdavConfig);
    this.fileReader = new LogFileReader(webdavClient, this.logger);
    this.fileDiscovery = new LogFileDiscovery(webdavClient, this.logger);
    this.processor = new LogProcessor(this.logger);
    this.analyzer = new LogAnalyzer(this.logger);
  }

  /**
   * Get the latest log entries for a specific log level
   */
  async getLatestLogs(level: LogLevel, limit: number, date?: string): Promise<string> {
    const targetDate = date ?? getCurrentDate();
    this.logger.methodEntry('getLatestLogs', { level, limit, date: targetDate });

    const startTime = Date.now();

    // Get and filter log files
    const levelFiles = await this.fileDiscovery.getLogFilesByLevel(level, targetDate);

    if (levelFiles.length === 0) {
      const allFiles = await this.fileDiscovery.getLogFiles(targetDate);
      const availableFiles = allFiles.map(f => normalizeFilePath(f.filename));
      const result = LogFormatter.formatNoFilesFound(level, targetDate, availableFiles);
      this.logger.warn(result);
      this.logger.methodExit('getLatestLogs', { result: 'no_files' });
      return result;
    }

    // Sort files by date (newest first)
    const sortedFiles = this.fileDiscovery.sortFilesByDate(levelFiles, true);

    // Read file contents
    const fileContents = await this.fileReader.readMultipleFiles(
      sortedFiles.map(f => f.filename),
      { maxBytes: LOG_CONSTANTS.DEFAULT_TAIL_BYTES },
    );

    // Process log entries
    const allLogEntries = await this.processor.processLogFiles(sortedFiles, level, fileContents);
    const sortedEntries = this.processor.sortAndLimitEntries(allLogEntries, limit);
    const latestEntries = this.processor.extractFormattedEntries(sortedEntries);

    // Format response
    const fileList = sortedFiles.map(f => normalizeFilePath(f.filename));
    const result = LogFormatter.formatLatestLogs(latestEntries, level, limit, fileList);

    this.logger.debug(LogFormatter.formatProcessingSummary(
      latestEntries.length,
      sortedFiles.length,
      allLogEntries.length,
    ));
    this.logger.timing('getLatestLogs', startTime);
    this.logger.methodExit('getLatestLogs', {
      entriesReturned: latestEntries.length,
      filesProcessed: sortedFiles.length,
    });

    return result;
  }

  /**
   * Get list of log files for a specific date (backward compatibility)
   */
  async getLogFiles(date?: string): Promise<Array<{ filename: string; lastmod: string }>> {
    const targetDate = date ?? getCurrentDate();
    this.logger.methodEntry('getLogFiles', { date: targetDate });

    const logFiles = await this.fileDiscovery.getLogFiles(targetDate);

    this.logger.methodExit('getLogFiles', { count: logFiles.length });
    return logFiles;
  }

  /**
   * Generate a comprehensive summary of logs for a specific date
   */
  async summarizeLogs(date?: string): Promise<string> {
    const targetDate = date ?? getCurrentDate();
    this.logger.methodEntry('summarizeLogs', { date: targetDate });

    const logFiles = await this.fileDiscovery.getLogFiles(targetDate);

    if (logFiles.length === 0) {
      const result = `No log files found for date ${targetDate}`;
      this.logger.methodExit('summarizeLogs', { result: 'no_files' });
      return result;
    }

    // Read file contents
    const fileContents = await this.fileReader.readMultipleFiles(
      logFiles.map(f => f.filename),
      { maxBytes: LOG_CONSTANTS.DEFAULT_TAIL_BYTES },
    );

    // Analyze logs
    const summary = await this.analyzer.analyzeLogs(logFiles, fileContents, targetDate);
    const result = LogFormatter.formatLogSummary(summary);

    this.logger.methodExit('summarizeLogs', { filesAnalyzed: logFiles.length });
    return result;
  }

  /**
   * Search for specific patterns across log files
   */
  async searchLogs(options: LogSearchOptions): Promise<string>;
  async searchLogs(pattern: string, logLevel?: LogLevel, limit?: number, date?: string): Promise<string>;
  async searchLogs(
    optionsOrPattern: LogSearchOptions | string,
    logLevel?: LogLevel,
    limit: number = LOG_CONSTANTS.DEFAULT_SEARCH_LIMIT,
    date?: string,
  ): Promise<string> {
    // Handle both new options interface and legacy parameters
    const options: LogSearchOptions = typeof optionsOrPattern === 'string'
      ? {
        pattern: optionsOrPattern,
        logLevel,
        limit,
        date,
      }
      : optionsOrPattern;

    const { pattern, logLevel: level, limit: searchLimit, date: searchDate } = options;
    const targetDate = searchDate ?? getCurrentDate();
    this.logger.methodEntry('searchLogs', { pattern, logLevel: level, limit: searchLimit, date: targetDate });

    const logFiles = await this.fileDiscovery.getLogFiles(targetDate);

    // Filter by log level if specified
    const filesToSearch = level
      ? this.fileDiscovery.filterLogFiles(logFiles, { level })
      : logFiles;

    if (filesToSearch.length === 0) {
      const result = LOG_MESSAGES.NO_SEARCH_MATCHES(pattern, targetDate);
      this.logger.methodExit('searchLogs', { result: 'no_files' });
      return result;
    }

    // Read file contents
    const fileContents = await this.fileReader.readMultipleFiles(
      filesToSearch.map(f => f.filename),
      { maxBytes: LOG_CONSTANTS.DEFAULT_TAIL_BYTES },
    );

    // Search for patterns
    const matches = this.processor.processSearchResults(filesToSearch, fileContents, pattern, searchLimit);
    const result = LogFormatter.formatSearchResults(matches, pattern, targetDate);

    this.logger.methodExit('searchLogs', { matchesFound: matches.length });
    return result;
  }

  /**
   * List available log files with metadata
   */
  async listLogFiles(): Promise<string> {
    this.logger.methodEntry('listLogFiles');

    const startTime = Date.now();
    try {
      const files = await this.fileDiscovery.getAllLogFiles();
      const result = LogFormatter.formatLogFilesList(files);
      this.logger.methodExit('listLogFiles', { fileCount: files.length });
      return result;
    } catch (error) {
      const errorMessage = LogFormatter.formatError('list_log_files', error);
      this.logger.error(errorMessage);
      this.logger.methodExit('listLogFiles', { error: true });
      throw new Error(`Failed to list log files: ${(error as Error).message}`);
    } finally {
      const duration = Date.now() - startTime;
      this.logger.debug(`listLogFiles completed in ${duration}ms`);
    }
  }

  /**
   * Get the complete contents of a specific log file
   */
  async getLogFileContents(filename: string, maxBytes?: number, tailOnly?: boolean): Promise<string> {
    this.logger.methodEntry('getLogFileContents', { filename, maxBytes, tailOnly });

    const startTime = Date.now();
    try {
      // Use tailOnly flag to determine reading strategy
      if (tailOnly) {
        const content = await this.fileReader.getFileContentsTail(filename, {
          maxBytes: maxBytes ?? LOG_CONSTANTS.DEFAULT_TAIL_BYTES,
        });
        const result = this.formatLogFileContents(filename, content, true);
        this.logger.methodExit('getLogFileContents', { tailOnly: true });
        return result;
      } else {
        // Read full file from beginning with optional size limit
        const content = await this.fileReader.getFileContentsHead(filename, maxBytes);
        const result = this.formatLogFileContents(filename, content, false);
        this.logger.methodExit('getLogFileContents', { tailOnly: false });
        return result;
      }
    } catch (error) {
      const errorMessage = LogFormatter.formatError('get_log_file_contents', error);
      this.logger.error(errorMessage);
      this.logger.methodExit('getLogFileContents', { error: true });
      return errorMessage;
    } finally {
      const duration = Date.now() - startTime;
      this.logger.debug(`getLogFileContents completed in ${duration}ms`);
    }
  }

  /**
   * Format log file contents for display
   */
  private formatLogFileContents(filename: string, content: string, isTailOnly: boolean): string {
    const lines = content.split('\n').filter(line => line.trim());
    const readType = isTailOnly ? 'tail' : 'full';

    return `# Log File Contents: ${filename} (${readType} read)

Total lines: ${lines.length}
Content size: ${content.length} bytes

---

${content}`;
  }

  /**
   * Get advanced log analysis with patterns and recommendations
   */
  async getAdvancedAnalysis(date?: string): Promise<string> {
    const targetDate = date ?? getCurrentDate();
    this.logger.methodEntry('getAdvancedAnalysis', { date: targetDate });

    const logFiles = await this.fileDiscovery.getLogFiles(targetDate);

    if (logFiles.length === 0) {
      return `No log files found for date ${targetDate}`;
    }

    // Read file contents
    const fileContents = await this.fileReader.readMultipleFiles(
      logFiles.map(f => f.filename),
      { maxBytes: LOG_CONSTANTS.DEFAULT_TAIL_BYTES },
    );

    // Perform comprehensive analysis
    const summary = await this.analyzer.analyzeLogs(logFiles, fileContents, targetDate);

    // Parse entries for pattern detection
    const allEntries = Array.from(fileContents.values())
      .flatMap(content => content.split('\n'))
      .filter(line => line.trim())
      .map(line => this.processor.parseLogEntry(line));

    const patterns = this.analyzer.detectPatterns(allEntries);
    const healthScore = this.analyzer.calculateHealthScore(summary);
    const recommendations = this.analyzer.generateRecommendations(summary, patterns);

    const result = this.analyzer.formatAnalysisResults(summary, patterns, healthScore, recommendations);

    this.logger.methodExit('getAdvancedAnalysis', {
      filesAnalyzed: logFiles.length,
      entriesProcessed: allEntries.length,
    });

    return result;
  }

  /**
   * Test WebDAV connection
   */
  async testConnection(): Promise<boolean> {
    return await this.webdavManager.testConnection();
  }

  /**
   * Get log statistics for a date range
   */
  async getLogStats(date?: string): Promise<string> {
    const targetDate = date ?? getCurrentDate();
    const stats = await this.fileDiscovery.getLogFileStats(targetDate);

    const sections = [
      `Log Statistics for ${targetDate}:`,
      '',
      '📊 Overview:',
      `- Total Files: ${stats.totalFiles}`,
      `- Files by Level: ${LogFormatter.formatLogLevelStats(stats.filesByLevel)}`,
      '',
      '📁 File Info:',
      `- Newest: ${stats.newestFile ?? 'N/A'}`,
      `- Oldest: ${stats.oldestFile ?? 'N/A'}`,
    ];

    return sections.join('\n');
  }

  /**
   * Get latest job log files
   */
  async getLatestJobLogFiles(limit?: number): Promise<string> {
    this.logger.methodEntry('getLatestJobLogFiles', { limit });

    try {
      const jobLogs = await this.fileDiscovery.getLatestJobLogFiles(limit);
      const result = LogFormatter.formatJobLogList(jobLogs);
      this.logger.methodExit('getLatestJobLogFiles', { count: jobLogs.length });
      return result;
    } catch (error) {
      const errorMessage = LogFormatter.formatError('get_latest_job_log_files', error);
      this.logger.error(errorMessage);
      this.logger.methodExit('getLatestJobLogFiles', { error: true });
      return errorMessage;
    }
  }

  /**
   * Search job logs by job name
   */
  async searchJobLogsByName(jobName: string, limit?: number): Promise<string> {
    this.logger.methodEntry('searchJobLogsByName', { jobName, limit });

    try {
      const jobLogs = await this.fileDiscovery.searchJobLogsByName(jobName, limit);
      const result = LogFormatter.formatJobLogList(jobLogs);
      this.logger.methodExit('searchJobLogsByName', { count: jobLogs.length });
      return result;
    } catch (error) {
      const errorMessage = LogFormatter.formatError('search_job_logs_by_name', error);
      this.logger.error(errorMessage);
      this.logger.methodExit('searchJobLogsByName', { error: true });
      return errorMessage;
    }
  }

  /**
   * Get job log entries for a specific log level or all levels
   */
  async getJobLogEntries(
    level: LogLevel | 'all' = 'all',
    limit: number = JOB_LOG_CONSTANTS.DEFAULT_JOB_LOG_LIMIT,
    jobName?: string,
  ): Promise<string> {
    this.logger.methodEntry('getJobLogEntries', { level, limit, jobName });

    try {
      // Get job logs based on filter
      const jobLogs = jobName
        ? await this.fileDiscovery.searchJobLogsByName(jobName, limit)
        : await this.fileDiscovery.getLatestJobLogFiles(limit);

      if (jobLogs.length === 0) {
        const result = jobName
          ? `No job logs found for job name: ${jobName}`
          : 'No job logs found';
        this.logger.methodExit('getJobLogEntries', { result: 'no_logs' });
        return result;
      }

      // Read job log contents
      const fileContents = await this.fileReader.readMultipleFiles(
        jobLogs.map(job => job.logFile),
        { maxBytes: LOG_CONSTANTS.DEFAULT_TAIL_BYTES },
      );

      // Process job log entries
      const jobLogEntries = await this.processor.processJobLogFiles(jobLogs, level, fileContents);
      const sortedEntries = this.processor.sortAndLimitEntries(jobLogEntries, limit);
      const latestEntries = this.processor.extractFormattedEntries(sortedEntries);

      // Format response
      const jobContext = jobName ? `job: ${jobName}` : 'latest jobs';
      const result = LogFormatter.formatJobLogEntries(latestEntries, level, limit, jobContext);

      this.logger.methodExit('getJobLogEntries', {
        entriesReturned: latestEntries.length,
        jobLogsProcessed: jobLogs.length,
      });

      return result;
    } catch (error) {
      const errorMessage = LogFormatter.formatError('get_job_log_entries', error);
      this.logger.error(errorMessage);
      this.logger.methodExit('getJobLogEntries', { error: true });
      return errorMessage;
    }
  }

  /**
   * Search for patterns in job logs
   */
  async searchJobLogs(
    pattern: string,
    level?: LogLevel | 'all',
    limit: number = LOG_CONSTANTS.DEFAULT_SEARCH_LIMIT,
    jobName?: string,
  ): Promise<string> {
    this.logger.methodEntry('searchJobLogs', { pattern, level, limit, jobName });

    try {
      // Get job logs based on filter
      const jobLogs = jobName
        ? await this.fileDiscovery.searchJobLogsByName(jobName)
        : await this.fileDiscovery.getLatestJobLogFiles();

      if (jobLogs.length === 0) {
        const result = jobName
          ? `No job logs found for job name: ${jobName}`
          : 'No job logs found';
        this.logger.methodExit('searchJobLogs', { result: 'no_logs' });
        return result;
      }

      // Read job log contents
      const fileContents = await this.fileReader.readMultipleFiles(
        jobLogs.map(job => job.logFile),
        { maxBytes: LOG_CONSTANTS.DEFAULT_TAIL_BYTES },
      );

      // Search for patterns in job logs
      const matches: string[] = [];
      for (const jobLog of jobLogs) {
        const content = fileContents.get(jobLog.logFile);
        if (!content) {
          continue;
        }

        const lines = content.split('\n');
        for (const line of lines) {
          if (line.toLowerCase().includes(pattern.toLowerCase()) && matches.length < limit) {
            // Filter by level if specified
            if (level && level !== 'all') {
              const levelUpper = level.toUpperCase();
              if (!line.includes(` ${levelUpper} `)) {
                continue;
              }
            }
            matches.push(`[${jobLog.jobName}] ${line.trim()}`);
          }
        }
      }

      const jobContext = jobName ? `job: ${jobName}` : 'job logs';
      const result = LogFormatter.formatJobSearchResults(matches, pattern, jobContext);

      this.logger.methodExit('searchJobLogs', { matchesFound: matches.length });
      return result;
    } catch (error) {
      const errorMessage = LogFormatter.formatError('search_job_logs', error);
      this.logger.error(errorMessage);
      this.logger.methodExit('searchJobLogs', { error: true });
      return errorMessage;
    }
  }

  /**
   * Get job execution summary for a specific job
   */
  async getJobExecutionSummary(jobName: string): Promise<string> {
    this.logger.methodEntry('getJobExecutionSummary', { jobName });

    try {
      const jobLogs = await this.fileDiscovery.searchJobLogsByName(jobName, 1);

      if (jobLogs.length === 0) {
        const result = `No job logs found for job name: ${jobName}`;
        this.logger.methodExit('getJobExecutionSummary', { result: 'no_logs' });
        return result;
      }

      const latestJobLog = jobLogs[0];
      const content = await this.fileReader.getFileContentsTail(latestJobLog.logFile, {
        maxBytes: LOG_CONSTANTS.DEFAULT_TAIL_BYTES,
      });

      const summary = this.processor.extractJobExecutionSummary(content);
      const result = LogFormatter.formatJobExecutionSummary(summary, jobName);

      this.logger.methodExit('getJobExecutionSummary', { jobLog: latestJobLog.logFile });
      return result;
    } catch (error) {
      const errorMessage = LogFormatter.formatError('get_job_execution_summary', error);
      this.logger.error(errorMessage);
      this.logger.methodExit('getJobExecutionSummary', { error: true });
      return errorMessage;
    }
  }
}

```

--------------------------------------------------------------------------------
/tests/utils.test.ts:
--------------------------------------------------------------------------------

```typescript
import {
  getCurrentDate,
  formatBytes,
  parseLogEntries,
  extractUniqueErrors,
  normalizeFilePath,
  extractTimestampFromLogEntry,
} from '../src/utils/utils';

describe('utils.ts', () => {
  describe('getCurrentDate', () => {
    it('should return current date in YYYYMMDD format', () => {
      const result = getCurrentDate();

      // Should be 8 characters long
      expect(result).toHaveLength(8);

      // Should match YYYYMMDD pattern
      expect(result).toMatch(/^\d{8}$/);

      // Should be a valid date when parsed
      const year = parseInt(result.substring(0, 4));
      const month = parseInt(result.substring(4, 6));
      const day = parseInt(result.substring(6, 8));

      expect(year).toBeGreaterThan(2020);
      expect(month).toBeGreaterThanOrEqual(1);
      expect(month).toBeLessThanOrEqual(12);
      expect(day).toBeGreaterThanOrEqual(1);
      expect(day).toBeLessThanOrEqual(31);
    });

    it('should return today\'s date', () => {
      const now = new Date();
      const expected = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`;

      expect(getCurrentDate()).toBe(expected);
    });

    it('should pad single digit months and days with zeros', () => {
      // Mock Date to return January 5th, 2023
      const mockDate = new Date('2023-01-05T10:00:00Z');
      jest.spyOn(global, 'Date').mockImplementation(() => mockDate);

      const result = getCurrentDate();
      expect(result).toBe('20230105');

      jest.restoreAllMocks();
    });
  });

  describe('formatBytes', () => {
    it('should format zero bytes', () => {
      expect(formatBytes(0)).toBe('0 Bytes');
    });

    it('should format bytes (less than 1024)', () => {
      expect(formatBytes(512)).toBe('512 Bytes');
      expect(formatBytes(1023)).toBe('1023 Bytes');
      expect(formatBytes(1)).toBe('1 Bytes');
    });

    it('should format kilobytes', () => {
      expect(formatBytes(1024)).toBe('1 KB');
      expect(formatBytes(1536)).toBe('1.5 KB'); // 1024 + 512
      expect(formatBytes(2048)).toBe('2 KB');
      expect(formatBytes(1024 * 1023)).toBe('1023 KB');
    });

    it('should format megabytes', () => {
      expect(formatBytes(1024 * 1024)).toBe('1 MB');
      expect(formatBytes(1024 * 1024 * 1.5)).toBe('1.5 MB');
      expect(formatBytes(1024 * 1024 * 2.75)).toBe('2.75 MB');
      expect(formatBytes(1024 * 1024 * 1023)).toBe('1023 MB');
    });

    it('should format gigabytes', () => {
      expect(formatBytes(1024 * 1024 * 1024)).toBe('1 GB');
      expect(formatBytes(1024 * 1024 * 1024 * 2.5)).toBe('2.5 GB');
      expect(formatBytes(1024 * 1024 * 1024 * 10.25)).toBe('10.25 GB');
    });

    it('should handle decimal values correctly', () => {
      expect(formatBytes(1536.7)).toBe('1.5 KB');
      expect(formatBytes(2097152.5)).toBe('2 MB');
    });

    it('should round to 2 decimal places', () => {
      expect(formatBytes(1126.4)).toBe('1.1 KB'); // 1126.4 / 1024 = 1.1000390625
      expect(formatBytes(1234567)).toBe('1.18 MB'); // Should round to 2 decimal places
    });

    it('should handle large numbers', () => {
      const largeNumber = 1024 * 1024 * 1024 * 1000; // 1TB in bytes
      expect(formatBytes(largeNumber)).toBe('1000 GB');
    });
  });

  describe('parseLogEntries', () => {
    it('should parse single log entry', () => {
      const content = '[2023-08-09T10:30:00.123 GMT] ERROR SomeClass - This is an error message';
      const result = parseLogEntries(content, 'ERROR');

      expect(result).toHaveLength(1);
      expect(result[0]).toBe('[2023-08-09T10:30:00.123 GMT] ERROR SomeClass - This is an error message');
    });

    it('should parse multiple log entries of same level', () => {
      const content = `[2023-08-09T10:30:00.123 GMT] ERROR Class1 - First error
[2023-08-09T10:31:00.456 GMT] ERROR Class2 - Second error
[2023-08-09T10:32:00.789 GMT] ERROR Class3 - Third error`;

      const result = parseLogEntries(content, 'ERROR');

      expect(result).toHaveLength(3);
      expect(result[0]).toBe('[2023-08-09T10:30:00.123 GMT] ERROR Class1 - First error');
      expect(result[1]).toBe('[2023-08-09T10:31:00.456 GMT] ERROR Class2 - Second error');
      expect(result[2]).toBe('[2023-08-09T10:32:00.789 GMT] ERROR Class3 - Third error');
    });

    it('should filter by log level', () => {
      const content = `[2023-08-09T10:30:00.123 GMT] ERROR Class1 - Error message
[2023-08-09T10:31:00.456 GMT] WARN Class2 - Warning message
[2023-08-09T10:32:00.789 GMT] INFO Class3 - Info message
[2023-08-09T10:33:00.012 GMT] ERROR Class4 - Another error`;

      const errorResult = parseLogEntries(content, 'ERROR');
      const warnResult = parseLogEntries(content, 'WARN');
      const infoResult = parseLogEntries(content, 'INFO');

      expect(errorResult).toHaveLength(2);
      expect(warnResult).toHaveLength(1);
      expect(infoResult).toHaveLength(1);

      expect(errorResult[0]).toContain('Error message');
      expect(errorResult[1]).toContain('Another error');
      expect(warnResult[0]).toContain('Warning message');
      expect(infoResult[0]).toContain('Info message');
    });

    it('should handle multi-line log entries', () => {
      const content = `[2023-08-09T10:30:00.123 GMT] ERROR Class1 - Error with stack trace
    at function1 (file1.js:10:5)
    at function2 (file2.js:20:3)
    at main (app.js:100:1)
[2023-08-09T10:31:00.456 GMT] ERROR Class2 - Another error`;

      const result = parseLogEntries(content, 'ERROR');

      expect(result).toHaveLength(2);
      expect(result[0]).toContain('Error with stack trace');
      expect(result[0]).toContain('at function1 (file1.js:10:5)');
      expect(result[0]).toContain('at function2 (file2.js:20:3)');
      expect(result[0]).toContain('at main (app.js:100:1)');
      expect(result[1]).toBe('[2023-08-09T10:31:00.456 GMT] ERROR Class2 - Another error');
    });

    it('should handle empty content', () => {
      expect(parseLogEntries('', 'ERROR')).toEqual([]);
      expect(parseLogEntries('   ', 'ERROR')).toEqual([]);
    });

    it('should handle content with no matching log level', () => {
      const content = `[2023-08-09T10:30:00.123 GMT] WARN Class1 - Warning message
[2023-08-09T10:31:00.456 GMT] INFO Class2 - Info message`;

      expect(parseLogEntries(content, 'ERROR')).toEqual([]);
    });

    it('should ignore lines that don\'t match the log pattern', () => {
      const content = `Some random text
[2023-08-09T10:30:00.123 GMT] ERROR Class1 - Valid error
[2023-08-09T10:31:00.456 GMT] ERROR Class2 - Another valid error`;

      const result = parseLogEntries(content, 'ERROR');

      expect(result).toHaveLength(2);
      expect(result[0]).toBe('[2023-08-09T10:30:00.123 GMT] ERROR Class1 - Valid error');
      expect(result[1]).toBe('[2023-08-09T10:31:00.456 GMT] ERROR Class2 - Another valid error');
    });

    it('should handle edge case with GMT requirement', () => {
      const content = `[2023-08-09T10:30:00.123] ERROR Class1 - Error without GMT
[2023-08-09T10:31:00.456 GMT] ERROR Class2 - Error with GMT`;

      const result = parseLogEntries(content, 'ERROR');

      expect(result).toHaveLength(1);
      expect(result[0]).toBe('[2023-08-09T10:31:00.456 GMT] ERROR Class2 - Error with GMT');
    });

    it('should trim entries properly', () => {
      const content = `  [2023-08-09T10:30:00.123 GMT] ERROR Class1 - Error with leading spaces  
  [2023-08-09T10:31:00.456 GMT] ERROR Class2 - Another error  `;

      const result = parseLogEntries(content, 'ERROR');

      expect(result).toHaveLength(2);
      expect(result[0]).toBe('[2023-08-09T10:30:00.123 GMT] ERROR Class1 - Error with leading spaces');
      expect(result[1]).toBe('[2023-08-09T10:31:00.456 GMT] ERROR Class2 - Another error');
    });
  });

  describe('extractUniqueErrors', () => {
    it('should extract unique error patterns', () => {
      const errors = [
        '[2023-08-09T10:30:00.123 GMT] ERROR ClassName1 - Database connection failed',
        '[2023-08-09T10:31:00.456 GMT] ERROR ClassName2 - File not found',
        '[2023-08-09T10:32:00.789 GMT] ERROR ClassName1 - Database connection failed',
        '[2023-08-09T10:33:00.012 GMT] ERROR ClassName3 - Permission denied',
      ];

      const result = extractUniqueErrors(errors);

      expect(result).toHaveLength(3);
      expect(result).toContain('Database connection failed');
      expect(result).toContain('File not found');
      expect(result).toContain('Permission denied');
    });

    it('should limit results to top 10 unique errors', () => {
      const errors = [];
      for (let i = 1; i <= 15; i++) {
        errors.push(`[2023-08-09T10:30:00.123 GMT] ERROR Class${i} - Error message ${i}`);
      }

      const result = extractUniqueErrors(errors);

      expect(result).toHaveLength(10);
    });

    it('should handle empty array', () => {
      expect(extractUniqueErrors([])).toEqual([]);
    });

    it('should handle errors without proper format', () => {
      const errors = [
        'Invalid log format',
        '[2023-08-09T10:30:00.123 GMT] ERROR ClassName - Valid error message',
        'Another invalid format',
      ];

      const result = extractUniqueErrors(errors);

      expect(result).toHaveLength(1);
      expect(result[0]).toBe('Valid error message');
    });

    it('should extract error message from multi-line entries', () => {
      const errors = [
        `[2023-08-09T10:30:00.123 GMT] ERROR ClassName - Connection timeout
    at database.connect()
    at service.initialize()`,
        '[2023-08-09T10:31:00.456 GMT] ERROR AnotherClass - File access denied',
      ];

      const result = extractUniqueErrors(errors);

      expect(result).toHaveLength(2);
      expect(result).toContain('Connection timeout');
      expect(result).toContain('File access denied');
    });

    it('should handle different class name formats', () => {
      const errors = [
        '[2023-08-09T10:30:00.123 GMT] ERROR dw.system.Pipeline - Pipeline execution failed',
        '[2023-08-09T10:31:00.456 GMT] ERROR CustomClass123 - Custom error occurred',
        '[2023-08-09T10:32:00.789 GMT] ERROR com.demandware.Core - Core system error',
      ];

      const result = extractUniqueErrors(errors);

      expect(result).toHaveLength(3);
      expect(result).toContain('Pipeline execution failed');
      expect(result).toContain('Custom error occurred');
      expect(result).toContain('Core system error');
    });

    it('should trim extracted error messages', () => {
      const errors = [
        '[2023-08-09T10:30:00.123 GMT] ERROR ClassName -   Error with extra spaces   ',
      ];

      const result = extractUniqueErrors(errors);

      expect(result).toHaveLength(1);
      expect(result[0]).toBe('Error with extra spaces');
    });

    it('should maintain order of first occurrence', () => {
      const errors = [
        '[2023-08-09T10:30:00.123 GMT] ERROR Class1 - Third error alphabetically',
        '[2023-08-09T10:31:00.456 GMT] ERROR Class2 - First error alphabetically',
        '[2023-08-09T10:32:00.789 GMT] ERROR Class3 - Second error alphabetically',
      ];

      const result = extractUniqueErrors(errors);

      expect(result).toHaveLength(3);
      expect(result[0]).toBe('Third error alphabetically');
      expect(result[1]).toBe('First error alphabetically');
      expect(result[2]).toBe('Second error alphabetically');
    });
  });

  describe('normalizeFilePath', () => {
    it('should remove leading slash from file path', () => {
      expect(normalizeFilePath('/path/to/file.js')).toBe('path/to/file.js');
      expect(normalizeFilePath('/single')).toBe('single');
      expect(normalizeFilePath('/deep/nested/path/file.txt')).toBe('deep/nested/path/file.txt');
    });

    it('should leave path unchanged if no leading slash', () => {
      expect(normalizeFilePath('path/to/file.js')).toBe('path/to/file.js');
      expect(normalizeFilePath('single')).toBe('single');
      expect(normalizeFilePath('deep/nested/path/file.txt')).toBe('deep/nested/path/file.txt');
    });

    it('should handle empty string', () => {
      expect(normalizeFilePath('')).toBe('');
    });

    it('should handle single slash', () => {
      expect(normalizeFilePath('/')).toBe('');
    });

    it('should handle multiple leading slashes (only remove first)', () => {
      expect(normalizeFilePath('//path/to/file')).toBe('/path/to/file');
      expect(normalizeFilePath('///path/to/file')).toBe('//path/to/file');
    });

    it('should handle paths with special characters', () => {
      expect(normalizeFilePath('/path/with spaces/file.js')).toBe('path/with spaces/file.js');
      expect(normalizeFilePath('/path/with-dashes/file_name.txt')).toBe('path/with-dashes/file_name.txt');
      expect(normalizeFilePath('/path/with.dots/file.name.ext')).toBe('path/with.dots/file.name.ext');
    });

    it('should handle Windows-style paths', () => {
      expect(normalizeFilePath('/C:/Windows/System32/file.dll')).toBe('C:/Windows/System32/file.dll');
      expect(normalizeFilePath('/folder\\subfolder\\file.txt')).toBe('folder\\subfolder\\file.txt');
    });

    it('should handle paths with query parameters and fragments', () => {
      expect(normalizeFilePath('/api/endpoint?param=value')).toBe('api/endpoint?param=value');
      expect(normalizeFilePath('/page.html#section')).toBe('page.html#section');
      expect(normalizeFilePath('/file.js?v=1.0.0&cache=false')).toBe('file.js?v=1.0.0&cache=false');
    });
  });

  describe('edge cases and integration', () => {
    it('should handle all functions with edge case inputs', () => {
      // Test all functions with various edge cases
      expect(() => getCurrentDate()).not.toThrow();
      expect(() => formatBytes(-1)).not.toThrow();
      expect(() => parseLogEntries('malformed log', 'INVALID')).not.toThrow();
      expect(() => extractUniqueErrors(['malformed'])).not.toThrow();
      expect(() => normalizeFilePath('  /path/with/spaces  ')).not.toThrow();
    });

    it('should handle realistic SFCC log parsing workflow', () => {
      const logContent = `[2023-08-09T10:30:00.123 GMT] INFO dw.system.Request - Request started
[2023-08-09T10:30:00.145 GMT] ERROR dw.catalog.ProductMgr - Product not found: ID-12345
    at ProductService.getProduct()
    at Controller.showPDP()
[2023-08-09T10:30:00.167 GMT] WARN dw.system.Cache - Cache miss for key: product-12345
[2023-08-09T10:30:00.189 GMT] ERROR dw.order.OrderMgr - Order creation failed: insufficient inventory
[2023-08-09T10:30:00.201 GMT] ERROR dw.catalog.ProductMgr - Product not found: ID-67890`;

      // Parse errors
      const errors = parseLogEntries(logContent, 'ERROR');
      expect(errors).toHaveLength(3);

      // Extract unique error patterns - there are actually 3 unique errors
      const uniqueErrors = extractUniqueErrors(errors);
      expect(uniqueErrors).toHaveLength(3);
      expect(uniqueErrors).toContain('Product not found: ID-12345');
      expect(uniqueErrors).toContain('Order creation failed: insufficient inventory');
      expect(uniqueErrors).toContain('Product not found: ID-67890');

      // Parse warnings
      const warnings = parseLogEntries(logContent, 'WARN');
      expect(warnings).toHaveLength(1);

      // Parse info
      const info = parseLogEntries(logContent, 'INFO');
      expect(info).toHaveLength(1);
    });

    it('should handle performance with large datasets', () => {
      // Test with larger datasets to ensure reasonable performance
      const largeLogContent = Array(1000).fill(0).map((_, i) =>
        `[2023-08-09T10:30:${String(i % 60).padStart(2, '0')}.123 GMT] ERROR Class${i % 10} - Error message ${i}`,
      ).join('\n');

      const start = Date.now();
      const errors = parseLogEntries(largeLogContent, 'ERROR');
      const uniqueErrors = extractUniqueErrors(errors);
      const duration = Date.now() - start;

      expect(errors).toHaveLength(1000);
      expect(uniqueErrors).toHaveLength(10); // Limited to 10 unique errors
      expect(duration).toBeLessThan(1000); // Should complete within 1 second
    });
  });

  describe('extractTimestampFromLogEntry', () => {
    it('should extract timestamp from valid log entry', () => {
      const logEntry = '[2025-08-19T10:30:00.000 GMT] ERROR Class - Test message';
      const result = extractTimestampFromLogEntry(logEntry);

      expect(result).toBeInstanceOf(Date);
      expect(result?.getUTCFullYear()).toBe(2025);
      expect(result?.getUTCMonth()).toBe(7); // 0-based months (August = 7)
      expect(result?.getUTCDate()).toBe(19);
      expect(result?.getUTCHours()).toBe(10);
      expect(result?.getUTCMinutes()).toBe(30);
    });

    it('should handle different time values', () => {
      const logEntry = '[2025-12-31T23:59:59.999 GMT] WARN Class - End of year';
      const result = extractTimestampFromLogEntry(logEntry);

      expect(result).toBeInstanceOf(Date);
      expect(result?.getUTCFullYear()).toBe(2025);
      expect(result?.getUTCMonth()).toBe(11); // December = 11
      expect(result?.getUTCDate()).toBe(31);
      expect(result?.getUTCHours()).toBe(23);
      expect(result?.getUTCMinutes()).toBe(59);
      expect(result?.getUTCSeconds()).toBe(59);
      expect(result?.getUTCMilliseconds()).toBe(999);
    });

    it('should return null for entry without timestamp', () => {
      const logEntry = 'ERROR Class - No timestamp here';
      const result = extractTimestampFromLogEntry(logEntry);

      expect(result).toBeNull();
    });

    it('should return null for malformed timestamp', () => {
      const logEntry = '[2025-13-45T25:70:70.000 GMT] ERROR Class - Invalid timestamp';
      const result = extractTimestampFromLogEntry(logEntry);

      expect(result).toBeNull();
    });

    it('should return null for entry without GMT marker', () => {
      const logEntry = '[2025-08-19T10:30:00.000] ERROR Class - No GMT marker';
      const result = extractTimestampFromLogEntry(logEntry);

      expect(result).toBeNull();
    });

    it('should handle entries with continuation lines', () => {
      const logEntry = '[2025-08-19T10:30:00.000 GMT] ERROR Class - Stack trace\n    at function1()\n    at function2()';
      const result = extractTimestampFromLogEntry(logEntry);

      expect(result).toBeInstanceOf(Date);
      expect(result?.getUTCHours()).toBe(10);
      expect(result?.getUTCMinutes()).toBe(30);
    });

    it('should handle edge case timestamps', () => {
      // Test midnight
      const midnightEntry = '[2025-01-01T00:00:00.000 GMT] INFO Class - Midnight';
      const midnightResult = extractTimestampFromLogEntry(midnightEntry);
      expect(midnightResult?.getUTCHours()).toBe(0);
      expect(midnightResult?.getUTCMinutes()).toBe(0);

      // Test leap year
      const leapYearEntry = '[2024-02-29T12:00:00.000 GMT] INFO Class - Leap year';
      const leapYearResult = extractTimestampFromLogEntry(leapYearEntry);
      expect(leapYearResult?.getMonth()).toBe(1); // February
      expect(leapYearResult?.getDate()).toBe(29);
    });
  });
});

```
Page 23/43FirstPrevNextLast