androjack-mcp 1.6.3 โ 1.6.4
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 +67 -46
- package/build/http-server.js +75 -60
- package/build/install.js +53 -9
- package/build/serve.js +23 -380
- package/build/server-factory.js +598 -0
- package/build/stdio.js +10 -585
- package/build/version.js +1 -1
- package/config/antigravity_mcp.json +1 -1
- package/config/claude_desktop_config.json +1 -1
- package/config/cursor_mcp.json +1 -1
- package/config/jetbrains_mcp.json +2 -2
- package/config/kiro_mcp.json +4 -4
- package/config/vscode_mcp.json +1 -1
- package/config/windsurf_mcp.json +1 -1
- package/manifest.json +2 -2
- package/package.json +22 -22
- package/server.json +2 -2
- package/src/cli-entry.ts +6 -6
- package/src/constants.ts +6 -6
- package/src/http-server.ts +189 -150
- package/src/http.ts +79 -79
- package/src/index.ts +77 -77
- package/src/install.ts +112 -66
- package/src/serve.ts +96 -488
- package/src/server-factory.ts +750 -0
- package/src/stdio.ts +29 -757
- package/src/version.ts +3 -3
package/README.md
CHANGED
|
@@ -19,21 +19,21 @@
|
|
|
19
19
|
|
|
20
20
|
[](https://www.npmjs.com/package/androjack-mcp)
|
|
21
21
|
[](https://marketplace.visualstudio.com/items?itemName=VIKAS9793.androjack-vscode)
|
|
22
|
-
[](https://github.com/VIKAS9793/AndroJack-mcp/stargazers)
|
|
23
|
-
[](https://nodejs.org)
|
|
24
|
-
[](https://modelcontextprotocol.io)
|
|
25
|
-
[](https://typescriptlang.org)
|
|
26
|
-
[](#-the-21-tools)
|
|
27
|
-
[](LICENSE)
|
|
28
|
-
[](SECURITY.md)
|
|
29
|
-
[](https://developer.android.com)
|
|
22
|
+
[](https://github.com/VIKAS9793/AndroJack-mcp/stargazers)
|
|
23
|
+
[](https://nodejs.org)
|
|
24
|
+
[](https://modelcontextprotocol.io)
|
|
25
|
+
[](https://typescriptlang.org)
|
|
26
|
+
[](#-the-21-tools)
|
|
27
|
+
[](LICENSE)
|
|
28
|
+
[](SECURITY.md)
|
|
29
|
+
[](https://developer.android.com)
|
|
30
30
|
|
|
31
31
|
### ๐ One-Click Install
|
|
32
32
|
|
|
33
33
|
[](https://marketplace.visualstudio.com/items?itemName=VIKAS9793.androjack-vscode)
|
|
34
|
-
[](https://claude.ai/integrations/install-mcp?params=eyJuYW1lIjoiYW5kcm9qYWNrIiwiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJhbmRyb2phY2stbWNwQDEuNi4zIl19)
|
|
35
|
-
[](https://cursor.com/install-mcp?name=androjack&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImFuZHJvamFjay1tY3BAMS42LjMiXX0=)
|
|
36
|
-
[](https://kiro.dev/launch/mcp/add?name=androjack&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22androjack-mcp%401.6.
|
|
34
|
+
[](https://claude.ai/integrations/install-mcp?params=eyJuYW1lIjoiYW5kcm9qYWNrIiwiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJhbmRyb2phY2stbWNwQDEuNi4zIl19)
|
|
35
|
+
[](https://cursor.com/install-mcp?name=androjack&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImFuZHJvamFjay1tY3BAMS42LjMiXX0=)
|
|
36
|
+
[](https://kiro.dev/launch/mcp/add?name=androjack&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22androjack-mcp%401.6.4%22%5D%2C%22disabled%22%3Afalse%2C%22autoApprove%22%3A%5B%5D%7D)
|
|
37
37
|
[](https://www.npmjs.com/package/androjack-mcp)
|
|
38
38
|
|
|
39
39
|
**VS Code distribution:** AndroJack MCP is also live on the VS Code Marketplace as [AndroJack MCP for VS Code](https://marketplace.visualstudio.com/items?itemName=VIKAS9793.androjack-vscode). The `VS Code` badge above always reflects the currently published Marketplace version.
|
|
@@ -258,28 +258,49 @@ That's the job AndroJack exists to do โ and nothing else in the current ecosys
|
|
|
258
258
|
|
|
259
259
|
## ๐ Quick Start โ Zero Install Required
|
|
260
260
|
|
|
261
|
-
### Option 1 โ Interactive CLI (v1.6.
|
|
262
|
-
```bash
|
|
263
|
-
npx androjack-mcp@1.6.
|
|
264
|
-
```
|
|
261
|
+
### Option 1 โ Interactive CLI (v1.6.4) โจ Recommended
|
|
262
|
+
```bash
|
|
263
|
+
npx -y androjack-mcp@1.6.4 install
|
|
264
|
+
```
|
|
265
265
|
Launches a full animated terminal wizard with auto-detection for **VS Code, Cursor, Claude, Windsurf, JetBrains, Kiro, and Antigravity.**
|
|
266
266
|
|
|
267
267
|
### Option 2 โ Targeted installs
|
|
268
|
-
```bash
|
|
269
|
-
#
|
|
270
|
-
npx androjack-mcp@1.6.
|
|
271
|
-
|
|
272
|
-
#
|
|
273
|
-
npx androjack-mcp@1.6.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
npx androjack-mcp@1.6.
|
|
277
|
-
|
|
268
|
+
```bash
|
|
269
|
+
# Preview detected IDEs and config paths
|
|
270
|
+
npx -y androjack-mcp@1.6.4 install --list
|
|
271
|
+
|
|
272
|
+
# Auto-detect all
|
|
273
|
+
npx -y androjack-mcp@1.6.4 install --auto
|
|
274
|
+
|
|
275
|
+
# Install to specific IDE
|
|
276
|
+
npx -y androjack-mcp@1.6.4 install --ide=cursor
|
|
277
|
+
npx -y androjack-mcp@1.6.4 install --ide=claude
|
|
278
|
+
npx -y androjack-mcp@1.6.4 install --ide=vscode
|
|
279
|
+
npx -y androjack-mcp@1.6.4 install --ide=windsurf
|
|
280
|
+
npx -y androjack-mcp@1.6.4 install --ide=jetbrains
|
|
281
|
+
npx -y androjack-mcp@1.6.4 install --ide=kiro
|
|
282
|
+
npx -y androjack-mcp@1.6.4 install --ide=antigravity
|
|
283
|
+
```
|
|
278
284
|
|
|
279
285
|
### Option 3 โ Test without IDE
|
|
280
|
-
```bash
|
|
281
|
-
npx @modelcontextprotocol/inspector npx -y androjack-mcp@1.6.
|
|
282
|
-
```
|
|
286
|
+
```bash
|
|
287
|
+
npx -y @modelcontextprotocol/inspector npx -y androjack-mcp@1.6.4
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## ๐งฉ Manual Config / Copy / Paste
|
|
291
|
+
|
|
292
|
+
If you prefer to wire the server manually, or want to inspect the exact JSON before writing anything:
|
|
293
|
+
|
|
294
|
+
- Run `npx -y androjack-mcp@1.6.4 install --list` to preview detected IDEs and target config paths.
|
|
295
|
+
- Ready-to-paste examples live in [`config/`](config/).
|
|
296
|
+
- Use [`config/claude_desktop_config.json`](config/claude_desktop_config.json) for Claude Desktop.
|
|
297
|
+
- Use [`config/cursor_mcp.json`](config/cursor_mcp.json) for Cursor.
|
|
298
|
+
- Use [`config/vscode_mcp.json`](config/vscode_mcp.json) for VS Code / GitHub Copilot.
|
|
299
|
+
- Use [`config/windsurf_mcp.json`](config/windsurf_mcp.json) for Windsurf.
|
|
300
|
+
- Use [`config/jetbrains_mcp.json`](config/jetbrains_mcp.json) for Android Studio / IntelliJ.
|
|
301
|
+
- Use [`config/kiro_mcp.json`](config/kiro_mcp.json) for AWS Kiro.
|
|
302
|
+
- Use [`config/antigravity_mcp.json`](config/antigravity_mcp.json) for Google Antigravity.
|
|
303
|
+
- Every example keeps AndroJack local by default and runs the published package via `npx -y androjack-mcp@1.6.4`.
|
|
283
304
|
|
|
284
305
|
---
|
|
285
306
|
|
|
@@ -291,26 +312,26 @@ npx @modelcontextprotocol/inspector npx -y androjack-mcp@1.6.3
|
|
|
291
312
|
| **Mechanism** | Context Retrieval | Context Enforcement |
|
|
292
313
|
| **Scope** | Generalist โ Firebase, Cloud, Maps | Android engineering specialist |
|
|
293
314
|
| **Tools** | 3 retrieval tools | 21 specialized tools |
|
|
294
|
-
| **Setup** | Google Cloud project + API key required | `npx androjack-mcp@1.6.
|
|
315
|
+
| **Setup** | Google Cloud project + API key required | `npx androjack-mcp@1.6.4` โ zero auth |
|
|
295
316
|
| **Enforcement**| Passive โ AI decides when to retrieve | Active โ mandating calls by task type |
|
|
296
317
|
|
|
297
|
-
## ๐ Security & Privacy
|
|
298
|
-
* **Domain allowlist**: Requests only to Google/Android/Kotlin official domains.
|
|
299
|
-
* **HTTPS only**: Outbound documentation fetches refuse non-HTTPS URLs, cap body size, and redact query strings in retry logs.
|
|
300
|
-
* **Local by default**: `serve` binds to loopback only unless you explicitly pass `--allow-remote`.
|
|
301
|
-
* **HTTP hardening**: Streamable HTTP validates `Origin` and `Host` headers and caps request bodies and active sessions.
|
|
302
|
-
* **Transparent agent**: User-Agent: `AndroJack-MCP/1.6.
|
|
303
|
-
* **Read-only**: All 21 tools are annotated `readOnlyHint: true`.
|
|
304
|
-
* **Zero credentials**: No API keys or tokens required for documentation fetching.
|
|
305
|
-
* **Security policy**: Disclosure process and supported versions live in [SECURITY.md](SECURITY.md).
|
|
306
|
-
|
|
307
|
-
## ๐ Changelog
|
|
308
|
-
### v1.6.
|
|
309
|
-
- **Fix:** `npx androjack-mcp install`, `install --auto`, `install --ide=...`, `help`, and `--version` now route correctly instead of falling through to the stdio server.
|
|
310
|
-
- **Fix:** `install --auto` no longer over-detects IDEs from parent directories and create configs in a clean workspace.
|
|
311
|
-
- **Fix:** HTTP serve mode now fails closed on non-loopback binds unless `--allow-remote` is explicit.
|
|
312
|
-
- **Security:** Streamable HTTP now validates `Origin` and `Host` headers and enforces request body and session limits.
|
|
313
|
-
- **Security:** Outbound fetches now enforce HTTPS, cap response size, and redact sensitive URL parts from retry logs.
|
|
318
|
+
## ๐ Security & Privacy
|
|
319
|
+
* **Domain allowlist**: Requests only to Google/Android/Kotlin official domains.
|
|
320
|
+
* **HTTPS only**: Outbound documentation fetches refuse non-HTTPS URLs, cap body size, and redact query strings in retry logs.
|
|
321
|
+
* **Local by default**: `serve` binds to loopback only unless you explicitly pass `--allow-remote`.
|
|
322
|
+
* **HTTP hardening**: Streamable HTTP validates `Origin` and `Host` headers and caps request bodies and active sessions.
|
|
323
|
+
* **Transparent agent**: User-Agent: `AndroJack-MCP/1.6.4`.
|
|
324
|
+
* **Read-only**: All 21 tools are annotated `readOnlyHint: true`.
|
|
325
|
+
* **Zero credentials**: No API keys or tokens required for documentation fetching.
|
|
326
|
+
* **Security policy**: Disclosure process and supported versions live in [SECURITY.md](SECURITY.md).
|
|
327
|
+
|
|
328
|
+
## ๐ Changelog
|
|
329
|
+
### v1.6.4 โ CLI Routing Fixes and Local Transport Hardening
|
|
330
|
+
- **Fix:** `npx androjack-mcp install`, `install --auto`, `install --ide=...`, `help`, and `--version` now route correctly instead of falling through to the stdio server.
|
|
331
|
+
- **Fix:** `install --auto` no longer over-detects IDEs from parent directories and create configs in a clean workspace.
|
|
332
|
+
- **Fix:** HTTP serve mode now fails closed on non-loopback binds unless `--allow-remote` is explicit.
|
|
333
|
+
- **Security:** Streamable HTTP now validates `Origin` and `Host` headers and enforces request body and session limits.
|
|
334
|
+
- **Security:** Outbound fetches now enforce HTTPS, cap response size, and redact sensitive URL parts from retry logs.
|
|
314
335
|
|
|
315
336
|
## ๐ฅ Authorship & Ownership
|
|
316
337
|
**Vikas Sahani** โ Product Lead (vikassahani17@gmail.com)
|
package/build/http-server.js
CHANGED
|
@@ -1,25 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AndroJack MCP โ Streamable HTTP Transport
|
|
3
3
|
*
|
|
4
|
-
* Runs the
|
|
4
|
+
* Runs the MCP server over HTTP instead of stdio.
|
|
5
5
|
* Spec: MCP 2025-11-25 Streamable HTTP transport.
|
|
6
6
|
*
|
|
7
|
+
* Security: each new initialize request creates a FRESH McpServer +
|
|
8
|
+
* StreamableHTTPServerTransport pair. Sessions are never shared โ
|
|
9
|
+
* this prevents cross-session state leakage that existed in v1.6.3.
|
|
10
|
+
*
|
|
7
11
|
* Usage:
|
|
8
12
|
* node build/index.js --http # default port 3000
|
|
9
13
|
* PORT=8080 node build/index.js --http # custom port
|
|
10
|
-
*
|
|
11
|
-
* Config for Claude Desktop / Cursor (remote team instance):
|
|
12
|
-
* {
|
|
13
|
-
* "mcpServers": {
|
|
14
|
-
* "androjack": {
|
|
15
|
-
* "type": "streamable-http",
|
|
16
|
-
* "url": "http://localhost:3000/mcp"
|
|
17
|
-
* }
|
|
18
|
-
* }
|
|
19
|
-
* }
|
|
20
|
-
*
|
|
21
|
-
* Security note: bind to 127.0.0.1 by default (loopback only).
|
|
22
|
-
* To expose on LAN, set HOST=0.0.0.0 and add your own auth layer.
|
|
23
14
|
*/
|
|
24
15
|
import http from "node:http";
|
|
25
16
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
@@ -32,8 +23,7 @@ const MCP_PATH = "/mcp";
|
|
|
32
23
|
const WELL_KNOWN_PATH = "/.well-known/mcp";
|
|
33
24
|
const MAX_REQUEST_BODY_BYTES = 1024 * 1024;
|
|
34
25
|
const MAX_ACTIVE_SESSIONS = 64;
|
|
35
|
-
//
|
|
36
|
-
const sessions = new Map();
|
|
26
|
+
// โโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
37
27
|
function createHttpError(statusCode, message) {
|
|
38
28
|
const error = new Error(message);
|
|
39
29
|
error.statusCode = statusCode;
|
|
@@ -103,16 +93,49 @@ function validateHostHeader(hostHeader, bindHost) {
|
|
|
103
93
|
throw createHttpError(403, `Host "${hostHeader}" is not allowed.`);
|
|
104
94
|
}
|
|
105
95
|
}
|
|
96
|
+
function readBody(req) {
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
const chunks = [];
|
|
99
|
+
let totalBytes = 0;
|
|
100
|
+
req.on("data", (chunk) => {
|
|
101
|
+
totalBytes += chunk.length;
|
|
102
|
+
if (totalBytes > MAX_REQUEST_BODY_BYTES) {
|
|
103
|
+
req.destroy(createHttpError(413, "Request body too large"));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
chunks.push(chunk);
|
|
107
|
+
});
|
|
108
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
109
|
+
req.on("error", reject);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// โโ Main export โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
106
113
|
/**
|
|
107
|
-
* Starts the Streamable HTTP server
|
|
108
|
-
*
|
|
114
|
+
* Starts the Streamable HTTP server.
|
|
115
|
+
*
|
|
116
|
+
* @param createServer - Factory called once per MCP initialize request.
|
|
117
|
+
* Each call MUST return a new McpServer instance โ never share instances
|
|
118
|
+
* across sessions.
|
|
119
|
+
* @param opts - Optional port/host overrides (fall back to env vars / defaults).
|
|
120
|
+
* @returns A handle with close() and the bound address.
|
|
121
|
+
*
|
|
122
|
+
* Security controls (all preserved from v1.6.3):
|
|
123
|
+
* - Loopback-only bind host by default
|
|
124
|
+
* - Explicit --allow-remote required for non-loopback (enforced in serve.ts)
|
|
125
|
+
* - Host header validation
|
|
126
|
+
* - Origin header validation
|
|
127
|
+
* - Body size cap (1 MiB)
|
|
128
|
+
* - Active session cap (64)
|
|
129
|
+
* - Per-session server isolation (NEW in v1.6.4)
|
|
109
130
|
*/
|
|
110
|
-
export async function startHttpServer(
|
|
111
|
-
const port = parseInt(process.env
|
|
112
|
-
const host = process.env
|
|
131
|
+
export async function startHttpServer(createServer, opts) {
|
|
132
|
+
const port = opts?.port ?? parseInt(process.env["PORT"] ?? String(DEFAULT_PORT), 10);
|
|
133
|
+
const host = opts?.host ?? process.env["HOST"] ?? DEFAULT_HOST;
|
|
113
134
|
let advertisedPort = port;
|
|
114
135
|
let advertisedHost = host;
|
|
115
136
|
let allowedOrigins = buildAllowedOrigins(host, port);
|
|
137
|
+
// Per-session state: each session owns its own server + transport pair
|
|
138
|
+
const sessions = new Map();
|
|
116
139
|
const httpServer = http.createServer(async (req, res) => {
|
|
117
140
|
try {
|
|
118
141
|
validateHostHeader(req.headers.host, host);
|
|
@@ -125,7 +148,7 @@ export async function startHttpServer(server) {
|
|
|
125
148
|
return;
|
|
126
149
|
}
|
|
127
150
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
128
|
-
// โโ .well-known/mcp โ capability discovery
|
|
151
|
+
// โโ .well-known/mcp โ capability discovery โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
129
152
|
if (url.pathname === WELL_KNOWN_PATH && req.method === "GET") {
|
|
130
153
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
131
154
|
res.end(JSON.stringify({
|
|
@@ -141,24 +164,25 @@ export async function startHttpServer(server) {
|
|
|
141
164
|
}));
|
|
142
165
|
return;
|
|
143
166
|
}
|
|
144
|
-
// โโ /mcp โ Streamable HTTP transport endpoint
|
|
167
|
+
// โโ /mcp โ Streamable HTTP transport endpoint โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
145
168
|
if (url.pathname !== MCP_PATH) {
|
|
146
169
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
147
170
|
res.end(`AndroJack MCP: endpoint is ${MCP_PATH}`);
|
|
148
171
|
return;
|
|
149
172
|
}
|
|
150
|
-
// Only POST and GET are valid for Streamable HTTP
|
|
151
173
|
if (req.method !== "POST" && req.method !== "GET" && req.method !== "DELETE") {
|
|
152
174
|
res.writeHead(405, { Allow: "GET, POST, DELETE" });
|
|
153
175
|
res.end();
|
|
154
176
|
return;
|
|
155
177
|
}
|
|
156
|
-
// DELETE โ client signals session teardown
|
|
178
|
+
// โโ DELETE โ client signals session teardown โโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
157
179
|
if (req.method === "DELETE") {
|
|
158
180
|
const sessionId = req.headers["mcp-session-id"];
|
|
159
181
|
if (sessionId && sessions.has(sessionId)) {
|
|
160
|
-
|
|
182
|
+
const session = sessions.get(sessionId);
|
|
183
|
+
await session.transport.close();
|
|
161
184
|
sessions.delete(sessionId);
|
|
185
|
+
process.stderr.write(`AndroJack HTTP: session deleted [${sessionId}]\n`);
|
|
162
186
|
}
|
|
163
187
|
res.writeHead(200);
|
|
164
188
|
res.end();
|
|
@@ -166,13 +190,14 @@ export async function startHttpServer(server) {
|
|
|
166
190
|
}
|
|
167
191
|
try {
|
|
168
192
|
const sessionId = req.headers["mcp-session-id"];
|
|
169
|
-
let transport;
|
|
170
193
|
if (sessionId && sessions.has(sessionId)) {
|
|
171
|
-
// Resume existing session
|
|
172
|
-
transport = sessions.get(sessionId);
|
|
194
|
+
// โโ Resume existing session โ reuse only this session's transport โโ
|
|
195
|
+
const { transport } = sessions.get(sessionId);
|
|
196
|
+
await transport.handleRequest(req, res);
|
|
197
|
+
return;
|
|
173
198
|
}
|
|
174
|
-
|
|
175
|
-
// New session โ only valid
|
|
199
|
+
if (!sessionId) {
|
|
200
|
+
// โโ New session โ only valid for MCP Initialize requests โโโโโโโโโโโ
|
|
176
201
|
if (sessions.size >= MAX_ACTIVE_SESSIONS) {
|
|
177
202
|
res.writeHead(503, { "Content-Type": "text/plain" });
|
|
178
203
|
res.end("Too many active MCP sessions");
|
|
@@ -193,11 +218,13 @@ export async function startHttpServer(server) {
|
|
|
193
218
|
res.end("First request must be an MCP Initialize request");
|
|
194
219
|
return;
|
|
195
220
|
}
|
|
221
|
+
// โโ Create a fresh server + transport pair for this session โโโโโโโโ
|
|
196
222
|
const newSessionId = randomUUID();
|
|
197
|
-
|
|
223
|
+
const mcpServer = createServer(); // <-- fresh instance per session
|
|
224
|
+
const transport = new StreamableHTTPServerTransport({
|
|
198
225
|
sessionIdGenerator: () => newSessionId,
|
|
199
226
|
onsessioninitialized: (id) => {
|
|
200
|
-
sessions.set(id, transport);
|
|
227
|
+
sessions.set(id, { server: mcpServer, transport });
|
|
201
228
|
process.stderr.write(`AndroJack HTTP: session opened [${id}]\n`);
|
|
202
229
|
},
|
|
203
230
|
});
|
|
@@ -205,18 +232,15 @@ export async function startHttpServer(server) {
|
|
|
205
232
|
sessions.delete(newSessionId);
|
|
206
233
|
process.stderr.write(`AndroJack HTTP: session closed [${newSessionId}]\n`);
|
|
207
234
|
};
|
|
208
|
-
// Connect
|
|
209
|
-
await
|
|
235
|
+
// Connect this session's fresh server to its own transport
|
|
236
|
+
await mcpServer.connect(transport);
|
|
210
237
|
// Handle the Initialize request on the new transport
|
|
211
238
|
await transport.handleRequest(req, res, parsed);
|
|
212
239
|
return;
|
|
213
240
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
await transport.handleRequest(req, res);
|
|
241
|
+
// Unknown session ID
|
|
242
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
243
|
+
res.end("Unknown session ID");
|
|
220
244
|
}
|
|
221
245
|
catch (err) {
|
|
222
246
|
process.stderr.write(`AndroJack HTTP error: ${err}\n`);
|
|
@@ -245,26 +269,17 @@ export async function startHttpServer(server) {
|
|
|
245
269
|
for (const sig of ["SIGINT", "SIGTERM"]) {
|
|
246
270
|
process.once(sig, async () => {
|
|
247
271
|
process.stderr.write(`\nAndroJack HTTP: shutting down (${sig})โฆ\n`);
|
|
248
|
-
for (const
|
|
249
|
-
await
|
|
272
|
+
for (const { transport } of sessions.values())
|
|
273
|
+
await transport.close().catch(() => { });
|
|
250
274
|
httpServer.close(() => process.exit(0));
|
|
251
275
|
});
|
|
252
276
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (totalBytes > MAX_REQUEST_BODY_BYTES) {
|
|
262
|
-
req.destroy(createHttpError(413, "Request body too large"));
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
chunks.push(chunk);
|
|
266
|
-
});
|
|
267
|
-
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
268
|
-
req.on("error", reject);
|
|
269
|
-
});
|
|
277
|
+
return {
|
|
278
|
+
close: () => {
|
|
279
|
+
for (const { transport } of sessions.values())
|
|
280
|
+
transport.close().catch(() => { });
|
|
281
|
+
httpServer.close();
|
|
282
|
+
},
|
|
283
|
+
address: { host: advertisedHost, port: advertisedPort },
|
|
284
|
+
};
|
|
270
285
|
}
|
package/build/install.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* Supports both automated (--auto) and guided interactive installation.
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
|
-
* npx androjack-mcp@1.6.
|
|
10
|
-
* npx androjack-mcp@1.6.
|
|
11
|
-
* npx androjack-mcp@1.6.
|
|
12
|
-
* npx androjack-mcp@1.6.
|
|
9
|
+
* npx androjack-mcp@1.6.4 install รขโ โ interactive guided mode
|
|
10
|
+
* npx androjack-mcp@1.6.4 install --auto รขโ โ auto-detect and install to all found IDEs
|
|
11
|
+
* npx androjack-mcp@1.6.4 install --ide cursor รขโ โ target a specific IDE
|
|
12
|
+
* npx androjack-mcp@1.6.4 install --list รขโ โ list all supported IDEs and their status
|
|
13
13
|
*/
|
|
14
14
|
import * as fs from "fs";
|
|
15
15
|
import * as path from "path";
|
|
@@ -105,6 +105,15 @@ function getConfigPaths(platform) {
|
|
|
105
105
|
path.join(process.cwd(), ".kiro", "settings", "mcp.json"), // project-level
|
|
106
106
|
path.join(HOME, ".kiro", "settings", "mcp.json"), // global
|
|
107
107
|
],
|
|
108
|
+
// Kiro creates ~/.kiro only after first launch + MCP plugin setup.
|
|
109
|
+
// Detect the Kiro app itself via its known install location.
|
|
110
|
+
ideInstalledPaths: [
|
|
111
|
+
...(platform === "win32"
|
|
112
|
+
? [path.join(process.env.LOCALAPPDATA ?? path.join(HOME, "AppData", "Local"), "Programs", "Kiro", "Kiro.exe")]
|
|
113
|
+
: platform === "darwin"
|
|
114
|
+
? ["/Applications/Kiro.app"]
|
|
115
|
+
: [path.join(HOME, ".local", "share", "kiro")]),
|
|
116
|
+
],
|
|
108
117
|
configKey: "mcpServers",
|
|
109
118
|
format: "standard",
|
|
110
119
|
oneClickUrl: (() => {
|
|
@@ -145,6 +154,21 @@ function getConfigPaths(platform) {
|
|
|
145
154
|
path.join(HOME, ".config", "JetBrains", "IdeaIC2024.3", "mcp.json"),
|
|
146
155
|
]),
|
|
147
156
|
],
|
|
157
|
+
// JetBrains config dir only exists after first launch with AI plugin installed.
|
|
158
|
+
// Detect IDE via its known application paths.
|
|
159
|
+
ideInstalledPaths: [
|
|
160
|
+
...(platform === "darwin"
|
|
161
|
+
? ["/Applications/Android Studio.app", "/Applications/IntelliJ IDEA.app", "/Applications/IntelliJ IDEA CE.app"]
|
|
162
|
+
: platform === "win32"
|
|
163
|
+
? [
|
|
164
|
+
path.join(process.env.LOCALAPPDATA ?? path.join(HOME, "AppData", "Local"), "Programs", "Android Studio", "bin", "studio64.exe"),
|
|
165
|
+
path.join("C:\\", "Program Files", "Android", "Android Studio", "bin", "studio64.exe"),
|
|
166
|
+
]
|
|
167
|
+
: [
|
|
168
|
+
path.join(HOME, ".local", "share", "JetBrains"),
|
|
169
|
+
"/opt/android-studio",
|
|
170
|
+
]),
|
|
171
|
+
],
|
|
148
172
|
configKey: "mcpServers",
|
|
149
173
|
format: "standard",
|
|
150
174
|
notes: "Or add manually: Android Studio รขโ โ Settings รขโ โ Tools รขโ โ AI Assistant รขโ โ MCP Servers รขโ โ +",
|
|
@@ -210,6 +234,18 @@ function detectInstalledIdes(targets) {
|
|
|
210
234
|
return target.configPaths.some((configPath) => fs.existsSync(path.dirname(configPath)));
|
|
211
235
|
});
|
|
212
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Returns true when the IDE application is installed on this machine, but the
|
|
239
|
+
* MCP config directory hasn't been created yet (IDE was never launched or
|
|
240
|
+
* the AI/MCP plugin hasn't been set up). Used in v1.6.4 to surface a
|
|
241
|
+
* more helpful message than silently showing "not found".
|
|
242
|
+
*/
|
|
243
|
+
function isIdeApplicationInstalled(target) {
|
|
244
|
+
if (!target.ideInstalledPaths || detectInstalledIdes([target]).length > 0) {
|
|
245
|
+
return false; // already detected via config dir โ no extra message needed
|
|
246
|
+
}
|
|
247
|
+
return target.ideInstalledPaths.some((p) => fs.existsSync(p));
|
|
248
|
+
}
|
|
213
249
|
function alreadyInstalled(target) {
|
|
214
250
|
for (const p of target.configPaths) {
|
|
215
251
|
if (!fs.existsSync(p))
|
|
@@ -283,18 +319,23 @@ function printStatusTable(targets) {
|
|
|
283
319
|
for (const t of targets) {
|
|
284
320
|
const installed = alreadyInstalled(t);
|
|
285
321
|
const detected = detectInstalledIdes([t]).length > 0;
|
|
322
|
+
const ideInstalled = isIdeApplicationInstalled(t);
|
|
286
323
|
let icon;
|
|
287
324
|
let label;
|
|
288
325
|
if (installed) {
|
|
289
|
-
icon = chalk.green("
|
|
290
|
-
label = chalk.green("already installed") + chalk.dim(`
|
|
326
|
+
icon = chalk.green(" โ");
|
|
327
|
+
label = chalk.green("already installed") + chalk.dim(` โ ${installed}`);
|
|
291
328
|
}
|
|
292
329
|
else if (detected) {
|
|
293
|
-
icon = chalk.cyan("
|
|
330
|
+
icon = chalk.cyan(" โ");
|
|
294
331
|
label = chalk.cyan("detected, not configured");
|
|
295
332
|
}
|
|
333
|
+
else if (ideInstalled) {
|
|
334
|
+
icon = chalk.yellow(" โ");
|
|
335
|
+
label = chalk.yellow("detected (MCP not yet configured โ open the IDE once to initialize)");
|
|
336
|
+
}
|
|
296
337
|
else {
|
|
297
|
-
icon = chalk.dim("
|
|
338
|
+
icon = chalk.dim(" โ");
|
|
298
339
|
label = chalk.dim("not found");
|
|
299
340
|
}
|
|
300
341
|
console.log(`${icon} ${chalk.bold(t.name)} ${label}`);
|
|
@@ -521,6 +562,7 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
521
562
|
options: targets.map((t) => {
|
|
522
563
|
const installed = alreadyInstalled(t);
|
|
523
564
|
const detected = detectInstalledIdes([t]).length > 0;
|
|
565
|
+
const ideInstalled = isIdeApplicationInstalled(t);
|
|
524
566
|
return {
|
|
525
567
|
value: t.id,
|
|
526
568
|
label: t.name,
|
|
@@ -528,7 +570,9 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
528
570
|
? chalk.green("already installed")
|
|
529
571
|
: detected
|
|
530
572
|
? chalk.cyan("detected")
|
|
531
|
-
:
|
|
573
|
+
: ideInstalled
|
|
574
|
+
? chalk.yellow("IDE installed โ run once to initialize MCP")
|
|
575
|
+
: chalk.dim("not found"),
|
|
532
576
|
};
|
|
533
577
|
}),
|
|
534
578
|
required: true,
|