Skip to content

Add corgea-mcp and sighthound-rules skills - #6

Merged
leenk7991 merged 5 commits into
feat/validation-cifrom
feat/mcp-and-rules-skills
Aug 17, 2026
Merged

Add corgea-mcp and sighthound-rules skills#6
leenk7991 merged 5 commits into
feat/validation-cifrom
feat/mcp-and-rules-skills

Conversation

@leenk7991

Copy link
Copy Markdown
Member

Stacked on #5. Review that first; this PR's diff is scoped to the two new skills.

Summary

Two capabilities the repo documented nowhere.

corgea-mcp

Covers the hosted MCP server: connecting, the CORGEA-TOKEN header (not Authorization — the most common setup mistake), client config for Cursor and Claude Desktop, and the ten read tools with guidance on which to reach for.

Sourced from the published docs, not from Corgea/mcp-server. That repo is private, was last pushed 2025-09-30, and describes a local stdio server started with python main.py exposing three tools. The shipped product is a hosted endpoint exposing ten. A skill written from the repo would document a product that no longer exists, from a private source a public skill cannot cite.

Added as its own plugin rather than a second skill under corgea: MCP needs no CLI install and authenticates differently, so a user should be able to install one without the other.

sighthound-rules

The existing sighthound skill mentions custom rules in one line. This is the authoring loop.

The value is the parts a reader loses an hour to:

  • Rules are RON, not YAML, wrapped in (rules: [...]) with explicit Some(...)/None
  • --use-file-rules is required when iterating, or edits are ignored in favour of the rules compiled into the binary via include_dir!
  • Unknown fields are dropped silently — there is no deny_unknown_fields — so the unless: key in rules/javascript/frontend_security.ron is inert
  • There is no rule linter or schema, so a malformed rule surfaces as a rule that never matches rather than a validation error

Added inside the existing sighthound plugin: same tool, same install, adjacent task. It also gives the CI in #5 a real multi-skill plugin to validate.

Test plan

  • node scripts/validate.mjs passes (3 plugins, 4 skills)
  • Every corgea-mcp claim verified against docs/modelcontextprotocol.mdx: all ten tool names, the CORGEA-TOKEN header, page_size cap of 50, 100/min and 1000/hour rate limits, the five reachability values, the response envelope, and both Cursor config paths
  • Every sighthound-rules claim verified against the Sighthound source rather than its guide: unless appears nowhere in src/, no deny_unknown_fields, the five [[test]] targets exist, make ci exists, and the --use-file-rules / --rules-dir usage matches src/main.rs
  • docs.corgea.app/modelcontextprotocol returns 200

Made with Cursor

Comment thread plugins/sighthound/skills/sighthound-rules/SKILL.md
Comment thread plugins/sighthound/skills/sighthound-rules/SKILL.md
Comment thread plugins/sighthound/skills/sighthound-rules/SKILL.md Outdated
Comment thread plugins/sighthound/skills/sighthound-rules/SKILL.md Outdated
Comment thread plugins/sighthound/skills/sighthound-rules/SKILL.md Outdated
@leenk7991
leenk7991 force-pushed the feat/mcp-and-rules-skills branch from 0c65991 to 78b6fd0 Compare August 11, 2026 14:19
@leenk7991
leenk7991 requested a review from juangaitanv August 12, 2026 20:01
@leenk7991
leenk7991 force-pushed the feat/mcp-and-rules-skills branch from 78b6fd0 to 5cf9d45 Compare August 12, 2026 20:13
Comment thread plugins/corgea-mcp/skills/corgea-mcp/SKILL.md
Comment thread plugins/sighthound/skills/sighthound-rules/SKILL.md Outdated
leenk7991 and others added 3 commits August 13, 2026 18:42
Two capabilities the repo documented nowhere.

corgea-mcp covers the hosted MCP server: connecting, the CORGEA-TOKEN
header, and the ten read tools. Sourced from the published docs rather
than Corgea/mcp-server, which is private, has not been touched since
September, and still describes a local stdio server with three tools.
A skill written from it would document a product that no longer exists.

sighthound-rules covers rule authoring, which the sighthound skill
mentions in one line. The value is the parts a reader loses an hour to:
rules are RON and not YAML, --use-file-rules is required or edits are
ignored in favour of the rules compiled into the binary, and unknown
fields are dropped silently, so the unless: key in frontend_security.ron
is inert.

MCP is a separate plugin since it needs no CLI and authenticates
differently. Rules sit inside the sighthound plugin, being the same tool
and the same install.

Co-authored-by: Cursor <cursoragent@cursor.com>
All five verified against the Sighthound source and, where runnable, against
the release binary.

The taint example was a bare rule tuple. Rule files deserialize into `Rules`,
whose list defaults to empty, so that shape parses cleanly and contributes
nothing — a search rule written this way exits 0 with `[]`. It is now wrapped,
and the trap is written up, including that `RULE_WRITING_GUIDE.md` shows a
bare tuple itself.

That example also filed `exec(` and `shell_exec(` under `cwe-89`, copying an
anomaly in `rules/php/taint.ron`. A rule spanning both sink classes has one
`cwe_id` to give them, so half its findings are misclassified. Split into a
SQL rule (`cwe-89`) and a command rule (`cwe-78`), each with matching
sanitizers; running both against a PHP fixture reports the two CWEs correctly.

The field table put severity's values on a row shared with `finding_type` and
`cwe_id`, which take Title Case phrases and lowercase `cwe-<number>`. Each now
has a row, with the `Medium` fallback severity and confidence take when unset.
`mode` is `#[serde(default = "default_search_mode")]`, so it was wrong to list
it as required.

The test loop called whatever `sighthound` is on `PATH`, right after building
the one that has your rules in it.

Co-authored-by: Cursor <cursoragent@cursor.com>
The connect examples pasted the token straight into the file the token section
tells you never to put it in, and the Cursor one dressed it up as a variable:
it set `CORGEA_TOKEN` to a literal and referenced it as `${CORGEA_TOKEN}`,
which is not a syntax Cursor interpolates. Cursor resolves `${env:NAME}` in
`args`, `env`, `url` and `headers`, so the token now stays in the environment
and never reaches the file. Noted that Cursor reads its own environment, so a
GUI launch on macOS may not see a shell export until restarted, and that the
syntax belongs to the client rather than to MCP. Also called out
`.cursor/mcp.json` in the token section, since the project-local copy is the
one committed by accident.

`file_types` and `conditions` were filed under metadata, but both decide what
a rule matches — one picks the files it runs against, the other filters by AST
and context. Split the table in two, so the group to search when a rule fires
in the wrong place is the group that can cause it.

Co-authored-by: Cursor <cursoragent@cursor.com>
@leenk7991
leenk7991 force-pushed the feat/mcp-and-rules-skills branch from 5cf9d45 to cf2499c Compare August 13, 2026 15:46
@leenk7991
leenk7991 requested a review from juangaitanv August 13, 2026 15:49
Comment thread plugins/corgea-mcp/skills/corgea-mcp/SKILL.md Outdated
Comment thread plugins/sighthound/skills/sighthound-rules/SKILL.md Outdated
"Same block as Cursor" was wrong: `${env:NAME}` is Cursor's syntax, and Claude
Desktop interpolates nothing. What resolves the placeholder there is mcp-remote,
which substitutes `${NAME}` in a header from its own `process.env`
(`src/lib/utils.ts`), so the value belongs in the `env` block. Cursor's spelling
does not fail loudly in that position — mcp-remote takes `env:CORGEA_TOKEN` as
the variable name, finds none, and sends the header empty — so the symptom is
written down next to the config that causes it. The token stays out of the
Cursor config, and the Claude Desktop file is a user-profile one, not a
repository one.

Both `--header` arguments also lost the space after the colon, which Cursor and
Claude Desktop on Windows mangle.

`sanitizers` and `propagators` were described as one thing that narrows
reported flows. Sanitizers do that. Propagators do nothing at all here:
`src/models.rs` declares the field and no other file under `src/` reads it,
which is why no rule in the repo sets one. They now have separate rows, and the
inert field is filed with `unless:` under the trap it belongs to.

Co-authored-by: Cursor <cursoragent@cursor.com>
@leenk7991
leenk7991 requested a review from juangaitanv August 17, 2026 07:14
* Point corgea-scan at the skill the CLI carries

The CLI now compiles its own skill into the binary and prints it with
`corgea skill show`, so the depth belongs there, pinned to the version
actually installed. Keeping a second copy here guarantees the two drift.

Cuts the body roughly in half: install, auth, and the commands worth
having loaded immediately stay, and the flag matrix, export formats,
upload matrix and troubleshooting go. The skill defers to `corgea skill
show` for the rest, and to `corgea --help` where that command does not
exist yet.

Widens the frontmatter description rather than narrowing it. The body
shrank, but this is now the entry point for the whole CLI, so the
trigger text has to cover the install gate, dependency inventory and
advisories as well as scanning.

Keeps the name: `npx skills add corgea/skills --skill corgea-scan` is
already published in the docs and the README.

Depends on a CLI release carrying `corgea skill show`; merging earlier
points users at a command their binary does not have.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Point corgea-scan at --help rather than a new CLI command

The pointer targeted `corgea skill show`, which does not exist yet and is no
longer being added: `corgea --help` is already compiled into the binary by
clap, already pinned to the installed version, and costs nothing.

Same shape as before — this file stays the lean entry point and defers the
detail — but the target is a command every released CLI already answers, so
this no longer waits on a release to be useful. Adds the version-skew
guidance: report an outdated CLI rather than upgrading unprompted, and treat
a surprising result as a version difference before a bug, since a changed
default will not error the way a missing flag does.

The long-tail reference now points at docs.corgea.app rather than at a second
copy of it.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
@leenk7991
leenk7991 merged commit 7abc071 into feat/validation-ci Aug 17, 2026
3 checks passed
leenk7991 added a commit that referenced this pull request Aug 17, 2026
* Validate manifests, skills and README on every PR

The repo had no CI. A plugin name that stops matching its directory, or
a skill absent from the README table, breaks installs for users rather
than failing anything at build time, so nothing caught it before merge.

Checks the marketplace and plugin manifests describe the same plugins,
that shared metadata has not diverged (plugin.json wins at runtime, so
divergence ships the wrong version silently), that every SKILL.md has
frontmatter whose name matches its directory, and that the README table
and its relative links match what is on disk.

Also drops the $schema line from marketplace.json. The URL 404s, so it
was an editor hint pointing at nothing.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Tighten the source check, stop failing valid YAML and links

Three holes from review.

`source` only had to start with `./` and end in the plugin name, so
`./vendor/corgea` passed while living outside the tree every other check
walks — a plugin could ship without anything here having looked at it. It
must now equal `./plugins/<name>`.

Quoted frontmatter was compared as raw text, so `name: "corgea-scan"` failed
the directory check on a name that is correct. Quoting is optional in YAML;
one layer of matching quotes is now stripped, with `''` and backslash escapes
undone. A quoted wrong name still fails.

README links were checked whole, so any `#fragment` or `?query` read as part
of the filename and never resolved. Both are addressing within the target,
not part of the path, and are now dropped before the existence check and
before comparing a table row to its skill directory.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Reject frontmatter the reader cannot represent, require the README

The reader keeps anything it does not understand as text and then validates
that text as though it were the value, so `description: [a, b` passed as a
description beginning with '['. It now reads a scalar, quoted or not, and
rejects the rest by name: flow collections, block scalars, anchors, quotes
that are never closed, and unquoted values holding ': ' or ' #', which mean a
nested mapping and a comment to a real parser. Duplicate keys are rejected too
rather than letting the last one win. A rejected key is not then reported a
second time as missing.

A missing README.md skipped the skills table and every relative link, so a
repo whose entry point had been deleted validated clean. Both it and the
marketplace manifest are now required, and their absence says so instead of
surfacing as a JSON parse error.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Scope the skills table, check every row, keep links in the repo

Skill rows were matched across the whole README, so the first other table to
carry links in its left column would be read as a list of skills and fail on
every row of it. Only the table under `## Skills` is read now, and a README
without that section says so once rather than once per skill.

Rows were matched by name and only the first of them was checked, so a second
row could name a real skill while linking at a different one. Every row is
checked against the skill it names now, and a name listed twice is rejected.

Relative links were resolved without regard for where they landed, so `../`
above the checkout passed as long as the path existed on the machine running
the validator — while being broken for anyone reading the README on GitHub.
Links must now stay inside the repository.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add corgea-mcp and sighthound-rules skills (#6)

* Add corgea-mcp and sighthound-rules skills

Two capabilities the repo documented nowhere.

corgea-mcp covers the hosted MCP server: connecting, the CORGEA-TOKEN
header, and the ten read tools. Sourced from the published docs rather
than Corgea/mcp-server, which is private, has not been touched since
September, and still describes a local stdio server with three tools.
A skill written from it would document a product that no longer exists.

sighthound-rules covers rule authoring, which the sighthound skill
mentions in one line. The value is the parts a reader loses an hour to:
rules are RON and not YAML, --use-file-rules is required or edits are
ignored in favour of the rules compiled into the binary, and unknown
fields are dropped silently, so the unless: key in frontend_security.ron
is inert.

MCP is a separate plugin since it needs no CLI and authenticates
differently. Rules sit inside the sighthound plugin, being the same tool
and the same install.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Correct the taint example, field table and test loop

All five verified against the Sighthound source and, where runnable, against
the release binary.

The taint example was a bare rule tuple. Rule files deserialize into `Rules`,
whose list defaults to empty, so that shape parses cleanly and contributes
nothing — a search rule written this way exits 0 with `[]`. It is now wrapped,
and the trap is written up, including that `RULE_WRITING_GUIDE.md` shows a
bare tuple itself.

That example also filed `exec(` and `shell_exec(` under `cwe-89`, copying an
anomaly in `rules/php/taint.ron`. A rule spanning both sink classes has one
`cwe_id` to give them, so half its findings are misclassified. Split into a
SQL rule (`cwe-89`) and a command rule (`cwe-78`), each with matching
sanitizers; running both against a PHP fixture reports the two CWEs correctly.

The field table put severity's values on a row shared with `finding_type` and
`cwe_id`, which take Title Case phrases and lowercase `cwe-<number>`. Each now
has a row, with the `Medium` fallback severity and confidence take when unset.
`mode` is `#[serde(default = "default_search_mode")]`, so it was wrong to list
it as required.

The test loop called whatever `sighthound` is on `PATH`, right after building
the one that has your rules in it.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Take the token out of the config, split matching from metadata

The connect examples pasted the token straight into the file the token section
tells you never to put it in, and the Cursor one dressed it up as a variable:
it set `CORGEA_TOKEN` to a literal and referenced it as `${CORGEA_TOKEN}`,
which is not a syntax Cursor interpolates. Cursor resolves `${env:NAME}` in
`args`, `env`, `url` and `headers`, so the token now stays in the environment
and never reaches the file. Noted that Cursor reads its own environment, so a
GUI launch on macOS may not see a shell export until restarted, and that the
syntax belongs to the client rather than to MCP. Also called out
`.cursor/mcp.json` in the token section, since the project-local copy is the
one committed by accident.

`file_types` and `conditions` were filed under metadata, but both decide what
a rule matches — one picks the files it runs against, the other filters by AST
and context. Split the table in two, so the group to search when a rule fires
in the wrong place is the group that can cause it.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Give Claude Desktop its own config, separate the taint controls

"Same block as Cursor" was wrong: `${env:NAME}` is Cursor's syntax, and Claude
Desktop interpolates nothing. What resolves the placeholder there is mcp-remote,
which substitutes `${NAME}` in a header from its own `process.env`
(`src/lib/utils.ts`), so the value belongs in the `env` block. Cursor's spelling
does not fail loudly in that position — mcp-remote takes `env:CORGEA_TOKEN` as
the variable name, finds none, and sends the header empty — so the symptom is
written down next to the config that causes it. The token stays out of the
Cursor config, and the Claude Desktop file is a user-profile one, not a
repository one.

Both `--header` arguments also lost the space after the colon, which Cursor and
Claude Desktop on Windows mangle.

`sanitizers` and `propagators` were described as one thing that narrows
reported flows. Sanitizers do that. Propagators do nothing at all here:
`src/models.rs` declares the field and no other file under `src/` reads it,
which is why no rule in the repo sets one. They now have separate rows, and the
inert field is filed with `unless:` under the trap it belongs to.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Slim corgea-scan and point it at --help (#7)

* Point corgea-scan at the skill the CLI carries

The CLI now compiles its own skill into the binary and prints it with
`corgea skill show`, so the depth belongs there, pinned to the version
actually installed. Keeping a second copy here guarantees the two drift.

Cuts the body roughly in half: install, auth, and the commands worth
having loaded immediately stay, and the flag matrix, export formats,
upload matrix and troubleshooting go. The skill defers to `corgea skill
show` for the rest, and to `corgea --help` where that command does not
exist yet.

Widens the frontmatter description rather than narrowing it. The body
shrank, but this is now the entry point for the whole CLI, so the
trigger text has to cover the install gate, dependency inventory and
advisories as well as scanning.

Keeps the name: `npx skills add corgea/skills --skill corgea-scan` is
already published in the docs and the README.

Depends on a CLI release carrying `corgea skill show`; merging earlier
points users at a command their binary does not have.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Point corgea-scan at --help rather than a new CLI command

The pointer targeted `corgea skill show`, which does not exist yet and is no
longer being added: `corgea --help` is already compiled into the binary by
clap, already pinned to the installed version, and costs nothing.

Same shape as before — this file stays the lean entry point and defers the
detail — but the target is a command every released CLI already answers, so
this no longer waits on a release to be useful. Adds the version-skew
guidance: report an outdated CLI rather than upgrading unprompted, and treat
a surprising result as a version difference before a bug, since a changed
default will not error the way a missing flag does.

The long-tail reference now points at docs.corgea.app rather than at a second
copy of it.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants