Skip to main content
Glama
AaroYazilim

AARO ERP MCP Server

by AaroYazilim

erp_token_al

Retrieves temporary access tokens from the AARO ERP system by opening a browser, awaiting user password input, and extracting the token. Simplifies ERP integration for secure access.

Instructions

ERP sisteminden geçici erişim anahtarı (token) alır. Tarayıcıyı açar, kullanıcının şifre girmesini bekler ve #anahtar elementinden token'i alır.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
passwordNoERP şifresi (opsiyonel - belirtilmezse kullanıcı manuel girer)

Implementation Reference

  • Core handler function that implements ERP token acquisition: connects/launches Puppeteer browser, navigates to ERP login, optionally fills password, waits for token element, extracts text, parses token info, caches it, and returns the token.
    private async getErpToken(password?: string): Promise<string> { // Önce cache'den token kontrol et const cachedToken = this.getCachedToken(); if (cachedToken) { return cachedToken; } this.log('Cache\'de geçerli token bulunamadı, yeni token alınıyor...'); try { // Sistemdeki Chrome'u bul const possibleChromePaths = [ 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', path.join(os.homedir(), 'AppData\\Local\\Google\\Chrome\\Application\\chrome.exe') ]; let chromePath: string | undefined; for (const possiblePath of possibleChromePaths) { if (fs.existsSync(possiblePath)) { chromePath = possiblePath; break; } } // Tarayıcı başlatma seçenekleri let launchOptions: any = { headless: this.settings.browser.headless, defaultViewport: this.settings.browser.defaultViewport, args: [...this.settings.browser.args], executablePath: chromePath }; if (chromePath) { this.log('Sistemdeki Chrome kullanılıyor: ' + chromePath); } else { this.log('Sistemdeki Chrome bulunamadı, Puppeteer Chromium kullanılıyor'); } // Önce mevcut Chrome'a bağlanmaya çalış try { this.log('Mevcut Chrome instance\'ına bağlanmaya çalışılıyor...'); // Chrome'un remote debugging portunu kontrol et const debuggingPorts = [9222, 9223, 9224, 9225]; let connectedBrowser = null; for (const port of debuggingPorts) { try { const browserWSEndpoint = `ws://127.0.0.1:${port}`; this.log(`Port ${port} kontrol ediliyor...`); // Mevcut Chrome'a bağlanmaya çalış connectedBrowser = await puppeteer.connect({ browserWSEndpoint, defaultViewport: this.settings.browser.defaultViewport }); this.log(`Mevcut Chrome'a başarıyla bağlanıldı (port: ${port})`); this.browser = connectedBrowser; break; } catch (connectError) { // Bu port çalışmıyor, diğerini dene continue; } } // Eğer mevcut Chrome'a bağlanamazsak, yeni instance başlat if (!connectedBrowser) { this.log('Mevcut Chrome\'a bağlanılamadı, yeni instance başlatılıyor...'); // Geçici profil dizini oluştur this.tempUserDataDir = path.join(os.tmpdir(), 'puppeteer-chrome-profile-' + Date.now()); const originalUserDataDir = path.join(os.homedir(), 'AppData', 'Local', 'Google', 'Chrome', 'User Data'); if (fs.existsSync(originalUserDataDir)) { this.log('Geçici profil dizini oluşturuluyor...'); if (!fs.existsSync(this.tempUserDataDir)) { fs.mkdirSync(this.tempUserDataDir, { recursive: true }); } const defaultProfileDir = path.join(originalUserDataDir, 'Default'); const tempDefaultDir = path.join(this.tempUserDataDir, 'Default'); if (fs.existsSync(defaultProfileDir) && !fs.existsSync(tempDefaultDir)) { fs.mkdirSync(tempDefaultDir, { recursive: true }); const prefsFile = path.join(defaultProfileDir, 'Preferences'); const tempPrefsFile = path.join(tempDefaultDir, 'Preferences'); if (fs.existsSync(prefsFile)) { fs.copyFileSync(prefsFile, tempPrefsFile); this.log('Profil tercihleri kopyalandı'); } } launchOptions.args.push(`--user-data-dir=${this.tempUserDataDir}`); launchOptions.args.push('--profile-directory=Default'); } // Remote debugging portunu ekle launchOptions.args.push('--remote-debugging-port=9222'); this.browser = await puppeteer.launch(launchOptions); this.log('Yeni Chrome instance başlatıldı'); } } catch (error) { this.log('Chrome bağlantısı başarısız, fallback modda başlatılıyor...', 'warn'); this.browser = await puppeteer.launch(launchOptions); } if (!this.browser) { throw new Error('Tarayıcı başlatılamadı'); } const page = await this.browser.newPage(); // ERP URL'sine git await page.goto(this.settings.erp.loginUrl, { waitUntil: 'networkidle2' }); this.log('ERP sayfası açıldı, kullanıcı girişi bekleniyor...'); // Eğer şifre verilmişse otomatik doldur if (password) { try { // Şifre alanını bul ve doldur (genel şifre input selektörleri) const passwordSelectors = [ 'input[type="password"]', '#password', '#Password', '[name="password"]', '[name="Password"]' ]; for (const selector of passwordSelectors) { try { await page.waitForSelector(selector, { timeout: 2000 }); await page.type(selector, password); this.log('Şifre otomatik olarak girildi'); break; } catch (e) { // Bu selektör bulunamadı, diğerini dene continue; } } } catch (e) { this.log('Şifre alanı bulunamadı, kullanıcı manuel girmeli'); } } // Token elementinin görünmesini bekle this.log('Token elementi bekleniyor...'); await page.waitForSelector(this.settings.erp.tokenSelector, { visible: true, timeout: this.settings.browser.timeout }); this.log('Token elementi bulundu, token alınıyor...'); // Token'i al const tokenText = await page.$eval(this.settings.erp.tokenSelector, (element) => { return element.textContent || element.getAttribute('value') || ''; }); if (!tokenText || tokenText.trim() === '') { throw new Error('Token elementinde token bulunamadı'); } const cleanTokenText = tokenText.trim(); this.log(`Token metni alındı, parse ediliyor...`); // Token bilgilerini parse et const tokenInfo = this.parseTokenInfo(cleanTokenText); if (!tokenInfo.token) { throw new Error('Token parse edilemedi'); } this.log(`Token başarıyla parse edildi`, 'info', { user: tokenInfo.user, validFrom: tokenInfo.validFrom, validTo: tokenInfo.validTo, group: tokenInfo.group }); // Token'i cache'e kaydet (parse edilen bilgilerle) this.saveTokenCacheWithInfo(tokenInfo); // Tarayıcıyı kapat await this.browser.close(); this.browser = null; return tokenInfo.token; } catch (error) { // Hata durumunda tarayıcıyı kapat if (this.browser) { await this.browser.close(); this.browser = null; } throw new Error(`ERP token alma işlemi başarısız: ${error instanceof Error ? error.message : 'Bilinmeyen hata'}`); } }
  • Helper function that parses raw token HTML/text from page to extract token string, user, validity dates, group using regex patterns.
    private parseTokenInfo(tokenText: string): TokenCache { try { this.log('Token parse işlemi başlıyor', 'info', { tokenTextLength: tokenText.length }); // HTML tag'lerini temizle ve <br> tag'lerini \n ile değiştir let cleanText = tokenText .replace(/<br\s*\/?>/gi, '\n') // <br> tag'lerini \n ile değiştir .replace(/<[^>]*>/g, '') // Diğer HTML tag'lerini kaldır .replace(/&nbsp;/g, ' ') // &nbsp; karakterlerini boşluk ile değiştir .trim(); this.log('HTML temizlendi', 'info', { cleanTextLength: cleanText.length, cleanText: cleanText.substring(0, 200) + '...' }); // Regex ile direkt parse et (satır bazlı değil, tüm metin üzerinde) let user = ''; let token = ''; let validFrom = ''; let validTo = ''; let group = ''; // Kullanıcı bilgisini bul const userMatch = cleanText.match(/Kullanıcı\s*:\s*([^\s]+@[^\s]+)/); if (userMatch) { user = userMatch[1]; this.log('Kullanıcı bulundu', 'info', { user }); } // Token'ı bul - "Geçici Erişim Anahtarı : " dan sonra boşluk öncesine kadar const tokenMatch = cleanText.match(/Geçici Erişim Anahtarı\s*:\s*([A-Za-z0-9_\-]+)/); if (tokenMatch) { token = tokenMatch[1]; this.log('Token bulundu', 'info', { tokenLength: token.length, tokenPreview: token.substring(0, 20) + '...' }); } // Geçerlilik başlangıç tarihini bul const validFromMatch = cleanText.match(/Geçerlilik Başlangıç\s*:\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2})/); if (validFromMatch) { validFrom = validFromMatch[1]; this.log('Başlangıç tarihi bulundu', 'info', { validFrom }); } // Geçerlilik bitiş tarihini bul const validToMatch = cleanText.match(/Geçerlilik Bitiş\s*:\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2})/); if (validToMatch) { validTo = validToMatch[1]; this.log('Bitiş tarihi bulundu', 'info', { validTo }); } // Grup bilgisini bul const groupMatch = cleanText.match(/Grup\s*:\s*(\d+)/); if (groupMatch) { group = groupMatch[1]; this.log('Grup bulundu', 'info', { group }); } // Token bulunamadıysa fallback if (!token || token.length < 10) { this.log('Token regex ile bulunamadı, fallback deneniyor...', 'warn'); // Fallback: En uzun alfanumerik+özel karakter dizisini bul const tokenFallbackMatch = cleanText.match(/([A-Za-z0-9_\-]{100,})/); if (tokenFallbackMatch) { token = tokenFallbackMatch[1]; this.log('Fallback token bulundu', 'info', { tokenLength: token.length, tokenPreview: token.substring(0, 20) + '...' }); } else { throw new Error(`Token bulunamadı. Metin: ${cleanText.substring(0, 200)}...`); } } // Geçerlilik bitiş tarihini parse et ve expiresAt hesapla let expiresAt = Date.now() + (this.settings.erp.tokenCacheMinutes * 60 * 1000); // Varsayılan if (validTo) { try { // "2025-07-25 22:43" formatını parse et const [datePart, timePart] = validTo.split(' '); const [year, month, day] = datePart.split('-').map(Number); const [hour, minute] = timePart.split(':').map(Number); const expireDate = new Date(year, month - 1, day, hour, minute); expiresAt = expireDate.getTime(); this.log(`Token geçerlilik bitiş tarihi parse edildi: ${expireDate.toISOString()}`); } catch (parseError) { this.log('Token geçerlilik tarihi parse edilemedi, varsayılan süre kullanılıyor', 'warn', parseError); } } const result = { token, expiresAt, createdAt: Date.now(), user, validFrom, validTo, group, rawText: tokenText }; this.log('Token parse işlemi tamamlandı', 'info', { hasToken: !!result.token, tokenLength: result.token.length, hasUser: !!result.user, hasValidTo: !!result.validTo, user: result.user, validFrom: result.validFrom, validTo: result.validTo, group: result.group }); return result; } catch (error) { this.log('Token parse hatası', 'error', { error: error instanceof Error ? error.message : 'Bilinmeyen hata', tokenText: tokenText.substring(0, 200) + '...' }); throw new Error(`Token bilgileri parse edilemedi: ${error instanceof Error ? error.message : 'Bilinmeyen hata'}`); } }
  • Dispatcher for special handlers; for 'getErpToken' (likely mapped from tool "erp_token_al" via config), validates args and calls getErpToken.
    private async callSpecialHandler(handlerName: string, args: any) { switch (handlerName) { case 'getErpToken': if (!isValidErpTokenArgs(args)) { throw new McpError(ErrorCode.InvalidParams, 'Geçersiz ERP token parametreleri'); } const token = await this.getErpToken(args.password); return { content: [{ type: 'text', text: `ERP Token başarıyla alındı: ${token}` }], }; case 'deleteToken': return await this.deleteToken(); case 'addManualToken': return await this.addManualToken(args); case 'createStok': return await this.createStok(args); case 'createCari': return await this.createCari(args); case 'createDekont': return await this.createDekont(args); case 'addDekontKalem': return await this.addDekontKalem(args); case 'updateDekont': return await this.updateDekont(args); case 'testWebhook': return await this.testWebhook(args); case 'callErpApi': if (!isValidErpApiArgs(args)) { throw new McpError(ErrorCode.InvalidParams, 'Geçersiz API çağrı parametreleri'); } return await this.callErpApi(args.endpoint!, args.method || 'GET', args); default: throw new McpError(ErrorCode.MethodNotFound, `Bilinmeyen handler: ${handlerName}`); } }
  • Input schema for ERP token tool: optional password.
    interface ErpTokenArgs { password?: string; }
  • src/index.ts:164-216 (registration)
    Registers MCP tool handlers dynamically from config/tools.json; special handlers like getErpToken dispatched if configured.
    private setupToolHandlers() { // Tools listesini dinamik olarak oluştur this.server.setRequestHandler(ListToolsRequestSchema, async () => { const tools = Object.entries(this.toolsConfig).map(([name, config]) => ({ name, description: config.description, inputSchema: config.inputSchema, })); return { tools }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { const toolConfig = this.toolsConfig[name]; if (!toolConfig) { throw new McpError(ErrorCode.MethodNotFound, `Bilinmeyen araç: ${name}`); } // Request bilgilerini logla this.log(`Tool çağrısı: ${name}`, 'info', { tool: name, params: args }); // Özel handler varsa onu kullan if (toolConfig.handler) { return await this.callSpecialHandler(toolConfig.handler, args); } // Normal API çağrısı if (toolConfig.endpoint && toolConfig.method) { return await this.callErpApi(toolConfig.endpoint, toolConfig.method as 'GET' | 'POST', args); } throw new McpError(ErrorCode.InternalError, `Tool konfigürasyonu eksik: ${name}`); } catch (error) { this.log(`Tool hatası: ${name}`, 'error', error); return { content: [ { type: 'text', text: `Hata: ${error instanceof Error ? error.message : 'Bilinmeyen hata'}`, }, ], isError: true, }; } }); }

Latest Blog Posts

MCP directory API

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

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AaroYazilim/aaro-erp-mcp-server'

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