Skip to main content
Glama

Connect Store

store_connect

Add, sync, or list Shopify and WooCommerce stores. Connect new stores to sync products, orders, and customers; refresh cached data; or view all connected stores with sync counts.

Instructions

Manage Shopify or WooCommerce store connections. action="connect" adds a new store and performs an initial sync of products, orders, and customers; action="sync" refreshes cached data for an existing store; action="list" returns all connected stores with their sync counts. Returns a JSON payload with store metadata (id, name, platform, url, counts, last_sync) — credentials are never returned.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
actionYesconnect = add new store, sync = refresh existing store, list = show all stores
nameNoHuman-readable store name (required for connect, e.g. "My Shop")
platformNo"shopify" or "woocommerce" (required for connect)
urlNoStore base URL starting with https:// (required for connect, e.g. "https://myshop.myshopify.com")
api_keyNoPlatform API access token (required for connect)
api_secretNoPlatform API secret — REQUIRED for woocommerce, ignored for shopify
store_idNoExisting store UUID (required for sync)

Implementation Reference

  • src/index.ts:37-106 (registration)
    Registration of the 'store_connect' tool on the MCP server, including the input schema (z.object with action, name, platform, url, api_key, api_secret, store_id) and the handler that dispatches to connectStore (action='connect'), syncStore (action='sync'), or lists stores (action='list').
    // ── Tool: store_connect ───────────────────────────────────────────
    server.registerTool(
      'store_connect',
      {
        title: 'Connect Store',
        description: 'Manage Shopify or WooCommerce store connections. action="connect" adds a new store and performs an initial sync of products, orders, and customers; action="sync" refreshes cached data for an existing store; action="list" returns all connected stores with their sync counts. Returns a JSON payload with store metadata (id, name, platform, url, counts, last_sync) — credentials are never returned.',
        inputSchema: z.object({
          action: z.enum(['connect', 'sync', 'list']).describe('connect = add new store, sync = refresh existing store, list = show all stores'),
          name: z.string().min(1).optional().describe('Human-readable store name (required for connect, e.g. "My Shop")'),
          platform: PlatformSchema.optional().describe('"shopify" or "woocommerce" (required for connect)'),
          url: z.string().url().optional().describe('Store base URL starting with https:// (required for connect, e.g. "https://myshop.myshopify.com")'),
          api_key: z.string().min(1).optional().describe('Platform API access token (required for connect)'),
          api_secret: z.string().min(1).optional().describe('Platform API secret — REQUIRED for woocommerce, ignored for shopify'),
          store_id: z.string().uuid().optional().describe('Existing store UUID (required for sync)'),
        }),
        annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
      },
      async ({ action, name, platform, url, api_key, api_secret, store_id }) => {
        try {
          if (action === 'list') {
            const stores = await storage.getStores();
            if (stores.length === 0) {
              return {
                content: [{ type: 'text' as const, text: 'No stores are connected yet. Use action="connect" with name, platform, url, and api_key to add one.' }],
              };
            }
            return {
              content: [{ type: 'text' as const, text: JSON.stringify({
                total: stores.length,
                stores: stores.map((s) => ({
                  id: s.id, name: s.name, platform: s.platform, url: s.url,
                  products: s.product_count, orders: s.order_count, customers: s.customer_count,
                  last_sync: s.last_sync_at,
                })),
              }, null, 2) }],
            };
          }
    
          if (action === 'sync') {
            if (!store_id) {
              return { content: [{ type: 'text' as const, text: 'store_id is required for action="sync". Use action="list" to find connected store IDs.' }], isError: true };
            }
            const result = await syncStore(store_id);
            return { content: [{ type: 'text' as const, text: `Store synced successfully — ${result.products} products, ${result.orders} orders, ${result.customers} customers refreshed.` }] };
          }
    
          // connect
          const missing: string[] = [];
          if (!name) missing.push('name');
          if (!platform) missing.push('platform');
          if (!url) missing.push('url');
          if (!api_key) missing.push('api_key');
          if (missing.length > 0) {
            return { content: [{ type: 'text' as const, text: `Missing required field(s) for connect: ${missing.join(', ')}. Required: name, platform, url, api_key. WooCommerce also needs api_secret.` }], isError: true };
          }
          if (platform === 'woocommerce' && !api_secret) {
            return { content: [{ type: 'text' as const, text: 'api_secret is required when platform="woocommerce" (WooCommerce REST API uses consumer_key + consumer_secret).' }], isError: true };
          }
          const result = await connectStore({ name: name!, platform: platform!, url: url!, api_key: api_key!, api_secret });
          return {
            content: [{ type: 'text' as const, text: JSON.stringify({
              message: `Store "${result.store.name}" connected successfully.`,
              store_id: result.store.id,
              synced: result.synced,
              next_steps: 'Use inventory_status, customers_segment, or report_daily with this store_id to start analyzing.',
            }, null, 2) }],
          };
        } catch (e) { return handleToolError(e); }
      }
    );
  • The core handler function connectStore() that validates for duplicates, tests the platform connection (Shopify or WooCommerce), fetches initial data (products, orders, customers), stores data, and returns the store metadata (with credentials redacted).
    export async function connectStore(input: StoreConnectInput): Promise<{ store: Omit<StoreConfig, 'api_key' | 'api_secret'>; synced: { products: number; orders: number; customers: number } }> {
      // Check for duplicate
      const existing = await storage.getStoreByUrl(input.url);
      if (existing) throw new DuplicateError('store URL', input.url);
    
      // Test connection
      if (input.platform === 'shopify') {
        await testShopifyConnection(input.url, input.api_key);
      } else {
        if (!input.api_secret) throw new PlatformError('WooCommerce', 'api_secret is required for WooCommerce');
        await testWooConnection(input.url, input.api_key, input.api_secret);
      }
    
      const storeId = uuidv4();
      const now = new Date().toISOString();
    
      // Fetch initial data
      let products, orders, customers;
      if (input.platform === 'shopify') {
        [products, orders, customers] = await Promise.all([
          fetchShopifyProducts(storeId, input.url, input.api_key),
          fetchShopifyOrders(storeId, input.url, input.api_key),
          fetchShopifyCustomers(storeId, input.url, input.api_key),
        ]);
      } else {
        [products, orders, customers] = await Promise.all([
          fetchWooProducts(storeId, input.url, input.api_key, input.api_secret!),
          fetchWooOrders(storeId, input.url, input.api_key, input.api_secret!),
          fetchWooCustomers(storeId, input.url, input.api_key, input.api_secret!),
        ]);
      }
    
      // Store data
      await Promise.all([
        storage.upsertProducts(products),
        storage.upsertOrders(orders),
        storage.upsertCustomers(customers),
      ]);
    
      const store: StoreConfig = {
        id: storeId,
        name: input.name,
        platform: input.platform,
        url: input.url,
        api_key: input.api_key,
        api_secret: input.api_secret,
        connected_at: now,
        last_sync_at: now,
        product_count: products.length,
        order_count: orders.length,
        customer_count: customers.length,
      };
    
      await storage.addStore(store);
    
      // Redact credentials from response
      const { api_key: _ak, api_secret: _as, ...safeStore } = store;
    
      return {
        store: safeStore,
        synced: { products: products.length, orders: orders.length, customers: customers.length },
      };
    }
  • StoreConnectInputSchema — Zod schema defining the input shape for store_connect: name, platform, url, api_key, and optional api_secret.
    export const StoreConnectInputSchema = z.object({
      name: z.string().min(1).describe('Store display name'),
      platform: PlatformSchema.describe('E-commerce platform'),
      url: z.string().url().describe('Store URL (e.g. https://your-store.myshopify.com)'),
      api_key: z.string().min(1).describe('API access token or consumer key'),
      api_secret: z.string().optional().describe('API secret (required for WooCommerce)'),
    });
    export type StoreConnectInput = z.infer<typeof StoreConnectInputSchema>;
  • syncStore helper function used by store_connect when action='sync' — refreshes data for an existing store by fetching products, orders, and customers again.
    export async function syncStore(storeId: string): Promise<{ products: number; orders: number; customers: number }> {
      const store = await storage.getStoreById(storeId);
      if (!store) throw new NotFoundError('Store', storeId);
    
      let products, orders, customers;
      if (store.platform === 'shopify') {
        [products, orders, customers] = await Promise.all([
          fetchShopifyProducts(store.id, store.url, store.api_key),
          fetchShopifyOrders(store.id, store.url, store.api_key),
          fetchShopifyCustomers(store.id, store.url, store.api_key),
        ]);
      } else {
        if (!store.api_secret) throw new PlatformError('WooCommerce', 'Missing API secret');
        [products, orders, customers] = await Promise.all([
          fetchWooProducts(store.id, store.url, store.api_key, store.api_secret),
          fetchWooOrders(store.id, store.url, store.api_key, store.api_secret),
          fetchWooCustomers(store.id, store.url, store.api_key, store.api_secret),
        ]);
      }
    
      await Promise.all([
        storage.upsertProducts(products),
        storage.upsertOrders(orders),
        storage.upsertCustomers(customers),
      ]);
    
      await storage.updateStore(storeId, {
        last_sync_at: new Date().toISOString(),
        product_count: products.length,
        order_count: orders.length,
        customer_count: customers.length,
      });
    
      return { products: products.length, orders: orders.length, customers: customers.length };
    }
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses that connect triggers an initial sync of products/orders/customers, that list returns metadata with sync counts, and that credentials are never returned. This adds behavioral context beyond annotations, which only indicate readOnly/false and destructive/false.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences convey all essential information: the three actions with their effects, and the return format. No redundant words; each sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 3 actions and 7 parameters but no output schema, the description covers purpose, usage, behavior, and output structure. It leaves no critical gaps for typical use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already provides 100% parameter descriptions. The description adds value by grouping parameters per action and by explicitly noting that credentials are not returned, but does not significantly extend the schema's semantic coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool manages Shopify/WooCommerce store connections with three distinct actions (connect, sync, list). It differentiates from sibling tools focused on analytics/reports by specifying the store connection domain.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells when to use each action: connect for adding and initial sync, sync for refreshing, list for viewing all stores. It also indirectly indicates required parameters per action (e.g., connect needs name, platform, url, api_key; sync needs store_id).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/enzoemir1/shopops-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server