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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/update-spec-types.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Update Spec Types

on:
schedule:
# Run nightly at 4 AM UTC
- cron: '0 4 * * *'
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
update-spec-types:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'

- name: Install dependencies
run: npm ci

- name: Fetch latest spec types
run: npm run fetch:spec-types

- name: Check for changes
id: check_changes
run: |
if git diff --quiet src/spec.types.ts; then
echo "has_changes=false" >> $GITHUB_OUTPUT
else
echo "has_changes=true" >> $GITHUB_OUTPUT
LATEST_SHA=$(grep "Last updated from commit:" src/spec.types.ts | cut -d: -f2 | tr -d ' ')
echo "sha=$LATEST_SHA" >> $GITHUB_OUTPUT
fi

- name: Create Pull Request
if: steps.check_changes.outputs.has_changes == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git checkout -B update-spec-types
git add src/spec.types.ts
git commit -m "chore: update spec.types.ts from upstream"
git push -f origin update-spec-types

# Create PR if it doesn't exist, or update if it does
PR_BODY="This PR updates \`src/spec.types.ts\` from the Model Context Protocol specification.

Source file: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/${{ steps.check_changes.outputs.sha }}/schema/draft/schema.ts

This is an automated update triggered by the nightly cron job."

if gh pr view update-spec-types &>/dev/null; then
echo "PR already exists, updating description..."
gh pr edit update-spec-types --body "$PR_BODY"
else
gh pr create \
--title "chore: update spec.types.ts from upstream" \
--body "$PR_BODY" \
--base main \
--head update-spec-types
fi
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ web_modules/
# Output of 'npm pack'
*.tgz

# Output of 'npm run fetch:spec-types'
spec.types.ts

# Yarn Integrity file
.yarn-integrity

Expand Down
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ node_modules
**/build
**/dist
.github/CODEOWNERS
pnpm-lock.yaml
pnpm-lock.yaml

# Ignore generated files
src/spec.types.ts
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"dist"
],
"scripts": {
"fetch:spec-types": "curl -o ./src/spec.types.ts https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/refs/heads/main/schema/draft/schema.ts",
"fetch:spec-types": "tsx scripts/fetch-spec-types.ts",
"typecheck": "tsgo --noEmit",
"build": "npm run build:esm && npm run build:cjs",
"build:esm": "mkdir -p dist/esm && echo '{\"type\": \"module\"}' > dist/esm/package.json && tsc -p tsconfig.prod.json",
Expand All @@ -70,8 +70,8 @@
"prepack": "npm run build:esm && npm run build:cjs",
"lint": "eslint src/ && prettier --check .",
"lint:fix": "eslint src/ --fix && prettier --write .",
"check": "npm run fetch:spec-types && npm run typecheck && npm run lint",
"test": "npm run fetch:spec-types && jest",
"check": "npm run typecheck && npm run lint",
"test": "jest",
"start": "npm run server",
"server": "tsx watch --clear-screen=false scripts/cli.ts server",
"client": "tsx scripts/cli.ts client"
Expand Down
86 changes: 86 additions & 0 deletions scripts/fetch-spec-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const PROJECT_ROOT = join(__dirname, '..');

interface GitHubCommit {
sha: string;
}

async function fetchLatestSHA(): Promise<string> {
const url = 'https://api.github.com/repos/modelcontextprotocol/modelcontextprotocol/commits?path=schema/draft/schema.ts&per_page=1';

const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch commit info: ${response.status} ${response.statusText}`);
}

const commits = (await response.json()) as GitHubCommit[];
if (!commits || commits.length === 0) {
throw new Error('No commits found');
}

return commits[0].sha;
}

async function fetchSpecTypes(sha: string): Promise<string> {
const url = `https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/${sha}/schema/draft/schema.ts`;

const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch spec types: ${response.status} ${response.statusText}`);
}

return await response.text();
}

async function main() {
try {
// Check if SHA is provided as command line argument
const providedSHA = process.argv[2];

let latestSHA: string;
if (providedSHA) {
console.log(`Using provided SHA: ${providedSHA}`);
latestSHA = providedSHA;
} else {
console.log('Fetching latest commit SHA...');
latestSHA = await fetchLatestSHA();
}

console.log(`Fetching spec.types.ts from commit: ${latestSHA}`);

const specContent = await fetchSpecTypes(latestSHA);

// Read header template
const headerTemplate = `/**
* This file is automatically generated from the Model Context Protocol specification.
*
* Source: https://github.com/modelcontextprotocol/modelcontextprotocol
* Pulled from: https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/draft/schema.ts
* Last updated from commit: {SHA}
*
* DO NOT EDIT THIS FILE MANUALLY. Changes will be overwritten by automated updates.
* To update this file, run: npm run fetch:spec-types
*/`;

const header = headerTemplate.replace('{SHA}', latestSHA);

// Combine header and content
const fullContent = header + specContent;

// Write to file
const outputPath = join(PROJECT_ROOT, 'src', 'spec.types.ts');
writeFileSync(outputPath, fullContent, 'utf-8');

console.log('Successfully updated src/spec.types.ts');
} catch (error) {
console.error('Error:', error instanceof Error ? error.message : String(error));
process.exit(1);
}
}

main();
Loading