@vendure/mcp-plugin 3.8.0-minor-bootstrap.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/api-extensions.d.ts +2 -0
- package/lib/api/api-extensions.js +239 -0
- package/lib/api/api-extensions.js.map +1 -0
- package/lib/api/mcp-actor.service.d.ts +19 -0
- package/lib/api/mcp-actor.service.js +52 -0
- package/lib/api/mcp-actor.service.js.map +1 -0
- package/lib/api/mcp-admin.resolver.d.ts +105 -0
- package/lib/api/mcp-admin.resolver.js +463 -0
- package/lib/api/mcp-admin.resolver.js.map +1 -0
- package/lib/api/mcp-shop.resolver.d.ts +25 -0
- package/lib/api/mcp-shop.resolver.js +74 -0
- package/lib/api/mcp-shop.resolver.js.map +1 -0
- package/lib/check-options.d.ts +7 -0
- package/lib/check-options.js +130 -0
- package/lib/check-options.js.map +1 -0
- package/lib/constants.d.ts +48 -0
- package/lib/constants.js +72 -0
- package/lib/constants.js.map +1 -0
- package/lib/delete-in-batches.d.ts +5 -0
- package/lib/delete-in-batches.js +28 -0
- package/lib/delete-in-batches.js.map +1 -0
- package/lib/entities/index.d.ts +5 -0
- package/lib/entities/index.js +22 -0
- package/lib/entities/index.js.map +1 -0
- package/lib/entities/mcp-authorization-code.entity.d.ts +33 -0
- package/lib/entities/mcp-authorization-code.entity.js +77 -0
- package/lib/entities/mcp-authorization-code.entity.js.map +1 -0
- package/lib/entities/mcp-authorization-request.entity.d.ts +31 -0
- package/lib/entities/mcp-authorization-request.entity.js +73 -0
- package/lib/entities/mcp-authorization-request.entity.js.map +1 -0
- package/lib/entities/mcp-oauth-client.entity.d.ts +56 -0
- package/lib/entities/mcp-oauth-client.entity.js +70 -0
- package/lib/entities/mcp-oauth-client.entity.js.map +1 -0
- package/lib/entities/mcp-oauth-grant.entity.d.ts +77 -0
- package/lib/entities/mcp-oauth-grant.entity.js +121 -0
- package/lib/entities/mcp-oauth-grant.entity.js.map +1 -0
- package/lib/entities/mcp-tool-call-log.entity.d.ts +50 -0
- package/lib/entities/mcp-tool-call-log.entity.js +101 -0
- package/lib/entities/mcp-tool-call-log.entity.js.map +1 -0
- package/lib/events/mcp-tool-call.event.d.ts +17 -0
- package/lib/events/mcp-tool-call.event.js +23 -0
- package/lib/events/mcp-tool-call.event.js.map +1 -0
- package/lib/get-client-ip.d.ts +2 -0
- package/lib/get-client-ip.js +10 -0
- package/lib/get-client-ip.js.map +1 -0
- package/lib/get-language-code.d.ts +3 -0
- package/lib/get-language-code.js +12 -0
- package/lib/get-language-code.js.map +1 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +26 -0
- package/lib/index.js.map +1 -0
- package/lib/internal-types.d.ts +27 -0
- package/lib/internal-types.js +3 -0
- package/lib/internal-types.js.map +1 -0
- package/lib/logging/mcp-tool-call-log-retention.service.d.ts +8 -0
- package/lib/logging/mcp-tool-call-log-retention.service.js +54 -0
- package/lib/logging/mcp-tool-call-log-retention.service.js.map +1 -0
- package/lib/logging/mcp-tool-call-log.service.d.ts +24 -0
- package/lib/logging/mcp-tool-call-log.service.js +128 -0
- package/lib/logging/mcp-tool-call-log.service.js.map +1 -0
- package/lib/oauth/cimd/cimd-client-resolver.service.d.ts +18 -0
- package/lib/oauth/cimd/cimd-client-resolver.service.js +109 -0
- package/lib/oauth/cimd/cimd-client-resolver.service.js.map +1 -0
- package/lib/oauth/cimd/cimd-document.d.ts +11 -0
- package/lib/oauth/cimd/cimd-document.js +66 -0
- package/lib/oauth/cimd/cimd-document.js.map +1 -0
- package/lib/oauth/cimd/cimd-fetch.d.ts +14 -0
- package/lib/oauth/cimd/cimd-fetch.js +192 -0
- package/lib/oauth/cimd/cimd-fetch.js.map +1 -0
- package/lib/oauth/cimd/cimd-url.d.ts +5 -0
- package/lib/oauth/cimd/cimd-url.js +94 -0
- package/lib/oauth/cimd/cimd-url.js.map +1 -0
- package/lib/oauth/grant-session.service.d.ts +19 -0
- package/lib/oauth/grant-session.service.js +141 -0
- package/lib/oauth/grant-session.service.js.map +1 -0
- package/lib/oauth/oauth-error.d.ts +18 -0
- package/lib/oauth/oauth-error.js +60 -0
- package/lib/oauth/oauth-error.js.map +1 -0
- package/lib/oauth/oauth-metadata.service.d.ts +34 -0
- package/lib/oauth/oauth-metadata.service.js +107 -0
- package/lib/oauth/oauth-metadata.service.js.map +1 -0
- package/lib/oauth/oauth-retention.service.d.ts +23 -0
- package/lib/oauth/oauth-retention.service.js +103 -0
- package/lib/oauth/oauth-retention.service.js.map +1 -0
- package/lib/oauth/oauth-types.d.ts +61 -0
- package/lib/oauth/oauth-types.js +32 -0
- package/lib/oauth/oauth-types.js.map +1 -0
- package/lib/oauth/oauth-utils.d.ts +20 -0
- package/lib/oauth/oauth-utils.js +111 -0
- package/lib/oauth/oauth-utils.js.map +1 -0
- package/lib/oauth/oauth.controller.d.ts +31 -0
- package/lib/oauth/oauth.controller.js +132 -0
- package/lib/oauth/oauth.controller.js.map +1 -0
- package/lib/oauth/oauth.service.d.ts +69 -0
- package/lib/oauth/oauth.service.js +650 -0
- package/lib/oauth/oauth.service.js.map +1 -0
- package/lib/oauth/token-hash.d.ts +6 -0
- package/lib/oauth/token-hash.js +21 -0
- package/lib/oauth/token-hash.js.map +1 -0
- package/lib/plugin.d.ts +54 -0
- package/lib/plugin.js +171 -0
- package/lib/plugin.js.map +1 -0
- package/lib/rate-limit/ip-bucket-key.d.ts +1 -0
- package/lib/rate-limit/ip-bucket-key.js +35 -0
- package/lib/rate-limit/ip-bucket-key.js.map +1 -0
- package/lib/rate-limit/mcp-oauth-rate-limit.guard.d.ts +20 -0
- package/lib/rate-limit/mcp-oauth-rate-limit.guard.js +63 -0
- package/lib/rate-limit/mcp-oauth-rate-limit.guard.js.map +1 -0
- package/lib/rate-limit/mcp-rate-limiter.service.d.ts +47 -0
- package/lib/rate-limit/mcp-rate-limiter.service.js +248 -0
- package/lib/rate-limit/mcp-rate-limiter.service.js.map +1 -0
- package/lib/registry/bm25.d.ts +12 -0
- package/lib/registry/bm25.js +60 -0
- package/lib/registry/bm25.js.map +1 -0
- package/lib/registry/mcp-tool-execution.service.d.ts +45 -0
- package/lib/registry/mcp-tool-execution.service.js +72 -0
- package/lib/registry/mcp-tool-execution.service.js.map +1 -0
- package/lib/registry/mcp-tool-registry.service.d.ts +89 -0
- package/lib/registry/mcp-tool-registry.service.js +668 -0
- package/lib/registry/mcp-tool-registry.service.js.map +1 -0
- package/lib/registry/mcp-tool-schema.service.d.ts +42 -0
- package/lib/registry/mcp-tool-schema.service.js +229 -0
- package/lib/registry/mcp-tool-schema.service.js.map +1 -0
- package/lib/registry/registry-types.d.ts +32 -0
- package/lib/registry/registry-types.js +3 -0
- package/lib/registry/registry-types.js.map +1 -0
- package/lib/resolve-options.d.ts +7 -0
- package/lib/resolve-options.js +85 -0
- package/lib/resolve-options.js.map +1 -0
- package/lib/shared/endpoint-paths.d.ts +8 -0
- package/lib/shared/endpoint-paths.js +12 -0
- package/lib/shared/endpoint-paths.js.map +1 -0
- package/lib/shared/loopback.d.ts +2 -0
- package/lib/shared/loopback.js +9 -0
- package/lib/shared/loopback.js.map +1 -0
- package/lib/shop-session/mcp-shop-session.service.d.ts +47 -0
- package/lib/shop-session/mcp-shop-session.service.js +132 -0
- package/lib/shop-session/mcp-shop-session.service.js.map +1 -0
- package/lib/tasks/mcp-oauth-retention.task.d.ts +9 -0
- package/lib/tasks/mcp-oauth-retention.task.js +21 -0
- package/lib/tasks/mcp-oauth-retention.task.js.map +1 -0
- package/lib/tasks/mcp-tool-call-log-retention.task.d.ts +7 -0
- package/lib/tasks/mcp-tool-call-log-retention.task.js +22 -0
- package/lib/tasks/mcp-tool-call-log-retention.task.js.map +1 -0
- package/lib/tools/built-in/active-order.service.d.ts +26 -0
- package/lib/tools/built-in/active-order.service.js +125 -0
- package/lib/tools/built-in/active-order.service.js.map +1 -0
- package/lib/tools/built-in/admin/add-customer-to-group.tool.d.ts +23 -0
- package/lib/tools/built-in/admin/add-customer-to-group.tool.js +68 -0
- package/lib/tools/built-in/admin/add-customer-to-group.tool.js.map +1 -0
- package/lib/tools/built-in/admin/add-note-to-order.tool.d.ts +104 -0
- package/lib/tools/built-in/admin/add-note-to-order.tool.js +66 -0
- package/lib/tools/built-in/admin/add-note-to-order.tool.js.map +1 -0
- package/lib/tools/built-in/admin/adjust-stock.tool.d.ts +26 -0
- package/lib/tools/built-in/admin/adjust-stock.tool.js +88 -0
- package/lib/tools/built-in/admin/adjust-stock.tool.js.map +1 -0
- package/lib/tools/built-in/admin/cancel-order.tool.d.ts +17 -0
- package/lib/tools/built-in/admin/cancel-order.tool.js +60 -0
- package/lib/tools/built-in/admin/cancel-order.tool.js.map +1 -0
- package/lib/tools/built-in/admin/channel-access.d.ts +2 -0
- package/lib/tools/built-in/admin/channel-access.js +9 -0
- package/lib/tools/built-in/admin/channel-access.js.map +1 -0
- package/lib/tools/built-in/admin/create-customer.tool.d.ts +41 -0
- package/lib/tools/built-in/admin/create-customer.tool.js +57 -0
- package/lib/tools/built-in/admin/create-customer.tool.js.map +1 -0
- package/lib/tools/built-in/admin/create-fulfillment.tool.d.ts +118 -0
- package/lib/tools/built-in/admin/create-fulfillment.tool.js +156 -0
- package/lib/tools/built-in/admin/create-fulfillment.tool.js.map +1 -0
- package/lib/tools/built-in/admin/create-product.tool.d.ts +52 -0
- package/lib/tools/built-in/admin/create-product.tool.js +61 -0
- package/lib/tools/built-in/admin/create-product.tool.js.map +1 -0
- package/lib/tools/built-in/admin/create-variant.tool.d.ts +48 -0
- package/lib/tools/built-in/admin/create-variant.tool.js +79 -0
- package/lib/tools/built-in/admin/create-variant.tool.js.map +1 -0
- package/lib/tools/built-in/admin/entity-field-schemas.d.ts +36 -0
- package/lib/tools/built-in/admin/entity-field-schemas.js +63 -0
- package/lib/tools/built-in/admin/entity-field-schemas.js.map +1 -0
- package/lib/tools/built-in/admin/get-customer.tool.d.ts +28 -0
- package/lib/tools/built-in/admin/get-customer.tool.js +55 -0
- package/lib/tools/built-in/admin/get-customer.tool.js.map +1 -0
- package/lib/tools/built-in/admin/get-order.tool.d.ts +100 -0
- package/lib/tools/built-in/admin/get-order.tool.js +68 -0
- package/lib/tools/built-in/admin/get-order.tool.js.map +1 -0
- package/lib/tools/built-in/admin/get-product.tool.d.ts +69 -0
- package/lib/tools/built-in/admin/get-product.tool.js +77 -0
- package/lib/tools/built-in/admin/get-product.tool.js.map +1 -0
- package/lib/tools/built-in/admin/get-stock-levels.tool.d.ts +23 -0
- package/lib/tools/built-in/admin/get-stock-levels.tool.js +61 -0
- package/lib/tools/built-in/admin/get-stock-levels.tool.js.map +1 -0
- package/lib/tools/built-in/admin/index.d.ts +25 -0
- package/lib/tools/built-in/admin/index.js +54 -0
- package/lib/tools/built-in/admin/index.js.map +1 -0
- package/lib/tools/built-in/admin/list-channels.tool.d.ts +24 -0
- package/lib/tools/built-in/admin/list-channels.tool.js +56 -0
- package/lib/tools/built-in/admin/list-channels.tool.js.map +1 -0
- package/lib/tools/built-in/admin/list-customer-groups.tool.d.ts +23 -0
- package/lib/tools/built-in/admin/list-customer-groups.tool.js +52 -0
- package/lib/tools/built-in/admin/list-customer-groups.tool.js.map +1 -0
- package/lib/tools/built-in/admin/list-customers.tool.d.ts +62 -0
- package/lib/tools/built-in/admin/list-customers.tool.js +66 -0
- package/lib/tools/built-in/admin/list-customers.tool.js.map +1 -0
- package/lib/tools/built-in/admin/list-orders.tool.d.ts +142 -0
- package/lib/tools/built-in/admin/list-orders.tool.js +87 -0
- package/lib/tools/built-in/admin/list-orders.tool.js.map +1 -0
- package/lib/tools/built-in/admin/list-products.tool.d.ts +63 -0
- package/lib/tools/built-in/admin/list-products.tool.js +71 -0
- package/lib/tools/built-in/admin/list-products.tool.js.map +1 -0
- package/lib/tools/built-in/admin/refund-order.tool.d.ts +38 -0
- package/lib/tools/built-in/admin/refund-order.tool.js +115 -0
- package/lib/tools/built-in/admin/refund-order.tool.js.map +1 -0
- package/lib/tools/built-in/admin/set-active-channel.tool.d.ts +22 -0
- package/lib/tools/built-in/admin/set-active-channel.tool.js +84 -0
- package/lib/tools/built-in/admin/set-active-channel.tool.js.map +1 -0
- package/lib/tools/built-in/admin/translation-schemas.d.ts +12 -0
- package/lib/tools/built-in/admin/translation-schemas.js +17 -0
- package/lib/tools/built-in/admin/translation-schemas.js.map +1 -0
- package/lib/tools/built-in/admin/update-customer.tool.d.ts +42 -0
- package/lib/tools/built-in/admin/update-customer.tool.js +59 -0
- package/lib/tools/built-in/admin/update-customer.tool.js.map +1 -0
- package/lib/tools/built-in/admin/update-order-state.tool.d.ts +16 -0
- package/lib/tools/built-in/admin/update-order-state.tool.js +58 -0
- package/lib/tools/built-in/admin/update-order-state.tool.js.map +1 -0
- package/lib/tools/built-in/admin/update-product.tool.d.ts +53 -0
- package/lib/tools/built-in/admin/update-product.tool.js +68 -0
- package/lib/tools/built-in/admin/update-product.tool.js.map +1 -0
- package/lib/tools/built-in/admin/update-variant.tool.d.ts +46 -0
- package/lib/tools/built-in/admin/update-variant.tool.js +77 -0
- package/lib/tools/built-in/admin/update-variant.tool.js.map +1 -0
- package/lib/tools/built-in/admin/upload-asset.tool.d.ts +32 -0
- package/lib/tools/built-in/admin/upload-asset.tool.js +56 -0
- package/lib/tools/built-in/admin/upload-asset.tool.js.map +1 -0
- package/lib/tools/built-in/catalog-query.service.d.ts +21 -0
- package/lib/tools/built-in/catalog-query.service.js +128 -0
- package/lib/tools/built-in/catalog-query.service.js.map +1 -0
- package/lib/tools/built-in/custom-field-input.service.d.ts +10 -0
- package/lib/tools/built-in/custom-field-input.service.js +64 -0
- package/lib/tools/built-in/custom-field-input.service.js.map +1 -0
- package/lib/tools/built-in/email-schema.d.ts +2 -0
- package/lib/tools/built-in/email-schema.js +12 -0
- package/lib/tools/built-in/email-schema.js.map +1 -0
- package/lib/tools/built-in/id-schema.d.ts +3 -0
- package/lib/tools/built-in/id-schema.js +11 -0
- package/lib/tools/built-in/id-schema.js.map +1 -0
- package/lib/tools/built-in/int32-schema.d.ts +4 -0
- package/lib/tools/built-in/int32-schema.js +8 -0
- package/lib/tools/built-in/int32-schema.js.map +1 -0
- package/lib/tools/built-in/list-helpers.d.ts +48 -0
- package/lib/tools/built-in/list-helpers.js +76 -0
- package/lib/tools/built-in/list-helpers.js.map +1 -0
- package/lib/tools/built-in/order-list-helpers.d.ts +20 -0
- package/lib/tools/built-in/order-list-helpers.js +48 -0
- package/lib/tools/built-in/order-list-helpers.js.map +1 -0
- package/lib/tools/built-in/providers.d.ts +2 -0
- package/lib/tools/built-in/providers.js +7 -0
- package/lib/tools/built-in/providers.js.map +1 -0
- package/lib/tools/built-in/remote-asset.d.ts +4 -0
- package/lib/tools/built-in/remote-asset.js +54 -0
- package/lib/tools/built-in/remote-asset.js.map +1 -0
- package/lib/tools/built-in/serializer.service.d.ts +327 -0
- package/lib/tools/built-in/serializer.service.js +413 -0
- package/lib/tools/built-in/serializer.service.js.map +1 -0
- package/lib/tools/built-in/shop/add-to-cart.tool.d.ts +21 -0
- package/lib/tools/built-in/shop/add-to-cart.tool.js +100 -0
- package/lib/tools/built-in/shop/add-to-cart.tool.js.map +1 -0
- package/lib/tools/built-in/shop/address-schema.d.ts +15 -0
- package/lib/tools/built-in/shop/address-schema.js +20 -0
- package/lib/tools/built-in/shop/address-schema.js.map +1 -0
- package/lib/tools/built-in/shop/apply-coupon-code.tool.d.ts +17 -0
- package/lib/tools/built-in/shop/apply-coupon-code.tool.js +62 -0
- package/lib/tools/built-in/shop/apply-coupon-code.tool.js.map +1 -0
- package/lib/tools/built-in/shop/collection-lookup.d.ts +15 -0
- package/lib/tools/built-in/shop/collection-lookup.js +35 -0
- package/lib/tools/built-in/shop/collection-lookup.js.map +1 -0
- package/lib/tools/built-in/shop/get-cart.tool.d.ts +94 -0
- package/lib/tools/built-in/shop/get-cart.tool.js +55 -0
- package/lib/tools/built-in/shop/get-cart.tool.js.map +1 -0
- package/lib/tools/built-in/shop/get-collection.tool.d.ts +42 -0
- package/lib/tools/built-in/shop/get-collection.tool.js +64 -0
- package/lib/tools/built-in/shop/get-collection.tool.js.map +1 -0
- package/lib/tools/built-in/shop/get-eligible-payment-methods.tool.d.ts +26 -0
- package/lib/tools/built-in/shop/get-eligible-payment-methods.tool.js +58 -0
- package/lib/tools/built-in/shop/get-eligible-payment-methods.tool.js.map +1 -0
- package/lib/tools/built-in/shop/get-eligible-shipping-methods.tool.d.ts +30 -0
- package/lib/tools/built-in/shop/get-eligible-shipping-methods.tool.js +59 -0
- package/lib/tools/built-in/shop/get-eligible-shipping-methods.tool.js.map +1 -0
- package/lib/tools/built-in/shop/get-my-account.tool.d.ts +22 -0
- package/lib/tools/built-in/shop/get-my-account.tool.js +57 -0
- package/lib/tools/built-in/shop/get-my-account.tool.js.map +1 -0
- package/lib/tools/built-in/shop/get-order.tool.d.ts +100 -0
- package/lib/tools/built-in/shop/get-order.tool.js +67 -0
- package/lib/tools/built-in/shop/get-order.tool.js.map +1 -0
- package/lib/tools/built-in/shop/get-product.tool.d.ts +57 -0
- package/lib/tools/built-in/shop/get-product.tool.js +81 -0
- package/lib/tools/built-in/shop/get-product.tool.js.map +1 -0
- package/lib/tools/built-in/shop/index.d.ts +19 -0
- package/lib/tools/built-in/shop/index.js +42 -0
- package/lib/tools/built-in/shop/index.js.map +1 -0
- package/lib/tools/built-in/shop/list-collections.tool.d.ts +42 -0
- package/lib/tools/built-in/shop/list-collections.tool.js +75 -0
- package/lib/tools/built-in/shop/list-collections.tool.js.map +1 -0
- package/lib/tools/built-in/shop/list-my-orders.tool.d.ts +100 -0
- package/lib/tools/built-in/shop/list-my-orders.tool.js +63 -0
- package/lib/tools/built-in/shop/list-my-orders.tool.js.map +1 -0
- package/lib/tools/built-in/shop/place-order.tool.d.ts +188 -0
- package/lib/tools/built-in/shop/place-order.tool.js +152 -0
- package/lib/tools/built-in/shop/place-order.tool.js.map +1 -0
- package/lib/tools/built-in/shop/product-search-query.d.ts +4 -0
- package/lib/tools/built-in/shop/product-search-query.js +34 -0
- package/lib/tools/built-in/shop/product-search-query.js.map +1 -0
- package/lib/tools/built-in/shop/remove-coupon-code.tool.d.ts +17 -0
- package/lib/tools/built-in/shop/remove-coupon-code.tool.js +63 -0
- package/lib/tools/built-in/shop/remove-coupon-code.tool.js.map +1 -0
- package/lib/tools/built-in/shop/remove-from-cart.tool.d.ts +17 -0
- package/lib/tools/built-in/shop/remove-from-cart.tool.js +58 -0
- package/lib/tools/built-in/shop/remove-from-cart.tool.js.map +1 -0
- package/lib/tools/built-in/shop/search-products.tool.d.ts +48 -0
- package/lib/tools/built-in/shop/search-products.tool.js +95 -0
- package/lib/tools/built-in/shop/search-products.tool.js.map +1 -0
- package/lib/tools/built-in/shop/set-checkout-details.tool.d.ts +56 -0
- package/lib/tools/built-in/shop/set-checkout-details.tool.js +187 -0
- package/lib/tools/built-in/shop/set-checkout-details.tool.js.map +1 -0
- package/lib/tools/built-in/shop/set-shipping-method.tool.d.ts +17 -0
- package/lib/tools/built-in/shop/set-shipping-method.tool.js +58 -0
- package/lib/tools/built-in/shop/set-shipping-method.tool.js.map +1 -0
- package/lib/tools/built-in/shop/update-cart-line.tool.d.ts +18 -0
- package/lib/tools/built-in/shop/update-cart-line.tool.js +60 -0
- package/lib/tools/built-in/shop/update-cart-line.tool.js.map +1 -0
- package/lib/tools/built-in/string-schemas.d.ts +4 -0
- package/lib/tools/built-in/string-schemas.js +13 -0
- package/lib/tools/built-in/string-schemas.js.map +1 -0
- package/lib/transport/mcp-server.factory.d.ts +13 -0
- package/lib/transport/mcp-server.factory.js +32 -0
- package/lib/transport/mcp-server.factory.js.map +1 -0
- package/lib/transport/mcp-transport.controller.d.ts +77 -0
- package/lib/transport/mcp-transport.controller.js +442 -0
- package/lib/transport/mcp-transport.controller.js.map +1 -0
- package/lib/types.d.ts +432 -0
- package/lib/types.js +3 -0
- package/lib/types.js.map +1 -0
- package/package.json +42 -0
- package/src/dashboard/components/activity-block.tsx +72 -0
- package/src/dashboard/components/actor-cell.tsx +31 -0
- package/src/dashboard/components/connection-dialog.tsx +106 -0
- package/src/dashboard/components/empty-cell.tsx +7 -0
- package/src/dashboard/components/grants-block.tsx +218 -0
- package/src/dashboard/components/stats-block.tsx +275 -0
- package/src/dashboard/components/tools-block.tsx +274 -0
- package/src/dashboard/components/tooltip-button.tsx +28 -0
- package/src/dashboard/components/use-mcp-table-state.ts +47 -0
- package/src/dashboard/i18n/ar.po +311 -0
- package/src/dashboard/i18n/bg.po +311 -0
- package/src/dashboard/i18n/cs.po +311 -0
- package/src/dashboard/i18n/de.po +311 -0
- package/src/dashboard/i18n/en.po +311 -0
- package/src/dashboard/i18n/es.po +311 -0
- package/src/dashboard/i18n/fa.po +311 -0
- package/src/dashboard/i18n/fr.po +311 -0
- package/src/dashboard/i18n/he.po +311 -0
- package/src/dashboard/i18n/hr.po +311 -0
- package/src/dashboard/i18n/hu.po +311 -0
- package/src/dashboard/i18n/it.po +311 -0
- package/src/dashboard/i18n/ja.po +311 -0
- package/src/dashboard/i18n/ko.po +311 -0
- package/src/dashboard/i18n/nb.po +311 -0
- package/src/dashboard/i18n/ne.po +311 -0
- package/src/dashboard/i18n/nl.po +311 -0
- package/src/dashboard/i18n/pl.po +311 -0
- package/src/dashboard/i18n/pt_BR.po +311 -0
- package/src/dashboard/i18n/pt_PT.po +311 -0
- package/src/dashboard/i18n/ro.po +311 -0
- package/src/dashboard/i18n/ru.po +311 -0
- package/src/dashboard/i18n/sv.po +311 -0
- package/src/dashboard/i18n/tr.po +311 -0
- package/src/dashboard/i18n/uk.po +311 -0
- package/src/dashboard/i18n/uz.po +311 -0
- package/src/dashboard/i18n/zh_Hans.po +311 -0
- package/src/dashboard/i18n/zh_Hant.po +311 -0
- package/src/dashboard/index.tsx +30 -0
- package/src/dashboard/mcp-activity-route.tsx +38 -0
- package/src/dashboard/mcp-authorize-route.tsx +369 -0
- package/src/dashboard/mcp-grants-route.tsx +38 -0
- package/src/dashboard/mcp-overview-route.tsx +46 -0
- package/src/dashboard/mcp-server-redirect-route.tsx +15 -0
- package/src/dashboard/mcp-tools-route.tsx +38 -0
- package/src/dashboard/mcp.graphql.ts +127 -0
- package/src/dashboard/published-files.spec.ts +54 -0
- package/src/dashboard/tsconfig.json +9 -0
- package/src/shared/endpoint-paths.ts +8 -0
- package/src/shared/loopback.ts +6 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { Trans, useLingui } from '@lingui/react/macro';
|
|
2
|
+
import {
|
|
3
|
+
Alert,
|
|
4
|
+
AlertDescription,
|
|
5
|
+
api,
|
|
6
|
+
Badge,
|
|
7
|
+
Button,
|
|
8
|
+
Card,
|
|
9
|
+
CardContent,
|
|
10
|
+
CardDescription,
|
|
11
|
+
CardFooter,
|
|
12
|
+
CardHeader,
|
|
13
|
+
CardTitle,
|
|
14
|
+
ChannelCodeLabel,
|
|
15
|
+
CopyableText,
|
|
16
|
+
DashboardRouteDefinition,
|
|
17
|
+
PermissionGuard,
|
|
18
|
+
Separator,
|
|
19
|
+
Skeleton,
|
|
20
|
+
useAuth,
|
|
21
|
+
useChannel,
|
|
22
|
+
useMutation,
|
|
23
|
+
useQuery,
|
|
24
|
+
z,
|
|
25
|
+
} from '@vendure/dashboard';
|
|
26
|
+
import { AlertTriangleIcon, ArrowRightIcon, ShieldCheckIcon } from 'lucide-react';
|
|
27
|
+
|
|
28
|
+
import { OAUTH_ENDPOINT_PATHS } from '../shared/endpoint-paths';
|
|
29
|
+
import { isLoopbackHostname } from '../shared/loopback';
|
|
30
|
+
|
|
31
|
+
import { TooltipButton } from './components/tooltip-button';
|
|
32
|
+
import { authorizeMcpClientMutation } from './mcp.graphql';
|
|
33
|
+
|
|
34
|
+
// Mirrors `AuthorizationRequestInfo` from the OAuth service.
|
|
35
|
+
interface AuthRequestInfo {
|
|
36
|
+
client_id: string;
|
|
37
|
+
client_id_source: 'cimd' | 'dcr';
|
|
38
|
+
client_name: string;
|
|
39
|
+
client_uri?: string;
|
|
40
|
+
logo_uri?: string;
|
|
41
|
+
redirect_uri: string;
|
|
42
|
+
resource: string;
|
|
43
|
+
toolset: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function hostnameOf(uri: string): string | null {
|
|
47
|
+
try {
|
|
48
|
+
return new URL(uri).hostname || null;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// One shell for loading, failed and real-request states, so the page doesn't jump around.
|
|
55
|
+
function ConsentShell({
|
|
56
|
+
title,
|
|
57
|
+
description,
|
|
58
|
+
children,
|
|
59
|
+
footer,
|
|
60
|
+
}: {
|
|
61
|
+
title: React.ReactNode;
|
|
62
|
+
description?: React.ReactNode;
|
|
63
|
+
children?: React.ReactNode;
|
|
64
|
+
footer?: React.ReactNode;
|
|
65
|
+
}) {
|
|
66
|
+
return (
|
|
67
|
+
<Card className="w-full max-w-lg">
|
|
68
|
+
<CardHeader>
|
|
69
|
+
<CardTitle>{title}</CardTitle>
|
|
70
|
+
{description ? <CardDescription className="pt-2">{description}</CardDescription> : null}
|
|
71
|
+
</CardHeader>
|
|
72
|
+
{children ? <CardContent className="space-y-4">{children}</CardContent> : null}
|
|
73
|
+
{footer}
|
|
74
|
+
</Card>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Sits by the domain, not the name, because the display name is always chosen by the client itself.
|
|
79
|
+
function HostnameTrustBadge({ verified, metadataUrl }: { verified: boolean; metadataUrl?: string }) {
|
|
80
|
+
return (
|
|
81
|
+
<TooltipButton
|
|
82
|
+
className="inline-flex h-auto p-0"
|
|
83
|
+
tooltipClassName="max-w-sm space-y-1"
|
|
84
|
+
tooltip={
|
|
85
|
+
verified ? (
|
|
86
|
+
<>
|
|
87
|
+
<p>
|
|
88
|
+
<Trans>
|
|
89
|
+
The client's details were fetched from this address, so the hostname is
|
|
90
|
+
verified. The name above is still chosen by the client itself.
|
|
91
|
+
</Trans>
|
|
92
|
+
</p>
|
|
93
|
+
{metadataUrl ? <p className="font-mono break-all">{metadataUrl}</p> : null}
|
|
94
|
+
</>
|
|
95
|
+
) : (
|
|
96
|
+
<p>
|
|
97
|
+
<Trans>
|
|
98
|
+
This name and address are reported by the application itself and are not verified.
|
|
99
|
+
</Trans>
|
|
100
|
+
</p>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
>
|
|
104
|
+
{verified ? (
|
|
105
|
+
<Badge variant="success">
|
|
106
|
+
<ShieldCheckIcon className="h-3 w-3" />
|
|
107
|
+
<Trans>Hostname verified</Trans>
|
|
108
|
+
</Badge>
|
|
109
|
+
) : (
|
|
110
|
+
<Badge variant="warning">
|
|
111
|
+
<Trans>Unverified</Trans>
|
|
112
|
+
</Badge>
|
|
113
|
+
)}
|
|
114
|
+
</TooltipButton>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// `account`, the signed-in administrator, lets the sentence name whose access is handed over.
|
|
119
|
+
function scopePresentation(toolset: string, account?: { name: string; emailAddress: string }) {
|
|
120
|
+
if (toolset === 'shop') {
|
|
121
|
+
return {
|
|
122
|
+
request: <Trans>wants to access the Shop API of your Vendure server</Trans>,
|
|
123
|
+
grant: <Trans>Access to the Shop API as a customer.</Trans>,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const request = <Trans>wants to access the Admin API of your Vendure server</Trans>;
|
|
127
|
+
if (!account) {
|
|
128
|
+
return {
|
|
129
|
+
request,
|
|
130
|
+
grant: <Trans>Full access to the Admin API as your administrator account.</Trans>,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const { name, emailAddress } = account;
|
|
134
|
+
return {
|
|
135
|
+
request,
|
|
136
|
+
grant: (
|
|
137
|
+
<Trans>
|
|
138
|
+
Full access to the Admin API as {name} ({emailAddress}).
|
|
139
|
+
</Trans>
|
|
140
|
+
),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function ConsentDetails({
|
|
145
|
+
info,
|
|
146
|
+
onApprove,
|
|
147
|
+
onDeny,
|
|
148
|
+
isPending,
|
|
149
|
+
error,
|
|
150
|
+
}: {
|
|
151
|
+
info: AuthRequestInfo;
|
|
152
|
+
onApprove: () => void;
|
|
153
|
+
onDeny: () => void;
|
|
154
|
+
isPending: boolean;
|
|
155
|
+
error: Error | null;
|
|
156
|
+
}) {
|
|
157
|
+
const { t } = useLingui();
|
|
158
|
+
const { user } = useAuth();
|
|
159
|
+
// Only used to name the account in the scope sentence.
|
|
160
|
+
const signedInAccount = user
|
|
161
|
+
? { name: `${user.firstName} ${user.lastName}`.trim(), emailAddress: user.emailAddress }
|
|
162
|
+
: undefined;
|
|
163
|
+
// The grant is bound to whichever channel the dashboard's channel selector is on.
|
|
164
|
+
const { activeChannel } = useChannel();
|
|
165
|
+
|
|
166
|
+
const redirectHost = hostnameOf(info.redirect_uri);
|
|
167
|
+
const isCimdClient = info.client_id_source === 'cimd';
|
|
168
|
+
// A CIMD client's address is the client_id URL its metadata came from; others just report a URI.
|
|
169
|
+
const clientAddress = isCimdClient ? info.client_id : info.client_uri;
|
|
170
|
+
const clientDomain = clientAddress ? (hostnameOf(clientAddress) ?? clientAddress) : null;
|
|
171
|
+
const scope = scopePresentation(info.toolset, signedInAccount);
|
|
172
|
+
// Amber flags a redirect worth a second look: unverified, plain-http, local, or off the client's host.
|
|
173
|
+
const isLoopbackRedirect = redirectHost != null && isLoopbackHostname(redirectHost);
|
|
174
|
+
const clientHost = isCimdClient ? hostnameOf(info.client_id) : null;
|
|
175
|
+
const redirectLeavesClientHost = clientHost != null && redirectHost !== clientHost;
|
|
176
|
+
const redirectNeedsWarning =
|
|
177
|
+
!isCimdClient ||
|
|
178
|
+
isLoopbackRedirect ||
|
|
179
|
+
redirectLeavesClientHost ||
|
|
180
|
+
!info.redirect_uri.startsWith('https://');
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<ConsentShell
|
|
184
|
+
title={info.client_name?.trim() || t`Unnamed application`}
|
|
185
|
+
description={
|
|
186
|
+
<div className="space-y-1">
|
|
187
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
188
|
+
{clientDomain ? <span className="break-all">{clientDomain}</span> : null}
|
|
189
|
+
<HostnameTrustBadge
|
|
190
|
+
verified={isCimdClient}
|
|
191
|
+
metadataUrl={isCimdClient ? info.client_id : undefined}
|
|
192
|
+
/>
|
|
193
|
+
</div>
|
|
194
|
+
<div>{scope.request}</div>
|
|
195
|
+
</div>
|
|
196
|
+
}
|
|
197
|
+
footer={
|
|
198
|
+
<>
|
|
199
|
+
{error != null ? (
|
|
200
|
+
<CardContent className="w-full pt-0">
|
|
201
|
+
<Alert variant="destructive">
|
|
202
|
+
<AlertTriangleIcon className="h-4 w-4" />
|
|
203
|
+
<AlertDescription>
|
|
204
|
+
<div>
|
|
205
|
+
<Trans>The request could not be completed</Trans>
|
|
206
|
+
</div>
|
|
207
|
+
<div className="break-all">{error.message}</div>
|
|
208
|
+
</AlertDescription>
|
|
209
|
+
</Alert>
|
|
210
|
+
</CardContent>
|
|
211
|
+
) : null}
|
|
212
|
+
<CardFooter className="justify-end gap-2">
|
|
213
|
+
<Button variant="outline" disabled={isPending} onClick={onDeny}>
|
|
214
|
+
<Trans>Deny</Trans>
|
|
215
|
+
</Button>
|
|
216
|
+
<Button disabled={isPending} onClick={onApprove}>
|
|
217
|
+
<Trans>Approve</Trans>
|
|
218
|
+
</Button>
|
|
219
|
+
</CardFooter>
|
|
220
|
+
</>
|
|
221
|
+
}
|
|
222
|
+
>
|
|
223
|
+
{/* Only flagged amber when there is something to be wary of, so the warning keeps its meaning. */}
|
|
224
|
+
<div
|
|
225
|
+
className={
|
|
226
|
+
redirectNeedsWarning
|
|
227
|
+
? 'space-y-2 rounded-md border border-warning/40 bg-warning/5 p-3'
|
|
228
|
+
: 'space-y-2 rounded-md border border-border p-3'
|
|
229
|
+
}
|
|
230
|
+
>
|
|
231
|
+
<div className="flex items-start gap-2">
|
|
232
|
+
{redirectNeedsWarning ? (
|
|
233
|
+
<AlertTriangleIcon className="h-4 w-4 shrink-0 text-warning" />
|
|
234
|
+
) : (
|
|
235
|
+
<ArrowRightIcon className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
236
|
+
)}
|
|
237
|
+
<CopyableText value={info.redirect_uri}>
|
|
238
|
+
<code className="font-mono text-xs break-all">{info.redirect_uri}</code>
|
|
239
|
+
</CopyableText>
|
|
240
|
+
</div>
|
|
241
|
+
<p className="text-xs text-muted-foreground">
|
|
242
|
+
<Trans>
|
|
243
|
+
After approval, the authorization code is sent here. Only approve if you recognise
|
|
244
|
+
this destination.
|
|
245
|
+
</Trans>
|
|
246
|
+
</p>
|
|
247
|
+
{isLoopbackRedirect ? (
|
|
248
|
+
<p className="text-xs font-medium text-warning">
|
|
249
|
+
<Trans>
|
|
250
|
+
This destination is on the local machine. Any application running on that machine
|
|
251
|
+
could receive the authorization code.
|
|
252
|
+
</Trans>
|
|
253
|
+
</p>
|
|
254
|
+
) : null}
|
|
255
|
+
{redirectLeavesClientHost ? (
|
|
256
|
+
<p className="text-xs font-medium text-warning">
|
|
257
|
+
<Trans>
|
|
258
|
+
This destination is not on the application's own host ({clientHost}). Only approve
|
|
259
|
+
if you expect the code to be sent there.
|
|
260
|
+
</Trans>
|
|
261
|
+
</p>
|
|
262
|
+
) : null}
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<Separator />
|
|
266
|
+
|
|
267
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
268
|
+
<Badge variant="secondary">{info.toolset}</Badge>
|
|
269
|
+
<span className="text-sm text-muted-foreground">{scope.grant}</span>
|
|
270
|
+
</div>
|
|
271
|
+
{info.toolset === 'admin' && activeChannel ? (
|
|
272
|
+
<p className="text-sm text-muted-foreground">
|
|
273
|
+
<Trans>The client will act on this channel:</Trans>{' '}
|
|
274
|
+
<span className="font-medium text-foreground">
|
|
275
|
+
<ChannelCodeLabel code={activeChannel.code} />
|
|
276
|
+
</span>
|
|
277
|
+
</p>
|
|
278
|
+
) : null}
|
|
279
|
+
</ConsentShell>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function ConsentCard({ requestToken }: { requestToken: string }) {
|
|
284
|
+
const { t } = useLingui();
|
|
285
|
+
|
|
286
|
+
const {
|
|
287
|
+
data: info,
|
|
288
|
+
error,
|
|
289
|
+
isLoading,
|
|
290
|
+
} = useQuery({
|
|
291
|
+
queryKey: ['mcp-authorization-request', requestToken],
|
|
292
|
+
queryFn: async (): Promise<AuthRequestInfo> => {
|
|
293
|
+
const res = await fetch(
|
|
294
|
+
`/${OAUTH_ENDPOINT_PATHS.authorizationRequest}?request_token=${encodeURIComponent(requestToken)}`,
|
|
295
|
+
{ credentials: 'include' },
|
|
296
|
+
);
|
|
297
|
+
if (!res.ok) {
|
|
298
|
+
throw new Error(t`Request failed (${res.status})`);
|
|
299
|
+
}
|
|
300
|
+
return res.json() as Promise<AuthRequestInfo>;
|
|
301
|
+
},
|
|
302
|
+
// Don't retry: expired/used requests will always fail
|
|
303
|
+
retry: false,
|
|
304
|
+
// Clear previous data to avoid showing the wrong app while loading
|
|
305
|
+
placeholderData: undefined,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const authorize = useMutation({
|
|
309
|
+
mutationFn: (approved: boolean) => api.mutate(authorizeMcpClientMutation, { requestToken, approved }),
|
|
310
|
+
onSuccess: data => {
|
|
311
|
+
window.location.href = data.authorizeMcpClient.redirectUrl;
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
if (isLoading) {
|
|
316
|
+
return (
|
|
317
|
+
<ConsentShell
|
|
318
|
+
title={<Trans>Authorize MCP access</Trans>}
|
|
319
|
+
description={<Trans>Loading authorization request…</Trans>}
|
|
320
|
+
>
|
|
321
|
+
<Skeleton className="h-20 w-full" />
|
|
322
|
+
<Skeleton className="h-5 w-2/3" />
|
|
323
|
+
</ConsentShell>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (error || !info) {
|
|
328
|
+
return (
|
|
329
|
+
<ConsentShell title={<Trans>Authorization request could not be loaded</Trans>}>
|
|
330
|
+
<p className="text-sm">
|
|
331
|
+
<Trans>
|
|
332
|
+
The link may have expired or already been used. Start the connection again from your
|
|
333
|
+
MCP client.
|
|
334
|
+
</Trans>
|
|
335
|
+
</p>
|
|
336
|
+
{/* Shown as a detail, not the reason, since the response doesn't say why it failed. */}
|
|
337
|
+
{error?.message ? (
|
|
338
|
+
<p className="text-xs text-muted-foreground break-all">{error.message}</p>
|
|
339
|
+
) : null}
|
|
340
|
+
</ConsentShell>
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return (
|
|
345
|
+
<ConsentDetails
|
|
346
|
+
info={info}
|
|
347
|
+
onApprove={() => authorize.mutate(true)}
|
|
348
|
+
onDeny={() => authorize.mutate(false)}
|
|
349
|
+
isPending={authorize.isPending}
|
|
350
|
+
error={authorize.error}
|
|
351
|
+
/>
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export const mcpAuthorizeRoute: DashboardRouteDefinition = {
|
|
356
|
+
path: '/mcp/authorize',
|
|
357
|
+
loader: () => ({ breadcrumb: () => <Trans>Authorize MCP Client</Trans> }),
|
|
358
|
+
validateSearch: search => z.object({ request_token: z.string() }).parse(search),
|
|
359
|
+
component: route => {
|
|
360
|
+
const { request_token: requestToken } = route.useSearch();
|
|
361
|
+
return (
|
|
362
|
+
<PermissionGuard requires={['UpdateMcpServer']}>
|
|
363
|
+
<div className="flex justify-center p-8">
|
|
364
|
+
<ConsentCard requestToken={requestToken} />
|
|
365
|
+
</div>
|
|
366
|
+
</PermissionGuard>
|
|
367
|
+
);
|
|
368
|
+
},
|
|
369
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Trans } from '@lingui/react/macro';
|
|
2
|
+
import {
|
|
3
|
+
DashboardRouteDefinition,
|
|
4
|
+
Page,
|
|
5
|
+
PageBlock,
|
|
6
|
+
PageLayout,
|
|
7
|
+
PageTitle,
|
|
8
|
+
PermissionGuard,
|
|
9
|
+
} from '@vendure/dashboard';
|
|
10
|
+
|
|
11
|
+
import { GrantsBlock } from './components/grants-block';
|
|
12
|
+
|
|
13
|
+
export const mcpGrantsRoute: DashboardRouteDefinition = {
|
|
14
|
+
navMenuItem: {
|
|
15
|
+
sectionId: 'mcp-server',
|
|
16
|
+
id: 'mcp-server-grants',
|
|
17
|
+
url: '/mcp-server/grants',
|
|
18
|
+
title: 'Grants',
|
|
19
|
+
order: 400,
|
|
20
|
+
requiresPermission: 'ReadMcpServer',
|
|
21
|
+
},
|
|
22
|
+
path: '/mcp-server/grants',
|
|
23
|
+
loader: () => ({ breadcrumb: () => <Trans>Grants</Trans> }),
|
|
24
|
+
component: () => (
|
|
25
|
+
<Page pageId="mcp-server-grants">
|
|
26
|
+
<PageTitle>
|
|
27
|
+
<Trans>Grants</Trans>
|
|
28
|
+
</PageTitle>
|
|
29
|
+
<PermissionGuard requires={['ReadMcpServer']}>
|
|
30
|
+
<PageLayout>
|
|
31
|
+
<PageBlock column="full" blockId="mcp-grants">
|
|
32
|
+
<GrantsBlock />
|
|
33
|
+
</PageBlock>
|
|
34
|
+
</PageLayout>
|
|
35
|
+
</PermissionGuard>
|
|
36
|
+
</Page>
|
|
37
|
+
),
|
|
38
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Trans } from '@lingui/react/macro';
|
|
2
|
+
import {
|
|
3
|
+
ActionBarItem,
|
|
4
|
+
DashboardRouteDefinition,
|
|
5
|
+
Page,
|
|
6
|
+
PageActionBar,
|
|
7
|
+
PageBlock,
|
|
8
|
+
PageLayout,
|
|
9
|
+
PageTitle,
|
|
10
|
+
PermissionGuard,
|
|
11
|
+
} from '@vendure/dashboard';
|
|
12
|
+
|
|
13
|
+
import { ConnectionDialog } from './components/connection-dialog';
|
|
14
|
+
import { StatsBlock } from './components/stats-block';
|
|
15
|
+
|
|
16
|
+
export const mcpOverviewRoute: DashboardRouteDefinition = {
|
|
17
|
+
navMenuItem: {
|
|
18
|
+
sectionId: 'mcp-server',
|
|
19
|
+
id: 'mcp-server-overview',
|
|
20
|
+
url: '/mcp-server/overview',
|
|
21
|
+
title: 'Overview',
|
|
22
|
+
order: 100,
|
|
23
|
+
requiresPermission: 'ReadMcpServer',
|
|
24
|
+
},
|
|
25
|
+
path: '/mcp-server/overview',
|
|
26
|
+
loader: () => ({ breadcrumb: () => <Trans>Overview</Trans> }),
|
|
27
|
+
component: () => (
|
|
28
|
+
<Page pageId="mcp-server-overview">
|
|
29
|
+
<PageTitle>
|
|
30
|
+
<Trans>MCP Server</Trans>
|
|
31
|
+
</PageTitle>
|
|
32
|
+
<PageActionBar>
|
|
33
|
+
<ActionBarItem itemId="mcp-connect-client">
|
|
34
|
+
<ConnectionDialog />
|
|
35
|
+
</ActionBarItem>
|
|
36
|
+
</PageActionBar>
|
|
37
|
+
<PermissionGuard requires={['ReadMcpServer']}>
|
|
38
|
+
<PageLayout>
|
|
39
|
+
<PageBlock column="full" blockId="mcp-stats" title={<Trans>Health & usage</Trans>}>
|
|
40
|
+
<StatsBlock />
|
|
41
|
+
</PageBlock>
|
|
42
|
+
</PageLayout>
|
|
43
|
+
</PermissionGuard>
|
|
44
|
+
</Page>
|
|
45
|
+
),
|
|
46
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DashboardRouteDefinition, useNavigate } from '@vendure/dashboard';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
function McpServerRedirect() {
|
|
5
|
+
const navigate = useNavigate();
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
void navigate({ to: '/mcp-server/overview', replace: true });
|
|
8
|
+
}, [navigate]);
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const mcpServerRedirectRoute: DashboardRouteDefinition = {
|
|
13
|
+
path: '/mcp-server',
|
|
14
|
+
component: () => <McpServerRedirect />,
|
|
15
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Trans } from '@lingui/react/macro';
|
|
2
|
+
import {
|
|
3
|
+
DashboardRouteDefinition,
|
|
4
|
+
Page,
|
|
5
|
+
PageBlock,
|
|
6
|
+
PageLayout,
|
|
7
|
+
PageTitle,
|
|
8
|
+
PermissionGuard,
|
|
9
|
+
} from '@vendure/dashboard';
|
|
10
|
+
|
|
11
|
+
import { ToolsBlock } from './components/tools-block';
|
|
12
|
+
|
|
13
|
+
export const mcpToolsRoute: DashboardRouteDefinition = {
|
|
14
|
+
navMenuItem: {
|
|
15
|
+
sectionId: 'mcp-server',
|
|
16
|
+
id: 'mcp-server-tools',
|
|
17
|
+
url: '/mcp-server/tools',
|
|
18
|
+
title: 'Tools',
|
|
19
|
+
order: 200,
|
|
20
|
+
requiresPermission: 'ReadMcpServer',
|
|
21
|
+
},
|
|
22
|
+
path: '/mcp-server/tools',
|
|
23
|
+
loader: () => ({ breadcrumb: () => <Trans>Tools</Trans> }),
|
|
24
|
+
component: () => (
|
|
25
|
+
<Page pageId="mcp-server-tools">
|
|
26
|
+
<PageTitle>
|
|
27
|
+
<Trans>Tools</Trans>
|
|
28
|
+
</PageTitle>
|
|
29
|
+
<PermissionGuard requires={['ReadMcpServer']}>
|
|
30
|
+
<PageLayout>
|
|
31
|
+
<PageBlock column="full" blockId="mcp-tools">
|
|
32
|
+
<ToolsBlock />
|
|
33
|
+
</PageBlock>
|
|
34
|
+
</PageLayout>
|
|
35
|
+
</PermissionGuard>
|
|
36
|
+
</Page>
|
|
37
|
+
),
|
|
38
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { graphql as dashboardGraphql } from '@vendure/dashboard';
|
|
2
|
+
import type { initGraphQLTada } from 'gql.tada';
|
|
3
|
+
import type { introspection } from '../../codegen/generated/graphql-env.d.ts';
|
|
4
|
+
|
|
5
|
+
// The dashboard's `graphql` is typed against the core Admin API only. It parses a query the same
|
|
6
|
+
// way whatever schema it is typed with, so it is retyped here against this plugin's schema. That
|
|
7
|
+
// keeps the queries typed without asking the project that installs the plugin for an `@/gql` alias.
|
|
8
|
+
const graphql = dashboardGraphql as unknown as ReturnType<
|
|
9
|
+
typeof initGraphQLTada<{
|
|
10
|
+
disableMasking: true;
|
|
11
|
+
introspection: introspection;
|
|
12
|
+
scalars: {
|
|
13
|
+
DateTime: string;
|
|
14
|
+
JSON: any;
|
|
15
|
+
Money: number;
|
|
16
|
+
};
|
|
17
|
+
}>
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
// A mismatch with src/api/api-extensions.ts stops the dashboard code compiling.
|
|
21
|
+
|
|
22
|
+
export const mcpToolsQuery = graphql(`
|
|
23
|
+
query McpTools {
|
|
24
|
+
mcpTools {
|
|
25
|
+
id
|
|
26
|
+
name
|
|
27
|
+
toolset
|
|
28
|
+
description
|
|
29
|
+
pluginSource
|
|
30
|
+
behavior
|
|
31
|
+
enabled
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
`);
|
|
35
|
+
|
|
36
|
+
export const mcpServerConfigQuery = graphql(`
|
|
37
|
+
query McpServerConfig {
|
|
38
|
+
mcpServerConfig {
|
|
39
|
+
toolExposure
|
|
40
|
+
shopAccess
|
|
41
|
+
oauthConfigured
|
|
42
|
+
issuer
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
export const mcpStatsQuery = graphql(`
|
|
48
|
+
query McpStats($timeRange: String) {
|
|
49
|
+
mcpStats(timeRange: $timeRange) {
|
|
50
|
+
totalCalls
|
|
51
|
+
successRate
|
|
52
|
+
errorRate
|
|
53
|
+
p50LatencyMs
|
|
54
|
+
p95LatencyMs
|
|
55
|
+
callsPerHour
|
|
56
|
+
topTools {
|
|
57
|
+
toolName
|
|
58
|
+
count
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
`);
|
|
63
|
+
|
|
64
|
+
export const mcpToolCallLogsQuery = graphql(`
|
|
65
|
+
query McpToolCallLogs($options: McpToolCallLogListOptions) {
|
|
66
|
+
mcpToolCallLogs(options: $options) {
|
|
67
|
+
items {
|
|
68
|
+
id
|
|
69
|
+
createdAt
|
|
70
|
+
toolName
|
|
71
|
+
actor
|
|
72
|
+
actorType
|
|
73
|
+
actorName
|
|
74
|
+
customerId
|
|
75
|
+
status
|
|
76
|
+
durationMs
|
|
77
|
+
pluginSource
|
|
78
|
+
}
|
|
79
|
+
totalItems
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
`);
|
|
83
|
+
|
|
84
|
+
export const mcpOauthGrantsQuery = graphql(`
|
|
85
|
+
query McpOauthGrants($options: McpOauthGrantListOptions) {
|
|
86
|
+
mcpOauthGrants(includeInactive: true, options: $options) {
|
|
87
|
+
items {
|
|
88
|
+
id
|
|
89
|
+
createdAt
|
|
90
|
+
updatedAt
|
|
91
|
+
actorId
|
|
92
|
+
actorType
|
|
93
|
+
actorName
|
|
94
|
+
customerId
|
|
95
|
+
channelId
|
|
96
|
+
oauthClientName
|
|
97
|
+
lastActivityAt
|
|
98
|
+
expiresAt
|
|
99
|
+
revokedAt
|
|
100
|
+
status
|
|
101
|
+
}
|
|
102
|
+
totalItems
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
`);
|
|
106
|
+
|
|
107
|
+
export const setMcpToolEnabledMutation = graphql(`
|
|
108
|
+
mutation SetMcpToolEnabled($toolName: String!, $toolset: McpToolset!, $enabled: Boolean!) {
|
|
109
|
+
setMcpToolEnabled(toolName: $toolName, toolset: $toolset, enabled: $enabled) {
|
|
110
|
+
enabled
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
`);
|
|
114
|
+
|
|
115
|
+
export const revokeMcpOauthGrantMutation = graphql(`
|
|
116
|
+
mutation RevokeMcpOauthGrant($id: ID!) {
|
|
117
|
+
revokeMcpOauthGrant(id: $id)
|
|
118
|
+
}
|
|
119
|
+
`);
|
|
120
|
+
|
|
121
|
+
export const authorizeMcpClientMutation = graphql(`
|
|
122
|
+
mutation AuthorizeMcpClient($requestToken: String!, $approved: Boolean!) {
|
|
123
|
+
authorizeMcpClient(requestToken: $requestToken, approved: $approved) {
|
|
124
|
+
redirectUrl
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
`);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The dashboard sources are shipped as-is and compiled by the consumer's Vite build, so every
|
|
7
|
+
* relative import they make must point at a file that the package.json `files` list publishes.
|
|
8
|
+
*/
|
|
9
|
+
describe('published dashboard sources', () => {
|
|
10
|
+
const packageRoot = resolve(__dirname, '../..');
|
|
11
|
+
const dashboardDir = join(packageRoot, 'src/dashboard');
|
|
12
|
+
// Mirrors the `files` list in package.json: only these source folders reach the published package.
|
|
13
|
+
const publishedSourceDirs = ['src/dashboard/', 'src/shared/'];
|
|
14
|
+
|
|
15
|
+
function isPublished(relativePath: string): boolean {
|
|
16
|
+
return publishedSourceDirs.some(dir => relativePath.startsWith(dir));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function sourceFiles(dir: string): string[] {
|
|
20
|
+
return readdirSync(dir).flatMap(name => {
|
|
21
|
+
const full = join(dir, name);
|
|
22
|
+
if (statSync(full).isDirectory()) {
|
|
23
|
+
return sourceFiles(full);
|
|
24
|
+
}
|
|
25
|
+
return /\.(ts|tsx)$/.test(name) && !name.endsWith('.spec.ts') ? [full] : [];
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function resolveImport(fromFile: string, specifier: string): string | undefined {
|
|
30
|
+
const base = resolve(dirname(fromFile), specifier);
|
|
31
|
+
for (const candidate of [base, `${base}.ts`, `${base}.tsx`, join(base, 'index.ts'), join(base, 'index.tsx')]) {
|
|
32
|
+
if (existsSync(candidate) && statSync(candidate).isFile()) {
|
|
33
|
+
return candidate;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
it('only imports files that the package publishes', () => {
|
|
40
|
+
const unpublished: string[] = [];
|
|
41
|
+
for (const file of sourceFiles(dashboardDir)) {
|
|
42
|
+
// The build drops type-only imports, so their targets never need publishing.
|
|
43
|
+
const source = readFileSync(file, 'utf8').replace(/^import type [^;]+;/gm, '');
|
|
44
|
+
for (const match of source.matchAll(/from\s+'(\.[^']+)'/g)) {
|
|
45
|
+
const target = resolveImport(file, match[1]);
|
|
46
|
+
expect(target, `${relative(packageRoot, file)} imports ${match[1]}`).toBeDefined();
|
|
47
|
+
if (target && !isPublished(relative(packageRoot, target))) {
|
|
48
|
+
unpublished.push(`${relative(packageRoot, file)} -> ${relative(packageRoot, target)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
expect(unpublished).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
});
|