# flconsole MCP server

The flconsole MCP server gives your AI agent new Upwork jobs and your job feeds. Your agent finds new jobs, reads them and shows you the ones that fit. This page lists every tool, argument, filter and limit.

## Overview

| Setting | Value |
|---|---|
| Server URL | `https://mcp.lite.flconsole.com/mcp` |
| Transport | Streamable HTTP |
| Sign-in | Through Telegram: no password, no API key |
| Price | Free |

- **New jobs only.** The server returns jobs posted in the last 30 minutes. There is no search in older jobs.
- **Your agent asks; the server doesn't push.** The agent checks for new jobs when you ask it or on a schedule. For alerts within a minute of posting, use the [Telegram bot](https://flconsole.com/docs/telegram).
- **Feeds are shared with Telegram.** A feed your agent creates shows up in the bot under `/feeds`, and its alerts arrive in Telegram. Up to 5 feeds in total.
- **No access to your Upwork account.** flconsole never asks for your Upwork login and can't act in your account.
- **Your agent does the reading.** Filters are structured JSON. Picking jobs by meaning is done by your agent, not by the server.

## Connect

Add the server to your client, then sign in. Sign-in works the same way everywhere:

1. Your client opens a sign-in page in the browser.
2. Tap **Continue in Telegram** (or scan the QR code with your phone). The flconsole bot opens. If you're new, tap Start — that's all it takes.
3. The bot names the app that wants access. Tap **Allow**, and the bot shows a 4-digit code.
4. Enter the code on the sign-in page. Your client is connected.

The sign-in link works for 10 minutes. After 5 wrong codes the request is cancelled; start again in your client.

### Claude Code

```bash
claude mcp add --transport http flconsole https://mcp.lite.flconsole.com/mcp
```

Add `--scope user` to make the server available in all your projects. Then run `/mcp` in Claude Code, pick `flconsole` and choose **Authenticate**.

To check jobs on a schedule: `/loop 10m check new jobs in my feeds and show only relevant ones`. Step-by-step guide: [flconsole in Claude Code](https://flconsole.com/mcp/claude-code).

### Codex

```bash
codex mcp add flconsole --url https://mcp.lite.flconsole.com/mcp
codex mcp login flconsole
```

The second command opens the sign-in page in your browser. Guide: [flconsole in Codex](https://flconsole.com/mcp/codex).

### Claude app

Web, desktop and mobile: **Settings → Connectors → Add custom connector**, then paste `https://mcp.lite.flconsole.com/mcp`. The app opens the sign-in page. Guide: [flconsole in the Claude app](https://flconsole.com/mcp/claude).

### Cursor

Add the server to `~/.cursor/mcp.json`:

```json title="~/.cursor/mcp.json"
{
  "mcpServers": {
    "flconsole": { "url": "https://mcp.lite.flconsole.com/mcp" }
  }
}
```

Then sign in from Cursor's MCP settings when it asks. Guide: [flconsole in Cursor](https://flconsole.com/mcp/cursor).

### Other clients

Any MCP client with Streamable HTTP works. If it supports sign-in (OAuth), add the server URL and sign in as above. If it doesn't, use a manual token.

### Manual token

For clients without sign-in support. In the Telegram bot, send `/mcp` and tap **Manual token (other apps)**. The bot shows the token **once** and doesn't store it, together with ready-to-paste commands.

Send it as a header: `Authorization: Bearer <token>`.

Claude Code:

```bash
claude mcp add --transport http flconsole https://mcp.lite.flconsole.com/mcp \
  --header "Authorization: Bearer <token>"
```

Codex, in `~/.codex/config.toml`, with the token in an environment variable:

```toml title="~/.codex/config.toml"
[mcp_servers.flconsole]
url = "https://mcp.lite.flconsole.com/mcp"
bearer_token_env_var = "FLCONSOLE_MCP_TOKEN"
```

```bash
export FLCONSOLE_MCP_TOKEN=<token>
```

Lost or leaked a token? Revoke it in `/mcp` and create a new one. Manual tokens and sign-ins share the limit of 5 connections.

## Tools

The server has 11 tools.

| Tool | What it does | Limit |
|---|---|---|
| `find_jobs` | New jobs by a feed, all feeds or any filters | 100 jobs an hour, 1,000 a day |
| `get_job_details` | Full data for the jobs you picked | 30 details an hour |
| `list_feeds` | Your feeds with filters and status | — |
| `create_feed` | Creates a feed; needs your approval | 20 changes an hour |
| `update_feed` | Changes, pauses or resumes a feed | 20 changes an hour |
| `delete_feed` | Deletes a feed; needs your approval | 20 changes an hour |
| `preview_filters` | How many jobs matched in the last hours, with samples | 10 an hour |
| `parse_upwork_url` | Turns an Upwork search link into filters | 30 an hour |
| `list_categories` | Upwork categories and subcategories | — |
| `find_locations` | Countries and regions by name | — |
| `get_usage` | What's left of your limits | — |

Every tool also counts toward 60 calls a minute per connection.

### find_jobs

Returns new jobs from one feed, from all your feeds, or by filters you pass in the call. Pass exactly one source: `feed_id`, `all_feeds` or `filters`.

| Argument | Type | Required | What it does |
|---|---|---|---|
| `feed_id` | `integer` | No | One feed |
| `all_feeds` | `boolean` | No | All your feeds |
| `filters` | `object` | No | Any filters, without a feed |
| `cursor` | `string` | No | Only with filters: cursor from the previous response |
| `lookback_minutes` | `integer` | No | Only with filters and without cursor; default and maximum 30 |
| `limit` | `integer` | No | 1–30, default 10; capped by the remaining quota |

- Each call returns only jobs newer than the previous check, and never jobs older than 30 minutes. Jobs come oldest first, as [short cards](#job-fields).
- With `feed_id` or `all_feeds`, the server remembers where each feed stopped, separately for each connection. Each job lists the feeds it matched in `matched_feeds`. Paused feeds are checked too.
- With `filters`, nothing is saved. Pass the `cursor` from the response into the next call to get only newer jobs.
- A feed's AI filter applies to Telegram alerts only. `find_jobs` doesn't apply it: your agent judges the jobs itself.
- The response also has `skipped_count`, your `quota` for the hour and the day, and `next_check_after`.

Limit: 100 jobs an hour, 1,000 a day, one call every 60 seconds.

### get_job_details

Full data of jobs returned by `find_jobs`: experience level, duration, workload, category and client statistics. Use it for the few jobs worth a closer look, not for every job found.

| Argument | Type | Required | What it does |
|---|---|---|---|
| `upwork_ids` | `array of strings` | Yes | 1–5 `upwork_id` values from `find_jobs` |

Every requested job counts toward the quota, including ones that weren't found. Jobs that weren't found are listed in `not_found`.

Limit: 30 jobs an hour.

### list_feeds

Your feeds: `feed_id`, name, filters, a one-line summary, AI filter and status. No arguments.

`telegram_delivery` shows whether Telegram alerts are on: `active`, `paused` (this feed is paused), `stopped` (you sent `/stop`) or `blocked` (you blocked the bot). The response also has your feed limit.

### create_feed

Creates a feed: a saved search in the Telegram bot that sends you an alert about every new matching job. To just look at jobs in your agent, use `find_jobs` with filters instead: no feed needed.

| Argument | Type | Required | What it does |
|---|---|---|---|
| `filters` | `object` | Yes | Complete [filters](#filters); `base.q` is required |
| `name` | `string` | No | Up to 40 characters; automatic if omitted |
| `ai_filter` | `string` | No | AI filter rules in plain words, up to 2,000 characters |
| `is_active` | `boolean` | No | Default `true`; `false` creates the feed paused |
| `user_confirmed` | `boolean` | Yes | `true` only after you agreed to a feed with Telegram alerts |

You get a message in Telegram about the new feed.

Limit: 20 feed changes an hour (create, update and delete together).

### update_feed

Changes a feed's name, filters or AI filter, or pauses and resumes it. Pass at least one of `name`, `filters`, `ai_filter`, `is_active`.

| Argument | Type | Required | What it does |
|---|---|---|---|
| `feed_id` | `integer` | Yes | Feed id from `list_feeds` |
| `filters` | `object` | No | Replaces the saved filters entirely: send the full object |
| `name` | `string` | No | New name |
| `ai_filter` | `string` | No | Replaces the AI filter; `""` removes it; omit to keep it |
| `is_active` | `boolean` | No | `false` pauses, `true` resumes |
| `user_confirmed` | `boolean` | Yes | `true` only after you approved this exact change |

You get a message in Telegram about what changed.

Limit: 20 feed changes an hour.

### delete_feed

Deletes a feed.

| Argument | Type | Required | What it does |
|---|---|---|---|
| `feed_id` | `integer` | Yes | Feed id from `list_feeds` |
| `user_confirmed` | `boolean` | Yes | `true` only after you approved deleting this feed |

You get a message in Telegram about the deleted feed.

Limit: 20 feed changes an hour.

### preview_filters

How many jobs matched the filters in the last hours, with up to 3 short samples. Use it to check filters before creating a feed or calling `find_jobs`.

| Argument | Type | Required | What it does |
|---|---|---|---|
| `filters` | `object` | Yes | Filters to check |
| `window_hours` | `integer` | No | 1–24, default 24 |

Each sample has the title, link, job type, rate or budget, posting time and the client's country, total spent and rating.

Limit: 10 calls an hour.

### parse_upwork_url

Turns an Upwork job search link into filters. Open `upwork.com/nx/search/jobs`, set your search and filters, and copy the address.

| Argument | Type | Required | What it does |
|---|---|---|---|
| `url` | `string` | Yes | Upwork job search link |

Returns `filters`, a readable list of them, a suggested feed name and any link parameters that weren't recognized. A link to a single job is not a search link.

Limit: 30 calls an hour.

### list_categories

Upwork categories and subcategories with their `upwork_id`, for `base.category2_uid` and `base.subcategory2_uid`. No arguments.

### find_locations

Finds countries, subregions and regions by name or alias, for `base.location`.

| Argument | Type | Required | What it does |
|---|---|---|---|
| `query` | `string` | Yes | Part of a name, 1–100 characters |

Returns up to 30 matches with type (`country`, `subregion` or `region`), aliases and the region each belongs to.

### get_usage

What's left of your hourly and daily quotas, when the next `find_jobs` is allowed, and your feed count and limit. No arguments.

## Filters

`create_feed`, `update_feed`, `preview_filters` and `find_jobs` take the same filters object:

```json title="Filters object"
{
  "schema_version": 1,
  "base": {
    "q": "elevenlabs OR vapi OR retell",
    "t": ["hourly"],
    "hourly_rate": { "min": 40, "max": null },
    "contractor_tier": ["expert"],
    "location": { "include": ["United States", "Western Europe"], "exclude": [] },
    "payment_verified": true
  },
  "advanced": {
    "cl_total_spent": { "min": 10000, "max": null },
    "cl_rating": { "min": 4.5, "max": null }
  }
}
```

- `schema_version` is required and is always `1`.
- A key that is omitted, `null` or `[]` is not set. Yes/no filters take `true`, `false` or `null` (any).
- Ranges are `{ "min": …, "max": … }`; either side can be `null`.
- Unknown keys are rejected with `invalid_filters`, so a typo never silently filters nothing.
- Client filters: a client without that statistic doesn't match a range.

### base

| Key | Type | What it filters |
|---|---|---|
| `q` | `string` | Search query, required. See [search syntax](#search-syntax) |
| `category2_uid` | `array of strings` | Categories: `upwork_id` from `list_categories` |
| `subcategory2_uid` | `array of strings` | Subcategories: `upwork_id` from `list_categories` |
| `t` | `array` | Job type: `hourly`, `fixed` |
| `contractor_tier` | `array` | Experience level: `entry`, `intermediate`, `expert` |
| `amount` | `range` | Fixed budget, $ |
| `hourly_rate` | `range` | Hourly rate, $ |
| `client_hires` | `array of ranges` | Client's number of hires; any of the ranges |
| `workload` | `array` | Hours per week: `less_than_30_hours`, `more_than_30_hours` |
| `duration_v3` | `array` | Project length: `less_than_one_month`, `1_to_3_months`, `3_to_6_months`, `more_than_6_months` |
| `contract_to_hire` | `boolean` | Contract-to-hire jobs |
| `location` | `object` | `include` and `exclude`: country, subregion or region names from `find_locations` |
| `payment_verified` | `boolean` | Client's payment method verified |

A job matches if it is in any of the listed categories or subcategories.

### advanced

| Key | Type | What it filters |
|---|---|---|
| `cl_total_spent` | `range` | Client's total spent, $ |
| `cl_rating` | `range` | Client's rating, 0–5 |
| `cl_review_cnt` | `range` | Number of reviews |
| `cl_hire_rate` | `range` | Hire rate, % |
| `cl_registered_at` | `object` | Account age: `max_days_ago: 1095` = older than 3 years; `min_days_ago: 30` = newer than 30 days |
| `cl_avg_hourly` | `range` | Average hourly rate paid, $ |
| `cl_paid_hours` | `range` | Hours paid |
| `cl_posted_jobs_cnt` | `range` | Jobs posted |
| `cl_open_jobs_cnt` | `range` | Open jobs |
| `cl_hires_cnt` | `range` | Hires |
| `cl_active_hires_cnt` | `range` | Active hires |
| `cl_work_history_cnt` | `range` | Jobs in work history |
| `cl_avg_spent_per_hire` | `range` | Total spent divided by hires, $ |
| `cl_phone_verified` | `boolean` | Phone verified |
| `cl_enterprise` | `boolean` | Enterprise client |
| `has_questions` | `boolean` | Job has screening questions |
| `has_attachments` | `boolean` | Job has attachments |

### Search syntax

`base.q` works like the search box on Upwork:

```text title="Search query"
react native          both words
"voice agent"         exact phrase
vapi OR retell        any of them
python NOT wordpress  exclude a word
```

- At least 3 letters: a shorter search matches almost every job.
- Not only exclusions: `NOT wordpress` alone is rejected. Add words to look for.
- Up to 1,000 characters. Parentheses group terms: `(vapi OR retell) AND voice`.
- Search in English: Upwork jobs are posted in English.
- Upwork search can't read field names like `skills:`, the characters `/ [ ] ~`, or `*` at the start of a word. Such a query is rejected with `invalid_query`.
- Rates, locations and client requirements belong in filters, not in the search.

## Job fields

`find_jobs` returns short cards: enough to judge a job by its text. `get_job_details` adds the terms and the client's history.

| Field | Short card | Full details |
|---|---|---|
| `upwork_id`, `link`, `title` | Yes | Yes |
| `description` | Up to 5,000 characters, with `description_truncated` | Full text |
| `skills` | Yes | Yes |
| `job_type`, `hourly_rate_from`, `hourly_rate_to`, `fixed_budget` | Yes | Yes |
| `publish_at` | Yes | Yes |
| `matched_feeds` | With `feed_id` or `all_feeds` | — |
| `experience_level`, `project_length`, `workload`, `contract_to_hire` | — | Yes |
| `has_questions`, `has_attachments`, `category` | — | Yes |
| `client.country` | Yes | Yes |
| Other client data | — | City, payment and phone verified, enterprise, rating, reviews, total spent, average hourly rate paid, hours paid, hire rate, account creation date, jobs posted, open jobs, hires, active hires, work history, company size and industry |

The client's name is never returned. `link` is a short flconsole link that opens the job on Upwork; if a short link can't be made, it is the direct upwork.com link.

## Limits

| What | Limit |
|---|---|
| Jobs via `find_jobs` | 100 an hour and 1,000 a day, across all feeds, filters and connections |
| Full job details (`get_job_details`) | 30 jobs an hour, up to 5 per call |
| Job age | Posted in the last 30 minutes; no search in older jobs |
| Checks for new jobs | At most once every 60 seconds; every 5–15 minutes is enough for monitoring |
| `find_jobs` with filters, no feed | At most 100 matches in the window, otherwise `filters_too_broad` |
| Feeds | 5, shared with Telegram; paused feeds count too |
| Feed changes (create, update, delete) | 20 an hour |
| `preview_filters` | 10 an hour |
| `parse_upwork_url` | 30 an hour |
| All tool calls | 60 a minute per connection |
| Connections | 5: sign-ins and manual tokens together |

Hourly quotas reset at the start of each hour (UTC), the daily quota at midnight UTC. `get_usage` shows what's left. MCP quotas don't affect Telegram alerts, and Telegram alerts don't use MCP quotas.

The server enforces these limits; your agent can't lift them, even if you ask.

## Errors

A failed call returns a normal tool result marked as an error, so your agent can read it and fix the call:

```json title="Error result"
{ "error": { "code": "filters_too_broad", "message": "…", "field": "filters", "retry_after_seconds": 60 } }
```

`field` and `retry_after_seconds` appear when they apply.

| Code | What it means |
|---|---|
| `invalid_request` | An argument is missing or wrong; `field` names it |
| `invalid_filters` | A filter is unknown or has a wrong value; `field` is its path, like `base.hourly_rate` |
| `invalid_query` | Upwork search can't read `base.q`, or it is too short or too long |
| `not_found` | No such feed among yours |
| `confirmation_required` | `create_feed`, `update_feed` or `delete_feed` without `user_confirmed: true` |
| `too_early` | Checked again sooner than 60 seconds |
| `quota_exhausted` | The hourly or daily `find_jobs` quota is used up |
| `rate_limited` | Another hourly limit is used up, or more than 60 calls a minute |
| `filters_too_broad` | Filters without a feed match more than 100 new jobs |
| `unavailable` | Temporary problem on our side; try again later |

### filters_too_broad

Your filters without a feed match more than 100 jobs in the window. Nothing is returned and no quota is used. Put every criterion into filters: category, job type, rate or budget, experience level, location, client statistics. Make the search more specific with `OR`, `AND` and parentheses. `preview_filters` shows how many jobs a set of filters matches. Try again after `retry_after_seconds`.

### Quota exceeded

`quota_exhausted` means the 100 jobs for this hour or the 1,000 for today have already been returned. The message says when the quota resets; `retry_after_seconds` is the wait. `get_job_details` and the other tools answer `rate_limited` when their hourly limit is used up. Narrow your filters so fewer, better jobs use the quota.

### skipped_count

`skipped_count` above 0 means more jobs matched than were returned. The skipped jobs are **not** returned later. The response then also has a `warning`. Narrow the filters or the feed's search so that everything that matches fits into one response.

### Too frequent checks

`too_early` means less than 60 seconds have passed since your last check. Wait `retry_after_seconds`. Nothing is lost: the next check returns the jobs posted in between. For regular monitoring, a check every 5–15 minutes is enough.

## Security

- **Sign-in through Telegram.** You approve each new connection in the bot and enter a 4-digit code on the sign-in page. Never share the code: whoever has it gets access to your feeds.
- **No password, no API key.** Connections use tokens; flconsole stores only their hashes. A sign-in session lasts up to 90 days from its last use.
- **Every feed change is reported in Telegram.** When your agent creates, changes, pauses, resumes or deletes a feed, the bot tells you. You see it even after `/stop`, so you notice a connection you didn't make.
- **Your approval for every write.** `create_feed`, `update_feed` and `delete_feed` require `user_confirmed: true`, which your agent may pass only after you agreed in the conversation.
- **Disconnect any time.** In the bot, send `/mcp`: it lists your connections with the date they were created and last used. Disconnect one, and it loses access right away. Your feeds stay.
- **No access to your Upwork account.** The server only reads public job data and your flconsole feeds.

See also: [Privacy](https://flconsole.com/privacy).

## Good to know

### Can the agent search old jobs?

No. Only jobs posted in the last 30 minutes.

### Does my agent get jobs pushed to it?

No. It checks when you ask or on a schedule. Telegram alerts arrive within a minute.

### What does the agent see about a job?

Title, description, skills, job type, budget or rate, and the client's country. Full details are available for the jobs it picks.

### How do I disconnect an agent?

In the Telegram bot: `/mcp`.

---

All tools, filters, job fields and limits of the flconsole MCP server for Upwork jobs. Server URL, sign-in and error handling.

Source: https://flconsole.com/docs/mcp
