🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

mcp-udacity-commit

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mcp-udacity-commit - npm Package Compare versions

Comparing version
1.0.2
to
1.2.0
+32
-2
build/index.js

@@ -5,4 +5,4 @@ #!/usr/bin/env node

import { z } from "zod";
import { STYLE_GUIDE, validate, formatMessage } from "./lint.js";
const VERSION = "1.0.2";
import { STYLE_GUIDE, BRANCH_GUIDE, validate, formatMessage, validateBranch } from "./lint.js";
const VERSION = "1.2.0";
export function createServer() {

@@ -15,2 +15,7 @@ const server = new McpServer({ name: "udacity-commit", version: VERSION });

}, async (uri) => ({ contents: [{ uri: uri.href, text: STYLE_GUIDE }] }));
server.registerResource("branch-naming", "udacity://branch-naming", {
title: "Branch Naming (companion convention)",
description: "type/kebab-case branch-naming rules that pair with the commit style.",
mimeType: "text/markdown",
}, async (uri) => ({ contents: [{ uri: uri.href, text: BRANCH_GUIDE }] }));
server.registerTool("validate_commit_message", {

@@ -74,2 +79,27 @@ title: "Validate a commit message",

});
server.registerTool("validate_branch_name", {
title: "Validate a git branch name",
description: "Check a git branch name against the companion type/kebab-case convention " +
'(e.g. "feat/add-dark-mode"). Returns whether it is compliant plus any problems ' +
"(violations) and warnings (hints). Base branches like main/master are exempt.",
inputSchema: { name: z.string().describe('The branch name to check, e.g. "feat/add-dark-mode"') },
outputSchema: {
valid: z.boolean(),
problems: z.array(z.string()),
warnings: z.array(z.string()),
},
}, async ({ name }) => {
const r = validateBranch(name);
const text = [
r.valid ? "✅ Compliant branch name." : "❌ Not compliant.",
...r.problems.map((p) => ` • ${p}`),
...r.warnings.map((w) => ` ⚠ ${w}`),
].join("\n");
const structuredContent = {
valid: r.valid,
problems: r.problems,
warnings: r.warnings,
};
return { content: [{ type: "text", text }], structuredContent };
});
return server;

@@ -76,0 +106,0 @@ }

@@ -13,2 +13,9 @@ /**

export const BODY_WRAP = 72;
export const BRANCH_MAX = 50;
/**
* Long-lived / protected branches that are exempt from the feature-branch
* `type/description` rule. `release/*` is NOT here — it is validated as a
* typed branch with a version-style description (e.g. `release/1.2.0`).
*/
export const BASE_BRANCHES = new Set(["main", "master", "dev", "develop", "trunk"]);
export const TYPES = {

@@ -23,2 +30,8 @@ feat: "A new feature",

};
/**
* Types allowed as a branch prefix: the commit types plus `release`, which is
* a branch-only type (release branches are named, not committed). `release`
* takes a version-style description; every other type takes kebab-case.
*/
export const BRANCH_TYPES = [...Object.keys(TYPES), "release"];
/** Footer keywords recognized for issue-reference validation. */

@@ -91,2 +104,28 @@ export const FOOTER_KEYS = ["Resolves", "Closes", "Fixes", "Fix", "See also", "Refs", "Ref"];

`;
export const BRANCH_GUIDE = `# Branch naming (companion convention)
Not part of the official Udacity *commit-message* guide, but a natural
companion: name feature branches after the change they carry, reusing the
same commit \`type\` set.
type/kebab-case-description
## Rules
- \`type/\` prefix — one of: ${BRANCH_TYPES.join(", ")}
- A single \`/\` separates the type from the description
- Description is **kebab-case**: lowercase letters and digits joined by single
hyphens (\`feat/add-dark-mode\`, not \`feat/Add_Dark_Mode\`)
- \`release/\` branches take a **version-style** description instead: lowercase
words/digits joined by dots or hyphens (\`release/1.2.0\`, \`release/2024-q1\`)
- No spaces, underscores, uppercase, or leading/trailing/double hyphens
- Keep it short — ${BRANCH_MAX} characters or fewer (a hint, not a hard limit)
## Examples
- \`feat/add-dark-mode\`
- \`fix/duplicate-auth-refresh\`
- \`chore/bump-deps\`
- \`release/1.2.0\`
Base branches (${[...BASE_BRANCHES].join(", ")}) are exempt.
`;
/** Length in Unicode code points (not UTF-16 code units). */

@@ -215,1 +254,69 @@ export function width(s) {

}
/** Kebab-case: lowercase words (letters/digits) joined by single hyphens. */
const KEBAB = /^[a-z0-9]+(-[a-z0-9]+)*$/;
/**
* Version-style (for `release/` branches): lowercase words/digits joined by
* dots or hyphens, so `1.2.0`, `2.0.0-rc1`, and `2024-q1` all pass.
*/
const RELEASE_DESC = /^[a-z0-9]+([.-][a-z0-9]+)*$/;
/**
* Validate a git branch name against the companion `type/kebab-case`
* convention. Base/long-lived branches (main, master, dev, develop, trunk)
* are accepted as-is with a note. `release/` is a typed branch and takes a
* version-style description (e.g. `release/1.2.0`).
*/
export function validateBranch(name) {
const problems = [];
const warnings = [];
const branch = name.trim();
if (!branch) {
return { valid: false, problems: ["Branch name is empty."], warnings };
}
if (branch !== name) {
problems.push("Branch name has leading/trailing whitespace.");
}
// Base / long-lived branches are exempt from the feature-branch rule.
// (`release/*` is NOT exempt — it is validated as a typed branch below.)
if (BASE_BRANCHES.has(branch)) {
warnings.push(`"${branch}" is a base branch — feature-branch naming rules don't apply.`);
return { valid: problems.length === 0, problems, warnings };
}
const slash = branch.indexOf("/");
if (slash === -1) {
problems.push(`Branch must follow "type/description". Got: "${branch}".`);
return { valid: false, problems, warnings };
}
const type = branch.slice(0, slash);
const description = branch.slice(slash + 1);
if (!BRANCH_TYPES.includes(type)) {
problems.push(`Unknown type "${type}". Use one of: ${BRANCH_TYPES.join(", ")}.`);
}
// `release/` takes a version-style description; every other type is kebab-case.
const isRelease = type === "release";
const pattern = isRelease ? RELEASE_DESC : KEBAB;
if (!description) {
problems.push("Description after the type is empty.");
}
else if (description.includes("/")) {
problems.push(`Use a single "/" after the type; the description must not contain "/". Got: "${description}".`);
}
else if (!pattern.test(description)) {
// Give the most specific reason we can, else a general shape message.
if (/[A-Z]/.test(description)) {
problems.push(`Description must be lowercase ${isRelease ? "version-style (e.g. 1.2.0)" : "kebab-case"}. Got: "${description}".`);
}
else if (/[_ ]/.test(description)) {
problems.push("Use hyphens, not spaces or underscores, to separate words.");
}
else if (isRelease) {
problems.push(`Release description must be version-style: lowercase words/digits joined by dots or hyphens (e.g. "1.2.0", "2024-q1"). Got: "${description}".`);
}
else {
problems.push(`Description must be kebab-case: lowercase words joined by single hyphens. Got: "${description}".`);
}
}
if (width(branch) > BRANCH_MAX) {
warnings.push(`Branch name is ${width(branch)} chars; keep it ${BRANCH_MAX} or fewer.`);
}
return { valid: problems.length === 0, problems, warnings };
}
+1
-1
{
"name": "mcp-udacity-commit",
"version": "1.0.2",
"version": "1.2.0",
"description": "MCP server that validates and formats git commit messages per the Udacity Git Commit Message Style Guide.",

@@ -5,0 +5,0 @@ "type": "module",

@@ -51,4 +51,6 @@ # mcp-udacity-commit

| Resource | `udacity://commit-styleguide` | The style-guide rules, as markdown |
| Resource | `udacity://branch-naming` | The companion `type/kebab-case` branch-naming rules, as markdown |
| Tool | `validate_commit_message` | Checks a message against every rule (type, ≤50-char subject, capitalization, no trailing period, blank line, ≤72-char body wrap) |
| Tool | `format_commit_message` | Builds a compliant message from `type` + `subject` + optional `body`/`footer` |
| Tool | `validate_branch_name` | Checks a branch name against the companion `type/kebab-case` convention (e.g. `feat/add-dark-mode`); `release/*` is a typed branch with a version-style description (`release/1.2.0`), and base branches like `main` are exempt |

@@ -82,2 +84,13 @@ ## Example

`validate_branch_name` enforces the companion `type/kebab-case` convention:
```text
"feat/add-dark-mode" → ✅ Compliant branch name.
"release/1.2.0" → ✅ Compliant branch name.
"Feature/Add_Dark_Mode" → ❌ Not compliant.
• Unknown type "Feature". Use one of: feat, fix, docs, style, refactor, test, chore, release.
• Description must be lowercase kebab-case. Got: "Add_Dark_Mode".
"main" → ✅ (base branch — feature-branch rules don't apply)
```
## Develop

@@ -84,0 +97,0 @@