Skip to content

Commit 947ceb3

Browse files
feat: add deployed-service archetype for mcp-servers without npm (#98)
* feat: add deployed-service archetype for mcp-servers without npm Declare the exemption in .drift-check.json so publish.yml and the catalog npm field are optional only when explicit, not inferred. Signed-off-by: fOuttaMyPaint <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix: keep STANDARDS_VERSION as a patch so fleet markers stay green A minor bump fails mode:all on unre-stamped tool repos. The archetype is opt-in and does not require a fleet re-stamp. Signed-off-by: fOuttaMyPaint <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: fOuttaMyPaint <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent dfd8f0b commit 947ceb3

23 files changed

Lines changed: 437 additions & 36 deletions

‎.github/workflows/validate.yml‎

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ jobs:
4444
"pagesType": str,
4545
"hasCI": bool,
4646
}
47-
OPTIONAL = {"npm": str}
47+
OPTIONAL = {"npm": str, "archetype": str}
4848
VALID_TYPES = {"cursor-plugin", "mcp-server"}
4949
VALID_STATUS = {"experimental", "beta", "active", "maintenance", "deprecated", "archived"}
5050
VALID_PAGES = {"static", "mkdocs", "none"}
51+
VALID_ARCHETYPES = {"deployed-service"}
5152
errors = []
5253
for i, tool in enumerate(data):
5354
label = "Entry %d (%s)" % (i, tool.get("name", "?"))
@@ -69,6 +70,19 @@ jobs:
6970
pass
7071
elif not all(isinstance(t, str) for t in tool.get("topics", [])):
7172
errors.append("%s: topics must be array of strings" % label)
73+
archetype = tool.get("archetype")
74+
if "archetype" in tool:
75+
if archetype not in VALID_ARCHETYPES:
76+
errors.append("%s: archetype must be one of %s" % (label, VALID_ARCHETYPES))
77+
if tool.get("type") != "mcp-server":
78+
errors.append("%s: archetype is only valid for mcp-server entries" % label)
79+
if tool.get("type") == "mcp-server":
80+
npm = tool.get("npm")
81+
if archetype == "deployed-service":
82+
if "npm" in tool:
83+
errors.append("%s: deployed-service must omit npm" % label)
84+
elif not (isinstance(npm, str) and npm.strip()):
85+
errors.append("%s: mcp-server entries must have a non-empty npm field" % label)
7286
if errors:
7387
for e in errors:
7488
print("::error::" + e, file=sys.stderr)
@@ -128,6 +142,21 @@ jobs:
128142
test -f /tmp/scaffold-test/ci-test-plugin/mcp-server/server.py
129143
echo "Scaffold test passed"
130144
145+
python3 scaffold/create-tool.py \
146+
--name "CI Deployed Service" \
147+
--description "Automated deployed-service test" \
148+
--type mcp-server \
149+
--deployed-service \
150+
--no-register \
151+
--output /tmp/scaffold-service
152+
153+
test -f /tmp/scaffold-service/ci-deployed-service/.drift-check.json
154+
test ! -f /tmp/scaffold-service/ci-deployed-service/.github/workflows/publish.yml
155+
test -f /tmp/scaffold-service/ci-deployed-service/.github/workflows/release.yml
156+
grep -q '"private": true' /tmp/scaffold-service/ci-deployed-service/package.json
157+
grep -qv 'gh workflow run publish.yml' /tmp/scaffold-service/ci-deployed-service/.github/workflows/release.yml
158+
echo "Deployed-service scaffold test passed"
159+
131160
- name: Scaffold YAML well-formed
132161
run: |
133162
# Catches Jinja2 trim_blocks bugs that collapse adjacent expression lines.

‎README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ flowchart LR
6262
| **Monday Cursor Plugin** | Plugin | 21 | 8 | 45 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/Monday-Cursor-Plugin) [![Docs](https://img.shields.io/badge/docs-7c3aed)](https://tmhsdigital.github.io/Monday-Cursor-Plugin/) |
6363
| **Steam Cursor Plugin** | Plugin | 30 | 9 | 25 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/Steam-Cursor-Plugin) [![Docs](https://img.shields.io/badge/docs-7c3aed)](https://tmhsdigital.github.io/Steam-Cursor-Plugin/) |
6464
| **Steam MCP Server** | MCP Server | - | - | 25 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/steam-mcp) [![npm](https://img.shields.io/badge/npm-cb3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/@tmhs/steam-mcp) |
65-
| **Developer Tools MCP** | MCP Server | - | - | 7 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/Developer-Tools-MCP) [![Docs](https://img.shields.io/badge/docs-7c3aed)](https://tmhsdigital.github.io/Developer-Tools-MCP/) |
66-
| **CFX MCP** | MCP Server | - | - | 0 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/cfx-mcp) |
65+
| **Developer Tools MCP** | MCP Server | - | - | 7 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/Developer-Tools-MCP) [![Docs](https://img.shields.io/badge/docs-7c3aed)](https://tmhsdigital.github.io/Developer-Tools-MCP/) [![npm](https://img.shields.io/badge/npm-cb3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/@tmhs/devtools-mcp) |
66+
| **CFX MCP** | MCP Server | - | - | 0 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/cfx-mcp) [![npm](https://img.shields.io/badge/npm-cb3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/@tmhsdigital/cfx-mcp) |
6767
| **Blender Developer Tools** | Plugin | 12 | 6 | 0 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/Blender-Developer-Tools) |
6868
| **Local AI MCP** | MCP Server | - | - | 16 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/local-ai-mcp) [![Docs](https://img.shields.io/badge/docs-7c3aed)](https://tmhsdigital.github.io/local-ai-mcp/) [![npm](https://img.shields.io/badge/npm-cb3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/@tmhs/local-ai-mcp) |
69-
| **Screencast MCP** | MCP Server | - | - | 25 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/screencast-mcp) [![Docs](https://img.shields.io/badge/docs-7c3aed)](https://tmhsdigital.github.io/screencast-mcp/) |
69+
| **Screencast MCP** | MCP Server | - | - | 25 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/screencast-mcp) [![Docs](https://img.shields.io/badge/docs-7c3aed)](https://tmhsdigital.github.io/screencast-mcp/) [![npm](https://img.shields.io/badge/npm-cb3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/@tmhs/screencast-mcp) |
7070
| **Tailscale MCP** | MCP Server | - | - | 6 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/tailscale-mcp) [![Docs](https://img.shields.io/badge/docs-7c3aed)](https://tmhsdigital.github.io/tailscale-mcp/) [![npm](https://img.shields.io/badge/npm-cb3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/@tmhs/tailscale-mcp) |
7171
| **Godot Correctness MCP** | MCP Server | - | - | 10 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/godot-correctness-mcp) [![Docs](https://img.shields.io/badge/docs-7c3aed)](https://tmhsdigital.github.io/godot-correctness-mcp/) [![npm](https://img.shields.io/badge/npm-cb3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/@tmhs/godot-correctness-mcp) |
7272
| **Bounty Radar MCP** | MCP Server | - | - | 1 | [![Repo](https://img.shields.io/badge/repo-blue?logo=github)](https://github.com/TMHSDigital/bounty-radar-mcp) [![Docs](https://img.shields.io/badge/docs-7c3aed)](https://tmhsdigital.github.io/bounty-radar-mcp/) |

‎STANDARDS_VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.10.0
1+
1.10.1

‎VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.18.0
1+
1.19.0

‎docs/index.html‎

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

‎docs/search-index.json‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎registry.json‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,8 @@
249249
"language": "TypeScript",
250250
"license": "CC-BY-NC-ND-4.0",
251251
"pagesType": "static",
252-
"hasCI": true
252+
"hasCI": true,
253+
"npm": "@tmhs/devtools-mcp"
253254
},
254255
{
255256
"name": "CFX MCP",
@@ -267,7 +268,8 @@
267268
"language": "TypeScript",
268269
"license": "CC-BY-NC-ND-4.0",
269270
"pagesType": "none",
270-
"hasCI": true
271+
"hasCI": true,
272+
"npm": "@tmhsdigital/cfx-mcp"
271273
},
272274
{
273275
"name": "Blender Developer Tools",
@@ -343,7 +345,8 @@
343345
"language": "TypeScript",
344346
"license": "CC-BY-NC-ND-4.0",
345347
"pagesType": "static",
346-
"hasCI": true
348+
"hasCI": true,
349+
"npm": "@tmhs/screencast-mcp"
347350
},
348351
{
349352
"name": "Tailscale MCP",
@@ -417,6 +420,7 @@
417420
"language": "TypeScript",
418421
"license": "CC-BY-NC-ND-4.0",
419422
"pagesType": "static",
420-
"hasCI": true
423+
"hasCI": true,
424+
"archetype": "deployed-service"
421425
}
422426
]

‎scaffold/create-tool.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def parse_args():
4141
Examples:
4242
python create-tool.py --name "Unreal Developer Tools" --description "Cursor plugin for Unreal Engine"
4343
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
4445
python create-tool.py --name "K8s Developer Tools" --mcp-server --skills 5 --rules 3
4546
python create-tool.py --name "Throwaway" --description test --no-register
4647
""",
@@ -55,6 +56,14 @@ def parse_args():
5556
help="Repository type (default: cursor-plugin)",
5657
)
5758
parser.add_argument("--mcp-server", action="store_true", help="Include MCP server scaffold")
59+
parser.add_argument(
60+
"--deployed-service",
61+
action="store_true",
62+
help=(
63+
"mcp-server archetype: deployed HTTP service. Omits publish.yml and "
64+
"the registry npm field; writes .drift-check.json. Requires --type mcp-server."
65+
),
66+
)
5867
parser.add_argument("--skills", type=int, default=0, help="Number of placeholder skill directories to create")
5968
parser.add_argument("--rules", type=int, default=0, help="Number of placeholder rule files to create")
6069
parser.add_argument(
@@ -115,6 +124,7 @@ def main() -> int:
115124
author_email=args.author_email,
116125
register=not args.no_register,
117126
registry_root=Path(args.registry_root) if args.registry_root else None,
127+
deployed_service=args.deployed_service,
118128
)
119129
except ScaffoldError as exc:
120130
print(f"Error: {exc}", file=sys.stderr)

‎scaffold/generator.py‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ def build_context(
128128
license_key: str,
129129
author_name: str,
130130
author_email: str,
131+
deployed_service: bool = False,
131132
) -> dict[str, Any]:
132133
standards_version = read_standards_version()
133134
meta_major, meta_minor, meta_patch = read_meta_version()
@@ -153,6 +154,7 @@ def build_context(
153154
"meta_patch": meta_patch,
154155
"meta_version": f"{meta_major}.{meta_minor}.{meta_patch}",
155156
"year": datetime.datetime.now(datetime.timezone.utc).year,
157+
"deployed_service": deployed_service,
156158
}
157159

158160

@@ -191,7 +193,15 @@ def _render_repo(output_dir: Path, ctx: dict, *, verbose: bool) -> None:
191193
# plus the two optional-for-both workflows (pages, label-sync). mcp-server
192194
# repos do NOT get cursor-plugin-specific validate.yml / release.yml.
193195
if repo_type == "mcp-server":
194-
write_file(output_dir, ".github/workflows/publish.yml", render_template(env, "publish.yml.j2", ctx), verbose=verbose)
196+
if ctx.get("deployed_service"):
197+
write_file(
198+
output_dir,
199+
".drift-check.json",
200+
json.dumps({"archetype": "deployed-service"}, indent=2) + "\n",
201+
verbose=verbose,
202+
)
203+
else:
204+
write_file(output_dir, ".github/workflows/publish.yml", render_template(env, "publish.yml.j2", ctx), verbose=verbose)
195205
write_file(output_dir, ".github/workflows/ci.yml", render_template(env, "ci.yml.j2", ctx), verbose=verbose)
196206
write_file(output_dir, ".github/workflows/release.yml", render_template(env, "release.mcp.yml.j2", ctx), verbose=verbose)
197207
write_file(output_dir, ".github/workflows/pages.yml", render_template(env, "pages.mcp.yml.j2", ctx), verbose=verbose)
@@ -279,6 +289,7 @@ def build_registry_entry(
279289
skill_count: int,
280290
rule_count: int,
281291
license_key: str,
292+
deployed_service: bool = False,
282293
) -> dict[str, Any]:
283294
"""Build a schema-valid registry.json entry for a generated repo.
284295
@@ -287,7 +298,7 @@ def build_registry_entry(
287298
a fresh repo has no MCP tools yet so ``mcpTools`` is 0.
288299
"""
289300
homepage = f"https://{REPO_OWNER.lower()}.github.io/{slug}/"
290-
return {
301+
entry: dict[str, Any] = {
291302
"name": name,
292303
"repo": f"{REPO_OWNER}/{slug}",
293304
"slug": slug,
@@ -305,6 +316,12 @@ def build_registry_entry(
305316
"pagesType": "static",
306317
"hasCI": True,
307318
}
319+
if repo_type == "mcp-server":
320+
if deployed_service:
321+
entry["archetype"] = "deployed-service"
322+
else:
323+
entry["npm"] = f"@tmhs/{slug}"
324+
return entry
308325

309326

310327
def register_in_registry(
@@ -375,6 +392,7 @@ def generate_repo(
375392
register: bool = True,
376393
registry_root: Optional[Path] = None,
377394
verbose: bool = True,
395+
deployed_service: bool = False,
378396
) -> Path:
379397
"""Render a complete, standards-compliant repo and (by default) register
380398
it in the meta catalog. Returns the path to the generated repo.
@@ -385,6 +403,8 @@ def generate_repo(
385403
"""
386404
if repo_type not in ("cursor-plugin", "mcp-server"):
387405
raise ScaffoldError(f"unknown repo type: {repo_type!r}")
406+
if deployed_service and repo_type != "mcp-server":
407+
raise ScaffoldError("--deployed-service requires --type mcp-server")
388408
if license_key not in LICENSE_FILES:
389409
raise ScaffoldError(f"unknown license: {license_key!r}")
390410

@@ -410,6 +430,7 @@ def generate_repo(
410430
license_key=license_key,
411431
author_name=author_name,
412432
author_email=author_email,
433+
deployed_service=deployed_service,
413434
)
414435

415436
if verbose:
@@ -432,6 +453,7 @@ def generate_repo(
432453
skill_count=len(skill_names),
433454
rule_count=len(rule_names),
434455
license_key=license_key,
456+
deployed_service=deployed_service,
435457
)
436458
register_in_registry(Path(root), entry, run_sync=True, verbose=verbose)
437459

‎scaffold/templates/README.md.j2‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,14 @@ pip install -r requirements.txt
5454

5555
## Installation
5656

57+
{% if deployed_service %}
58+
This is a deployed HTTP service, not an npm package. Run it from source or a container; there is no `npm install`.
59+
{% else %}
5760
```bash
5861
npm install {{ slug }}
5962
```
6063
{% endif %}
64+
{% endif %}
6165

6266
## Project Structure
6367

0 commit comments

Comments
 (0)