본문으로 건너뛰기
cccskills
무료GitHub에 공개

creating-tools

Build a new external service integration as a packaged Python CLI. The skill guides the project layout, API client, command-line interface, credential declarations, health check, and tests so the tool can be installed and used by agents.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

원문원문 설명 보기

Scaffold and build new tool integrations in tools/. Use when asked to create a new tool, add an API integration, or build a new client for an external service.

paradigmxyz/centaur스타 1,407(GitHub 1,407 + cccskills 0)

실행 점수 미검증

설치 방법 보기

이럴 때 유용합니다

  • Create a tool for a new external API
  • Build a Python client for a service
  • Add a CLI command for an integration
  • Set up credential declarations and health checks

포함된 파일(1)

  • SKILL.md8.4 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Creating Tools

A tool is an independently packaged Python CLI under tools/<category>/<name>/ (categories: business, comms, crypto, infra, media, productivity, research). api-rs reads its pyproject.toml for secret grants, and sandboxes install its [project.scripts] entry as a CLI shim. Agents use it through centaur-tools list, <tool> --help, and the CLI itself; there is no HTTP tool-method API.

Read docs/pages/extend/tools.mdx for the full [tool.centaur] secret reference. Copy the shape of a recent tool such as tools/productivity/luma or tools/research/pangram rather than inventing a new layout.

Layout

tools/<category>/<name>/
├── pyproject.toml   # metadata, [project.scripts], wheel mapping, [tool.centaur]
├── __init__.py      # one-line docstring
├── client.py        # API client class + _client() factory
├── cli.py           # thin Typer CLI over the client, with a `health` command
├── test_client.py   # client tests against httpx.MockTransport
└── test_cli.py      # CLI tests with typer.testing.CliRunner

pyproject.toml

[project]
name = "<name>"
description = "<One-line description>"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    "httpx>=0.27.0",
    "typer>=0.12.0",
]

[project.scripts]
<name> = "centaur_tool_<module>.cli:app"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["."]

[tool.hatch.build.targets.wheel.sources]
"." = "centaur_tool_<module>"

[tool.centaur]
module = "client.py"
secrets = [
    {type = "http", name = "<NAME>_API_KEY", match_headers = ["Authorization"], hosts = ["api.example.com"]},
]
  • [project.scripts] is required for the tool to be visible to agents. Name the package centaur_tool_<module>, where <module> is the tool name in snake_case, so it cannot shadow an installed dependency. The wheel packages/sources mapping must match the script's package; scripts/validate_cli_packaging.py checks this in CI.
  • Declare every credential in secrets (or optional_secrets) with the narrowest hosts list. Omit secrets for public APIs.
  • Add dependencies only when needed. Use httpx, never requests: httpx honors the proxy that iron-proxy relies on.

client.py

  • Get credentials with from centaur_sdk import secret and secret("KEY"). In a sandbox this returns a placeholder that iron-proxy swaps for the real value on the allowed hosts. Never use os.getenv/os.environ or load_dotenv; tools/ruff.toml bans them.
  • One client class. Public methods are the tool's operations; prefix helpers with _. Add type hints and a docstring to each public method.
  • Raise on HTTP errors (response.raise_for_status()) and validate response shape at the boundary.
  • Support close() and the context-manager protocol, and end the module with a _client() factory.
"""Client for the <Name> API."""

from __future__ import annotations

from typing import Any

import httpx

from centaur_sdk import secret

BASE_URL = "https://api.example.com"


class <Name>Client:
    def __init__(self, api_key: str | None = None, timeout: float = 30.0) -> None:
        self._api_key = api_key
        self.timeout = timeout
        self._client: httpx.Client | None = None

    @property
    def client(self) -> httpx.Client:
        if self._client is None:
            self._client = httpx.Client(timeout=self.timeout)
        return self._client

    def _request(self, method: str, path: str, **kwargs: Any) -> dict[str, Any]:
        headers = {"Authorization": f"Bearer {self._api_key or secret('<NAME>_API_KEY')}"}
        response = self.client.request(method, f"{BASE_URL}{path}", headers=headers, **kwargs)
        response.raise_for_status()
        result = response.json()
        if not isinstance(result, dict):
            raise RuntimeError("<Name> returned an unexpected response.")
        return result

    def search(self, query: str, limit: int = 10) -> dict[str, Any]:
        """Search for items."""
        return self._request("GET", "/search", params={"q": query, "limit": limit})

    def close(self) -> None:
        if self._client is not None:
            self._client.close()
            self._client = None

    def __enter__(self) -> <Name>Client:
        return self

    def __exit__(self, *args: Any) -> None:
        self.close()


def _client() -> <Name>Client:
    return <Name>Client()

cli.py

  • Keep it thin: parse arguments, call one client method, and print JSON to stdout with typer.echo(json.dumps(...)).
  • Catch expected errors, print a short message to stderr, and exit 1. Never echo response bodies or headers from failed requests; they can contain credentials.
  • Provide health: make one cheap authenticated read and print {"ok": bool, "tool": "<name>", "error": str | null, "details": {...}}, exiting 1 when ok is false. The tool-health-smoke skill runs it on every tool.
"""Command-line interface for the <Name> API."""

from __future__ import annotations

import json

import httpx
import typer

from .client import _client

app = typer.Typer(name="<name>", help="<Description>")


def _echo(payload: dict) -> None:
    typer.echo(json.dumps(payload, indent=2, ensure_ascii=False))


def _error_message(exc: Exception) -> str:
    if isinstance(exc, httpx.HTTPStatusError):
        return f"<Name> API error: HTTP {exc.response.status_code}"
    if isinstance(exc, httpx.HTTPError):
        return f"<Name> request failed ({type(exc).__name__})."
    return str(exc)


@app.command()
def health() -> None:
    """Check authentication and connectivity."""
    try:
        with _client() as client:
            details = client.search("health", limit=1)
    except (httpx.HTTPError, KeyError, RuntimeError) as exc:
        _echo({"ok": False, "tool": "<name>", "error": _error_message(exc), "details": {}})
        raise typer.Exit(1) from None
    _echo({"ok": True, "tool": "<name>", "error": None, "details": details})


@app.command()
def search(
    query: str = typer.Argument(..., help="Search query."),
    limit: int = typer.Option(10, "--limit", "-n", help="Maximum results."),
) -> None:
    """Search for items."""
    try:
        with _client() as client:
            _echo(client.search(query, limit=limit))
    except (httpx.HTTPError, KeyError, RuntimeError) as exc:
        typer.echo(_error_message(exc), err=True)
        raise typer.Exit(1) from None


if __name__ == "__main__":
    app()

Tests

Import the tool as centaur_tool_<module>; .github/scripts/run-tool-tests.sh links the tool directory under that name before running pytest.

  • Client: inject httpx.Client(transport=httpx.MockTransport(handler)) and assert the method, URL, query or body, and auth header the real API expects, plus how responses are parsed. See tools/research/pangram/test_client.py.
  • CLI: patch cli._client, invoke with CliRunner, and check exit codes, JSON output, and that a failed request does not print the response body.
  • Test real behavior such as pagination, polling, and error mapping. Don't add tests that only restate the code.

Checks:

uvx ruff check tools/<category>/<name>
uvx ruff format --check tools/<category>/<name>
python scripts/validate_cli_packaging.py
.github/scripts/run-tool-tests.sh   # runs every tool's tests the way CI does

Credentials and verification

  1. Store the secret in the deployment's configured secret source under the exact name from pyproject.toml (see docs/pages/secrets/).
  2. Grant it: centaur-perms ... principals grant <principal> --tool <name> registers the declared secrets, creates the tool-<slug> role, and assigns it. See "Register and Grant a Tool" in docs/pages/secrets/advanced-permissioning.mdx.
  3. From a fresh local sandbox, run centaur-tools list, <name> --help, <name> health, and one real command. Configuration alone does not prove access works.

If the tool is missing, check the [project.scripts] entry, the wheel mapping, [tool.centaur] module = "client.py", and the configured TOOL_DIRS or overlay toolsSubdir. Add organization-specific tools to an overlay repo instead of this one (docs/pages/extend/overlay.mdx).

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Investigate authentication, permission, credential, OAuth, token, secret, and API proxy failures by checking live VictoriaLogs evidence first. Summarize relevant status codes and error details, and distinguish upstream denials from local proxy or secret resolution problems.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Investigate a 401, 403, or 503 response
  • Trace an integration authentication failure
  • Check why a credential or secret is not resolving
paradigmxyz/centaur1.4천2026년 10월 26일 업데이트

centaur

무료

Use an authenticated Centaur MCP deployment to find and call team-approved tools. It checks identity when permissions matter, follows the deployment’s current CLI help, and summarizes consequential changes with relevant identifiers or links.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Finding the right approved Centaur tool
  • Running an unfamiliar Centaur CLI command
  • Checking the signed-in user's permissions
paradigmxyz/centaur1.4천2026년 10월 26일 업데이트

Use this skill to translate a Console administrator’s request into Centaur permission changes. It can create supported principals and secrets, grant or revoke access, adjust Slack and sandbox permissions, and manage Slack Connect allowlists while checking for unsafe personal credential sharing.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Grant a Slack channel access to a shared tool credential
  • Create a principal for a Slack channel, user, or workflow
  • Adjust a channel’s access to another Slack channel
paradigmxyz/centaur1.4천2026년 10월 26일 업데이트

Investigate Centaur incidents using VictoriaLogs for runtime logs and VictoriaMetrics for deployment and health metrics. The skill correlates a narrow time window with relevant code and workflow status, then reports a supported cause or bounded hypotheses and the evidence still needed.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Investigating a stalled workflow or missing post
  • Checking thread and execution evidence
  • Reviewing tool-call or sandbox activity
paradigmxyz/centaur1.4천2026년 10월 26일 업데이트

Answer internal history and project context questions by searching indexed company information and checking relevant live sources such as Slack, Linear, Google Docs, Drive, Calendar, or Granola. Reconcile the evidence and cite dates or source links when available.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Find what was discussed or decided internally
  • Check a project's prior context or current status
  • Search meeting notes and calendar events
paradigmxyz/centaur1.4천2026년 10월 26일 업데이트

docsend

무료

Retrieve standalone DocSend documents or files in a DocSend Space for inspection, summarization, or download. The workflow handles email verification, passcodes, resumable sessions, and folder traversal, while reporting files that could not be fetched.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Download a standalone DocSend document
  • Retrieve files from a DocSend Space
  • Resume a document request after email verification
paradigmxyz/centaur1.4천2026년 10월 26일 업데이트

paradigmxyz의 스킬 모두 보기

이 스킬의 문제 신고