Skip to content

Commit e01c2cc

Browse files
TMHSDigitalclaude
andcommitted
fix(scaffold): born-green mcp-servers, escaped output, single pin table
Born green (#102): --type mcp-server now generates a minimal TypeScript MCP server (src/index.ts, src/server.ts with createServer(), a vitest test, tsconfig.json) with real tsc/vitest scripts and SDK dependencies. ci.yml and publish.yml use npm ci plus the npm cache once a lockfile is committed and npm install before that, so the first push is green. New born-green clause C7, and the Validate scaffold CI job installs, builds and tests a generated server. Verified locally, including a stdio tools/call round trip against the built dist/index.js. Escaping (#103): JSON and Python templates use json_str / json_inner filters, HTML templates autoescape, and generate_repo validates input (no control characters; no quotes, backslashes, angle brackets or backticks in names; length limits) before writing anything. Runtimes and docs (#121): --mcp-server with --type mcp-server is rejected instead of mixing a Python server into a TypeScript package. Deployed service docs no longer describe publish.yml or npm publishing, and the mcp-server docs describe the generated layout instead of a nonexistent src/providers/ProviderManager. Pins (#122): ACTION_PINS in generator.py is the one source for actions/* majors (matching the meta-repo); rendered workflows are normalized to it and a test keeps template literals in sync. Node matrix is [22, 24] and engines >=22 (Node 20 is end of life). Papercuts (#123): the registry duplicate check runs before rendering, so a duplicate leaves no partial repo; negative counts are rejected; the printed next step uses git commit -s with double quotes; package.json whitespace fixed. Docs (#104): every documented scaffold test command passes --no-register, CLAUDE.md lists the real CLI flags and outputs and warns that registration is on by default, the --help examples include the required --description, and the dead index.html.j2 template is removed. New registry entries default to the developer-tooling category. Closes #102 Closes #103 Closes #104 Closes #121 Closes #122 Closes #123 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
1 parent 4b04c83 commit e01c2cc

33 files changed

Lines changed: 546 additions & 228 deletions

‎.github/workflows/README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ Third-party actions are pinned by full commit SHA, with the version tag in a com
2727
First-party GitHub actions (`actions/`, `github/`) may use a major-version tag:
2828

2929
```yaml
30-
- uses: actions/checkout@v6
31-
- uses: actions/setup-python@v5
30+
- uses: actions/checkout@v7
31+
- uses: actions/setup-python@v6
3232
```
3333

3434
Rationale: tags are mutable and can be retargeted to a malicious commit. SHAs are immutable. GitHub-owned actions are exempt because GitHub's own security model protects them.

‎.github/workflows/validate.yml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,27 @@ jobs:
9696
grep -qv 'gh workflow run publish.yml' /tmp/scaffold-service/ci-deployed-service/.github/workflows/release.yml
9797
echo "Deployed-service scaffold test passed"
9898
99+
python3 scaffold/create-tool.py \
100+
--name "CI Npm Server" \
101+
--description "Automated npm mcp-server test" \
102+
--type mcp-server \
103+
--no-register \
104+
--output /tmp/scaffold-npm
105+
106+
- uses: actions/setup-node@v6
107+
with:
108+
node-version: 22
109+
110+
# Born-green clause C7: the generated repo's own CI commands pass on
111+
# the untouched generated tree.
112+
- name: Generated mcp-server builds and tests green
113+
working-directory: /tmp/scaffold-npm/ci-npm-server
114+
run: |
115+
npm install --no-audit --no-fund
116+
npm run build
117+
npm test
118+
test -f dist/index.js
119+
99120
- name: Scaffold YAML well-formed
100121
run: |
101122
# Catches Jinja2 trim_blocks bugs that collapse adjacent expression lines.

‎CLAUDE.md‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,11 @@ Pure-stdlib Python script that regenerates README tables and stats, CLAUDE.md ca
5959

6060
### scaffold/create-tool.py
6161

62-
CLI args: `--name`, `--slug`, `--description`, `--type`, `--mcp-server`, `--skills N`, `--rules N`, `--license`, `--output`, `--author-name`, `--author-email`.
62+
Thin CLI over `scaffold/generator.py` (`generate_repo`). CLI args: `--name`, `--slug`, `--description`, `--type` (cursor-plugin | mcp-server), `--mcp-server` (cursor-plugin only: adds a Python MCP server), `--deployed-service` (mcp-server archetype without npm), `--skills N`, `--rules N`, `--license`, `--output`, `--author-name`, `--author-email`, `--no-register`, `--registry-root`. Run `python scaffold/create-tool.py --help` for the authoritative list.
6363

64-
Templates in `scaffold/templates/` produce: plugin.json, GitHub Actions workflows, README.md, AGENTS.md, CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md, CODE_OF_CONDUCT.md, SECURITY.md, ROADMAP.md, LICENSE, .cursorrules, .gitignore, docs/index.html, and optional mcp-server/ scaffold.
64+
**Registration is on by default:** without `--no-register`, the scaffold appends the new repo to `registry.json` and regenerates README, CLAUDE.md, and the docs site. Always pass `--no-register` for throwaway or test runs.
65+
66+
Templates in `scaffold/templates/` produce: GitHub Actions workflows, README.md, AGENTS.md, CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md, CODE_OF_CONDUCT.md, SECURITY.md, ROADMAP.md, LICENSE, .cursorrules, .gitignore, .gitattributes, site.json, mcp-tools.json, and dependabot.yml. Cursor plugins also get `.cursor-plugin/plugin.json` (plus an optional Python `mcp-server/`); mcp-servers get `package.json`, a buildable TypeScript skeleton (`src/`, `tsconfig.json`, vitest test), and `docs/index.html`. Generated output must meet [`standards/born-green-contract.md`](standards/born-green-contract.md).
6567

6668
### docs/ (catalog site)
6769

@@ -135,9 +137,12 @@ python scaffold/create-tool.py \
135137
--mcp-server \
136138
--skills 2 \
137139
--rules 1 \
140+
--no-register \
138141
--output /tmp/test
139142
```
140143

144+
`--no-register` keeps the throwaway repo out of `registry.json`.
145+
141146
### Testing the docs site locally
142147

143148
Open `docs/index.html` in a browser. The script falls back to embedded registry data when not served from GitHub Pages.

‎CONTRIBUTING.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,17 @@ python scripts/drift_check/cli.py --local /path/to/tool-repo
157157
--mcp-server \
158158
--skills 2 \
159159
--rules 1 \
160+
--no-register \
160161
--output /tmp/scaffold-test
161162
```
163+
`--no-register` keeps the test repo out of `registry.json`.
162164
3. Verify the generated files look correct
163165
4. Commit with `fix:` or `chore:` prefix
164166

165167
### Adding a new template
166168

167169
1. Create the `.j2` file in `scaffold/templates/`
168-
2. Update `scaffold/create-tool.py` to render it via `write_file()`
170+
2. Render it from `_render_repo()` in `scaffold/generator.py` via `write_file()`
169171
3. Run a test generation and verify the new file appears
170172
4. Update `README.md` "What it generates" section if visible to users
171173
5. Commit with `feat: add <template-name> scaffold template`

‎docs/contributors/adding-a-tool.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ python scaffold/create-tool.py \
2020
--mcp-server \
2121
--skills 6 \
2222
--rules 4 \
23-
--license CC-BY-NC-ND-4.0 \
23+
--license cc-by-nc-nd-4.0 \
2424
--output ../
2525
```
2626

27-
This produces `../Cloudflare-Developer-Tools/` with the full standards-compliant layout.
27+
This produces `../cloudflare-developer-tools/` with the full standards-compliant layout, and (because registration is on by default) adds a schema-valid entry to `registry.json` and regenerates the README, CLAUDE.md, and docs site. Pass `--no-register` only for a throwaway trial run.
2828

2929
## Step 2: Push the new repo
3030

‎scaffold/create-tool.py‎

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,26 @@
3333
)
3434

3535

36+
def _non_negative_int(value: str) -> int:
37+
try:
38+
number = int(value)
39+
except ValueError:
40+
raise argparse.ArgumentTypeError(f"{value!r} is not an integer") from None
41+
if number < 0:
42+
raise argparse.ArgumentTypeError("must be zero or greater")
43+
return number
44+
45+
3646
def parse_args():
3747
parser = argparse.ArgumentParser(
3848
description="Scaffold a new TMHSDigital developer tool repository",
3949
formatter_class=argparse.RawDescriptionHelpFormatter,
4050
epilog="""
4151
Examples:
4252
python create-tool.py --name "Unreal Developer Tools" --description "Cursor plugin for Unreal Engine"
43-
python create-tool.py --name "AWS MCP Server" --type mcp-server --mcp-server
44-
python create-tool.py --name "Hosted Radar" --type mcp-server --deployed-service
45-
python create-tool.py --name "K8s Developer Tools" --mcp-server --skills 5 --rules 3
53+
python create-tool.py --name "AWS MCP Server" --description "MCP server for AWS" --type mcp-server
54+
python create-tool.py --name "Hosted Radar" --description "Hosted MCP service" --type mcp-server --deployed-service
55+
python create-tool.py --name "K8s Developer Tools" --description "Cursor plugin for Kubernetes" --mcp-server --skills 5 --rules 3
4656
python create-tool.py --name "Throwaway" --description test --no-register
4757
""",
4858
)
@@ -55,7 +65,14 @@ def parse_args():
5565
default="cursor-plugin",
5666
help="Repository type (default: cursor-plugin)",
5767
)
58-
parser.add_argument("--mcp-server", action="store_true", help="Include MCP server scaffold")
68+
parser.add_argument(
69+
"--mcp-server",
70+
action="store_true",
71+
help=(
72+
"cursor-plugin only: include a Python MCP server under mcp-server/. "
73+
"--type mcp-server already generates a TypeScript server."
74+
),
75+
)
5976
parser.add_argument(
6077
"--deployed-service",
6178
action="store_true",
@@ -64,8 +81,8 @@ def parse_args():
6481
"the registry npm field; writes .drift-check.json. Requires --type mcp-server."
6582
),
6683
)
67-
parser.add_argument("--skills", type=int, default=0, help="Number of placeholder skill directories to create")
68-
parser.add_argument("--rules", type=int, default=0, help="Number of placeholder rule files to create")
84+
parser.add_argument("--skills", type=_non_negative_int, default=0, help="Number of placeholder skill directories to create")
85+
parser.add_argument("--rules", type=_non_negative_int, default=0, help="Number of placeholder rule files to create")
6986
parser.add_argument(
7087
"--license",
7188
choices=list(LICENSE_FILES.keys()),
@@ -134,7 +151,7 @@ def main() -> int:
134151
print(f"\nDone! Repository scaffolded at: {output_dir}")
135152
print("\nNext steps:")
136153
print(f" 1. cd {output_dir}")
137-
print(" 2. git init && git add -A && git commit -m 'feat: initial scaffold'")
154+
print(' 2. git init && git add -A && git commit -s -m "feat: initial scaffold"')
138155
print(f" 3. Create GitHub repo: gh repo create TMHSDigital/{slug} --public --source .")
139156
print(" 4. Enable GitHub Pages in Settings > Pages > Source: GitHub Actions")
140157
if args.no_register:

0 commit comments

Comments
 (0)