Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cursor-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"metadata": {
"description": "JFrog Platform plugins for Cursor",
"version": "0.5.18",
"version": "0.5.19",
"pluginRoot": "plugins"
},
"plugins": [
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/sync-skills-vendor.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"repo": "jfrog/jfrog-skills",
"pin": "v0.29.0",
"pin": "v0.31.1",
"paths": [
"skills"
]
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ JFrog plugin for [Cursor](https://cursor.com): artifact management, security sca
## What's new

- **Agent Package Resolution (Preview).** A hook automatically routes the packages your AI agent installs through your JFrog Artifactory instead of public registries. See [Agent Package Resolution](#agent-package-resolution-preview).
- **AI Catalog skill.** New `jfrog-ai-catalog-skills` skill to discover, install, update, and publish agent skills hosted in the JFrog AI Catalog.
- **AI Catalog skill.** New `jfrog-ai-catalog` skill to discover, install, update, and publish agent skills hosted in the JFrog AI Catalog.
---

## Features
Expand All @@ -16,7 +16,7 @@ The JFrog plugin provides the following capabilities, grouped by component:
| --- | --- | --- |
| **MCP** | JFrog MCP server | Remote JFrog MCP server auto-attached to every session via `mcp.json` at `https://${JFROG_PLATFORM_URL}/mcp` (OAuth, no API keys). |
| **Skill** | JFrog Platform | Interact with Artifactory repositories, builds, permissions, users, access tokens, projects, release bundles, and platform administration via the JFrog CLI and REST/GraphQL APIs. Also covers security audits, CVE lookups, and Advanced Security exposure queries. |
| **Skill** | Package safety & download | Check whether npm, Maven, PyPI, Go, and other packages are safe, curated, or allowed, then download them through Artifactory remote caches or curation-aware package managers. |
| **Skill** | Package curation | Check whether npm, Maven, PyPI, Go, and other packages are safe, curated, or allowed, then download them through Artifactory remote caches or curation-aware package managers. |
| **Hook + Skill** | Agent Package Resolution (Preview) | Automatically route packages installed by the AI agent through your organization's JFrog Artifactory, keeping agent-driven installs inside your Curation, Xray, and governance perimeter. |
| **Skill** | Agent Guard | Manage MCPs through the JFrog Agent Guard. Through the Agent Guard you can discover, install, configure, update, and remove MCP servers from the JFrog AI Catalog approved for your project, and authenticate to remote HTTP MCPs via OAuth, API key, or bearer token. |

Expand All @@ -29,7 +29,7 @@ Before installing, make sure you have:
- **JFrog host URL and access token** — Your JFrog platform URL and a valid access token.
- **Cursor** — Installed with AI features enabled.
- **Node.js** (≥ 18) — with `npx` on your `PATH`.
- **Skill runtime requirements** — `jf` CLI, `jq`, and `curl` on `PATH`, plus a configured JFrog instance. For the minimum versions, see the upstream skills [`Requirements`](https://github.com/jfrog/jfrog-skills/blob/v0.11.0/README.md#requirements). Configure the CLI with `jf config add` — see [Authentication](#authentication).
- **Skill runtime requirements** — `jf` CLI, `jq`, and `curl` on `PATH`, plus a configured JFrog instance. For the minimum versions, see the upstream skills [`Requirements`](https://github.com/jfrog/jfrog-skills/blob/v0.31.1/README.md#requirements). Configure the CLI with `jf config add` — see [Authentication](#authentication).
- **JFrog Platform access** (optional) — If you want to use the Agent Guard feature, your JFrog subscription needs to include the AI Catalog entitlement. Contact your JFrog account team if you're unsure whether it's enabled.
- **JFrog CLI ≥ 2.105.0** (optional) — If you want the Agent Guard to auto-resolve credentials/server ID from the JFrog CLI instead of `JFROG_PLATFORM_URL`/`JFROG_ACCESS_TOKEN` env vars. Older CLIs don't support the `--format` flag used by `jf config show`/`jf config export` for this.
- **JFrog project** (optional) — If you want to use the Agent Guard feature.
Expand Down Expand Up @@ -92,7 +92,7 @@ Once configured, interact with the JFrog plugin through natural language. Exampl
| "Create a scoped access token for CI." | Creates an access token with the requested scope. |
| "Promote this release bundle to production." | Uses Lifecycle / Distribution APIs to promote the bundle. |

### Package safety & download skill
### Package curation skill

| Ask the agent… | What happens |
| --- | --- |
Expand Down
2 changes: 1 addition & 1 deletion VENDOR.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The skill packages under `plugins/jfrog/skills/` are vendored from **[jfrog/jfro
| **Repository** | https://github.com/jfrog/jfrog-skills |
| **Pinned release** | see `pin` in [`.github/scripts/sync-skills-vendor.json`](.github/scripts/sync-skills-vendor.json) |

Included directories: `jfrog/`, `jfrog-ai-catalog-skills/`, `jfrog-package-safety-and-download/`, `jfrog-reference-architecture/`, `jfrog-setup-package-managers/` (as of the pinned release).
Included directories: `jfrog/`, `jfrog-ai-catalog/`, `jfrog-package-curation/`, `jfrog-reference-architecture/`, `jfrog-setup-package-managers/` (as of the pinned release).

## Refreshing

Expand Down
2 changes: 1 addition & 1 deletion plugins/jfrog/.cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "jfrog",
"displayName": "JFrog Platform",
"version": "0.5.18",
"version": "0.5.19",
"description": "JFrog Platform integration with MCP, security skills, Agent Package Resolution, supply-chain best practices, and JFrog Agent Guard governance for adding, removing, and listing MCP servers.",
"author": {
"name": "JFrog",
Expand Down
8 changes: 4 additions & 4 deletions plugins/jfrog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ CLI authentication options: run `jf login` for browser-based setup, or set the `
| Skill | Triggers when you mention... |
|-------|------------------------------|
| **jfrog** | any JFrog product, artifactory, xray, security, access token, curation, distribution, release bundle, apptrust, runtime, mission control, worker, jf command, or best practice |
| **jfrog-ai-catalog-skills** | discovering/installing/updating/publishing agent skills, JFrog AI Catalog, `jf skills`, Agent Guard |
| **jfrog-package-safety-and-download** | package safety, curation, allowed/blocked packages, downloading packages via JFrog |
| **jfrog-ai-catalog** | discovering/installing/updating/publishing agent skills, JFrog AI Catalog, `jf skills`, Agent Guard |
| **jfrog-package-curation** | package safety, curation, allowed/blocked packages, downloading packages via JFrog |

The **jfrog** skill (`skills/jfrog/`) provides platform-wide coverage via MCP tools, JFrog CLI commands, and `jf api` REST/GraphQL. It includes 24 reference files under `references/` and 3 automation scripts under `scripts/` covering Artifactory, Security/Xray, Access, Distribution, Curation, AppTrust, Mission Control, Workers, and architectural patterns.

The **jfrog-ai-catalog-skills** skill (`skills/jfrog-ai-catalog-skills/`) discovers, installs, manages, and publishes agent skills hosted in the JFrog AI Catalog via `jf skills` and Agent Guard.
The **jfrog-ai-catalog** skill (`skills/jfrog-ai-catalog/`) discovers, installs, manages, and publishes agent skills hosted in the JFrog AI Catalog via `jf skills` and Agent Guard.

The **jfrog-package-safety-and-download** skill (`skills/jfrog-package-safety-and-download/`) handles package safety checks — querying the JFrog Public Catalog, interpreting security signals, checking curation policies, and downloading packages through Artifactory remote caches.
The **jfrog-package-curation** skill (`skills/jfrog-package-curation/`) handles package safety checks — querying the JFrog Public Catalog, interpreting security signals, checking curation policies, and downloading packages through Artifactory remote caches.

## Agent Package Resolution (Preview)

Expand Down
21 changes: 15 additions & 6 deletions plugins/jfrog/skills/jfrog-init/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ metadata:

# /jfrog-init — verify and guide JFrog plugin readiness

**First output must be a tool call, not text.** No "I'll start..." preamble.

Walks a fixed, ordered checklist and stops at the first red result, guiding
the user through the matching fix before re-checking. Every detector in
`scripts/` is idempotent, read-only, JSON-emitting, and implemented in
Expand Down Expand Up @@ -50,8 +52,8 @@ substitutes it automatically, identically, in both this text and the
`allowed-tools` Bash rules above — write it literally rather than
resolving it yourself, so the two stay byte-for-byte consistent
regardless of install depth (see `references/script-invocation.md`). On
a harness that doesn't perform this substitution (e.g. Cursor, which
doesn't consult `allowed-tools` for approval at all — every command
a harness that doesn't perform this substitution (e.g. Cursor or Codex,
neither of which consults `allowed-tools` for approval — every command
below still raises its own prompt there), replace it with the real
absolute path of this file's directory yourself, same as before.

Expand Down Expand Up @@ -140,10 +142,12 @@ user needs to know or act on:
- **Do not** announce that you're about to run the checklist, or name
which check comes first — not even generically ("I'll run the setup
checklist silently, starting with the JFrog CLI check" is itself a
violation: it names a step while claiming to be silent). Silently
means no preamble message at all. Say nothing until you have
something the user needs to act on (an ask, a red result) or the
final summary.
violation: it names a step while claiming to be silent). The same
applies to reading reference docs: "I'll start by reading the flow
docs" is a preamble. Silently means no preamble message at all — not
before running commands, not before reading files. Say nothing until
you have something the user needs to act on (an ask, a red result)
or the final summary.

Instead:

Expand Down Expand Up @@ -251,6 +255,11 @@ Read the output yourself, no JSON to parse:
- `node --version` ≥ 18 **and** `npx --version` succeeds → **green** →
proceed to Step 2.

**Never paste the raw shell output.** Translate to plain English —
"npx is not installed" not `` `command not found` ``, "Node.js v16 is
too old" not the version string verbatim. The raw output is for your
reasoning, not for the user.

On red, **stop and read `references/node-install-prompt.md` in full
before responding to the user.** It has the exact `AskUserQuestion`
payload, the forbidden phrases, and the install commands — required
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ flowchart TD
S4 -->|unreachable/timeout/other| STOP2["STOP: show raw error (network/URL hint included)"]:::stopBox
S4 -->|yes| S5

S5["5. Plugin mcp.json has mcpServers.jfrog? (auto-substitutes a JFROG_PLATFORM_URL/JFROG_URL placeholder inline, if present)"]:::stepBox
S5["5. Plugin mcp.json has a valid jfrog entry? (auto-substitutes a JFROG_PLATFORM_URL/JFROG_URL placeholder inline, if present)"]:::stepBox
S5 -->|substitution needed, server-id ambiguous| ASKSRV5["AskUserQuestion: pick server-id"]:::fixBox
ASKSRV5 --> S5
S5 -->|missing/invalid/no entry, incl. substitution failure| F5["Note: reinstall or update the JFrog plugin, or resolve jf config (non-blocking)"]:::fixBox
Expand Down
19 changes: 19 additions & 0 deletions plugins/jfrog/skills/jfrog-init/references/how-to-ask-user.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# How to ask the user questions

When the skill needs a Yes/No answer, a selection, or any other input
from the user, use the **native interactive prompt tool** built into
your harness so the user can click or select rather than type:

| Harness | Preferred tool |
|-------------|------------------------|
| Claude Code | `AskUserQuestion` |
| Codex | `request_user_input` |

Each reference file specifies the question text and option labels; use
your harness's native tool to present them. Native prompt tools already
offer a free-text "Other" fallback for values not in the list — don't
add a duplicate "Other" option yourself.

**Fallback**: if no native prompt tool is available, or the tool
returns without a selection, surface the question as plain text in
your reply — never silently stop without presenting it.
37 changes: 25 additions & 12 deletions plugins/jfrog/skills/jfrog-init/references/mcp-plugin-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,31 @@ resolved at runtime from an env var:
{"mcpServers": {"jfrog": {"url": "https://${JFROG_PLATFORM_URL}/mcp"}}}
```

Codex's plugin ships the same idea in a different shape — no
`mcpServers` wrapper, and angle brackets instead of `${...}`:

```json
{"jfrog": {"url": "https://<JFROG_PLATFORM_URL>/mcp"}}
```

Because we have that URL sitting in `jf config`, and because leaving
the placeholder in place means the MCP silently fails to load in the
IDE / agent, Step 5 auto-substitutes it. If the detector finds the
placeholder pattern anywhere in the file, it calls
`jfrog-substitute-mcp-placeholders.mjs`, which:

1. Parses the file as JSON and looks **only** at
`mcpServers.jfrog.url` — never a file-wide text replace, so an
unrelated MCP server entry or JSON value that happens to contain the
same placeholder text is never touched.
1. Parses the file as JSON and looks **only** at the `jfrog` entry's
`url` (nested under `mcpServers` on every harness but Codex, which
has no wrapper) — never a file-wide text replace, so an unrelated
MCP server entry or JSON value that happens to contain the same
placeholder text is never touched.
2. Reads the JPD URL from `jf config` (default server, or the one
passed as arg 2), normalizes it to the JPD root, and substitutes it
into that one `url` string.
3. Handles both the `https://${...}` form (where our own scheme would
double up) and the bare `${...}` form.
3. Replaces in two passes — first a placeholder preceded by a scheme
(`https://${...}`, where our own scheme would otherwise double up),
then a bare one. Each pass recognizes all three syntaxes: `${VAR}`,
`$VAR`, and Codex's `<VAR>`.
4. Re-serializes the whole file (`JSON.stringify(parsed, null, 2)`) and
writes atomically (temp file + rename) so a partial write cannot
corrupt the file. Original formatting/whitespace elsewhere in the
Expand All @@ -44,16 +54,19 @@ This is the ONLY place `/jfrog-init` writes to the plugin-owned
| Cursor | `~/.cursor/plugins/cache/cursor-public/jfrog/<sha>/mcp.json` (glob → newest) |
| VS Code | `~/.vscode/agent-plugins/github.com/jfrog/vscode-plugin/plugin/.mcp.json` |
| Claude Code | `~/.claude/plugins/cache/<marketplace>/jfrog/<version>/.mcp.json` (glob) |
| Codex | `$CODEX_HOME/plugins/cache/codex-plugin/jfrog/<version>/.mcp.json` (glob → newest; `$CODEX_HOME` defaults to `~/.codex`) |

Harness detection: `CLAUDECODE` / `CURSOR_TRACE_ID` / `VSCODE_PID` /
`TERM_PROGRAM`. Override with `JFROG_INIT_HARNESS=claude|cursor|vscode`
or a specific file via `JFROG_INIT_MCP_CONFIG=/abs/path`.
Harness detection (in priority order): `CODEX_SANDBOX` / `CLAUDECODE` /
`CURSOR_TRACE_ID` / `VSCODE_PID` / `TERM_PROGRAM`. Override with
`JFROG_INIT_HARNESS=claude|cursor|vscode|codex` or a specific file via
`JFROG_INIT_MCP_CONFIG=/abs/path`.

**What the detector verifies** (three things):

1. Plugin file exists and is non-empty at its harness-specific path.
2. Parses as valid JSON.
3. Contains an `mcpServers.jfrog` entry with a non-empty `url`.
3. Contains a `jfrog` entry (nested under `mcpServers` on every harness
but Codex, which has no wrapper) with a non-empty `url`.

It does NOT enforce any other `type`/`url` shape (each plugin owns its
own schema) and it does NOT probe the endpoint — a mis-configured MCP
Expand All @@ -67,12 +80,12 @@ reachable.
- **Exit 1 (red)** or **Exit 3 (error)** → **non-blocking** — proceed
to Step 6 as if green, but remember the cause for the Final Summary.
Steps 6 and 7 call the JPD's REST APIs directly with `jf config`
credentials, never through `mcpServers.jfrog`, so a broken or
credentials, never through the JFrog MCP, so a broken or
missing plugin `mcp.json` doesn't affect whether those checks are
accurate — there's nothing to gain by stopping the walk over it.
Tell the two red causes apart from the detector's `detail` for the
Final Summary note:
- Plugin file missing / empty / lacks `mcpServers.jfrog`. Fix:
- Plugin file missing / empty / lacks a valid `jfrog` entry. Fix:
**reinstall or update the JFrog plugin.** If the user asks why or
how to fix it, run:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
# Step 1 — the Node.js install prompt

**Required behavior for Step 1's red branch, not optional background.**
When `node --version` is missing, unparseable, or its major version is
`< 18`, call `AskUserQuestion` with this exact payload shape (fill in
`<reason>` with either `isn't installed` or the specific `` `<version>`
is too old (need ≥ 18) ``, matching whichever is actually true):
When `node --version` is missing/unparseable/`< 18`, or when `npx
--version` fails (node present but npx absent — same red branch), ask
the user (see `how-to-ask-user.md` for native tool and fallback rules):

```json
{
"questions": [
{
"question": "Node.js <reason>. Install it now?",
"header": "Install Node",
"multiSelect": false,
"options": [
{"label": "Yes", "description": "Install Node.js now. Adds a line to your shell startup file so future terminals can find it."},
{"label": "No", "description": "Cancel /jfrog-init."}
]
}
]
}
```
- **Question**: "Node.js `<note>`. Install it now?"
— where `<note>` is a brief, accurate description of what the tool
output actually showed (e.g. "isn't installed", "`v16.4.0` is too old
(need ≥ 18)", "is installed but npx is missing").
- **Yes**: "Install Node.js now. Adds a line to your shell startup file so future terminals can find it."
- **No**: "Cancel /jfrog-init."

**Do not** mention any install method (nvm, winget), a version manager
name, or any URL — not in the question, not in an option description.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ lookups), `node
commands, not the function calls a pre-approved script makes once
running, so the mutation executes with no prompt whenever Step 5 finds
a placeholder. This is intentional, not an oversight: the write is
narrowly scoped to `mcpServers.jfrog.url`, atomic (temp file + rename),
narrowly scoped to the jfrog entry's `url` field, atomic (temp file + rename),
and idempotent — see `mcp-plugin-config.md` for why that scope was
judged safe to run unattended, unlike the two mutations below.
- `jfrog-state-file.mjs`'s **`set`** mode (writes `~/.jfrog/setup.json`
Expand Down
33 changes: 11 additions & 22 deletions plugins/jfrog/skills/jfrog-init/references/server-picker.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,20 @@ triggered this — is reasoning for you to follow silently, never to
narrate.** The user never sees why they're being asked, only the
`AskUserQuestion` payload itself.

Call `AskUserQuestion` with the **first two** entries of `candidates`
(in the order the detector returned them — never reordered, never
chosen by matching a hostname, git identity, or any other signal) as
the two options, and rely on the tool's built-in "Other" for typing a
different server-id:
Ask the user (see `how-to-ask-user.md` for native tool and fallback
rules) with the **first two** entries of `candidates` (in the order
the detector returned them — never reordered, never chosen by matching
a hostname, git identity, or any other signal) as the two options, and
include a free-text escape hatch for typing a different server-id:

```json
{
"questions": [
{
"question": "Which JFrog server do you want to use?",
"header": "Server",
"multiSelect": false,
"options": [
{"label": "<candidates[0]>", "description": "Server ID: <candidates[0]>"},
{"label": "<candidates[1]>", "description": "Server ID: <candidates[1]>"}
]
}
]
}
```
- **Question**: "Which JFrog server do you want to use?"
- **Option 1**: `<candidates[0]>` (description: "Server ID: `<candidates[0]>`")
- **Option 2**: `<candidates[1]>` (description: "Server ID: `<candidates[1]>`")
- **Other**: let the user type a different server-id if needed

**Never surface the full candidate list or a count** to the user in
any case — the picker's two options (plus "Other") are the entire
user-facing surface, same rule as the project picker.
any case — the picker's two options (plus the free-text escape hatch)
are the entire user-facing surface, same rule as the project picker.

On picking option 1 or 2, or typing a value via **Other** → re-invoke
**the same detector that emitted the ask** (never a different one)
Expand Down
Loading
Loading