Skip to main content
Glama

Twitter MCP Server

request-maker.mixin.js12.9 kB
import { TwitterApiPluginResponseOverride } from '../types'; import TweetStream from '../stream/TweetStream'; import { applyResponseHooks, hasRequestErrorPlugins } from '../plugins/helpers'; import { trimUndefinedProperties } from '../helpers'; import OAuth1Helper from './oauth1.helper'; import RequestHandlerHelper from './request-handler.helper'; import RequestParamHelpers from './request-param.helper'; import { OAuth2Helper } from './oauth2.helper'; export class ClientRequestMaker { constructor(settings) { this.rateLimits = {}; this.clientSettings = {}; if (settings) { this.clientSettings = settings; } } /** @deprecated - Switch to `@twitter-api-v2/plugin-rate-limit` */ getRateLimits() { return this.rateLimits; } saveRateLimit(originalUrl, rateLimit) { this.rateLimits[originalUrl] = rateLimit; } /** Send a new request and returns a wrapped `Promise<TwitterResponse<T>`. */ async send(requestParams) { var _a, _b, _c, _d, _e; // Pre-request config hooks if ((_a = this.clientSettings.plugins) === null || _a === void 0 ? void 0 : _a.length) { const possibleResponse = await this.applyPreRequestConfigHooks(requestParams); if (possibleResponse) { return possibleResponse; } } const args = this.getHttpRequestArgs(requestParams); const options = { method: args.method, headers: args.headers, timeout: requestParams.timeout, agent: this.clientSettings.httpAgent, }; const enableRateLimitSave = requestParams.enableRateLimitSave !== false; if (args.body) { RequestParamHelpers.setBodyLengthHeader(options, args.body); } // Pre-request hooks if ((_b = this.clientSettings.plugins) === null || _b === void 0 ? void 0 : _b.length) { await this.applyPreRequestHooks(requestParams, args, options); } let request = new RequestHandlerHelper({ url: args.url, options, body: args.body, rateLimitSaver: enableRateLimitSave ? this.saveRateLimit.bind(this, args.rawUrl) : undefined, requestEventDebugHandler: requestParams.requestEventDebugHandler, compression: (_d = (_c = requestParams.compression) !== null && _c !== void 0 ? _c : this.clientSettings.compression) !== null && _d !== void 0 ? _d : true, forceParseMode: requestParams.forceParseMode, }) .makeRequest(); if (hasRequestErrorPlugins(this)) { request = this.applyResponseErrorHooks(requestParams, args, options, request); } const response = await request; // Post-request hooks if ((_e = this.clientSettings.plugins) === null || _e === void 0 ? void 0 : _e.length) { const responseOverride = await this.applyPostRequestHooks(requestParams, args, options, response); if (responseOverride) { return responseOverride.value; } } return response; } sendStream(requestParams) { var _a, _b; // Pre-request hooks if (this.clientSettings.plugins) { this.applyPreStreamRequestConfigHooks(requestParams); } const args = this.getHttpRequestArgs(requestParams); const options = { method: args.method, headers: args.headers, agent: this.clientSettings.httpAgent, }; const enableRateLimitSave = requestParams.enableRateLimitSave !== false; const enableAutoConnect = requestParams.autoConnect !== false; if (args.body) { RequestParamHelpers.setBodyLengthHeader(options, args.body); } const requestData = { url: args.url, options, body: args.body, rateLimitSaver: enableRateLimitSave ? this.saveRateLimit.bind(this, args.rawUrl) : undefined, payloadIsError: requestParams.payloadIsError, compression: (_b = (_a = requestParams.compression) !== null && _a !== void 0 ? _a : this.clientSettings.compression) !== null && _b !== void 0 ? _b : true, }; const stream = new TweetStream(requestData); if (!enableAutoConnect) { return stream; } return stream.connect(); } /* Token helpers */ initializeToken(token) { if (typeof token === 'string') { this.bearerToken = token; } else if (typeof token === 'object' && 'appKey' in token) { this.consumerToken = token.appKey; this.consumerSecret = token.appSecret; if (token.accessToken && token.accessSecret) { this.accessToken = token.accessToken; this.accessSecret = token.accessSecret; } this._oauth = this.buildOAuth(); } else if (typeof token === 'object' && 'username' in token) { const key = encodeURIComponent(token.username) + ':' + encodeURIComponent(token.password); this.basicToken = Buffer.from(key).toString('base64'); } else if (typeof token === 'object' && 'clientId' in token) { this.clientId = token.clientId; this.clientSecret = token.clientSecret; } } getActiveTokens() { if (this.bearerToken) { return { type: 'oauth2', bearerToken: this.bearerToken, }; } else if (this.basicToken) { return { type: 'basic', token: this.basicToken, }; } else if (this.consumerSecret && this._oauth) { return { type: 'oauth-1.0a', appKey: this.consumerToken, appSecret: this.consumerSecret, accessToken: this.accessToken, accessSecret: this.accessSecret, }; } else if (this.clientId) { return { type: 'oauth2-user', clientId: this.clientId, }; } return { type: 'none' }; } buildOAuth() { if (!this.consumerSecret || !this.consumerToken) throw new Error('Invalid consumer tokens'); return new OAuth1Helper({ consumerKeys: { key: this.consumerToken, secret: this.consumerSecret }, }); } getOAuthAccessTokens() { if (!this.accessSecret || !this.accessToken) return; return { key: this.accessToken, secret: this.accessSecret, }; } /* Plugin helpers */ getPlugins() { var _a; return (_a = this.clientSettings.plugins) !== null && _a !== void 0 ? _a : []; } hasPlugins() { var _a; return !!((_a = this.clientSettings.plugins) === null || _a === void 0 ? void 0 : _a.length); } async applyPluginMethod(method, args) { var _a; let returnValue; for (const plugin of this.getPlugins()) { const value = await ((_a = plugin[method]) === null || _a === void 0 ? void 0 : _a.call(plugin, args)); if (value && value instanceof TwitterApiPluginResponseOverride) { returnValue = value; } } return returnValue; } /* Request helpers */ writeAuthHeaders({ headers, bodyInSignature, url, method, query, body }) { headers = { ...headers }; if (this.bearerToken) { headers.Authorization = 'Bearer ' + this.bearerToken; } else if (this.basicToken) { // Basic auth, to request a bearer token headers.Authorization = 'Basic ' + this.basicToken; } else if (this.clientId && this.clientSecret) { // Basic auth with clientId + clientSecret headers.Authorization = 'Basic ' + OAuth2Helper.getAuthHeader(this.clientId, this.clientSecret); } else if (this.consumerSecret && this._oauth) { // Merge query and body const data = bodyInSignature ? RequestParamHelpers.mergeQueryAndBodyForOAuth(query, body) : query; const auth = this._oauth.authorize({ url: url.toString(), method, data, }, this.getOAuthAccessTokens()); headers = { ...headers, ...this._oauth.toHeader(auth) }; } return headers; } getUrlObjectFromUrlString(url) { // Add protocol to URL if needed if (!url.startsWith('http')) { url = 'https://' + url; } // Convert URL to object that will receive all URL modifications return new URL(url); } getHttpRequestArgs({ url: stringUrl, method, query: rawQuery = {}, body: rawBody = {}, headers, forceBodyMode, enableAuth, params, }) { let body = undefined; method = method.toUpperCase(); headers = headers !== null && headers !== void 0 ? headers : {}; // Add user agent header (Twitter recommends it) if (!headers['x-user-agent']) { headers['x-user-agent'] = 'Node.twitter-api-v2'; } const url = this.getUrlObjectFromUrlString(stringUrl); // URL without query string to save as endpoint name const rawUrl = url.origin + url.pathname; // Apply URL parameters if (params) { RequestParamHelpers.applyRequestParametersToUrl(url, params); } // Build a URL without anything in QS, and QSP in query const query = RequestParamHelpers.formatQueryToString(rawQuery); RequestParamHelpers.moveUrlQueryParamsIntoObject(url, query); // Delete undefined parameters if (!(rawBody instanceof Buffer)) { trimUndefinedProperties(rawBody); } // OAuth signature should not include parameters when using multipart. const bodyType = forceBodyMode !== null && forceBodyMode !== void 0 ? forceBodyMode : RequestParamHelpers.autoDetectBodyType(url); // If undefined or true, enable auth by headers if (enableAuth !== false) { // OAuth needs body signature only if body is URL encoded. const bodyInSignature = ClientRequestMaker.BODY_METHODS.has(method) && bodyType === 'url'; headers = this.writeAuthHeaders({ headers, bodyInSignature, method, query, url, body: rawBody }); } if (ClientRequestMaker.BODY_METHODS.has(method)) { body = RequestParamHelpers.constructBodyParams(rawBody, headers, bodyType) || undefined; } RequestParamHelpers.addQueryParamsToUrl(url, query); return { rawUrl, url, method, headers, body, }; } /* Plugin helpers */ async applyPreRequestConfigHooks(requestParams) { var _a; const url = this.getUrlObjectFromUrlString(requestParams.url); for (const plugin of this.getPlugins()) { const result = await ((_a = plugin.onBeforeRequestConfig) === null || _a === void 0 ? void 0 : _a.call(plugin, { client: this, url, params: requestParams, })); if (result) { return result; } } } applyPreStreamRequestConfigHooks(requestParams) { var _a; const url = this.getUrlObjectFromUrlString(requestParams.url); for (const plugin of this.getPlugins()) { (_a = plugin.onBeforeStreamRequestConfig) === null || _a === void 0 ? void 0 : _a.call(plugin, { client: this, url, params: requestParams, }); } } async applyPreRequestHooks(requestParams, computedParams, requestOptions) { await this.applyPluginMethod('onBeforeRequest', { client: this, url: this.getUrlObjectFromUrlString(requestParams.url), params: requestParams, computedParams, requestOptions, }); } async applyPostRequestHooks(requestParams, computedParams, requestOptions, response) { return await this.applyPluginMethod('onAfterRequest', { client: this, url: this.getUrlObjectFromUrlString(requestParams.url), params: requestParams, computedParams, requestOptions, response, }); } applyResponseErrorHooks(requestParams, computedParams, requestOptions, promise) { return promise.catch(applyResponseHooks.bind(this, requestParams, computedParams, requestOptions)); } } ClientRequestMaker.BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']);

MCP directory API

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

curl -X GET 'https://glama.ai/api/mcp/v1/servers/crazyrabbitLTC/mcp-twitter-server'

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