my-personal-code-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.
@@ -0,0 +1,26 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(gh issue create:*)",
5
+ "mcp__github__get_me",
6
+ "Bash(git remote:*)",
7
+ "mcp__github__issue_write",
8
+ "Bash(npm init:*)",
9
+ "Bash(npm install:*)",
10
+ "Bash(npm run build:*)",
11
+ "Bash(set SKILLS_REPO_OWNER=lfdantoni)",
12
+ "Bash(set SKILLS_REPO_NAME=my-skills-repo:*)",
13
+ "Bash(set SKILLS_PATH=skills)",
14
+ "Bash(npx tsx:*)",
15
+ "Bash($env:SKILLS_REPO_OWNER=\"lfdantoni\")",
16
+ "Bash($env:SKILLS_REPO_NAME=\"my-skills-repo\")",
17
+ "Bash($env:SKILLS_PATH=\"skills\")",
18
+ "Bash(export SKILLS_REPO_OWNER=lfdantoni)",
19
+ "Bash(export SKILLS_REPO_NAME=my-skills-repo)",
20
+ "Bash(export SKILLS_PATH=skills)",
21
+ "mcp__github__get_file_contents",
22
+ "Bash(git add:*)",
23
+ "Bash(git commit -m \"$\\(cat <<''EOF''\nImplement MCP server for skills from GitHub repository\n\n- Add MCP server with stdio transport using @modelcontextprotocol/sdk\n- Implement list_skills tool to fetch skill names from GitHub repo\n- Implement get_skill tool to retrieve skill content by name\n- Add GitHubService with error handling for API calls\n- Configure TypeScript build with ES modules\n- Add documentation with Cursor, Claude Desktop, and CLI examples\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")"
24
+ ]
25
+ }
26
+ }
package/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # my-personal-code-mcp
2
+
3
+ An MCP (Model Context Protocol) server that provides AI assistants with access to best practice skills stored as markdown files in a GitHub repository.
4
+
5
+ ## Features
6
+
7
+ - **list_skills**: Returns a list of all available skill names from the configured repository
8
+ - **get_skill**: Retrieves the content of a specific skill by name
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ # Clone the repository
14
+ git clone https://github.com/lfdantoni/my-personal-code-mcp.git
15
+ cd my-personal-code-mcp
16
+
17
+ # Install dependencies
18
+ npm install
19
+
20
+ # Build
21
+ npm run build
22
+ ```
23
+
24
+ ## Configuration
25
+
26
+ ### Environment Variables
27
+
28
+ | Variable | Description | Required |
29
+ |----------|-------------|----------|
30
+ | `SKILLS_REPO_OWNER` | GitHub repository owner (username or organization) | Yes |
31
+ | `SKILLS_REPO_NAME` | GitHub repository name | Yes |
32
+ | `SKILLS_PATH` | Subdirectory containing skill files (default: root) | No |
33
+ | `GITHUB_TOKEN` | GitHub personal access token (required for private repos) | No |
34
+
35
+ ### Skills Repository Structure
36
+
37
+ Your skills repository should contain markdown files (`.md`) with best practices:
38
+
39
+ ```
40
+ your-skills-repo/
41
+ ├── typescript-best-practices.md
42
+ ├── react-patterns.md
43
+ ├── testing-guidelines.md
44
+ └── ...
45
+ ```
46
+
47
+ Or with a subdirectory:
48
+
49
+ ```
50
+ your-skills-repo/
51
+ └── skills/
52
+ ├── typescript-best-practices.md
53
+ ├── react-patterns.md
54
+ └── ...
55
+ ```
56
+
57
+ ## MCP Client Configuration
58
+
59
+ ### Cursor
60
+
61
+ Add the following to your Cursor MCP settings file (`~/.cursor/mcp.json` or via Settings > MCP):
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "my-personal-code-mcp": {
67
+ "command": "node",
68
+ "args": ["C:/path/to/my-personal-code-mcp/dist/index.js"],
69
+ "env": {
70
+ "SKILLS_REPO_OWNER": "your-github-username",
71
+ "SKILLS_REPO_NAME": "your-skills-repo",
72
+ "SKILLS_PATH": "",
73
+ "GITHUB_TOKEN": "ghp_your_token_here"
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ ### Claude Desktop
81
+
82
+ Add the following to your Claude Desktop configuration file:
83
+
84
+ **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
85
+ **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
86
+
87
+ ```json
88
+ {
89
+ "mcpServers": {
90
+ "my-personal-code-mcp": {
91
+ "command": "node",
92
+ "args": ["C:/path/to/my-personal-code-mcp/dist/index.js"],
93
+ "env": {
94
+ "SKILLS_REPO_OWNER": "your-github-username",
95
+ "SKILLS_REPO_NAME": "your-skills-repo",
96
+ "SKILLS_PATH": "",
97
+ "GITHUB_TOKEN": "ghp_your_token_here"
98
+ }
99
+ }
100
+ }
101
+ }
102
+ ```
103
+
104
+ ### Claude Code CLI
105
+
106
+ Add to your Claude Code settings (`~/.claude/settings.json`):
107
+
108
+ ```json
109
+ {
110
+ "mcpServers": {
111
+ "my-personal-code-mcp": {
112
+ "command": "node",
113
+ "args": ["/path/to/my-personal-code-mcp/dist/index.js"],
114
+ "env": {
115
+ "SKILLS_REPO_OWNER": "your-github-username",
116
+ "SKILLS_REPO_NAME": "your-skills-repo"
117
+ }
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
123
+ ## Available Tools
124
+
125
+ ### list_skills
126
+
127
+ Returns a list of all available skill names.
128
+
129
+ **Input**: None
130
+
131
+ **Output**:
132
+ ```json
133
+ {
134
+ "skills": ["typescript-best-practices", "react-patterns", "testing-guidelines"]
135
+ }
136
+ ```
137
+
138
+ ### get_skill
139
+
140
+ Returns the content of a specific skill.
141
+
142
+ **Input**:
143
+ ```json
144
+ {
145
+ "skill_name": "typescript-best-practices"
146
+ }
147
+ ```
148
+
149
+ **Output**: The markdown content of the skill file.
150
+
151
+ ## Development
152
+
153
+ ```bash
154
+ # Build
155
+ npm run build
156
+
157
+ # Watch mode
158
+ npm run dev
159
+
160
+ # Run the server
161
+ npm start
162
+ ```
163
+
164
+ ## License
165
+
166
+ ISC
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,62 @@
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 { GitHubService } from "./services/github.js";
5
+ import { handleListSkills, getSkillSchema, handleGetSkill, } from "./tools/index.js";
6
+ const server = new McpServer({
7
+ name: "my-personal-code-mcp",
8
+ version: "1.0.0",
9
+ description: "MCP server for AI best practices skills from GitHub repository",
10
+ });
11
+ let githubService;
12
+ try {
13
+ githubService = new GitHubService();
14
+ }
15
+ catch (error) {
16
+ console.error("Failed to initialize GitHub service:", error);
17
+ process.exit(1);
18
+ }
19
+ server.registerTool("list_skills", {
20
+ description: "Returns a list of all available best practice skills",
21
+ }, async () => {
22
+ try {
23
+ const result = await handleListSkills(githubService);
24
+ return {
25
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
26
+ };
27
+ }
28
+ catch (error) {
29
+ const message = error instanceof Error ? error.message : "Unknown error";
30
+ return {
31
+ content: [{ type: "text", text: `Error: ${message}` }],
32
+ isError: true,
33
+ };
34
+ }
35
+ });
36
+ server.registerTool("get_skill", {
37
+ description: "Returns the content of a specific best practice skill",
38
+ inputSchema: getSkillSchema,
39
+ }, async (input) => {
40
+ try {
41
+ const result = await handleGetSkill(githubService, input);
42
+ return {
43
+ content: [{ type: "text", text: result.content }],
44
+ };
45
+ }
46
+ catch (error) {
47
+ const message = error instanceof Error ? error.message : "Unknown error";
48
+ return {
49
+ content: [{ type: "text", text: `Error: ${message}` }],
50
+ isError: true,
51
+ };
52
+ }
53
+ });
54
+ async function main() {
55
+ const transport = new StdioServerTransport();
56
+ await server.connect(transport);
57
+ }
58
+ main().catch((error) => {
59
+ console.error("Server error:", error);
60
+ process.exit(1);
61
+ });
62
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,sBAAsB;IAC5B,OAAO,EAAE,OAAO;IAChB,WAAW,EAAE,gEAAgE;CAC9E,CAAC,CAAC;AAEH,IAAI,aAA4B,CAAC;AAEjC,IAAI,CAAC;IACH,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;AACtC,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;IAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;IACE,WAAW,EAAE,sDAAsD;CACpE,EACD,KAAK,IAAI,EAAE;IACT,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,aAAa,CAAC,CAAC;QACrD,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SACnE,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;YACtD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,WAAW,EACX;IACE,WAAW,EAAE,uDAAuD;IACpE,WAAW,EAAE,cAAc;CAC5B,EACD,KAAK,EAAE,KAAK,EAAE,EAAE;IACd,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QAC1D,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;SAClD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;YACtD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ export interface SkillFile {
2
+ name: string;
3
+ path: string;
4
+ }
5
+ export declare class GitHubServiceError extends Error {
6
+ readonly code: "NOT_FOUND" | "RATE_LIMIT" | "AUTH_ERROR" | "UNKNOWN";
7
+ constructor(message: string, code: "NOT_FOUND" | "RATE_LIMIT" | "AUTH_ERROR" | "UNKNOWN");
8
+ }
9
+ export declare class GitHubService {
10
+ private octokit;
11
+ private owner;
12
+ private repo;
13
+ private skillsPath;
14
+ constructor();
15
+ listSkillFiles(): Promise<SkillFile[]>;
16
+ getSkillContent(skillName: string): Promise<string>;
17
+ private handleError;
18
+ }
19
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/services/github.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,kBAAmB,SAAQ,KAAK;aAGzB,IAAI,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS;gBAD3E,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS;CAK9E;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,UAAU,CAAS;;IAiBrB,cAAc,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IA0BtC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmCzD,OAAO,CAAC,WAAW;CA0CpB"}
@@ -0,0 +1,92 @@
1
+ import { Octokit } from "octokit";
2
+ export class GitHubServiceError extends Error {
3
+ code;
4
+ constructor(message, code) {
5
+ super(message);
6
+ this.code = code;
7
+ this.name = "GitHubServiceError";
8
+ }
9
+ }
10
+ export class GitHubService {
11
+ octokit;
12
+ owner;
13
+ repo;
14
+ skillsPath;
15
+ constructor() {
16
+ const token = process.env.GITHUB_TOKEN;
17
+ this.owner = process.env.SKILLS_REPO_OWNER || "";
18
+ this.repo = process.env.SKILLS_REPO_NAME || "";
19
+ this.skillsPath = process.env.SKILLS_PATH || "";
20
+ if (!this.owner || !this.repo) {
21
+ throw new Error("SKILLS_REPO_OWNER and SKILLS_REPO_NAME environment variables are required");
22
+ }
23
+ this.octokit = new Octokit({ auth: token });
24
+ }
25
+ async listSkillFiles() {
26
+ try {
27
+ const response = await this.octokit.rest.repos.getContent({
28
+ owner: this.owner,
29
+ repo: this.repo,
30
+ path: this.skillsPath,
31
+ });
32
+ if (!Array.isArray(response.data)) {
33
+ throw new GitHubServiceError("Expected directory but found file", "UNKNOWN");
34
+ }
35
+ return response.data
36
+ .filter((item) => item.type === "file" && item.name.endsWith(".md"))
37
+ .map((item) => ({
38
+ name: item.name.replace(/\.md$/, ""),
39
+ path: item.path,
40
+ }));
41
+ }
42
+ catch (error) {
43
+ throw this.handleError(error, "listing skills");
44
+ }
45
+ }
46
+ async getSkillContent(skillName) {
47
+ const filePath = this.skillsPath
48
+ ? `${this.skillsPath}/${skillName}.md`
49
+ : `${skillName}.md`;
50
+ try {
51
+ const response = await this.octokit.rest.repos.getContent({
52
+ owner: this.owner,
53
+ repo: this.repo,
54
+ path: filePath,
55
+ });
56
+ if (Array.isArray(response.data)) {
57
+ throw new GitHubServiceError(`"${skillName}" is a directory, not a skill file`, "NOT_FOUND");
58
+ }
59
+ if (response.data.type !== "file" || !("content" in response.data)) {
60
+ throw new GitHubServiceError(`"${skillName}" is not a valid skill file`, "NOT_FOUND");
61
+ }
62
+ const content = Buffer.from(response.data.content, "base64").toString("utf-8");
63
+ return content;
64
+ }
65
+ catch (error) {
66
+ throw this.handleError(error, `fetching skill "${skillName}"`);
67
+ }
68
+ }
69
+ handleError(error, context) {
70
+ if (error instanceof GitHubServiceError) {
71
+ return error;
72
+ }
73
+ if (error && typeof error === "object" && "status" in error) {
74
+ const status = error.status;
75
+ switch (status) {
76
+ case 404:
77
+ return new GitHubServiceError(`Resource not found while ${context}`, "NOT_FOUND");
78
+ case 403:
79
+ const message = "message" in error ? String(error.message) : "Rate limit exceeded";
80
+ if (message.toLowerCase().includes("rate limit")) {
81
+ return new GitHubServiceError(`GitHub API rate limit exceeded while ${context}`, "RATE_LIMIT");
82
+ }
83
+ return new GitHubServiceError(`Access forbidden while ${context}. Check GITHUB_TOKEN.`, "AUTH_ERROR");
84
+ case 401:
85
+ return new GitHubServiceError(`Authentication failed while ${context}. Check GITHUB_TOKEN.`, "AUTH_ERROR");
86
+ }
87
+ }
88
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
89
+ return new GitHubServiceError(`Error ${context}: ${errorMessage}`, "UNKNOWN");
90
+ }
91
+ }
92
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/services/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+ import { GitHubService } from "../services/github.js";
3
+ export declare const getSkillSchema: {
4
+ skill_name: z.ZodString;
5
+ };
6
+ export interface GetSkillInput {
7
+ skill_name: string;
8
+ }
9
+ export interface GetSkillResult {
10
+ skill_name: string;
11
+ content: string;
12
+ }
13
+ export declare function handleGetSkill(githubService: GitHubService, input: GetSkillInput): Promise<GetSkillResult>;
14
+ //# sourceMappingURL=get-skill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-skill.d.ts","sourceRoot":"","sources":["../../src/tools/get-skill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAsB,MAAM,uBAAuB,CAAC;AAE1E,eAAO,MAAM,cAAc;;CAE1B,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,cAAc,CAClC,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,aAAa,GACnB,OAAO,CAAC,cAAc,CAAC,CA2BzB"}
@@ -0,0 +1,32 @@
1
+ import { z } from "zod";
2
+ import { GitHubServiceError } from "../services/github.js";
3
+ export const getSkillSchema = {
4
+ skill_name: z.string().describe("The name of the skill to retrieve"),
5
+ };
6
+ export async function handleGetSkill(githubService, input) {
7
+ const { skill_name } = input;
8
+ if (!skill_name || typeof skill_name !== "string") {
9
+ throw new Error("skill_name is required and must be a string");
10
+ }
11
+ const trimmedName = skill_name.trim();
12
+ if (trimmedName.length === 0) {
13
+ throw new Error("skill_name cannot be empty");
14
+ }
15
+ try {
16
+ const content = await githubService.getSkillContent(trimmedName);
17
+ return {
18
+ skill_name: trimmedName,
19
+ content,
20
+ };
21
+ }
22
+ catch (error) {
23
+ if (error instanceof GitHubServiceError) {
24
+ if (error.code === "NOT_FOUND") {
25
+ throw new Error(`Skill "${trimmedName}" not found`);
26
+ }
27
+ throw new Error(`Failed to get skill: ${error.message}`);
28
+ }
29
+ throw error;
30
+ }
31
+ }
32
+ //# sourceMappingURL=get-skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-skill.js","sourceRoot":"","sources":["../../src/tools/get-skill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAiB,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE1E,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;CACrE,CAAC;AAWF,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,aAA4B,EAC5B,KAAoB;IAEpB,MAAM,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAE7B,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IACtC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;QACjE,OAAO;YACL,UAAU,EAAE,WAAW;YACvB,OAAO;SACR,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;YACxC,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CAAC,UAAU,WAAW,aAAa,CAAC,CAAC;YACtD,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,wBAAwB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { listSkillsSchema, handleListSkills, type ListSkillsResult, } from "./list-skills.js";
2
+ export { getSkillSchema, handleGetSkill, type GetSkillInput, type GetSkillResult, } from "./get-skill.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,cAAc,EACd,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { listSkillsSchema, handleListSkills, } from "./list-skills.js";
2
+ export { getSkillSchema, handleGetSkill, } from "./get-skill.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,GAEjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,cAAc,EACd,cAAc,GAGf,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { GitHubService } from "../services/github.js";
2
+ export declare const listSkillsSchema: {};
3
+ export interface ListSkillsResult {
4
+ skills: string[];
5
+ }
6
+ export declare function handleListSkills(githubService: GitHubService): Promise<ListSkillsResult>;
7
+ //# sourceMappingURL=list-skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-skills.d.ts","sourceRoot":"","sources":["../../src/tools/list-skills.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAsB,MAAM,uBAAuB,CAAC;AAE1E,eAAO,MAAM,gBAAgB,IAAK,CAAC;AAEnC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAsB,gBAAgB,CACpC,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CAW3B"}
@@ -0,0 +1,16 @@
1
+ import { GitHubServiceError } from "../services/github.js";
2
+ export const listSkillsSchema = {};
3
+ export async function handleListSkills(githubService) {
4
+ try {
5
+ const files = await githubService.listSkillFiles();
6
+ const skills = files.map((file) => file.name);
7
+ return { skills };
8
+ }
9
+ catch (error) {
10
+ if (error instanceof GitHubServiceError) {
11
+ throw new Error(`Failed to list skills: ${error.message}`);
12
+ }
13
+ throw error;
14
+ }
15
+ }
16
+ //# sourceMappingURL=list-skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-skills.js","sourceRoot":"","sources":["../../src/tools/list-skills.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAMnC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,aAA4B;IAE5B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,cAAc,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "my-personal-code-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for AI best practices skills",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "my-personal-code-mcp": "dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "dev": "tsc --watch",
14
+ "start": "node dist/index.js",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/lfdantoni/my-personal-code-mcp.git"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "model-context-protocol",
24
+ "ai",
25
+ "skills"
26
+ ],
27
+ "author": "",
28
+ "license": "ISC",
29
+ "bugs": {
30
+ "url": "https://github.com/lfdantoni/my-personal-code-mcp/issues"
31
+ },
32
+ "homepage": "https://github.com/lfdantoni/my-personal-code-mcp#readme",
33
+ "dependencies": {
34
+ "@modelcontextprotocol/sdk": "^1.25.2",
35
+ "octokit": "^5.0.5",
36
+ "zod": "^4.3.5"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^25.0.6",
40
+ "typescript": "^5.9.3"
41
+ }
42
+ }
package/src/index.ts ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { GitHubService } from "./services/github.js";
6
+ import {
7
+ handleListSkills,
8
+ getSkillSchema,
9
+ handleGetSkill,
10
+ } from "./tools/index.js";
11
+
12
+ const server = new McpServer({
13
+ name: "my-personal-code-mcp",
14
+ version: "1.0.0",
15
+ description: "MCP server for AI best practices skills from GitHub repository",
16
+ });
17
+
18
+ let githubService: GitHubService;
19
+
20
+ try {
21
+ githubService = new GitHubService();
22
+ } catch (error) {
23
+ console.error("Failed to initialize GitHub service:", error);
24
+ process.exit(1);
25
+ }
26
+
27
+ server.registerTool(
28
+ "list_skills",
29
+ {
30
+ description: "Returns a list of all available best practice skills",
31
+ },
32
+ async () => {
33
+ try {
34
+ const result = await handleListSkills(githubService);
35
+ return {
36
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
37
+ };
38
+ } catch (error) {
39
+ const message = error instanceof Error ? error.message : "Unknown error";
40
+ return {
41
+ content: [{ type: "text", text: `Error: ${message}` }],
42
+ isError: true,
43
+ };
44
+ }
45
+ }
46
+ );
47
+
48
+ server.registerTool(
49
+ "get_skill",
50
+ {
51
+ description: "Returns the content of a specific best practice skill",
52
+ inputSchema: getSkillSchema,
53
+ },
54
+ async (input) => {
55
+ try {
56
+ const result = await handleGetSkill(githubService, input);
57
+ return {
58
+ content: [{ type: "text", text: result.content }],
59
+ };
60
+ } catch (error) {
61
+ const message = error instanceof Error ? error.message : "Unknown error";
62
+ return {
63
+ content: [{ type: "text", text: `Error: ${message}` }],
64
+ isError: true,
65
+ };
66
+ }
67
+ }
68
+ );
69
+
70
+ async function main() {
71
+ const transport = new StdioServerTransport();
72
+ await server.connect(transport);
73
+ }
74
+
75
+ main().catch((error) => {
76
+ console.error("Server error:", error);
77
+ process.exit(1);
78
+ });
@@ -0,0 +1,142 @@
1
+ import { Octokit } from "octokit";
2
+
3
+ export interface SkillFile {
4
+ name: string;
5
+ path: string;
6
+ }
7
+
8
+ export class GitHubServiceError extends Error {
9
+ constructor(
10
+ message: string,
11
+ public readonly code: "NOT_FOUND" | "RATE_LIMIT" | "AUTH_ERROR" | "UNKNOWN"
12
+ ) {
13
+ super(message);
14
+ this.name = "GitHubServiceError";
15
+ }
16
+ }
17
+
18
+ export class GitHubService {
19
+ private octokit: Octokit;
20
+ private owner: string;
21
+ private repo: string;
22
+ private skillsPath: string;
23
+
24
+ constructor() {
25
+ const token = process.env.GITHUB_TOKEN;
26
+ this.owner = process.env.SKILLS_REPO_OWNER || "";
27
+ this.repo = process.env.SKILLS_REPO_NAME || "";
28
+ this.skillsPath = process.env.SKILLS_PATH || "";
29
+
30
+ if (!this.owner || !this.repo) {
31
+ throw new Error(
32
+ "SKILLS_REPO_OWNER and SKILLS_REPO_NAME environment variables are required"
33
+ );
34
+ }
35
+
36
+ this.octokit = new Octokit({ auth: token });
37
+ }
38
+
39
+ async listSkillFiles(): Promise<SkillFile[]> {
40
+ try {
41
+ const response = await this.octokit.rest.repos.getContent({
42
+ owner: this.owner,
43
+ repo: this.repo,
44
+ path: this.skillsPath,
45
+ });
46
+
47
+ if (!Array.isArray(response.data)) {
48
+ throw new GitHubServiceError(
49
+ "Expected directory but found file",
50
+ "UNKNOWN"
51
+ );
52
+ }
53
+
54
+ return response.data
55
+ .filter((item) => item.type === "file" && item.name.endsWith(".md"))
56
+ .map((item) => ({
57
+ name: item.name.replace(/\.md$/, ""),
58
+ path: item.path,
59
+ }));
60
+ } catch (error) {
61
+ throw this.handleError(error, "listing skills");
62
+ }
63
+ }
64
+
65
+ async getSkillContent(skillName: string): Promise<string> {
66
+ const filePath = this.skillsPath
67
+ ? `${this.skillsPath}/${skillName}.md`
68
+ : `${skillName}.md`;
69
+
70
+ try {
71
+ const response = await this.octokit.rest.repos.getContent({
72
+ owner: this.owner,
73
+ repo: this.repo,
74
+ path: filePath,
75
+ });
76
+
77
+ if (Array.isArray(response.data)) {
78
+ throw new GitHubServiceError(
79
+ `"${skillName}" is a directory, not a skill file`,
80
+ "NOT_FOUND"
81
+ );
82
+ }
83
+
84
+ if (response.data.type !== "file" || !("content" in response.data)) {
85
+ throw new GitHubServiceError(
86
+ `"${skillName}" is not a valid skill file`,
87
+ "NOT_FOUND"
88
+ );
89
+ }
90
+
91
+ const content = Buffer.from(response.data.content, "base64").toString(
92
+ "utf-8"
93
+ );
94
+ return content;
95
+ } catch (error) {
96
+ throw this.handleError(error, `fetching skill "${skillName}"`);
97
+ }
98
+ }
99
+
100
+ private handleError(error: unknown, context: string): GitHubServiceError {
101
+ if (error instanceof GitHubServiceError) {
102
+ return error;
103
+ }
104
+
105
+ if (error && typeof error === "object" && "status" in error) {
106
+ const status = (error as { status: number }).status;
107
+
108
+ switch (status) {
109
+ case 404:
110
+ return new GitHubServiceError(
111
+ `Resource not found while ${context}`,
112
+ "NOT_FOUND"
113
+ );
114
+ case 403:
115
+ const message =
116
+ "message" in error ? String(error.message) : "Rate limit exceeded";
117
+ if (message.toLowerCase().includes("rate limit")) {
118
+ return new GitHubServiceError(
119
+ `GitHub API rate limit exceeded while ${context}`,
120
+ "RATE_LIMIT"
121
+ );
122
+ }
123
+ return new GitHubServiceError(
124
+ `Access forbidden while ${context}. Check GITHUB_TOKEN.`,
125
+ "AUTH_ERROR"
126
+ );
127
+ case 401:
128
+ return new GitHubServiceError(
129
+ `Authentication failed while ${context}. Check GITHUB_TOKEN.`,
130
+ "AUTH_ERROR"
131
+ );
132
+ }
133
+ }
134
+
135
+ const errorMessage =
136
+ error instanceof Error ? error.message : "Unknown error";
137
+ return new GitHubServiceError(
138
+ `Error ${context}: ${errorMessage}`,
139
+ "UNKNOWN"
140
+ );
141
+ }
142
+ }
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import { GitHubService, GitHubServiceError } from "../services/github.js";
3
+
4
+ export const getSkillSchema = {
5
+ skill_name: z.string().describe("The name of the skill to retrieve"),
6
+ };
7
+
8
+ export interface GetSkillInput {
9
+ skill_name: string;
10
+ }
11
+
12
+ export interface GetSkillResult {
13
+ skill_name: string;
14
+ content: string;
15
+ }
16
+
17
+ export async function handleGetSkill(
18
+ githubService: GitHubService,
19
+ input: GetSkillInput
20
+ ): Promise<GetSkillResult> {
21
+ const { skill_name } = input;
22
+
23
+ if (!skill_name || typeof skill_name !== "string") {
24
+ throw new Error("skill_name is required and must be a string");
25
+ }
26
+
27
+ const trimmedName = skill_name.trim();
28
+ if (trimmedName.length === 0) {
29
+ throw new Error("skill_name cannot be empty");
30
+ }
31
+
32
+ try {
33
+ const content = await githubService.getSkillContent(trimmedName);
34
+ return {
35
+ skill_name: trimmedName,
36
+ content,
37
+ };
38
+ } catch (error) {
39
+ if (error instanceof GitHubServiceError) {
40
+ if (error.code === "NOT_FOUND") {
41
+ throw new Error(`Skill "${trimmedName}" not found`);
42
+ }
43
+ throw new Error(`Failed to get skill: ${error.message}`);
44
+ }
45
+ throw error;
46
+ }
47
+ }
@@ -0,0 +1,12 @@
1
+ export {
2
+ listSkillsSchema,
3
+ handleListSkills,
4
+ type ListSkillsResult,
5
+ } from "./list-skills.js";
6
+
7
+ export {
8
+ getSkillSchema,
9
+ handleGetSkill,
10
+ type GetSkillInput,
11
+ type GetSkillResult,
12
+ } from "./get-skill.js";
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ import { GitHubService, GitHubServiceError } from "../services/github.js";
3
+
4
+ export const listSkillsSchema = {};
5
+
6
+ export interface ListSkillsResult {
7
+ skills: string[];
8
+ }
9
+
10
+ export async function handleListSkills(
11
+ githubService: GitHubService
12
+ ): Promise<ListSkillsResult> {
13
+ try {
14
+ const files = await githubService.listSkillFiles();
15
+ const skills = files.map((file) => file.name);
16
+ return { skills };
17
+ } catch (error) {
18
+ if (error instanceof GitHubServiceError) {
19
+ throw new Error(`Failed to list skills: ${error.message}`);
20
+ }
21
+ throw error;
22
+ }
23
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "resolveJsonModule": true
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }