Mcp.ToolsDoc 0.1.1

dotnet tool install --global Mcp.ToolsDoc --version 0.1.1
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local Mcp.ToolsDoc --version 0.1.1
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Mcp.ToolsDoc&version=0.1.1
                    
nuke :add-package Mcp.ToolsDoc --version 0.1.1
                    

mcp-tooling

🌐 Language: English | Русский

Shared tooling for our .NET Model Context Protocol servers.

Mcp.ToolsDoc β€” tool reference generator

A config-driven .NET tool that generates a Markdown tool reference for an MCP server from its [McpServerToolType] / [McpServerTool] / [Description] attributes (Roslyn, syntax-only β€” no build, runs in <1s), and a --check mode that fails CI when the committed doc drifts from the code. Reusable across every .NET ModelContextProtocol MCP server.

Install (per consuming repo)

Add it to a local tool manifest:

dotnet new tool-manifest        # if you don't have .config/dotnet-tools.json yet
dotnet tool install Mcp.ToolsDoc

Configure β€” toolsdoc.json at the repo root

{
  // One section per MCP server. toolsDir is repo-relative.
  "servers": [
    {
      "id": "my-mcp",
      "displayName": "my-mcp",
      "toolsDir": "src/MyMcp.Server/Tools",
      "blurb": "Example MCP server."
    }
  ],
  "generatedOutput": "docs/TOOLS.generated.md",
  // optional: keep N markers in sync
  // Convention: include each plugin's SKILL.md here so its headline tool-count
  // stays accurate.  = sum across all servers;
  //  = a single server's count.
  "markerFiles": [
    "README.md",
    "docs/INSTALL.md",
    "plugins/<plugin>/skills/<skill>/SKILL.md"
  ],
  // optional: a hand-curated cheatsheet that must mention every tool by name
  "cheatsheet": "docs/TOOLS.md"
}

Only servers is required. markerFiles and cheatsheet are opt-in. The cross-repo convention is that every plugin SKILL.md whose body mentions a tool count is listed in markerFiles, so its headline stays accurate automatically and the --check CI gate fails on drift. SKILL.md files hand-maintain agent-trigger keywords and per-tool intent tables; the tool-count headline is the one piece that can be auto-substituted, and now is.

Run

dotnet tool run mcp-toolsdoc            # --write (default): (re)generate docs in place
dotnet tool run mcp-toolsdoc --check    # CI: exit non-zero if anything is out of sync

Options: --config <path> (default <repo-root>/toolsdoc.json), --repo-root <path> (default: the git root found from the current directory).

CI integration

# .github/workflows/docs-codegen.yml
on: { push: { branches: [main] }, pull_request: { branches: [main] } }
jobs:
  toolsdoc:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-dotnet@v4
        with: { dotnet-version: '10.0.x' }
      - run: dotnet tool restore
      - run: dotnet tool run mcp-toolsdoc --check

The generated TOOLS.generated.md is English-only; if your repo enforces a bilingual-docs gate, list it in that gate's ignore file.

Mcp.I18nCheck β€” bilingual-docs gate

A config-less .NET tool that enforces our bilingual-docs convention in CI: English is canonical; every English doc must have its Russian counterpart and the Russian file must be non-stub (β‰₯ 200 bytes).

  • docs/<path>.md ↔ docs/ru/<path>.md (mirror subtree).
  • X.md ↔ X.ru.md (suffix) for the repo root, plugins/*, examples/**, servers/*, infra/.

Exemptions: a repo-root .i18nignore (English-only / generated / agent files, one repo-relative path per line). Pairs outside the conventional locations: .i18npairs (en:ru per line).

dotnet tool install Mcp.I18nCheck
dotnet tool run mcp-i18ncheck            # exit non-zero if any pair is missing/stub
# .github/workflows/docs-i18n.yml
on: { push: { branches: [main] }, pull_request: { branches: [main] } }
jobs:
  bilingual-docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-dotnet@v4
        with: { dotnet-version: '10.0.x' }
      - run: dotnet tool restore
      - run: dotnet tool run mcp-i18ncheck

Reusable CI/CD workflows

Two reusable GitHub Actions workflows let every consuming repo share one definition of how images are built, published, and deployed β€” callers stay thin and never drift apart. They live under .github/workflows/ here and are referenced with uses: <owner>/mcp-tooling/.github/workflows/<file>@main.

docker-build-push.yml β€” build + push ghcr.io images

Builds one or more multi-arch images from a JSON image matrix and pushes each as ghcr.io/<owner-lowercase>/<image_suffix> with :<version> and :latest.

# .github/workflows/docker.yml in the consuming repo
on:
  push: { tags: ['v*.*.*'] }
  workflow_dispatch:
    inputs:
      version: { description: 'Version without leading v', required: true, type: string }
jobs:
  build-push:
    permissions: { contents: read, packages: write }
    uses: <owner>/mcp-tooling/.github/workflows/docker-build-push.yml@main
    with:
      version: ${{ inputs.version != '' && inputs.version || github.ref_name }}
      images: |
        [
          { "name": "my-mcp", "image_suffix": "my-mcp",
            "dockerfile": "./Dockerfile", "context": ".", "cache_scope": "my-mcp" }
        ]
      # use_gh_packages_secret: true   # when the Dockerfile restores a private GH Packages feed
    # secrets:
    #   gh_packages_token: ${{ secrets.GITHUB_TOKEN }}
input meaning
version image tag; a leading v is stripped; :latest is also pushed
images JSON array of {name, image_suffix, dockerfile, context, cache_scope} (one entry per image)
platforms buildx platforms (default linux/amd64,linux/arm64)
use_gh_packages_secret mount github_token as a build-secret for private NuGet restore

deploy-vps.yml β€” ship an image to a VPS over SSH

Ships a published image to a host without any registry login on the host: the runner pulls the image, docker save | ssh streams the tarball into a forced-command deploy.sh, the tag arrives as the SSH command (re-validated server-side), then the runner smoke-tests a healthz URL.

# .github/workflows/deploy.yml in the consuming repo
on:
  workflow_dispatch:
    inputs:
      tag: { description: 'Image tag (semver or latest)', required: true, type: string, default: latest }
jobs:
  deploy:
    uses: <owner>/mcp-tooling/.github/workflows/deploy-vps.yml@main
    with:
      image_suffix: my-mcp
      tag: ${{ inputs.tag }}
      healthz_url: https://my-mcp.example.com/healthz
    secrets:
      deploy_ssh_key: ${{ secrets.DEPLOY_SSH_KEY }}
      deploy_host: ${{ secrets.DEPLOY_HOST }}
      deploy_user: ${{ secrets.DEPLOY_USER }}
      deploy_known_hosts: ${{ secrets.DEPLOY_KNOWN_HOSTS }}

Host prerequisite (per service): a CI deploy key locked to the forced command in the host's ~/.ssh/authorized_keys, so the key can only run the deploy script and nothing else:

command="/opt/<name>/deploy.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAA... ci-deploy

deploy.sh validates the tag, docker loads the piped tarball, pins it in the compose .env, recreates the container, and waits for the healthcheck. A reference script is kept in each consuming repo under deploy/deploy.sh.

Releasing

Each tool's version lives in its csproj (src/Mcp.ToolsDoc, src/Mcp.I18nCheck). Pushing a v X.Y.Z tag packs both and publishes them to nuget.org via .github/workflows/publish.yml (--skip-duplicate, so unchanged versions are no-ops; requires the repo secret NUGET_API_KEY). Bump the relevant csproj <Version> before tagging.

License: MIT.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.1.1 480 5/30/2026
0.1.0 198 5/29/2026