Skip to main content
Glama

manual_login

Manually log in to Upwork by opening a visible browser window where you can solve CAPTCHA, enter credentials, and handle 2FA. Then use save_session to persist the session.

Instructions

Step 1: Open a visible browser window at Upwork login page. Returns immediately — you then login manually in the browser (solve CAPTCHA, enter credentials, handle 2FA). After login is complete, call save_session to persist the session.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Main implementation of manualLogin: connects to Chrome via CDP, finds an Upwork tab, extracts cookies, checks login state, and saves session file in Playwright-compatible format.
    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}` };
      }
    }
  • src/index.ts:25-32 (registration)
    Tool registration in the primary MCP server (index.ts): defines 'manual_login' tool metadata with name, description, and empty inputSchema.
    const TOOLS: Tool[] = [
      {
        name: 'manual_login',
        description: `Step 1: Open a visible browser window at Upwork login page.
    Returns immediately — you then login manually in the browser (solve CAPTCHA, enter credentials, handle 2FA).
    After login is complete, call save_session to persist the session.`,
        inputSchema: { type: 'object', properties: {} },
      },
  • Tool dispatch in index.ts: routes 'manual_login' calls to the manualLogin() function.
    switch (name) {
      case 'manual_login': {
        result = await manualLogin();
        break;
  • src/gateway.ts:48-53 (registration)
    Tool registration in gateway.ts: defines 'manual_login' tool metadata for the gateway layer that bridges to the worker.
    const TOOLS: Tool[] = [
      {
        name: 'manual_login',
        description: 'Connect to existing Chrome tab via CDP and save Upwork session cookies. Run connect-chrome.bat first, then call this.',
        inputSchema: { type: 'object', properties: {} },
      },
  • Worker dispatch: routes 'manual_login' tool calls from the HTTP worker to the manualLogin() function.
    async function runTool(name: string, args: Record<string, unknown>): Promise<unknown> {
      switch (name) {
        case 'manual_login':    return manualLogin();
        case 'save_session':    return saveSession();
Behavior4/5

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

No annotations provided, but description clearly states that it returns immediately and then requires manual user interaction. Discloses the key behavior without contradictions, though could mention if multiple calls create multiple windows.

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 short, front-loaded sentences. No unnecessary words, every sentence adds value.

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

Completeness5/5

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

For a tool with no parameters and no output schema, the description is complete. It explains the workflow and references the next step (save_session), making it actionable for the agent.

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?

Input schema has zero parameters, so description does not need to add parameter semantics. The description explains the tool's action adequately, achieving a baseline of 4 for a parameterless tool.

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?

The description clearly states the tool's purpose: to open a browser window at the Upwork login page for manual login. It distinguishes itself from siblings like save_session by explaining the sequential flow.

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 instructs the agent to open the browser as Step 1 and then call save_session after manual login. Does not explicitly mention when not to use it, but the context of manual login for CAPTCHA/2FA is implied.

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