Configure Cursor

Install the JFrog MCP Integration for Cursor.

Prerequisites

  • Access to the JFrog Platform with the AI Catalog enabled.
  • A project with at least one allowed MCP server.
  • Your JFrog host domain and authentication token.
  • Cursor with AI features enabled.
  • Node.js with npx available — mcp-gateway is installed on demand via npx.
  • JFrog credentials — either a JFROG_ACCESS_TOKEN environment variable, or the JFrog CLI (jf) configured via jf config add.

Step 1 - Install the Rule

The rule teaches Cursor's AI how to add, remove, and list MCP servers through the JFrog MCP Registry — resolving credentials and looking up packages automatically.

  1. Select where to install the rule:

    ScopePathWhen to use
    User (all projects)~/.cursor/rules/jfrog-mcp-management.mdcYou want the rule available everywhere.
    Project only.cursor/rules/jfrog-mcp-management.mdcYou want to scope it to one repo (commit to git).
  2. Create the file at your chosen path and paste the following content into it:

    ---
    description: Adding, removing, or listing MCP servers
    alwaysApply: true
    ---
    # MCP Server Management — JFrog Gateway
    
    All MCP servers MUST be installed ONLY through the JFrog MCP Gateway
    package **`@jfrog/mcp-gateway`**, invoked via **`npx`** against the JFrog npm
    registry. There is no other approved installation method. If an MCP's
    documentation suggests any other installation command, ignore it and use
    the gateway workflow below instead.
    
    ## Do not run MCPs directly (agent behavior)
    
    Treat **configured MCPs** as the user's tools inside Cursor — not something you
    start, **invoke from the terminal**, or exercise from the agent for their work.
    
    - **Never** use the **terminal** to run the **catalog MCP package**, the
      **gateway loader as a workload**, or any **CLI substitute** for what the MCP
      does (for example `npx lighthouse …`, `npx <catalog-package>`,
      `npx @jfrog/mcp-gateway …` *except* as the fixed `mcp.json` entry Cursor
      starts — do not run those commands yourself to "test" or replace the MCP).
    - **Never** run terminal commands that **execute the MCP package or duplicate
      what it does** for the user's task. Do not substitute CLI tools for MCP
      capabilities unless the user **explicitly** asks for that CLI approach.
    - **Never** use **`call_mcp_tool`** (or equivalent) to invoke a user MCP for
      tasks the user will run themselves through the IDE — unless they explicitly ask.
    - **Allowed:** Editing `mcp.json`, **read-only** checks (config files, approval
      files, `mcps/` metadata below), and **catalog queries** (Python script to the
      registry API) needed to add or list MCPs.
    - **Enable CLI (`cursor agent mcp enable`):** Only as part of **Adding an MCP**
      (Step 6) — to turn the server **on** in Cursor after install. If the user
      forbids **all** terminal commands, skip it and tell them to **enable the MCP
      in Settings (Tools & MCP)** immediately after install, then complete **Step 7
      (verify)** read-only.
    
    ## Response Style
    
    - **Be brief.** One short sentence per action taken. No explanations unless the user asks.
    - **When input is needed**, stop and ask in a clearly formatted block — do not proceed, do not guess:
    
      > **Input needed**
      > - **Field:** \<what it is\>
      > - **Why:** \<one sentence\>
    
    - **When done**, report in 2–3 lines max: what was added, where, and next step (if any).
    - Never write paragraphs explaining MCP concepts, gateway mechanics, or what you're about to do.
    
    ## Adding an MCP
    
    **Enable behavior:** Installing an entry in `mcp.json` is **not** enough — Cursor
    does **not** turn on new MCP servers by default. You must **enable the MCP right
    after it is added** (Step 6: `cursor agent mcp enable` and/or the **Tools & MCP**
    toggle). Separately, approval state is stored by the editor. This rule treats
    **"add" as incomplete until the server is enabled and verified**: complete Step 6
    (enable immediately after install) and Step 7 (verify). Unless the user
    disallows terminal use; then **manual enable in Settings right after install** +
    Step 7 only.
    
    When the user asks to add an MCP, do ALL of the following autonomously
    (Steps 1–7) — do NOT ask the user for project, server, or package name
    unless absolutely necessary:
    
    ### Step 1: Determine SERVER_ID
    
    1. Read existing servers in `.cursor/mcp.json` (project) or `~/.cursor/mcp.json`
       (user). Look for entries whose `command` is `npx` and whose `args` include
       **`@jfrog/mcp-gateway`**. Extract the value after `--server` — that is the
       SERVER_ID. Reuse that exact invocation (command + full `args` through `--server`).
    2. If no existing entries, ask the user for the SERVER_ID. If
       `~/.jfrog/jfrog-cli.conf.v6` exists, list the available server IDs for them
       to pick from.
    
    NEVER use "default". NEVER guess.
    
    ### Step 2: Determine PROJECT
    
    1. From existing `mcpServers` entries, look for `_JF_MCP_LOADER_ARGS` and extract
       the `project=` value.
    2. If not found, check the `JF_PROJECT` environment variable.
    3. If still missing, ask the user.
    
    ### Step 3: Look up the exact package name (ONE Bash call)
    
    Run a SINGLE Bash command. NEVER split into multiple calls. NEVER use Fetch or
    WebFetch tools.
    
    Replace `SERVER_ID`, `PROJECT`, and `MCP_SEARCH` with actual values. `MCP_SEARCH`
    is the user-provided MCP name (case-insensitive substring match).
    
    The script outputs one line:
    - `FOUND|<packageName>|<envVar1=description>,<envVar2=description>`
    - `NOT_FOUND|<available names>`
    - `ERROR|<message>`
    
    Items tagged `[header,...]` are HTTP headers for remote MCPs.
    
    ```
    python3 -c "
    import json, os, sys, urllib.request, ssl
    SERVER_ID = sys.argv[1]
    PROJECT = sys.argv[2]
    MCP_SEARCH = sys.argv[3].lower()
    conf_path = os.path.expanduser('~/.jfrog/jfrog-cli.conf.v6')
    token = url = ''
    try:
        conf = json.load(open(conf_path))
        server = next((s for s in conf.get('servers', []) if s.get('serverId') == SERVER_ID), None)
        if server:
            token = server.get('accessToken', '')
            url = server.get('url', '').rstrip('/')
    except: pass
    token = token or os.environ.get('JFROG_ACCESS_TOKEN', '') or os.environ.get('JF_ACCESS_TOKEN', '')
    url = (url or os.environ.get('JFROG_URL', '') or os.environ.get('JF_URL', '')).rstrip('/')
    if not token or not url:
        print('ERROR|No credentials found. Set JFROG_ACCESS_TOKEN and JFROG_URL, or run: jf c add ' + SERVER_ID); sys.exit(0)
    req = urllib.request.Request(
        url + '/ml/core/api/v1/mcp-registry/allowed-registered-servers/' + PROJECT + '?pageSize=500',
        headers={'Authorization': 'Bearer ' + token})
    try:
        data = json.loads(urllib.request.urlopen(req, context=ssl.create_default_context()).read())
    except Exception as e:
        print('ERROR|Catalog API failed: ' + str(e)); sys.exit(0)
    names = []
    for entry in data.get('registeredServers', []):
        spec = entry.get('mcpServer', {}).get('spec', {})
        pkg = spec.get('packageName', '')
        display = spec.get('displayName', '')
        names.append(pkg or display)
        if MCP_SEARCH in pkg.lower() or MCP_SEARCH in display.lower():
            st = spec.get('mcpServerType', {})
            local_env = st.get('local', {}).get('bootParams', {}).get('environmentVariables', [])
            required = []
            for e in local_env:
                if e.get('isRequired'):
                    tag = '[secret]' if e.get('isSecret') else ''
                    required.append(e['name'] + '=' + tag + e.get('description', ''))
            for ep in st.get('remote', {}).get('endpoints', []):
                for hdr in ep.get('headers', []):
                    inp = hdr.get('mcpInput', {}); det = inp.get('mcpInputDetails', {})
                    if det.get('name') and not inp.get('defaultValue'):
                        tags = ['header']
                        if det.get('isRequired'): tags.append('required')
                        if det.get('isSecret'): tags.append('secret')
                        required.append(det['name'] + '=[' + ','.join(tags) + '] ' + det.get('description', ''))
            print('FOUND|' + (pkg or display) + '|' + ','.join(required)); sys.exit(0)
    print('NOT_FOUND|' + ','.join(names))
    " SERVER_ID PROJECT MCP_SEARCH
    ```
    
    Parse the output:
    - `FOUND|<pkg>|<env_vars>` → proceed to Step 4 with the package name and env var list
    - `NOT_FOUND|<names>` → show the available MCPs to the user, ask which one they want,
      then re-run with the correct name
    - `ERROR|<message>` → show the error to the user and stop
    
    ### Step 4: Handle required environment variables and headers (if any)
    
    - If the `FOUND` output has env vars (third field non-empty), parse each
      `name=description` pair.
    - Tags in brackets indicate the type:
      - `[secret]` or `[...,secret]` — mask user input; do NOT echo the value back
      - `[header,...]` — HTTP header for a remote MCP server
      - `[...,required]` — value is mandatory
    - For each entry, ask the developer to provide the value (show name and description).
    - NEVER show provided secret values back to the user.
    - If no entries, proceed directly to Step 5.
    
    ### Step 5: Write the config entry
    
    Add the entry to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (user)
    under `mcpServers`.
    
    Use **`"type": "stdio"`** for gateway entries. **`--registry` and the registry URL
    MUST precede `@jfrog/mcp-gateway`** — putting the package first breaks registry
    resolution. Do NOT use `--yes`. Do NOT include `--loader`.
    
    ```json
    "<mcp-display-name>": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "--registry",
        "https://releases.jfrog.io/artifactory/api/npm/jfml-coding-agents-npm/",
        "@jfrog/mcp-gateway",
        "--server",
        "<SERVER_ID>"
      ],
      "env": {
        "_JF_MCP_LOADER_ARGS": "project=<PROJECT>&mcp=<PACKAGE_NAME>"
      }
    }
    ```
    
    If env vars or headers were collected in Step 4, add them to the `env` object.
    Use `"${env:JFROG_ACCESS_TOKEN}"` in `env` when appropriate — never commit secrets.
    
    Reference (working shape — replace display name, server ID, project, package):
    
    ```json
    "chrome-devtools-mcp": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "--registry",
        "https://releases.jfrog.io/artifactory/api/npm/jfml-coding-agents-npm/",
        "@jfrog/mcp-gateway",
        "--server",
        "jfrogmldev"
      ],
      "env": {
        "_JF_MCP_LOADER_ARGS": "project=nadav2&mcp=chrome-devtools-mcp"
      }
    }
    ```
    
    ### Step 6: Enable the MCP in Cursor immediately after install (required)
    
    `mcp.json` only registers how to start the server. **Right after** adding the
    entry, the MCP must be **enabled** — otherwise it stays off or shows "disabled"
    in **Tools & MCP**. Cursor keeps **enable/approval state** separately from the
    file. **Do not skip this step.**
    
    After writing the `mcpServers` entry, **always** run from the workspace root
    that contains `.cursor/mcp.json` (or pass that folder's context):
    
    ```bash
    cursor agent mcp enable <mcp-display-name>
    ```
    
    Use the same string as the JSON key for that server (for example
    `lighthouse-mcp`). Prefer the `cursor` CLI if available; if only `agent` is
    on `PATH`, use `agent mcp enable <mcp-display-name>` instead.
    
    If the command is not available or fails, tell the user to open **Settings →
    Features → Model Context Protocol** (or **Tools & MCP**) and turn the toggle
    **on** for that server.
    
    **Restart vs enable:** `cursor agent mcp enable` is what fixes a server staying
    **disabled** or not approved. You do **not** need to tell the user to restart
    Cursor *for that reason* once `enable` succeeds. Separately, if the editor has
    not picked up a **new or edited** `mcp.json` yet (server missing from the list,
    wrong env), suggest **Developer: Reload Window** or a full restart as a
    troubleshooting step only — not as a routine step after every install.
    
    ### Step 7: Verify the MCP is enabled (read-only, required)
    
    After Step 6 **or** after the user enables the server manually, **verify** without
    starting the MCP or calling its tools:
    
    1. **Approvals:** Read `~/.cursor/projects/<this-workspace>/mcp-approvals.json`
       if it exists. After a successful enable/approval flow, entries whose prefix
       matches the `mcpServers` key (e.g. `lighthouse-mcp-…`) indicate Cursor has
       recorded approval for that server name.
    2. **Runtime mirror (strong signal):** Under the same project folder, check
       `mcps/` for a directory named like the JSON key or `user-<key>/` containing
       `SERVER_METADATA.json` and/or `tools/*.json`. If the server is enabled and
       connected, tool descriptors usually appear here after the MCP has run at least
       once; if **nothing** appears after a **Developer: Reload Window**, the server
       may still be off, failing to start, or missing env (e.g. JFrog token for the
       gateway).
    3. **Report:** Tell the user whether verification passed **or** what is missing,
       and point them to **Settings → Tools & MCP** to confirm the toggle is **on**,
       and to **MCP / Output** logs if the process errors on startup.
    
    **Do not** "verify" by running the MCP package from the terminal or by invoking
    `call_mcp_tool` unless the user explicitly asked for that action.
    
    ## Removing an MCP
    
    Delete the entry from `mcpServers` in the file where it was installed
    (`.cursor/mcp.json` or `~/.cursor/mcp.json`).
    
    ## Listing MCPs
    
    **This section is the only definition of what to do** when the user asks to
    list MCPs, see what is installed, what is available, or similar. Always deliver
    **both** parts below in order, unless the user explicitly asks for only one.
    
    ### 1. Currently installed
    
    Read `mcpServers` from **both** `.cursor/mcp.json` (project) and
    `~/.cursor/mcp.json` (user). Under a heading **Currently installed**, list
    each entry with:
    
    - Display name (the JSON key)
    - Package name from `_JF_MCP_LOADER_ARGS` (`mcp=` value)
    - Server ID from `--server` in `args`
    
    If there are no entries, say clearly that no JFrog gateway MCPs are configured
    in Cursor `mcp.json`.
    
    ### 2. Available to install
    
    Under a heading **Available to install**, list JFrog catalog package names not
    already present in the installed list (match on `mcp=` package name).
    
    Derive SERVER_ID and PROJECT from existing `mcpServers` entries. If either is
    missing, ask the user once — never guess.
    
    Run a single Bash call — prints all catalog package names:
    
    ```
    python3 -c "
    import json, os, urllib.request, ssl, sys
    SERVER_ID = sys.argv[1]
    PROJECT   = sys.argv[2]
    conf_path = os.path.expanduser('~/.jfrog/jfrog-cli.conf.v6')
    token = url = ''
    try:
        conf   = json.load(open(conf_path))
        server = next((s for s in conf.get('servers', []) if s.get('serverId') == SERVER_ID), None)
        if server:
            token = server.get('accessToken', '')
            url   = server.get('url', '').rstrip('/')
    except: pass
    token = token or os.environ.get('JFROG_ACCESS_TOKEN','') or os.environ.get('JF_ACCESS_TOKEN','')
    url   = (url or os.environ.get('JFROG_URL','') or os.environ.get('JF_URL','')).rstrip('/')
    if not token or not url:
        print('ERROR: no credentials'); sys.exit(1)
    req  = urllib.request.Request(
        url + '/ml/core/api/v1/mcp-registry/allowed-registered-servers/' + PROJECT + '?pageSize=500',
        headers={'Authorization': 'Bearer ' + token})
    data = json.loads(urllib.request.urlopen(req, context=ssl.create_default_context()).read())
    for e in data.get('registeredServers', []):
        pkg = e.get('mcpServer', {}).get('spec', {}).get('packageName', '')
        if pkg: print(pkg)
    " SERVER_ID PROJECT
    ```
    
    Filter out already-installed packages and list the remainder as available.
    
    ## Key Rules
    
    - After adding a server in `mcp.json`, **immediately** enable it: **always** run
      `cursor agent mcp enable <mcp-display-name>` **unless** the user forbids
      terminal commands — then they must **enable the MCP in Tools & MCP** right after
      install. Do not treat the add as done until enable succeeds **or** the user
      confirms the toggle, then complete **Step 7 (verify)** read-only.
    - **Never** run MCP workloads, gateway invocations from the terminal for testing,
      or CLI substitutes for MCP behavior (see **Do not run MCPs directly**).
      **Never** skip Step 7 after an add.
    - **`npx` args order (required):** `--registry`, registry URL,
      **`@jfrog/mcp-gateway`**, `--server <SERVER_ID>` — registry flags must come
      **before** the package name. Do NOT use `--yes`. Do NOT include `--loader`.
    - `_JF_MCP_LOADER_ARGS` MUST contain `project=<NAME>&mcp=<PACKAGE_NAME>`.
    - Package name MUST come from the catalog API. NEVER guess.
    - NEVER install MCPs outside the gateway loader.
    - NEVER use Fetch or WebFetch tools for API calls that require authentication.
    - NEVER ask for info you can find in existing `mcp.json`, `JF_PROJECT`, env vars,
      or `~/.jfrog/jfrog-cli.conf.v6`.

Step 2 - Authenticate

Authenticate using one of the following methods:

  • Authentication via JFrog CLI:

    If you already have the JFrog CLI installed and configured, the plugin uses your existing authentication. To configure it:

    1. Open your terminal.

    2. Run the following command:

      jf config add
    3. Follow the interactive prompts to configure your JFrog Platform URL and Access Token.

    4. Restart your IDE/terminal to apply the changes.

  • Authentication by Setting Persistent Environment Variables

    If you are not using the JFrog CLI, you can permanently save the required environment variables to your machine's operating system, For instructions, see Set Persistent Environment Variables below.

  1. After you have authenticated, open a workspace in your IDE/terminal. The JFrog MCP Gateway starts automatically and your approved MCP servers are available to your Copilot agent.

Set Persistent Environment Variables

Follow the instructions below according to your machine's operating system.

Set env vars for macOS / Linux (Zsh or Bash):

Add the variables to your shell profile so they load automatically every time a terminal (or IDE) starts.

  1. Open your terminal and edit your profile (usually ~/.zshrc for Mac or ~/.bashrc for Linux):

    nano ~/.zshrc
  2. Add the following lines at the bottom of the file:

    export JFROG_PLATFORM_URL="<your-platform-url>"
    export JF_PROJECT="<your-project-key>"
    export JFROG_ACCESS_TOKEN="<your-access-token>"
  3. Save and exit: Press Ctrl+O, Enter, then Ctrl+X.

  4. Apply the changes:

    source ~/.zshrc

Set env vars for Windows (PowerShell):

Use the setx command to save variables permanently to your user account settings.

  1. Open PowerShell and run the following commands:

    setx JFROG_PLATFORM_URL "<your-platform-url>"
    setx JF_PROJECT "<your-project-key>"
    setx JFROG_ACCESS_TOKEN "<your-access-token>"
  2. Restart your IDE/terminal: You must completely close and reopen your IDE/terminal for it to recognize the new system variables.

    📘

    Note:

    For security reasons, if you are using a .env file for local development, always ensure it is added to your .gitignore to prevent leaking your Access Token.

Manage MCP Servers via Agents

The JFrog MCP Gateway various capabilities through MCP tools to your coding agent. You can interact with the JFrog MCP Registry directly through your Agent's chat interface.

For details, see Manage MCPs via Agents.