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