Skip to content

Commit 922dcf4

Browse files
authored
Merge branch 'documentation-reorg' into google-analytics
2 parents 9ab6076 + 7679d0a commit 922dcf4

17 files changed

+244
-325
lines changed

.ci/lint-docs-source-page.sh

Lines changed: 59 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,36 @@
22
set -e
33

44
python3 - << 'EOF'
5+
"""
6+
MCP TOOLBOX: SOURCE PAGE LINTER
7+
This script enforces a standardized structure for integration Source pages
8+
(_index.md files). It ensures users can predictably find
9+
information across all database integrations.
10+
11+
MAINTENANCE GUIDE:
12+
------------------
13+
1. TO ADD A NEW HEADING:
14+
Add the exact heading text to the 'ALLOWED_ORDER' list in the desired
15+
sequence.
16+
17+
2. TO MAKE A HEADING MANDATORY:
18+
Add the heading text to the 'REQUIRED' set.
19+
20+
3. TO IGNORE NEW CONTENT TYPES:
21+
Update the regex in the 'clean_body' variable (Step 3) to strip out
22+
Markdown before linting.
23+
24+
4. SCOPE:
25+
This script ignores top-level directory files and only targets
26+
integrations/{provider}/_index.md.
27+
"""
28+
529
import os
630
import re
731
import sys
832
from pathlib import Path
933
10-
integration_dir = Path("./docs/en/integrations")
11-
if not integration_dir.exists():
12-
print("Info: Directory './docs/en/integrations' not found. Skipping linting.")
13-
sys.exit(0)
14-
34+
# --- CONFIGURATION ---
1535
ALLOWED_ORDER = [
1636
"About",
1737
"Available Tools",
@@ -23,95 +43,78 @@ ALLOWED_ORDER = [
2343
"Additional Resources"
2444
]
2545
REQUIRED = {"About", "Example", "Reference"}
26-
27-
# Regex to catch any variation of the list-tools shortcode, including parameters
2846
SHORTCODE_PATTERN = r"\{\{<\s*list-tools.*?>\}\}"
47+
# ---------------------
48+
49+
integration_dir = Path("./docs/en/integrations")
50+
if not integration_dir.exists():
51+
print("Info: Directory './docs/en/integrations' not found. Skipping linting.")
52+
sys.exit(0)
2953
3054
has_errors = False
3155
32-
# Find all _index.md files inside the subdirectories of integrations/
3356
for filepath in integration_dir.rglob("_index.md"):
34-
# Skip the top-level integrations/_index.md if it exists
3557
if filepath.parent == integration_dir:
3658
continue
3759
3860
with open(filepath, "r", encoding="utf-8") as f:
3961
content = f.read()
4062
41-
# Separate YAML frontmatter from the markdown body
4263
match = re.match(r'^\s*---\s*\n(.*?)\n---\s*(.*)', content, re.DOTALL)
4364
if match:
44-
frontmatter = match.group(1)
45-
body = match.group(2)
65+
frontmatter, body = match.group(1), match.group(2)
4666
else:
47-
frontmatter = ""
48-
body = content
67+
frontmatter, body = "", content
4968
50-
# If the file has no markdown content (metadata placeholder only), skip it entirely
5169
if not body.strip():
5270
continue
5371
5472
file_errors = False
5573
56-
# 1. Check Frontmatter Title
57-
title_source = frontmatter if frontmatter else content
58-
title_match = re.search(r"^title:\s*[\"']?(.*?)[\"']?\s*$", title_source, re.MULTILINE)
74+
# 1. Frontmatter Title Check
75+
title_match = re.search(r"^title:\s*[\"']?(.*?)[\"']?\s*$", frontmatter if frontmatter else content, re.MULTILINE)
5976
if not title_match or not title_match.group(1).strip().endswith("Source"):
60-
found_title = title_match.group(1) if title_match else "None"
61-
print(f"[{filepath}] Error: Frontmatter title must end with 'Source'. Found: '{found_title}'")
77+
print(f"[{filepath}] Error: Title must end with 'Source'.")
6278
file_errors = True
6379
64-
# 2. Check Shortcode Placement ONLY IF "Available Tools" heading is present
65-
tools_section_match = re.search(r"^##\s+Available Tools\s*(.*?)(?=^##\s|\Z)", body, re.MULTILINE | re.DOTALL)
66-
if tools_section_match:
67-
if not re.search(SHORTCODE_PATTERN, tools_section_match.group(1)):
68-
print(f"[{filepath}] Error: The list-tools shortcode must be placed under the '## Available Tools' heading.")
69-
file_errors = True
70-
else:
71-
# Prevent edge case where shortcode is used but the heading was forgotten
72-
if re.search(SHORTCODE_PATTERN, body):
73-
print(f"[{filepath}] Error: A list-tools shortcode was found, but the '## Available Tools' heading is missing.")
80+
# 2. Shortcode Placement Check
81+
tools_section = re.search(r"^##\s+Available Tools\s*(.*?)(?=^##\s|\Z)", body, re.MULTILINE | re.DOTALL)
82+
if tools_section:
83+
if not re.search(SHORTCODE_PATTERN, tools_section.group(1)):
84+
print(f"[{filepath}] Error: {{< list-tools >}} must be under '## Available Tools'.")
7485
file_errors = True
86+
elif re.search(SHORTCODE_PATTERN, body):
87+
print(f"[{filepath}] Error: {{< list-tools >}} found, but '## Available Tools' heading is missing.")
88+
file_errors = True
7589
76-
# 3. Strip code blocks from body to avoid linting example markdown headings
90+
# 3. Heading Linting (Stripping code blocks first)
7791
clean_body = re.sub(r"```.*?```", "", body, flags=re.DOTALL)
7892
79-
# 4. Check H1 Headings
8093
if re.search(r"^#\s+\w+", clean_body, re.MULTILINE):
81-
print(f"[{filepath}] Error: H1 headings (#) are forbidden in the body.")
94+
print(f"[{filepath}] Error: H1 (#) headings are forbidden in the body.")
8295
file_errors = True
8396
84-
# 5. Check H2 Headings
85-
h2s = re.findall(r"^##\s+(.*)", clean_body, re.MULTILINE)
86-
h2s = [h2.strip() for h2 in h2s]
97+
h2s = [h.strip() for h in re.findall(r"^##\s+(.*)", clean_body, re.MULTILINE)]
8798
88-
# Missing Required
89-
missing = REQUIRED - set(h2s)
90-
if missing:
91-
print(f"[{filepath}] Error: Missing required H2 headings: {missing}")
99+
# 4. Required & Unauthorized Check
100+
if missing := (REQUIRED - set(h2s)):
101+
print(f"[{filepath}] Error: Missing required H2s: {missing}")
92102
file_errors = True
93103
94-
# Unauthorized Headings
95-
unauthorized = set(h2s) - set(ALLOWED_ORDER)
96-
if unauthorized:
97-
print(f"[{filepath}] Error: Unauthorized H2 headings found: {unauthorized}")
104+
if unauthorized := (set(h2s) - set(ALLOWED_ORDER)):
105+
print(f"[{filepath}] Error: Unauthorized H2s found: {unauthorized}")
98106
file_errors = True
99107
100-
# Strict Ordering
101-
filtered_h2s = [h for h in h2s if h in ALLOWED_ORDER]
102-
expected_order = [h for h in ALLOWED_ORDER if h in h2s]
103-
if filtered_h2s != expected_order:
104-
print(f"[{filepath}] Error: Headings are out of order.")
105-
print(f" Expected: {expected_order}")
106-
print(f" Found: {filtered_h2s}")
108+
# 5. Order Check
109+
if [h for h in h2s if h in ALLOWED_ORDER] != [h for h in ALLOWED_ORDER if h in h2s]:
110+
print(f"[{filepath}] Error: Headings out of order. Reference: {ALLOWED_ORDER}")
107111
file_errors = True
108112
109-
if file_errors:
110-
has_errors = True
113+
if file_errors: has_errors = True
111114
112115
if has_errors:
113-
print("Linting failed. Please fix the structure errors above.")
116+
print("Linting failed. Fix structure errors above.")
114117
sys.exit(1)
115-
else:
116-
print("Success: All Source pages passed structure validation.")
118+
print("Success: Source pages validated.")
119+
sys.exit(0)
117120
EOF

.ci/lint-docs-tool-page.sh

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,34 @@
22
set -e
33

44
python3 - << 'EOF'
5+
"""
6+
MCP TOOLBOX: TOOL PAGE LINTER
7+
This script enforces a standardized structure for individual Tool pages
8+
(e.g., integrations/postgres/postgres-sql.md). It ensures that LLM agents
9+
can parse tool capabilities and parameter definitions reliably.
10+
11+
MAINTENANCE GUIDE:
12+
------------------
13+
1. TO ADD A NEW HEADING:
14+
Add the exact heading text to the 'ALLOWED_ORDER' list in the desired
15+
sequence.
16+
17+
2. TO MAKE A HEADING MANDATORY:
18+
Add the heading text to the 'REQUIRED' set.
19+
20+
3. TO UPDATE SHORTCODE LOGIC:
21+
If the shortcode name changes, update 'SHORTCODE_PATTERN'.
22+
23+
4. SCOPE:
24+
This script targets all .md files in integrations/ EXCEPT _index.md files.
25+
"""
26+
527
import os
628
import re
729
import sys
830
from pathlib import Path
931
10-
integration_dir = Path("./docs/en/integrations")
11-
if not integration_dir.exists():
12-
print("Info: Directory './docs/en/integrations' not found. Skipping linting.")
13-
sys.exit(0)
14-
32+
# --- CONFIGURATION ---
1533
ALLOWED_ORDER = [
1634
"About",
1735
"Compatible Sources",
@@ -25,9 +43,13 @@ ALLOWED_ORDER = [
2543
"Additional Resources"
2644
]
2745
REQUIRED = {"About", "Example"}
28-
29-
# Regex to catch any variation of the compatible-sources shortcode
3046
SHORTCODE_PATTERN = r"\{\{<\s*compatible-sources.*?>\}\}"
47+
# ---------------------
48+
49+
integration_dir = Path("./docs/en/integrations")
50+
if not integration_dir.exists():
51+
print("Info: Directory './docs/en/integrations' not found. Skipping linting.")
52+
sys.exit(0)
3153
3254
has_errors = False
3355
@@ -48,7 +70,6 @@ for filepath in integration_dir.rglob("*.md"):
4870
frontmatter = ""
4971
body = content
5072
51-
# If the file has no markdown content (metadata placeholder only), skip it entirely
5273
if not body.strip():
5374
continue
5475
@@ -66,35 +87,30 @@ for filepath in integration_dir.rglob("*.md"):
6687
sources_section_match = re.search(r"^##\s+Compatible Sources\s*(.*?)(?=^##\s|\Z)", body, re.MULTILINE | re.DOTALL)
6788
if sources_section_match:
6889
if not re.search(SHORTCODE_PATTERN, sources_section_match.group(1)):
69-
print(f"[{filepath}] Error: The compatible-sources shortcode must be placed under the '## Compatible Sources' heading.")
70-
file_errors = True
71-
else:
72-
# Prevent edge case where shortcode is used but the heading was forgotten
73-
if re.search(SHORTCODE_PATTERN, body):
74-
print(f"[{filepath}] Error: A compatible-sources shortcode was found, but the '## Compatible Sources' heading is missing.")
90+
print(f"[{filepath}] Error: The compatible-sources shortcode must be placed under '## Compatible Sources'.")
7591
file_errors = True
92+
elif re.search(SHORTCODE_PATTERN, body):
93+
print(f"[{filepath}] Error: Shortcode found, but '## Compatible Sources' heading is missing.")
94+
file_errors = True
7695
77-
# 3. Strip code blocks from body to avoid linting example markdown headings
78-
clean_body = re.sub(r"```.*?```", "", body, flags=re.DOTALL)
96+
# 3. Strip code blocks
97+
clean_body = re.sub(r"^(?:```|~~~).*?^(?:```|~~~)", "", body, flags=re.DOTALL | re.MULTILINE)
7998
8099
# 4. Check H1 Headings
81100
if re.search(r"^#\s+\w+", clean_body, re.MULTILINE):
82101
print(f"[{filepath}] Error: H1 headings (#) are forbidden in the body.")
83102
file_errors = True
84103
85104
# 5. Check H2 Headings
86-
h2s = re.findall(r"^##\s+(.*)", clean_body, re.MULTILINE)
87-
h2s = [h2.strip() for h2 in h2s]
105+
h2s = [h.strip() for h in re.findall(r"^##\s+(.*)", clean_body, re.MULTILINE)]
88106
89107
# Missing Required
90-
missing = REQUIRED - set(h2s)
91-
if missing:
108+
if missing := (REQUIRED - set(h2s)):
92109
print(f"[{filepath}] Error: Missing required H2 headings: {missing}")
93110
file_errors = True
94111
95112
# Unauthorized Headings
96-
unauthorized = set(h2s) - set(ALLOWED_ORDER)
97-
if unauthorized:
113+
if unauthorized := (set(h2s) - set(ALLOWED_ORDER)):
98114
print(f"[{filepath}] Error: Unauthorized H2 headings found: {unauthorized}")
99115
file_errors = True
100116

.github/workflows/deploy_dev_docs_to_cf.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,6 @@ jobs:
7171
HUGO_BASEURL: https://mcp-toolbox.dev/dev/
7272
HUGO_RELATIVEURLS: false
7373

74-
- name: Build Pagefind Search Index
75-
run: npx pagefind --site public
76-
7774
- name: Create Staging Directory
7875
run: |
7976
mkdir staging

.github/workflows/deploy_previous_version_docs.yaml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ jobs:
7373
HUGO_RELATIVEURLS: false
7474
HUGO_PARAMS_VERSION: ${{ github.event.inputs.version_tag }}
7575

76-
- name: Build Pagefind Index (Archived Version)
77-
run: npx pagefind --site public
78-
working-directory: .hugo
79-
8076
- name: Deploy to gh-pages
8177
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
8278
with:
@@ -99,10 +95,6 @@ jobs:
9995
HUGO_RELATIVEURLS: false
10096
HUGO_PARAMS_VERSION: ${{ github.event.inputs.version_tag }}
10197

102-
- name: Build Pagefind Index (Root)
103-
run: npx pagefind --site public
104-
working-directory: .hugo
105-
10698
- name: Deploy to root
10799
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
108100
with:

.github/workflows/deploy_previous_version_docs_to_cf.yaml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ jobs:
7373
HUGO_RELATIVEURLS: false
7474
HUGO_PARAMS_VERSION: ${{ github.event.inputs.version_tag }}
7575

76-
- name: Build Pagefind Index (Archived Version)
77-
run: npx pagefind --site public
78-
working-directory: .hugo
79-
8076
- name: Deploy to cloudflare-pages
8177
uses: peaceiris/actions-gh-pages@v4
8278
with:
@@ -99,10 +95,6 @@ jobs:
9995
HUGO_RELATIVEURLS: false
10096
HUGO_PARAMS_VERSION: ${{ github.event.inputs.version_tag }}
10197

102-
- name: Build Pagefind Index (Root)
103-
run: npx pagefind --site public
104-
working-directory: .hugo
105-
10698
- name: Deploy to root
10799
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
108100
with:

.github/workflows/deploy_versioned_docs.yaml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,6 @@ jobs:
6363
HUGO_RELATIVEURLS: false
6464
HUGO_PARAMS_VERSION: ${{ steps.get_version.outputs.VERSION }}
6565

66-
- name: Build Pagefind Index (Versioned)
67-
run: npx pagefind --site public
68-
working-directory: .hugo
69-
7066
- name: Deploy
7167
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
7268
with:
@@ -88,10 +84,6 @@ jobs:
8884
HUGO_RELATIVEURLS: false
8985
HUGO_PARAMS_VERSION: ${{ steps.get_version.outputs.VERSION }}
9086

91-
- name: Build Pagefind Index (Root)
92-
run: npx pagefind --site public
93-
working-directory: .hugo
94-
9587
- name: Deploy to root
9688
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
9789
with:

.github/workflows/deploy_versioned_docs_to_cf.yaml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,6 @@ jobs:
6363
HUGO_RELATIVEURLS: false
6464
HUGO_PARAMS_VERSION: ${{ steps.get_version.outputs.VERSION }}
6565

66-
- name: Build Pagefind Index (Versioned)
67-
run: npx pagefind --site public
68-
working-directory: .hugo
69-
7066
- name: Deploy
7167
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
7268
with:
@@ -88,10 +84,6 @@ jobs:
8884
HUGO_RELATIVEURLS: false
8985
HUGO_PARAMS_VERSION: ${{ steps.get_version.outputs.VERSION }}
9086

91-
- name: Build Pagefind Index (Root)
92-
run: npx pagefind --site public
93-
working-directory: .hugo
94-
9587
- name: Deploy to root
9688
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
9789
with:

0 commit comments

Comments
 (0)