UNPKG

@coretext-ai/public-gsuite-mcp-d495ced0-92b0-4d0e-ad89-f27c9e31f4f9

Version:
2,568 lines 126 kB
import axios from 'axios';
import { Logger } from '../services/logger.js';
export class GoogleGmailClient {
    constructor(config) {
        this.config = config;
        // Generate unique session ID for this client instance
        this.sessionId = `google-gmail-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
        // Initialize logger (fallback to console if not provided)
        this.logger = config.logger || new Logger({
            logLevel: 'ERROR',
            component: 'client',
            enableConsole: true,
            enableShipping: false,
            serverName: 'public-gsuite-mcp'
        });
        this.logger.info('CLIENT_INIT', 'Client instance created', {
            baseUrl: this.resolveBaseUrl(),
            timeout: this.config.timeout || 30000,
            hasRateLimit: !!this.config.rateLimit,
            configKeys: Object.keys(config)
        });
        // Initialize OAuth client from config if provided
        this.oauthClient = config.oauthClient;
        this.httpClient = axios.create({
            baseURL: this.resolveBaseUrl(),
            timeout: this.config.timeout || 30000,
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json',
                'User-Agent': 'public-gsuite-mcp/1.0.0',
                ...this.getAuthHeaders()
            },
        });
        // Add request interceptor for rate limiting
        if (this.config.rateLimit) {
            this.setupRateLimit(this.config.rateLimit);
        }
        // Add request interceptor for logging
        this.httpClient.interceptors.request.use((config) => {
            this.logger.logRequestStart(config.method?.toUpperCase() || 'GET', `${config.baseURL}${config.url}`, {
                hasData: !!config.data,
                hasParams: !!(config.params && Object.keys(config.params).length > 0),
                headers: Object.keys(config.headers || {})
            });
            if (config.data) {
                this.logger.debug('HTTP_REQUEST_BODY', 'Request body data', {
                    dataType: typeof config.data,
                    dataSize: JSON.stringify(config.data).length
                });
            }
            if (config.params && Object.keys(config.params).length > 0) {
                this.logger.debug('HTTP_REQUEST_PARAMS', 'Query parameters', {
                    paramCount: Object.keys(config.params).length,
                    paramKeys: Object.keys(config.params)
                });
            }
            return config;
        }, (error) => {
            this.logger.error('HTTP_REQUEST_ERROR', 'Request interceptor error', {
                error: error.message,
                code: error.code
            });
            return Promise.reject(error);
        });
        // Add response interceptor for logging and error handling
        this.httpClient.interceptors.response.use((response) => {
            this.logger.logRequestSuccess(response.config?.method?.toUpperCase() || 'GET', `${response.config?.baseURL}${response.config?.url}`, response.status, 0, // Duration will be calculated in endpoint methods
            {
                statusText: response.statusText,
                responseSize: JSON.stringify(response.data).length,
                headers: Object.keys(response.headers || {})
            });
            return response;
        }, (error) => {
            this.logger.logRequestError(error.config?.method?.toUpperCase() || 'GET', `${error.config?.baseURL}${error.config?.url}`, error, 0, // Duration will be calculated in endpoint methods
            {
                hasResponseData: !!error.response?.data
            });
            throw error;
        });
    }
    setupRateLimit(requestsPerMinute) {
        const interval = 60000 / requestsPerMinute; // ms between requests
        let lastRequestTime = 0;
        this.logger.info('RATE_LIMIT_SETUP', 'Rate limiting configured', {
            requestsPerMinute,
            intervalMs: interval
        });
        this.httpClient.interceptors.request.use(async (config) => {
            const now = Date.now();
            const timeSinceLastRequest = now - lastRequestTime;
            if (timeSinceLastRequest < interval) {
                const delayMs = interval - timeSinceLastRequest;
                this.logger.logRateLimit('HTTP_REQUEST', delayMs, {
                    timeSinceLastRequest,
                    requiredInterval: interval
                });
                await new Promise(resolve => setTimeout(resolve, delayMs));
            }
            lastRequestTime = Date.now();
            return config;
        });
    }
    resolveBaseUrl() {
        let baseUrl = 'https://gmail.googleapis.com/gmail/v1';
        // Handle dynamic domain replacement (e.g., YOUR_DOMAIN placeholder)
        if (baseUrl.includes('YOUR_DOMAIN')) {
            const domainEnvVar = `GOOGLE_GMAIL_DOMAIN`;
            const domain = process.env[domainEnvVar];
            if (!domain) {
                throw new Error(`Missing domain configuration. Please set ${domainEnvVar} environment variable.`);
            }
            baseUrl = baseUrl.replace('YOUR_DOMAIN', domain);
            console.error(`[GOOGLE_GMAIL] Resolved base URL: ${baseUrl}`);
        }
        return baseUrl;
    }
    getAuthHeaders() {
        // OAuth authentication (both ConstructionWire and standard OAuth) - handled dynamically
        // Tokens will be applied asynchronously via makeAuthenticatedRequest
        this.logger.logAuthEvent('oauth_auth_setup', true, {
            authType: 'oauth2',
            message: 'OAuth tokens will be applied dynamically during requests',
            oauthClientPresent: !!this.oauthClient
        });
        return {};
    }
    /**
     * Initialize the client (for OAuth clients that need initialization)
     */
    async initialize() {
        if (this.oauthClient) {
            await this.oauthClient.initialize();
            this.logger.info('CLIENT_INITIALIZE', 'OAuth client initialized');
        }
    }
    /**
     * Get the session ID for this client instance
     */
    getSessionId() {
        return this.sessionId;
    }
    /**
     * Make an authenticated request with proper headers
     */
    async makeAuthenticatedRequest(config) {
        // Get OAuth token for standard OAuth
        this.logger.info('REQUEST_AUTH', 'Applying standard OAuth authentication', {
            authType: 'oauth2',
            requestUrl: config.url,
            hasOAuthClient: !!this.oauthClient
        });
        if (this.oauthClient) {
            const accessToken = await this.oauthClient.getValidAccessToken();
            config.headers = {
                ...config.headers,
                'Authorization': `Bearer ${accessToken}`
            };
            this.logger.logAuthEvent('oauth_token_applied', true, {
                authType: 'oauth2',
                tokenPreview: accessToken ? accessToken.substring(0, 8) + '...' : 'null',
                header: 'Authorization',
                tokenSource: 'standard_oauth',
                finalHeaders: Object.keys(config.headers)
            });
        }
        else {
            this.logger.warn('OAUTH_CLIENT_MISSING', 'OAuth client not available for OAuth-enabled template', {
                authType: 'oauth2',
                requestUrl: config.url
            });
        }
        return this.httpClient.request(config);
    }
    buildPath(template, params) {
        let path = template;
        // Custom encoding that preserves forward slashes for API paths
        const encodePathComponent = (value) => {
            // For Google API resource names like "people/c123", preserve the forward slash
            return encodeURIComponent(value).replace(/%2F/g, '/');
        };
        // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
        const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
        let match;
        const processedParams = [];
        while ((match = googlePathTemplateRegex.exec(template)) !== null) {
            const fullMatch = match[0]; // e.g., "{resourceName=people/*}"
            const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
            if (paramName && params[paramName] !== undefined) {
                path = path.replace(fullMatch, encodePathComponent(String(params[paramName])));
                processedParams.push(paramName);
            }
        }
        // Handle standard path templates: {resourceName}
        for (const [key, value] of Object.entries(params)) {
            if (!processedParams.includes(key)) {
                const standardTemplate = `{${key}}`;
                if (path.includes(standardTemplate)) {
                    path = path.replace(standardTemplate, encodePathComponent(String(value)));
                    processedParams.push(key);
                }
            }
        }
        this.logger.debug('PATH_BUILD', 'Built API path from template', {
            template,
            resultPath: path,
            paramCount: Object.keys(params).length,
            paramKeys: Object.keys(params),
            processedParams,
            hasGoogleTemplates: googlePathTemplateRegex.test(template)
        });
        return path;
    }
    async sendMessage(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'send_message',
            method: 'POST',
            path: '/users/{userId}/messages/send',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/messages/send';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["raw"] !== undefined) {
                bodyParams["raw"] = params["raw"];
                extractedParams.push("raw");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.raw) {
                throw new Error(`Missing required parameter: raw`);
            }
            const path = this.buildPath('/users/{userId}/messages/send', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'POST', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'send_message',
                method: 'POST',
                path: '/users/{userId}/messages/send',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'send_message',
                method: 'POST',
                path: '/users/{userId}/messages/send',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute send_message: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async getMessage(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'get_message',
            method: 'GET',
            path: '/users/{userId}/messages/{id}',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/messages/{id}';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["format"] !== undefined) {
                queryParams["format"] = params["format"];
                extractedParams.push("format");
            }
            if (params["metadataHeaders"] !== undefined) {
                queryParams["metadataHeaders"] = params["metadataHeaders"];
                extractedParams.push("metadataHeaders");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/messages/{id}', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'GET', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'get_message',
                method: 'GET',
                path: '/users/{userId}/messages/{id}',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'get_message',
                method: 'GET',
                path: '/users/{userId}/messages/{id}',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute get_message: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async listMessages(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'list_messages',
            method: 'GET',
            path: '/users/{userId}/messages',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/messages';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["q"] !== undefined) {
                queryParams["q"] = params["q"];
                extractedParams.push("q");
            }
            if (params["labelIds"] !== undefined) {
                queryParams["labelIds"] = params["labelIds"];
                extractedParams.push("labelIds");
            }
            if (params["maxResults"] !== undefined) {
                queryParams["maxResults"] = params["maxResults"];
                extractedParams.push("maxResults");
            }
            if (params["pageToken"] !== undefined) {
                queryParams["pageToken"] = params["pageToken"];
                extractedParams.push("pageToken");
            }
            if (params["includeSpamTrash"] !== undefined) {
                queryParams["includeSpamTrash"] = params["includeSpamTrash"];
                extractedParams.push("includeSpamTrash");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            const path = this.buildPath('/users/{userId}/messages', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'GET', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'list_messages',
                method: 'GET',
                path: '/users/{userId}/messages',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'list_messages',
                method: 'GET',
                path: '/users/{userId}/messages',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute list_messages: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async modifyMessage(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'modify_message',
            method: 'POST',
            path: '/users/{userId}/messages/{id}/modify',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/messages/{id}/modify';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["addLabelIds"] !== undefined) {
                bodyParams["addLabelIds"] = params["addLabelIds"];
                extractedParams.push("addLabelIds");
            }
            if (params["removeLabelIds"] !== undefined) {
                bodyParams["removeLabelIds"] = params["removeLabelIds"];
                extractedParams.push("removeLabelIds");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/messages/{id}/modify', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'POST', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'modify_message',
                method: 'POST',
                path: '/users/{userId}/messages/{id}/modify',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'modify_message',
                method: 'POST',
                path: '/users/{userId}/messages/{id}/modify',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute modify_message: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async trashMessage(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'trash_message',
            method: 'POST',
            path: '/users/{userId}/messages/{id}/trash',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/messages/{id}/trash';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/messages/{id}/trash', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'POST', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'trash_message',
                method: 'POST',
                path: '/users/{userId}/messages/{id}/trash',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'trash_message',
                method: 'POST',
                path: '/users/{userId}/messages/{id}/trash',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute trash_message: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async untrashMessage(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'untrash_message',
            method: 'POST',
            path: '/users/{userId}/messages/{id}/untrash',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/messages/{id}/untrash';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/messages/{id}/untrash', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'POST', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'untrash_message',
                method: 'POST',
                path: '/users/{userId}/messages/{id}/untrash',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'untrash_message',
                method: 'POST',
                path: '/users/{userId}/messages/{id}/untrash',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute untrash_message: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async deleteMessage(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'delete_message',
            method: 'DELETE',
            path: '/users/{userId}/messages/{id}',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/messages/{id}';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/messages/{id}', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'DELETE', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'delete_message',
                method: 'DELETE',
                path: '/users/{userId}/messages/{id}',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'delete_message',
                method: 'DELETE',
                path: '/users/{userId}/messages/{id}',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute delete_message: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async listLabels(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'list_labels',
            method: 'GET',
            path: '/users/{userId}/labels',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/labels';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            const path = this.buildPath('/users/{userId}/labels', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'GET', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'list_labels',
                method: 'GET',
                path: '/users/{userId}/labels',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'list_labels',
                method: 'GET',
                path: '/users/{userId}/labels',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute list_labels: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async getLabel(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'get_label',
            method: 'GET',
            path: '/users/{userId}/labels/{id}',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/labels/{id}';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/labels/{id}', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'GET', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'get_label',
                method: 'GET',
                path: '/users/{userId}/labels/{id}',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'get_label',
                method: 'GET',
                path: '/users/{userId}/labels/{id}',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute get_label: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async createLabel(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'create_label',
            method: 'POST',
            path: '/users/{userId}/labels',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/labels';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["name"] !== undefined) {
                bodyParams["name"] = params["name"];
                extractedParams.push("name");
            }
            if (params["labelListVisibility"] !== undefined) {
                bodyParams["labelListVisibility"] = params["labelListVisibility"];
                extractedParams.push("labelListVisibility");
            }
            if (params["messageListVisibility"] !== undefined) {
                bodyParams["messageListVisibility"] = params["messageListVisibility"];
                extractedParams.push("messageListVisibility");
            }
            if (params["color"] !== undefined) {
                bodyParams["color"] = params["color"];
                extractedParams.push("color");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.name) {
                throw new Error(`Missing required parameter: name`);
            }
            const path = this.buildPath('/users/{userId}/labels', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'POST', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'create_label',
                method: 'POST',
                path: '/users/{userId}/labels',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'create_label',
                method: 'POST',
                path: '/users/{userId}/labels',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute create_label: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async updateLabel(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'update_label',
            method: 'PUT',
            path: '/users/{userId}/labels/{id}',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/labels/{id}';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["name"] !== undefined) {
                bodyParams["name"] = params["name"];
                extractedParams.push("name");
            }
            if (params["labelListVisibility"] !== undefined) {
                bodyParams["labelListVisibility"] = params["labelListVisibility"];
                extractedParams.push("labelListVisibility");
            }
            if (params["messageListVisibility"] !== undefined) {
                bodyParams["messageListVisibility"] = params["messageListVisibility"];
                extractedParams.push("messageListVisibility");
            }
            if (params["color"] !== undefined) {
                bodyParams["color"] = params["color"];
                extractedParams.push("color");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/labels/{id}', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'PUT', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'update_label',
                method: 'PUT',
                path: '/users/{userId}/labels/{id}',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'update_label',
                method: 'PUT',
                path: '/users/{userId}/labels/{id}',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute update_label: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async deleteLabel(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'delete_label',
            method: 'DELETE',
            path: '/users/{userId}/labels/{id}',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/labels/{id}';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/labels/{id}', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'DELETE', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'delete_label',
                method: 'DELETE',
                path: '/users/{userId}/labels/{id}',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'delete_label',
                method: 'DELETE',
                path: '/users/{userId}/labels/{id}',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute delete_label: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async listThreads(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'list_threads',
            method: 'GET',
            path: '/users/{userId}/threads',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/threads';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["q"] !== undefined) {
                queryParams["q"] = params["q"];
                extractedParams.push("q");
            }
            if (params["labelIds"] !== undefined) {
                queryParams["labelIds"] = params["labelIds"];
                extractedParams.push("labelIds");
            }
            if (params["maxResults"] !== undefined) {
                queryParams["maxResults"] = params["maxResults"];
                extractedParams.push("maxResults");
            }
            if (params["pageToken"] !== undefined) {
                queryParams["pageToken"] = params["pageToken"];
                extractedParams.push("pageToken");
            }
            if (params["includeSpamTrash"] !== undefined) {
                queryParams["includeSpamTrash"] = params["includeSpamTrash"];
                extractedParams.push("includeSpamTrash");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            const path = this.buildPath('/users/{userId}/threads', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'GET', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'list_threads',
                method: 'GET',
                path: '/users/{userId}/threads',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'list_threads',
                method: 'GET',
                path: '/users/{userId}/threads',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute list_threads: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async getThread(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'get_thread',
            method: 'GET',
            path: '/users/{userId}/threads/{id}',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/threads/{id}';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["format"] !== undefined) {
                queryParams["format"] = params["format"];
                extractedParams.push("format");
            }
            if (params["metadataHeaders"] !== undefined) {
                queryParams["metadataHeaders"] = params["metadataHeaders"];
                extractedParams.push("metadataHeaders");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/threads/{id}', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'GET', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'get_thread',
                method: 'GET',
                path: '/users/{userId}/threads/{id}',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'get_thread',
                method: 'GET',
                path: '/users/{userId}/threads/{id}',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute get_thread: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async modifyThread(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'modify_thread',
            method: 'POST',
            path: '/users/{userId}/threads/{id}/modify',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/threads/{id}/modify';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["addLabelIds"] !== undefined) {
                bodyParams["addLabelIds"] = params["addLabelIds"];
                extractedParams.push("addLabelIds");
            }
            if (params["removeLabelIds"] !== undefined) {
                bodyParams["removeLabelIds"] = params["removeLabelIds"];
                extractedParams.push("removeLabelIds");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/threads/{id}/modify', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'POST', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'modify_thread',
                method: 'POST',
                path: '/users/{userId}/threads/{id}/modify',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'modify_thread',
                method: 'POST',
                path: '/users/{userId}/threads/{id}/modify',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute modify_thread: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async trashThread(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'trash_thread',
            method: 'POST',
            path: '/users/{userId}/threads/{id}/trash',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/threads/{id}/trash';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/threads/{id}/trash', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'POST', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'trash_thread',
                method: 'POST',
                path: '/users/{userId}/threads/{id}/trash',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'trash_thread',
                method: 'POST',
                path: '/users/{userId}/threads/{id}/trash',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute trash_thread: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async untrashThread(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'untrash_thread',
            method: 'POST',
            path: '/users/{userId}/threads/{id}/untrash',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/threads/{id}/untrash';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/threads/{id}/untrash', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'POST', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'untrash_thread',
                method: 'POST',
                path: '/users/{userId}/threads/{id}/untrash',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'untrash_thread',
                method: 'POST',
                path: '/users/{userId}/threads/{id}/untrash',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute untrash_thread: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async deleteThread(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'delete_thread',
            method: 'DELETE',
            path: '/users/{userId}/threads/{id}',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/threads/{id}';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/threads/{id}', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'DELETE', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'delete_thread',
                method: 'DELETE',
                path: '/users/{userId}/threads/{id}',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'delete_thread',
                method: 'DELETE',
                path: '/users/{userId}/threads/{id}',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute delete_thread: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async listDrafts(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'list_drafts',
            method: 'GET',
            path: '/users/{userId}/drafts',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/drafts';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["q"] !== undefined) {
                queryParams["q"] = params["q"];
                extractedParams.push("q");
            }
            if (params["maxResults"] !== undefined) {
                queryParams["maxResults"] = params["maxResults"];
                extractedParams.push("maxResults");
            }
            if (params["pageToken"] !== undefined) {
                queryParams["pageToken"] = params["pageToken"];
                extractedParams.push("pageToken");
            }
            if (params["includeSpamTrash"] !== undefined) {
                queryParams["includeSpamTrash"] = params["includeSpamTrash"];
                extractedParams.push("includeSpamTrash");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            const path = this.buildPath('/users/{userId}/drafts', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'GET', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'list_drafts',
                method: 'GET',
                path: '/users/{userId}/drafts',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'list_drafts',
                method: 'GET',
                path: '/users/{userId}/drafts',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute list_drafts: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async getDraft(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'get_draft',
            method: 'GET',
            path: '/users/{userId}/drafts/{id}',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/drafts/{id}';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["format"] !== undefined) {
                queryParams["format"] = params["format"];
                extractedParams.push("format");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/drafts/{id}', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'GET', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'get_draft',
                method: 'GET',
                path: '/users/{userId}/drafts/{id}',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'get_draft',
                method: 'GET',
                path: '/users/{userId}/drafts/{id}',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute get_draft: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async createDraft(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'create_draft',
            method: 'POST',
            path: '/users/{userId}/drafts',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/drafts';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["message"] !== undefined) {
                bodyParams["message"] = params["message"];
                extractedParams.push("message");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.message) {
                throw new Error(`Missing required parameter: message`);
            }
            const path = this.buildPath('/users/{userId}/drafts', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'POST', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'create_draft',
                method: 'POST',
                path: '/users/{userId}/drafts',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'create_draft',
                method: 'POST',
                path: '/users/{userId}/drafts',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute create_draft: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async updateDraft(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'update_draft',
            method: 'PUT',
            path: '/users/{userId}/drafts/{id}',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/drafts/{id}';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["message"] !== undefined) {
                bodyParams["message"] = params["message"];
                extractedParams.push("message");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            if (!params.message) {
                throw new Error(`Missing required parameter: message`);
            }
            const path = this.buildPath('/users/{userId}/drafts/{id}', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'PUT', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'update_draft',
                method: 'PUT',
                path: '/users/{userId}/drafts/{id}',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'update_draft',
                method: 'PUT',
                path: '/users/{userId}/drafts/{id}',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute update_draft: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async sendDraft(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'send_draft',
            method: 'POST',
            path: '/users/{userId}/drafts/send',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/drafts/send';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            if (params["id"] !== undefined) {
                bodyParams["id"] = params["id"];
                extractedParams.push("id");
            }
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/drafts/send', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'POST', url: path, params: queryParams, data: Object.keys(bodyParams).length > 0 ? bodyParams : undefined });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'send_draft',
                method: 'POST',
                path: '/users/{userId}/drafts/send',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'send_draft',
                method: 'POST',
                path: '/users/{userId}/drafts/send',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute send_draft: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async deleteDraft(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'delete_draft',
            method: 'DELETE',
            path: '/users/{userId}/drafts/{id}',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/drafts/{id}';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            if (!params.id) {
                throw new Error(`Missing required parameter: id`);
            }
            const path = this.buildPath('/users/{userId}/drafts/{id}', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'DELETE', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'delete_draft',
                method: 'DELETE',
                path: '/users/{userId}/drafts/{id}',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'delete_draft',
                method: 'DELETE',
                path: '/users/{userId}/drafts/{id}',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute delete_draft: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async getProfile(params) {
        const startTime = Date.now();
        this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
            endpoint: 'get_profile',
            method: 'GET',
            path: '/users/{userId}/profile',
            paramCount: Object.keys(params || {}).length,
            paramKeys: Object.keys(params || {})
        });
        try {
            // Extract and separate parameters by location: path, query, body
            const pathTemplate = '/users/{userId}/profile';
            const pathParams = {};
            const queryParams = {};
            const bodyParams = {};
            const extractedParams = [];
            // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
            const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
            let match;
            while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
                const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
                if (paramName && params[paramName] !== undefined) {
                    pathParams[paramName] = params[paramName];
                    extractedParams.push(paramName);
                }
            }
            // Handle standard path templates: {resourceName}
            const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
            standardPathParams.forEach(paramTemplate => {
                const paramName = paramTemplate.slice(1, -1); // Remove { }
                // Only process if not already handled by Google template logic
                if (!extractedParams.includes(paramName)) {
                    if (params[paramName] !== undefined) {
                        pathParams[paramName] = params[paramName];
                        extractedParams.push(paramName);
                    }
                    else {
                        // Provide default values for optional path parameters
                        if (paramName === 'userId') {
                            pathParams[paramName] = 'me'; // Default to authenticated user
                            extractedParams.push(paramName);
                        }
                    }
                }
            });
            // Separate remaining parameters by location (query vs body)
            // Any remaining unprocessed parameters default to body for backward compatibility
            for (const [key, value] of Object.entries(params)) {
                if (!extractedParams.includes(key)) {
                    bodyParams[key] = value;
                }
            }
            // Validate required parameters
            const path = this.buildPath('/users/{userId}/profile', pathParams);
            // Use authenticated request for OAuth (both ConstructionWire and standard OAuth)
            const response = await this.makeAuthenticatedRequest({ method: 'GET', url: path, params: queryParams });
            const duration = Date.now() - startTime;
            this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
                endpoint: 'get_profile',
                method: 'GET',
                path: '/users/{userId}/profile',
                duration_ms: duration,
                responseDataSize: JSON.stringify(response.data).length
            });
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(response.data, null, 2)
                    }
                ]
            };
        }
        catch (error) {
            const duration = Date.now() - startTime;
            this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
                endpoint: 'get_profile',
                method: 'GET',
                path: '/users/{userId}/profile',
                duration_ms: duration,
                error: error instanceof Error ? error.message : String(error),
                errorType: error instanceof Error ? error.constructor.name : 'unknown'
            });
            throw new Error(`Failed to execute get_profile: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
}
//# sourceMappingURL=google-gmail-client.js.map