@lionad/port-key-mcp 0.2.0 → 0.4.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.
Files changed (56) hide show
  1. package/README.md +137 -85
  2. package/dist/config/index.d.ts +26 -0
  3. package/dist/config/index.js +24 -0
  4. package/dist/config/index.js.map +1 -0
  5. package/dist/logger.d.ts +7 -0
  6. package/dist/logger.js +48 -0
  7. package/dist/logger.js.map +1 -0
  8. package/dist/mcp-cli.js +84 -12
  9. package/dist/mcp-cli.js.map +1 -1
  10. package/dist/mcp-server.d.ts +16 -2
  11. package/dist/mcp-server.js +250 -92
  12. package/dist/mcp-server.js.map +1 -1
  13. package/dist/resources/index.d.ts +13 -0
  14. package/dist/resources/index.js +5 -0
  15. package/dist/resources/index.js.map +1 -0
  16. package/dist/resources/port-mapping-config.d.ts +13 -0
  17. package/dist/resources/port-mapping-config.js +22 -0
  18. package/dist/resources/port-mapping-config.js.map +1 -0
  19. package/dist/resources/project-port-history.d.ts +15 -0
  20. package/dist/resources/project-port-history.js +30 -0
  21. package/dist/resources/project-port-history.js.map +1 -0
  22. package/dist/session-manager.d.ts +35 -0
  23. package/dist/session-manager.js +66 -0
  24. package/dist/session-manager.js.map +1 -0
  25. package/dist/tools/check-port-availability.d.ts +25 -0
  26. package/dist/tools/check-port-availability.js +65 -0
  27. package/dist/tools/check-port-availability.js.map +1 -0
  28. package/dist/tools/get-design-philosophy.d.ts +24 -0
  29. package/dist/tools/get-design-philosophy.js +52 -0
  30. package/dist/tools/get-design-philosophy.js.map +1 -0
  31. package/dist/tools/get-port-occupancy.d.ts +25 -0
  32. package/dist/tools/get-port-occupancy.js +69 -0
  33. package/dist/tools/get-port-occupancy.js.map +1 -0
  34. package/dist/tools/index.d.ts +94 -0
  35. package/dist/tools/index.js +11 -0
  36. package/dist/tools/index.js.map +1 -0
  37. package/dist/tools/map-project-name-to-port.d.ts +27 -0
  38. package/dist/tools/map-project-name-to-port.js +51 -0
  39. package/dist/tools/map-project-name-to-port.js.map +1 -0
  40. package/dist/utils/logger.d.ts +7 -0
  41. package/dist/utils/logger.js +55 -0
  42. package/dist/utils/logger.js.map +1 -0
  43. package/dist/utils/session-manager.d.ts +35 -0
  44. package/dist/utils/session-manager.js +66 -0
  45. package/dist/utils/session-manager.js.map +1 -0
  46. package/locales/ar.json +1 -1
  47. package/locales/cn.json +1 -1
  48. package/locales/de.json +1 -1
  49. package/locales/es.json +1 -1
  50. package/locales/fr.json +1 -1
  51. package/locales/it.json +1 -1
  52. package/locales/ja.json +1 -1
  53. package/locales/ko.json +1 -1
  54. package/locales/pt.json +1 -1
  55. package/locales/ru.json +1 -1
  56. package/package.json +11 -5
package/README.md CHANGED
@@ -1,84 +1,139 @@
1
- # PortKey
1
+ # PortKey MCP Server
2
2
 
3
- <p align="center">
4
- <img width="200" src="/public/logo.png" />
5
- </p>
3
+ ## Description
6
4
 
7
- <p align="center">
8
- <strong>PortKey:A Simple, Practical Port Naming Strategy</strong>
9
- </p>
5
+ [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for PortKey. It provides tools to map project names to port numbers using keyboard-based letter-to-number mapping, check port availability, and access PortKey configuration.
10
6
 
11
- <p align="center">
12
- <!-- LANGUAGES=("cn" "es" "fr" "de" "ja" "ko" "ru" "ar" "pt" "it") -->
13
- <a href="./docs/README.cn.md">中文</a> | <a href="./docs/README.es.md">Español</a> | <a href="./docs/README.fr.md">Français</a> | <a href="./docs/README.de.md">Deutsch</a> | <a href="./docs/README.ja.md">日本語</a> | <a href="./docs/README.ko.md">한국어</a> | <a href="./docs/README.ru.md">Русский</a> | <a href="./docs/README.ar.md">العربية</a> | <a href="./docs/README.pt.md">Português</a> | <a href="./docs/README.it.md">Italiano</a>
14
- </p>
7
+ ## Usage
15
8
 
16
- ## Brief
9
+ ### Running the Server
17
10
 
18
- Generate ports with a letter-to-number keyboard mapping
11
+ The PortKey MCP server supports both Stdio and Streamable HTTP transport modes.
19
12
 
20
- When you’re running a bunch of projects locally, picking port numbers becomes annoying.
13
+ #### Stdio Mode (Default)
21
14
 
22
- - Over the last couple of years, there have been *so many* new projects. To really try them out, you often need to boot them locally—and then ports start colliding.
23
- - If you want to keep browser tabs (or bookmarks) stable, a project’s port shouldn’t keep changing.
15
+ Designed to be run by an IDE-based MCP client like [Visual Studio Code's Copilot](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode).
24
16
 
25
- For example, I have more than ten Nuxt apps on my machine. If they all default to `3000`, that’s obviously not going to work. So I came up with a simple, consistent port naming rule to “assign” ports per project.
17
+ ```shell
18
+ npx @lionad/port-key-mcp
19
+ ```
20
+
21
+ #### HTTP Mode (Streamable)
22
+
23
+ Runs an HTTP server implementing the Streamable HTTP transport. This mode is suitable for remote connections or when stdio is not available.
26
24
 
27
- [Source Blog Post](https://lionad.art/articles/simple-naming-method)
25
+ ```shell
26
+ # Run on default port 10945
27
+ npx @lionad/port-key-mcp --streamable
28
28
 
29
- ### Core idea
29
+ # Run on specific port
30
+ npx @lionad/port-key-mcp --streamable --port 8080
30
31
 
31
- Instead of picking random numbers, map the **project name to numbers based on the keyboard**, so the port is *readable* and *memorable*.
32
+ # Reuse the already-running server on the same port (default: true)
33
+ npx @lionad/port-key-mcp --streamable --port 8080 --reuse
32
34
 
33
- As long as the result is within the valid port range (**1024–65535**) and doesn’t hit reserved/system ports, you can just use it.
35
+ # Force starting a new process (will fail if the port is already in use by another service)
36
+ npx @lionad/port-key-mcp --streamable --port 8080 --reuse false
37
+ ```
34
38
 
35
- More specifically: using a standard QWERTY keyboard, map each letter to a single digit based on its **row/column position**.
39
+ Environment variables are also supported:
40
+ ```shell
41
+ PORT=8080 LOG_LEVEL=debug npx @lionad/port-key-mcp --streamable
42
+ ```
36
43
 
37
- Example:
44
+ ##### Single-instance reuse (Streamable)
38
45
 
39
- `"cfetch"` `c(3) f(4) e(3) t(5) c(3) h(6)` `34353`(port number)
46
+ When you run `npx @lionad/port-key-mcp --streamable` from multiple terminals using the same port, the CLI will detect an existing PortKey MCP server (via `GET /health`) and exit successfully without starting a new server process. This keeps “one port = one MCP server instance” while allowing repeated invocations.
40
47
 
41
- Then you can take the first 4 digits (e.g. `3453`), or keep more digits (e.g. `34353`). Either is fine.
48
+ ##### Endpoints (Streamable)
42
49
 
43
- If a project needs multiple ports (frontend, backend, database, etc.), pick **one** of these two approaches:
50
+ - `GET /health` returns a JSON health payload.
51
+ - `POST /mcp`, `GET /mcp`, `DELETE /mcp` implement MCP Streamable HTTP transport (stateful sessions).
44
52
 
45
- 1. Use the project prefix, then append a “role suffix”
46
- - For `"cfetch"`, take `3435` as the base
47
- - Frontend (`fe`, i.e. `43`) → `34354`
48
- - Backend (`server`) → `34352`
49
- - Database (`mongo`) → `34357`
50
- - …and so on
53
+ ### Available Tools
51
54
 
52
- 2. Use the project prefix, then assign sequential roles
53
- - For `"cfetch"`, take `3435` as the base
54
- - Web → `34351`
55
- - Backend → `34352`
56
- - Database → `34353`
57
- - …and so on
55
+ The MCP server provides the following tools:
58
56
 
59
- ### Valid port range
57
+ - **map-project-name-to-port**: Map a project name to a port number using keyboard-based letter-to-number mapping
58
+ - **get-design-philosophy**: Get design philosophy and background of PortKey
59
+ - **check-port-availability**: Check if a specific port is available or occupied
60
+ - **get-port-occupancy**: Get information about processes occupying specific ports
60
61
 
61
- - Ports must be within **1024–65535** (System ports 0-1023 are blocked).
62
- - **System Ports (0-1023)**: Assigned by IETF. Strictly blocked.
63
- - **User Ports (1024-49151)**: Assigned by IANA. Use with caution as they might conflict with registered services.
64
- - **Dynamic/Private Ports (49152-65535)**: Not assigned. Safest for private or dynamic use.
62
+ #### map-project-name-to-port
65
63
 
66
- ---
64
+ ##### Tool Parameters
67
65
 
68
- ## How to use
66
+ - `projectName` (string, required): The project name to map to a port number
67
+ - `map` (string, optional): Custom mapping in JSON format (e.g., `{ "1": "qaz", "2": "wsx", ... }`)
68
+ - `preferDigitCount` (number, optional): Preferred digit count for port (2-5, default: 4)
69
+ - `minPort` (number, optional): Minimum port number (0-65535, default: 0)
70
+ - `maxPort` (number, optional): Maximum port number (0-65535, default: 65535)
71
+ - `blockedPorts` (array of numbers, optional): List of blocked port numbers to avoid
69
72
 
70
- Simple command:
73
+ ##### Example Tool Call
71
74
 
72
- ```sh
73
- npx -y @lionad/port-key <your-project-name>
75
+ ```json
76
+ {
77
+ "name": "map-project-name-to-port",
78
+ "arguments": {
79
+ "projectName": "cfetch",
80
+ "preferDigitCount": 4
81
+ }
82
+ }
74
83
  ```
75
84
 
76
- Or you want a stdio MCP server:
85
+ ##### Example Response
77
86
 
78
- ```sh
79
- npx -y @lionad/port-key-mcp
87
+ ```json
88
+ {
89
+ "digits": "343536",
90
+ "port": 3435,
91
+ "rejectedCandidates": []
92
+ }
80
93
  ```
81
94
 
95
+ #### get-design-philosophy
96
+
97
+ ##### Tool Parameters
98
+
99
+ - `lang` (string, optional): Language code for design philosophy content. Supported languages: `"cn"`, `"es"`, `"fr"`, `"de"`, `"ja"`, `"ko"`, `"ru"`, `"ar"`, `"pt"`, `"it"`. Default: `"cn"`.
100
+
101
+ #### check-port-availability
102
+
103
+ ##### Tool Parameters
104
+
105
+ - `port` (number, required): The port number to check (0-65535)
106
+
107
+ #### get-port-occupancy
108
+
109
+ ##### Tool Parameters
110
+
111
+ - `ports` (array of numbers, optional): List of port numbers to filter results
112
+
113
+ ### Available Resources
114
+
115
+ - **config://port-mapping**: Get default port mapping configuration and blocked ports
116
+ - **projects://{projectName}/port-history**: Get history of port assignments for a project
117
+
118
+ ### Supported Languages (for design philosophy)
119
+
120
+ | Code | Language |
121
+ |-------|------------|
122
+ | cn | 中文 |
123
+ | es | Español |
124
+ | fr | Français |
125
+ | de | Deutsch |
126
+ | ja | 日本語 |
127
+ | ko | 한국어 |
128
+ | ru | Русский |
129
+ | ar | العربية |
130
+ | pt | Português |
131
+ | it | Italiano |
132
+
133
+ ## Configuration
134
+
135
+ You can configure MCP server in your MCP client's configuration file (e.g., for VS Code Copilot, configure in your `.copilot/settings.json`):
136
+
82
137
  ```json
83
138
  {
84
139
  "mcpServers": {
@@ -90,52 +145,49 @@ npx -y @lionad/port-key-mcp
90
145
  }
91
146
  ```
92
147
 
148
+ For HTTP mode configuration in clients that support it:
93
149
 
94
- ### CLI options
150
+ ```json
151
+ {
152
+ "mcpServers": {
153
+ "port-key": {
154
+ "command": "npx",
155
+ "args": ["@lionad/port-key-mcp", "--streamable"]
156
+ }
157
+ }
158
+ }
159
+ ```
95
160
 
96
- - `-m, --map <object>`: custom mapping (JSON or JS-like object literal)
97
- - `--lang <code>`: output language (currently only `en` and `cn`, default: `cn`)
98
- - `-d, --digits <count>`: preferred digit count for port (4 or 5, default: 4)
99
- - `-h, --help`: show help
161
+ ## Logs
100
162
 
101
- Examples:
163
+ In Streamable HTTP mode, logs are written under `~/.port-key/logs`.
102
164
 
103
- ```bash
104
- npx @lionad/port-key cfetch # -> 3435
105
- npx @lionad/port-key cfetch --digits 4 # -> 3435 (4-digit port)
106
- npx @lionad/port-key cfetch --digits 5 # -> 34353 (5-digit port)
107
- ```
165
+ ## Development
108
166
 
109
- Notes:
110
- - Default log language is `cn`. Use `--lang en` to show English messages.
111
- - Use `-h` or `--help` to show help.
167
+ Build locales (generates from docs/ directory):
112
168
 
113
- ### Config
169
+ ```shell
170
+ pnpm run build:locales
171
+ ```
114
172
 
115
- PortKey reads optional user config from:
173
+ Run tests:
116
174
 
117
- - `~/.port-key/config.json`
175
+ ```shell
176
+ pnpm test
177
+ ```
118
178
 
119
- A full Example:
179
+ Run tests in watch mode:
120
180
 
121
- ```json
122
- {
123
- // Preferred digit count for port (4 or 5)
124
- "preferDigitCount": 5,
125
- // Custom letter-to-digit mapping
126
- "blockedPorts": [3000, 3001, 3002, 6666],
127
- // Port range limits (inclusive)
128
- "minPort": 1024,
129
- "maxPort": 49151
130
- }
181
+ ```shell
182
+ pnpm run test:watch
131
183
  ```
132
184
 
133
- ---
185
+ E2E tests live under `tests/e2e/` and are included in `pnpm test`.
134
186
 
135
- ## For Developer
187
+ ## Thanks
136
188
 
137
- ### Project Structure
189
+ - **[Template-Nodejs-MCP-Server](https://github.com/HarveyYifanLi/Template-Nodejs-MCP-Server)** by HarveyYifanLi
190
+ - Provided excellent examples of stateful session management and modular tool/resource architecture.
138
191
 
139
- - 本仓库采用 pnpm monorepo;核心包位于 `packages/core`。
140
- - 安装:在根目录执行 `pnpm install`。
141
- - 运行测试:`pnpm -C packages/core test` 或 `pnpm -C packages/core test:watch`。
192
+ - **[mcp-restaurant-booking](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/servers/restaurant)** by Model Context Protocol
193
+ - Provided reference for production-grade TypeScript project configuration and logging systems.
@@ -0,0 +1,26 @@
1
+ interface LoggingConfig {
2
+ level: string;
3
+ }
4
+ interface ServerConfig {
5
+ port: number;
6
+ streamable: boolean;
7
+ }
8
+ interface SessionConfig {
9
+ ttl: number;
10
+ timeout: number;
11
+ }
12
+ interface HealthConfig {
13
+ enableDiskCheck: boolean;
14
+ enableMemoryCheck: boolean;
15
+ enableSessionCheck: boolean;
16
+ diskThreshold: number;
17
+ memoryThreshold: number;
18
+ }
19
+ interface Config {
20
+ logging: LoggingConfig;
21
+ server: ServerConfig;
22
+ session: SessionConfig;
23
+ health: HealthConfig;
24
+ }
25
+ declare const config: Config;
26
+ export default config;
@@ -0,0 +1,24 @@
1
+ import dotenv from 'dotenv';
2
+ dotenv.config();
3
+ const config = {
4
+ logging: {
5
+ level: process.env.LOG_LEVEL || 'info',
6
+ },
7
+ server: {
8
+ port: process.env.PORT ? parseInt(process.env.PORT, 10) : 10945,
9
+ streamable: process.env.STREAMABLE === 'true' || process.env.STREAMABLE === '1',
10
+ },
11
+ session: {
12
+ ttl: parseInt(process.env.SESSION_TTL || '3600', 10),
13
+ timeout: parseInt(process.env.SESSION_TIMEOUT || '300000', 10),
14
+ },
15
+ health: {
16
+ enableDiskCheck: process.env.ENABLE_DISK_CHECK !== 'false',
17
+ enableMemoryCheck: process.env.ENABLE_MEMORY_CHECK !== 'false',
18
+ enableSessionCheck: process.env.ENABLE_SESSION_CHECK !== 'false',
19
+ diskThreshold: parseInt(process.env.DISK_THRESHOLD || '1024', 10),
20
+ memoryThreshold: parseInt(process.env.MEMORY_THRESHOLD || '512', 10),
21
+ },
22
+ };
23
+ export default config;
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,CAAC,MAAM,EAAE,CAAC;AA+BhB,MAAM,MAAM,GAAW;IACrB,OAAO,EAAE;QACP,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;KACvC;IACD,MAAM,EAAE;QACN,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK;QAC/D,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,GAAG;KAChF;IACD,OAAO,EAAE;QACP,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,MAAM,EAAE,EAAE,CAAC;QACpD,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,QAAQ,EAAE,EAAE,CAAC;KAC/D;IACD,MAAM,EAAE;QACN,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,OAAO;QAC1D,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,OAAO;QAC9D,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,OAAO;QAChE,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,MAAM,EAAE,EAAE,CAAC;QACjE,eAAe,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,KAAK,EAAE,EAAE,CAAC;KACrE;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,7 @@
1
+ import winston from 'winston';
2
+ interface CustomLogger extends winston.Logger {
3
+ getLevel(): string;
4
+ setLevel(level: string): void;
5
+ }
6
+ declare const logger: CustomLogger;
7
+ export default logger;
package/dist/logger.js ADDED
@@ -0,0 +1,48 @@
1
+ import winston from 'winston';
2
+ import DailyRotateFile from 'winston-daily-rotate-file';
3
+ import config from './config/index.js';
4
+ const logger = winston.createLogger({
5
+ level: config.logging.level,
6
+ format: winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json()),
7
+ transports: [
8
+ new DailyRotateFile({
9
+ filename: 'logs/error-%DATE%.log',
10
+ datePattern: 'YYYY-MM-DD',
11
+ zippedArchive: true,
12
+ maxSize: '10m',
13
+ maxFiles: '14d',
14
+ level: 'error',
15
+ }),
16
+ new DailyRotateFile({
17
+ filename: 'logs/combined-%DATE%.log',
18
+ datePattern: 'YYYY-MM-DD',
19
+ zippedArchive: true,
20
+ maxSize: '1',
21
+ maxFiles: '14d',
22
+ }),
23
+ ],
24
+ });
25
+ if (process.env.NODE_ENV !== 'production') {
26
+ logger.add(new winston.transports.Console({
27
+ format: winston.format.combine(winston.format.colorize(), winston.format.printf(({ timestamp, level, message, ...rest }) => {
28
+ let args = '';
29
+ if (typeof message === 'object') {
30
+ args = JSON.stringify(message, null, 2);
31
+ message = '';
32
+ }
33
+ const extraArgs = Object.keys(rest).length
34
+ ? JSON.stringify(rest, null, 2)
35
+ : '';
36
+ return `${timestamp} ${level}: ${message} ${args} ${extraArgs}`.trim();
37
+ })),
38
+ }));
39
+ }
40
+ logger.getLevel = () => logger.level;
41
+ logger.setLevel = (level) => {
42
+ logger.level = level;
43
+ logger.transports.forEach((transport) => {
44
+ transport.level = level;
45
+ });
46
+ };
47
+ export default logger;
48
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,eAAe,MAAM,2BAA2B,CAAC;AACxD,OAAO,MAAM,MAAM,mBAAmB,CAAC;AAOvC,MAAM,MAAM,GAAiB,OAAO,CAAC,YAAY,CAAC;IAChD,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK;IAC3B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAC5B,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,EAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EACtC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CACtB;IACD,UAAU,EAAE;QACV,IAAI,eAAe,CAAC;YAClB,QAAQ,EAAE,uBAAuB;YACjC,WAAW,EAAE,YAAY;YACzB,aAAa,EAAE,IAAI;YACnB,OAAO,EAAE,KAAK;YACd,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,OAAO;SACf,CAAC;QACF,IAAI,eAAe,CAAC;YAClB,QAAQ,EAAE,0BAA0B;YACpC,WAAW,EAAE,YAAY;YACzB,aAAa,EAAE,IAAI;YACnB,OAAO,EAAE,GAAG;YACZ,QAAQ,EAAE,KAAK;SAChB,CAAC;KACH;CACF,CAAiB,CAAC;AAEnB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;IAC1C,MAAM,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;QACxC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,EACzB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE;YAC/D,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAChC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACxC,OAAO,GAAG,EAAE,CAAC;YACf,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM;gBACxC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/B,CAAC,CAAC,EAAE,CAAC;YACP,OAAO,GAAG,SAAS,IAAI,KAAK,KAAK,OAAO,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC;QACzE,CAAC,CAAC,CACH;KACF,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,CAAC,QAAQ,GAAG,GAAW,EAAE,CAAC,MAAM,CAAC,KAAe,CAAC;AACvD,MAAM,CAAC,QAAQ,GAAG,CAAC,KAAa,EAAQ,EAAE;IACxC,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;QACtC,SAAS,CAAC,KAAK,GAAG,KAAK,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/mcp-cli.js CHANGED
@@ -1,13 +1,85 @@
1
- import { mcpServer } from "./mcp-server.js";
2
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- function disconnect() {
4
- mcpServer.close();
5
- process.exitCode = 0;
6
- }
7
- await mcpServer.connect(new StdioServerTransport());
8
- // ! never use console.log in stdio MCP servers, but stderr is safe
9
- // @see https://modelcontextprotocol.io/docs/develop/build-server#logging-in-mcp-servers-2
10
- console.error(`PortKey MCP server is running. cwd: ${process.cwd()}`);
11
- process.on("SIGINT", disconnect);
12
- process.on("SIGTERM", disconnect);
1
+ import { mcpServerApp } from "./mcp-server.js";
2
+ import config from "./config/index.js";
3
+ // Parse command line arguments
4
+ const args = process.argv.slice(2);
5
+ const options = {};
6
+ function printHelp() {
7
+ console.log(`
8
+ Usage: port-key-mcp [options]
9
+
10
+ Options:
11
+ -s, --streamable Run in streamable mode (HTTP)
12
+ -p, --port <number> Specify port number (default: 10945)
13
+ -l, --local [boolean] Enable/disable local tools (default: true)
14
+ --reuse [boolean] Reuse existing streamable server on same port (default: true)
15
+ -h, --help Show this help message
16
+
17
+ Examples:
18
+ port-key-mcp --streamable --port 8080
19
+ port-key-mcp --local false
20
+ `);
21
+ }
22
+ for (let i = 0; i < args.length; i++) {
23
+ const arg = args[i];
24
+ if (arg === "--help" || arg === "-h") {
25
+ printHelp();
26
+ process.exit(0);
27
+ }
28
+ else if (arg === "--streamable" || arg === "-s") {
29
+ options.streamable = true;
30
+ }
31
+ else if (arg === "--port" || arg === "-p") {
32
+ const port = parseInt(args[i + 1], 10);
33
+ if (!isNaN(port)) {
34
+ options.port = port;
35
+ i++; // Skip next argument
36
+ }
37
+ }
38
+ else if (arg === "--local" || arg === "-l") {
39
+ options.local = args[i + 1] !== 'false';
40
+ if (args[i + 1] === 'true' || args[i + 1] === 'false') {
41
+ i++; // Skip next argument if it's a boolean value
42
+ }
43
+ }
44
+ else if (arg === "--reuse") {
45
+ options.reuse = args[i + 1] !== "false";
46
+ if (args[i + 1] === "true" || args[i + 1] === "false") {
47
+ i++;
48
+ }
49
+ }
50
+ }
51
+ function resolvePort() {
52
+ if (typeof options.port === "number")
53
+ return options.port;
54
+ const envPort = process.env.PORT ? parseInt(process.env.PORT, 10) : NaN;
55
+ if (!Number.isNaN(envPort))
56
+ return envPort;
57
+ return config.server.port;
58
+ }
59
+ async function isPortKeyMcpServerRunning(port) {
60
+ try {
61
+ const res = await fetch(`http://127.0.0.1:${port}/health`, {
62
+ signal: AbortSignal.timeout(1000),
63
+ headers: { Accept: "application/json" },
64
+ });
65
+ if (!res.ok)
66
+ return false;
67
+ const body = await res.json().catch(() => null);
68
+ return Boolean(body && typeof body === "object" && body.status === "ok");
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ const shouldRunStreamable = options.streamable || config.server.streamable;
75
+ const reuseEnabled = options.reuse !== false;
76
+ if (shouldRunStreamable && reuseEnabled) {
77
+ const port = resolvePort();
78
+ const running = await isPortKeyMcpServerRunning(port);
79
+ if (running) {
80
+ console.log(`PortKey MCP Server already running at http://127.0.0.1:${port}`);
81
+ process.exit(0);
82
+ }
83
+ }
84
+ await mcpServerApp.run(options);
13
85
  //# sourceMappingURL=mcp-cli.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-cli.js","sourceRoot":"","sources":["../src/mcp-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,SAAS,UAAU;IAClB,SAAS,CAAC,KAAK,EAAE,CAAC;IAClB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AAEpD,mEAAmE;AACnE,0FAA0F;AAC1F,OAAO,CAAC,KAAK,CAAC,uCAAuC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AAEtE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AACjC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC"}
1
+ {"version":3,"file":"mcp-cli.js","sourceRoot":"","sources":["../src/mcp-cli.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,17 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- declare const mcpServer: McpServer;
3
- export { mcpServer };
2
+ export declare class MCPServerApp {
3
+ private server;
4
+ constructor();
5
+ private createServer;
6
+ private registerTools;
7
+ private registerResources;
8
+ getMcpServer(): McpServer;
9
+ runStdio(): Promise<void>;
10
+ runHttp(port?: number): Promise<void>;
11
+ run(options?: {
12
+ streamable?: boolean;
13
+ port?: number;
14
+ local?: boolean;
15
+ }): Promise<void>;
16
+ }
17
+ export declare const mcpServerApp: MCPServerApp;