Skip to main content
Glama
MarveleE
by MarveleE

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
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • 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) }] };
    }
  • Tool registration entry in the tools array, including name, description, and input schema.
    {
      name: 'rap2_ensure_session',
      description: '使用环境参数登录并保存 Cookie 到会话缓存',
      inputSchema: {
        type: 'object',
        properties: {},
      },
    },
  • 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;
    }
  • 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 || '登录失败(多次尝试后仍未通过验证码)' };
    }

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/MarveleE/rap2-mcp'

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