@karlbur/kick
v0.2.2
Published
CLI scaffolding tool for LLM-Konnector MCP servers and host servers
Downloads
340
Readme
@karlbur/kick — Project Generator
A zero-dependency CLI that scaffolds new MCP server and konnector host
server packages from battle-tested templates. It reads .tpl files under
templates/, substitutes {{PLACEHOLDER}} tokens, and writes the result to
your chosen target directory.
Reference: The templates are derived from
samples/oxb/— the canonical working sample in this monorepo.
Table of Contents
- Quick Start
- Installation
- Commands
- Options Reference
- Name Validation Rules
- Dependency Modes
- Generated Project Structure
- Placeholder Variables
- Post-Generation Steps
- Recipes
- Architecture
Quick Start
# Generate an MCP server (inside monorepo)
pnpm run gen mcp acme
# Generate a host server
pnpm run gen server acme
# Add a single tool/prompt/resource to an existing MCP server (run in its dir)
pnpm run gen mcp-t tool get-orders
# Shorthand — first positional arg defaults to "mcp"
pnpm run gen acmeEach command prints a summary of created files and next-step instructions.
Installation
Inside the monorepo
No installation needed — tsx is already a root devDependency. The root
package.json exposes these scripts:
{
"gen": "tsx tools/scaffold/cli.ts", // unified entry
"gen:mcp": "tsx tools/scaffold/gen-mcp.ts", // direct MCP generator
"gen:server": "tsx tools/scaffold/gen-server.ts", // direct server generator
}Outside the monorepo (published package)
npx @karlbur/kick mcp acme
# or
npx @karlbur/kick server acmeThe published package exposes three binary names:
| Binary | Entry point |
| ---------------------- | ----------- |
| kick | ./cli.ts |
| llm-konnect-scaffold | ./cli.ts |
| lk-scaffold | ./cli.ts |
kickmatches the package name, sonpx @karlbur/kick mcp acmeauto-detects it without needing--packageflags.
Commands
The unified CLI (cli.ts) supports both subcommand and shorthand syntax:
npx @karlbur/kick <subcommand> <name> [options]
pnpm run gen <subcommand> <name> [options]scaffold mcp <name>
Generates a complete MCP server package at <target>/mcp-<name>/.
pnpm run gen mcp acmeMCP-specific option:
| Flag | Default | Description |
| ----------- | ----------------------- | -------------------------------------------------------------- |
| --api-url | http://localhost:3201 | Upstream API URL (written to .env.example as AUTH_API_URL) |
See Options Reference for all shared flags.
scaffold server <name>
Generates a konnector host server at <target>/server-<name>/.
pnpm run gen server acmeServer-specific options:
| Flag | Default | Description |
| ----------- | ----------------------- | ------------------------------------------------------------------- |
| --mcp-url | http://localhost:3101 | MCP server URL (written to .env.example as MCP_SERVERS) |
| --api-url | http://localhost:3201 | Upstream auth API URL (written to .env.example as AUTH_API_URL) |
scaffold mcp-t <type> <name> [--kind term|formula]
Generates a single tool, prompt, or resource file inside an existing MCP
server project. Run this from the MCP server's root directory (where
package.json lives). The brand name is auto-detected from package.json.
# Must be inside an MCP server directory (mcp-*/):
pnpm run gen mcp-t tool get-orders # → src/tools/get-orders.ts
pnpm run gen mcp-t prompt comparison # → src/prompts/comparison.ts
pnpm run gen mcp-t resource guide-deposit # → src/resources/guide-deposit.md (term)
pnpm run gen mcp-t resource promo-cashback --kind formula # → src/resources/promo-cashback.md (formula)Arguments:
| Argument | Description |
| -------- | ------------------------------------------------------------------------- |
| <type> | One of: tool, prompt, resource |
| <name> | Item name (lowercase letters, numbers, hyphens; must start with a letter) |
| --kind | (resource only) term (default) or formula |
Generated file locations:
| Type | Output path |
| ---------- | ------------------------- |
| tool | src/tools/<name>.ts |
| prompt | src/prompts/<name>.ts |
| resource | src/resources/<name>.md |
The generated tool template includes a standalone handler, a ToolDefinition
array, and TODO placeholders. After generation, you need to manually add
the export to the relevant index.ts aggregator (the CLI prints a reminder).
Resource Kinds: Term vs Formula
The --kind flag (resource type only) selects between two structured
templates:
| Kind | Purpose | Template sections |
| --------- | --------------------------------------------------- | ------------------------------------------------------- |
| term | Policies, guidelines, rules (default) | Rules & Conditions, Important Notes, Terms & Conditions |
| formula | Promotions, commissions, rank benefits (calculable) | Eligibility, Formula, Period & Boundary Rules, Examples |
Why two kinds? Term resources answer "what does the policy say?". Formula resources answer "how much do I get?" — the template forces the author to define boundaries, caps, and worked examples so the LLM can calculate edge cases (cross-period, cap interactions) accurately.
Brand detection: The CLI reads
package.jsonand handles both naming patterns —mcp-<brand>and<brand>-mcp-server— to derive the brand prefix for tool/prompt names (e.g.tool_acme__get_orders).
Shorthand Mode
If the first argument isn't mcp or server, the CLI treats it as the project
name and defaults to MCP generation:
# These are equivalent:
pnpm run gen acme
pnpm run gen mcp acme
# For a host server, you must specify the subcommand:
pnpm run gen server acmeInteractive Mode
In an interactive terminal, any argument you don't pass as a flag is prompted sequentially — press Enter to accept the default. This lets you provide just the name and accept defaults for the rest, or mix CLI flags with prompts:
# Provide name, prompt for everything else:
pnpm run gen mcp acme
# Provide name + port, prompt for the rest:
pnpm run gen server acme --port 8080
# Prompt for everything (including name):
pnpm run gen mcp
pnpm run gen serverExample — pnpm run gen server acme (name provided, rest prompted):
╔═══════════════════════════════════════════════════════════╗
║ Host Server Generator — interactive mode ║
║ Press Enter to accept the [default] shown in parentheses ║
╚═══════════════════════════════════════════════════════════╝
target (default: .): ↵
dep-mode (default: latest): ↵
dep-version (default: 0.1.0): ↵
scope: ↵
display-name: ↵
port (default: 3001): ↵
mcp-url (default: http://localhost:3101): ↵
api-url (default: http://localhost:3201): ↵
force (overwrite if exists) (default: false): ↵Note:
namewas not prompted — it was provided positionally. Andportwould be skipped if you passed--port 8080.
Behavior details:
- Fields passed as CLI flags (or positionally for
name) are skipped — only unprompted fields are asked. namehas no default and is always required; if omitted positionally, it's the first prompt. Invalid names are re-prompted with the validation error.dep-modeandforcevalidate against their allowed values.portmust be a number between 1–65535.Ctrl+C/Ctrl+Daborts gracefully.- In a non-TTY environment (CI, piped stdin), the CLI uses flags + built-in
defaults without prompting. If
nameis missing in non-TTY, it errors.
Help & Version
pnpm run gen --help # Show full help
pnpm run gen --version # Print versionOptions Reference
These flags apply to both mcp and server commands:
| Flag | Default (MCP / Server) | Description |
| --------------------- | ---------------------- | ------------------------------------------------------------------------------------ |
| <name> | (required) | Brand/service name. Directory = mcp-<name> or server-<name>. |
| --target <path> | . | Output directory where the package folder is created (default = current dir). |
| --dep-mode <mode> | latest | How @karlbur/* packages are referenced. See Dependency Modes. |
| --dep-version <ver> | 0.1.0 | Version string when --dep-mode=version. |
| --scope <@org> | (none) | npm scope for package name (e.g. @acme). If omitted, package is unscoped. |
| --display-name <n> | (PascalCase of name) | Human-readable name used in package.json, server config, .env.example. |
| --port <number> | 3101 / 3001 | Default port in generated .env.example. |
| --force | false | Overwrite existing files. Does not delete extra files you added. |
--target Details
| Command | Default target | Example output path |
| -------- | -------------- | ------------------- |
| mcp | . | ./mcp-acme/ |
| server | . | ./server-acme/ |
Override with any relative path:
pnpm run gen mcp acme --target packages/
# → packages/mcp-acme/Argument Syntax
The parser supports three forms:
--flag value # space-separated
--flag=value # equals sign
--flag # boolean (sets value to 'true')Name Validation Rules
The <name> argument must satisfy all of the following:
| Rule | Valid examples | Invalid examples |
| ------------------------------------- | -------------- | ------------------------------------------- |
| Lowercase letters + numbers + hyphens | acme, vip2 | Acme, VIP |
| Must start with a letter | acme2 | 2acme, 1brand |
| No leading or trailing hyphens | my-brand | -acme, acme- |
| Max 40 characters | acme | (41+ character string) |
| Not a reserved name | — | oxb, shared, mcp-factory, konnector |
Regex: /^[a-z][a-z0-9-]{0,39}$/
Reserved names: oxb, shared, mcp-factory, konnector
Dependency Modes
Controls how @karlbur/shared, @karlbur/mcp-factory, and
@karlbur/konnector appear in the generated package.json:
| --dep-mode | Dependency value | When to use |
| ------------ | ------------------------ | ---------------------------------------------------- |
| workspace | workspace:* | Inside this monorepo as a pnpm workspace member |
| link | link:../packages/<pkg> | Monorepo checkout, but not a pnpm workspace member |
| version | ^<dep-version> | Published packages from a registry (GitHub Packages) |
| latest | latest | Always pull newest published version (default) |
Effect on tsconfig.json
| Mode | tsconfig.json behavior |
| ------------------- | ----------------------------------------------------------------- |
| workspace, link | Extends ../../../tsconfig.base.json (monorepo template) |
| version, latest | Standalone compilerOptions — no extends (standalone template) |
Generated Project Structure
Generated MCP Server
<target>/mcp-<name>/
├── package.json # @karlbur/shared + mcp-factory deps
├── tsconfig.json # monorepo or standalone (based on --dep-mode)
├── .env.example # MCP_PORT, AUTH_API_URL
└── src/
├── index.ts # Re-exports: tools[], resources[], types
├── main.ts # bootServer({ id, name, tools, resources }, port)
├── type.ts # Brand response types (placeholder)
├── tools/
│ ├── index.ts # Aggregates all tool arrays
│ └── get-example.ts # Example tool with variant-block transforms
├── resources/
│ ├── index.ts # ResourceDefinition[] with readMd helper
│ └── example.md # Placeholder resource document
└── prompts/
└── index.ts # prompt_ guide tools (placeholder)The generated tool template follows the variant blocks pattern — handlers
use transform helpers (toCards(), toTable(), etc.) to produce pre-shaped
VariantContent[] for the frontend.
Generated Host Server
<target>/server-<name>/
├── package.json # @karlbur/shared + konnector deps
├── tsconfig.json # monorepo or standalone
├── .env.example # PORT, LLM_*, MCP_SERVERS, AUTH_API_URL, etc.
└── src/
├── main.ts # createKonnector({...}) + Express app + listen
└── routes/
└── auth.routes.ts # Brand-specific auth proxy template (/login, /logout, /refresh)Placeholder Variables
All {{KEY}} tokens in .tpl files are replaced at generation time:
| Key | Example (gen mcp acme) | Description |
| -------------------- | ----------------------------- | ----------------------------------------------------------------------- |
| NAME | acme | Lowercase name as provided |
| NAME_PASCAL | Acme | PascalCase transform (handles hyphens: my-brand → MyBrand) |
| DISPLAY_NAME | Acme | Human-readable (defaults to PascalCase; override with --display-name) |
| SCOPE_PREFIX | @acme/ or (empty) | npm scope + /, or empty string |
| DEP_SHARED | workspace:* or ^0.1.0 | Dependency spec for @karlbur/shared |
| DEP_FACTORY | workspace:* or ^0.1.0 | Dependency spec for @karlbur/mcp-factory |
| DEP_KONNECTOR | workspace:* or ^0.1.0 | Dependency spec for @karlbur/konnector (server template only) |
| PORT | 3101 / 3001 | Port number |
| API_URL | http://localhost:3201 | Upstream API URL |
| MCP_URL | http://localhost:3101 | MCP server URL (server template only) |
| TSCONFIG_BASE_PATH | ../../../tsconfig.base.json | Relative path to base tsconfig (monorepo modes only) |
Post-Generation Steps
After generating an MCP server
# Standalone (latest/version mode — default):
cd mcp-acme
npm install
npm run dev
# Monorepo (workspace mode):
pnpm install # link the new workspace package
pnpm --filter mcp-acme run dev # start dev server on port 3101After generating a host server
# Copy and edit env file first:
cd server-acme
cp .env.example .env
# Edit .env: set LLM_API_KEY, MCP_SERVERS, AUTH_API_URL, etc.
# Standalone (latest/version mode — default):
npm install
npm run dev
# Monorepo (workspace mode):
pnpm install
pnpm --filter server-acme run dev # start on port 3001Recipes
Generate a standalone project (default — latest published deps)
npx @karlbur/kick mcp acme
npx @karlbur/kick server acme
cd mcp-acme && npm installGenerate inside the monorepo (workspace mode)
pnpm run gen mcp acme --dep-mode workspace
pnpm run gen server acme --dep-mode workspace
pnpm installGenerate with specific published versions
npx @karlbur/kick mcp acme --dep-mode version --dep-version 0.2.0 --scope @acme
npx @karlbur/kick server acme --dep-mode version --dep-version 0.2.0 --scope @acmeCustom target directory
pnpm run gen mcp billing --target packages/
# → packages/mcp-billing/Custom ports and URLs
pnpm run gen mcp acme --port 3200 --api-url https://api.acme.com
pnpm run gen server acme --port 8080 --mcp-url http://localhost:3200Custom display name
pnpm run gen mcp acme --display-name "ACME Corporation"Overwrite existing files
pnpm run gen mcp acme --force
--forceoverwrites individual template files but does not delete extra files you may have added after the initial scaffold.
Architecture
Source Layout
tools/scaffold/
├── cli.ts # Unified CLI entry — subcommands + shorthand
├── gen-mcp.ts # MCP server generator (delegated from cli.ts)
├── gen-server.ts # Host server generator (delegated from cli.ts)
├── gen-mcp-t.ts # Single item generator (tool/prompt/resource)
├── args.ts # Minimal --flag parser (no external deps)
├── prompt.ts # Interactive readline prompts (TTY-only, no deps)
├── fs-utils.ts # File ops: mkdirp, writeFile, copyFile, printSummary
├── placeholders.ts # {{KEY}} substitution, name transforms, validation
├── package.json # Package metadata + bin entries
└── templates/
├── mcp/ # MCP server .tpl files
└── server/ # Host server .tpl filesDesign Principles
- Zero external dependencies — Uses only Node.js built-ins (
node:fs,node:path,node:url). Nocommander,yargs, orinquirer. - Template-driven — All generated code lives in
.tplfiles with{{PLACEHOLDER}}tokens. The substitution engine is a single regex. - Safe by default — Won't overwrite existing files unless
--forceis passed. - Informative output — Prints a summary of created/skipped files and context-aware next-step instructions (monorepo vs standalone).
CLI Dispatch Flow
cli.ts
├── argv[0] === 'mcp' → runGen('mcp', rest) → import gen-mcp.ts
├── argv[0] === 'server' → runGen('server', rest) → import gen-server.ts
├── argv[0] === 'mcp-t' → override argv + import gen-mcp-t.ts
├── argv[0] === <name> → runGen('mcp', argv) → import gen-mcp.ts
└── --help / --version → print and exitrunGen() overrides process.argv and dynamically imports the target script,
which reads process.argv.slice(2) itself. The mcp-t subcommand similarly
overrides process.argv and dynamically imports gen-mcp-t.ts.
