@event4u/agent-config 8.2.0 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +3 -3
- package/CHANGELOG.md +38 -0
- package/MIGRATION.md +18 -0
- package/README.md +86 -3
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +1 -1
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +1 -1
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +1 -1
- package/dist/install/install.mjs +586 -386
- package/dist/mcp/registry-manifest.json +2 -2
- package/docs/contracts/CHANGELOG-conventions.md +8 -5
- package/docs/contracts/branch-protection-policy.md +14 -5
- package/docs/contracts/mcp-phase-1-scope.md +75 -0
- package/docs/contracts/mcp-tool-inventory.md +22 -15
- package/docs/contracts/release-pr-gating.md +22 -11
- package/docs/decisions/ADR-111-canonical-glama-listing.md +97 -0
- package/docs/decisions/ADR-112-stdio-lite-stays-read-only.md +92 -0
- package/docs/decisions/ADR-113-ci-native-release-label-trigger.md +201 -0
- package/docs/decisions/INDEX.md +3 -0
- package/docs/distribution/registry-submissions.md +1 -1
- package/docs/getting-started.md +8 -6
- package/docs/installation.md +13 -14
- package/docs/mcp-server.md +32 -20
- package/docs/setup/mcp-cloud-endpoints.md +9 -2
- package/package.json +2 -2
- package/src/scripts/_cli/cmd_doctor.ts +111 -11
- package/src/scripts/_cli/cmd_uninstall.ts +22 -0
- package/src/scripts/_cli/cmd_upgrade.ts +74 -13
- package/src/scripts/_lib/claude_settings_hooks.ts +251 -0
- package/src/scripts/condense.ts +7 -29
- package/src/scripts/install +23 -4
- package/src/scripts/install.ts +30 -0
- package/src/scripts/lint_glama_drift.ts +75 -0
- package/src/scripts/mcp_exec/safety_envelope.ts +105 -0
- package/src/scripts/mcp_registry_submit.sh +14 -3
- package/src/scripts/mcp_server/__main__.ts +6 -8
- package/src/scripts/mcp_server/consumer_tool_catalog.json +78 -3
- package/src/scripts/mcp_server/tools.ts +731 -0
- package/src/scripts/release.ts +203 -39
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
adr: 113
|
|
3
|
+
status: accepted
|
|
4
|
+
date: 2026-07-08
|
|
5
|
+
decision: ci-native-release-label-trigger
|
|
6
|
+
supersedes: —
|
|
7
|
+
superseded_by: —
|
|
8
|
+
phase: road-to-ci-native-release · Phase 1
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# ADR-113 — CI-native release path, triggered by a `release` label on a merged PR
|
|
12
|
+
|
|
13
|
+
## Status
|
|
14
|
+
|
|
15
|
+
**Accepted** · 2026-07-08.
|
|
16
|
+
|
|
17
|
+
## Context
|
|
18
|
+
|
|
19
|
+
`task release` (`src/scripts/release.ts`) is a fully interactive, local
|
|
20
|
+
9-step pipeline: preflight → plan → confirm → branch+bump → commit/push+PR →
|
|
21
|
+
wait for CI → merge → tag → GitHub Release. The back half is already CI
|
|
22
|
+
(`publish-npm.yml`, `cloud-release.yml`, `release-guard.yml` all trigger on
|
|
23
|
+
the pushed numeric tag), but a maintainer must run the front half from a
|
|
24
|
+
terminal. This ADR adds an unattended, CI-native entry point alongside
|
|
25
|
+
`task release` — not a replacement.
|
|
26
|
+
|
|
27
|
+
## Decision
|
|
28
|
+
|
|
29
|
+
1. **Trigger: a `release` / `release:major` / `release:minor` /
|
|
30
|
+
`release:patch` label on a PR, checked at merge time**
|
|
31
|
+
(`pull_request: types: [closed]`, job-guarded on `merged == true` +
|
|
32
|
+
an exact label match). `release` auto-detects the bump from
|
|
33
|
+
Conventional Commits since the last tag (same logic `task release`
|
|
34
|
+
uses); the `release:*` variants force the bump. `workflow_dispatch`
|
|
35
|
+
(with `bump` / `version` / `dry_run` / `resume` inputs) is the
|
|
36
|
+
manual/recovery entry for a release not tied to a PR merge.
|
|
37
|
+
|
|
38
|
+
2. **The numeric `X.Y.Z` tag stays the pipeline's *output*, never its
|
|
39
|
+
input.** `publish-npm.yml`, `cloud-release.yml`, and
|
|
40
|
+
`release-guard.yml` all trigger on `push: tags:
|
|
41
|
+
[0-9]+.[0-9]+.[0-9]+` and assume the tagged commit already carries
|
|
42
|
+
the bumped `package.json` / `marketplace.json` / regenerated
|
|
43
|
+
projections. A tag-shaped trigger (hand-pushed numeric tag, or a
|
|
44
|
+
`cut-*` intent tag) was considered and rejected — see Alternatives.
|
|
45
|
+
|
|
46
|
+
3. **One orchestration workflow (`release.yml`), not a prepare/finalize
|
|
47
|
+
split.** GitHub's `GITHUB_TOKEN` recursion guard means events caused
|
|
48
|
+
by the default token do not start new workflow runs (with narrow,
|
|
49
|
+
documented exceptions — see the bot-PR finding below). A
|
|
50
|
+
bot-auto-merged release PR would never trigger a separate
|
|
51
|
+
merge-listening "finalize" workflow. `release.yml` instead runs the
|
|
52
|
+
whole chain synchronously in one job, calling
|
|
53
|
+
`src/scripts/release.ts --ci` end-to-end — the exact same pipeline
|
|
54
|
+
`task release` runs locally, just non-interactively.
|
|
55
|
+
|
|
56
|
+
4. **`release.ts` gains a `--ci` mode, not a rewrite.** `--ci` changes
|
|
57
|
+
exactly three things relative to the existing interactive path:
|
|
58
|
+
- the `gh`-auth preflight probe (`gh api repos/{slug} --jq .id`
|
|
59
|
+
instead of `gh api user --jq .login` — the default `GITHUB_TOKEN`
|
|
60
|
+
is an installation token and `gh api user` returns 403 for it even
|
|
61
|
+
when perfectly valid);
|
|
62
|
+
- a `nothing_to_release_ci` short-circuit (empty commits + no
|
|
63
|
+
explicit bump/version override → exit 0) so firing the label path
|
|
64
|
+
twice in a row is a clean no-op, not a spurious empty release;
|
|
65
|
+
- explicit post-release dispatch of `release-guard.yml`,
|
|
66
|
+
`publish-npm.yml`, and `cloud-release.yml` via their `tag` input,
|
|
67
|
+
because a `GITHUB_TOKEN`-pushed tag does not fire their
|
|
68
|
+
`push: tags:` trigger either.
|
|
69
|
+
|
|
70
|
+
Every other code path (bump inference, changelog-era rendering, the
|
|
71
|
+
10-step `execute()` sequence, all idempotent-resume probes) is
|
|
72
|
+
shared, unmodified, between `task release` and `release.yml`.
|
|
73
|
+
|
|
74
|
+
5. **Token: `secrets.RELEASE_PR_TOKEN` with a `GITHUB_TOKEN` fallback.**
|
|
75
|
+
`release.yml` uses `${{ secrets.RELEASE_PR_TOKEN || secrets.GITHUB_TOKEN
|
|
76
|
+
}}` for checkout, git push, and `gh` auth. See the bot-PR-approval
|
|
77
|
+
finding below for why this matters and why the fallback is safe to
|
|
78
|
+
ship without the secret configured.
|
|
79
|
+
|
|
80
|
+
## Finding — bot-created PRs need a manual "Approve workflows to run" click (no PAT)
|
|
81
|
+
|
|
82
|
+
Verified against GitHub's own changelog and community docs (2026-06-11,
|
|
83
|
+
["Bot-created pull requests can run workflows if
|
|
84
|
+
approved"](https://github.blog/changelog/2026-06-11-bot-created-pull-requests-can-run-workflows-if-approved/)):
|
|
85
|
+
when a workflow uses the default `GITHUB_TOKEN` to open or update a PR,
|
|
86
|
+
the resulting `pull_request` events (`opened` / `synchronize` /
|
|
87
|
+
`reopened`) DO create workflow runs for other `pull_request`-triggered
|
|
88
|
+
workflows — but those runs sit in an **approval-required** state until a
|
|
89
|
+
repo collaborator with write access clicks **Approve workflows to run**
|
|
90
|
+
in the PR's merge box. This is a deliberate GitHub security control
|
|
91
|
+
(distinct from, but related to, the fork-first-time-contributor approval
|
|
92
|
+
gate), not a bug in this design.
|
|
93
|
+
|
|
94
|
+
This repo's branch-protection ruleset (verified live via `gh api
|
|
95
|
+
repos/event4u-app/agent-config/rulesets/17749383`) requires
|
|
96
|
+
**zero** approving reviews (`required_approving_review_count: 0`) and
|
|
97
|
+
exactly **one** required status check, `Sync + Generate Tools
|
|
98
|
+
Consistency` (from `consistency.yml`, `pull_request`-triggered). That
|
|
99
|
+
check does not run automatically on a `GITHUB_TOKEN`-opened release PR
|
|
100
|
+
without the one-time approval click — so without `RELEASE_PR_TOKEN`, a
|
|
101
|
+
release via the label path pauses at "waiting for checks" until a
|
|
102
|
+
maintainer approves the run once per release.
|
|
103
|
+
|
|
104
|
+
**Accepted as the default**, not worked around, because:
|
|
105
|
+
|
|
106
|
+
- It requires zero new secrets and zero change to repo-wide security
|
|
107
|
+
settings (disabling the org-wide first-time-contributor approval
|
|
108
|
+
setting would lower security posture for every fork/bot PR, not just
|
|
109
|
+
release PRs — out of proportion to this problem).
|
|
110
|
+
- A release is a production action; per this repo's own engineering
|
|
111
|
+
safety floor (`engineering-safety-floor` rule) and Hard-Floor stance
|
|
112
|
+
on deploys, a single human checkpoint before a release's own CI even
|
|
113
|
+
runs is arguably a feature, not friction.
|
|
114
|
+
- `RELEASE_PR_TOKEN` (a fine-grained PAT or GitHub App installation
|
|
115
|
+
token with `contents: write` + `pull-requests: write` on this repo)
|
|
116
|
+
removes the approval requirement entirely — documented as the
|
|
117
|
+
zero-friction upgrade path in `release.yml`'s header comment. Adding
|
|
118
|
+
it is a maintainer action, not part of this ADR.
|
|
119
|
+
|
|
120
|
+
## Verified facts (not assumed)
|
|
121
|
+
|
|
122
|
+
- Branch protection ruleset `main protection` (id `17749383`):
|
|
123
|
+
`required_approving_review_count: 0`; required check =
|
|
124
|
+
`Sync + Generate Tools Consistency`; `bypass_actors` includes a
|
|
125
|
+
repository-role bypass. No human-review gate blocks a bot merge once
|
|
126
|
+
checks are green.
|
|
127
|
+
- `consistency.yml` triggers on `pull_request` with path filters that
|
|
128
|
+
match every file a release PR touches (`package.json`,
|
|
129
|
+
`.claude-plugin/marketplace.json`, `CHANGELOG.md`, `dist/agent-src/**`,
|
|
130
|
+
`.augment/**`) — the required check fires (subject to the approval
|
|
131
|
+
gate above), it is not silently skipped.
|
|
132
|
+
- No `RELEASE_PR_TOKEN`-shaped secret exists in the repo today (`gh
|
|
133
|
+
secret list`) — the fallback path is the one that ships by default.
|
|
134
|
+
- `softprops/action-gh-release@v2`'s `overwrite_files` input defaults to
|
|
135
|
+
`true` — re-attaching the same-named cloud-release assets on a
|
|
136
|
+
dispatch-chain re-run is idempotent by construction, no extra guard
|
|
137
|
+
needed.
|
|
138
|
+
- `npm publish` hard-rejects re-publishing an existing version — added a
|
|
139
|
+
`npm view @event4u/agent-config@<tag> version` pre-check to
|
|
140
|
+
`publish-npm.yml` so a double-dispatch (native tag-push trigger racing
|
|
141
|
+
the explicit dispatch, when `RELEASE_PR_TOKEN` IS configured) is a
|
|
142
|
+
clean skip, not a red run.
|
|
143
|
+
|
|
144
|
+
## Consequences
|
|
145
|
+
|
|
146
|
+
- Releasing needs exactly one human action: label a PR `release` (or a
|
|
147
|
+
`release:*` variant) and merge it. Without `RELEASE_PR_TOKEN`, one
|
|
148
|
+
additional "Approve workflows to run" click is needed once per
|
|
149
|
+
release on the auto-created release PR.
|
|
150
|
+
- `task release` is unaffected — same script, same flags, same tests.
|
|
151
|
+
It remains the interactive/local path and the documented fallback if
|
|
152
|
+
the CI path misbehaves.
|
|
153
|
+
- Three existing workflows (`release-guard.yml`, `publish-npm.yml`)
|
|
154
|
+
gained a `workflow_dispatch` input / idempotency guard respectively;
|
|
155
|
+
`cloud-release.yml` needed no change (already idempotent).
|
|
156
|
+
- A future maintainer who configures `RELEASE_PR_TOKEN` gets a fully
|
|
157
|
+
unattended release with zero code changes required — the fallback
|
|
158
|
+
expression already prefers it.
|
|
159
|
+
|
|
160
|
+
## Alternatives considered
|
|
161
|
+
|
|
162
|
+
- **Hand-pushed numeric tag as the trigger.** Rejected: the tag-triggered
|
|
163
|
+
workflows require the tagged commit to already carry the bump: a
|
|
164
|
+
hand-pushed `X.Y.Z` on plain main fires them against the *un-bumped*
|
|
165
|
+
tree (red `release-guard`, wrong npm tarball), and the tag would then
|
|
166
|
+
have to be deleted and re-pushed onto the eventual bump-PR merge
|
|
167
|
+
commit — mutable tags are an anti-pattern (stale fetches, npm
|
|
168
|
+
provenance binds to the tag commit).
|
|
169
|
+
- **A `cut-*` intent tag** (`cut-X.Y.Z` / `cut-auto` / `cut-major` / …)
|
|
170
|
+
in a separate namespace from the numeric tags. Functionally workable
|
|
171
|
+
(does not collide with the publish/guard tag patterns), but the
|
|
172
|
+
release label keeps the intent visible on the PR itself — reviewable,
|
|
173
|
+
revocable pre-merge, no second git-tag vocabulary to teach.
|
|
174
|
+
- **`workflow_dispatch` as the sole/primary trigger**, no label. Loses
|
|
175
|
+
the "the intent to release lives on the PR, and is revocable until
|
|
176
|
+
merge" property that the label gives for free; kept as the
|
|
177
|
+
manual/recovery entry, not the primary path.
|
|
178
|
+
- **Prepare + finalize workflow split** (one workflow opens the release
|
|
179
|
+
PR, a second listens for its merge and finalizes). Needs a
|
|
180
|
+
`RELEASE_PR_TOKEN`-class token as a *hard* requirement — under plain
|
|
181
|
+
`GITHUB_TOKEN`, the bot-merged PR's `pull_request: closed` event is
|
|
182
|
+
the SAME kind of GITHUB_TOKEN-caused event subject to the same
|
|
183
|
+
approval/recursion nuance, and splitting the pipeline across two
|
|
184
|
+
workflow boundaries adds a second place idempotency can drift. The
|
|
185
|
+
single-workflow design collapses this into one synchronous run using
|
|
186
|
+
the already-battle-tested `execute()` sequence.
|
|
187
|
+
- **Disable the org-wide "require approval for first-time contributors"
|
|
188
|
+
setting** to remove the bot-PR approval gate without a PAT. Rejected:
|
|
189
|
+
it is an org-wide security posture change affecting every fork/bot PR
|
|
190
|
+
in the organization, not scoped to releases — disproportionate to
|
|
191
|
+
this problem; `RELEASE_PR_TOKEN` is the properly-scoped fix.
|
|
192
|
+
|
|
193
|
+
## References
|
|
194
|
+
|
|
195
|
+
- `.github/workflows/release.yml` — the orchestration workflow.
|
|
196
|
+
- `src/scripts/release.ts` — the shared pipeline (`--ci` mode).
|
|
197
|
+
- `docs/contracts/release-pr-gating.md`,
|
|
198
|
+
`docs/contracts/branch-protection-policy.md` — release-PR shape and
|
|
199
|
+
required-check contracts this design must keep satisfying.
|
|
200
|
+
- [GitHub Changelog — Bot-created pull requests can run workflows if
|
|
201
|
+
approved (2026-06-11)](https://github.blog/changelog/2026-06-11-bot-created-pull-requests-can-run-workflows-if-approved/)
|
package/docs/decisions/INDEX.md
CHANGED
|
@@ -114,6 +114,9 @@ _Auto-generated by `scripts/adr/regenerate_index.py`. Do not edit._
|
|
|
114
114
|
| [ADR-108](ADR-108-open-source-forever-no-commercial-tier.md) | Open Source Forever No Commercial Tier | accepted | 2026-06-24 | — |
|
|
115
115
|
| [ADR-109](ADR-109-subagent-v1-contract.md) | Subagent V1 Contract | accepted | 2026-07-04 | — |
|
|
116
116
|
| [ADR-110](ADR-110-discipline-profile-resolution-locus.md) | Discipline Profile Resolution Locus | accepted | 2026-07-07 | — |
|
|
117
|
+
| [ADR-111](ADR-111-canonical-glama-listing.md) | Canonical Glama Listing | accepted | 2026-07-07 | — |
|
|
118
|
+
| [ADR-112](ADR-112-stdio-lite-stays-read-only.md) | Stdio Lite Stays Read Only | accepted | 2026-07-07 | — |
|
|
119
|
+
| [ADR-113](ADR-113-ci-native-release-label-trigger.md) | Ci Native Release Label Trigger | accepted | 2026-07-08 | — |
|
|
117
120
|
| [ADR-200](ADR-200-python-to-typescript-migration.md) | Python To Typescript Migration | accepted | 2026-06-11 | — |
|
|
118
121
|
|
|
119
122
|
## Unnumbered (legacy)
|
|
@@ -34,7 +34,7 @@ the submission shape evolves; this file accumulates history.
|
|
|
34
34
|
|
|
35
35
|
| # | Registry | Submission shape | Status | PR / form URL | Date | Maintainer notes |
|
|
36
36
|
|---|---|---|---|---|---|---|
|
|
37
|
-
| 1 | `punkpeye/awesome-mcp-servers` | One-line entry under agent-tooling section; PR via `scripts/mcp_registry_submit.sh` | `
|
|
37
|
+
| 1 | `punkpeye/awesome-mcp-servers` | One-line entry under agent-tooling section; PR via `scripts/mcp_registry_submit.sh` | `submitted` | <https://github.com/punkpeye/awesome-mcp-servers/pull/9607> | 2026-07-07 | PR opened via the submit script (two script bugs fixed on the way: idempotent upstream remote, `--head OWNER:branch` format). Awaiting upstream review. |
|
|
38
38
|
| 2 | `mcp.so` | Directory form submission (web form, no programmatic API) | `pending` | — | — | Form URL: <https://mcp.so/>. Submission shape: same one-line entry, paste into the form's `description` field. |
|
|
39
39
|
| 3 | `mcpservers.org` | Directory form submission (web form, verify URL at submission time) | `pending` | — | — | Form URL: <https://mcpservers.org/>. Submission shape: one-line entry; check the site is live before posting. |
|
|
40
40
|
|
package/docs/getting-started.md
CHANGED
|
@@ -37,16 +37,18 @@ The suite is global-only (ADR-020) — refresh the global install and every
|
|
|
37
37
|
project sees the new skills, rules, and hooks at once, no per-repo bump:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
agent-config upgrade # fetch + install the latest globally (
|
|
41
|
-
agent-config refresh --global # idempotent re-install, same version
|
|
40
|
+
agent-config upgrade # fetch + install the latest globally (content + hooks)
|
|
41
|
+
agent-config refresh --global # idempotent re-install, same version
|
|
42
42
|
agent-config refresh --project # refresh a project's minimal surface — bridge
|
|
43
43
|
# marker, agents/overrides/, .gitignore (no wizard)
|
|
44
|
-
agent-config doctor # PATH,
|
|
44
|
+
agent-config doctor # PATH, hook wiring, duplicate surfaces, bridge presence
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
Claude Code is single-surface: the file projection carries content AND the
|
|
48
|
+
managed hooks in `~/.claude/settings.json` — there is no marketplace plugin
|
|
49
|
+
to keep in sync anymore. A leftover plugin from an older install is flagged
|
|
50
|
+
by `doctor` as `claude-plugin: duplicate surface` (fix:
|
|
51
|
+
`claude plugin uninstall agent-config@event4u-agent-config`).
|
|
50
52
|
|
|
51
53
|
## Project CLI — `./agent-config`
|
|
52
54
|
|
package/docs/installation.md
CHANGED
|
@@ -413,25 +413,24 @@ This is additional to project-installed mode, not a replacement.
|
|
|
413
413
|
auggie plugin install agent-config@event4u-agent-config
|
|
414
414
|
```
|
|
415
415
|
|
|
416
|
-
### Claude Code
|
|
416
|
+
### Claude Code — plugin deprecated (single-surface model)
|
|
417
417
|
|
|
418
|
-
|
|
418
|
+
**Do not install the marketplace plugin for Claude Code.** The npx/npm
|
|
419
|
+
global install (`npx -y @event4u/agent-config init`) is the single supported
|
|
420
|
+
surface: it deploys skills/commands/rules to `~/.claude/` **and** registers
|
|
421
|
+
the deterministic hooks in a managed `~/.claude/settings.json` block. The
|
|
422
|
+
plugin would only duplicate every skill/command listing while its git-SHA
|
|
423
|
+
snapshot silently lags each release.
|
|
419
424
|
|
|
420
|
-
|
|
421
|
-
# From your shell (CLI)
|
|
422
|
-
claude plugin install agent-config@event4u-agent-config
|
|
423
|
-
```
|
|
425
|
+
Already have the plugin from an earlier install?
|
|
424
426
|
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
/plugin marketplace add event4u-app/agent-config
|
|
428
|
-
/plugin install agent-config@event4u-agent-config
|
|
427
|
+
```bash
|
|
428
|
+
claude plugin uninstall agent-config@event4u-agent-config
|
|
429
429
|
```
|
|
430
430
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
`.claude/skills/` — no separate ZIP download.
|
|
431
|
+
Hooks keep firing after removal (they live in `settings.json`, not the
|
|
432
|
+
plugin); `agent-config doctor` verifies via the `hook-wiring` check and
|
|
433
|
+
flags a leftover plugin as `claude-plugin: duplicate surface`.
|
|
435
434
|
|
|
436
435
|
### Copilot CLI
|
|
437
436
|
|
package/docs/mcp-server.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MCP Server
|
|
2
2
|
|
|
3
|
-
> Status: **experimental** —
|
|
3
|
+
> Status: **experimental** — read-only prompts + resources plus a growing `tools/*` surface (stub-by-default: every catalogued tool name resolves, unimplemented ones return a structured `not_implemented` envelope). Tool coverage and the write/exec unlock path are governed by [`agents/settings/contexts/mcp-coverage-strategy.md`](../agents/settings/contexts/mcp-coverage-strategy.md). Promotion to **beta** is gated on the six criteria in [`docs/contracts/mcp-beta-criteria.md`](contracts/mcp-beta-criteria.md); current gate status: `./agent-config doctor --check mcp-beta-readiness`.
|
|
4
4
|
|
|
5
5
|
`agent-config` ships a built-in [Model Context Protocol](https://modelcontextprotocol.io)
|
|
6
6
|
server that exposes the package's read-only governance surface to MCP-aware
|
|
@@ -20,22 +20,30 @@ coexist:
|
|
|
20
20
|
[`docs/contracts/mcp-cloud-scope.md`](contracts/mcp-cloud-scope.md).
|
|
21
21
|
Wire-parity-checked against the local stdio kernel on every release.
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
[`docs/contracts/mcp-phase-1-scope.md`](contracts/mcp-phase-1-scope.md)
|
|
23
|
+
Every implemented tool is either read-only or writes only inside the
|
|
24
|
+
consumer's project tree via a path-guard (`_validateInTreePath`) —
|
|
25
|
+
[`docs/contracts/mcp-phase-1-scope.md`](contracts/mcp-phase-1-scope.md)
|
|
26
|
+
is the scope contract; exec-tier tools (shell-spawning) and network-tier
|
|
27
|
+
tools (billable) are gated behind
|
|
28
|
+
[`agents/settings/contexts/mcp-coverage-strategy.md`](../agents/settings/contexts/mcp-coverage-strategy.md).
|
|
26
29
|
|
|
27
30
|
## What the server exposes
|
|
28
31
|
|
|
29
|
-
| Primitive | URIs | Source |
|
|
30
|
-
|
|
31
|
-
| `prompts/list` + `prompts/get` | `skill.<name>`, `command.<name>` | `dist/agent-src/skills/`, `dist/agent-src/commands/` |
|
|
32
|
-
| `resources/list` + `resources/read` | `rule://<stem>` | `dist/agent-src/rules/` |
|
|
33
|
-
| ↳ | `guideline://<relpath>` | `docs/guidelines/` |
|
|
34
|
-
| ↳ | `context://<relpath>` | `dist/agent-src/contexts/` |
|
|
32
|
+
| Primitive | URIs | Source |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `prompts/list` + `prompts/get` | `skill.<name>`, `command.<name>` | `dist/agent-src/skills/`, `dist/agent-src/commands/` |
|
|
35
|
+
| `resources/list` + `resources/read` | `rule://<stem>` | `dist/agent-src/rules/` |
|
|
36
|
+
| ↳ | `guideline://<relpath>` | `docs/guidelines/` |
|
|
37
|
+
| ↳ | `context://<relpath>` | `dist/agent-src/contexts/` |
|
|
38
|
+
| `tools/list` + `tools/call` | see [`agents/settings/contexts/mcp-tool-tier-map.md`](../agents/settings/contexts/mcp-tool-tier-map.md) for the full candidate surface | `src/scripts/mcp_server/consumer_tool_catalog.json` |
|
|
35
39
|
|
|
36
40
|
All resources are served with `mimeType: text/markdown`. Pagination is
|
|
37
41
|
cursor-based (default page size: 100). Hot-reload triggers automatically on
|
|
38
42
|
file mtime changes — edit a rule, reissue `resources/list`, see the update.
|
|
43
|
+
Exact prompt/resource/tool counts drift with every content change — the smoke
|
|
44
|
+
test below reports the live numbers rather than a number pinned in prose.
|
|
45
|
+
_Last verified boot (`task mcp:glama-test`, 2026-07-07): 430 prompts / 232
|
|
46
|
+
resources / 27 tools (18 implemented, 9 stubs — post write/exec cut)._
|
|
39
47
|
|
|
40
48
|
## Setup — one-line install
|
|
41
49
|
|
|
@@ -60,10 +68,9 @@ task mcp:run # maintainer / dev repo
|
|
|
60
68
|
./agent-config mcp:run # consumer projects
|
|
61
69
|
```
|
|
62
70
|
|
|
63
|
-
Both forms launch the TypeScript MCP server over stdio via `tsx`. Use these
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
the config snippets below.
|
|
71
|
+
Both forms launch the TypeScript MCP server over stdio via `tsx`. Use these
|
|
72
|
+
for a manual smoke test — MCP clients (Claude Desktop, Cursor, Zed, Continue)
|
|
73
|
+
launch the server themselves via the config snippets below.
|
|
67
74
|
|
|
68
75
|
## Client configuration
|
|
69
76
|
|
|
@@ -140,8 +147,9 @@ boots cleanly:
|
|
|
140
147
|
|
|
141
148
|
```bash
|
|
142
149
|
./agent-config mcp:run < /dev/null
|
|
143
|
-
# Expect stderr: "mcp-server: loaded N prompts (0 warnings)"
|
|
144
|
-
# "mcp-server: loaded
|
|
150
|
+
# Expect stderr: "mcp-server: loaded N prompts (0 warnings)",
|
|
151
|
+
# "mcp-server: loaded N resources (0 warnings)", and
|
|
152
|
+
# "mcp-server: registered N tools (N implemented, N stubs): [...]"
|
|
145
153
|
```
|
|
146
154
|
|
|
147
155
|
## Troubleshooting
|
|
@@ -155,9 +163,13 @@ boots cleanly:
|
|
|
155
163
|
## Scope
|
|
156
164
|
|
|
157
165
|
- **In scope:** read-only prompts + resources, pagination, hot-reload, stdio
|
|
158
|
-
transport, free-tier client compatibility
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
transport, free-tier client compatibility, a growing read-only and
|
|
167
|
+
in-tree-write `tools/*` surface (see
|
|
168
|
+
[`agents/settings/contexts/mcp-tool-tier-map.md`](../agents/settings/contexts/mcp-tool-tier-map.md)).
|
|
169
|
+
- **Out of scope:** SSE / native HTTP transport for this server (the
|
|
170
|
+
Cloudflare Worker above is the HTTP bridge instead), shell-exec and
|
|
171
|
+
network-tier tools until the safety envelope in
|
|
172
|
+
[`agents/settings/contexts/mcp-coverage-strategy.md`](../agents/settings/contexts/mcp-coverage-strategy.md)
|
|
173
|
+
ships, signed payloads.
|
|
162
174
|
|
|
163
175
|
← [Architecture](architecture.md) · [MCP config generation (consumer side)](mcp.md)
|
|
@@ -19,8 +19,15 @@ resources. `tools/list` returns two **deprecated stubs**
|
|
|
19
19
|
successors; `tools/call` against either returns `isError=true`. No
|
|
20
20
|
script execution, no FS access, no shell.
|
|
21
21
|
|
|
22
|
-
Full power — the
|
|
23
|
-
|
|
22
|
+
Full power — the implemented tool surface (18 tools as of the 2026-07-07
|
|
23
|
+
write/exec cut: memory, roadmap, capabilities, doctor/conformance,
|
|
24
|
+
telemetry, council-estimate, and the vitest `run_tests` pilot) — requires
|
|
25
|
+
the **local kernel server** (`agent-config mcp:run` on a repo checkout).
|
|
26
|
+
`implemented_on` stays `["stdio"]` for every execution tool, permanently:
|
|
27
|
+
the Worker has no kernel runtime endpoint — it serves a release-pinned
|
|
28
|
+
content snapshot from R2, so there is no process that could run the
|
|
29
|
+
handlers, and the A0-cloud contract's no-execution boundary is a design
|
|
30
|
+
invariant, not a backlog item. See
|
|
24
31
|
[`../contracts/mcp-cloud-scope.md`](../contracts/mcp-cloud-scope.md)
|
|
25
32
|
for the execution-safety boundary and the Phase-7-DEFERRED gate that
|
|
26
33
|
governs any future tool restoration.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@event4u/agent-config",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.3.0",
|
|
4
4
|
"description": "Universal AI Agent OS — audited skills, governance rules, commands, and templates for AI coding tools (Claude Code, Cursor, Windsurf, Copilot).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build:cli": "tsc -p tsconfig.json && chmod +x dist/cli/agent-config.js",
|
|
59
|
-
"build:install-bundle": "esbuild src/scripts/install.ts --bundle --platform=node --format=esm --target=node20 --define:__AGENT_CONFIG_BUNDLE__=true --outfile=dist/install/install.mjs",
|
|
59
|
+
"build:install-bundle": "esbuild src/scripts/install.ts --bundle --platform=node --format=esm --target=node20 --define:__AGENT_CONFIG_BUNDLE__=true --outfile=dist/install/install.mjs --banner:js=\"import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);\"",
|
|
60
60
|
"build:ui": "vite build",
|
|
61
61
|
"build:discovery": "./scripts-run src/scripts/build_discovery_manifest --write --strict",
|
|
62
62
|
"build:mcp-manifest": "./scripts-run src/scripts/build_mcp_registry_manifest --write --strict",
|
|
@@ -81,6 +81,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
81
81
|
import * as YAML from 'yaml';
|
|
82
82
|
|
|
83
83
|
import * as claude_plugin from '../_lib/claude_plugin.js';
|
|
84
|
+
import * as claude_settings_hooks from '../_lib/claude_settings_hooks.js';
|
|
84
85
|
import { _is_source_repo } from './cmd_refresh.js';
|
|
85
86
|
import * as installed_lock from '../_lib/installed_lock.js';
|
|
86
87
|
import * as installed_tools from '../_lib/installed_tools.js';
|
|
@@ -730,6 +731,7 @@ const CHECK_IDS = [
|
|
|
730
731
|
'scope',
|
|
731
732
|
'global-binary',
|
|
732
733
|
'claude-plugin',
|
|
734
|
+
'hook-wiring',
|
|
733
735
|
'stale-orphans',
|
|
734
736
|
'manifest-integrity',
|
|
735
737
|
'lockfile-freshness',
|
|
@@ -749,6 +751,7 @@ const GLOBAL_CHECK_IDS: ReadonlySet<string> = new Set([
|
|
|
749
751
|
'scope',
|
|
750
752
|
'global-binary',
|
|
751
753
|
'claude-plugin',
|
|
754
|
+
'hook-wiring',
|
|
752
755
|
'stale-orphans',
|
|
753
756
|
'mcp-mode',
|
|
754
757
|
'mcp-beta-readiness',
|
|
@@ -1005,12 +1008,14 @@ function lstripV(s: string): string {
|
|
|
1005
1008
|
}
|
|
1006
1009
|
|
|
1007
1010
|
/**
|
|
1008
|
-
* Claude Code plugin state
|
|
1009
|
-
*
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1011
|
+
* Claude Code plugin state under the single-surface model
|
|
1012
|
+
* (road-to-claude-code-single-surface): the `~/.claude/` file projection is
|
|
1013
|
+
* THE distribution surface for Claude Code — content, and hooks via the
|
|
1014
|
+
* managed settings.json block. An installed marketplace plugin next to the
|
|
1015
|
+
* projection is a **duplicate surface**: every skill/command lists twice
|
|
1016
|
+
* and the plugin's SHA snapshot rots silently. That is `fail`, with the
|
|
1017
|
+
* uninstall command as the fix (never uninstalled autonomously — the plugin
|
|
1018
|
+
* is a user-owned surface).
|
|
1014
1019
|
*/
|
|
1015
1020
|
function _check_claude_plugin(): Dict {
|
|
1016
1021
|
const claude = shutilWhich('claude');
|
|
@@ -1027,11 +1032,27 @@ function _check_claude_plugin(): Dict {
|
|
|
1027
1032
|
id: 'claude-plugin',
|
|
1028
1033
|
status: 'ok',
|
|
1029
1034
|
message:
|
|
1030
|
-
'plugin not installed (
|
|
1031
|
-
'
|
|
1035
|
+
'plugin not installed (single-surface model — the ~/.claude/ file ' +
|
|
1036
|
+
'projection carries content AND hooks)',
|
|
1032
1037
|
remedy: '',
|
|
1033
1038
|
};
|
|
1034
1039
|
}
|
|
1040
|
+
const projection = path.join(os.homedir(), '.claude', 'skills');
|
|
1041
|
+
const projection_present = isDir(projection);
|
|
1042
|
+
if (projection_present) {
|
|
1043
|
+
return {
|
|
1044
|
+
id: 'claude-plugin',
|
|
1045
|
+
status: 'fail',
|
|
1046
|
+
message:
|
|
1047
|
+
'duplicate surface: the marketplace plugin AND the ~/.claude/ file ' +
|
|
1048
|
+
'projection are both installed — every skill/command lists twice and ' +
|
|
1049
|
+
'the plugin snapshot rots silently',
|
|
1050
|
+
remedy:
|
|
1051
|
+
`claude plugin uninstall ${claude_plugin.CLAUDE_PLUGIN_ID}@${claude_plugin.CLAUDE_MARKETPLACE_NAME} ` +
|
|
1052
|
+
'(hooks stay — they are registered in ~/.claude/settings.json; ' +
|
|
1053
|
+
'verify with the hook-wiring check)',
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1035
1056
|
const snapshot_v = lstripV(claude_plugin.claude_plugin_snapshot_version() || '');
|
|
1036
1057
|
const binary_v = lstripV(_current_package_version() || '');
|
|
1037
1058
|
if (snapshot_v && binary_v && snapshot_v !== binary_v) {
|
|
@@ -1046,9 +1067,86 @@ function _check_claude_plugin(): Dict {
|
|
|
1046
1067
|
}
|
|
1047
1068
|
return {
|
|
1048
1069
|
id: 'claude-plugin',
|
|
1049
|
-
status: '
|
|
1050
|
-
message:
|
|
1051
|
-
|
|
1070
|
+
status: 'warn',
|
|
1071
|
+
message:
|
|
1072
|
+
`plugin installed${snapshot_v ? ` (snapshot ${snapshot_v})` : ''} without the ` +
|
|
1073
|
+
'file projection — deprecated surface; the projection is the supported path',
|
|
1074
|
+
remedy: 'agent-config global (then uninstall the plugin per the printed hint)',
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Managed hook wiring (single-surface model): the deterministic hook matrix
|
|
1080
|
+
* must be registered in ~/.claude/settings.json AND the dispatch target
|
|
1081
|
+
* (`agent-config` binary) must resolve on PATH. Both halves are required —
|
|
1082
|
+
* hooks without a binary silently no-op every session.
|
|
1083
|
+
*/
|
|
1084
|
+
function _check_hook_wiring(): Dict {
|
|
1085
|
+
const claude = shutilWhich('claude');
|
|
1086
|
+
if (claude === null) {
|
|
1087
|
+
return {
|
|
1088
|
+
id: 'hook-wiring',
|
|
1089
|
+
status: 'skipped',
|
|
1090
|
+
message: 'Claude Code CLI not on PATH — hook-wiring check not applicable',
|
|
1091
|
+
remedy: '',
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
const settings_path = path.join(os.homedir(), '.claude', 'settings.json');
|
|
1095
|
+
let missing: string[];
|
|
1096
|
+
try {
|
|
1097
|
+
const matrix = claude_settings_hooks.build_claude_hook_matrix(
|
|
1098
|
+
path.join(_package_root(), 'src', 'scripts', 'hook_manifest.yaml'),
|
|
1099
|
+
);
|
|
1100
|
+
const settings = pathExists(settings_path)
|
|
1101
|
+
? (JSON.parse(fs.readFileSync(settings_path, 'utf8')) as Record<string, unknown>)
|
|
1102
|
+
: {};
|
|
1103
|
+
const hooks = ((settings['hooks'] ?? {}) as Record<string, unknown>) || {};
|
|
1104
|
+
missing = Object.keys(matrix).filter((ev) => {
|
|
1105
|
+
const groups = hooks[ev];
|
|
1106
|
+
if (!Array.isArray(groups)) return true;
|
|
1107
|
+
return !JSON.stringify(groups).includes(claude_settings_hooks.MANAGED_SIGNATURE);
|
|
1108
|
+
});
|
|
1109
|
+
} catch (e) {
|
|
1110
|
+
return {
|
|
1111
|
+
id: 'hook-wiring',
|
|
1112
|
+
status: 'fail',
|
|
1113
|
+
message: `cannot evaluate managed hooks: ${String(e)}`,
|
|
1114
|
+
remedy: 'fix ~/.claude/settings.json (invalid JSON?), then agent-config refresh --global',
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
const binary_ok = shutilWhich('agent-config') !== null;
|
|
1118
|
+
if (missing.length === 0 && binary_ok) {
|
|
1119
|
+
return {
|
|
1120
|
+
id: 'hook-wiring',
|
|
1121
|
+
status: 'ok',
|
|
1122
|
+
message: 'managed hooks registered in ~/.claude/settings.json; dispatch binary on PATH',
|
|
1123
|
+
remedy: '',
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
if (missing.length > 0 && claude_plugin.claude_plugin_installed()) {
|
|
1127
|
+
return {
|
|
1128
|
+
id: 'hook-wiring',
|
|
1129
|
+
status: 'warn',
|
|
1130
|
+
message:
|
|
1131
|
+
`managed hooks missing for: ${missing.join(', ')} — currently carried by the ` +
|
|
1132
|
+
'installed plugin (deprecated surface)',
|
|
1133
|
+
remedy: 'agent-config refresh --global (writes the managed settings.json block)',
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
const parts: string[] = [];
|
|
1137
|
+
if (missing.length > 0) parts.push(`managed hooks missing for: ${missing.join(', ')}`);
|
|
1138
|
+
if (!binary_ok) parts.push('`agent-config` not on PATH (hooks would silently no-op)');
|
|
1139
|
+
if (parts.length === 0) {
|
|
1140
|
+
// hooks complete, binary missing was the only issue → covered above.
|
|
1141
|
+
parts.push('`agent-config` not on PATH');
|
|
1142
|
+
}
|
|
1143
|
+
return {
|
|
1144
|
+
id: 'hook-wiring',
|
|
1145
|
+
status: 'fail',
|
|
1146
|
+
message: parts.join('; '),
|
|
1147
|
+
remedy: binary_ok
|
|
1148
|
+
? 'agent-config refresh --global'
|
|
1149
|
+
: 'npm install -g @event4u/agent-config, then agent-config refresh --global',
|
|
1052
1150
|
};
|
|
1053
1151
|
}
|
|
1054
1152
|
|
|
@@ -1818,6 +1916,7 @@ function _run_checks(
|
|
|
1818
1916
|
scope: () => _check_scope(project_root),
|
|
1819
1917
|
'global-binary': () => _check_global_binary(project_root),
|
|
1820
1918
|
'claude-plugin': _check_claude_plugin,
|
|
1919
|
+
'hook-wiring': _check_hook_wiring,
|
|
1821
1920
|
'stale-orphans': _check_stale_orphans,
|
|
1822
1921
|
'manifest-integrity': () => _check_manifest_integrity(manifest),
|
|
1823
1922
|
'lockfile-freshness': () => _check_lockfile_freshness(manifest),
|
|
@@ -1890,6 +1989,7 @@ function _run_checks_no_manifest(
|
|
|
1890
1989
|
scope: () => _check_scope(project_root),
|
|
1891
1990
|
'global-binary': () => _check_global_binary(project_root),
|
|
1892
1991
|
'claude-plugin': _check_claude_plugin,
|
|
1992
|
+
'hook-wiring': _check_hook_wiring,
|
|
1893
1993
|
'stale-orphans': _check_stale_orphans,
|
|
1894
1994
|
'manifest-integrity': () => _skipped_manifest_check('manifest-integrity'),
|
|
1895
1995
|
'lockfile-freshness': () => _skipped_manifest_check('lockfile-freshness'),
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
57
|
import * as fs from 'node:fs';
|
|
58
|
+
import * as os from 'node:os';
|
|
58
59
|
import * as path from 'node:path';
|
|
59
60
|
import process from 'node:process';
|
|
60
61
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -62,6 +63,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
62
63
|
import * as fs_atomic from '../_lib/fs_atomic.js';
|
|
63
64
|
import * as installed_lock from '../_lib/installed_lock.js';
|
|
64
65
|
import * as installed_tools from '../_lib/installed_tools.js';
|
|
66
|
+
import { remove_managed_hooks } from '../_lib/claude_settings_hooks.js';
|
|
65
67
|
import { resolve_project_root } from '../_lib/agent_settings.js';
|
|
66
68
|
import { subtract_pointers } from '../_lib/json_pointers.js';
|
|
67
69
|
import { PROJECT_BRIDGE_MARKERS, USER_SCOPE_PATHS } from '../install.js';
|
|
@@ -771,6 +773,26 @@ function _uninstall_global(opts: Options): number {
|
|
|
771
773
|
removed_names.push(tool);
|
|
772
774
|
}
|
|
773
775
|
}
|
|
776
|
+
// Managed Claude hooks live in ~/.claude/settings.json (single-surface
|
|
777
|
+
// model) — remove exactly our entries, never user-owned ones.
|
|
778
|
+
if (tools.includes('claude-code')) {
|
|
779
|
+
const settings_path = path.join(os.homedir(), '.claude', 'settings.json');
|
|
780
|
+
if (opts.dry_run) {
|
|
781
|
+
_print(_stdout, ` · claude-code: would remove managed hooks from ${settings_path}`);
|
|
782
|
+
} else {
|
|
783
|
+
try {
|
|
784
|
+
const res = remove_managed_hooks(settings_path);
|
|
785
|
+
if (res.changed) {
|
|
786
|
+
_print(
|
|
787
|
+
_stdout,
|
|
788
|
+
` · claude-code: removed managed hooks (${res.events.join(', ')})`,
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
} catch (e) {
|
|
792
|
+
_print(_stderr, `⚠️ claude-code: managed-hook removal skipped — ${String(e)}`);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
774
796
|
if (lock !== null && !opts.dry_run) {
|
|
775
797
|
const remaining = (lock.tools ?? []).filter((t) => !tools.includes(t));
|
|
776
798
|
const version = String((lock as unknown as AnyDict)['agent_config_version'] ?? '');
|