Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 4 additions & 2 deletions .ci/lint-docs-source-page.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ MAINTENANCE GUIDE:
Markdown before linting.

4. SCOPE:
This script ignores top-level directory files and only targets
integrations/{provider}/_index.md.
This script targets ONLY the top-level _index.md of an integration.
"""

import os
Expand Down Expand Up @@ -58,6 +57,9 @@ for filepath in integration_dir.rglob("_index.md"):
if filepath.parent == integration_dir:
continue

if filepath.parent.parent != integration_dir:
continue

with open(filepath, "r", encoding="utf-8") as f:
content = f.read()

Expand Down
85 changes: 53 additions & 32 deletions .ci/lint-docs-tool-page.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,29 @@ python3 - << 'EOF'
MCP TOOLBOX: TOOL PAGE LINTER
=============================
This script enforces a standardized structure for individual Tool pages
(e.g., integrations/postgres/postgres-sql.md). It ensures that LLM agents
can parse tool capabilities and parameter definitions reliably.
and their parent directory wrappers. It ensures LLM agents can parse
tool capabilities and parameter definitions reliably.

MAINTENANCE GUIDE:
------------------
1. TO ADD A NEW HEADING:
Add the exact heading text to the 'ALLOWED_ORDER' list in the desired
sequence.

2. TO MAKE A HEADING MANDATORY:
Add the heading text to the 'REQUIRED' set.
2. TO MAKE A HEADING MANDATORY/OPTIONAL:
Add or remove the heading text in the 'REQUIRED' set.

3. TO UPDATE SHORTCODE LOGIC:
If the shortcode name changes, update 'SHORTCODE_PATTERN'.

4. SCOPE:
This script targets all .md files in integrations/ EXCEPT _index.md files.
If the shortcode name changes, update the 'SHORTCODE_PATTERN' variable.

4. SCOPE & BEHAVIOR:
This script targets all .md files in docs/en/integrations/**/tools/.
- For `_index.md` files: It only validates the frontmatter (requiring
`title: "Tools"` and `weight: 2`) and ignores the body.
- For regular tool files: It validates H1/H2 hierarchy, checks for
required headings ("About", "Example"), and enforces that the
`{{< compatible-sources >}}` shortcode is paired with the
"## Compatible Sources" heading.
"""

import os
Expand Down Expand Up @@ -53,12 +59,12 @@ if not integration_dir.exists():
sys.exit(0)

has_errors = False
tools_pages_found = 0

# Find all .md files, excluding _index.md (which are Source pages)
for filepath in integration_dir.rglob("*.md"):
if filepath.name == "_index.md":
continue

# Specifically target the tools directories
for filepath in integration_dir.rglob("tools/*.md"):
tools_pages_found += 1

with open(filepath, "r", encoding="utf-8") as f:
content = f.read()

Expand All @@ -68,23 +74,34 @@ for filepath in integration_dir.rglob("*.md"):
frontmatter = match.group(1)
body = match.group(2)
else:
frontmatter = ""
body = content

if not body.strip():
print(f"[{filepath}] Error: Missing or invalid YAML frontmatter.")
has_errors = True
continue

file_errors = False

# 1. Check Frontmatter Title
title_source = frontmatter if frontmatter else content
title_match = re.search(r"^title:\s*[\"']?(.*?)[\"']?\s*$", title_source, re.MULTILINE)
if not title_match or not title_match.group(1).strip().endswith("Tool"):
found_title = title_match.group(1) if title_match else "None"
print(f"[{filepath}] Error: Frontmatter title must end with 'Tool'. Found: '{found_title}'")
file_errors = True
# --- SPECIAL VALIDATION FOR tools/_index.md ---
if filepath.name == "_index.md":
title_match = re.search(r"^title:\s*[\"']?(.*?)[\"']?\s*$", frontmatter, re.MULTILINE)
if not title_match or title_match.group(1).strip() != "Tools":
print(f"[{filepath}] Error: tools/_index.md must have exactly title: \"Tools\"")
file_errors = True

weight_match = re.search(r"^weight:\s*(\d+)\s*$", frontmatter, re.MULTILINE)
if not weight_match or weight_match.group(1).strip() != "2":
print(f"[{filepath}] Error: tools/_index.md must have exactly weight: 2")
file_errors = True

if file_errors:
has_errors = True
continue # Skip the rest of the body linting for this structural file

# 2. Check Shortcode Placement
# --- VALIDATION FOR REGULAR TOOL PAGES ---
# If the file has no markdown content (metadata placeholder only), skip it entirely
if not body.strip():
continue

# 1. Check Shortcode Placement
sources_section_match = re.search(r"^##\s+Compatible Sources\s*(.*?)(?=^##\s|\Z)", body, re.MULTILINE | re.DOTALL)
if sources_section_match:
if not re.search(SHORTCODE_PATTERN, sources_section_match.group(1)):
Expand All @@ -94,16 +111,17 @@ for filepath in integration_dir.rglob("*.md"):
print(f"[{filepath}] Error: Shortcode found, but '## Compatible Sources' heading is missing.")
file_errors = True

# 3. Strip code blocks
clean_body = re.sub(r"^(?:```|~~~).*?^(?:```|~~~)", "", body, flags=re.DOTALL | re.MULTILINE)
# 2. Strip code blocks from body to avoid linting example markdown headings
clean_body = re.sub(r"```.*?```", "", body, flags=re.DOTALL)

# 4. Check H1 Headings
# 3. Check H1 Headings
if re.search(r"^#\s+\w+", clean_body, re.MULTILINE):
print(f"[{filepath}] Error: H1 headings (#) are forbidden in the body.")
file_errors = True

# 5. Check H2 Headings
h2s = [h.strip() for h in re.findall(r"^##\s+(.*)", clean_body, re.MULTILINE)]
# 4. Check H2 Headings
h2s = re.findall(r"^##\s+(.*)", clean_body, re.MULTILINE)
h2s = [h2.strip() for h2 in h2s]

# Missing Required
if missing := (REQUIRED - set(h2s)):
Expand All @@ -127,9 +145,12 @@ for filepath in integration_dir.rglob("*.md"):
if file_errors:
has_errors = True

if has_errors:
if tools_pages_found == 0:
print("Info: No tool directories found. Passing gracefully.")
sys.exit(0)
elif has_errors:
print("Linting failed for Tool pages. Please fix the structure errors above.")
sys.exit(1)
else:
print("Success: All Tool pages passed structure validation.")
print(f"Success: All {tools_pages_found} Tool page(s) passed structure validation.")
EOF
2 changes: 2 additions & 0 deletions docs/en/integrations/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ type: docs
weight: 4
description: >
Integrations connect the MCP Toolbox to your external data sources, unlocking specific sets of tools for your agents.
no_list: true
---

An **Integration** represents a connection to a database or a HTTP Server.
Expand All @@ -13,3 +14,4 @@ You can define the connection the **Source** just once in your `tools.yaml` file
## Exploring Integrations & Tools

Select an integration below to view its configuration requirements. Depending on the integration, the documentation will provide the tools.yaml snippets needed to establish a source connection, detail any specific tools available to your agents, or both.

48 changes: 2 additions & 46 deletions docs/en/integrations/alloydb-admin/_index.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,4 @@
---
title: "AlloyDB Admin Source"
linkTitle: AlloyDB Admin
type: docs
title: "AlloyDB Admin"
weight: 1
description: "The \"alloydb-admin\" source provides a client for the AlloyDB API.\n"
no_list: true
---

## About

The `alloydb-admin` source provides a client to interact with the [Google
AlloyDB API](https://cloud.google.com/alloydb/docs/reference/rest). This allows
tools to perform administrative tasks on AlloyDB resources, such as managing
clusters, instances, and users.

Authentication can be handled in two ways:

1. **Application Default Credentials (ADC):** By default, the source uses ADC
to authenticate with the API.
2. **Client-side OAuth:** If `useClientOAuth` is set to `true`, the source will
expect an OAuth 2.0 access token to be provided by the client (e.g., a web
browser) for each request.

## Available Tools

{{< list-tools >}}

## Example

```yaml
kind: sources
name: my-alloydb-admin
type: alloydb-admin
---
kind: sources
name: my-oauth-alloydb-admin
type: alloydb-admin
useClientOAuth: true
```

## Reference

| **field** | **type** | **required** | **description** |
| -------------- | :------: | :----------: | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| type | string | true | Must be "alloydb-admin". |
| defaultProject | string | false | The Google Cloud project ID to use for AlloyDB infrastructure tools. |
| useClientOAuth | boolean | false | If true, the source will use client-side OAuth for authorization. Otherwise, it will use Application Default Credentials. Defaults to `false`. |
---
48 changes: 48 additions & 0 deletions docs/en/integrations/alloydb-admin/source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: "AlloyDB Admin"
linkTitle: "Source"
type: docs
weight: 1
description: "The \"alloydb-admin\" source provides a client for the AlloyDB API.\n"
no_list: true
---

## About

The `alloydb-admin` source provides a client to interact with the [Google
AlloyDB API](https://cloud.google.com/alloydb/docs/reference/rest). This allows
tools to perform administrative tasks on AlloyDB resources, such as managing
clusters, instances, and users.

Authentication can be handled in two ways:

1. **Application Default Credentials (ADC):** By default, the source uses ADC
to authenticate with the API.
2. **Client-side OAuth:** If `useClientOAuth` is set to `true`, the source will
expect an OAuth 2.0 access token to be provided by the client (e.g., a web
browser) for each request.

## Available Tools

{{< list-tools >}}

## Example

```yaml
kind: sources
name: my-alloydb-admin
type: alloydb-admin
---
kind: sources
name: my-oauth-alloydb-admin
type: alloydb-admin
useClientOAuth: true
```

## Reference

| **field** | **type** | **required** | **description** |
| -------------- | :------: | :----------: | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| type | string | true | Must be "alloydb-admin". |
| defaultProject | string | false | The Google Cloud project ID to use for AlloyDB infrastructure tools. |
| useClientOAuth | boolean | false | If true, the source will use client-side OAuth for authorization. Otherwise, it will use Application Default Credentials. Defaults to `false`. |
4 changes: 4 additions & 0 deletions docs/en/integrations/alloydb-admin/tools/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: "Tools"
weight: 2
---
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: alloydb-create-cluster Tool
title: alloydb-create-cluster
type: docs
weight: 1
description: "The \"alloydb-create-cluster\" tool creates a new AlloyDB for PostgreSQL cluster in a specified project and location.\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: alloydb-create-instance Tool
title: alloydb-create-instance
type: docs
weight: 1
description: "The \"alloydb-create-instance\" tool creates a new AlloyDB instance within a specified cluster.\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: alloydb-create-user Tool
title: alloydb-create-user
type: docs
weight: 2
description: "The \"alloydb-create-user\" tool creates a new database user within a specified AlloyDB cluster.\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: alloydb-get-cluster Tool
title: alloydb-get-cluster
type: docs
weight: 1
description: "The \"alloydb-get-cluster\" tool retrieves details for a specific AlloyDB cluster.\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: alloydb-get-instance Tool
title: alloydb-get-instance
type: docs
weight: 1
description: "The \"alloydb-get-instance\" tool retrieves details for a specific AlloyDB instance.\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: alloydb-get-user Tool
title: alloydb-get-user
type: docs
weight: 1
description: "The \"alloydb-get-user\" tool retrieves details for a specific AlloyDB user.\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: alloydb-list-clusters Tool
title: alloydb-list-clusters
type: docs
weight: 1
description: "The \"alloydb-list-clusters\" tool lists the AlloyDB clusters in a given project and location.\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: alloydb-list-instances Tool
title: alloydb-list-instances
type: docs
weight: 1
description: "The \"alloydb-list-instances\" tool lists the AlloyDB instances for a given project, cluster and location.\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: alloydb-list-users Tool
title: alloydb-list-users
type: docs
weight: 1
description: "The \"alloydb-list-users\" tool lists all database users within an AlloyDB cluster.\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: alloydb-wait-for-operation Tool
title: alloydb-wait-for-operation
type: docs
weight: 10
description: "Wait for a long-running AlloyDB operation to complete.\n"
Expand Down
Loading
Loading