Skip to content
Closed
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
84 changes: 84 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Validate

on:
pull_request: {}
push:
branches: [main]
workflow_dispatch: {}

permissions:
contents: read

concurrency:
group: validate-${{ github.ref }}
cancel-in-progress: true

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install tools
run: pip install --quiet ruff pytest

Comment on lines +30 to +32
- name: Validate JSON files
run: |
set -euo pipefail
# jq ships on ubuntu-latest. Every tracked .json must parse.
git ls-files '*.json' | while read -r f; do
echo "jq: $f"
jq empty "$f"
done

- name: Plugin manifest versions must match
run: |
set -euo pipefail
claude=$(jq -r .version .claude-plugin/plugin.json)
codex=$(jq -r .version .codex-plugin/plugin.json)
echo "claude=$claude codex=$codex"
if [ "$claude" != "$codex" ]; then
echo "::error::plugin version mismatch: .claude-plugin=$claude vs .codex-plugin=$codex"
exit 1
fi

- name: Validate docs (links + frontmatter)
run: python scripts/validate_docs.py

- name: Executable bit matches shebang under bin/
run: |
set -euo pipefail
fail=0
# git ls-files -s prints: <mode> <sha> <stage>\t<path>
while IFS= read -r line; do
mode=${line%% *}
file=${line#*$'\t'}
[ -z "$file" ] && continue
if head -c2 "$file" | grep -q '#!'; then
if [ "$mode" != "100755" ]; then
echo "::error file=$file::has a shebang but git mode is $mode (expected 100755). Run: git update-index --chmod=+x $file"
fail=1
fi
fi
done < <(git ls-files -s 'skills/*/bin/*')
exit $fail

- name: Byte-compile and lint Python
run: |
set -euo pipefail
mapfile -t pyfiles < <(git ls-files 'skills/*/bin/*.py' 'scripts/*.py' 'tests/*.py')
if [ ${#pyfiles[@]} -gt 0 ]; then
python -m py_compile "${pyfiles[@]}"
ruff check "${pyfiles[@]}"
fi

- name: Run tests
run: pytest -q
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@

.claude/state/
.claude/worktrees/

# Python
__pycache__/
*.pyc
.pytest_cache/
.ruff_cache/
4 changes: 3 additions & 1 deletion .mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"roboflow": {
"type": "http",
"url": "https://mcp.roboflow.com/mcp",
"note": "Official Roboflow MCP server. Set ROBOFLOW_API_KEY in the agent environment so plugin-managed MCP requests can authenticate."
"headers": {
"x-api-key": "${ROBOFLOW_API_KEY}"
}
}
}
}
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ claude --plugin-dir .

### Codex

> _Codex's plugin CLI is fast-moving; the flow below is accurate as of 2026-07. If the commands or keystrokes differ, check [Codex's own plugin documentation](https://developers.openai.com/codex) — it is authoritative._

The Codex CLI currently exposes `codex plugin marketplace add`, `upgrade`, and `remove`. It does not expose a direct `codex plugin install` command or a `codex --plugin-dir` flow, so add this repo as a marketplace source and install the plugin from the plugin browser.

Install from GitHub:
Expand Down Expand Up @@ -150,14 +152,16 @@ That separation keeps the install model simple:
**Grab your Roboflow API key** from the Roboflow settings:
[app.roboflow.com/settings/api](https://app.roboflow.com/settings/api)

The key authenticates the bundled MCP server against `https://mcp.roboflow.com` via the `x-api-key` header.
The bundled [`.mcp.json`](.mcp.json) maps this key into the `x-api-key` header sent to `https://mcp.roboflow.com` the config expands `${ROBOFLOW_API_KEY}` from the environment that launches your agent, so the variable must be set before the agent starts. If it is unset, MCP tool calls reach the server unauthenticated and fail.

Export it in the shell that launches your agent:

```bash
export ROBOFLOW_API_KEY=your_key
```

> Some clients (e.g. Claude Code interactive) can instead sign in to the Roboflow MCP server via OAuth. The `x-api-key` mapping above is the headless / CI path where no interactive sign-in is possible.

For persistence, add the `export` to your shell profile (`~/.zshrc`, `~/.bashrc`) or to a project-local `.env` file loaded by your agent's environment. Per-project isolation is the safer default — keeps separate workspaces and billing accounts from leaking across projects.

</details>
Expand Down
Loading
Loading