rap2_ensure_session
Logs into RAP2 using environment variables and caches the session cookie for subsequent API documentation queries.
Instructions
使用环境参数登录并保存 Cookie 到会话缓存
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/mcp-server.js:197-205 (handler)Tool handler logic: creates Rap2Client, invokes login() to ensure session, returns success payload with cookie status.if (name === 'rap2_ensure_session') { const client = createClient(); const result = await client.login(); if (result?.error) throw new Error(String(result.error)); const cookies = client.getCookies(); const payload = { ok: true, cookies: { hasSid: !!cookies.sid, hasSidSig: !!cookies.sidSig } }; logger.info({ tool: name, result: payload }, 'tool success'); return { content: [{ type: 'text', text: JSON.stringify(payload) }] }; }
- src/mcp-server.js:102-109 (registration)Tool registration entry in the tools array, including name, description, and input schema.{ name: 'rap2_ensure_session', description: '使用环境参数登录并保存 Cookie 到会话缓存', inputSchema: { type: 'object', properties: {}, }, },
- src/mcp-server.js:27-46 (helper)Helper function to create Rap2Client with global session cookie caching and update callbacks.function createClient() { const cfg = readEnvConfig(); assertConfig(cfg); // 会话级 Cookie 缓存 if (!globalThis.__RAP2_SESSION__) { globalThis.__RAP2_SESSION__ = { sid: cfg.sid || '', sidSig: cfg.sidSig || '' }; } const session = globalThis.__RAP2_SESSION__; const client = new Rap2Client({ ...cfg, sid: session.sid, sidSig: session.sidSig, onCookieUpdate: ({ name, value }) => { if (name === 'koa.sid') session.sid = value; if (name === 'koa.sid.sig') session.sidSig = value; }, logger, }); return client; }
- src/rap2Client.js:95-158 (helper)Core login implementation in Rap2Client, handles cookie verification, captcha solving loop, and session establishment.async login() { // 0) If preset cookies exist, try verify if (this.cookies['koa.sid'] && this.cookies['koa.sid.sig']) { if (this.logger) this.logger.info({ step: 'verify-cookie' }, 'login flow'); const verify = await this._fetch('/repository/joined', { __skipPreLogin: true }); if (verify?.data && verify.data.isOk) return { status: '登录成功(使用Cookie)' }; } // 1) Warm-up to obtain cookies if (this.logger) this.logger.info({ step: 'warmup' }, 'login flow'); await this._fetch('/account/info', { __skipPreLogin: true }); if (!this.cookies['koa.sid'] || !this.cookies['koa.sid.sig']) { // some instances set cookie only on captcha fetch } // 2) Loop: fetch captcha then read truth from /captcha_data const maxAttempts = 6; let lastError = null; for (let attempt = 1; attempt <= maxAttempts; attempt++) { if (this.logger) this.logger.info({ step: 'captcha-loop', attempt }, 'login flow'); await this._fetch(`/captcha?t=${Date.now()}`, { __skipPreLogin: true }); const { data: cap } = await this._fetch('/captcha_data', { __skipPreLogin: true }); let truth = ''; if (cap && typeof cap === 'object') { const raw = cap.data; if (typeof raw === 'string') { try { truth = JSON.parse(raw).captcha || ''; } catch { truth = ''; } } else if (raw && typeof raw === 'object') { truth = String(raw.captcha || ''); } } if (!truth || truth.length !== 4) { await new Promise(r => setTimeout(r, 600)); continue; } const payload = JSON.stringify({ email: this.email, password: this.password, captcha: truth.toLowerCase() }); const loginRes = await this._fetch('/account/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: payload, __skipPreLogin: true }); const lr = loginRes?.data || {}; const dataField = lr?.data || {}; let success = false; if (lr?.isOk === true) success = true; else if (dataField && (dataField.email || dataField.id)) success = true; if (success) { // verify authorization by hitting a restricted API if (this.logger) this.logger.info({ step: 'verify-auth' }, 'login flow'); const v = await this._fetch('/repository/joined', { __skipPreLogin: true }); if (v?.data && v.data.isOk === false && String(v.data.errMsg || '').includes('没有访问权限')) { // not authorized yet, retry await new Promise(r => setTimeout(r, 800)); continue; } if (this.logger) this.logger.info({ step: 'login-success' }, 'login flow'); return { status: '登录成功' }; } lastError = String(lr?.errMsg || '登录失败'); const lower = lastError.toLowerCase(); if (this.logger) this.logger.warn({ step: 'login-retry', lastError }, 'login flow'); if (['验证码', 'captcha', 'code', 'verify'].some(k => lower.includes(k))) { await new Promise(r => setTimeout(r, 800)); continue; } if (['密码','password','account','邮箱','用户'].some(k => lower.includes(k))) return { error: lastError }; await new Promise(r => setTimeout(r, 800)); } if (this.logger) this.logger.error({ step: 'login-failed' }, 'login flow'); return { error: lastError || '登录失败(多次尝试后仍未通过验证码)' }; }