agent-install-templates
npx machina-cli add skill phazurlabs/install-labs/agent-install-templates --openclawAI Agent Install Templates
Production-ready templates for packaging AI agents and automations as installable software. Every template is complete, annotated, and ready to copy into a project.
1. MCP Server (Node.js / TypeScript)
package.json
{
"name": "@my-org/my-agent-mcp",
"version": "0.1.0",
"description": "MCP server that exposes my-agent capabilities as tools",
"license": "MIT",
"author": "my-org",
"type": "module",
"bin": {
"my-agent-mcp": "./dist/index.js"
},
"main": "./dist/index.js",
"files": [
"dist"
],
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"start": "node dist/index.js",
"lint": "eslint src/",
"prepublishOnly": "npm run build"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.0",
"zod": "^3.23.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.7.0",
"eslint": "^9.0.0"
},
"engines": {
"node": ">=18.0.0"
},
"keywords": [
"mcp",
"model-context-protocol",
"ai-agent"
]
}
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
src/index.ts
#!/usr/bin/env node
// MCP Server skeleton — registers tools that an LLM client can invoke.
// The MCP SDK handles stdio transport, JSON-RPC framing, and capability negotiation.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// --- Server instance ---------------------------------------------------
const server = new McpServer({
name: "my-agent-mcp", // Appears in client UI (Claude Desktop, etc.)
version: "0.1.0", // Keep in sync with package.json
});
// --- Tool: greet -------------------------------------------------------
// Replace this with your agent's real capability.
// Each tool gets a name, description (shown to the LLM), input schema, and handler.
server.tool(
"greet", // Tool name (lowercase, hyphen-separated)
"Generate a greeting for the given name", // LLM-facing description
{
name: z.string().describe("Name to greet"), // Zod schema = JSON Schema for the LLM
},
async ({ name }) => {
// Your agent logic goes here.
// Return content as an array of text/image/resource blocks.
return {
content: [
{ type: "text", text: `Hello, ${name}! Welcome to my-agent.` },
],
};
}
);
// --- Tool: analyze (example with structured output) --------------------
server.tool(
"analyze",
"Analyze the provided text and return key insights",
{
text: z.string().describe("Text to analyze"),
depth: z.enum(["quick", "thorough"]).default("quick").describe("Analysis depth"),
},
async ({ text, depth }) => {
// Replace with your real analysis logic.
const wordCount = text.split(/\s+/).length;
const result = {
wordCount,
depth,
summary: `Analyzed ${wordCount} words at ${depth} depth.`,
};
return {
content: [
{ type: "text", text: JSON.stringify(result, null, 2) },
],
};
}
);
// --- Start server ------------------------------------------------------
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
// Server is now listening on stdin/stdout. The MCP client drives the conversation.
}
main().catch((error) => {
console.error("Fatal:", error);
process.exit(1);
});
.github/workflows/publish.yml
# Publishes to npm on every GitHub Release (tag v*)
name: Publish to npm
on:
release:
types: [published]
permissions:
contents: read
id-token: write # Required for npm provenance
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"
- run: npm ci
- run: npm run build
- run: npm run lint
# --provenance attaches a build attestation so users can verify origin
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Customize: Change @my-org/my-agent-mcp to your package name. Replace the greet and analyze tools with your agent's real capabilities. Update version, description, author, and keywords.
2. MCP Server (Python)
pyproject.toml
[project]
name = "my-agent-mcp"
version = "0.1.0"
description = "MCP server exposing my-agent capabilities as tools"
readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.11"
authors = [{ name = "my-org" }]
keywords = ["mcp", "model-context-protocol", "ai-agent"]
dependencies = [
"mcp[cli]>=1.9.0",
"httpx>=0.27.0",
]
[project.scripts]
# Entry point for `uvx my-agent-mcp` or `pip install && my-agent-mcp`
my-agent-mcp = "my_agent_mcp.server:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/my_agent_mcp"]
src/my_agent_mcp/server.py
"""
MCP Server for my-agent.
Run directly: python -m my_agent_mcp.server
Run via uvx: uvx my-agent-mcp
Run via mcp: mcp run my-agent-mcp
"""
from mcp.server.fastmcp import FastMCP
# --- Server instance ---------------------------------------------------
# The name appears in client UI (Claude Desktop, etc.)
server = FastMCP("my-agent-mcp")
# --- Tool: greet -------------------------------------------------------
# Replace with your agent's real capability.
# The docstring becomes the LLM-facing description.
# Type hints become the JSON Schema for the LLM.
@server.tool()
def greet(name: str) -> str:
"""Generate a greeting for the given name."""
return f"Hello, {name}! Welcome to my-agent."
# --- Tool: analyze -----------------------------------------------------
@server.tool()
def analyze(text: str, depth: str = "quick") -> str:
"""Analyze the provided text and return key insights.
Args:
text: Text to analyze.
depth: Analysis depth — "quick" or "thorough".
"""
word_count = len(text.split())
return f"Analyzed {word_count} words at {depth} depth."
# --- Entrypoint --------------------------------------------------------
def main():
server.run(transport="stdio")
if __name__ == "__main__":
main()
src/my_agent_mcp/init.py
"""my-agent MCP server package."""
Customize: Change my-agent-mcp and my_agent_mcp to your package/module name. Replace greet and analyze with your tools. Add dependencies to pyproject.toml.
3. Claude Code Plugin
plugin.json
{
"name": "My Agent Plugin",
"version": "1.0.0",
"description": "One-sentence description of what this plugin does for the user",
"author": "my-org",
"skills": [
"skills/core-capability/SKILL.md"
],
"commands": [
"commands/run-agent/command.md"
]
}
marketplace.json
{
"slug": "my-agent-plugin",
"display_name": "My Agent Plugin",
"tagline": "One-liner shown in marketplace search results",
"category": "ai-agents",
"tags": ["agent", "automation"],
"icon": "icon.png",
"repository": "https://github.com/my-org/my-agent-plugin",
"install_command": "git clone https://github.com/my-org/my-agent-plugin.git ~/.claude/plugins/my-agent-plugin"
}
skills/core-capability/SKILL.md
---
name: core-capability
description: "Description of when this skill activates. Use when the user mentions: keyword1, keyword2, keyword3"
---
# Core Capability
## Context
Describe the domain knowledge this skill provides.
## Guidance
- Bullet point instructions for Claude when this skill is active.
- Reference specific frameworks, patterns, or rules.
- Keep guidance actionable — every line should change Claude's behavior.
## Reference
Key facts, tables, or data Claude needs to apply this skill.
commands/run-agent/command.md
---
name: run-agent
description: "Run the agent with the user's input"
---
# /run-agent
## Steps
1. Collect the user's input or goal.
2. Validate any required configuration (API keys, model selection).
3. Execute the agent's core capability.
4. Return structured results.
## Output Format
- Summary of what the agent did.
- Key results or artifacts produced.
- Suggested next steps.
CLAUDE.md (plugin root)
# My Agent Plugin (v1.0.0)
Installed as a Claude Code plugin at `~/.claude/plugins/my-agent-plugin/`.
Skills are auto-invoked by the plugin system based on context.
Commands are user-invocable via `/command-name`.
## Available Skills
- **core-capability** — Description of what it does
## Available Commands
`/run-agent` — Description of what it does
Customize: Replace all instances of my-agent-plugin, my-org, core-capability, and run-agent. Add more skills and commands as needed. The CLAUDE.md is what appears in the host project's instructions.
4. Docker AI Agent (Python)
Dockerfile
# --- Stage 1: Build dependencies ---
FROM python:3.12-slim AS builder
WORKDIR /app
# Install build tools only in the builder stage
RUN pip install --no-cache-dir uv
COPY pyproject.toml uv.lock ./
RUN uv pip install --system --no-cache -r pyproject.toml
# --- Stage 2: Runtime ---
FROM python:3.12-slim AS runtime
WORKDIR /app
# Copy installed packages from builder
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
# Copy application code last (cache-friendly layer ordering)
COPY src/ ./src/
# Non-root user for security
RUN useradd --create-home agent
USER agent
# Health check endpoint (see src/health.py)
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')"
EXPOSE 8080
CMD ["python", "-m", "src.main"]
docker-compose.yml
services:
agent:
build: .
ports:
- "8080:8080"
env_file: .env # Load secrets from .env (never committed)
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: agent_db
POSTGRES_USER: agent
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme} # Override in .env
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U agent"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
volumes:
pgdata:
.env.example
# --- Required: AI Provider ---
ANTHROPIC_API_KEY=sk-ant-... # Get one at https://console.anthropic.com/
# OPENAI_API_KEY=sk-... # Optional fallback provider
# --- Required: Database ---
POSTGRES_PASSWORD=changeme # CHANGE THIS in production
DATABASE_URL=postgresql://agent:${POSTGRES_PASSWORD}@postgres:5432/agent_db
# --- Optional ---
REDIS_URL=redis://redis:6379/0
LOG_LEVEL=info # debug | info | warning | error
AGENT_MODEL=claude-sonnet-4-20250514 # Model to use for agent reasoning
Customize: Change the Python version if needed. Replace src.main with your actual module. Add agent-specific environment variables to .env.example. Adjust Postgres/Redis config for your data model.
5. Docker AI Agent (Node.js)
Dockerfile
# --- Stage 1: Install dependencies ---
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
# --- Stage 2: Runtime ---
FROM node:22-alpine AS runtime
WORKDIR /app
# Copy node_modules from builder
COPY --from=builder /app/node_modules ./node_modules
# Copy application code
COPY src/ ./src/
COPY package.json ./
# Non-root user
RUN adduser -D agent
USER agent
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1
EXPOSE 8080
CMD ["node", "src/index.js"]
docker-compose.yml
services:
agent:
build: .
ports:
- "8080:8080"
env_file: .env
depends_on:
redis:
condition: service_healthy
restart: unless-stopped
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
.env.example
# --- Required: AI Provider ---
ANTHROPIC_API_KEY=sk-ant-... # Get one at https://console.anthropic.com/
# --- Optional ---
REDIS_URL=redis://redis:6379/0
LOG_LEVEL=info
PORT=8080
AGENT_MODEL=claude-sonnet-4-20250514
Customize: Change Node.js version if needed. Replace src/index.js with your entry point. Add a Postgres service if your agent needs persistent storage.
6. Python CLI Agent
pyproject.toml
[project]
name = "my-agent-cli"
version = "0.1.0"
description = "CLI agent that does X"
readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.11"
authors = [{ name = "my-org" }]
dependencies = [
"click>=8.1.0",
"anthropic>=0.42.0",
"rich>=13.0.0", # Pretty terminal output
"python-dotenv>=1.0.0", # .env file support
]
[project.scripts]
# This creates the CLI command when installed via pip/pipx
my-agent = "my_agent_cli.cli:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/my_agent_cli"]
src/my_agent_cli/cli.py
"""
CLI entry point for my-agent.
Install: pipx install my-agent-cli
Run: my-agent ask "What is the capital of France?"
"""
import os
import sys
import click
from dotenv import load_dotenv
load_dotenv() # Load .env file if present
@click.group()
@click.version_option()
def main():
"""My Agent CLI — one-line description of what it does."""
pass
@main.command()
@click.argument("prompt")
@click.option("--model", default="claude-sonnet-4-20250514", help="Model to use.")
def ask(prompt: str, model: str):
"""Send a prompt to the agent and print the response."""
api_key = os.environ.get("ANTHROPIC_API_KEY")
if not api_key:
click.echo(
"Error: ANTHROPIC_API_KEY not set.\n"
"Get one at: https://console.anthropic.com/\n"
"Then run: export ANTHROPIC_API_KEY=sk-ant-...",
err=True,
)
sys.exit(1)
# Lazy import: heavy deps load only when the command actually runs.
# This keeps `my-agent --help` and `my-agent --version` fast.
from anthropic import Anthropic
client = Anthropic(api_key=api_key)
response = client.messages.create(
model=model,
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
# Rich output for terminals, plain text for pipes
if sys.stdout.isatty():
from rich.console import Console
from rich.markdown import Markdown
console = Console()
console.print(Markdown(response.content[0].text))
else:
click.echo(response.content[0].text)
@main.command()
def config():
"""Show current configuration and check connectivity."""
api_key = os.environ.get("ANTHROPIC_API_KEY")
status = "set" if api_key else "MISSING"
click.echo(f"ANTHROPIC_API_KEY: {status}")
click.echo(f"Config file: ~/.my-agent/config.toml")
src/my_agent_cli/init.py
"""my-agent CLI package."""
Customize: Change my-agent-cli, my_agent_cli, and my-agent to your names. Replace the ask command with your agent's capabilities. Add more commands under @main.command().
7. LangGraph Deployment
langgraph.json
{
"dependencies": ["."],
"graphs": {
"my_agent": "./src/my_agent/graph.py:graph"
},
"env": ".env"
}
pyproject.toml
[project]
name = "my-agent-langgraph"
version = "0.1.0"
description = "LangGraph agent that does X"
requires-python = ">=3.11"
dependencies = [
"langgraph>=0.3.0",
"langchain-anthropic>=0.3.0",
"langchain-core>=0.3.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/my_agent"]
src/my_agent/graph.py
"""
LangGraph agent graph definition.
This file defines the agent as a state machine. Each node is a function,
edges define transitions, and the state schema flows through the graph.
"""
from typing import Annotated, TypedDict
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import BaseMessage
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages
# --- State schema -------------------------------------------------------
class AgentState(TypedDict):
"""State that flows through the graph. `messages` accumulates the conversation."""
messages: Annotated[list[BaseMessage], add_messages]
# --- Nodes --------------------------------------------------------------
model = ChatAnthropic(model="claude-sonnet-4-20250514")
def call_model(state: AgentState) -> dict:
"""Invoke the LLM with the current conversation history."""
response = model.invoke(state["messages"])
return {"messages": [response]}
def should_continue(state: AgentState) -> str:
"""Route to 'end' or back to the model based on the last message."""
last = state["messages"][-1]
# If the model made tool calls, continue the loop; otherwise finish.
if hasattr(last, "tool_calls") and last.tool_calls:
return "continue"
return "end"
# --- Graph assembly -----------------------------------------------------
workflow = StateGraph(AgentState)
workflow.add_node("agent", call_model)
workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue, {
"continue": "agent",
"end": END,
})
# Compile the graph — this is the object LangGraph Platform serves.
graph = workflow.compile()
Dockerfile (for self-hosted LangGraph deployment)
FROM python:3.12-slim
WORKDIR /app
RUN pip install --no-cache-dir uv
COPY pyproject.toml ./
RUN uv pip install --system --no-cache -r pyproject.toml
COPY src/ ./src/
COPY langgraph.json ./
EXPOSE 8000
CMD ["langgraph", "up", "--host", "0.0.0.0", "--port", "8000"]
Customize: Rename my_agent and my-agent-langgraph. Add tool nodes between call_model and should_continue for agentic tool use. Extend AgentState with agent-specific fields.
8. Multi-Agent Docker Compose
docker-compose.yml
# Multi-agent system: coordinator dispatches tasks to specialized workers.
# Shared state via Redis (ephemeral) and Postgres (persistent).
services:
# --- Coordinator: routes tasks to the right worker ---
coordinator:
build:
context: .
dockerfile: agents/coordinator/Dockerfile
ports:
- "8080:8080"
env_file: .env
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
environment:
AGENT_ROLE: coordinator
WORKER_URLS: "http://researcher:8081,http://writer:8082"
restart: unless-stopped
# --- Worker: research agent ---
researcher:
build:
context: .
dockerfile: agents/researcher/Dockerfile
env_file: .env
environment:
AGENT_ROLE: researcher
PORT: 8081
depends_on:
redis:
condition: service_healthy
restart: unless-stopped
# --- Worker: writing agent ---
writer:
build:
context: .
dockerfile: agents/writer/Dockerfile
env_file: .env
environment:
AGENT_ROLE: writer
PORT: 8082
depends_on:
redis:
condition: service_healthy
restart: unless-stopped
# --- Shared state: Redis (task queue, ephemeral cache) ---
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
volumes:
- redis_data:/data
# --- Shared state: Postgres (persistent results, audit log) ---
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: agents_db
POSTGRES_USER: agents
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U agents"]
interval: 5s
timeout: 3s
retries: 5
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
redis_data:
pgdata:
.env.example
# --- Required: AI Provider (shared by all agents) ---
ANTHROPIC_API_KEY=sk-ant-...
# --- Required: Database ---
POSTGRES_PASSWORD=changeme
# --- Optional ---
LOG_LEVEL=info
COORDINATOR_MODEL=claude-sonnet-4-20250514
WORKER_MODEL=claude-haiku-4-20250514 # Cheaper model for workers
Customize: Add or remove worker services. Change WORKER_URLS in the coordinator to match. Each agent gets its own Dockerfile under agents/<name>/Dockerfile sharing the same base image pattern from Template 4.
9. GitHub Release Action for Agent Binaries
.github/workflows/release.yml
# Build and publish agent binaries for macOS, Linux, and Windows.
# Triggered by pushing a tag like v1.0.0.
name: Release Agent Binaries
on:
push:
tags: ["v*"]
permissions:
contents: write # Required to create GitHub Releases
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-linux
artifact: my-agent-linux-amd64
- os: macos-latest
target: aarch64-darwin
artifact: my-agent-darwin-arm64
- os: windows-latest
target: x86_64-windows
artifact: my-agent-windows-amd64.exe
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
# Build a self-contained binary with PyInstaller
- run: pip install pyinstaller
- run: pip install -r requirements.txt
- run: pyinstaller --onefile --name ${{ matrix.artifact }} src/main.py
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: dist/${{ matrix.artifact }}
release:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts/
merge-multiple: true
# Generate checksums so users can verify integrity
- name: Generate SHA-256 checksums
run: |
cd artifacts
sha256sum * > SHA256SUMS.txt
- uses: softprops/action-gh-release@v2
with:
files: |
artifacts/*
generate_release_notes: true
Customize: Swap PyInstaller for your actual build tool (Go: go build, Rust: cargo build --release, Node.js: pkg). Add/remove OS targets from the matrix. Add code signing steps for macOS (codesign) and Windows (signtool).
10. curl Install Script for Agent
install.sh
#!/usr/bin/env bash
# install.sh — One-liner installer for my-agent.
#
# Usage:
# curl -fsSL https://raw.githubusercontent.com/my-org/my-agent/main/install.sh | bash
#
# What it does:
# 1. Detects OS and architecture
# 2. Checks prerequisites (Python >= 3.11 or Node.js >= 18)
# 3. Downloads the latest release binary
# 4. Installs to ~/.local/bin (or /usr/local/bin with sudo)
# 5. Verifies the checksum
# 6. Prints next steps (API key setup)
set -euo pipefail
# --- Configuration (change these) ---
REPO="my-org/my-agent"
BINARY_NAME="my-agent"
MIN_PYTHON="3.11"
MIN_NODE="18"
# --- Colors (disabled if not a terminal) ---
if [ -t 1 ]; then
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
else
RED=''; GREEN=''; YELLOW=''; NC=''
fi
info() { echo -e "${GREEN}[info]${NC} $*"; }
warn() { echo -e "${YELLOW}[warn]${NC} $*"; }
error() { echo -e "${RED}[error]${NC} $*" >&2; exit 1; }
# --- OS and architecture detection ---
detect_platform() {
OS="$(uname -s)"
ARCH="$(uname -m)"
case "$OS" in
Linux) PLATFORM="linux" ;;
Darwin) PLATFORM="darwin" ;;
*) error "Unsupported OS: $OS. This installer supports Linux and macOS." ;;
esac
case "$ARCH" in
x86_64) ARCH="amd64" ;;
aarch64|arm64) ARCH="arm64" ;;
*) error "Unsupported architecture: $ARCH" ;;
esac
info "Detected platform: ${PLATFORM}-${ARCH}"
}
# --- Prerequisite checks ---
check_python() {
if command -v python3 &>/dev/null; then
PY_VER="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
if python3 -c "import sys; exit(0 if sys.version_info >= (3, 11) else 1)" 2>/dev/null; then
info "Python ${PY_VER} found (>= ${MIN_PYTHON} required)"
return 0
else
warn "Python ${PY_VER} found but >= ${MIN_PYTHON} required"
return 1
fi
fi
warn "Python not found"
return 1
}
check_node() {
if command -v node &>/dev/null; then
NODE_VER="$(node -v | sed 's/v//' | cut -d. -f1)"
if [ "$NODE_VER" -ge "$MIN_NODE" ] 2>/dev/null; then
info "Node.js v${NODE_VER} found (>= ${MIN_NODE} required)"
return 0
else
warn "Node.js v${NODE_VER} found but >= ${MIN_NODE} required"
return 1
fi
fi
warn "Node.js not found"
return 1
}
check_prerequisites() {
# Adjust this: require Python, Node, or either
if ! check_python; then
error "Python >= ${MIN_PYTHON} is required. Install it from https://python.org"
fi
}
# --- Download and install ---
install_binary() {
LATEST_TAG="$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | cut -d'"' -f4)"
info "Latest release: ${LATEST_TAG}"
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${LATEST_TAG}/${BINARY_NAME}-${PLATFORM}-${ARCH}"
CHECKSUM_URL="https://github.com/${REPO}/releases/download/${LATEST_TAG}/SHA256SUMS.txt"
INSTALL_DIR="${HOME}/.local/bin"
mkdir -p "$INSTALL_DIR"
info "Downloading ${BINARY_NAME}..."
curl -fsSL "$DOWNLOAD_URL" -o "${INSTALL_DIR}/${BINARY_NAME}"
chmod +x "${INSTALL_DIR}/${BINARY_NAME}"
# Verify checksum
info "Verifying checksum..."
EXPECTED="$(curl -fsSL "$CHECKSUM_URL" | grep "${BINARY_NAME}-${PLATFORM}-${ARCH}" | awk '{print $1}')"
if command -v sha256sum &>/dev/null; then
ACTUAL="$(sha256sum "${INSTALL_DIR}/${BINARY_NAME}" | awk '{print $1}')"
else
ACTUAL="$(shasum -a 256 "${INSTALL_DIR}/${BINARY_NAME}" | awk '{print $1}')"
fi
if [ "$EXPECTED" = "$ACTUAL" ]; then
info "Checksum verified"
else
error "Checksum mismatch. Expected: ${EXPECTED}, Got: ${ACTUAL}. The download may be corrupted."
fi
}
# --- PATH check ---
check_path() {
if [[ ":$PATH:" != *":${INSTALL_DIR}:"* ]]; then
warn "${INSTALL_DIR} is not in your PATH."
echo ""
echo " Add it by running:"
echo " echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ~/.bashrc"
echo " source ~/.bashrc"
echo ""
fi
}
# --- Post-install guidance ---
print_next_steps() {
echo ""
info "Installation complete! ${BINARY_NAME} ${LATEST_TAG}"
echo ""
echo " Next steps:"
echo ""
echo " 1. Set your API key:"
echo " export ANTHROPIC_API_KEY=sk-ant-..."
echo " (Get one at https://console.anthropic.com/)"
echo ""
echo " 2. Run your first command:"
echo " ${BINARY_NAME} --help"
echo ""
}
# --- Main ---
main() {
info "Installing ${BINARY_NAME}..."
detect_platform
check_prerequisites
install_binary
check_path
print_next_steps
}
main "$@"
Customize: Change REPO, BINARY_NAME, MIN_PYTHON, and MIN_NODE. Adjust check_prerequisites to require the right runtime. Update the post-install guidance with your agent's actual first command.
Sources & References
- [MCP TypeScript SDK] — Model Context Protocol. https://github.com/modelcontextprotocol/typescript-sdk Official TypeScript SDK for building MCP servers and clients, providing the McpServer class, transport layers, and Zod-based tool schemas.
- [MCP Python SDK (FastMCP)] — Model Context Protocol. Part of the MCP Python SDK (
mcppackage on PyPI). Provides the FastMCP class for building Python MCP servers with decorator-based tool and resource registration. - [Docker Multi-Stage Builds] — Docker, Inc. https://docs.docker.com/build/building/multi-stage/ Guide to separating build-time dependencies from runtime images, used in all Docker agent templates to minimize image size.
- [GitHub Actions Documentation] — GitHub. https://docs.github.com/en/actions Reference for CI/CD workflows including npm provenance publishing, PyPI trusted publishers, binary release builds, and install smoke tests.
- [ShellCheck — Shell Script Analysis Tool] — Vidar Holen. https://www.shellcheck.net/ Static analysis tool for bash/sh scripts, used to validate curl install scripts for correctness, portability, and security.
- [Semantic Versioning 2.0.0] — Tom Preston-Werner. https://semver.org/ The versioning specification used across all agent templates for package.json version fields, pyproject.toml versions, and Git release tags.
Source
git clone https://github.com/phazurlabs/install-labs/blob/main/skills/agent-install-templates/SKILL.mdView on GitHub Overview
AI Agent Install Templates deliver ready-to-copy boilerplates for packaging AI agents as installable software. Each template is annotated and complete, including MCP Server skeletons, package.json, tsconfig.json, and deployment artifacts like Dockerfiles, docker-compose, and CI workflows. Use these templates to accelerate project setup and ensure consistency across packaging and deployment.
How This Skill Works
Templates ship as ready-to-copy project skeletons powered by the MCP SDK. You customize the tool definitions and runtime logic in a minimal MCP Server scaffold (Node.js/TypeScript), then build, package, and deploy using standard scripts. Annotations and standard config files (package.json, tsconfig.json, etc.) guide integration with tooling, tests, and CI/CD.
When to Use It
- Starting a new AI agent project and needing a ready MCP Server skeleton with tool definitions
- Packaging an agent as installable software with standard configs like package.json or pyproject.toml
- Setting up CI/CD and release automation with GitHub Actions for agent projects
- Deploying agents with Docker, including Dockerfile and docker-compose templates
- Bootstrapping a reproducible project structure using cookiecutter-style starter templates
Quick Start
- Step 1: Copy the MCP Server template into your project and set a new name/version in package.json
- Step 2: Implement or replace the sample tools (e.g., greet, analyze) in src/index.ts to fit your agent
- Step 3: Run npm install, npm run build, and npm run start to test locally; add Docker/CI steps as needed
Best Practices
- Keep the MCP Server name, version, and tool definitions in sync with your project metadata
- Use the provided build/start scripts and align Node engine to >= 18.0.0
- Maintain clear, annotated tool definitions (e.g., greet, analyze) and replace with real capabilities
- Document customizations in README and inline comments to ease onboarding
- Validate locally with a full build and run cycle before introducing CI/CD or deployment steps
Example Use Cases
- MCP Server skeleton registering a greet tool and a text analysis tool in src/index.ts
- package.json setup including dependencies like @modelcontextprotocol/sdk and zod with common scripts
- tsconfig.json tuned for ES2022, Node16 module, and strict type checking
- Dockerfile and docker-compose scaffold to containerize the agent for local dev
- GitHub Actions workflow template for automated builds and releases