diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml new file mode 100644 index 0000000..be870fa --- /dev/null +++ b/.github/workflows/gitleaks.yml @@ -0,0 +1,36 @@ +name: Secret Scan (gitleaks) + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: gitleaks-${{ github.ref }} + cancel-in-progress: true + +jobs: + gitleaks: + name: gitleaks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - name: Install gitleaks + env: + GITLEAKS_VERSION: "8.21.2" + run: | + curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ + | tar -xz -C "$RUNNER_TEMP" gitleaks + sudo install "$RUNNER_TEMP/gitleaks" /usr/local/bin/gitleaks + gitleaks version + # Uses the open-source gitleaks binary directly (no org license required, + # unlike gitleaks-action). Scans the full git history for committed secrets. + - name: Scan git history + run: gitleaks detect --source . --config .gitleaks.toml --redact --verbose --exit-code 1 diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..268ef1f --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,42 @@ +# WARNING: this workflow uses `pull_request_target`, which runs in the context +# of the BASE repo with access to secrets, even when triggered by a fork PR. +# It currently only reads the PR title (a sanitized string) and does not check +# out the PR head, so it is safe as written. +# +# Do NOT add `actions/checkout` here without pinning `ref` to the BASE sha, +# and do NOT execute any code from the PR head — that would expose repository +# secrets to fork authors. If you need to run code from the PR, use a +# `pull_request` trigger in a separate workflow file instead. + +name: PR Title Lint + +on: + pull_request_target: + types: [opened, edited, synchronize] + +permissions: + pull-requests: read + +jobs: + lint: + name: Conventional Commit + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + chore + docs + ci + refactor + perf + test + revert + build + requireScope: false + subjectPattern: ^.+$ + subjectPatternError: "PR title must not be empty after the type prefix." diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..4894a48 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,7 @@ +# gitleaks config for elnora-plugins. +# Extend the built-in ruleset. This repository ships client-side plugin +# configuration and Agent Skills only — no source secrets and no vendored +# files with token-shaped example strings — so there is nothing to allow-list. +# Add a [[rules.allowlist]] path here if that ever changes. +[extend] +useDefault = true diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6d3fb11 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,84 @@ +# AGENTS.md + +Universal guide for any coding agent working with `elnora-plugins`. Read natively by Codex, Cursor, Aider, Continue, Amp, Jules, and Roo. Claude Code reads `CLAUDE.md` instead — but note this repo's `CLAUDE.md` is the marketplace-maintainer guide, not a copy of this file (see [Claude Code](#claude-code) below). + +## What this is + +`elnora-plugins` is the Elnora AI plugins marketplace: one Claude Code plugin (`elnora`) bundling **9 Agent Skills** plus an **MCP server declaration** for the Elnora AI Platform at `https://mcp.elnora.ai/mcp`. It ships client-side configuration only — no CLI in this repo, no server code, no credentials. Agents drive the platform by invoking the Elnora MCP tools; the skills tell you which tool serves which intent. + +Every skill routes to MCP tools named `elnora_*` (e.g. `elnora_health_check`, `elnora_projects_list`, `elnora_tasks_create`). Under a namespaced MCP client the same tools surface as `elnora__elnora_*` (or `mcp__elnora__elnora_*` in Claude Code). + +## Setup + +Every platform needs two things: (1) the MCP server connection and (2) the skill files copied into your tool's skills directory. + +MCP config — use wherever your platform expects MCP server definitions: + +```json +{ "mcpServers": { "elnora": { "type": "http", "url": "https://mcp.elnora.ai/mcp" } } } +``` + +Skills: + +```sh +cp -r elnora/skills/* / # e.g. .cursor/skills, .codex/skills, .vscode/skills, .gemini/skills +``` + +Authentication happens on the first MCP call — OAuth 2.1 (browser popup, recommended) or an API key generated at `platform.elnora.ai`. For a guided, gated walkthrough (verify → auth → smoke test), point your agent at [`INSTALL_FOR_AGENTS.md`](INSTALL_FOR_AGENTS.md). + +## Dispatch — which skill for what + +| User intent | Skill | Representative MCP tools | +|---|---|---| +| Generate / iterate a protocol; manage tasks + messages | `elnora-tasks` | `elnora_tasks_create`, `elnora_tasks_send`, `elnora_protocols_generate` | +| Projects — list, create, members | `elnora-projects` | `elnora_projects_list` | +| Files — list, read, upload, version, fork/promote, working copies | `elnora-files` | `elnora_files_list`, `elnora_files_content`, `elnora_files_upload`, `elnora_files_version` | +| Folders / knowledge-base tree | `elnora-folders` | `elnora_folders_list`, `elnora_folders_create`, `elnora_folders_move` | +| Search tasks / files / everything by keyword | `elnora-search` | `elnora_files_search` | +| Organizations, members, invitations, billing, shared library | `elnora-orgs` | `elnora_orgs_get`, `elnora_orgs_invite`, `elnora_library_files` | +| Auth, API keys, health, account, audit log, feature flags, agreements | `elnora-admin` | `elnora_health_check`, `elnora_account_get`, `elnora_audit_list`, `elnora_flags_list` | +| Scientific tools + literature (PubMed / ArXiv / web) via the cloud agent | `elnora-agent` | `elnora_ask_agent` | +| General "how do I use Elnora" / routing | `elnora-platform` | routes to the domain skill above | + +A read-only `elnora_health_check` proves the MCP connection; `elnora_account_get` or `elnora_projects_list` proves auth works. + +## Pitfalls + +- **MCP-only.** This repo references no CLI commands inside skills — every skill maps to an `elnora_*` MCP tool. Keep it that way when editing skills. +- **Skill dir must match `name`.** A skill's directory name must equal the `name:` in its `SKILL.md` frontmatter — CI (`Validate Skills`) fails otherwise. +- **Two "elnora" servers.** If you register the API-key MCP manually (`claude mcp add elnora …`) AND enable the plugin, you get two servers both named `elnora`. Use one, not both. +- **OAuth is per-client.** Tokens are stored and refreshed by your MCP client, never by files in this repo. There is nothing to persist here. + +## Safety + +Client-side config + skills only; no secrets in-repo. `.env` and `*.local` files are gitignored, and secret scanning (`gitleaks`) plus CodeQL run on every PR. Agent Skills execute in your tool's context — only install marketplaces you trust and review skill files before running them in a privileged environment. Report vulnerabilities privately per [`.github/SECURITY.md`](.github/SECURITY.md) (security@elnora.ai) — do not open a public issue. + +## Claude Code + +The plugin provides a native slash command, the 9 skills, and the MCP declaration. Install as **two separate slash commands** (paste the first, wait for it to finish, then paste the second): + +``` +/plugin marketplace add https://github.com/Elnora-AI/elnora-plugins.git +``` + +``` +/plugin install elnora@elnora-plugins +``` + +Surface: `/elnora:protocol ` generates a bioprotocol; the 9 skills auto-load and route by intent. Definitions live in [`elnora/commands/`](elnora/commands/) and [`elnora/skills/`](elnora/skills/). + +This repo already ships a `CLAUDE.md`, but it is the **marketplace-maintainer** guide (structure, skill spec, how to add a plugin) — a different audience from this file. Do **not** `ln -s AGENTS.md CLAUDE.md` here; the two are intentionally distinct. + +## Per-harness install + +- **Codex CLI** — `AGENTS.md` is auto-loaded at repo root. `codex mcp add elnora -- https://mcp.elnora.ai/mcp`, then `cp -r elnora/skills/* .codex/skills/`. +- **Cursor** — reads `AGENTS.md` natively. MCP in `.cursor/mcp.json`; `cp -r elnora/skills/* .cursor/skills/`. +- **VS Code Copilot** — MCP in `.vscode/mcp.json`; `cp -r elnora/skills/* .vscode/skills/`. +- **Gemini CLI** — MCP in your Gemini settings; `cp -r elnora/skills/* .gemini/skills/`. +- **Continue / Amp / Jules / Roo** — read `AGENTS.md` at repo root automatically; add the MCP config per that tool's convention. + +## Contributing to this repo + +Skill spec (agentskills.io): `name` is lowercase letters/digits/hyphens, ≤64 chars; `description` ≤1024 chars; body under 500 lines; the directory name matches the frontmatter `name`. Register new plugins in `.claude-plugin/marketplace.json`. + +CI on every PR: `Validate Skills` (manifest + frontmatter checks), CodeQL (`actions`), and `gitleaks` secret scan. PR titles must be Conventional Commits — `feat`, `fix`, `chore`, `docs`, `ci`, `refactor`, `perf`, `test`, `revert`, `build`. diff --git a/INSTALL_FOR_AGENTS.md b/INSTALL_FOR_AGENTS.md new file mode 100644 index 0000000..bba1e8c --- /dev/null +++ b/INSTALL_FOR_AGENTS.md @@ -0,0 +1,225 @@ +# INSTALL_FOR_AGENTS.md — `elnora-plugins` setup + +> **Step 0 — Identity gate.** If you are NOT an autonomous coding agent (Claude +> Code, Codex CLI, Cursor, Aider, Continue, Amp, Jules, Roo, Windsurf, etc.), +> stop reading this file and open `README.md` instead. This file walks an agent +> through connecting the Elnora MCP server, authenticating (OAuth or an API key +> you help the user create in a browser), copying the skill files, and running +> a live smoke test against the Elnora Platform. Humans should follow the +> README's Installation section. + +You are the agent handing off after the user decided to install the Elnora +plugin / MCP server. Your job: confirm the MCP connection is registered, get +the user authenticated, make sure the 9 skills are where your harness reads +them, run one read-only tool call to prove the stack works end to end, and hand +back a working environment. + +Be transparent: announce each step before you run it, show the output, and +explain what you found. The user may not know what an MCP server or an Agent +Skill is — keep the language plain and ask one question at a time. Never paste, +echo, or commit an API key. + +**Universal:** the MCP endpoint (`https://mcp.elnora.ai/mcp`) and the tool names +(`elnora_*`) are identical under every harness. The only harness-specific parts +are *where* the MCP server is registered and *where* skills are copied — the +per-harness table in Step 3 covers that. + +## Browser steps — offer to drive, don't assume + +Two steps below happen in a web browser: completing OAuth (Step 2, option A) +and creating an API key at `platform.elnora.ai` (Step 2, option B). How you +handle them depends on what your harness can do: + +- **If you have the Chrome DevTools MCP server available** (tools under + `mcp__chrome-devtools__*`, e.g. Claude Code with it enabled): *offer* to open + and drive the page for the user — "I can open `platform.elnora.ai` in your + Chrome and walk the key-creation with you, or you can do it yourself and paste + the result. Which do you prefer?" Only drive the browser if the user says yes. + Never submit a login form or read back a secret without the user watching. +- **If you do not have a browser-automation tool**, give the user the exact URL + and click-path and wait for them to paste the result. Do not try to fetch an + authenticated page yourself. + +Either way the user stays in control of their own credentials. + +## Step 1 — Verify the MCP connection is registered + +The plugin declares the Elnora MCP server; a manual `mcp add` registers it +directly. Confirm your harness sees a server named `elnora` pointed at +`https://mcp.elnora.ai/mcp` before going further. + +- **Claude Code (plugin path).** Confirm the plugin loaded: + + ```sh + ls .claude/plugins 2>/dev/null || ls ~/.claude/plugins 2>/dev/null + ``` + + You should see `elnora` (or `elnora-plugins`) listed. If not, the + `/plugin install elnora@elnora-plugins` didn't complete — ask the user to + rerun it. You can also list MCP servers with `claude mcp list` and confirm an + `elnora` entry appears. + +- **Claude Code (API-key MCP path, no plugin).** Register it: + + ```sh + claude mcp add elnora --transport http --scope user \ + https://mcp.elnora.ai/mcp \ + --header "X-API-Key: " + ``` + + If you register this AND enable the plugin, you'll have two servers both named + `elnora` — keep one, not both. + +- **Other harnesses (Codex / Cursor / VS Code Copilot / Gemini).** Confirm the + MCP config block exists in the right file (see Step 3). There is no CLI to + `--version` here — the check is "is the `elnora` server in my MCP config and + does a tool call reach it," which Step 4 proves. + +Gate: your harness lists an `elnora` MCP server. If it doesn't, stop and fix the +registration before spending an auth round-trip. + +## Step 2 — Authenticate + +The Elnora MCP server accepts two auth methods. Ask the user which they want: + +> Elnora authenticates two ways. (A) **OAuth** — recommended; a browser popup +> opens on the first call, you click approve, and your client stores and +> refreshes the token for you. Nothing to paste. (B) **API key** — better for +> CI or non-interactive setups; you create a key once at platform.elnora.ai and +> it's passed as a header. Which do you want? + +### Option A — OAuth 2.1 (recommended) + +No key handling. The first tool call (Step 4) triggers the browser popup +automatically; the user approves once and the token is cached by their MCP +client. There is nothing to persist in this repo. + +- If you have the Chrome DevTools MCP server, you may *offer* to bring the + approval page forward and confirm the redirect completes — but the user + approves; you don't. If you don't have it, just tell the user "a browser + window will open on the first Elnora call — approve it there," and continue. + +Gate: none yet. OAuth is validated by the smoke test in Step 4 succeeding +without a `401`. + +### Option B — API key + +1. **Create the key.** Tell the user, verbatim: + + > Open https://platform.elnora.ai in your browser, go to account settings → + > API keys, click **Create key**, name it something like "mcp-", + > and copy the value. It starts with `elnora_live_`. + + Follow the browser-step rules above: if you have Chrome DevTools MCP, offer + to open the page and walk the click-path with the user watching; otherwise + wait for them to paste the key. + +2. **Store it in the client's secret store or an env var — never in this repo.** + The key goes in your MCP client's header config (`X-API-Key` or + `Authorization: Bearer `), your OS keychain, or an environment variable. + Do NOT write it into any file under the repository, a skill file, or a commit + — `.env` and `*.local` are gitignored precisely so a stray key can't be + committed, but the safest place is the client secret store. + + For Claude Code's API-key MCP path, the key is passed on the + `claude mcp add` command from Step 1 (`--header "X-API-Key: "`), which + stores it in the user-scope MCP config, not in this repo. + +Gate: the key is set in the client's auth config (verified by Step 4), starts +with `elnora_live_`, and appears in NO file tracked by git. If the user pasted +it into a chat or a repo file, tell them to rotate it. + +## Step 3 — Register MCP + copy skills (non-Claude harnesses) + +Claude Code users who installed the plugin can skip this — the plugin ships both +the MCP declaration and the skills. Everyone else does both halves: + +| Harness | MCP config file | Skills directory | +|---|---|---| +| Cursor | `.cursor/mcp.json` | `.cursor/skills/` | +| OpenAI Codex | `codex mcp add elnora -- https://mcp.elnora.ai/mcp` | `.codex/skills/` | +| VS Code Copilot | `.vscode/mcp.json` | `.vscode/skills/` | +| Gemini CLI | Gemini settings | `.gemini/skills/` | +| Generic MCP client | point at `https://mcp.elnora.ai/mcp` | your tool's skills dir | + +MCP block for the JSON-config harnesses: + +```json +{ "mcpServers": { "elnora": { "type": "http", "url": "https://mcp.elnora.ai/mcp" } } } +``` + +Copy the skills: + +```sh +cp -r elnora/skills/* / +``` + +Gate: all 9 skill directories (`elnora-platform`, `elnora-orgs`, +`elnora-projects`, `elnora-tasks`, `elnora-files`, `elnora-folders`, +`elnora-search`, `elnora-admin`, `elnora-agent`) landed in the target dir, each +containing a `SKILL.md`. + +## Step 4 — Smoke test + +Two tool calls, in order. The first needs no auth; the second proves auth works. + +1. **Liveness.** Call `elnora_health_check` (via the `elnora-admin` / + `elnora-platform` skill, or the raw MCP tool). + + Gate: it returns a healthy status. A transport error here means the MCP + server isn't registered — go back to Step 1. This call does not prove auth. + +2. **Authenticated read.** Call `elnora_account_get` (whoami) or + `elnora_projects_list`. + + Gates: + - **OAuth (Option A):** the first call opens the approval popup. After the + user approves, the call returns account/project data. If it keeps failing + with `401`, the OAuth flow didn't complete — have the user re-approve. + - **API key (Option B):** the call returns data immediately. A `401` means + the key is wrong or not reaching the header — re-check Step 2. Do NOT retry + with a key you guessed or reconstructed; ask the user to paste it again or + regenerate it. + - An empty projects list is a valid result (a new account may have none) — + distinguish "empty, exit ok" from "error." + +If you have the Chrome DevTools MCP server and OAuth stalls, you may offer to +bring the approval tab forward so the user can complete it — but they click +approve, not you. + +## Step 5 — Handoff summary + +Tell the user, in this order: + +1. **What's connected** — the `elnora` MCP server at + `https://mcp.elnora.ai/mcp`, and (non-Claude harnesses) where the 9 skills + were copied. +2. **How they're authenticated** — OAuth (token cached by their client) or API + key (stored in the client's secret store / header config). If API key, remind + them it's never stored in this repo and to rotate it if it ever leaks. +3. **How to use it** — one entry point that matches the harness: + - **Claude Code with the plugin:** `/elnora:protocol `, or just + ask in natural language ("use Elnora to list my projects"). + - **Any other harness:** ask in natural language; the skills route the intent + to the right `elnora_*` MCP tool. Dispatch table in [`AGENTS.md`](AGENTS.md). +4. **Where auth lives** — their MCP client's config / secret store, never this + repo. + +## Completion checklist + +Verify ALL of these before declaring done. If any fails, finish it first. + +1. The harness lists an `elnora` MCP server pointed at + `https://mcp.elnora.ai/mcp`. +2. `elnora_health_check` returns a healthy status. +3. `elnora_account_get` (or `elnora_projects_list`) returns data — no `401` + (empty project list is OK; an auth error is not). +4. **Non-Claude harnesses:** all 9 skill directories are present in the target + skills dir, each with a `SKILL.md`. +5. If the user chose the API key: it is set in the client's auth config, and it + appears in NO git-tracked file. You did not echo it into chat or a repo file. +6. You wrote nothing to the repository that the user didn't ask for — no keys, + no local config committed. + +When all applicable items pass, print `ELNORA_PLUGINS_READY` on its own line so +the user (and any wrapping harness) can grep for it. diff --git a/README.md b/README.md index 7299b60..6a51cf7 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,13 @@ The Elnora MCP server supports two authentication methods: - **OAuth 2.1** (recommended) — A browser popup opens automatically on first connection. No manual configuration required. - **API Key** — Pass a Bearer token in the Authorization header. Generate keys from your Elnora account settings. +## For coding agents + +Running Codex, Cursor, Aider, Continue, Amp, Jules, or Roo? Two files are written for agents rather than humans: + +- **[`AGENTS.md`](AGENTS.md)** — usage conventions: the MCP endpoint, the `elnora_*` tools, and a dispatch table mapping user intent to the right skill. Auto-loaded at repo root by most agent harnesses. +- **[`INSTALL_FOR_AGENTS.md`](INSTALL_FOR_AGENTS.md)** — a gated, step-by-step setup runbook (verify → authenticate → copy skills → smoke test), with browser-assist offers for the OAuth and API-key steps. + ## Available Plugins | Plugin | Description | @@ -151,6 +158,7 @@ The Elnora MCP server supports two authentication methods: - **Website**: [elnora.ai](https://elnora.ai) - **Agent Skills Standard**: [agentskills.io](https://agentskills.io) - **MCP Endpoint**: [mcp.elnora.ai/mcp](https://mcp.elnora.ai/mcp) +- **For agents**: [AGENTS.md](AGENTS.md) · [INSTALL_FOR_AGENTS.md](INSTALL_FOR_AGENTS.md) - **Issues**: [GitHub Issues](https://github.com/Elnora-AI/elnora-plugins/issues) ## License