Skip to main content
Glama

scp_authorize

Authorize secure access to customer e-commerce data from SCP-enabled merchants. Request customer permissions for orders, loyalty points, and shopping preferences using their real email address.

Instructions

BEFORE USING THIS ENSURE THE DOMAIN SUPPORTS SCP BY CALLING scp_discover FIRST. Authorize access to a merchant's customer context via SCP. Must be called before accessing any customer data. IMPORTANT: You must ask the user for their REAL email address - never use placeholder emails like user@example.com. Ask: "What email address do you use with [Merchant]?" and wait for their response.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
domainYesMerchant domain (e.g., 'acmestore.com')
emailYesCustomer's REAL email address (must ask user for this - never use example.com or placeholder emails)
scopesYesRequested scopes (e.g., ['orders', 'loyalty', 'intent:read']). Best practice: request all needed scopes upfront.

Implementation Reference

  • Implements the core logic for scp_authorize: validates real email (rejects example.com), checks existing authorization and scopes, discovers SCP endpoint, performs OAuth authorization flow using completeAuthorizationFlow from auth/oauth-client.js, encrypts tokens, and stores authorization info.
    async function handleAuthorize(
      domain: string,
      email: string,
      scopes: string[]
    ) {
      // Validate email - reject example/test emails
      if (email.endsWith('@example.com') || email.includes('example')) {
        return {
          content: [
            {
              type: 'text',
              text: `❌ Invalid email address: ${email}\n\nPlease use a REAL email address, not an example one.\n\nAsk the user: "What email address do you use with ${domain}?"\n\nThen call scp_authorize again with their actual email address.`
            }
          ]
        };
      }
    
      // Check if already authorized
      const existing = await getAuthorization(domain);
    
      if (existing) {
        // Compare requested scopes with existing scopes
        const existingScopes = new Set(existing.scopes);
        const requestedScopes = new Set(scopes);
    
        // Check if scopes are identical
        const scopesMatch =
          existingScopes.size === requestedScopes.size &&
          [...requestedScopes].every(s => existingScopes.has(s));
    
        if (scopesMatch && existing.customer_email === email) {
          // Same scopes and email - just return that they're already authorized
          return {
            content: [
              {
                type: 'text',
                text: `✓ Already authorized with ${domain}\nEmail: ${existing.customer_email}\nScopes: ${existing.scopes.join(', ')}`
              }
            ]
          };
        }
    
        // Scopes are different or email is different - need to re-authorize
        if (existing.customer_email !== email) {
          // Different email - must revoke first
          return {
            content: [
              {
                type: 'text',
                text: `⚠️ Already authorized with ${domain} using ${existing.customer_email}.\n\nTo authorize with a different email (${email}), please revoke the existing authorization first:\nscp_revoke_authorization(domain="${domain}")\n\nThen try authorizing again.`
              }
            ]
          };
        }
    
        // Same email, different scopes - re-authorize
        const addedScopes = [...requestedScopes].filter(s => !existingScopes.has(s));
        const removedScopes = [...existingScopes].filter(s => !requestedScopes.has(s));
    
        console.error(`[SCP] Re-authorizing ${domain} with scope changes:`, {
          added: addedScopes,
          removed: removedScopes
        });
      }
    
      // Discover endpoint
      const discovery = await discoverWithCapabilities(domain);
      if (!discovery) {
        throw new Error(`Could not discover Shopper Context Protocol endpoint for ${domain}`);
      }
    
      const { endpoint, capabilities } = discovery;
    
      // Verify scopes are supported
      if (capabilities && capabilities.scopes_supported) {
        const unsupported = scopes.filter(s => !capabilities.scopes_supported.includes(s));
        if (unsupported.length > 0) {
          throw new Error(`Unsupported scopes: ${unsupported.join(', ')}`);
        }
      }
    
      // Complete OAuth flow (will trigger email verification)
      const { tokenResponse } = await completeAuthorizationFlow(endpoint, email, domain, scopes);
    
      // Store authorization (will update if existing)
      const now = Date.now();
      await storeAuthorization({
        merchant_domain: domain,
        scp_endpoint: endpoint,
        customer_id: tokenResponse.customer_id,
        customer_email: tokenResponse.email,
        access_token_encrypted: encryptToken(tokenResponse.access_token),
        refresh_token_encrypted: encryptToken(tokenResponse.refresh_token),
        expires_at: now + (tokenResponse.expires_in * 1000),
        scopes: tokenResponse.scope.split(' '),
        created_at: existing ? existing.created_at : now, // Preserve original creation time
        updated_at: now
      });
    
      const action = existing ? 'Re-authorized' : 'Connected to';
    
      return {
        content: [
          {
            type: 'text',
            text: `✓ ${action} ${domain}!\nGranted scopes: ${tokenResponse.scope}\nCustomer: ${tokenResponse.email}`
          }
        ]
      };
    }
  • src/server.ts:306-327 (registration)
    Registers the scp_authorize tool in the MCP server's ListToolsRequestSchema handler, defining its name, detailed description emphasizing real email usage, and input schema.
      name: 'scp_authorize',
      description: 'BEFORE USING THIS ENSURE THE DOMAIN SUPPORTS SCP BY CALLING scp_discover FIRST. Authorize access to a merchant\'s customer context via SCP. Must be called before accessing any customer data. IMPORTANT: You must ask the user for their REAL email address - never use placeholder emails like user@example.com. Ask: "What email address do you use with [Merchant]?" and wait for their response.',
      inputSchema: {
        type: 'object',
        properties: {
          domain: {
            type: 'string',
            description: 'Merchant domain (e.g., \'acmestore.com\')'
          },
          email: {
            type: 'string',
            description: 'Customer\'s REAL email address (must ask user for this - never use example.com or placeholder emails)'
          },
          scopes: {
            type: 'array',
            items: { type: 'string' },
            description: 'Requested scopes (e.g., [\'orders\', \'loyalty\', \'intent:read\']). Best practice: request all needed scopes upfront.'
          }
        },
        required: ['domain', 'email', 'scopes']
      }
    },
  • Input schema for scp_authorize tool, specifying required properties: domain (string), email (string), scopes (array of strings).
    inputSchema: {
      type: 'object',
      properties: {
        domain: {
          type: 'string',
          description: 'Merchant domain (e.g., \'acmestore.com\')'
        },
        email: {
          type: 'string',
          description: 'Customer\'s REAL email address (must ask user for this - never use example.com or placeholder emails)'
        },
        scopes: {
          type: 'array',
          items: { type: 'string' },
          description: 'Requested scopes (e.g., [\'orders\', \'loyalty\', \'intent:read\']). Best practice: request all needed scopes upfront.'
        }
      },
      required: ['domain', 'email', 'scopes']
    }
  • Switch case in CallToolRequestSchema handler that routes scp_authorize calls to the handleAuthorize function.
    case 'scp_authorize':
      return await handleAuthorize(args.domain as string, args.email as string, args.scopes as string[]);

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/shopper-context-protocol/scp-mcp-wrapper'

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