add-tool

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

A workflow for adding a new MCP tool definition to a server. MCP is a standard way for an agent to call tools provided by another program.

In plain words
What is it for?
Use it to scaffold a tool, define its inputs and outputs, register it in the server, and check that the project builds correctly.
Why use it?
It gives a new tool the expected file structure, registration, input handling, and verification steps.

Skill for Claude Code

Written for Claude Code: shipped in a Claude Code plugin. Also seen: mentions Claude Code.

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

Good fit Use it to scaffold a tool, define its inputs and outputs, register it in the server, and check that the project builds correctly.

Compare 6 skills from other repositories ↓
Install with agentmods
npx agentmods add skills/cyanheads/brapi-mcp-server/add-tool
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.

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

Made for: Claude Code.

Or install brapi-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/brapi-mcp-server/add-tool/github.svg)](https://agentmods.dev/skills/cyanheads/brapi-mcp-server/add-tool)
Your own site
<a href="https://agentmods.dev/skills/cyanheads/brapi-mcp-server/add-tool"><img src="https://agentmods.dev/badge/skills/cyanheads/brapi-mcp-server/add-tool/github.svg" alt="Measured on agentmods" height="20"></a>

Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.

agentmods 80×15 button for add-tool

Your own site · 80×15
<a href="https://agentmods.dev/skills/cyanheads/brapi-mcp-server/add-tool"><img src="https://agentmods.dev/badge/skills/cyanheads/brapi-mcp-server/add-tool.svg" alt="Reviewed on agentmods" width="80" 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. A grade says what 26 rules found in the file — not that it is safe.
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.1 $0.00035 $0.12329
Opus 5 $0.00017 $0.06164
Sonnet 5 $0.00007 $0.02466
Haiku 4.5 $0.00003 $0.01233

Measured 9d ago against content hash 5ba100b246b6, method: parsed. Prices are Anthropic first-party input rates as of 2026-09-09, 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 9d 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. 9d 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/brapi-mcp-server (3 stars, last pushed 17d 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

api-context

Canonical reference for the unified Context object passed to every tool and resource handler in @cyanheads/mcp-ts-core. Covers the full interface, its RequestContext base, all sub-APIs (ctx.log, ctx.state, ctx.requestInput, ctx.inputs, ctx.enrich, ctx.content), and when to use each.

cyanheads/pubchem-mcp-server · 79 tokens

api-errors

McpError constructor, JsonRpcErrorCode reference, and error handling patterns for @cyanheads/mcp-ts-core. Use when looking up error codes, understanding where errors should be thrown vs. caught, or using ErrorHandler.tryCatch in services.

cyanheads/pubchem-mcp-server · 54 tokens

add-service

Scaffold a new service integration. Use when the user asks to add a service, integrate an external API, or create a reusable domain module with its own initialization and state.

cyanheads/pubchem-mcp-server · 38 tokens

api-workers

Cloudflare Workers deployment using createWorkerHandler from @cyanheads/mcp-ts-core/worker. Covers the full handler signature, binding types, CloudflareBindings extensibility, runtime compatibility guards, and wrangler.toml requirements.

cyanheads/pubchem-mcp-server · 51 tokens

add-app-tool

Scaffold an MCP App tool + UI resource pair. Use when the user asks to add a tool with interactive UI, create an MCP App, or build a visual/interactive tool.

cyanheads/pubchem-mcp-server · 41 tokens

api-auth

Authentication, authorization, and multi-tenancy patterns for @cyanheads/mcp-ts-core. Use when implementing auth scopes on tools/resources, configuring auth modes (none/jwt/oauth), working with JWT/OAuth env vars, or understanding how tenantId flows through ctx.state.

cyanheads/pubchem-mcp-server · 58 tokens