@solidcommerce/mcp-server 2.0.0-alpha.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/README.md +74 -0
- package/bin/solidcommerce-mcp.js +8 -0
- package/dist/audit/auditClient.d.ts +51 -0
- package/dist/audit/auditClient.d.ts.map +1 -0
- package/dist/audit/auditClient.js +93 -0
- package/dist/audit/auditClient.js.map +1 -0
- package/dist/auth/apiKey.d.ts +44 -0
- package/dist/auth/apiKey.d.ts.map +1 -0
- package/dist/auth/apiKey.js +80 -0
- package/dist/auth/apiKey.js.map +1 -0
- package/dist/auth/oauth.d.ts +108 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +300 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/generator/fromOpenApi.d.ts +54 -0
- package/dist/generator/fromOpenApi.d.ts.map +1 -0
- package/dist/generator/fromOpenApi.js +411 -0
- package/dist/generator/fromOpenApi.js.map +1 -0
- package/dist/generator/schemaToZod.d.ts +81 -0
- package/dist/generator/schemaToZod.d.ts.map +1 -0
- package/dist/generator/schemaToZod.js +277 -0
- package/dist/generator/schemaToZod.js.map +1 -0
- package/dist/generator/scopeInference.d.ts +26 -0
- package/dist/generator/scopeInference.d.ts.map +1 -0
- package/dist/generator/scopeInference.js +91 -0
- package/dist/generator/scopeInference.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +106 -0
- package/dist/index.js.map +1 -0
- package/dist/server/buildServer.d.ts +35 -0
- package/dist/server/buildServer.d.ts.map +1 -0
- package/dist/server/buildServer.js +104 -0
- package/dist/server/buildServer.js.map +1 -0
- package/dist/server/dispatcher.d.ts +14 -0
- package/dist/server/dispatcher.d.ts.map +1 -0
- package/dist/server/dispatcher.js +190 -0
- package/dist/server/dispatcher.js.map +1 -0
- package/dist/session.d.ts +9 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +11 -0
- package/dist/session.js.map +1 -0
- package/dist/telemetry/hostName.d.ts +26 -0
- package/dist/telemetry/hostName.d.ts.map +1 -0
- package/dist/telemetry/hostName.js +80 -0
- package/dist/telemetry/hostName.js.map +1 -0
- package/dist/tools/contract.d.ts +125 -0
- package/dist/tools/contract.d.ts.map +1 -0
- package/dist/tools/contract.js +101 -0
- package/dist/tools/contract.js.map +1 -0
- package/dist/tools/foundation/catalog_amazon_items_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_amazon_items_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_amazon_items_list.js +47 -0
- package/dist/tools/foundation/catalog_amazon_items_list.js.map +1 -0
- package/dist/tools/foundation/catalog_attribute_filters_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_attribute_filters_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_attribute_filters_list.js +47 -0
- package/dist/tools/foundation/catalog_attribute_filters_list.js.map +1 -0
- package/dist/tools/foundation/catalog_attribute_tags_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_attribute_tags_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_attribute_tags_list.js +47 -0
- package/dist/tools/foundation/catalog_attribute_tags_list.js.map +1 -0
- package/dist/tools/foundation/catalog_attributes_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_attributes_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_attributes_list.js +57 -0
- package/dist/tools/foundation/catalog_attributes_list.js.map +1 -0
- package/dist/tools/foundation/catalog_column_views_default_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_column_views_default_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_column_views_default_list.js +47 -0
- package/dist/tools/foundation/catalog_column_views_default_list.js.map +1 -0
- package/dist/tools/foundation/catalog_column_views_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_column_views_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_column_views_list.js +47 -0
- package/dist/tools/foundation/catalog_column_views_list.js.map +1 -0
- package/dist/tools/foundation/catalog_ebay_categories_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_ebay_categories_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_ebay_categories_list.js +56 -0
- package/dist/tools/foundation/catalog_ebay_categories_list.js.map +1 -0
- package/dist/tools/foundation/catalog_ebay_category_specifics_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_ebay_category_specifics_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_ebay_category_specifics_list.js +56 -0
- package/dist/tools/foundation/catalog_ebay_category_specifics_list.js.map +1 -0
- package/dist/tools/foundation/catalog_ebay_listing_templates_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_ebay_listing_templates_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_ebay_listing_templates_list.js +47 -0
- package/dist/tools/foundation/catalog_ebay_listing_templates_list.js.map +1 -0
- package/dist/tools/foundation/catalog_ebay_product_search_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_ebay_product_search_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_ebay_product_search_list.js +56 -0
- package/dist/tools/foundation/catalog_ebay_product_search_list.js.map +1 -0
- package/dist/tools/foundation/catalog_ebay_template_fields_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_ebay_template_fields_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_ebay_template_fields_list.js +47 -0
- package/dist/tools/foundation/catalog_ebay_template_fields_list.js.map +1 -0
- package/dist/tools/foundation/catalog_ebay_variation_groups_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_ebay_variation_groups_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_ebay_variation_groups_list.js +56 -0
- package/dist/tools/foundation/catalog_ebay_variation_groups_list.js.map +1 -0
- package/dist/tools/foundation/catalog_inventory_sources_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_inventory_sources_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_inventory_sources_list.js +47 -0
- package/dist/tools/foundation/catalog_inventory_sources_list.js.map +1 -0
- package/dist/tools/foundation/catalog_listing_rules_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_listing_rules_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_listing_rules_list.js +47 -0
- package/dist/tools/foundation/catalog_listing_rules_list.js.map +1 -0
- package/dist/tools/foundation/catalog_listing_templates_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_listing_templates_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_listing_templates_list.js +47 -0
- package/dist/tools/foundation/catalog_listing_templates_list.js.map +1 -0
- package/dist/tools/foundation/catalog_manufacturers_all_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_manufacturers_all_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_manufacturers_all_list.js +47 -0
- package/dist/tools/foundation/catalog_manufacturers_all_list.js.map +1 -0
- package/dist/tools/foundation/catalog_manufacturers_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_manufacturers_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_manufacturers_list.js +47 -0
- package/dist/tools/foundation/catalog_manufacturers_list.js.map +1 -0
- package/dist/tools/foundation/catalog_market_lists_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_market_lists_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_market_lists_list.js +47 -0
- package/dist/tools/foundation/catalog_market_lists_list.js.map +1 -0
- package/dist/tools/foundation/catalog_product_usage_filters_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_product_usage_filters_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_product_usage_filters_list.js +47 -0
- package/dist/tools/foundation/catalog_product_usage_filters_list.js.map +1 -0
- package/dist/tools/foundation/catalog_product_usage_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_product_usage_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_product_usage_list.js +61 -0
- package/dist/tools/foundation/catalog_product_usage_list.js.map +1 -0
- package/dist/tools/foundation/catalog_product_usage_totals_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_product_usage_totals_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_product_usage_totals_list.js +58 -0
- package/dist/tools/foundation/catalog_product_usage_totals_list.js.map +1 -0
- package/dist/tools/foundation/catalog_products_generate_sku_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_products_generate_sku_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_products_generate_sku_list.js +47 -0
- package/dist/tools/foundation/catalog_products_generate_sku_list.js.map +1 -0
- package/dist/tools/foundation/catalog_products_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_products_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_products_list.js +60 -0
- package/dist/tools/foundation/catalog_products_list.js.map +1 -0
- package/dist/tools/foundation/catalog_taxonomy_channels_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_taxonomy_channels_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_taxonomy_channels_list.js +47 -0
- package/dist/tools/foundation/catalog_taxonomy_channels_list.js.map +1 -0
- package/dist/tools/foundation/catalog_taxonomy_mappings_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_taxonomy_mappings_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_taxonomy_mappings_list.js +56 -0
- package/dist/tools/foundation/catalog_taxonomy_mappings_list.js.map +1 -0
- package/dist/tools/foundation/catalog_taxonomy_product_values_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_taxonomy_product_values_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_taxonomy_product_values_list.js +57 -0
- package/dist/tools/foundation/catalog_taxonomy_product_values_list.js.map +1 -0
- package/dist/tools/foundation/catalog_tree_list.d.ts +4 -0
- package/dist/tools/foundation/catalog_tree_list.d.ts.map +1 -0
- package/dist/tools/foundation/catalog_tree_list.js +47 -0
- package/dist/tools/foundation/catalog_tree_list.js.map +1 -0
- package/dist/tools/foundation/crm_customers_list.d.ts +4 -0
- package/dist/tools/foundation/crm_customers_list.d.ts.map +1 -0
- package/dist/tools/foundation/crm_customers_list.js +59 -0
- package/dist/tools/foundation/crm_customers_list.js.map +1 -0
- package/dist/tools/foundation/fulfillment_manifests_list.d.ts +4 -0
- package/dist/tools/foundation/fulfillment_manifests_list.d.ts.map +1 -0
- package/dist/tools/foundation/fulfillment_manifests_list.js +47 -0
- package/dist/tools/foundation/fulfillment_manifests_list.js.map +1 -0
- package/dist/tools/foundation/fulfillment_ship_now_addresses_list.d.ts +4 -0
- package/dist/tools/foundation/fulfillment_ship_now_addresses_list.d.ts.map +1 -0
- package/dist/tools/foundation/fulfillment_ship_now_addresses_list.js +47 -0
- package/dist/tools/foundation/fulfillment_ship_now_addresses_list.js.map +1 -0
- package/dist/tools/foundation/fulfillment_ship_now_dictionaries_list.d.ts +4 -0
- package/dist/tools/foundation/fulfillment_ship_now_dictionaries_list.d.ts.map +1 -0
- package/dist/tools/foundation/fulfillment_ship_now_dictionaries_list.js +56 -0
- package/dist/tools/foundation/fulfillment_ship_now_dictionaries_list.js.map +1 -0
- package/dist/tools/foundation/index.d.ts +8 -0
- package/dist/tools/foundation/index.d.ts.map +1 -0
- package/dist/tools/foundation/index.js +93 -0
- package/dist/tools/foundation/index.js.map +1 -0
- package/dist/tools/foundation/inventory_alert_rules_list.d.ts +4 -0
- package/dist/tools/foundation/inventory_alert_rules_list.d.ts.map +1 -0
- package/dist/tools/foundation/inventory_alert_rules_list.js +47 -0
- package/dist/tools/foundation/inventory_alert_rules_list.js.map +1 -0
- package/dist/tools/foundation/inventory_alert_settings_list.d.ts +4 -0
- package/dist/tools/foundation/inventory_alert_settings_list.d.ts.map +1 -0
- package/dist/tools/foundation/inventory_alert_settings_list.js +47 -0
- package/dist/tools/foundation/inventory_alert_settings_list.js.map +1 -0
- package/dist/tools/foundation/inventory_analytics_alert_count_list.d.ts +4 -0
- package/dist/tools/foundation/inventory_analytics_alert_count_list.d.ts.map +1 -0
- package/dist/tools/foundation/inventory_analytics_alert_count_list.js +47 -0
- package/dist/tools/foundation/inventory_analytics_alert_count_list.js.map +1 -0
- package/dist/tools/foundation/inventory_rules_list.d.ts +4 -0
- package/dist/tools/foundation/inventory_rules_list.d.ts.map +1 -0
- package/dist/tools/foundation/inventory_rules_list.js +47 -0
- package/dist/tools/foundation/inventory_rules_list.js.map +1 -0
- package/dist/tools/foundation/inventory_rules_periods_list.d.ts +4 -0
- package/dist/tools/foundation/inventory_rules_periods_list.d.ts.map +1 -0
- package/dist/tools/foundation/inventory_rules_periods_list.js +47 -0
- package/dist/tools/foundation/inventory_rules_periods_list.js.map +1 -0
- package/dist/tools/foundation/inventory_seasonal_factors_list.d.ts +4 -0
- package/dist/tools/foundation/inventory_seasonal_factors_list.d.ts.map +1 -0
- package/dist/tools/foundation/inventory_seasonal_factors_list.js +47 -0
- package/dist/tools/foundation/inventory_seasonal_factors_list.js.map +1 -0
- package/dist/tools/foundation/inventory_warehouses_list.d.ts +4 -0
- package/dist/tools/foundation/inventory_warehouses_list.d.ts.map +1 -0
- package/dist/tools/foundation/inventory_warehouses_list.js +47 -0
- package/dist/tools/foundation/inventory_warehouses_list.js.map +1 -0
- package/dist/tools/foundation/listings_dictionary_marketplace_locales_list.d.ts +4 -0
- package/dist/tools/foundation/listings_dictionary_marketplace_locales_list.d.ts.map +1 -0
- package/dist/tools/foundation/listings_dictionary_marketplace_locales_list.js +56 -0
- package/dist/tools/foundation/listings_dictionary_marketplace_locales_list.js.map +1 -0
- package/dist/tools/foundation/listings_dictionary_marketplaces_list.d.ts +4 -0
- package/dist/tools/foundation/listings_dictionary_marketplaces_list.d.ts.map +1 -0
- package/dist/tools/foundation/listings_dictionary_marketplaces_list.js +47 -0
- package/dist/tools/foundation/listings_dictionary_marketplaces_list.js.map +1 -0
- package/dist/tools/tasks/bulk_update_prices.d.ts +4 -0
- package/dist/tools/tasks/bulk_update_prices.d.ts.map +1 -0
- package/dist/tools/tasks/bulk_update_prices.js +96 -0
- package/dist/tools/tasks/bulk_update_prices.js.map +1 -0
- package/dist/tools/tasks/cancel_order_with_reason.d.ts +4 -0
- package/dist/tools/tasks/cancel_order_with_reason.d.ts.map +1 -0
- package/dist/tools/tasks/cancel_order_with_reason.js +117 -0
- package/dist/tools/tasks/cancel_order_with_reason.js.map +1 -0
- package/dist/tools/tasks/check_listing_health.d.ts +4 -0
- package/dist/tools/tasks/check_listing_health.d.ts.map +1 -0
- package/dist/tools/tasks/check_listing_health.js +123 -0
- package/dist/tools/tasks/check_listing_health.js.map +1 -0
- package/dist/tools/tasks/create_and_submit_purchase_order.d.ts +4 -0
- package/dist/tools/tasks/create_and_submit_purchase_order.d.ts.map +1 -0
- package/dist/tools/tasks/create_and_submit_purchase_order.js +142 -0
- package/dist/tools/tasks/create_and_submit_purchase_order.js.map +1 -0
- package/dist/tools/tasks/find_product_by_sku.d.ts +4 -0
- package/dist/tools/tasks/find_product_by_sku.d.ts.map +1 -0
- package/dist/tools/tasks/find_product_by_sku.js +68 -0
- package/dist/tools/tasks/find_product_by_sku.js.map +1 -0
- package/dist/tools/tasks/fulfill_order.d.ts +4 -0
- package/dist/tools/tasks/fulfill_order.d.ts.map +1 -0
- package/dist/tools/tasks/fulfill_order.js +121 -0
- package/dist/tools/tasks/fulfill_order.js.map +1 -0
- package/dist/tools/tasks/get_inventory_across_warehouses.d.ts +4 -0
- package/dist/tools/tasks/get_inventory_across_warehouses.d.ts.map +1 -0
- package/dist/tools/tasks/get_inventory_across_warehouses.js +118 -0
- package/dist/tools/tasks/get_inventory_across_warehouses.js.map +1 -0
- package/dist/tools/tasks/index.d.ts +14 -0
- package/dist/tools/tasks/index.d.ts.map +1 -0
- package/dist/tools/tasks/index.js +32 -0
- package/dist/tools/tasks/index.js.map +1 -0
- package/dist/tools/tasks/publish_product_to_channels.d.ts +4 -0
- package/dist/tools/tasks/publish_product_to_channels.d.ts.map +1 -0
- package/dist/tools/tasks/publish_product_to_channels.js +109 -0
- package/dist/tools/tasks/publish_product_to_channels.js.map +1 -0
- package/dist/tools/tasks/reply_to_buyer_message.d.ts +4 -0
- package/dist/tools/tasks/reply_to_buyer_message.d.ts.map +1 -0
- package/dist/tools/tasks/reply_to_buyer_message.js +72 -0
- package/dist/tools/tasks/reply_to_buyer_message.js.map +1 -0
- package/dist/tools/tasks/reprice_listing.d.ts +4 -0
- package/dist/tools/tasks/reprice_listing.d.ts.map +1 -0
- package/dist/tools/tasks/reprice_listing.js +74 -0
- package/dist/tools/tasks/reprice_listing.js.map +1 -0
- package/dist/transports/stdio.d.ts +12 -0
- package/dist/transports/stdio.d.ts.map +1 -0
- package/dist/transports/stdio.js +30 -0
- package/dist/transports/stdio.js.map +1 -0
- package/dist/transports/streamableHttp.d.ts +33 -0
- package/dist/transports/streamableHttp.d.ts.map +1 -0
- package/dist/transports/streamableHttp.js +237 -0
- package/dist/transports/streamableHttp.js.map +1 -0
- package/package.json +47 -0
- package/vendor/openapi.snapshot.json +19887 -0
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @solidcommerce/mcp-server
|
|
2
|
+
|
|
3
|
+
> **The Solid Commerce Platform — for AI agents.** Drop a single line into Claude Desktop (or any MCP-aware client) and an LLM can query catalog, orders, listings, inventory, vendors, CRM, and webhooks against your live tenant — read endpoints, repricing, fulfillment, bulk operations, the works — using your existing Solid Commerce API key. Foundation tools auto-track every endpoint in our OpenAPI spec; curated task tools collapse 5-10-call workflows into a single agent action so the LLM doesn't have to plan them itself.
|
|
4
|
+
>
|
|
5
|
+
> **Pick this when:** you want Claude (Desktop or hosted), the OpenAI Responses API, or any MCP client to operate on Solid Commerce data without writing custom integration code, OR you're a Solid Commerce customer building an internal copilot and need the platform's full API surface exposed safely (scoped per-tool, audited per-call, no token gymnastics).
|
|
6
|
+
|
|
7
|
+
The official Model Context Protocol (MCP) server for the Solid Commerce Platform API. Exposes a hybrid tool set — foundation tools auto-generated from the OpenAPI spec plus curated task-shaped tools — over both stdio (for local clients like Claude Desktop / Claude Code) and **Streamable HTTP over HTTPS** (for hosted `mcp.solidcommerce.com`, the transport required by the Claude UI Connectors Directory). SSE was removed in Phase 0 / E-A1.
|
|
8
|
+
|
|
9
|
+
See `phase3_solution_plan.md` and `requirements_api_cli_mcp.md` for the wider initiative context.
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Local (stdio, for Claude Desktop)
|
|
15
|
+
npx @solidcommerce/cli mcp install # idempotent Claude Desktop config snippet
|
|
16
|
+
npx @solidcommerce/cli mcp serve # boot stdio server
|
|
17
|
+
|
|
18
|
+
# Or invoke this package directly
|
|
19
|
+
SOLIDCOMMERCE_API_KEY=sk_... npx @solidcommerce/mcp-server
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Auth
|
|
23
|
+
|
|
24
|
+
Two modes coexist on the same server (Phase 0 / E-A3):
|
|
25
|
+
|
|
26
|
+
**API key (stdio / service-to-service / Scotty / internal).** Pass your Solid
|
|
27
|
+
Commerce API key via env var:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
SOLIDCOMMERCE_API_KEY=sk_live_...
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Per-user OAuth (remote hosts — Claude UI, ChatGPT, Gemini, M365, Grok).** A
|
|
34
|
+
host sends a per-user `Authorization: Bearer <token>` issued by SC.Base.Auth on
|
|
35
|
+
the Streamable HTTP transport. The server introspects the token against
|
|
36
|
+
`/oauth/introspect` (using the `sc-mcp-server` confidential client) to fast-fail
|
|
37
|
+
invalid tokens and extract claims for audit + telemetry, then forwards the token
|
|
38
|
+
to Platform, which independently re-validates it. Configure with:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
SC_MCP_OAUTH_ENABLED=true # auto-on when the two below are set
|
|
42
|
+
SC_AUTH_INTROSPECT_URL=https://auth.solidcommerce.com/oauth/introspect
|
|
43
|
+
SC_MCP_CLIENT_ID=sc-mcp-server
|
|
44
|
+
SC_MCP_CLIENT_SECRET=... # from Key Vault
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Resilience: introspection results are cached (bounded TTL under token `exp`) and
|
|
48
|
+
guarded by a circuit breaker. Because Platform is the authority, an Auth outage
|
|
49
|
+
**fails open** — a present bearer is still forwarded (marked degraded in audit) —
|
|
50
|
+
while an explicitly *inactive* token is hard-rejected with `401`. A request with
|
|
51
|
+
no `Authorization` header falls back to the API-key path.
|
|
52
|
+
|
|
53
|
+
## Tool surface
|
|
54
|
+
|
|
55
|
+
- **Foundation tools** — auto-generated from `vendor/openapi.snapshot.json`. ~40 selected via heuristic (read-heavy lists + composition-target POSTs). Naming: `<segment>_<resource>_<verb>` (matching the CLI's resource driver).
|
|
56
|
+
- **Task tools** — 10 hand-authored workflow tools (publish_product_to_channels, reprice_listing, fulfill_order, …). Each composes 2-10 SDK calls and reports a clean structured result.
|
|
57
|
+
|
|
58
|
+
## Audit trail
|
|
59
|
+
|
|
60
|
+
Every tool invocation tags the underlying SDK call with `X-Audit-Source: mcp` and `X-Audit-Tool-Name: <tool>` headers; Platform's `AuditLogMiddleware` (W3) writes one `audit_log` row per call.
|
|
61
|
+
|
|
62
|
+
## Development
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install
|
|
66
|
+
npm run typecheck
|
|
67
|
+
npm run lint
|
|
68
|
+
npm run test
|
|
69
|
+
npm run build
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
UNLICENSED — internal Solid Commerce code. Distribution via private Azure Artifacts npm feed (gated until 2026-05-10).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// bin/solidcommerce-mcp.js
|
|
4
|
+
// Wave: W6 — MCP Server entrypoint shim.
|
|
5
|
+
//
|
|
6
|
+
// Forwards to dist/index.js. The TS source lives at src/index.ts.
|
|
7
|
+
// =============================================================================
|
|
8
|
+
import "../dist/index.js";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { SolidCommerceClient } from "@solidcommerce/sdk";
|
|
2
|
+
import type { SessionAuthContext } from "../auth/oauth.js";
|
|
3
|
+
export declare const AUDIT_HEADER_SOURCE = "X-Audit-Source";
|
|
4
|
+
export declare const AUDIT_HEADER_TOOL_NAME = "X-Audit-Tool-Name";
|
|
5
|
+
export declare const AUDIT_HEADER_AUTH_MODE = "X-Audit-Auth-Mode";
|
|
6
|
+
export declare const AUDIT_HEADER_INSTALL_ID = "X-Audit-Install-Id";
|
|
7
|
+
/** Set when the bearer was forwarded WITHOUT a successful introspection
|
|
8
|
+
* (Auth outage / circuit open). Lets Platform audit flag unverified calls. */
|
|
9
|
+
export declare const AUDIT_HEADER_AUTH_DEGRADED = "X-Audit-Auth-Degraded";
|
|
10
|
+
/** Canonical calling surface (E-A4) — powers the per-surface KQL dashboard. */
|
|
11
|
+
export declare const AUDIT_HEADER_HOST_NAME = "X-Audit-Host-Name";
|
|
12
|
+
export declare const AUDIT_SOURCE_VALUE = "mcp";
|
|
13
|
+
export interface CurrentToolNameRef {
|
|
14
|
+
current: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface AttachAuditHookOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Resolved session auth identity (Phase 0 / E-A3). When present, the hook
|
|
19
|
+
* tags every outbound call with the auth mode and (if known) the install id
|
|
20
|
+
* so Platform's audit log can attribute the call to a user/install/surface.
|
|
21
|
+
* Static for the session, so captured once here rather than threaded per-call.
|
|
22
|
+
*/
|
|
23
|
+
readonly auth?: SessionAuthContext;
|
|
24
|
+
/**
|
|
25
|
+
* Canonical calling surface (Phase 0 / E-A4). Emitted as X-Audit-Host-Name so
|
|
26
|
+
* Platform telemetry can pivot per-surface. Best-effort (see telemetry/hostName).
|
|
27
|
+
*/
|
|
28
|
+
readonly hostName?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface AttachAuditHookResult {
|
|
31
|
+
/** Dispose the hook (mainly for tests). */
|
|
32
|
+
readonly dispose: () => void;
|
|
33
|
+
/**
|
|
34
|
+
* Set the tool name to be attached to the NEXT outbound request(s). The
|
|
35
|
+
* dispatcher pairs every set with a clear (set name → invoke handler →
|
|
36
|
+
* clear name) so leakage between calls is impossible even when the SDK
|
|
37
|
+
* issues parallel sub-requests.
|
|
38
|
+
*/
|
|
39
|
+
readonly setCurrentToolName: (name: string | null) => void;
|
|
40
|
+
/** Inspect the current ref — useful in tests; do not mutate externally. */
|
|
41
|
+
readonly ref: CurrentToolNameRef;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Register an `onRequest` hook on `client` that injects audit headers.
|
|
45
|
+
*
|
|
46
|
+
* Returns helpers the dispatcher (or tests) use to control the per-call tool
|
|
47
|
+
* name. Returning the ref itself lets unit tests assert isolation without
|
|
48
|
+
* resorting to Sinon spies.
|
|
49
|
+
*/
|
|
50
|
+
export declare function attachAuditHook(client: SolidCommerceClient, opts?: AttachAuditHookOptions): AttachAuditHookResult;
|
|
51
|
+
//# sourceMappingURL=auditClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditClient.d.ts","sourceRoot":"","sources":["../../src/audit/auditClient.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AACpD,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAE1D,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAC1D,eAAO,MAAM,uBAAuB,uBAAuB,CAAC;AAC5D;8EAC8E;AAC9E,eAAO,MAAM,0BAA0B,0BAA0B,CAAC;AAClE,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAC1D,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,kBAAkB,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3D,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,mBAAmB,EAC3B,IAAI,GAAE,sBAA2B,GAChC,qBAAqB,CAyCvB"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// src/audit/auditClient.ts
|
|
3
|
+
// Wave: W6 — Stream C
|
|
4
|
+
//
|
|
5
|
+
// Wraps the @solidcommerce/sdk transport so every SDK call carries
|
|
6
|
+
// X-Audit-Source: mcp
|
|
7
|
+
// X-Audit-Tool-Name: <tool-name>
|
|
8
|
+
//
|
|
9
|
+
// Platform's AuditLogMiddleware (SC.Base.API.Common, extended in W6 Step 6)
|
|
10
|
+
// reads these and persists them to dbo.audit_log.
|
|
11
|
+
//
|
|
12
|
+
// Design notes:
|
|
13
|
+
//
|
|
14
|
+
// - The audit hook is a `client.use({ onRequest })` registration. We deliberately
|
|
15
|
+
// do NOT modify the SDK package; the SDK exposes a public hook system for
|
|
16
|
+
// this exact use case (see options.d.ts → ClientHooks).
|
|
17
|
+
//
|
|
18
|
+
// - The current tool name is threaded via a closed-over ref-cell (a plain
|
|
19
|
+
// `{ current: string | null }` object). We rejected AsyncLocalStorage to
|
|
20
|
+
// keep this portable across server transports (stdio + Streamable HTTP) and to avoid
|
|
21
|
+
// the extra complexity. The dispatcher (server/dispatcher.ts) sets the ref
|
|
22
|
+
// *just before* invoking the tool's handler and clears it after — see
|
|
23
|
+
// `setCurrentToolName` returned from `attachAuditHook`.
|
|
24
|
+
//
|
|
25
|
+
// - `X-Audit-Source: mcp` is ALWAYS injected. The CLI resource driver (W5)
|
|
26
|
+
// must not see this header — it ships a different audit hook. Keeping the
|
|
27
|
+
// hook scoped to *this* client (constructed in src/auth/apiKey.ts) ensures
|
|
28
|
+
// isolation by construction.
|
|
29
|
+
//
|
|
30
|
+
// - The hook is non-throwing per the SDK contract (options.d.ts:60). If the
|
|
31
|
+
// headers are read-only somehow we silently no-op rather than blow up the
|
|
32
|
+
// request — audit data loss is preferable to losing the user's call.
|
|
33
|
+
// =============================================================================
|
|
34
|
+
export const AUDIT_HEADER_SOURCE = "X-Audit-Source";
|
|
35
|
+
export const AUDIT_HEADER_TOOL_NAME = "X-Audit-Tool-Name";
|
|
36
|
+
// Phase 0 / E-A3 + E-A4: identity + surface attribution on every audited call.
|
|
37
|
+
export const AUDIT_HEADER_AUTH_MODE = "X-Audit-Auth-Mode";
|
|
38
|
+
export const AUDIT_HEADER_INSTALL_ID = "X-Audit-Install-Id";
|
|
39
|
+
/** Set when the bearer was forwarded WITHOUT a successful introspection
|
|
40
|
+
* (Auth outage / circuit open). Lets Platform audit flag unverified calls. */
|
|
41
|
+
export const AUDIT_HEADER_AUTH_DEGRADED = "X-Audit-Auth-Degraded";
|
|
42
|
+
/** Canonical calling surface (E-A4) — powers the per-surface KQL dashboard. */
|
|
43
|
+
export const AUDIT_HEADER_HOST_NAME = "X-Audit-Host-Name";
|
|
44
|
+
export const AUDIT_SOURCE_VALUE = "mcp";
|
|
45
|
+
/**
|
|
46
|
+
* Register an `onRequest` hook on `client` that injects audit headers.
|
|
47
|
+
*
|
|
48
|
+
* Returns helpers the dispatcher (or tests) use to control the per-call tool
|
|
49
|
+
* name. Returning the ref itself lets unit tests assert isolation without
|
|
50
|
+
* resorting to Sinon spies.
|
|
51
|
+
*/
|
|
52
|
+
export function attachAuditHook(client, opts = {}) {
|
|
53
|
+
const ref = { current: null };
|
|
54
|
+
const auth = opts.auth;
|
|
55
|
+
const hostName = opts.hostName;
|
|
56
|
+
const dispose = client.use({
|
|
57
|
+
onRequest: ({ request }) => {
|
|
58
|
+
// The SDK's hook contract documents that request.headers is mutable;
|
|
59
|
+
// wrap defensively so a frozen header bag doesn't kill the call.
|
|
60
|
+
try {
|
|
61
|
+
request.headers.set(AUDIT_HEADER_SOURCE, AUDIT_SOURCE_VALUE);
|
|
62
|
+
const toolName = ref.current;
|
|
63
|
+
if (toolName !== null && toolName.length > 0) {
|
|
64
|
+
request.headers.set(AUDIT_HEADER_TOOL_NAME, toolName);
|
|
65
|
+
}
|
|
66
|
+
if (hostName !== undefined && hostName.length > 0) {
|
|
67
|
+
request.headers.set(AUDIT_HEADER_HOST_NAME, hostName);
|
|
68
|
+
}
|
|
69
|
+
if (auth) {
|
|
70
|
+
request.headers.set(AUDIT_HEADER_AUTH_MODE, auth.mode);
|
|
71
|
+
if (auth.installId !== undefined && auth.installId.length > 0) {
|
|
72
|
+
request.headers.set(AUDIT_HEADER_INSTALL_ID, auth.installId);
|
|
73
|
+
}
|
|
74
|
+
if (auth.introspectionDegraded) {
|
|
75
|
+
request.headers.set(AUDIT_HEADER_AUTH_DEGRADED, "true");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Audit data loss > losing the user's call. SDK swallows hook
|
|
81
|
+
// throws but we don't want to depend on that behavior.
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
dispose,
|
|
87
|
+
setCurrentToolName: (name) => {
|
|
88
|
+
ref.current = name;
|
|
89
|
+
},
|
|
90
|
+
ref,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=auditClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditClient.js","sourceRoot":"","sources":["../../src/audit/auditClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { SolidCommerceClient } from "@solidcommerce/sdk";
|
|
2
|
+
export declare const DEFAULT_BASE_URL = "https://api.solidcommerce.com";
|
|
3
|
+
export declare const ENV_API_KEY = "SOLIDCOMMERCE_API_KEY";
|
|
4
|
+
export declare const ENV_BASE_URL = "SOLIDCOMMERCE_API_BASE_URL";
|
|
5
|
+
export interface ApiKeySource {
|
|
6
|
+
readonly apiKey: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Read the API key from env. Throws an actionable error if missing — fail
|
|
10
|
+
* fast at boot, do NOT defer to the first tool call.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadApiKeyFromEnv(): ApiKeySource;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the API base URL. Caller-provided override beats env beats default.
|
|
15
|
+
* We trim trailing slashes so that `${baseUrl}/v1/...` paths don't double-slash.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveBaseUrl(override?: string): string;
|
|
18
|
+
export interface BuildClientOptions {
|
|
19
|
+
readonly apiKey?: string;
|
|
20
|
+
readonly baseUrl?: string;
|
|
21
|
+
/** Inject for tests. */
|
|
22
|
+
readonly fetchImpl?: typeof fetch;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build a configured SolidCommerceClient with apiKey auth + base URL.
|
|
26
|
+
*
|
|
27
|
+
* Note: audit hooks are NOT attached here. The caller (index.ts) attaches
|
|
28
|
+
* them via `attachAuditHook(client)` so the ref-cell ownership stays at
|
|
29
|
+
* the dispatcher level.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildClient(opts?: BuildClientOptions): SolidCommerceClient;
|
|
32
|
+
export interface BuildBearerClientOptions {
|
|
33
|
+
readonly baseUrl?: string;
|
|
34
|
+
/** Inject for tests. */
|
|
35
|
+
readonly fetchImpl?: typeof fetch;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build a SolidCommerceClient that forwards a per-user OAuth bearer token
|
|
39
|
+
* (Phase 0 / E-A3). Platform re-validates the token via its JWKS config on
|
|
40
|
+
* every call, so this client does not itself verify the token. Audit hooks are
|
|
41
|
+
* NOT attached here — the caller attaches them via `attachAuditHook(client)`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildBearerClient(token: string, opts?: BuildBearerClientOptions): SolidCommerceClient;
|
|
44
|
+
//# sourceMappingURL=apiKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiKey.d.ts","sourceRoot":"","sources":["../../src/auth/apiKey.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,eAAO,MAAM,gBAAgB,kCAAkC,CAAC;AAChE,eAAO,MAAM,WAAW,0BAA0B,CAAC;AACnD,eAAO,MAAM,YAAY,+BAA+B,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,YAAY,CAUhD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,wBAAwB;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,mBAAmB,CAW9E;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,wBAAwB;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,wBAA6B,GAClC,mBAAmB,CAcrB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// src/auth/apiKey.ts
|
|
3
|
+
// Wave: W6 — Stream C
|
|
4
|
+
// Phase 0 / E-A3 — added buildBearerClient for per-user OAuth sessions.
|
|
5
|
+
//
|
|
6
|
+
// Resolves SOLIDCOMMERCE_API_KEY from env and builds a SolidCommerceClient.
|
|
7
|
+
// Two auth modes now coexist on the same server:
|
|
8
|
+
// * apiKey — service-to-service (stdio / Scotty / internal), env key.
|
|
9
|
+
// * bearer — per-user OAuth token forwarded from a remote host (E-A3).
|
|
10
|
+
//
|
|
11
|
+
// Mirrors the W5 CLI sdk.ts factory pattern (auth resolution + base URL +
|
|
12
|
+
// retry config) but simplified — no token refresh / keychain here; bearer
|
|
13
|
+
// tokens are captured per session and Platform re-validates every call.
|
|
14
|
+
// =============================================================================
|
|
15
|
+
import { SolidCommerceClient } from "@solidcommerce/sdk";
|
|
16
|
+
export const DEFAULT_BASE_URL = "https://api.solidcommerce.com";
|
|
17
|
+
export const ENV_API_KEY = "SOLIDCOMMERCE_API_KEY";
|
|
18
|
+
export const ENV_BASE_URL = "SOLIDCOMMERCE_API_BASE_URL";
|
|
19
|
+
/**
|
|
20
|
+
* Read the API key from env. Throws an actionable error if missing — fail
|
|
21
|
+
* fast at boot, do NOT defer to the first tool call.
|
|
22
|
+
*/
|
|
23
|
+
export function loadApiKeyFromEnv() {
|
|
24
|
+
const key = process.env[ENV_API_KEY]?.trim();
|
|
25
|
+
if (!key || key.length === 0) {
|
|
26
|
+
throw new Error(`${ENV_API_KEY} env var is required. Get one at ` +
|
|
27
|
+
"https://app.solidcommerce.com/settings/api-keys, then set it before " +
|
|
28
|
+
"starting the MCP server (e.g., in your Claude Desktop mcpServers config).");
|
|
29
|
+
}
|
|
30
|
+
return { apiKey: key };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the API base URL. Caller-provided override beats env beats default.
|
|
34
|
+
* We trim trailing slashes so that `${baseUrl}/v1/...` paths don't double-slash.
|
|
35
|
+
*/
|
|
36
|
+
export function resolveBaseUrl(override) {
|
|
37
|
+
const raw = (override ?? process.env[ENV_BASE_URL] ?? DEFAULT_BASE_URL).trim();
|
|
38
|
+
return raw.replace(/\/+$/, "");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build a configured SolidCommerceClient with apiKey auth + base URL.
|
|
42
|
+
*
|
|
43
|
+
* Note: audit hooks are NOT attached here. The caller (index.ts) attaches
|
|
44
|
+
* them via `attachAuditHook(client)` so the ref-cell ownership stays at
|
|
45
|
+
* the dispatcher level.
|
|
46
|
+
*/
|
|
47
|
+
export function buildClient(opts = {}) {
|
|
48
|
+
const apiKey = opts.apiKey ?? loadApiKeyFromEnv().apiKey;
|
|
49
|
+
const baseUrl = resolveBaseUrl(opts.baseUrl);
|
|
50
|
+
const clientOptions = {
|
|
51
|
+
baseUrl,
|
|
52
|
+
auth: { kind: "apiKey", apiKey },
|
|
53
|
+
};
|
|
54
|
+
if (opts.fetchImpl) {
|
|
55
|
+
clientOptions.fetch = opts.fetchImpl;
|
|
56
|
+
}
|
|
57
|
+
return new SolidCommerceClient(clientOptions);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build a SolidCommerceClient that forwards a per-user OAuth bearer token
|
|
61
|
+
* (Phase 0 / E-A3). Platform re-validates the token via its JWKS config on
|
|
62
|
+
* every call, so this client does not itself verify the token. Audit hooks are
|
|
63
|
+
* NOT attached here — the caller attaches them via `attachAuditHook(client)`.
|
|
64
|
+
*/
|
|
65
|
+
export function buildBearerClient(token, opts = {}) {
|
|
66
|
+
const trimmed = token.trim();
|
|
67
|
+
if (trimmed.length === 0) {
|
|
68
|
+
throw new Error("buildBearerClient: token must be a non-empty string.");
|
|
69
|
+
}
|
|
70
|
+
const baseUrl = resolveBaseUrl(opts.baseUrl);
|
|
71
|
+
const clientOptions = {
|
|
72
|
+
baseUrl,
|
|
73
|
+
auth: { kind: "bearer", token: trimmed },
|
|
74
|
+
};
|
|
75
|
+
if (opts.fetchImpl) {
|
|
76
|
+
clientOptions.fetch = opts.fetchImpl;
|
|
77
|
+
}
|
|
78
|
+
return new SolidCommerceClient(clientOptions);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=apiKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiKey.js","sourceRoot":"","sources":["../../src/auth/apiKey.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,qBAAqB;AACrB,sBAAsB;AACtB,wEAAwE;AACxE,EAAE;AACF,4EAA4E;AAC5E,iDAAiD;AACjD,yEAAyE;AACzE,0EAA0E;AAC1E,EAAE;AACF,0EAA0E;AAC1E,0EAA0E;AAC1E,wEAAwE;AACxE,gFAAgF;AAEhF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,+BAA+B,CAAC;AAChE,MAAM,CAAC,MAAM,WAAW,GAAG,uBAAuB,CAAC;AACnD,MAAM,CAAC,MAAM,YAAY,GAAG,4BAA4B,CAAC;AAMzD;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC;IAC7C,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,GAAG,WAAW,mCAAmC;YAC/C,sEAAsE;YACtE,2EAA2E,CAC9E,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAAiB;IAC9C,MAAM,GAAG,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,gBAAgB,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/E,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AASD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B,EAAE;IACvD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,iBAAiB,EAAE,CAAC,MAAM,CAAC;IACzD,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,aAAa,GAAyD;QAC1E,OAAO;QACP,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE;KACjC,CAAC;IACF,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,aAAa,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,mBAAmB,CAAC,aAAa,CAAC,CAAC;AAChD,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAa,EACb,OAAiC,EAAE;IAEnC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,aAAa,GAAyD;QAC1E,OAAO;QACP,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE;KACzC,CAAC;IACF,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,aAAa,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,mBAAmB,CAAC,aAAa,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export declare const ENV_OAUTH_ENABLED = "SC_MCP_OAUTH_ENABLED";
|
|
2
|
+
export declare const ENV_INTROSPECT_URL = "SC_AUTH_INTROSPECT_URL";
|
|
3
|
+
export declare const ENV_MCP_CLIENT_ID = "SC_MCP_CLIENT_ID";
|
|
4
|
+
export declare const ENV_MCP_CLIENT_SECRET = "SC_MCP_CLIENT_SECRET";
|
|
5
|
+
export interface OAuthConfig {
|
|
6
|
+
/** When false, the server is apiKey-only and ignores bearer headers. */
|
|
7
|
+
readonly enabled: boolean;
|
|
8
|
+
readonly introspectUrl: string;
|
|
9
|
+
readonly clientId: string;
|
|
10
|
+
readonly clientSecret: string;
|
|
11
|
+
/** Per-introspection-call network timeout. */
|
|
12
|
+
readonly timeoutMs: number;
|
|
13
|
+
/** Max lifetime for a cached ACTIVE result (capped under token `exp`). */
|
|
14
|
+
readonly positiveTtlMs: number;
|
|
15
|
+
/** Lifetime for a cached INACTIVE result (short — revocation re-issue). */
|
|
16
|
+
readonly negativeTtlMs: number;
|
|
17
|
+
/** Clock skew subtracted from token `exp` when computing positive TTL. */
|
|
18
|
+
readonly skewMs: number;
|
|
19
|
+
/** Consecutive failures before the circuit opens. */
|
|
20
|
+
readonly circuitThreshold: number;
|
|
21
|
+
/** How long the circuit stays open before a probe is allowed. */
|
|
22
|
+
readonly circuitCooldownMs: number;
|
|
23
|
+
/** Hard ceiling on cache entries (bounded memory). */
|
|
24
|
+
readonly maxCacheEntries: number;
|
|
25
|
+
}
|
|
26
|
+
/** Default MCP confidential client id — matches OpenIddictSeeder.McpServerClientId. */
|
|
27
|
+
export declare const DEFAULT_MCP_CLIENT_ID = "sc-mcp-server";
|
|
28
|
+
/**
|
|
29
|
+
* Load OAuth config from env. `enabled` defaults to ON when both an introspect
|
|
30
|
+
* URL and a client secret are present, so a correctly-provisioned deployment
|
|
31
|
+
* "just works"; an operator can force it on/off with SC_MCP_OAUTH_ENABLED.
|
|
32
|
+
*/
|
|
33
|
+
export declare function loadOAuthConfigFromEnv(env?: NodeJS.ProcessEnv): OAuthConfig;
|
|
34
|
+
export type AuthMode = "oauth" | "apiKey";
|
|
35
|
+
export interface SessionAuthContext {
|
|
36
|
+
readonly mode: AuthMode;
|
|
37
|
+
/** Bearer token to forward to Platform (oauth mode only). */
|
|
38
|
+
readonly token?: string;
|
|
39
|
+
readonly companyId?: string;
|
|
40
|
+
readonly installId?: string;
|
|
41
|
+
readonly subject?: string;
|
|
42
|
+
readonly scopes: ReadonlyArray<string>;
|
|
43
|
+
/** Token `exp` (epoch seconds), when known. */
|
|
44
|
+
readonly expiresAtEpoch?: number;
|
|
45
|
+
/** True when forwarded unverified (Auth outage). Platform is authority. */
|
|
46
|
+
readonly introspectionDegraded: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Resolution outcome: either an accepted session context, or a rejection that
|
|
49
|
+
* the transport turns into a 401 (we never silently downgrade a bad bearer). */
|
|
50
|
+
export type AuthResolution = {
|
|
51
|
+
readonly ok: true;
|
|
52
|
+
readonly context: SessionAuthContext;
|
|
53
|
+
} | {
|
|
54
|
+
readonly ok: false;
|
|
55
|
+
readonly status: number;
|
|
56
|
+
readonly error: string;
|
|
57
|
+
};
|
|
58
|
+
type IntrospectOutcome = {
|
|
59
|
+
readonly kind: "active";
|
|
60
|
+
readonly claims: IntrospectionClaims;
|
|
61
|
+
} | {
|
|
62
|
+
readonly kind: "inactive";
|
|
63
|
+
} | {
|
|
64
|
+
readonly kind: "degraded";
|
|
65
|
+
readonly reason: string;
|
|
66
|
+
};
|
|
67
|
+
export interface IntrospectionClaims {
|
|
68
|
+
readonly companyId?: string;
|
|
69
|
+
readonly installId?: string;
|
|
70
|
+
readonly subject?: string;
|
|
71
|
+
readonly scopes: ReadonlyArray<string>;
|
|
72
|
+
readonly expiresAtEpoch?: number;
|
|
73
|
+
}
|
|
74
|
+
export interface Introspector {
|
|
75
|
+
introspect(token: string): Promise<IntrospectOutcome>;
|
|
76
|
+
/** Test/diagnostics: current circuit state. */
|
|
77
|
+
readonly isCircuitOpen: () => boolean;
|
|
78
|
+
}
|
|
79
|
+
export interface IntrospectorDeps {
|
|
80
|
+
/** Injectable for tests. Defaults to global fetch. */
|
|
81
|
+
readonly fetchImpl?: typeof fetch;
|
|
82
|
+
/** Injectable clock (ms epoch). Defaults to Date.now. */
|
|
83
|
+
readonly now?: () => number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Build an introspector. Holds the cache + circuit state for the process; one
|
|
87
|
+
* instance is shared across all sessions (cache hits make repeated initialize
|
|
88
|
+
* calls for the same token cheap).
|
|
89
|
+
*/
|
|
90
|
+
export declare function createIntrospector(config: OAuthConfig, deps?: IntrospectorDeps): Introspector;
|
|
91
|
+
/** Extract a bearer token, or undefined if the header is absent / not Bearer. */
|
|
92
|
+
export declare function parseBearer(authorizationHeader: string | undefined): string | undefined;
|
|
93
|
+
export interface AuthResolverDeps {
|
|
94
|
+
readonly config: OAuthConfig;
|
|
95
|
+
readonly introspector: Introspector;
|
|
96
|
+
/** Whether an env API key is available for the no-bearer fallback. */
|
|
97
|
+
readonly apiKeyAvailable: boolean;
|
|
98
|
+
}
|
|
99
|
+
export interface SessionAuthResolver {
|
|
100
|
+
resolve(authorizationHeader: string | undefined): Promise<AuthResolution>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Build the resolver used by the Streamable HTTP transport at `initialize`.
|
|
104
|
+
* See the resilience matrix in this file's header for the full decision table.
|
|
105
|
+
*/
|
|
106
|
+
export declare function createSessionAuthResolver(deps: AuthResolverDeps): SessionAuthResolver;
|
|
107
|
+
export {};
|
|
108
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"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"}
|