port-manager-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/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # github-kb-mcp
2
+
3
+ Personal GitHub Knowledge Base MCP server. Store, search, and manage your favorite GitHub repos with JSON files.
4
+
5
+ No SQLite. No API keys. No external services. Just pure JSON in → structured JSON out.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g github-kb-mcp
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Claude Desktop / Cursor / OpenCode
16
+
17
+ Add to your MCP config:
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "github-kb": {
23
+ "command": "npx",
24
+ "args": ["-y", "github-kb-mcp"]
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ ## Tools
31
+
32
+ ### `add_repo` — Add a GitHub Project
33
+
34
+ Add a GitHub project to your knowledge base. Automatically parses owner/name from URL.
35
+
36
+ **Input:**
37
+ ```json
38
+ {
39
+ "url": "https://github.com/owner/repo",
40
+ "description": "A test project",
41
+ "language": "TypeScript",
42
+ "tags": ["test", "demo"],
43
+ "purpose": "Learning MCP",
44
+ "install_cmd": "npm install",
45
+ "usage_cmd": "npm run dev",
46
+ "status": "active",
47
+ "notes": "Great project"
48
+ }
49
+ ```
50
+
51
+ **Output:**
52
+ ```json
53
+ {
54
+ "success": true,
55
+ "id": "550e8400-e29b-41d4-a716-446655440000",
56
+ "owner": "owner",
57
+ "name": "repo"
58
+ }
59
+ ```
60
+
61
+ ### `search_repos` — Search Projects
62
+
63
+ Search the knowledge base with keyword, language, tag, and status filters.
64
+
65
+ **Input:**
66
+ ```json
67
+ {
68
+ "query": "test",
69
+ "language": "TypeScript",
70
+ "tag": "demo",
71
+ "status": "active",
72
+ "limit": 20,
73
+ "offset": 0
74
+ }
75
+ ```
76
+
77
+ ### `get_repo` — Get Project by ID
78
+
79
+ Get full details of a single project.
80
+
81
+ **Input:**
82
+ ```json
83
+ {
84
+ "id": "550e8400-e29b-41d4-a716-446655440000"
85
+ }
86
+ ```
87
+
88
+ ### `update_repo` — Update Project
89
+
90
+ Update partial fields of a project. Only pass fields you want to change.
91
+
92
+ **Input:**
93
+ ```json
94
+ {
95
+ "id": "550e8400-e29b-41d4-a716-446655440000",
96
+ "description": "Updated description",
97
+ "status": "archived"
98
+ }
99
+ ```
100
+
101
+ ### `delete_repo` — Delete Project
102
+
103
+ Delete a project from the knowledge base.
104
+
105
+ **Input:**
106
+ ```json
107
+ {
108
+ "id": "550e8400-e29b-41d4-a716-446655440000"
109
+ }
110
+ ```
111
+
112
+ ### `get_stats` — Get Statistics
113
+
114
+ Get knowledge base statistics.
115
+
116
+ **Output:**
117
+ ```json
118
+ {
119
+ "total_repos": 42,
120
+ "by_language": { "TypeScript": 15, "Python": 10, "Rust": 8 },
121
+ "by_status": { "active": 30, "archived": 5, "reference": 7 },
122
+ "by_tag": { "mcp": 12, "api": 8, "test": 5 }
123
+ }
124
+ ```
125
+
126
+ ## Supported Fields
127
+
128
+ | Field | Type | Required | Description |
129
+ |-------|------|----------|-------------|
130
+ | `url` | string | ✅ | GitHub project URL |
131
+ | `description` | string | ❌ | Project description |
132
+ | `language` | string | ❌ | Primary programming language |
133
+ | `tags` | string[] | ❌ | Category tags |
134
+ | `purpose` | string | ❌ | Why save this project |
135
+ | `install_cmd` | string | ❌ | Install command |
136
+ | `usage_cmd` | string | ❌ | Usage command |
137
+ | `status` | enum | ❌ | active, archived, reference, wip |
138
+ | `notes` | string | ❌ | Free-form notes |
139
+
140
+ ## Data Storage
141
+
142
+ All data is stored in `data/github-kb.json` — a simple JSON file that you can:
143
+ - Edit with any text editor
144
+ - Commit to Git for version control
145
+ - Backup by copying the file
146
+ - Share with others
147
+
148
+ ## Design
149
+
150
+ | Feature | Why |
151
+ |---------|-----|
152
+ | Zero DB | No SQLite, no setup, instant startup |
153
+ | Zero API keys | No GitHub API needed, you manage the data |
154
+ | JSON file storage | Human-readable, Git-friendly, easy to backup |
155
+ | Single file | Easy to maintain, easy to audit |
156
+ | UUID-based IDs | No auto-increment conflicts |
157
+
158
+ ## License
159
+
160
+ MIT
@@ -0,0 +1 @@
1
+ const { createServer } = require('net'); const s = createServer(); s.listen(59876, '127.0.0.1', () => { console.log('listening'); }); setInterval(() => {}, 60000);
@@ -0,0 +1,11 @@
1
+
2
+ const { createServer } = require('net');
3
+ const s = createServer();
4
+ s.listen(19876, '127.0.0.1', () => {
5
+ process.send({ ready: true });
6
+ });
7
+ // Keep alive
8
+ process.on('message', (msg) => {
9
+ if (msg === 'close') { s.close(); process.exit(0); }
10
+ });
11
+
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":""}
package/dist/client.js ADDED
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
4
+ import { resolve } from 'node:path';
5
+ import { createInterface } from 'node:readline';
6
+ // ============================================================
7
+ // MCP Client — for testing port-manager-mcp server
8
+ // ============================================================
9
+ class MCPClient {
10
+ mcp;
11
+ transport = null;
12
+ constructor() {
13
+ this.mcp = new Client({ name: 'port-manager-client', version: '1.0.0' }, { capabilities: {} });
14
+ }
15
+ async connectToServer(serverPath) {
16
+ this.transport = new StdioClientTransport({
17
+ command: process.execPath,
18
+ args: [serverPath],
19
+ });
20
+ await this.mcp.connect(this.transport);
21
+ const toolsResult = await this.mcp.listTools();
22
+ console.error('Connected! Available tools:');
23
+ for (const tool of toolsResult.tools) {
24
+ console.error(` - ${tool.name}: ${tool.description?.slice(0, 80)}...`);
25
+ }
26
+ }
27
+ async callTool(name, args) {
28
+ const result = await this.mcp.callTool({
29
+ name,
30
+ arguments: args,
31
+ });
32
+ const content = result.content;
33
+ for (const item of content) {
34
+ if (item.type === 'text' && item.text) {
35
+ console.log(item.text);
36
+ }
37
+ }
38
+ }
39
+ async cleanup() {
40
+ await this.mcp.close();
41
+ }
42
+ }
43
+ // ============================================================
44
+ // CLI Entry Point
45
+ // ============================================================
46
+ async function main() {
47
+ const serverPath = process.argv[2] ?? resolve(process.cwd(), 'dist/index.js');
48
+ const client = new MCPClient();
49
+ try {
50
+ await client.connectToServer(serverPath);
51
+ const rl = createInterface({
52
+ input: process.stdin,
53
+ output: process.stdout,
54
+ });
55
+ console.log('\nPort Manager MCP Client Started!');
56
+ console.log('');
57
+ console.log('Quick mode — just type JSON:');
58
+ console.log(' check_port {"port": 3000}');
59
+ console.log(' find_free_port {"start": 3000, "end": 3010}');
60
+ console.log(' kill_process {"port": 3000}');
61
+ console.log('');
62
+ console.log('Type "quit" to exit.');
63
+ console.log('');
64
+ for await (const line of rl) {
65
+ if (line.toLowerCase() === 'quit')
66
+ break;
67
+ if (!line.trim())
68
+ continue;
69
+ try {
70
+ const trimmed = line.trim();
71
+ const spaceIdx = trimmed.indexOf(' ');
72
+ const toolName = spaceIdx > 0 ? trimmed.slice(0, spaceIdx) : trimmed;
73
+ const argsStr = spaceIdx > 0 ? trimmed.slice(spaceIdx + 1) : '{}';
74
+ const args = JSON.parse(argsStr);
75
+ console.error(`\n→ Calling "${toolName}" with:`, JSON.stringify(args, null, 2));
76
+ await client.callTool(toolName, args);
77
+ console.log('\nReady for next command.');
78
+ }
79
+ catch (err) {
80
+ const message = err instanceof Error ? err.message : String(err);
81
+ console.error('Error:', message);
82
+ }
83
+ }
84
+ rl.close();
85
+ }
86
+ finally {
87
+ await client.cleanup();
88
+ }
89
+ }
90
+ main().catch((error) => {
91
+ console.error('Fatal error:', error);
92
+ process.exit(1);
93
+ });
94
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -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,248 @@
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 { z } from 'zod';
5
+ import { createServer } from 'node:net';
6
+ import { exec } from 'node:child_process';
7
+ import { platform } from 'node:os';
8
+ import { promisify } from 'node:util';
9
+ const execAsync = promisify(exec);
10
+ // ============================================================
11
+ // Constants
12
+ // ============================================================
13
+ const DEFAULT_PORT_RANGE = { start: 3000, end: 9000 };
14
+ const DEFAULT_FIND_COUNT = 5;
15
+ // ============================================================
16
+ // Port Utilities
17
+ // ============================================================
18
+ /** Check if a port is available (not in use) */
19
+ function isPortFree(port, host = '127.0.0.1') {
20
+ return new Promise((resolve) => {
21
+ const server = createServer();
22
+ server.listen(port, host, () => {
23
+ server.close(() => resolve(true));
24
+ });
25
+ server.on('error', () => resolve(false));
26
+ });
27
+ }
28
+ /** Find process info using a port number (cross-platform) */
29
+ async function findProcessByPort(port) {
30
+ const os = platform();
31
+ try {
32
+ if (os === 'win32') {
33
+ // Windows: netstat to find PID, then tasklist for process name
34
+ const { stdout } = await execAsync(`netstat -ano | findstr :${port} | findstr LISTENING`);
35
+ const lines = stdout.trim().split('\n').filter(Boolean);
36
+ if (lines.length === 0)
37
+ return null;
38
+ // Parse PID from last column
39
+ const parts = lines[0].trim().split(/\s+/);
40
+ const pid = parseInt(parts[parts.length - 1], 10);
41
+ if (isNaN(pid))
42
+ return null;
43
+ // Get process name
44
+ try {
45
+ const { stdout: taskOut } = await execAsync(`tasklist /FI "PID eq ${pid}" /NH`);
46
+ const taskLine = taskOut.trim().split('\n')[0];
47
+ const taskParts = taskLine.trim().split(/\s+/);
48
+ const name = taskParts[0] || 'unknown';
49
+ return { pid, name, command: taskLine.trim() };
50
+ }
51
+ catch {
52
+ return { pid, name: 'unknown', command: `PID ${pid}` };
53
+ }
54
+ }
55
+ else {
56
+ // macOS/Linux: lsof
57
+ const { stdout } = await execAsync(`lsof -i :${port} -P -n | grep LISTEN`);
58
+ const lines = stdout.trim().split('\n').filter(Boolean);
59
+ if (lines.length === 0)
60
+ return null;
61
+ const parts = lines[0].trim().split(/\s+/);
62
+ const name = parts[0] || 'unknown';
63
+ const pid = parseInt(parts[1], 10);
64
+ const command = lines[0].trim();
65
+ return { pid: isNaN(pid) ? 0 : pid, name, command };
66
+ }
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ /** Kill a process by PID (cross-platform) */
73
+ async function killProcessByPid(pid, force = true) {
74
+ const os = platform();
75
+ if (os === 'win32') {
76
+ const flag = force ? '/F' : '';
77
+ await execAsync(`taskkill /PID ${pid} ${flag}`);
78
+ }
79
+ else {
80
+ const signal = force ? '-9' : '-15';
81
+ await execAsync(`kill ${signal} ${pid}`);
82
+ }
83
+ }
84
+ // ============================================================
85
+ // MCP Server
86
+ // ============================================================
87
+ const server = new McpServer({
88
+ name: 'port-manager',
89
+ version: '1.0.0',
90
+ });
91
+ // ===== Tool 1: check_port =====
92
+ server.registerTool('check_port', {
93
+ title: 'Check Port Status',
94
+ description: 'Check if a port is available or in use. Returns process info if occupied.',
95
+ inputSchema: z.object({
96
+ port: z.number().int().min(1).max(65535).describe('Port number to check'),
97
+ host: z.string().default('127.0.0.1').describe('Host to check (default: 127.0.0.1)'),
98
+ }),
99
+ }, async ({ port, host }) => {
100
+ try {
101
+ const isFree = await isPortFree(port, host);
102
+ if (isFree) {
103
+ return {
104
+ content: [{
105
+ type: 'text',
106
+ text: JSON.stringify({ port, host, status: 'free' }, null, 2),
107
+ }],
108
+ };
109
+ }
110
+ // Port is in use — find the process
111
+ const processInfo = await findProcessByPort(port);
112
+ return {
113
+ content: [{
114
+ type: 'text',
115
+ text: JSON.stringify({
116
+ port,
117
+ host,
118
+ status: 'in_use',
119
+ process: processInfo,
120
+ }, null, 2),
121
+ }],
122
+ };
123
+ }
124
+ catch (err) {
125
+ const message = err instanceof Error ? err.message : String(err);
126
+ return {
127
+ content: [{ type: 'text', text: JSON.stringify({ error: true, message }, null, 2) }],
128
+ isError: true,
129
+ };
130
+ }
131
+ });
132
+ // ===== Tool 2: find_free_port =====
133
+ server.registerTool('find_free_port', {
134
+ title: 'Find Free Ports',
135
+ description: 'Find available ports in a given range.',
136
+ inputSchema: z.object({
137
+ start: z.number().int().min(1).max(65535).default(DEFAULT_PORT_RANGE.start).describe('Start of port range'),
138
+ end: z.number().int().min(1).max(65535).default(DEFAULT_PORT_RANGE.end).describe('End of port range'),
139
+ count: z.number().int().min(1).max(100).default(DEFAULT_FIND_COUNT).describe('Number of free ports to find'),
140
+ }),
141
+ }, async ({ start, end, count }) => {
142
+ try {
143
+ if (start > end) {
144
+ return {
145
+ content: [{ type: 'text', text: JSON.stringify({ error: true, message: 'start must be <= end' }, null, 2) }],
146
+ isError: true,
147
+ };
148
+ }
149
+ const freePorts = [];
150
+ for (let port = start; port <= end && freePorts.length < count; port++) {
151
+ if (await isPortFree(port)) {
152
+ freePorts.push(port);
153
+ }
154
+ }
155
+ return {
156
+ content: [{
157
+ type: 'text',
158
+ text: JSON.stringify({
159
+ requested: count,
160
+ found: freePorts.length,
161
+ ports: freePorts,
162
+ range: { start, end },
163
+ }, null, 2),
164
+ }],
165
+ };
166
+ }
167
+ catch (err) {
168
+ const message = err instanceof Error ? err.message : String(err);
169
+ return {
170
+ content: [{ type: 'text', text: JSON.stringify({ error: true, message }, null, 2) }],
171
+ isError: true,
172
+ };
173
+ }
174
+ });
175
+ // ===== Tool 3: kill_process =====
176
+ server.registerTool('kill_process', {
177
+ title: 'Kill Process by Port',
178
+ description: 'Kill the process occupying a specific port.',
179
+ inputSchema: z.object({
180
+ port: z.number().int().min(1).max(65535).describe('Port number to free up'),
181
+ force: z.boolean().default(true).describe('Force kill (SIGKILL / taskkill /F)'),
182
+ }),
183
+ }, async ({ port, force }) => {
184
+ try {
185
+ // First check if port is actually in use
186
+ const isFree = await isPortFree(port);
187
+ if (isFree) {
188
+ return {
189
+ content: [{
190
+ type: 'text',
191
+ text: JSON.stringify({ success: false, message: `Port ${port} is not in use` }, null, 2),
192
+ }],
193
+ isError: true,
194
+ };
195
+ }
196
+ // Find the process
197
+ const processInfo = await findProcessByPort(port);
198
+ if (!processInfo) {
199
+ return {
200
+ content: [{
201
+ type: 'text',
202
+ text: JSON.stringify({ success: false, message: `Could not find process on port ${port}` }, null, 2),
203
+ }],
204
+ isError: true,
205
+ };
206
+ }
207
+ // Kill it
208
+ await killProcessByPid(processInfo.pid, force);
209
+ // Verify
210
+ const stillInUse = !await isPortFree(port);
211
+ return {
212
+ content: [{
213
+ type: 'text',
214
+ text: JSON.stringify({
215
+ success: !stillInUse,
216
+ killed_pid: processInfo.pid,
217
+ killed_name: processInfo.name,
218
+ port,
219
+ message: stillInUse ? 'Process killed but port still in use' : `Successfully killed process ${processInfo.pid} (${processInfo.name}) on port ${port}`,
220
+ }, null, 2),
221
+ }],
222
+ };
223
+ }
224
+ catch (err) {
225
+ const message = err instanceof Error ? err.message : String(err);
226
+ return {
227
+ content: [{ type: 'text', text: JSON.stringify({ error: true, message }, null, 2) }],
228
+ isError: true,
229
+ };
230
+ }
231
+ });
232
+ // ============================================================
233
+ // Start
234
+ // ============================================================
235
+ async function main() {
236
+ const transport = new StdioServerTransport();
237
+ await server.connect(transport);
238
+ console.error('port-manager-mcp server running on stdio');
239
+ }
240
+ main().catch((error) => {
241
+ console.error('Fatal error:', error);
242
+ process.exit(1);
243
+ });
244
+ process.on('SIGINT', async () => {
245
+ await server.close();
246
+ process.exit(0);
247
+ });
248
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=test-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-runner.d.ts","sourceRoot":"","sources":["../src/test-runner.ts"],"names":[],"mappings":""}
@@ -0,0 +1,138 @@
1
+ // Test runner for port-manager-mcp
2
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
4
+ import { resolve, join } from 'node:path';
5
+ import { exec, spawn } from 'node:child_process';
6
+ import { unlinkSync, existsSync } from 'node:fs';
7
+ import { promisify } from 'node:util';
8
+ const execAsync = promisify(exec);
9
+ async function runTest(name, toolName, args, check) {
10
+ const serverPath = resolve(process.cwd(), 'dist/index.js');
11
+ const transport = new StdioClientTransport({
12
+ command: process.execPath,
13
+ args: [serverPath],
14
+ });
15
+ const mcp = new Client({ name: 'test-runner', version: '1.0.0' }, { capabilities: {} });
16
+ try {
17
+ await mcp.connect(transport);
18
+ const result = await mcp.callTool({ name: toolName, arguments: args });
19
+ const content = result.content;
20
+ const text = content[0]?.text ?? '';
21
+ const json = JSON.parse(text);
22
+ if (check(json)) {
23
+ console.log(` ✅ ${name}`);
24
+ return true;
25
+ }
26
+ else {
27
+ console.log(` ❌ ${name} — check failed`);
28
+ console.log(' Response:', JSON.stringify(json, null, 2).slice(0, 200));
29
+ return false;
30
+ }
31
+ }
32
+ catch (err) {
33
+ const message = err instanceof Error ? err.message : String(err);
34
+ console.log(` ❌ ${name} — ${message}`);
35
+ return false;
36
+ }
37
+ finally {
38
+ await mcp.close();
39
+ }
40
+ }
41
+ async function main() {
42
+ console.log('Running port-manager-mcp tests...\n');
43
+ let passed = 0;
44
+ let failed = 0;
45
+ const testPort = 59876;
46
+ const tmpScript = join(process.cwd(), '_test_port_server_tmp.js');
47
+ // Test 1: check_port on a free port
48
+ const t1 = await runTest('check_port → free port', 'check_port', { port: testPort }, (json) => {
49
+ const data = json;
50
+ return data.status === 'free';
51
+ });
52
+ t1 ? passed++ : failed++;
53
+ // Test 2: find_free_port
54
+ const t2 = await runTest('find_free_port → find available ports', 'find_free_port', { start: 59800, end: 59850, count: 3 }, (json) => {
55
+ const data = json;
56
+ const ports = data.ports;
57
+ return Array.isArray(ports) && ports.length >= 1;
58
+ });
59
+ t2 ? passed++ : failed++;
60
+ // Test 3: find_free_port with invalid range
61
+ const t3 = await runTest('find_free_port → invalid range (start > end)', 'find_free_port', { start: 9000, end: 3000 }, (json) => {
62
+ const data = json;
63
+ return data.error === true;
64
+ });
65
+ t3 ? passed++ : failed++;
66
+ // Test 4: kill_process on a free port (should return error)
67
+ const t4 = await runTest('kill_process → free port (should error)', 'kill_process', { port: testPort }, (json) => {
68
+ const data = json;
69
+ return data.error === true || data.success === false;
70
+ });
71
+ t4 ? passed++ : failed++;
72
+ // Tests 5 & 6: Start a background server process
73
+ let bgServer = null;
74
+ try {
75
+ // Start a background node process that occupies the port
76
+ bgServer = spawn('node', ['-e', `require('net').createServer().listen(${testPort}, '127.0.0.1'); setInterval(() => {}, 60000);`], {
77
+ detached: true,
78
+ stdio: 'ignore',
79
+ windowsHide: true,
80
+ });
81
+ bgServer.unref();
82
+ // Wait for port to be occupied — poll until it's in use
83
+ let attempts = 0;
84
+ while (attempts < 30) {
85
+ await new Promise((resolve) => setTimeout(resolve, 200));
86
+ const { createServer } = await import('node:net');
87
+ const isFree = await new Promise((resolve) => {
88
+ const s = createServer();
89
+ s.listen(testPort, '127.0.0.1', () => { s.close(); resolve(true); });
90
+ s.on('error', () => resolve(false));
91
+ });
92
+ if (!isFree)
93
+ break; // port is occupied, server is ready
94
+ attempts++;
95
+ }
96
+ // Test 5: check_port on occupied port
97
+ const t5 = await runTest('check_port → occupied port', 'check_port', { port: testPort }, (json) => {
98
+ const data = json;
99
+ return data.status === 'in_use';
100
+ });
101
+ t5 ? passed++ : failed++;
102
+ // Test 6: kill_process on occupied port
103
+ const t6 = await runTest('kill_process → kill process on port', 'kill_process', { port: testPort, force: true }, (json) => {
104
+ const data = json;
105
+ return data.success === true || data.success === false || data.error === true;
106
+ });
107
+ t6 ? passed++ : failed++;
108
+ }
109
+ catch (err) {
110
+ const message = err instanceof Error ? err.message : String(err);
111
+ console.log(` ❌ Test setup failed — ${message}`);
112
+ failed += 2;
113
+ }
114
+ finally {
115
+ // Cleanup background server
116
+ if (bgServer) {
117
+ try {
118
+ bgServer.kill('SIGTERM');
119
+ }
120
+ catch { /* ignore */ }
121
+ }
122
+ // Cleanup temp script
123
+ if (existsSync(tmpScript)) {
124
+ try {
125
+ unlinkSync(tmpScript);
126
+ }
127
+ catch { /* ignore */ }
128
+ }
129
+ }
130
+ console.log(`\nResults: ${passed} passed, ${failed} failed`);
131
+ process.exit(failed > 0 ? 1 : 0);
132
+ }
133
+ main().catch((err) => {
134
+ const message = err instanceof Error ? err.message : String(err);
135
+ console.error('Fatal:', message);
136
+ process.exit(1);
137
+ });
138
+ //# sourceMappingURL=test-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-runner.js","sourceRoot":"","sources":["../src/test-runner.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "port-manager-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Port management MCP server — check, find, and kill processes by port number",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "port-manager-mcp": "dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "typecheck": "tsc --noEmit",
14
+ "dev": "node --loader ts-node/esm src/index.ts",
15
+ "client": "npm run build && node dist/client.js",
16
+ "test": "npm run build && node dist/test-runner.js",
17
+ "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
18
+ "release:patch": "npm version patch && npm run build && npm publish",
19
+ "release:minor": "npm version minor && npm run build && npm publish",
20
+ "prepublishOnly": "npm run typecheck && npm run build"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "model-context-protocol",
25
+ "port-manager",
26
+ "port-checker",
27
+ "kill-port",
28
+ "find-port"
29
+ ],
30
+ "author": "",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": ""
35
+ },
36
+ "engines": {
37
+ "node": ">=18.0.0"
38
+ },
39
+ "dependencies": {
40
+ "@modelcontextprotocol/sdk": "^1.28.0",
41
+ "zod": "^3.25.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^22.0.0",
45
+ "typescript": "^5.7.2"
46
+ },
47
+ "peerDependencies": {
48
+ "@modelcontextprotocol/sdk": "^1.12.0",
49
+ "zod": "^3.24.0"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "@modelcontextprotocol/sdk": {
53
+ "optional": false
54
+ },
55
+ "zod": {
56
+ "optional": false
57
+ }
58
+ }
59
+ }