kadins-personal-mcp 1.0.0 → 1.0.1

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/PUBLISHING.md DELETED
@@ -1,43 +0,0 @@
1
- # Publishing Kadins Personal MCP
2
-
3
- ## Prerequisites
4
-
5
- 1. Make sure you have an npm account at https://www.npmjs.com/
6
- 2. Ensure you're logged in to npm: `npm login`
7
-
8
- ## To Publish
9
-
10
- 1. Update the version in package.json if needed: `npm version patch` (or minor/major)
11
- 2. Run tests to make sure everything works: `npm test`
12
- 3. Build the project: `npm run build`
13
- 4. Publish to npm: `npm publish`
14
-
15
- ## Local Installation for Testing
16
-
17
- To install locally for testing:
18
-
19
- ```bash
20
- npm install -g .
21
- ```
22
-
23
- Or to link for development:
24
-
25
- ```bash
26
- npm link
27
- ```
28
-
29
- ## Verification
30
-
31
- After publishing, you can install your package globally:
32
-
33
- ```bash
34
- npm install -g kadins-personal-mcp
35
- ```
36
-
37
- And then run it:
38
-
39
- ```bash
40
- kadins-personal-mcp
41
- ```
42
-
43
- The server will start and be available for MCP-compatible clients.
package/gitignore DELETED
@@ -1,7 +0,0 @@
1
- dist/
2
- node_modules/
3
-
4
- # System
5
- .DS_Store
6
- .idea/
7
- .vscode/
package/src/index.ts DELETED
@@ -1,63 +0,0 @@
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 { z } from "zod";
6
- import { loadTools } from "./toolLoader.js";
7
-
8
- const packageJson = require("../package.json") as any;
9
-
10
- // Create a new MCP server
11
- const server = new McpServer(
12
- {
13
- name: packageJson.name,
14
- version: packageJson.version,
15
- },
16
- {
17
- instructions:
18
- "These tools communicate with a reference Model Context Protocol (MCP) server.",
19
- }
20
- );
21
-
22
- // Implement the ping_pong tool
23
- server.tool(
24
- "ping_pong",
25
- "Ping the server and receive a pong back",
26
- {},
27
- async () => {
28
- return {
29
- content: [{ type: "text", text: "pong" }],
30
- };
31
- }
32
- );
33
-
34
- // Implement the echo tool
35
- server.tool(
36
- "echo",
37
- "Send a message to the server and receive the message back",
38
- { message: z.string() },
39
- async (params) => {
40
- return {
41
- content: [{ type: "text", text: params.message }],
42
- };
43
- }
44
- );
45
-
46
- // Start the server
47
- async function run() {
48
- try {
49
- // Load tools from the tools directory
50
- await loadTools(server);
51
-
52
- // Use stdio for transport
53
- const transport = new StdioServerTransport();
54
- await server.connect(transport);
55
- // Since stdout is used for MCP messages, use stderr for logging
56
- console.error("MCP server connected via stdio");
57
- } catch (error) {
58
- console.error("Error starting MCP server:", error);
59
- process.exit(1);
60
- }
61
- }
62
-
63
- run();
@@ -1,9 +0,0 @@
1
- import { test } from '@jest/globals';
2
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
-
4
- // Simple test to verify the tool loader can be imported without errors
5
- test('toolLoader can be imported', async () => {
6
- // We'll just check that the module can be imported without errors
7
- // Since the actual loading depends on the file system, we'll do a basic import test
8
- expect(typeof (await import('./toolLoader.js'))).toBe('object');
9
- });
package/src/toolLoader.ts DELETED
@@ -1,59 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
-
5
- // Define the interface for tool files
6
- interface ToolDefinition {
7
- name: string;
8
- description: string;
9
- parameters: Record<string, any>;
10
- handler: (params: any) => Promise<any>;
11
- }
12
-
13
- // Validates that a loaded module has the required tool properties
14
- function isValidTool(module: any): module is ToolDefinition {
15
- return (
16
- typeof module.name === 'string' &&
17
- typeof module.description === 'string' &&
18
- typeof module.parameters === 'object' &&
19
- typeof module.handler === 'function'
20
- );
21
- }
22
-
23
- // Load and register all tools from the tools directory
24
- export async function loadTools(server: McpServer) {
25
- const toolsDir = path.join(process.cwd(), 'tools');
26
-
27
- if (!fs.existsSync(toolsDir)) {
28
- console.error(`Tools directory does not exist: ${toolsDir}`);
29
- return;
30
- }
31
-
32
- const files = fs.readdirSync(toolsDir);
33
-
34
- for (const file of files) {
35
- if (file.endsWith('.ts') || file.endsWith('.js')) {
36
- try {
37
- // Use dynamic import to load the tool file
38
- const modulePath = path.join(toolsDir, file);
39
- const module = await import(modulePath);
40
-
41
- if (isValidTool(module)) {
42
- // Register the tool with the MCP server
43
- server.tool(
44
- module.name,
45
- module.description,
46
- module.parameters,
47
- module.handler
48
- );
49
-
50
- console.error(`Registered tool: ${module.name}`);
51
- } else {
52
- console.error(`Invalid tool format in file: ${file}. Required exports: name, description, parameters, handler`);
53
- }
54
- } catch (error) {
55
- console.error(`Error loading tool from file ${file}:`, error);
56
- }
57
- }
58
- }
59
- }
@@ -1,40 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const name = "calculator";
4
- export const description = "Perform basic mathematical calculations";
5
- export const parameters = {
6
- operation: z.enum(["add", "subtract", "multiply", "divide"]).describe("The mathematical operation to perform"),
7
- a: z.number().describe("The first operand"),
8
- b: z.number().describe("The second operand")
9
- };
10
- export const handler = async (params: { operation: string; a: number; b: number }) => {
11
- let result: number;
12
-
13
- switch (params.operation) {
14
- case "add":
15
- result = params.a + params.b;
16
- break;
17
- case "subtract":
18
- result = params.a - params.b;
19
- break;
20
- case "multiply":
21
- result = params.a * params.b;
22
- break;
23
- case "divide":
24
- if (params.b === 0) {
25
- return {
26
- content: [{ type: "text", text: "Error: Division by zero is not allowed" }]
27
- };
28
- }
29
- result = params.a / params.b;
30
- break;
31
- default:
32
- return {
33
- content: [{ type: "text", text: `Error: Unknown operation ${params.operation}` }]
34
- };
35
- }
36
-
37
- return {
38
- content: [{ type: "text", text: `Result: ${params.a} ${params.operation} ${params.b} = ${result}` }]
39
- };
40
- };
@@ -1,11 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const name = "get_current_time";
4
- export const description = "Get the current date and time";
5
- export const parameters = {};
6
- export const handler = async () => {
7
- const now = new Date();
8
- return {
9
- content: [{ type: "text", text: `Current date and time: ${now.toString()}` }]
10
- };
11
- };
@@ -1,43 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const name = "directory_create";
4
- export const description = "Create a new directory";
5
- export const parameters = {
6
- path: z.string().describe("The path to the directory to create"),
7
- recursive: z.boolean().describe("Whether to create parent directories if they don't exist").optional().default(true)
8
- };
9
- export const handler = async (params: { path: string; recursive: boolean }) => {
10
- try {
11
- const fs = await import("fs");
12
- const path = await import("path");
13
-
14
- // Resolve the path relative to the current working directory
15
- const resolvedPath = path.resolve(params.path);
16
-
17
- // Check if the path is within the current working directory to prevent path traversal
18
- const cwd = process.cwd();
19
- if (!resolvedPath.startsWith(cwd)) {
20
- return {
21
- content: [{ type: "text", text: "Error: Path traversal is not allowed" }]
22
- };
23
- }
24
-
25
- // Check if directory already exists
26
- if (fs.existsSync(resolvedPath)) {
27
- return {
28
- content: [{ type: "text", text: `Error: Directory already exists: ${params.path}` }]
29
- };
30
- }
31
-
32
- // Create the directory
33
- fs.mkdirSync(resolvedPath, { recursive: params.recursive });
34
-
35
- return {
36
- content: [{ type: "text", text: `Successfully created directory: ${params.path}` }]
37
- };
38
- } catch (error) {
39
- return {
40
- content: [{ type: "text", text: `Error creating directory: ${(error as Error).message}` }]
41
- };
42
- }
43
- };
@@ -1,70 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const name = "directory_list";
4
- export const description = "List directory contents with details (size, type, etc.)";
5
- export const parameters = {
6
- directory: z.string().describe("The directory to list").optional().default("."),
7
- showHidden: z.boolean().describe("Whether to show hidden files/directories").optional().default(false)
8
- };
9
- export const handler = async (params: { directory: string; showHidden?: boolean }) => {
10
- try {
11
- const fs = await import("fs");
12
- const path = await import("path");
13
-
14
- const searchDir = params.directory || ".";
15
- const resolvedDir = path.resolve(searchDir);
16
-
17
- // Check if the directory is within the current working directory to prevent path traversal
18
- const cwd = process.cwd();
19
- if (!resolvedDir.startsWith(cwd)) {
20
- return {
21
- content: [{ type: "text", text: "Error: Directory traversal is not allowed" }]
22
- };
23
- }
24
-
25
- if (!fs.existsSync(resolvedDir)) {
26
- return {
27
- content: [{ type: "text", text: `Error: Directory does not exist: ${resolvedDir}` }]
28
- };
29
- }
30
-
31
- if (!fs.statSync(resolvedDir).isDirectory()) {
32
- return {
33
- content: [{ type: "text", text: `Error: Path is not a directory: ${resolvedDir}` }]
34
- };
35
- }
36
-
37
- const items = fs.readdirSync(resolvedDir);
38
- const results: string[] = [];
39
-
40
- for (const item of items) {
41
- // Skip hidden files if showHidden is false
42
- if (!params.showHidden && item.startsWith('.')) {
43
- continue;
44
- }
45
-
46
- const itemPath = path.join(resolvedDir, item);
47
- const stat = fs.statSync(itemPath);
48
-
49
- let type = stat.isDirectory() ? "DIR" : "FILE";
50
- let size = stat.isDirectory() ? "-" : `${stat.size} bytes`;
51
- let permissions = stat.mode.toString(8).slice(-3);
52
-
53
- results.push(`${type} ${permissions} ${size} ${item}`);
54
- }
55
-
56
- if (results.length === 0) {
57
- return {
58
- content: [{ type: "text", text: `Directory is empty: ${params.directory || "."}` }]
59
- };
60
- }
61
-
62
- return {
63
- content: [{ type: "text", text: `Contents of ${params.directory || "."}:\n${results.join("\n")}` }]
64
- };
65
- } catch (error) {
66
- return {
67
- content: [{ type: "text", text: `Error listing directory: ${(error as Error).message}` }]
68
- };
69
- }
70
- };
package/tools/echo.ts DELETED
@@ -1,12 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const name = "file_echo";
4
- export const description = "Send a message to the server and receive the message back (file-based tool)";
5
- export const parameters = {
6
- message: z.string().describe("The message to echo back")
7
- };
8
- export const handler = async (params: { message: string }) => {
9
- return {
10
- content: [{ type: "text", text: params.message }]
11
- };
12
- };
package/tools/fileCopy.ts DELETED
@@ -1,55 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const name = "file_copy";
4
- export const description = "Copy a file from one location to another";
5
- export const parameters = {
6
- source: z.string().describe("The path to the source file"),
7
- destination: z.string().describe("The path to the destination file")
8
- };
9
- export const handler = async (params: { source: string; destination: string }) => {
10
- try {
11
- const fs = await import("fs");
12
- const path = await import("path");
13
-
14
- // Resolve paths relative to the current working directory
15
- const resolvedSource = path.resolve(params.source);
16
- const resolvedDestination = path.resolve(params.destination);
17
-
18
- // Check if paths are within the current working directory to prevent path traversal
19
- const cwd = process.cwd();
20
- if (!resolvedSource.startsWith(cwd) || !resolvedDestination.startsWith(cwd)) {
21
- return {
22
- content: [{ type: "text", text: "Error: Path traversal is not allowed" }]
23
- };
24
- }
25
-
26
- if (!fs.existsSync(resolvedSource)) {
27
- return {
28
- content: [{ type: "text", text: `Error: Source file does not exist: ${params.source}` }]
29
- };
30
- }
31
-
32
- if (fs.statSync(resolvedSource).isDirectory()) {
33
- return {
34
- content: [{ type: "text", text: `Error: Source path is a directory, not a file: ${params.source}` }]
35
- };
36
- }
37
-
38
- // Create destination directory if it doesn't exist
39
- const destDir = path.dirname(resolvedDestination);
40
- if (!fs.existsSync(destDir)) {
41
- fs.mkdirSync(destDir, { recursive: true });
42
- }
43
-
44
- // Copy the file
45
- fs.copyFileSync(resolvedSource, resolvedDestination);
46
-
47
- return {
48
- content: [{ type: "text", text: `Successfully copied file from ${params.source} to ${params.destination}` }]
49
- };
50
- } catch (error) {
51
- return {
52
- content: [{ type: "text", text: `Error copying file: ${(error as Error).message}` }]
53
- };
54
- }
55
- };
@@ -1,48 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const name = "file_delete";
4
- export const description = "Delete a specified file";
5
- export const parameters = {
6
- path: z.string().describe("The path to the file to delete")
7
- };
8
- export const handler = async (params: { path: string }) => {
9
- try {
10
- const fs = await import("fs");
11
- const path = await import("path");
12
-
13
- // Resolve the path relative to the current working directory
14
- const resolvedPath = path.resolve(params.path);
15
-
16
- // Check if the path is within the current working directory to prevent path traversal
17
- const cwd = process.cwd();
18
- if (!resolvedPath.startsWith(cwd)) {
19
- return {
20
- content: [{ type: "text", text: "Error: Path traversal is not allowed" }]
21
- };
22
- }
23
-
24
- if (!fs.existsSync(resolvedPath)) {
25
- return {
26
- content: [{ type: "text", text: `Error: File does not exist: ${params.path}` }]
27
- };
28
- }
29
-
30
- const stat = fs.statSync(resolvedPath);
31
- if (stat.isDirectory()) {
32
- return {
33
- content: [{ type: "text", text: `Error: Path is a directory, not a file: ${params.path}` }]
34
- };
35
- }
36
-
37
- // Delete the file
38
- fs.unlinkSync(resolvedPath);
39
-
40
- return {
41
- content: [{ type: "text", text: `Successfully deleted file: ${params.path}` }]
42
- };
43
- } catch (error) {
44
- return {
45
- content: [{ type: "text", text: `Error deleting file: ${(error as Error).message}` }]
46
- };
47
- }
48
- };
package/tools/fileList.ts DELETED
@@ -1,88 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const name = "file_list";
4
- export const description = "List files in a directory with optional filtering";
5
- export const parameters = {
6
- directory: z.string().describe("The directory to list").optional().default("."),
7
- filter: z.string().describe("Optional filter pattern to match files").optional(),
8
- recursive: z.boolean().describe("Whether to list files recursively").optional().default(false)
9
- };
10
- export const handler = async (params: { directory: string; filter?: string; recursive?: boolean }) => {
11
- try {
12
- const fs = await import("fs");
13
- const path = await import("path");
14
-
15
- const searchDir = params.directory || ".";
16
- const resolvedDir = path.resolve(searchDir);
17
-
18
- // Check if the directory is within the current working directory to prevent path traversal
19
- const cwd = process.cwd();
20
- if (!resolvedDir.startsWith(cwd)) {
21
- return {
22
- content: [{ type: "text", text: "Error: Directory traversal is not allowed" }]
23
- };
24
- }
25
-
26
- if (!fs.existsSync(resolvedDir)) {
27
- return {
28
- content: [{ type: "text", text: `Error: Directory does not exist: ${resolvedDir}` }]
29
- };
30
- }
31
-
32
- if (!fs.statSync(resolvedDir).isDirectory()) {
33
- return {
34
- content: [{ type: "text", text: `Error: Path is not a directory: ${resolvedDir}` }]
35
- };
36
- }
37
-
38
- const results: string[] = [];
39
-
40
- function listDirectory(dir: string, isRoot: boolean = true) {
41
- const items = fs.readdirSync(dir);
42
-
43
- for (const item of items) {
44
- const itemPath = path.join(dir, item);
45
- const relativePath = path.relative(resolvedDir, itemPath);
46
-
47
- // Apply filter if provided
48
- if (params.filter && !item.includes(params.filter)) {
49
- continue;
50
- }
51
-
52
- // Skip node_modules and other common directories when not in recursive mode
53
- if (!params.recursive && !isRoot && (item === "node_modules" || item.startsWith("."))) {
54
- continue;
55
- }
56
-
57
- const stat = fs.statSync(itemPath);
58
-
59
- if (stat.isDirectory()) {
60
- if (params.recursive && item !== "node_modules" && !item.startsWith(".")) {
61
- results.push(`${relativePath}/ (directory)`);
62
- listDirectory(itemPath, false);
63
- } else if (!params.recursive) {
64
- results.push(`${relativePath}/ (directory)`);
65
- }
66
- } else {
67
- results.push(`${relativePath} (${stat.size} bytes)`);
68
- }
69
- }
70
- }
71
-
72
- listDirectory(resolvedDir);
73
-
74
- if (results.length === 0) {
75
- return {
76
- content: [{ type: "text", text: `No files found in directory: ${resolvedDir}` }]
77
- };
78
- }
79
-
80
- return {
81
- content: [{ type: "text", text: `Files in ${params.directory || "."}:\n${results.join("\n")}` }]
82
- };
83
- } catch (error) {
84
- return {
85
- content: [{ type: "text", text: `Error listing files: ${(error as Error).message}` }]
86
- };
87
- }
88
- };
package/tools/fileMove.ts DELETED
@@ -1,62 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const name = "file_move";
4
- export const description = "Move/rename a file";
5
- export const parameters = {
6
- source: z.string().describe("The path to the source file"),
7
- destination: z.string().describe("The path to the destination file")
8
- };
9
- export const handler = async (params: { source: string; destination: string }) => {
10
- try {
11
- const fs = await import("fs");
12
- const path = await import("path");
13
-
14
- // Resolve paths relative to the current working directory
15
- const resolvedSource = path.resolve(params.source);
16
- const resolvedDestination = path.resolve(params.destination);
17
-
18
- // Check if paths are within the current working directory to prevent path traversal
19
- const cwd = process.cwd();
20
- if (!resolvedSource.startsWith(cwd) || !resolvedDestination.startsWith(cwd)) {
21
- return {
22
- content: [{ type: "text", text: "Error: Path traversal is not allowed" }]
23
- };
24
- }
25
-
26
- if (!fs.existsSync(resolvedSource)) {
27
- return {
28
- content: [{ type: "text", text: `Error: Source file does not exist: ${params.source}` }]
29
- };
30
- }
31
-
32
- if (fs.statSync(resolvedSource).isDirectory()) {
33
- return {
34
- content: [{ type: "text", text: `Error: Source path is a directory, not a file: ${params.source}` }]
35
- };
36
- }
37
-
38
- // Check if destination already exists
39
- if (fs.existsSync(resolvedDestination)) {
40
- return {
41
- content: [{ type: "text", text: `Error: Destination file already exists: ${params.destination}` }]
42
- };
43
- }
44
-
45
- // Create destination directory if it doesn't exist
46
- const destDir = path.dirname(resolvedDestination);
47
- if (!fs.existsSync(destDir)) {
48
- fs.mkdirSync(destDir, { recursive: true });
49
- }
50
-
51
- // Move the file (rename it)
52
- fs.renameSync(resolvedSource, resolvedDestination);
53
-
54
- return {
55
- content: [{ type: "text", text: `Successfully moved file from ${params.source} to ${params.destination}` }]
56
- };
57
- } catch (error) {
58
- return {
59
- content: [{ type: "text", text: `Error moving file: ${(error as Error).message}` }]
60
- };
61
- }
62
- };
@@ -1,50 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const name = "file_permissions";
4
- export const description = "Change file permissions";
5
- export const parameters = {
6
- path: z.string().describe("The path to the file"),
7
- permissions: z.string().describe("The new permissions in octal format (e.g., '755', '644')")
8
- };
9
- export const handler = async (params: { path: string; permissions: string }) => {
10
- try {
11
- const fs = await import("fs");
12
- const path = await import("path");
13
-
14
- // Resolve the path relative to the current working directory
15
- const resolvedPath = path.resolve(params.path);
16
-
17
- // Check if the path is within the current working directory to prevent path traversal
18
- const cwd = process.cwd();
19
- if (!resolvedPath.startsWith(cwd)) {
20
- return {
21
- content: [{ type: "text", text: "Error: Path traversal is not allowed" }]
22
- };
23
- }
24
-
25
- if (!fs.existsSync(resolvedPath)) {
26
- return {
27
- content: [{ type: "text", text: `Error: File does not exist: ${params.path}` }]
28
- };
29
- }
30
-
31
- // Validate permissions format (should be 3 or 4 digits)
32
- if (!/^[0-7]{3,4}$/.test(params.permissions)) {
33
- return {
34
- content: [{ type: "text", text: `Error: Invalid permissions format. Use octal format like '755' or '644'.` }]
35
- };
36
- }
37
-
38
- // Change file permissions
39
- const permissionsNum = parseInt(params.permissions, 8);
40
- fs.chmodSync(resolvedPath, permissionsNum);
41
-
42
- return {
43
- content: [{ type: "text", text: `Successfully changed permissions of ${params.path} to ${params.permissions}` }]
44
- };
45
- } catch (error) {
46
- return {
47
- content: [{ type: "text", text: `Error changing permissions: ${(error as Error).message}` }]
48
- };
49
- }
50
- };