alexa-mcp 0.1.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/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "alexa-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server and CLI for Alexa devices and smart home control",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "alexa-mcp": "dist/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "postinstall": "patch-package",
13
+ "prepare": "npm run build",
14
+ "start": "node dist/index.js",
15
+ "cli": "node dist/cli.js",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest",
18
+ "test:integration": "TEST_INTEGRATION=1 vitest run",
19
+ "test:e2e": "TEST_INTEGRATION=1 vitest run test/e2e.test.ts",
20
+ "test:e2e:devices": "TEST_INTEGRATION=1 vitest run test/e2e-devices.test.ts"
21
+ },
22
+ "keywords": [
23
+ "alexa",
24
+ "mcp",
25
+ "smart-home",
26
+ "echo",
27
+ "model-context-protocol"
28
+ ],
29
+ "author": "m0nkmaster",
30
+ "license": "MIT",
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "dependencies": {
35
+ "@modelcontextprotocol/sdk": "^1.0.0",
36
+ "alexa-cookie2": "^5.0.3",
37
+ "commander": "^12.0.0",
38
+ "undici": "^7.0.0",
39
+ "zod": "^3.23.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^22.0.0",
43
+ "patch-package": "^8.0.1",
44
+ "tsx": "^4.19.0",
45
+ "typescript": "^5.6.0",
46
+ "vitest": "^2.1.0"
47
+ },
48
+ "files": [
49
+ "dist",
50
+ "patches",
51
+ "README.md"
52
+ ]
53
+ }
@@ -0,0 +1,89 @@
1
+ diff --git a/node_modules/alexa-cookie2/alexa-cookie.js b/node_modules/alexa-cookie2/alexa-cookie.js
2
+ index be22ba8..1b252f8 100644
3
+ --- a/node_modules/alexa-cookie2/alexa-cookie.js
4
+ +++ b/node_modules/alexa-cookie2/alexa-cookie.js
5
+ @@ -386,7 +386,8 @@ function AlexaCookie() {
6
+ if (!_options.proxyPort || _options.proxyPort === 0) {
7
+ _options.proxyPort = proxyServer.address().port;
8
+ }
9
+ - const errMessage = `Please open http://${_options.proxyOwnIp}:${_options.proxyPort}/ with your browser and login to Amazon. The cookie will be output here after successfull login.`;
10
+ + const openUrl = _options.proxyTunnelUrl || `http://${_options.proxyOwnIp}:${_options.proxyPort}/`;
11
+ + const errMessage = `Please open ${openUrl} with your browser and login to Amazon. The cookie will be output here after successfull login.`;
12
+ callback && callback(new Error(errMessage), null);
13
+ });
14
+ }
15
+ diff --git a/node_modules/alexa-cookie2/lib/proxy.js b/node_modules/alexa-cookie2/lib/proxy.js
16
+ index a70aa48..aec2fb4 100644
17
+ --- a/node_modules/alexa-cookie2/lib/proxy.js
18
+ +++ b/node_modules/alexa-cookie2/lib/proxy.js
19
+ @@ -172,19 +172,25 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
20
+ };
21
+ };
22
+ let returnedInitUrl;
23
+ + const proxyBaseUrl = _options.proxyTunnelUrl || `http://${_options.proxyOwnIp}:${_options.proxyPort}`;
24
+ + const hostMatch = (host) => {
25
+ + if (host === `${_options.proxyOwnIp}:${_options.proxyPort}`) return true;
26
+ + if (_options.proxyTunnelUrl && host === _options.proxyOwnIp) return true;
27
+ + return false;
28
+ + };
29
+
30
+ function router(req) {
31
+ const url = (req.originalUrl || req.url);
32
+ _options.logger && _options.logger(`Router: ${url} / ${req.method} / ${JSON.stringify(req.headers)}`);
33
+ - if (req.headers.host === `${_options.proxyOwnIp}:${_options.proxyPort}`) {
34
+ + if (hostMatch(req.headers.host)) {
35
+ if (url.startsWith(`/www.${_options.baseAmazonPage}/`)) {
36
+ return `https://www.${_options.baseAmazonPage}`;
37
+ } else if (url.startsWith(`/alexa.${_options.baseAmazonPage}/`)) {
38
+ return `https://alexa.${_options.baseAmazonPage}`;
39
+ } else if (req.headers.referer) {
40
+ - if (req.headers.referer.startsWith(`http://${_options.proxyOwnIp}:${_options.proxyPort}/www.${_options.baseAmazonPage}/`)) {
41
+ + if (req.headers.referer.startsWith(`${proxyBaseUrl}/www.${_options.baseAmazonPage}/`)) {
42
+ return `https://www.${_options.baseAmazonPage}`;
43
+ - } else if (req.headers.referer.startsWith(`http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.${_options.baseAmazonPage}/`)) {
44
+ + } else if (req.headers.referer.startsWith(`${proxyBaseUrl}/alexa.${_options.baseAmazonPage}/`)) {
45
+ return `https://alexa.${_options.baseAmazonPage}`;
46
+ }
47
+ }
48
+ @@ -217,18 +223,18 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
49
+ const amazonRegex = new RegExp(`https?://www.${_options.baseAmazonPage}:?[0-9]*/`.replace(/\./g, '\\.'), 'g');
50
+ const alexaRegex = new RegExp(`https?://alexa.${_options.baseAmazonPage}:?[0-9]*/`.replace(/\./g, '\\.'), 'g');
51
+ data = data.replace(///g, '/');
52
+ - data = data.replace(amazonRegex, `http://${_options.proxyOwnIp}:${_options.proxyPort}/www.${_options.baseAmazonPage}/`);
53
+ - data = data.replace(alexaRegex, `http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.${_options.baseAmazonPage}/`);
54
+ + data = data.replace(amazonRegex, `${proxyBaseUrl}/www.${_options.baseAmazonPage}/`);
55
+ + data = data.replace(alexaRegex, `${proxyBaseUrl}/alexa.${_options.baseAmazonPage}/`);
56
+ //_options.logger && _options.logger('REPLACEHOSTS: ' + dataOrig + ' --> ' + data);
57
+ return data;
58
+ }
59
+
60
+ function replaceHostsBack(data) {
61
+ - const amazonRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/www.${_options.baseAmazonPage}/`.replace(/\./g, '\\.'), 'g');
62
+ - const alexaRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.${_options.baseAmazonPage}/`.replace(/\./g, '\\.'), 'g');
63
+ + const amazonRegex = new RegExp(`${proxyBaseUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/www\\.${_options.baseAmazonPage.replace(/\./g, '\\.')}/`, 'g');
64
+ + const alexaRegex = new RegExp(`${proxyBaseUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/alexa\\.${_options.baseAmazonPage.replace(/\./g, '\\.')}/`, 'g');
65
+ data = data.replace(amazonRegex, `https://www.${_options.baseAmazonPage}/`);
66
+ data = data.replace(alexaRegex, `https://alexa.${_options.baseAmazonPage}/`);
67
+ - if (data === `http://${_options.proxyOwnIp}:${_options.proxyPort}/`) {
68
+ + if (data === `${proxyBaseUrl}/`) {
69
+ data = returnedInitUrl;
70
+ }
71
+ return data;
72
+ @@ -321,7 +327,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
73
+ const queryParams = querystring.parse(proxyRes.headers.location.substr(paramStart + 1));
74
+
75
+ proxyRes.statusCode = 302;
76
+ - proxyRes.headers.location = `http://${_options.proxyOwnIp}:${_options.proxyPort}/cookie-success`;
77
+ + proxyRes.headers.location = `${proxyBaseUrl}/cookie-success`;
78
+ delete proxyRes.headers.referer;
79
+
80
+ _options.logger && _options.logger(`Alexa-Cookie: Proxy catched cookie: ${proxyCookies}`);
81
+ @@ -343,7 +349,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
82
+ _options.logger && _options.logger(`Redirect: Original Location ----> ${proxyRes.headers.location}`);
83
+ proxyRes.headers.location = replaceHosts(proxyRes.headers.location);
84
+ if (reqestHost && proxyRes.headers.location.startsWith('/')) {
85
+ - proxyRes.headers.location = `http://${_options.proxyOwnIp}:${_options.proxyPort}/${reqestHost}${proxyRes.headers.location}`;
86
+ + proxyRes.headers.location = `${proxyBaseUrl}/${reqestHost}${proxyRes.headers.location}`;
87
+ }
88
+ _options.logger && _options.logger(`Redirect: Final Location ----> ${proxyRes.headers.location}`);
89
+ return;