Skip to content

Z-M-Huang/git-doc-mcp

Repository files navigation

git-doc-mcp - Turn Any Manifest into an MCP Server

npm version License: MIT Node.js TypeScript Linux macOS Windows

Write a manifest, host it anywhere, and users can instantly use it with their AI tools.

What is git-doc-mcp?

git-doc-mcp is a declarative MCP manifest system that lets anyone create and host MCP servers without running infrastructure. It turns any manifest URL into a fully-functional MCP server with custom tools, resources, and prompts - all defined in YAML.

Key features:

  • No hosting required - Use GitHub Pages, GitLab Pages, S3, any static hosting
  • Custom JavaScript actions - Define your own tool logic
  • Capability-scoped secrets - Fine-grained access control with URL pattern matching
  • Platform-agnostic - Works with GitHub, GitLab, S3, local files
  • TOFU manifest verification - Trust-on-first-use with fail-closed security
  • Three-layer isolation - Worker process + isolated-vm + URL validation
  • Cross-platform - Linux, macOS, Windows

Quick Start

1. Install

# Using npm
npm install -g git-doc-mcp

# Using npx (no install needed)
npx git-doc-mcp --manifest https://example.com/.mcp/manifest.yml

2. Configure with Claude Code

Add to ~/.claude/config.json:

{
  "mcpServers": {
    "my-repo": {
      "command": "npx",
      "args": ["git-doc-mcp", "--manifest", "https://example.com/.mcp/manifest.yml"]
    }
  }
}

3. Create Your Manifest

Create .mcp/manifest.yml in your repository:

schemaVersion: "1.0"
name: my-repo-mcp
version: 1.0.0
description: MCP server for my repository

tools:
  - name: fetch-file
    description: Fetch a file from the repository
    inputSchema:
      type: object
      properties:
        path: { type: string }
      required: [path]
    action: https://example.com/.mcp/actions/fetch-file.v1.js  # URL or local path
    actionHash: "sha256:..."
    annotations:
      readOnlyHint: true
      openWorldHint: true

Actions can reference HTTP(S) URLs or local file paths (relative to the manifest file's directory):

# Remote action (hosted)
action: https://raw.githubusercontent.com/owner/repo/main/.mcp/actions/fetch-file.v1.js

# Local action (for development)
action: ./actions/fetch-file.v1.js

Try It — Live Demo

This project uses its own manifest system to serve documentation. Add it to your AI tool to see git-doc-mcp in action:

Claude Code — add to your project's .mcp.json:

{
  "mcpServers": {
    "git-doc-mcp-docs": {
      "command": "npx",
      "args": [
        "git-doc-mcp",
        "--manifest",
        "https://raw.githubusercontent.com/Z-M-Huang/git-doc-mcp/main/.mcp/manifest.yml"
      ]
    }
  }
}

Claude Desktop — add to ~/.claude/claude_desktop_config.json:

{
  "mcpServers": {
    "git-doc-mcp-docs": {
      "command": "npx",
      "args": [
        "git-doc-mcp",
        "--manifest",
        "https://raw.githubusercontent.com/Z-M-Huang/git-doc-mcp/main/.mcp/manifest.yml"
      ]
    }
  }
}

This gives you 5 tools, 3 resources, and 2 prompts that fetch documentation from the project wiki:

Tool Description
list_topics Browse documentation topics, filter by tag
get_guide Fetch a specific guide (e.g., Getting-Started, Manifest-Reference)
search_docs Search across all documentation by keyword
get_example Get complete working examples (GitHub tools, REST wrapper, etc.)
get_action_api Action scripting API reference (ctx.fetch, ctx.getSecret, etc.)

The manifest source and action scripts serve as a reference for building your own.

Installation

npm

npm install -g git-doc-mcp

npx (no install)

npx git-doc-mcp --manifest <url-or-path>

Local Development

git clone https://github.com/Z-M-Huang/git-doc-mcp.git
cd git-doc-mcp
npm install
npm run build

Usage Examples

Public Manifest

npx git-doc-mcp --manifest https://raw.githubusercontent.com/owner/repo/main/.mcp/manifest.yml

Private Manifest with Authentication

npx git-doc-mcp --manifest https://private.example.com/.mcp/manifest.yml \
  --manifest-header "Authorization: Bearer $TOKEN"

Local Development

# Manifest and action scripts can all be local files
npx git-doc-mcp --manifest ./path/to/manifest.yml

When using a local manifest, action and resource.uri fields can reference local files with paths relative to the manifest's directory. Local actions are re-read from disk on each tool call, so edits take effect without restarting the server.

With Pre-approved Secrets

# Via CLI flag
npx git-doc-mcp --manifest https://example.com/.mcp/manifest.yml \
  --secret GITHUB_TOKEN=$GITHUB_TOKEN

# Via environment variable
export GIT_MCP_SECRET_GITHUB_TOKEN=$GITHUB_TOKEN
npx git-doc-mcp --manifest https://example.com/.mcp/manifest.yml

Hash Pinning (for CI/CD)

npx git-doc-mcp --manifest https://example.com/.mcp/manifest.yml \
  --manifest-hash sha256:abc123...

Rate Limiting

# Limit to 30 tool calls per minute
npx git-doc-mcp --manifest https://example.com/.mcp/manifest.yml \
  --rate-limit 30

Manifest Schema

schemaVersion: "1.0"           # Required - schema version
name: my-repo-mcp              # Required - server name
version: 1.0.0                 # Required - semantic version
description: Description       # Optional - shown to AI
instructions: Use when...      # Optional - helps AI understand when to use

secrets:                       # Optional - secrets needed
  - name: GITHUB_TOKEN
    description: GitHub token
    scope:
      - "https://api.github.com/*"
    required: false

tools:                         # Optional - tool definitions
  - name: fetch-file
    title: Fetch File          # Optional - human-readable title
    description: Fetch a file  # Required
    inputSchema:               # Required - JSON Schema
      type: object
      properties:
        path: { type: string }
      required: [path]
    action: https://...        # Required - URL or local file path to action script
    actionHash: sha256:...     # Required - SHA-256 hash of action content
    annotations:               # Optional - hints for AI
      readOnlyHint: true
      destructiveHint: false
      idempotentHint: true
      openWorldHint: true

resources:                     # Optional - static resources
  - name: readme
    uri: https://...           # URL or local file path
    description: README
    mimeType: text/markdown

prompts:                       # Optional - prompt templates
  - name: explain-code
    description: Explain code
    args:
      - name: path
        required: true
    messages:                    # Optional - MCP PromptMessage[]
      - role: user
        content:
          type: resource
          resource:
            uri: "https://example.com/{{path}}"
            mimeType: text/plain
      - role: user
        content:
          type: text
          text: "Explain the code above from {{path}}"

Prompt Messages

Prompts support the full MCP PromptMessage format with multi-message templates:

  • Without messages: A single user message is built from description + args (simple mode)
  • With messages: Messages are returned directly with {{argName}} substitution

Each message has a role (user or assistant) and content (either text or embedded resource):

messages:
  - role: user
    content:
      type: text
      text: "Analyze {{path}} for {{focus}}"
  - role: assistant
    content:
      type: text
      text: "I'll analyze the code structure first."
  - role: user
    content:
      type: resource
      resource:
        uri: "https://example.com/{{path}}"
        mimeType: text/plain

Action API

Actions are plain JavaScript files that export a default async function:

export default async function myAction(input, ctx) {
  const { fetch, getSecret, log, manifest } = ctx;

  log('info', `Running action for ${manifest.name}`);

  // Get a secret scoped to the target URL
  const url = 'https://api.example.com/data';
  const token = getSecret('API_KEY', url);

  const response = await fetch(url, {
    headers: token ? { 'Authorization': `Bearer ${token}` } : {}
  });

  // response.text is a property (not a method)
  // response.json() is a synchronous method
  const data = response.json();

  return {
    content: [{ type: 'text', text: JSON.stringify(data, null, 2) }]
  };
}

Context Methods

Method Description
ctx.fetch(url, options) Scoped fetch with SSRF protection and redirect validation
ctx.getSecret(name, url) Get a secret value if the URL matches the secret's scope pattern
ctx.log(level, message) Logging (levels: debug, info, warn, error)
ctx.manifest Manifest metadata ({ name, version })

Fetch Response

ctx.fetch returns a serialized response object (not a native Response):

Property/Method Type Description
response.ok boolean true if status is 200-299
response.status number HTTP status code
response.statusText string HTTP status text
response.text string Response body as a string (property, not method)
response.json() object Parse body as JSON (synchronous method)
response.headers object Response headers as key-value pairs

Return Format

// Success
return {
  content: [{ type: 'text', text: 'Result' }]
};

// Error
return {
  content: [{ type: 'text', text: 'Error message' }],
  isError: true
};

CLI Reference

Options

Option Description Default
--manifest <path> URL or local path to manifest.yml (required)
--manifest-header <header> Header for fetching manifest (repeatable)
--manifest-hash <hash> Expected manifest hash for pinning
--action-code-header <header> Header for downloading action scripts (repeatable)
--resource-header <header> Header for fetching resources (repeatable)
--secret <name=value> Pre-approved secret (repeatable)
--timeout <ms> Worker timeout in milliseconds 60000
--memory-limit <bytes> Sandbox memory limit (8MB - 1GB) 134217728 (128MB)
--rate-limit <n> Max tool calls per minute (0 = unlimited) 0
--allow-http Allow insecure HTTP URLs (HTTPS-only by default) false
--trust-changed Accept manifest hash changes (TOFU override) false

Environment Variables

Secrets can be provided via environment variables prefixed with GIT_MCP_SECRET_:

export GIT_MCP_SECRET_GITHUB_TOKEN=ghp_abc123
export GIT_MCP_SECRET_API_KEY=sk-xyz789

The --secret CLI flag takes precedence over environment variables.

Examples

# Basic usage
git-doc-mcp --manifest ./manifest.yml

# With authentication headers
git-doc-mcp --manifest https://... \
  --manifest-header "Authorization: Bearer $TOKEN" \
  --action-code-header "Authorization: Bearer $TOKEN"

# Separate resource headers
git-doc-mcp --manifest https://... \
  --resource-header "Authorization: Bearer $RESOURCE_TOKEN"

# With secrets and rate limiting
git-doc-mcp --manifest https://... \
  --secret GITHUB_TOKEN=$TOKEN \
  --rate-limit 60

# Hash pinned (for CI/CD)
git-doc-mcp --manifest https://... --manifest-hash sha256:abc123...

# Accept manifest changes (TOFU override)
git-doc-mcp --manifest https://... --trust-changed

# Custom memory limit (256MB)
git-doc-mcp --manifest https://... --memory-limit 268435456

# Allow HTTP (not recommended for production)
git-doc-mcp --manifest http://localhost:8080/manifest.yml --allow-http

Comparison

Feature git-doc-mcp Context7 idosal/git-doc-mcp
Hosting Any static host Hosted service Needs separate server
Custom actions User-defined JS Fixed tools Limited actions
Private repos Auth headers OAuth Unknown
Platform Any (GitHub, GitLab, S3, etc.) Any GitHub only
Local development Local files No No
Secret scoping URL pattern matching N/A N/A
Manifest integrity TOFU + hash pinning N/A N/A

Security Model

Trusted-Manifest System

Users explicitly configure manifest URLs they trust. This is consistent with the official GitHub MCP Server's approach — the user configures which server to use, so they trust the server author.

Three-Layer Isolation

Layer 1: Worker Process (Primary Boundary)
  - Separate Node.js child process
  - Sanitized environment (no inherited credentials)
  - Crash in action doesn't kill CLI

Layer 2: isolated-vm (Defense-in-Depth)
  - Configurable memory limit (default: 128MB)
  - CPU timeout: 30s
  - No direct filesystem/network access

Layer 3: Controlled API Surface
  - ctx.fetch with SSRF protection
  - ctx.getSecret with URL scope validation
  - Audit logging of all network calls

SSRF Protection

All HTTP(S) URLs are validated before fetch:

  • HTTPS-only by default (use --allow-http to override)
  • Private IP ranges blocked (10.x, 172.16-31.x, 192.168.x, localhost)
  • DNS resolution validated before connection
  • Every redirect URL re-validated
  • Cross-origin redirects strip sensitive headers (Authorization, Cookie)

Local file paths in action and resource.uri are read directly from disk and are not subject to SSRF validation. ctx.fetch inside action scripts remains HTTP(S)-only regardless of how the action was loaded.

Capability-Scoped Secrets

Secrets are scoped to specific URL patterns:

secrets:
  - name: GITHUB_TOKEN
    scope:
      - "https://api.github.com/*"
      - "https://raw.githubusercontent.com/*"

ctx.getSecret(name, url) only returns the secret value if the URL matches the scope pattern. Path-boundary-aware wildcard matching prevents /repos-private from matching a /repos/* scope.

TOFU (Trust-on-First-Use)

On first use, git-doc-mcp stores the manifest's SHA-256 hash. If the manifest changes:

Warning: Manifest content has changed since last use!
  Previous: sha256:abc123...
  Current:  sha256:def456...

To accept this change, re-run with: --trust-changed
For CI pinning, use: --manifest-hash sha256:def456...

The server exits with a non-zero code unless --trust-changed is provided. For CI/CD, use --manifest-hash for hard pinning.

Rate Limiting

Sliding-window rate limiter prevents excessive tool calls:

# 60 calls per minute
git-doc-mcp --manifest https://... --rate-limit 60

Audit Logging

All ctx.fetch calls are logged to ~/.git-doc-mcp/logs/audit.jsonl:

  • URL, HTTP status, duration
  • Redirect hops
  • Secret access (allowed/denied)
  • Action start/end with timing

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Build: npm run build
  5. Run tests: npm test
  6. Submit a pull request

Development Setup

npm install        # Install dependencies
npm run build      # Build all packages
npm test           # Run all tests (298 tests)

Project Structure

git-doc-mcp/
  packages/
    core/          # Manifest loading, sandbox, worker, MCP server
    cli/           # CLI entry point and serve command
    template/      # Example manifest and actions

Documentation

Full documentation is available on the GitHub Wiki:

License

MIT License - see LICENSE for details.

About

Define your MCP server in YAML, write actions in JavaScript, host the files anywhere. No backend needed.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors