Agent skill
mcp-doc-init
Initialize documentation manifest for git-doc-mcp. Scans the project for documentation files (*.md, READMEs, ADRs, etc.), creates .mcp/manifest.yml with resource entries and search tools, and configures .mcp.json for the git-doc-mcp MCP server. Run this once when setting up project documentation.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/mcp-doc-init
SKILL.md
MCP Doc Initialization
Create a .mcp/manifest.yml with documentation resources and embedded-index tools, generate action scripts in .mcp/actions/, and configure .mcp.json with a git-doc-mcp server entry. The manifest indexes existing documentation files wherever they live in the project.
Step 1: Check for Existing Config
Read .mcp/manifest.yml and .mcp.json from the project root.
| manifest.yml exists | .mcp.json has entry | Action |
|---|---|---|
| Yes | Yes | Show current config (resource count, tool count, server name), ask if user wants to reconfigure or keep. If keep, stop here. |
| Yes | No | Skip to Step 7 (.mcp.json generation only). |
| No | * | Full init (Steps 2-8). |
Step 2: Scan Project for Documentation Files
Use Glob to find all documentation files. Apply base exclude patterns plus ecosystem-specific excludes.
Base exclude patterns
Always exclude these directories from scanning:
node_modules, .git, dist, build, coverage, __pycache__, .pytest_cache,
target, vendor, .venv, .next, .nuxt, .cache, .turbo, tmp, .output, out
Ecosystem-specific excludes
Detect the project ecosystem from root files and add extra excludes:
| Root file detected | Additional excludes |
|---|---|
package.json |
node_modules/**, .next/**, .nuxt/**, .turbo/** |
pyproject.toml or requirements.txt |
__pycache__/**, .pytest_cache/**, .venv/**, venv/**, .mypy_cache/** |
Cargo.toml |
target/** |
go.mod |
vendor/** |
pom.xml or build.gradle |
target/**, build/**, .gradle/** |
Gemfile |
vendor/bundle/** |
Glob patterns
Search for these patterns in non-excluded directories:
**/*.md— all markdown files**/*.rst— reStructuredText files**/*.txt— text files that may be documentation (filter to only doc-like ones: those containing headings, sections, or located indocs/directories)
Prioritized files
Flag these as high-value documentation when found:
README.md(at any level)CONTRIBUTING.mdARCHITECTURE.mdCHANGELOG.mddocs/**/*.mdADR-*.md,adr-*.md
Non-markdown docs
Detect but do not index these — list them in the final report as "non-markdown docs found" for user awareness:
openapi.yaml,openapi.jsonswagger.yaml,swagger.json*.api.md(API spec files)
Step 2b: Large Project Detection
If >50 doc files found OR >100 directories scanned:
-
Tell the user: "This is a large project with N documentation files across M directories."
-
Ask via AskUserQuestion how to scope. Present these options:
- Scan everything — index all docs found (may produce a large manifest)
- Specify include roots — user provides paths (e.g.,
packages/my-team,src/api) and only docs under those roots are indexed - Use CODEOWNERS/ownership file — if a
CODEOWNERSfile exists, offer to filter by team pattern - Other — let user describe their scoping strategy
-
Re-scan with the chosen scope applied. Only docs matching the scope are carried forward to Step 3.
Step 3: Build Documentation Map
For each documentation file found in the scan, read the file and extract structured data.
Content extraction
For each file:
-
Title — the first H1 heading (
# ...). If no H1 exists, use the filename without extension (e.g.,setup.mdbecomesSetup). -
Section headers — all H2, H3, and H4 headings.
-
Section content excerpts — for each H2/H3 section:
- Include ALL paragraphs, bullet lists, and table content within the section
- Cap at ~500 characters per section to control index size
- Preserve bullet list items and table rows (these often contain requirements and key details)
- Include H4+ subsections as part of their parent H2/H3 section content (do not create separate entries for H4+)
-
Description — a one-sentence summary of the file. Use the first paragraph after the H1, or synthesize from the content if no suitable paragraph exists.
Scope assignment rules
Assign each file a scope and tags based on its location and type:
| Doc type | Scope value | Tags |
|---|---|---|
{dir}/README.md |
{dir} (e.g., src/api) |
— |
Root README.md |
"" (empty string) |
global |
Root CONTRIBUTING.md, ARCHITECTURE.md, CHANGELOG.md |
"" |
global |
docs/*.md at project root |
"" |
global, plus auto-tagged by content |
docs/*.md inside a subtree (e.g., packages/auth/docs/guide.md) |
parent of docs/ (e.g., packages/auth) |
— |
ADR-*.md or adr-*.md |
their directory path | adr |
Any other *.md |
their directory path | — |
*.rst, *.txt (doc-like) |
their directory path | — |
Auto-tagging
Generate tags from:
- Directory name (e.g.,
src/apiaddsapi) - Doc type keywords (CONTRIBUTING adds
contributing, ADR addsadr) - Content keywords — scan section headers for common topics:
auth,security,api,database,testing,deployment,config,standards,setup,migration
Resource naming
Convert file paths to resource names:
- Replace
/with_ - Remove the file extension
- Lowercase everything
Examples:
README.md→readmesrc/api/README.md→src_api_readmedocs/coding-standards.md→docs_coding-standards
Collision detection: If two paths normalize to the same resource name, append -2, -3 suffixes to subsequent entries.
Index entry structure
Each file produces an index entry:
{
name: "src_api_readme", // resource name
path: "src/api/README.md", // file path from project root
scope: "src/api", // directory scope
title: "API Module", // first H1 or filename
description: "REST API endpoints, middleware, and authentication",
tags: ["api", "rest", "express", "middleware"],
sections: [
{
heading: "Authentication",
content: "JWT-based auth using passport.js. All routes require Bearer token. Tokens expire after 24h. Refresh tokens stored in httpOnly cookies."
},
{
heading: "Error Handling",
content: "All endpoints return structured errors:\n- 400: validation failures with field-level details\n- 401: missing or expired token\n- 403: insufficient permissions\n- 500: internal errors logged, generic message returned"
}
]
}
Step 4: Confirm with User
Present a summary of what was found:
Found N documentation files:
- M READMEs across directories
- K standalone docs (CONTRIBUTING, architecture, standards, etc.)
- J in docs/ directories
- P ADRs
Top-level directories: src/, docs/, packages/, ...
Non-markdown docs detected (not indexed): openapi.yaml, swagger.json, ...
Ask the user:
- Project name — used in the manifest
namefield (suggest based on directory name or package.jsonname) - Review the file list — let user add or remove files from the index
- Confirm — proceed with generation
Wait for explicit confirmation before proceeding.
Step 5: Generate .mcp/manifest.yml
Create the .mcp/ directory if it does not exist. Write .mcp/manifest.yml with:
schemaVersion: "1.0"
name: {project-name}-docs
version: 1.0.0
description: "Documentation index for {project-name}"
instructions: |
You are a documentation assistant for {project-name}.
You MUST use the tools below to answer questions — do NOT rely on prior knowledge.
MANDATORY RULES:
- ALWAYS use at least one tool BEFORE answering any question about this project.
- NEVER answer from memory alone — the documentation is the source of truth.
- If a tool returns no results, try different keywords or a different tool before giving up.
DECISION TREE — match the user's intent to a tool:
User asks a specific question or has a keyword → search_docs
User asks "what docs cover this file/directory?" → get_applicable_docs
User wants to see documentation coverage or structure → get_doc_tree
User wants to read a specific doc → use Resources directly
If unsure which tool to use → search_docs (safest default)
prompts:
- name: welcome
title: Welcome - Get Started
description: Introduction to {project-name} documentation and available tools
messages:
- role: user
content:
type: text
text: |
I'm exploring this project. Give me a brief overview of what documentation
is available and what tools I can use, then ask what I'd like to learn about.
resources:
- name: root_readme
uri: ../README.md
description: "Project overview, architecture, and getting started"
mimeType: text/markdown
- name: src_api_readme
uri: ../src/api/README.md
description: "REST API endpoints, middleware, and authentication"
mimeType: text/markdown
# ... one entry per indexed doc file
URI paths: All URIs are relative to the .mcp/ directory. A file at src/api/README.md (relative to project root) gets URI ../src/api/README.md. A file at project root gets URI ../README.md.
Do NOT add tool entries yet — those are added in Step 6 after action files are generated and hashed.
Step 6: Generate Tools (Embedded-Index Actions)
Create the .mcp/actions/ directory. Generate three action scripts, each embedding the full index data built in Step 3.
6a: search-docs.js
Section-level search across all indexed documentation. The embedded INDEX array contains entries with title, description, tags, path, scope, and sections (headings + content excerpts).
Search logic:
- Match the query against: title, description, tags (array items), path, and each section's heading + content
- Case-insensitive matching
- Return matching docs with the specific sections that matched and the matched excerpt
- Sort results by relevance (title match > section heading match > content match)
// Auto-generated by mcp-doc-init — do not edit manually
// Regenerate with /mcp-doc-sync
const INDEX = [
// ... full index entries with sections
];
export default async function(input, ctx) {
const q = (input.query || "").toLowerCase();
if (!q) return { content: [{ type: "text", text: "Please provide a search query." }] };
const results = [];
for (const doc of INDEX) {
const matchedSections = [];
const titleMatch = doc.title.toLowerCase().includes(q);
const descMatch = doc.description.toLowerCase().includes(q);
const tagMatch = doc.tags.some(t => t.toLowerCase().includes(q));
const pathMatch = doc.path.toLowerCase().includes(q);
for (const sec of doc.sections) {
if (sec.heading.toLowerCase().includes(q) || sec.content.toLowerCase().includes(q)) {
matchedSections.push(sec);
}
}
if (titleMatch || descMatch || tagMatch || pathMatch || matchedSections.length > 0) {
results.push({
name: doc.name,
path: doc.path,
title: doc.title,
description: doc.description,
matchedSections: matchedSections.length > 0
? matchedSections.map(s => `### ${s.heading}\n${s.content}`).join("\n\n")
: null,
relevance: (titleMatch ? 3 : 0) + (descMatch ? 2 : 0) + (tagMatch ? 2 : 0) + matchedSections.length
});
}
}
results.sort((a, b) => b.relevance - a.relevance);
if (results.length === 0) {
return { content: [{ type: "text", text: `No documentation found matching "${input.query}".` }] };
}
const text = results.map(r => {
let entry = `- **${r.title}** (${r.path})\n ${r.description}`;
if (r.matchedSections) entry += `\n Matched sections:\n${r.matchedSections}`;
return entry;
}).join("\n\n");
const footer = `\n\n---\nNext steps:\n- Read a specific doc above using its resource URI\n- Use get_applicable_docs to find docs for a specific source file\n- Refine your search with different keywords`;
return { content: [{ type: "text", text: `Found ${results.length} result(s) for "${input.query}":\n\n${text}${footer}` }] };
}
6b: get-applicable-docs.js
Given a source file path, returns all documentation that applies to it: direct parent docs, scope-matched docs, and global docs.
// Auto-generated by mcp-doc-init — do not edit manually
// Regenerate with /mcp-doc-sync
const INDEX = [
// ... entries with name, scope, tags, path, title, description
];
export default async function(input, ctx) {
const p = (input.path || "").replace(/\\/g, "/");
if (!p) return { content: [{ type: "text", text: "Please provide a file or directory path." }] };
const results = INDEX.filter(doc =>
doc.tags.includes("global") ||
p === doc.scope ||
p.startsWith(doc.scope + "/")
);
// Sort: most specific scope first, then globals at the end
results.sort((a, b) => {
const aGlobal = a.tags.includes("global") ? 1 : 0;
const bGlobal = b.tags.includes("global") ? 1 : 0;
if (aGlobal !== bGlobal) return aGlobal - bGlobal;
return b.scope.length - a.scope.length;
});
if (results.length === 0) {
return { content: [{ type: "text", text: `No documentation found applicable to "${p}".` }] };
}
const text = results.map(r => {
const scopeNote = r.tags.includes("global") ? " (global)" : ` (scope: ${r.scope || "root"})`;
return `- **${r.title}** (${r.path})${scopeNote}\n ${r.description}`;
}).join("\n\n");
const footer = `\n\n---\nNext steps:\n- Read any doc above using its resource URI\n- Use search_docs to find specific topics within these docs\n- Use get_doc_tree to see the full documentation structure`;
return { content: [{ type: "text", text: `Documentation applicable to "${p}":\n\n${text}${footer}` }] };
}
6c: get-doc-tree.js
Filterable documentation tree with coverage status. Embeds the full directory tree data.
// Auto-generated by mcp-doc-init — do not edit manually
// Regenerate with /mcp-doc-sync
const DIRS = [
// { path: "src", depth: 1, documented: true, docFile: "src/README.md" },
// { path: "src/api", depth: 2, documented: true, docFile: "src/api/README.md" },
// { path: "src/utils", depth: 2, documented: false, docFile: null },
];
export default async function(input, ctx) {
const root = input.root || "";
const maxDepth = input.maxDepth || 3;
const undocOnly = input.undocumentedOnly || false;
const rootDepthOffset = root === "" ? 0 : root.split("/").length;
let filtered = DIRS.filter(d =>
(root === "" || d.path === root || d.path.startsWith(root + "/")) &&
d.depth <= maxDepth + rootDepthOffset &&
(!undocOnly || !d.documented)
);
if (filtered.length === 0) {
return { content: [{ type: "text", text: root ? `No directories found under "${root}".` : "No directories found." }] };
}
const total = filtered.length;
const documented = filtered.filter(d => d.documented).length;
const coverage = total > 0 ? Math.round((documented / total) * 100) : 0;
const lines = filtered.map(d => {
const indent = " ".repeat(d.depth - rootDepthOffset - (root === "" ? 0 : 0));
const icon = d.documented ? "[documented]" : "[no docs]";
const docNote = d.docFile ? ` <- ${d.docFile}` : "";
return `${indent}${d.path}/ ${icon}${docNote}`;
});
const header = `Documentation coverage: ${documented}/${total} directories (${coverage}%)\n`;
const footer = `\n\n---\nNext steps:\n- Use search_docs to find specific topics\n- Use get_applicable_docs to find docs for a specific source file\n- Filter with undocumentedOnly: true to find gaps`;
const text = header + "\n" + lines.join("\n") + footer;
return { content: [{ type: "text", text }] };
}
Hash computation
After writing each action file, compute its SHA-256 hash and add the tool entry to .mcp/manifest.yml:
sha256sum .mcp/actions/search-docs.js | awk '{print "sha256:" $1}'
sha256sum .mcp/actions/get-applicable-docs.js | awk '{print "sha256:" $1}'
sha256sum .mcp/actions/get-doc-tree.js | awk '{print "sha256:" $1}'
Add tool entries to the manifest with the computed hashes:
tools:
- name: search_docs
title: Search Documentation
description: |
Search project documentation by keyword.
Searches titles, section headers, summaries, tags, and file paths.
USE THIS WHEN:
- The user asks a question about the project
- The user mentions a keyword, error, or concept
- You need to find relevant documentation before answering
- You're unsure which tool to use (this is the safest default)
DO NOT USE WHEN:
- You know the exact file path (use get_applicable_docs instead)
- The user wants to see the documentation tree/structure (use get_doc_tree)
Example: "How does authentication work?" / "What is the API rate limit?"
inputSchema:
type: object
properties:
query:
type: string
description: "Search keyword or phrase"
required: [query]
action: ./actions/search-docs.js
actionHash: sha256:{computed-hash}
annotations:
readOnlyHint: true
- name: get_applicable_docs
title: Get Applicable Documentation
description: |
Given a source file or directory path, find all documentation that applies to it.
Returns parent READMEs, scope-matched docs, and global docs sorted by specificity.
USE THIS WHEN:
- The user asks "what documentation covers this file/directory?"
- You need context before modifying a specific file
- The user provides a file path and wants related docs
DO NOT USE WHEN:
- The user has a general question without a specific file (use search_docs)
- The user wants to browse the doc tree (use get_doc_tree)
Example: "What docs apply to src/api/routes/users.ts?" / "Show me docs for the auth module"
inputSchema:
type: object
properties:
path:
type: string
description: "Source file or directory path (e.g., src/api/routes/users.ts)"
required: [path]
action: ./actions/get-applicable-docs.js
actionHash: sha256:{computed-hash}
annotations:
readOnlyHint: true
- name: get_doc_tree
title: Documentation Tree
description: |
Get the project documentation tree with coverage status.
Supports filtering by subtree, depth, and undocumented-only mode.
USE THIS WHEN:
- The user wants to see what documentation exists
- The user asks about documentation coverage or gaps
- The user wants to browse the project structure
DO NOT USE WHEN:
- The user has a specific question (use search_docs)
- The user asks about a specific file (use get_applicable_docs)
Example: "What directories have documentation?" / "Show me the doc tree" / "Which areas are undocumented?"
inputSchema:
type: object
properties:
root:
type: string
description: "Subtree root path (default: project root)"
maxDepth:
type: number
description: "Maximum depth to display (default: 3)"
undocumentedOnly:
type: boolean
description: "Only show undocumented directories (default: false)"
action: ./actions/get-doc-tree.js
actionHash: sha256:{computed-hash}
annotations:
readOnlyHint: true
Step 7: Generate/Update .mcp.json
Read the existing .mcp.json if present. Preserve any existing MCP server entries. Add (or update) the git-doc-mcp entry using the project name from Step 4.
{
"mcpServers": {
"{project-name}-docs": {
"command": "npx",
"args": ["-y", "[email protected]", "--manifest", ".mcp/manifest.yml"]
}
}
}
If .mcp.json already exists with other servers, merge the new entry into the existing mcpServers object. Do not overwrite other entries.
Step 8: Report
Present the final summary:
MCP Doc initialized for {project-name}.
Manifest: .mcp/manifest.yml
MCP config: .mcp.json
Server name: {project-name}-docs
Resources: N documentation files indexed
Tools: 3 (search_docs, get_applicable_docs, get_doc_tree)
Indexed documentation:
- M READMEs
- K standalone docs
- J docs/ directory files
- P ADRs
Undocumented directories: X (run /mcp-doc-scan for details)
Next steps:
- Run /mcp-doc-scan to see coverage report
- Run /mcp-doc-generate <path> to create docs for undocumented areas
- Run /mcp-doc-sync after adding or changing documentation files
- Run /mcp-doc-add-tool to create custom search/filter tools
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?