add-tool

add-tool is a skill for Claude Code, Codex from cyanheads/attack-surface-mcp-server. It costs 35 tokens per session (12,329 once invoked), scanned A, a copy of add-tool, Apache-2.0.

A definition for a new MCP tool, where a tool is an action an AI application can ask a server to perform. It describes the tool’s inputs and outputs and connects it to the server.

In plain words
What is it for?
Use it to add actions such as searches, calculations, or updates to an MCP server, register them, and verify their definitions.
Why use it?
It provides a standard place and process for adding a new server action, including cases where the caller must provide extra information.

Skill for Claude CodeCodex

Part of the attack-surface-mcp-server plugin — 32 skills, 1 MCP server shipped together

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add skills/cyanheads/attack-surface-mcp-server/add-tool
Any agent
npx skills add cyanheads/attack-surface-mcp-server --skill add-tool
Clone the repo
git clone --depth 1 https://github.com/cyanheads/attack-surface-mcp-server

Made for: Claude Code, Codex.

Or install attack-surface-mcp-server, the plugin that ships this one along with the rest of its 32 skills, 1 MCP server.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for add-tool

README.md
[![agentmods](https://agentmods.dev/badge/skills/cyanheads/attack-surface-mcp-server/add-tool.svg)](https://agentmods.dev/skills/cyanheads/attack-surface-mcp-server/add-tool)
Your own site
<a href="https://agentmods.dev/skills/cyanheads/attack-surface-mcp-server/add-tool"><img src="https://agentmods.dev/badge/skills/cyanheads/attack-surface-mcp-server/add-tool.svg" alt="Measured on agentmods" height="20"></a>
Per session 35 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 12,329 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 1 finding. Scan, not verified.
Origin 100% copy Near-identical to another mod in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5 $0.00035 $0.12329
Opus 5 $0.00017 $0.06164
Sonnet 5 $0.00007 $0.02466
Haiku 4.5 $0.00003 $0.01233

Measured 4d ago against content hash 5ba100b246b6, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

Grade A, and why

add-tool scanned grade A with 1 finding against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 4d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Makes network callslowCapability

Not a fault in itself. Listed so you know the mod talks to something, and to what.

const articles = await fetch(input.pmids);
Origin

This is a copy

100% identical to add-tool — 4 lines differ, which has more behind it and is treated as the original. This page carries a canonical link to it rather than competing with it.

skills/add-tool/SKILL.md · 804 lines

How it starts

The opening of the file, as written. The whole thing — 804 lines — stays where its author put it; the contents beside it link to each section on GitHub.

Context

Tools use the tool() builder from @cyanheads/mcp-ts-core. Each tool lives in src/mcp-server/tools/definitions/ with a .tool.ts suffix. The standard registration pattern uses a definitions/index.ts barrel that collects all tools into an allToolDefinitions array for createApp(). Fresh scaffolds from init start with direct imports in src/index.ts — the barrel is introduced as definitions grow. Match the pattern already used by the project you're editing.

Steps

  1. Gather the tool's name, purpose, and input/output shape from the user's request — ask only if genuinely absent
  2. Determine if it needs input the caller may not supply — a confirmation, a choice, the client's roots — which makes it a multi-round-trip handler (ctx.requestInput / ctx.inputs, see api-context)
  3. Create the file at src/mcp-server/tools/definitions/{{tool-name}}.tool.ts
  4. Register the tool in the project's existing createApp() tool list (directly in src/index.ts for fresh scaffolds, or via a barrel if the repo already has one)
  5. Run bun run devcheck to verify — if Biome reports formatting issues, run bun run format to auto-fix, then re-run devcheck
  6. Smoke-test with bun run rebuild && bun run start:stdio (or start:http)

Naming

Tools use lowercase snake_case with a canonical server/domain prefix: {server}_{verb}_{noun} — 3 words.

Examples: pubmed_search_articles, pubmed_fetch_fulltext, clinicaltrials_find_studies.

The server prefix uses the canonical platform/brand name, not an abbreviation (patentsview_ not patents_, clinicaltrials_ not ct_). When a name resists the schema — can't pick a verb, noun feels generic, wants 4+ segments — that's usually a signal the scope is fuzzy; split the tool, rename, or reconsider.

For shape selection (Workflow or Instruction variants — standard single-action tools are the default), see the design-mcp-server skill's Tool shapes section.

Template

/**
 * @fileoverview {{TOOL_DESCRIPTION}}
 * @module mcp-server/tools/definitions/{{TOOL_NAME}}
 */

import { tool, z } from '@cyanheads/mcp-ts-core';
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';

export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
  title: '{{TOOL_TITLE}}',
  // Single cohesive paragraph — pack operational guidance into prose sentences,
  // not bullet lists or blank-line-separated sections. Descriptions render inline.
  description: '{{TOOL_DESCRIPTION}}',
  annotations: { readOnlyHint: true },
  input: z.object({
    // All fields need .describe(). Only JSON-Schema-serializable Zod types allowed.
  }),
  output: z.object({
    // All fields need .describe(). Only JSON-Schema-serializable Zod types allowed.
  }),
  // Agent-facing context on the success path — empty-result notices, the query as
  // the server parsed it, pagination totals. The counterpart to errors[]: merged
  // into structuredContent AND mirrored into content[] automatically (no format()
  // entry needed, never touched by format-parity). Populate via ctx.enrich(...) in
  // the handler or service layer. Keys must be disjoint from output. Delete if unused.
  enrichment: {
    effectiveQuery: z.string().describe('The query as the server parsed it.'),
    totalCount: z.number().describe('Total matches before any limit was applied.'),
  },
  // auth: ['tool:{{tool_name}}:read'],

  // Each entry declares a domain-specific failure mode and types
  // `ctx.fail(reason, …)` against the declared union. Baseline codes
  // (InternalError, ServiceUnavailable, Timeout, ValidationError,
  // SerializationError) bubble freely — only declare domain-specific reasons.
  // Delete this block if no domain failures apply.
  //
  // Keep contracts inline on this tool, even when other tools have similar
  // entries. The contract is part of the tool's documented public surface —
  // don't extract a shared `errors[]` constant; per-tool repetition is the
  // intended cost of self-contained tool defs.
  //
  // `recovery` is required (≥ 5 words) — it's the agent's next move when this
  // failure fires. Forcing function for thoughtful guidance: placeholders like
  // "Try again." get flagged by the linter. The contract `recovery` is the
  // single source of truth for what flows to the wire — opt in at the throw
  // site by spreading `ctx.recoveryFor('reason')` into the `data` arg.
  errors: [
    { reason: 'queue_full', code: JsonRpcErrorCode.RateLimited,
      when: 'Local queue at capacity.', retryable: true,
      recovery: 'Wait a few seconds before retrying or reduce batch size.' },
  ],

  async handler(input, ctx) {
    ctx.log.info('Processing', { /* relevant input fields */ });
    // Pure logic — throw on failure, no try/catch.
    // With an `errors[]` contract: `throw ctx.fail('reason_id', message?, data?)`.
    // Without: throw via factories (`notFound`, `validationError`, …) or plain `Error`.
    const items = await search(input);
    if (queue.full()) {
      // Static recovery — resolve from the contract via ctx.recoveryFor('reason').
      // Single source of truth: the string lives in errors[] above; this spread
      // pulls it onto the wire so format()-only clients see the recovery hint.
      throw ctx.fail('queue_full', undefined, { ...ctx.recoveryFor('queue_full') });
    }
    // Surface what the agent reasons with — echoed query, true total — on BOTH
    // client surfaces, with no format() plumbing. An empty result is a notice,
    // not a throw: reserve ctx.fail for genuine failures (queue full, upstream down).
    ctx.enrich.echo(input.query);
    ctx.enrich.total(items.length);
    if (items.length === 0) {
      ctx.enrich.notice(`No items matched "${input.query}". Try broader terms or check the spelling.`);
    }
    return { items };
  },

  // format() populates MCP content[] — the markdown twin of structuredContent.
  // Different clients read different surfaces (Claude Code → structuredContent,
  // Claude Desktop → content[]), so both must carry the same data.
  // Enforced at lint time: every field in `output` must appear in the rendered text.
  format: (result) => {
    const lines: string[] = [];
    // Render each item with all relevant fields — not just a count or title.
    // A thin one-liner (e.g., "Found 5 items") leaves the model blind to the data.
    for (const item of result.items) {
      lines.push(`## ${item.name}`);
      lines.push(`**ID:** ${item.id} | **Status:** ${item.status}`);
      if (item.description) lines.push(item.description);
    }
    return [{ type: 'text', text: lines.join('\n') }];
  },
});

Read the full file on GitHub · 804 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 4d ago First seen · 804 lines · 35 tokens per session scan A 5ba100b246b6

Subscribe to this mod's changes

add-tool is a skill published in the GitHub repository cyanheads/attack-surface-mcp-server (1 stars, last pushed 5d ago), licensed Apache-2.0. It adds 35 tokens to every session and 12,329 once invoked, about $0.0002 per session on Opus 5. A static security scan graded it A with 1 finding (makes network calls). It is 100% identical to add-tool, differing in 4 lines, and is treated as a copy.

Related

Other skills, from other repositories

analyzing-certificate-transparency-for-phishing

Monitor Certificate Transparency logs using crt.sh and Certstream to detect phishing domains, lookalike certificates, and unauthorized certificate issuance targeting your organization.

mukul975/Anthropic-Cybersecurity-Skills · 39 tokens

analyzing-dns-logs-for-exfiltration

Analyzes DNS query logs to detect data exfiltration via DNS tunneling, DGA domain communication, and covert C2 channels using entropy analysis, query volume anomalies, and subdomain length detection in SIEM platforms. Use when SOC teams need to identify DNS-based threats that bypass traditional network security…

mukul975/Anthropic-Cybersecurity-Skills · 73 tokens

ensembl-database

Query Ensembl genome database REST API for 250+ species. Gene lookups, sequence retrieval, variant analysis, comparative genomics, orthologs, VEP predictions, for genomic research.

synthetic-sciences/openscience · 45 tokens

colab-finetuning

Fine-tune LLMs on Google Colab GPUs directly from openscience. Connects to Colab runtimes via WebSocket bridge for remote training with Unsloth. Supports SFT, GRPO, DPO, vision, and TTS workflows on free T4 to Pro A100 GPUs.

synthetic-sciences/openscience · 67 tokens

subdomain-enumeration

Map subdomains via crt.sh and subfinder at recon kickoff.

uphiago/recon-skills · 19 tokens

swarm-pr-review

Run a graph-guided, tool-augmented PR review using context packing, parallel exploration, mandatory repository-agnostic risk-family coverage with dispatch scaled to diff size and risk, independent reviewer validation, critic challenge, and metrics writeback. Use for deep pull request review with low false-positive…

ZaxbyHub/opencode-swarm · 91 tokens