Skip to main content
Glama

save_session

Save the browser session after manual login to Upwork. Verifies successful login, then persists cookies and storage for future headless automation. Call this after fully logged in.

Instructions

Step 2: After manually logging in via manual_login, call this to save the browser session. Checks if login was successful and saves cookies/storage for future headless use. Must be called AFTER you have fully logged in to Upwork in the browser window.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Core handler for save_session. The `manualLogin` function connects to Chrome via CDP, extracts cookies from an Upwork tab, and saves the session file. It is re-exported as `saveSession` on line 132.
    export async function manualLogin(): Promise<{ success: boolean; message: string }> {
      try {
        log('Checking CDP at localhost:', CDP_PORT);
    
        // Get list of tabs
        const targets = await cdpFetch('/json') as Array<{
          type: string; url: string; webSocketDebuggerUrl: string; title: string;
        }>;
    
        const upworkTarget = targets.find(t =>
          t.type === 'page' && t.url.includes('upwork.com')
        );
    
        if (!upworkTarget) {
          const allUrls = targets.filter(t => t.type === 'page').map(t => t.url);
          return {
            success: false,
            message: `Chrome is connected but no Upwork tab found.\nOpen tabs: ${allUrls.join(', ')}\nPlease navigate to upwork.com first.`,
          };
        }
    
        log('Found Upwork tab:', upworkTarget.url);
    
        // Check if logged in
        if (upworkTarget.url.includes('/login') || upworkTarget.url.includes('account-security')) {
          return {
            success: false,
            message: `Upwork tab found but not logged in. URL: ${upworkTarget.url}\nPlease login to Upwork first, then call manual_login again.`,
          };
        }
    
        // Get all cookies for upwork.com
        const cookieResult = await cdpCommand(
          upworkTarget.webSocketDebuggerUrl,
          'Network.getAllCookies'
        ) as { cookies: Array<{
          name: string; value: string; domain: string; path: string;
          expires: number; httpOnly: boolean; secure: boolean; sameSite?: string;
        }> };
    
        const upworkCookies = cookieResult.cookies.filter(c =>
          c.domain.includes('upwork.com')
        );
    
        log(`Found ${upworkCookies.length} Upwork cookies`);
    
        // Build Playwright-compatible storageState format
        const storageState = {
          cookies: upworkCookies.map(c => ({
            name: c.name,
            value: c.value,
            domain: c.domain,
            path: c.path,
            expires: c.expires,
            httpOnly: c.httpOnly,
            secure: c.secure,
            sameSite: (c.sameSite as 'Strict' | 'Lax' | 'None') ?? 'None',
          })),
          origins: [] as unknown[],
        };
    
        // Save session file
        const sessionDir = path.dirname(config.session.file);
        fs.mkdirSync(sessionDir, { recursive: true });
        fs.writeFileSync(config.session.file, JSON.stringify(storageState, null, 2));
    
        log('Session saved to', config.session.file);
    
        return {
          success: true,
          message: `Session saved! Extracted ${upworkCookies.length} cookies from: ${upworkTarget.url}\nAll tools are now ready. Session file: ${config.session.file}`,
        };
      } catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        if (msg.includes('ECONNREFUSED') || msg.includes('fetch') || msg.includes('connect')) {
          return {
            success: false,
            message:
              'Cannot connect to Chrome CDP. Chrome is not running with --remote-debugging-port=9222.\n' +
              'Fix: Run "connect-chrome.bat" in the upwork-mcp folder, then call manual_login again.',
          };
        }
        return { success: false, message: `Error: ${msg}` };
      }
    }
  • Re-export alias: `manualLogin` is exported as `saveSession`, making the same function available under both names.
    export { manualLogin as saveSession };
  • Tool schema definition for `save_session` — declares it as an object with no required properties, with a description that it's Step 2 after manual_login.
      {
        name: 'save_session',
        description: `Step 2: After manually logging in via manual_login, call this to save the browser session.
    Checks if login was successful and saves cookies/storage for future headless use.
    Must be called AFTER you have fully logged in to Upwork in the browser window.`,
        inputSchema: { type: 'object', properties: {} },
      },
  • src/index.ts:284-287 (registration)
    Registration in the direct MCP server (`src/index.ts`): the `save_session` case dispatches to `saveSession()` in the CallTool request handler.
    case 'save_session': {
      result = await saveSession();
      break;
    }
  • src/gateway.ts:54-58 (registration)
    Registration in the gateway MCP server (`src/gateway.ts`): the `save_session` tool definition (alias for manual_login) in the gateway's tool list.
    {
      name: 'save_session',
      description: 'Alias for manual_login — re-save session from current Chrome tab.',
      inputSchema: { type: 'object', properties: {} },
    },
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided. Description discloses that it checks login success and saves cookies/storage, but does not detail failure handling or whether it overwrites. Adequate but not exhaustive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three concise sentences front-loaded with 'Step 2' and clear instructions. No extraneous information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no parameters, no output schema, and no annotations, the description covers prerequisites, purpose, and ordering. Lacks details on error handling but sufficient for simple tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters in input schema (0 params, 100% coverage). Description adds value by explaining the tool's purpose without needing param details. Baseline 4 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the verb 'save the browser session' and specifies the resource (cookies/storage) and context (after manual login). It distinguishes from sibling manual_login by specifying it's step 2.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Must be called AFTER you have fully logged in' and positions it as step 2 after manual_login. Does not list alternatives but the workflow is clear enough.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/zcrossoverz/upwork-mcp'

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