Claude Integration

Custom Skills

Step-by-step guide to creating your own Claude Code slash commands, from simple instruction skills to full API-backed Python scripts.

Skill Types

There are three types of skills, each suited to different levels of complexity.

TypeFilesBest For
Pure InstructionSKILL.md onlySimple workflows using Claude's built-in tools
Python ScriptSKILL.md + script.pyAPI calls, data processing, external integrations
SubagentSKILL.md (with context: fork)Long-running research, exploratory tasks

File Structure

Skills are directories containing at minimum a SKILL.md file:

~/.claude/skills/
  my-skill/
    SKILL.md          # Required: instructions and metadata
    my_skill.py       # Optional: Python script for complex logic

Locations:

PathScope
~/.claude/skills/Personal skills, available in all projects
.claude/skills/Project-specific, scoped to one codebase

SKILL.md Anatomy

Every skill starts with YAML frontmatter followed by markdown instructions:

---
name: my-skill
description: When Claude should use this skill
allowed-tools: Bash, Read, Write
---

# My Skill

Instructions that Claude follows when this skill is invoked.

## Usage

/my-skill <arguments>

## Process

1. First step
2. Second step

Frontmatter Options

FieldTypeDescription
namestringCommand name -- becomes /name
descriptionstringTells Claude when to auto-invoke this skill
allowed-toolsstringComma-separated tools: Bash, Read, Write, Edit, WebSearch, WebFetch, Grep, Glob
disable-model-invocationbooleanIf true, only the user can invoke (Claude will not auto-trigger)
user-invocablebooleanIf false, hidden from user menu (Claude-only)
contextstringSet to fork to run in a subagent with its own context
agentstringAgent type for subagents: Explore, Plan, or general-purpose

Creating a Pure Instruction Skill

Pure instruction skills have no Python code. Claude uses its existing tools (Read, Write, Bash) to follow the instructions.

Example: Quick capture to inbox

---
name: capture
description: Quickly capture a note to the inbox folder
allowed-tools: Write
---

# Quick Capture

Save a quick note to the user's inbox.

## Usage

/capture <note content>

## Process

1. Generate filename: `inbox/YYYY-MM-DD-<slugified-title>.md`
2. Create the file at `$VAULT_PATH/<filename>` with frontmatter:
   ```markdown
   ---
   tags: []
   shared: false
   ---

   <note content>
  1. Confirm creation to the user

Save this as `~/.claude/skills/capture/SKILL.md` and the `/capture` command is immediately available.

## Creating a Python Script Skill

For skills that need to call APIs, process data, or perform complex logic, add a Python script alongside `SKILL.md`.

### Step 1: Create the Directory

```bash
mkdir -p ~/.claude/skills/my-skill

Step 2: Write the Python Script

Use argparse for subcommands and import the shared API client:

#!/usr/bin/env python3
"""My custom skill."""

import argparse
import sys
from pathlib import Path

# Import the shared Cognova API client
sys.path.insert(0, str(Path(__file__).parent.parent / '_lib'))
from api import get, post, put, delete

def cmd_list(args):
    """List items."""
    ok, data = get('/my-endpoint')
    if not ok:
        print(f"Error: {data}", file=sys.stderr)
        sys.exit(1)
    for item in data:
        print(f"- {item['name']}")

def cmd_create(args):
    """Create an item."""
    ok, result = post('/my-endpoint', {'name': args.name})
    if ok:
        print(f"Created: {result['name']}")
    else:
        print(f"Error: {result}", file=sys.stderr)
        sys.exit(1)

def main():
    parser = argparse.ArgumentParser(description='My Skill')
    subparsers = parser.add_subparsers(dest='command', required=True)

    subparsers.add_parser('list', help='List items')

    create_p = subparsers.add_parser('create', help='Create an item')
    create_p.add_argument('name', help='Item name')

    args = parser.parse_args()
    {'list': cmd_list, 'create': cmd_create}[args.command](args)

if __name__ == '__main__':
    main()

Step 3: Write SKILL.md

---
name: my-skill
description: Manage my custom items
allowed-tools: Bash, Read
---

# My Skill

## Commands

\`\`\`bash
python3 ~/.claude/skills/my-skill/my_skill.py <command> [options]
\`\`\`

## Available Commands

- `list` -- show all items
- `create <name>` -- create a new item

Step 4: Test

# Test directly
python3 ~/.claude/skills/my-skill/my_skill.py --help
python3 ~/.claude/skills/my-skill/my_skill.py list

# Test via Claude
claude
> /my-skill list

Using the Shared API Client

The _lib/api.py module provides four HTTP helpers that handle authentication automatically:

from api import get, post, put, delete

# All return (success: bool, data_or_error)
ok, tasks = get('/tasks', params={'status': 'todo'})
ok, result = post('/tasks', {'title': 'New task', 'priority': 2})
ok, result = put('/tasks/abc-123', {'status': 'done'})
ok, result = delete('/tasks/abc-123')

Authentication uses the .api-token file generated during Cognova setup. The client checks multiple paths (project directory, Docker mount, working directory) so it works across all deployment types.

Accessing Secrets

For skills that call external APIs, use the secrets store instead of hardcoding credentials:

from api import get_secret

success, api_key = get_secret("DISCORD_WEBHOOK_URL")
if not success:
    print(f"Error: {api_key}")
    print("Add this secret at Settings > Secrets in the dashboard")
    sys.exit(1)

Secrets are managed through the dashboard at Settings > Secrets and encrypted at rest.

Never hardcode API keys, tokens, or credentials in skill scripts. Always use get_secret() for external service credentials and environment variables for Cognova system configuration.

Using the Skill Creator

The fastest way to build a new skill is to ask Claude directly:

/skill-creator

The wizard guides you through:

  1. Requirements gathering -- what the skill does, who invokes it, whether it needs external APIs
  2. Existing solution check -- searches for MCPs, plugins, and community tools that already solve the problem
  3. Type selection -- determines whether you need a pure instruction skill, Python script, or subagent
  4. File generation -- creates SKILL.md and any supporting scripts
  5. Secrets setup -- shows how to configure API keys if needed

The skill creator searches the web for existing MCP servers before building from scratch. If a maintained MCP already does what you need, it will suggest installing that instead.

Environment Variables

These are available to all skills automatically:

VariableDefaultDescription
COGNOVA_API_URLhttp://localhost:3000Cognova API base URL
COGNOVA_API_TOKEN(auto-generated)Auth token, also read from .api-token file
COGNOVA_PROJECT_DIR--Install directory path
VAULT_PATH--Path to the vault directory

Real-World Example: Weekly Digest Skill

A Python script skill that summarizes completed tasks and sends a digest to a webhook:

#!/usr/bin/env python3
"""Weekly digest -- summarize completed tasks."""

import json
import subprocess
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent / '_lib'))
from api import get, get_secret

def main():
    # Get completed tasks from the last 7 days
    ok, tasks = get('/tasks', {'status': 'done'})
    if not ok:
        print(f"Error: {tasks}", file=sys.stderr)
        sys.exit(1)

    if not tasks:
        print("No completed tasks this week.")
        return

    # Build summary
    summary = f"Completed {len(tasks)} tasks this week:\n"
    for task in tasks[:20]:
        summary += f"- {task['title']}\n"

    # Send to webhook
    ok, webhook_url = get_secret("DIGEST_WEBHOOK_URL")
    if ok:
        subprocess.run([
            "curl", "-sL", "-X", "POST",
            "-H", "Content-Type: application/json",
            "-d", json.dumps({"text": summary}),
            webhook_url
        ], capture_output=True)
        print(f"Digest sent with {len(tasks)} tasks.")
    else:
        # Fall back to printing
        print(summary)

if __name__ == '__main__':
    main()

This pattern -- fetch data from Cognova, transform it, send it somewhere -- is the foundation of most custom skills.