@michaelbel/ai-workflow-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/github.js ADDED
@@ -0,0 +1,23 @@
1
+ const BASE_RAW = "https://raw.githubusercontent.com";
2
+ const BASE_API = "https://api.github.com";
3
+ export async function fetchFile(owner, repo, branch, path) {
4
+ const url = `${BASE_RAW}/${owner}/${repo}/${branch}/${path}`;
5
+ const res = await fetch(url);
6
+ if (!res.ok) {
7
+ throw new Error(`Not found: ${path} (HTTP ${res.status})`);
8
+ }
9
+ return res.text();
10
+ }
11
+ export async function listTree(owner, repo, branch) {
12
+ const url = `${BASE_API}/repos/${owner}/${repo}/git/trees/${branch}?recursive=1`;
13
+ const res = await fetch(url, {
14
+ headers: { Accept: "application/vnd.github.v3+json" },
15
+ });
16
+ if (!res.ok) {
17
+ throw new Error(`Failed to fetch tree (HTTP ${res.status})`);
18
+ }
19
+ const data = (await res.json());
20
+ return data.tree
21
+ .filter((item) => item.type === "blob")
22
+ .map((item) => item.path);
23
+ }
package/dist/index.js ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { execSync } from "child_process";
5
+ import { z } from "zod";
6
+ import { fetchFile, listTree } from "./github.js";
7
+ const OWNER = "michaelbel";
8
+ const REPO = "ai-workflow";
9
+ const BRANCH = "main";
10
+ const server = new McpServer({
11
+ name: "@michaelbel/ai-workflow-mcp",
12
+ version: "1.0.0",
13
+ });
14
+ // ─── list ────────────────────────────────────────────────────────────────────
15
+ server.tool("list", "List all available rules and skills in the ai-workflow repository", {}, async () => {
16
+ const tree = await listTree(OWNER, REPO, BRANCH);
17
+ const rules = tree
18
+ .filter((f) => f.startsWith("rules/") && f.endsWith(".md"))
19
+ .map((f) => `- ${f.replace(/^rules\//, "").replace(/\.md$/, "")}`);
20
+ const skills = tree
21
+ .filter((f) => f.startsWith("skills/") && f.endsWith(".md"))
22
+ .map((f) => `- ${f.replace(/^skills\//, "").replace(/\.md$/, "")}`);
23
+ const lines = [
24
+ "## Rules",
25
+ rules.length ? rules.join("\n") : "_none_",
26
+ "",
27
+ "## Skills",
28
+ skills.length ? skills.join("\n") : "_none_",
29
+ ];
30
+ return { content: [{ type: "text", text: lines.join("\n") }] };
31
+ });
32
+ // ─── get_rule ─────────────────────────────────────────────────────────────────
33
+ server.tool("get_rule", "Get the content of a rule. Use the name from the `list` tool (e.g. 'git/GIT_RULES').", { name: z.string().describe("Rule name without extension, e.g. 'git/GIT_RULES'") }, async ({ name }) => {
34
+ const content = await fetchFile(OWNER, REPO, BRANCH, `rules/${name}.md`);
35
+ return { content: [{ type: "text", text: content }] };
36
+ });
37
+ // ─── get_skill ────────────────────────────────────────────────────────────────
38
+ server.tool("get_skill", "Get the instructions for a skill. Use the name from the `list` tool.", { name: z.string().describe("Skill name without extension, e.g. 'deploy'") }, async ({ name }) => {
39
+ const content = await fetchFile(OWNER, REPO, BRANCH, `skills/${name}.md`);
40
+ return { content: [{ type: "text", text: content }] };
41
+ });
42
+ // ─── run_skill ────────────────────────────────────────────────────────────────
43
+ server.tool("run_skill", "Run the shell command associated with a skill. The skill must have a `command:` field in its YAML frontmatter.", {
44
+ name: z.string().describe("Skill name without extension"),
45
+ args: z.string().optional().describe("Optional extra arguments appended to the command"),
46
+ }, async ({ name, args }) => {
47
+ const content = await fetchFile(OWNER, REPO, BRANCH, `skills/${name}.md`);
48
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
49
+ if (!frontmatterMatch) {
50
+ return {
51
+ content: [{ type: "text", text: `Skill '${name}' has no frontmatter — nothing to run.` }],
52
+ };
53
+ }
54
+ const commandMatch = frontmatterMatch[1].match(/^command:\s*(.+)$/m);
55
+ if (!commandMatch) {
56
+ return {
57
+ content: [{ type: "text", text: `Skill '${name}' has no 'command:' field in frontmatter.` }],
58
+ };
59
+ }
60
+ const command = commandMatch[1].trim() + (args ? ` ${args}` : "");
61
+ try {
62
+ const output = execSync(command, { encoding: "utf8", stdio: "pipe" });
63
+ return { content: [{ type: "text", text: output || "(no output)" }] };
64
+ }
65
+ catch (err) {
66
+ const msg = err instanceof Error ? err.message : String(err);
67
+ return {
68
+ content: [{ type: "text", text: `Error running '${command}':\n${msg}` }],
69
+ isError: true,
70
+ };
71
+ }
72
+ });
73
+ // ─── start ────────────────────────────────────────────────────────────────────
74
+ const transport = new StdioServerTransport();
75
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@michaelbel/ai-workflow-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for ai-workflow rules and skills",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "ai-workflow-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsx src/index.ts",
13
+ "prepare": "npm run build"
14
+ },
15
+ "files": [
16
+ "dist/"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "dependencies": {
22
+ "@modelcontextprotocol/sdk": "^1.0.0",
23
+ "zod": "^3.22.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^20.0.0",
27
+ "tsx": "^4.0.0",
28
+ "typescript": "^5.0.0"
29
+ }
30
+ }