Skip to content

Contributing

github-actions[bot] edited this page May 30, 2026 · 3 revisions

Contributing

Contributions are welcome. Please read this page before opening a PR.


Git Workflow

All work happens on branches — never commit directly to main.

git checkout -b feat/my-new-tool   # or fix/, chore/, docs/, refactor/
# make changes
npm test                           # must pass before pushing
git push -u origin feat/my-new-tool
# open a PR targeting main — squash merge

Branch naming uses kebab-case prefixes: feat/, fix/, chore/, docs/, refactor/.
Commit messages follow Conventional Commits: feat(scope): summary, fix: summary, etc.


Adding a Tool

  1. Create a file in src/domain/tools/ (or add to an existing one).
  2. Define the tool using the ToolDefinition interface — see the pattern in CLAUDE.md or any existing tool file.
  3. Export it and add it to allTools in src/domain/tools/index.ts.
  4. Add unit tests in test/unit/ covering metadata, input schema, and all handler paths.
  5. Document it in both of these files (lockstep tests enforce this — CI fails if either is missing):
    • docs/wiki/Available-Tools.md — add a section with a parameter table and example prompt.
    • skills/mikromcp/references/tool-map.md — add the tool name to the appropriate category row.
  6. Add a line to CHANGELOG.md under [Unreleased] → Added.

Minimal tool skeleton:

import { z } from "zod";
import type { ToolDefinition, ToolContext, ToolResult } from "./tool-definition.js";
import { enrichError } from "../errors/error-enricher.js";
import { MikroMCPError } from "../errors/error-types.js";
import { createLogger } from "../../observability/logger.js";

const log = createLogger("my-tools");

const myInputSchema = z.object({
  routerId: z.string().describe("Target router identifier from the router registry"),
  // all params need .describe() for AI clients
}).strict();  // always .strict()

const myTool: ToolDefinition = {
  name: "my_tool",
  title: "My Tool",
  description: "What this tool does. Mention idempotency and dry-run if applicable.",
  inputSchema: myInputSchema,
  annotations: {
    readOnlyHint: true,       // true → auto-retry enabled in tool-registry
    destructiveHint: false,   // true → circuit breaker trips on failure
    idempotentHint: true,
    openWorldHint: false,
  },
  async handler(params: Record<string, unknown>, context: ToolContext): Promise<ToolResult> {
    const parsed = myInputSchema.parse(params);
    log.info({ routerId: context.routerId }, "Doing thing");

    try {
      const records = await context.routerClient.get("ros/path", {});
      return {
        content: `Found ${records.length} things.`,
        structuredContent: { routerId: context.routerId, records },
      };
    } catch (err) {
      if (err instanceof MikroMCPError) throw err;
      throw enrichError(err, { routerId: context.routerId, tool: "my_tool" });
    }
  },
};

export const myTools: ToolDefinition[] = [myTool];

Coding Guidelines

  • Idempotency first — write tools must check existing state before acting and return already_exists / no_change when nothing needs to be done.
  • Always support dryRun on write tools — no exceptions.
  • Never log credentials — they are resolved by tool-registry.ts and never reach handlers.
  • Always use .strict() on Zod schemas — reject extra fields.
  • Enrich errors — use enrichError() in every catch block; throw MikroMCPError directly for domain errors (NOT_FOUND, CONFLICT, VALIDATION).
  • No retry/circuit-breaker logic in handlers — that is handled by tool-registry.ts.
  • ESM imports with .js extensionsfrom "../../adapter/rest-client.js" even for .ts source.
  • Run npm run format && npm run lint before pushing.

PR Checklist

  • npm test passes (vitest + tsc + eslint + doc-accuracy guards + skill tool-map lockstep)
  • New tool: docs/wiki/Available-Tools.md updated with parameter table and example prompt
  • New tool: skills/mikromcp/references/tool-map.md updated (lockstep test will fail otherwise)
  • New tool: CHANGELOG.md [Unreleased] section updated
  • Write tool: dryRun supported
  • Write tool: idempotency check included
  • Write tool: snapshotPaths set if the tool modifies a RouterOS path that should be rollback-able
  • PR title follows Conventional Commits format

Clone this wiki locally