Skip to content
Merged
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.6.0",
"version": "0.6.1",
"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.32.0",
"paths": [
"skills"
]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,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. |
| **Hook** | Agent Guard | Cursor 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. |
| **Hook** | Skills governance | When a skill is invoked, the plugin checks it against your JFrog governance policy and blocks disallowed or unscanned skills before they run. Covers the two entry points that carry a skill's identity: skills you run with `/<skill-name>`, and any read of a `SKILL.md` (how a skill's body reaches the model, since Cursor has no dedicated `Skill` tool). Content that reaches the model without a `Read` tool call is outside both — see [Skills governance](#skills-governance). Enforced only when your account is entitled to AI Catalog skills governance. |
Expand Down Expand Up @@ -94,7 +94,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-skills/`, `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.6.0",
"version": "0.6.1",
"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
4 changes: 2 additions & 2 deletions plugins/jfrog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ CLI authentication options: run `jf login` for browser-based setup, or set the `
|-------|------------------------------|
| **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-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-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
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ its stdout as `<UA>`. Parse the `tool=<h>` field from `<UA>` and map it to a

If `tool` is `unknown`, empty, or not in the table — do **not** guess. Ask
the user for the desired install path and use `--path <dir>` instead.
**Exception — Kiro (install only):** if you're self-identified as Kiro (IDE
or `kiro-cli`, per your system prompt — `check-environment.sh` doesn't
detect it), `--harness kiro` is rejected by `jf`, so skip asking and use
`--path` with `.kiro/skills` (project) / `~/.kiro/skills` (global, or
`$KIRO_HOME/skills` if `KIRO_HOME` is set) directly. This exception does not
extend to `jf skills list` — see *List currently installed skills* in
`managing-installed-skills.md`.

Choose exactly one install target (these are mutually exclusive):

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ jf skills list --server-id "<SID>" --harness "<harness>" --check-updates
```

Resolve `<harness>` to the current agent (see `installing-skills.md`).
**Exception — Kiro:** `--harness kiro` errors `unknown agent`, and `list` has
no `--path` flag. List directly from the filesystem instead: skill directory
names under `.kiro/skills` (project) / `~/.kiro/skills` (global, or
`$KIRO_HOME/skills`), each containing a `SKILL.md`. Version/description
aren't available this way — omit those columns. This is a filesystem
inventory only: a directory with a `SKILL.md` cannot confirm the skill was
installed via `jf skills install` — a manually added skill looks identical.
Present it as such rather than implying AI Catalog provenance.
**Never run a bare `jf skills list`** because it errors. Always pass
`--harness <h>` (installed skills) or `--repo <key>` (registry contents).
`--check-updates` is only supported with `--harness` (not with `--repo`). Merge
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