# skillhook (npm · @meterapp/skillhook)

Turn this machine into a webhook endpoint that runs Agent Skills with Claude Code or Codex.

- Trust score: 76/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-11-04

## Components

- npm · `@meterapp/skillhook`: 76/100 (this document), [markdown](https://verifymcp.io/servers/dev-skillhook-skillhook/meterapp-skillhook.md), [page](https://verifymcp.io/servers/dev-skillhook-skillhook/meterapp-skillhook)

## Channel facts

- Registry: `npm`
- Package: `@meterapp/skillhook`
- Version: `0.14.7`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-11-04.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 0 of 4 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 100/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to MeterApp/skillhook).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 0 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 62/100
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 4877 tokens (~118/item across 41 items; 41 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 77/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 30% of tool parameters carry a description.
- **Tool Safety**: 75/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 0 of 3 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "send_test_webhook" implies "send" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - An AI judge read all 42 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the skillhook MCP server?

skillhook runs locally as an npm package, launched with npx -y @meterapp/skillhook. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add dev-skillhook-skillhook -- npx -y @meterapp/skillhook
```

### Cursor

```json
{
  "mcpServers": {
    "dev-skillhook-skillhook": {
      "command": "npx",
      "args": [
        "-y",
        "@meterapp/skillhook"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "dev-skillhook-skillhook": {
      "command": "npx",
      "args": [
        "-y",
        "@meterapp/skillhook"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add dev-skillhook-skillhook -- npx -y @meterapp/skillhook
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "dev-skillhook-skillhook": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "@meterapp/skillhook"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add dev-skillhook-skillhook --command npx --arg -y --arg @meterapp/skillhook
```

### Hermes

```yaml
mcp_servers:
  dev-skillhook-skillhook:
    command: "npx"
    args: ["-y", "@meterapp/skillhook"]
```

### Netclaw

```json
{
  "McpServers": {
    "dev-skillhook-skillhook": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "@meterapp/skillhook"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add dev-skillhook-skillhook -t stdio -c npx -a -y @meterapp/skillhook
```

### Other

```json
{
  "mcpServers": {
    "dev-skillhook-skillhook": {
      "command": "npx",
      "args": [
        "-y",
        "@meterapp/skillhook"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-11-04 (score 76, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-11-03 (score 61)

First indexed and scored.

## MCP tools (41)

### `skillhook_status` (~29 tokens)

skillhook status

Overview: home directory, server state, public URL, skills and recent jobs. Call this first.

### `list_skills` (~31 tokens)

List skills

Lists every skill with runner, model, auth type, whether its secret is configured, and its webhook URL.

### `get_skill` (~33 tokens)

Get skill

Returns a skill's summary and the full SKILL.md content.

Input parameters:

- `name` (string, required): skill name (directory name)

### `create_skill` (~329 tokens)

Create skill

Creates <home>/skills/<name>/SKILL.md. Writes the instructions (markdown body) and the skillhook frontmatter (runner, model, auth, filters). For bearer/basic/hmac auth a secret is generated and returned once; for provider-signed auth (github, sentry, granola, stripe, slack, linear, svix, standard-webhooks) call set_secret with the provider's signing secret afterwards.

Input parameters:

- `auth_type` (string): default bearer
- `cwd` (string): working directory for the agent, e.g. ~/dev/my-repo
- `description` (string, required): what the skill does and when it runs (1-1024 chars)
- `effort` (string): low|medium|high|xhigh|max
- `env` (array): env var names from .env to expose to the agent
- `instructions` (string, required): markdown body: what the agent should do with the payload. Use {{payload}} / {{payload.some.path}} placeholders or let skillhook append the event automatically.
- `model` (string): e.g. opus, sonnet, haiku, claude-opus-5, gpt-5-codex
- `name` (string, required): lowercase letters, digits and hyphens
- `overwrite` (boolean)
- `runner` (string)
- `secret_env` (string): env var holding the secret; default SKILLHOOK_SECRET_<NAME>
- `timeout_seconds` (integer)
- `when` (array): filters, e.g. [{"path":"action","equals":"created"}]

### `update_skill_file` (~48 tokens)

Update SKILL.md

Replaces a skill's SKILL.md with new content after validating the frontmatter.

Input parameters:

- `content` (string, required): complete SKILL.md text including frontmatter
- `name` (string, required)

### `validate_skills` (~32 tokens)

Validate skills

Parses every SKILL.md (or one) and reports errors and missing secrets.

Input parameters:

- `name` (string)

### `run_skill` (~167 tokens)

Run skill

Runs a skill with a payload through the same prompt, runner and job pipeline as a webhook, skipping HTTP auth, the skill's when filters and de-duplication. Uses the running server when there is one (job visible in its queue), otherwise runs in-process. Set wait_seconds to block for the result; otherwise poll get_job. Use send_test_webhook to exercise auth and filters too.

Input parameters:

- `effort` (string)
- `headers` (object): extra request headers to simulate, e.g. {"x-github-event":"issues"}
- `model` (string)
- `name` (string, required)
- `payload`: JSON payload the skill receives (object, array, string…)
- `runner` (string)
- `wait_seconds` (integer): default 120

### `test_skill` (~198 tokens)

Test a SKILL.md that is not installed

Runs the complete text of a SKILL.md (frontmatter included) with a payload, exactly like run_skill, without installing it: the file is kept in the job directory (jobs/<id>/skill/<name>/SKILL.md) and the job has trigger `test`. Use it to try a draft before create_skill, or to see how a change would behave. Through the running server when there is one, otherwise in-process.

Input parameters:

- `cwd` (string): working directory for the run (default: the skill's `cwd`, else the copy's own directory inside the job)
- `effort` (string)
- `headers` (object)
- `model` (string)
- `payload`
- `runner` (string)
- `skill_md` (string, required): the whole SKILL.md text, frontmatter included; `name` must be a valid skill name
- `wait_seconds` (integer): default 120

### `send_test_webhook` (~94 tokens)

Send signed test webhook

Signs a payload the way the skill's auth expects and POSTs it to /hooks/<name>, proving the HTTP path (auth, filters, queue) works. Defaults to the local server; set public=true to go through the public URL.

Input parameters:

- `base_url` (string)
- `name` (string, required)
- `payload`
- `public` (boolean)
- `wait_seconds` (integer)

### `list_jobs` (~187 tokens)

List jobs

Recent jobs, newest first. `status` is how the process ended, `outcome` whether the task was done. `waiting: true` lists only the jobs waiting for a person (an unanswered question, or outcome needs_human not yet resumed): answer them with answer_job. `after` (the `next_after` of the previous call) pages further back; `since` is an ISO-8601 instant.

Input parameters:

- `after` (string)
- `failure` (string): only jobs that failed this way: auth, usage_limit, rate_limit, budget, max_turns, not_found, timeout, crash, unknown
- `limit` (integer)
- `outcome` (string)
- `since` (string)
- `skill` (string)
- `status` (string)
- `trigger` (string)
- `waiting` (boolean)

### `list_deliveries` (~120 tokens)

List deliveries

Every request to /hooks/<skill> the server received, newest first, with its outcome: accepted (a job was created), duplicate, in_flight, skipped (a when filter), rejected (401, 404, 413, 503, …), challenge, error. Use it to see why a webhook did not run. `after` pages further back.

Input parameters:

- `after` (string)
- `limit` (integer)
- `outcome` (string)
- `since` (string)
- `skill` (string)

### `replay_delivery` (~178 tokens)

Replay delivery

Runs a recorded delivery again through the skill as it is now, as a new job with trigger `replay`: the original payload, headers and query, no signature check, `when` filters applied unless skip_filters, identical in-flight work is reused when deduplication is enabled on the running server. A delivery that was rejected needs `force` (its body was never verified). Uses the running server when there is one, otherwise runs in-process.

Input parameters:

- `effort` (string)
- `force` (boolean): replay a delivery that was rejected or errored
- `id` (string, required)
- `model` (string)
- `runner` (string)
- `skip_filters` (boolean): run even when the skill's `when` conditions do not match the original request
- `wait_seconds` (integer): default 120

### `replay_job` (~126 tokens)

Replay job

Runs the request an earlier job received again, as a new job with trigger `replay` and `replay_of` pointing at the original (same payload, headers and query; `when` filters applied unless skip_filters; runner/model/effort may be overridden).

Input parameters:

- `effort` (string)
- `id` (string, required)
- `model` (string)
- `runner` (string)
- `skip_filters` (boolean): run even when the skill's `when` conditions do not match the original request
- `wait_seconds` (integer): default 120

### `get_delivery` (~58 tokens)

Get delivery

One delivery record; `include_body` adds the request body when the log kept it (rejected and filtered deliveries) or the payload of the job an accepted delivery created.

Input parameters:

- `id` (string, required)
- `include_body` (boolean)

### `get_job` (~59 tokens)

Get job

Job record plus optional artifacts (result, response, prompt, stdout, stderr, payload, event) and, when the agent reported any, its progress timeline, pending question and answer.

Input parameters:

- `id` (string, required)
- `include` (array)

### `answer_job` (~231 tokens)

Answer a job

A person's answer to a job that is waiting: delivered live to the agent's pending job_ask_human call when the job is still running (`delivered: live`); otherwise recorded and, unless resume is `never`, a new job with trigger `resume` continues the agent's session with it (`claude --resume` / `codex exec resume`; `delivered: resumed`, `resume_job_id`). Use list_jobs with waiting: true to find such jobs; pass `option` when the person picked one of the question's options, `options` for the picks of a multiple-choice question (the answer then lists them one per line), `by` to say who answered.

Input parameters:

- `answer` (string): The answer; may be left out when option or options say it all
- `by` (string)
- `id` (string, required)
- `option` (string)
- `options` (array)
- `resume` (string)
- `wait_seconds` (integer): how long to wait for the resume job (default 120; 0 returns at once)

### `cancel_job` (~27 tokens)

Cancel job

Cancels a queued or running job on the running server.

Input parameters:

- `id` (string, required)

### `set_secret` (~92 tokens)

Set secret

Stores a secret in <home>/.env (mode 600). `name` is an ENV_VAR_NAME, a skill name (its secret_env) or "admin". Use it for provider signing secrets (Granola whsec_…, Sentry client secret, GitHub webhook secret) and for API keys a skill needs via `env:`.

Input parameters:

- `name` (string, required)
- `value` (string, required)

### `generate_secret` (~47 tokens)

Generate secret

Generates a random secret for a skill (or "admin") and returns it once. Existing secrets are kept unless force=true.

Input parameters:

- `force` (boolean)
- `name` (string, required)

### `list_secrets` (~24 tokens)

List secret names

Names of secrets in <home>/.env (values are never returned).

### `get_webhook_urls` (~37 tokens)

Webhook URLs

Webhook URL per skill, using the public URL (Tailscale/config) when one exists.

Input parameters:

- `skill` (string)

### `expose` (~62 tokens)

Expose via Tailscale

mode=funnel: public HTTPS URL (internet). mode=serve: tailnet-only URL. mode=off: disable. mode=status: current exposure. The URL is permanent (your Tailscale node name) and survives reboots.

Input parameters:

- `mode` (string, required)

### `service` (~47 tokens)

Background service

install: run the server at login (launchd/systemd) and keep it alive. status/restart/uninstall/logs.

Input parameters:

- `action` (string, required)
- `lines` (integer)

### `cloud_status` (~172 tokens)

Skillhook Cloud status

Whether this machine is paired with Skillhook Cloud (the opt-in dashboard for every machine's webhooks, jobs and health), its URL, machine id and mode (observe: the cloud can look; control: it can also run skills and change the configuration), whether the token is present (never the token itself), the running server's link state (connected, degraded, disconnected with the reason, last sync, events waiting) and whether an organisation API key is kept here for the fleet (api_key; the skillhook-cloud MCP server and `skillhook cloud overview` use it). Pairing is not a tool on purpose: the person runs `skillhook cloud connect --code <code from their dashboard>` in a terminal, so a code from an untrusted source can never hand this machine to someone else's account.

### `cloud_disconnect` (~76 tokens)

Disconnect from Skillhook Cloud

Stops the link to Skillhook Cloud: sets cloud.enabled to false, revokes and removes the machine token and key, deletes the local spool, and tells a running server (which stops syncing within seconds). Nothing leaves the machine afterwards. keep_token keeps the token in .env for a later reconnect.

Input parameters:

- `keep_token` (boolean)

### `cloud_report_issue` (~306 tokens)

Report an issue to Skillhook

Sends a problem report to the Skillhook team through Skillhook Cloud, from this paired machine (what `skillhook cloud report` does). Use it only when the person asked to report something, and tell them what goes out (dry_run: true returns exactly that without sending): the title and body (write them from what the person said), optional kind, severity, contact_email and the job id, delivery id or skill it is about, and unless diagnostics is false the machine's facts: skillhook, Node and OS versions, cloud mode, the link's state, whether each runner is ready and the failing or warning health checks, all scrubbed of every .env value. Never payloads, logs, prompts or job output. Returns the issue number and URL and whether a confirmation email went out. Fails on a machine that is not paired (`skillhook cloud connect`) or under SKILLHOOK_NO_CLOUD.

Input parameters:

- `body` (string)
- `contact_email` (string): where the Skillhook team answers
- `delivery_id` (string)
- `diagnostics` (boolean): attach the machine's facts (default true)
- `dry_run` (boolean): return the report that would be sent, send nothing
- `job_id` (string): this machine's job id the report is about
- `kind` (string): default bug
- `severity` (string): default normal
- `skill` (string)
- `title` (string, required)

### `doctor` (~31 tokens)

Doctor

Checks Node, config, secrets, skills, Claude/Codex login, Tailscale exposure, server and service.

### `get_health` (~128 tokens)

Health report

Everything doctor checks plus, with deep (default), every MCP server Claude Code and Codex know (connected, needs authentication, failed), installed plugins, `codex doctor`, disk space and each skill's last run, grouped (system, skillhook, runners, tools, skills, exposure). Through the running server's cached report when there is one (`refresh` probes again); otherwise probed now.

Input parameters:

- `deep` (boolean)
- `network` (boolean): ask the npm registry and probe the public URL (default false through the server, true locally)
- `refresh` (boolean)

### `get_config` (~57 tokens)

Get config

The effective skillhook.json (defaults applied), which keys the running server applies live (`hot_keys`) and which need a restart (`restart_keys`: host, port), and what is pending a restart. From the running server when there is one.

### `update_config` (~104 tokens)

Update config

Change skillhook.json: `set` maps dotted keys to values ({"concurrency": 3, "defaults.model": "sonnet"}), `unset` lists dotted keys to remove. One validated write; an invalid result changes nothing. The running server re-reads the file at once and says which keys it applied live and which (host, port) wait for a restart (`restart_server`).

Input parameters:

- `set` (object)
- `unset` (array)

### `restart_server` (~121 tokens)

Restart server

Ask the running server to restart: it pauses dispatch while keeping intake and cloud sync available, lets running jobs finish for up to wait_seconds (default 30), then exits only if idle. On timeout it resumes dispatch without terminating jobs; `force: true` terminates them. Launchd / systemd starts it again after an exit. Only works for a server run as the service (409 otherwise); use it after update_config changed host or port, or after an update was installed.

Input parameters:

- `force` (boolean)
- `wait_seconds` (integer)

### `check_update` (~79 tokens)

Check for updates

Ask the npm registry for the newest skillhook (what `skillhook update` does). With `install: true` and a newer version, upgrade with the package manager that installed skillhook and restart the background service when it has no jobs in progress; a busy service keeps running the old version until `restart_server`.

Input parameters:

- `install` (boolean)

### `get_stats` (~105 tokens)

Stats

Numbers over the jobs and deliveries on this machine: jobs by status, outcome, trigger, runner and failure kind, success and completion rates, duration and queue-wait percentiles, cost and tokens, deliveries by outcome and HTTP status, and the same per skill. `since` takes 24h, 7d, 2w or an ISO-8601 instant.

Input parameters:

- `since` (string)
- `skill` (string)
- `until` (string)

### `get_runners` (~82 tokens)

Runner readiness

Is each runner (claude, codex, shell) installed and logged in, or given an API key: what every job checks before it starts (a `fallback:` runner takes over, or the job fails fast with failure.kind auth). Through the running server's cached answer when there is one (`refresh` probes again).

Input parameters:

- `refresh` (boolean)

### `list_accounts` (~221 tokens)

Accounts

The Claude Code and Codex accounts (named logins) skills run as: for each its state (ready, needs_login, expired, limited, missing_secret, disabled), who it is logged in as, its plan, its usage windows (5h, weekly, per model; with `usage: true` asked from each account's CLI now), its last use, its jobs of the last 24 hours, the skills that use it, and `attention`: what needs a person, each with the command that fixes it. With `mcp: true` (slow: every server connects) also `mcp`: the MCP servers, claude.ai / ChatGPT connectors and plugins each account's runs get, whether each connected, needs authentication or failed, and attention items for them. Logging in is done by a person in a terminal (`skillhook accounts login <name>`), never by a tool.

Input parameters:

- `account` (string)
- `mcp` (boolean)
- `refresh` (boolean)
- `usage` (boolean)

### `list_projects` (~61 tokens)

List linked projects

Repositories whose skillhook.yaml is served by this machine: directory, file, and each hook with its runner, kind (run/skill/prompt), auth, cwd and webhook URL. This is the version-controlled answer to 'which skill runs from which webhook'.

### `link_project` (~151 tokens)

Link project

Registers a repository's skillhook.yaml (dir, or the YAML file itself) in skillhook.json so its hooks are served at /hooks/<name>; the running server picks them up without a restart. With init=true a starter skillhook.yaml is written first when none exists (a `run: git pull --ff-only` hook for merged GitHub pull requests). Secrets skillhook manages (bearer/basic/hmac) are generated and returned once; provider-signed hooks need set_secret afterwards.

Input parameters:

- `dir` (string, required): repository directory (or path to its skillhook.yaml)
- `init` (boolean): write a starter skillhook.yaml when the directory has none
- `no_secret` (boolean)

### `unlink_project` (~40 tokens)

Unlink project

Stops serving a repository's hooks (they answer 404 at once). The repository and its skillhook.yaml are not touched.

Input parameters:

- `dir` (string, required)

### `list_schedules` (~79 tokens)

List schedules

Every skill or hook with a `schedule:`: cron, time zone, catch_up and overlap policy, whether it is enabled and also has a webhook, the next due time, and the last slot, job and status (live from the running server when there is one). A hook with `webhook: false` runs only on its schedule.

### `list_examples` (~35 tokens)

List example skills

Bundled example skills (hello, granola-meeting-actions, sentry-triage, …) that add_example can copy.

### `add_example` (~48 tokens)

Add example skill

Copies a bundled example into <home>/skills (optionally under another name) and generates its secret when skillhook manages it.

Input parameters:

- `as` (string)
- `name` (string, required)

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/dev-skillhook-skillhook/meterapp-skillhook#diagnostics

## Score history

- 2026-11-04: 76
- 2026-11-03: 61

## Common questions

### What is the skillhook MCP server?

skillhook is an MCP server listed in the public MCP registry as dev.skillhook/skillhook. Turn this machine into a webhook endpoint that runs Agent Skills with Claude Code or Codex. This page covers its npm package (@meterapp/skillhook).

### Is the skillhook MCP server safe to use?

skillhook scores 76 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 4 November 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the skillhook MCP server expose?

skillhook exposes 41 tools: skillhook_status, list_skills, get_skill, create_skill, update_skill_file, and 36 more. Their descriptions and schemas cost roughly 4,152 tokens of context every time the server is loaded.

### Is the skillhook MCP server still maintained?

skillhook is still listed as active in the MCP registry. We last reached this channel on 4 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the skillhook MCP server under?

skillhook declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- npm package: https://www.npmjs.com/package/@meterapp/skillhook
- Socket report: https://socket.dev/npm/package/@meterapp/skillhook
- Repository: https://github.com/MeterApp/skillhook
- Website: https://skillhook.dev/
- Changelog RSS feed: https://verifymcp.io/servers/dev-skillhook-skillhook/meterapp-skillhook.xml
- Changelog JSON feed: https://verifymcp.io/servers/dev-skillhook-skillhook/meterapp-skillhook.json
- HTML version of this page: https://verifymcp.io/servers/dev-skillhook-skillhook/meterapp-skillhook
