Skip to content
Open
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "planetscale",
"version": "1.1.0",
"version": "1.2.0",
"description": "An authenticated hosted MCP server that accesses your PlanetScale organizations, databases, branches, schema, and Insights data.",
"author": {
"name": "PlanetScale",
Expand All @@ -18,11 +18,11 @@
"mcp",
"skills"
],
"skills": "./database-skills/skills/",
"skills": "./skills/",
"mcpServers": "./.mcp.json",
"interface": {
"displayName": "PlanetScale",
"shortDescription": "PlanetScale MCP server and skills",
"shortDescription": "PlanetScale MCP and skills",
"longDescription": "Connect Codex to PlanetScale with the official hosted MCP server for organizations, databases, branches, schema, and Insights, plus PlanetScale operating skills and Database Skills for MySQL, Postgres, Vitess, and Neki.",
"developerName": "PlanetScale",
"category": "Developer Tools",
Expand All @@ -33,6 +33,9 @@
"websiteURL": "https://planetscale.com",
"privacyPolicyURL": "https://planetscale.com/legal/privacy",
"termsOfServiceURL": "https://planetscale.com/legal/agreement",
"logo": "./assets/logo.png",
"logoDark": "./assets/logo-dark.png",
"composerIcon": "./assets/composer-icon.png",
"defaultPrompt": [
"Run a PlanetScale best-practices assessment for my database.",
"List my PlanetScale organizations and databases, then inspect a branch schema.",
Expand Down
39 changes: 39 additions & 0 deletions .codex-plugin/skill-sources.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"sources": [
{
"repo": "skills",
"url": "https://github.com/planetscale/skills",
"sha": "5467683425a616c2d8100cc6ba56952609ecafd0",
"namespace": "planetscale",
"skills": [
"safe-orchestrator",
"readonly-inventory",
"vitess-safety-review",
"postgres-safety-review",
"query-insights-and-tags",
"traffic-control-recommendations",
"webhook-automation-recommendations",
"schema-recommendations-agent-loop",
"codebase-sqlcommenter-instrumentation",
"mcp-agent-operating-model",
"customer-report-template",
"change-gates-and-approval-contract",
"best-practices-matrix",
"autonomous-execution-mode",
"pscale-cli-automation"
]
},
{
"repo": "database-skills",
"url": "https://github.com/planetscale/database-skills",
"sha": "73b20b7eb64716d8c7100c054f0677c0c6e77e30",
"namespace": "database",
"skills": [
"database-mysql",
"database-neki",
"database-postgres",
"database-vitess"
]
}
]
}
11 changes: 5 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,20 @@ jobs:

- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
if: ${{ steps.release.outputs.release_created }}
with:
submodules: true

- name: Create archive
if: ${{ steps.release.outputs.release_created }}
run: |
tar -czvf planetscale-codex-plugin.tar.gz \
--dereference \
.codex-plugin/plugin.json \
.codex-plugin/ \
LICENSE \
.mcp.json \
.agents/plugins/marketplace.json \
database-skills/LICENSE \
database-skills/skills/ \
skills/
skill-index/ \
assets/ \
skills/ \
third_party/

- name: Upload release asset
if: ${{ steps.release.outputs.release_created }}
Expand Down
175 changes: 111 additions & 64 deletions .github/workflows/update-skills.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Update Skills Submodules
name: Update Vendored Skills

on:
workflow_dispatch:
Expand All @@ -15,84 +15,131 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
submodules: recursive

- name: Capture current submodule commits
id: before
run: |
echo "database_skills_sha=$(git rev-parse HEAD:database-skills)" >> "$GITHUB_OUTPUT"
echo "skills_sha=$(git rev-parse HEAD:skills)" >> "$GITHUB_OUTPUT"
- name: Sync vendored skills
id: sync
run: python3 scripts/sync-skills.py

- name: Sync and update skill submodules
- name: Validate skills layout
run: |
git submodule sync --recursive
git submodule update --init --remote database-skills skills
python3 - <<'PY'
import pathlib
import json
import re

- name: Validate expected skills layout
run: |
test -d database-skills/skills
for skill in mysql neki postgres vitess; do
test -f "database-skills/skills/$skill/SKILL.md"
done

skill_count=0
for skill in skills/[0-9]*; do
test -f "$skill/SKILL.md"
skill_count=$((skill_count + 1))
done
test "$skill_count" -ge 1

- name: Capture updated submodule commits
id: after
run: |
echo "database_skills_sha=$(git -C database-skills rev-parse HEAD)" >> "$GITHUB_OUTPUT"
echo "skills_sha=$(git -C skills rev-parse HEAD)" >> "$GITHUB_OUTPUT"
root = pathlib.Path("skills")
provenance = json.loads(
pathlib.Path(".codex-plugin/skill-sources.json").read_text(encoding="utf-8")
)
reference = re.compile(
r"(?P<prefix>(?:\.\./)+)"
r"(?P<target>[a-z0-9]+(?:-[a-z0-9]+)*)"
r"(?P<suffix>(?:/[A-Za-z0-9._-]+)+)"
)
markdown_link = re.compile(
r"\]\((?P<link>(?!(?:[A-Za-z][A-Za-z0-9+.-]*:|//|#))[^)\s]+)"
)

def frontmatter_name(skill_md):
lines = skill_md.read_text(encoding="utf-8").splitlines()
assert lines and lines[0].strip() == "---", f"missing frontmatter in {skill_md}"
end = lines.index("---", 1)
names = [
line.partition(":")[2].strip().strip("\"'")
for line in lines[1:end]
if line.startswith("name:")
]
assert len(names) == 1, f"missing or duplicate name in {skill_md}"
return names[0]

namespaces = {"database", "planetscale"}
recorded_names = set()
for source in provenance["sources"]:
assert source["namespace"] in namespaces, source
names = source["skills"]
assert len(names) == len(set(names)), source
recorded_names.update(names)
expected_dirs = namespaces | recorded_names
skill_dirs = sorted(path for path in root.iterdir() if path.is_dir())
assert {path.name for path in skill_dirs} == expected_dirs, (
{path.name for path in skill_dirs},
expected_dirs,
)

for skill_dir in skill_dirs:
skill_md = skill_dir / "SKILL.md"
assert skill_md.is_file(), f"missing {skill_md}"
assert frontmatter_name(skill_md) == skill_dir.name

for namespace in namespaces:
index = root / namespace / "SKILL.md"
text = index.read_text(encoding="utf-8")
begin = "<!-- BEGIN GENERATED INDEX -->"
end = "<!-- END GENERATED INDEX -->"
start = text.index(begin) + len(begin)
finish = text.index(end, start)
assert text[start:finish].strip(), f"empty generated index in {index}"

assert not any(path.name == "script" for path in root.rglob("*")), "helper script directory was vendored"

reference_count = 0
root_resolved = root.resolve()
for markdown in root.rglob("*.md"):
text = markdown.read_text(encoding="utf-8")
links = {
match.start(): match.group(0)
for match in reference.finditer(text)
}
links.update(
{
match.start("link"): match.group("link")
for match in markdown_link.finditer(text)
}
)
for link in links.values():
reference_count += 1
target_link = re.split(r"[#?]", link, maxsplit=1)[0]
target = (markdown.parent / target_link).resolve()
assert target.is_relative_to(root_resolved), (
f"reference escapes skills root: {markdown}: {link}"
)
assert target.is_file(), (
f"dangling skill reference: {markdown}: "
f"{link} -> {target}"
)
print(f"vendored skill references passed: {reference_count}")
PY

- name: Compute change metadata
id: meta
- name: Detect vendored changes
id: changes
run: |
DB_BEFORE="${{ steps.before.outputs.database_skills_sha }}"
DB_AFTER="${{ steps.after.outputs.database_skills_sha }}"
SK_BEFORE="${{ steps.before.outputs.skills_sha }}"
SK_AFTER="${{ steps.after.outputs.skills_sha }}"

CHANGED="false"
if [ "$DB_BEFORE" != "$DB_AFTER" ] || [ "$SK_BEFORE" != "$SK_AFTER" ]; then
CHANGED="true"
if [ -n "$(git status --porcelain -- skills third_party .codex-plugin/skill-sources.json)" ]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
fi

{
echo "changed=$CHANGED"
echo "database_skills_before_sha=$DB_BEFORE"
echo "database_skills_after_sha=$DB_AFTER"
echo "database_skills_compare_url=https://github.com/planetscale/database-skills/compare/$DB_BEFORE...$DB_AFTER"
echo "skills_before_sha=$SK_BEFORE"
echo "skills_after_sha=$SK_AFTER"
echo "skills_compare_url=https://github.com/planetscale/skills/compare/$SK_BEFORE...$SK_AFTER"
} >> "$GITHUB_OUTPUT"

- name: Open pull request with submodule updates
if: steps.meta.outputs.changed == 'true'
- name: Open pull request with vendored skill updates
if: steps.changes.outputs.changed == 'true'
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7
with:
branch: chore/update-skills-submodules
branch: chore/update-vendored-skills
delete-branch: true
title: "chore: update skills submodules"
commit-message: "chore: update skills submodules"
title: "chore: update vendored skills"
commit-message: "chore: update vendored skills"
body: |
This automated PR updates the skill submodules to the latest `main` commits.
This automated PR updates the vendored skills to the latest `main` commits.

### database-skills
- Previous commit: `${{ steps.meta.outputs.database_skills_before_sha }}`
- Updated commit: `${{ steps.meta.outputs.database_skills_after_sha }}`
- Upstream compare: ${{ steps.meta.outputs.database_skills_compare_url }}
- Previous commit: `${{ steps.sync.outputs.database_skills_before_sha }}`
- Updated commit: `${{ steps.sync.outputs.database_skills_after_sha }}`
- Upstream compare: ${{ steps.sync.outputs.database_skills_compare_url }}

### skills
- Previous commit: `${{ steps.meta.outputs.skills_before_sha }}`
- Updated commit: `${{ steps.meta.outputs.skills_after_sha }}`
- Upstream compare: ${{ steps.meta.outputs.skills_compare_url }}
- Previous commit: `${{ steps.sync.outputs.skills_before_sha }}`
- Updated commit: `${{ steps.sync.outputs.skills_after_sha }}`
- Upstream compare: ${{ steps.sync.outputs.skills_compare_url }}
add-paths: |
.gitmodules
database-skills
skills
third_party
.codex-plugin/skill-sources.json
8 changes: 0 additions & 8 deletions .gitmodules

This file was deleted.

43 changes: 15 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ Plugin for installing the [PlanetScale MCP server](https://planetscale.com/docs/
## Prerequisites

- A plugin-capable Codex CLI or ChatGPT desktop app
- Git with submodule support when cloning the repository locally
- A PlanetScale account for authenticated MCP operations

## Install from GitHub
Expand All @@ -30,42 +29,33 @@ codex mcp login PlanetScale

## Skills Source and Sync

This plugin pulls in skills from two upstream repositories via Git submodules:
This plugin vendors skills from two upstream repositories:

| Upstream | Submodule path | What it provides |
| Upstream | Vendored path | What it provides |
| --- | --- | --- |
| [`planetscale/skills`](https://github.com/planetscale/skills) | `skills` | PlanetScale operating/assessment skills (safe orchestrator, inventory, Insights, Traffic Control, schema recommendations, and more) |
| [`planetscale/database-skills`](https://github.com/planetscale/database-skills) | `database-skills` | Engine skills for MySQL, Postgres, Vitess, and Neki |
| [`planetscale/skills`](https://github.com/planetscale/skills) | `skills/` | PlanetScale operating/assessment skills |
| [`planetscale/database-skills`](https://github.com/planetscale/database-skills) | `skills/` | Engine skills for MySQL, Postgres, Vitess, and Neki |

Both track `main`. Codex loads `./skills/` by default and also loads `./database-skills/skills/` via the plugin manifest.
Both track `main`. The sync script copies each upstream skill directory directly under `skills/`, using the frontmatter `name` as the directory name after stripping the `planetscale-` prefix from operating skills and adding the `database-` prefix to engine skills. It skips upstream directories without a `SKILL.md` and records source commit SHAs, namespaces, and vendored names in `.codex-plugin/skill-sources.json`.

### Local bootstrap
Each namespace has an index skill at `skills/database/SKILL.md` or `skills/planetscale/SKILL.md`. The index tables link to every child skill and are generated from the child frontmatter descriptions. Their source prose and generated-region markers live in `skill-index/`; edit those templates rather than the rendered indexes.

Clone with submodules:
The 19 source skills are direct siblings under `skills/` so both legacy and direct-child skill discovery can load them. Operating skills use names such as `safe-orchestrator`, while engine skills use the `database-` prefix, such as `database-mysql`.

```bash
git clone --recurse-submodules https://github.com/planetscale/codex-plugin.git
```
### Local sync and testing

If you already cloned without submodules:
Clone the repository normally:

```bash
git submodule update --init --recursive
git clone https://github.com/planetscale/codex-plugin.git
```

### Manual one-off update

To pull the latest upstream skills into this repository:
Refresh vendored skills from upstream:

```bash
git submodule sync --recursive
git submodule update --init --remote database-skills skills
python3 scripts/sync-skills.py
```

Commit the resulting submodule pointer changes in this repository.

### Local testing

Add a personal or repo marketplace that points at this working copy, then install the plugin and restart the ChatGPT desktop app or Codex CLI.

Example personal marketplace entry (`~/.agents/plugins/marketplace.json`):
Expand Down Expand Up @@ -100,17 +90,14 @@ codex plugin marketplace add /absolute/path/to/codex-plugin
```

1. Confirm the `PlanetScale` MCP server is listed (authentication required on first use).
2. Confirm PlanetScale operating skills (for example `00-safe-orchestrator`) are available.
3. Confirm the MySQL, Postgres, Vitess, and Neki database skills are available.
2. Confirm PlanetScale operating skills (for example `safe-orchestrator`) are available.
3. Confirm the `database-mysql`, `database-postgres`, `database-vitess`, and `database-neki` skills are available.

### Automated weekly updates

GitHub Actions runs `.github/workflows/update-skills.yml` weekly and also supports manual runs (`workflow_dispatch`).

When either submodule has new commits, the workflow opens or updates a PR that contains only:

- The `database-skills` and/or `skills` submodule pointer updates
- `.gitmodules` (if submodule metadata changed)
It runs `scripts/sync-skills.py`, validates the vendored layout, and opens a PR when the vendored skills, third-party licenses, or provenance file change. The PR body includes compare links to the upstream commits.

## Contributing

Expand Down
Binary file added assets/composer-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logo-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion database-skills
Submodule database-skills deleted from af0ce0
Loading