Skip to content

Commit 92d8a3f

Browse files
authored
Merge branch 'documentation-reorg' into final-changes
2 parents 158e3d3 + ee89f26 commit 92d8a3f

14 files changed

+249
-307
lines changed

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

Lines changed: 60 additions & 57 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.")
117-
EOF
118+
print("Success: Source pages validated.")
119+
sys.exit(0)
120+
EOF

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

Lines changed: 39 additions & 23 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
@@ -115,4 +131,4 @@ if has_errors:
115131
sys.exit(1)
116132
else:
117133
print("Success: All Tool pages passed structure validation.")
118-
EOF
134+
EOF

.github/workflows/deploy_previous_version_docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,4 @@ jobs:
111111
publish_branch: versioned-gh-pages
112112
keep_files: true
113113
allow_empty_commit: true
114-
commit_message: "deploy: docs to root for ${{ github.event.inputs.version_tag }}"
114+
commit_message: "deploy: docs to root for ${{ github.event.inputs.version_tag }}"

.github/workflows/deploy_previous_version_docs_to_cf.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ jobs:
7373
HUGO_RELATIVEURLS: false
7474
HUGO_PARAMS_VERSION: ${{ github.event.inputs.version_tag }}
7575

76-
- name: Build Pagefind Index (Archived Version)
76+
- name: Build Pagefind Index (Root)
7777
run: npx pagefind --site public
7878
working-directory: .hugo
7979

.github/workflows/docs_lint.yaml

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ on:
2323

2424
jobs:
2525
lint-source-pages:
26-
name: Validate Source Page Structure
26+
name: Lint Documentation
2727
runs-on: ubuntu-latest
2828

2929
steps:
@@ -35,26 +35,19 @@ jobs:
3535
with:
3636
python-version: '3.x'
3737

38-
- name: Check for large files in docs/
38+
- name: Check for large files (>24MB)
3939
run: |
40-
if [ -d "docs" ]; then
41-
LARGE_FILES=$(find docs/ -type f -size +24M)
42-
if [ -n "$LARGE_FILES" ]; then
43-
echo "Error: The following files in the docs/ directory exceed the 24MB size limit:"
44-
echo "$LARGE_FILES"
45-
exit 1
46-
else
47-
echo "Success: No files in docs/ exceed 24MB."
48-
fi
49-
else
50-
echo "Info: docs/ directory not found. Skipping file size check."
40+
LARGE_FILES=$(find docs/ -type f -size +24M)
41+
if [ -n "$LARGE_FILES" ]; then
42+
echo "Error: Files exceed 24MB limit: $LARGE_FILES"
43+
exit 1
5144
fi
5245
5346
- name: Make scripts executable
5447
run: chmod +x .ci/lint-docs-*.sh
5548

5649
- name: Run Structure Linter for Source Pages
57-
run: .ci/lint-docs-source-page.sh
50+
run: bash .ci/lint-docs-source-page.sh
5851

5952
- name: Run Structure Linter for Tool Pages
60-
run: .ci/lint-docs-tool-page.sh
53+
run: bash .ci/lint-docs-tool-page.sh

.github/workflows/docs_preview_deploy_cf.yaml

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
name: "CF: Deploy Docs Preview"
16-
17-
on:
18-
workflow_run:
19-
workflows: ["CF: Build Docs Preview"]
20-
types:
21-
- completed
22-
workflow_dispatch:
23-
inputs:
24-
pr_number:
25-
description: 'PR Number to deploy (Manual override)'
26-
required: true
27-
type: string
28-
build_run_id:
29-
description: 'The Run ID from the successful "CF: Build Docs Preview" workflow'
30-
required: true
31-
type: string
15+
name: "CF: Deploy PR Preview"
3216

3317
permissions:
3418
contents: read

0 commit comments

Comments
 (0)