@coffeerunhobby/mcp-ha-connect 0.5.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/.dockerignore +8 -0
- package/.env.example +149 -0
- package/.gitattributes +30 -0
- package/Dockerfile +33 -0
- package/LICENSE +21 -0
- package/README.md +394 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +183 -0
- package/dist/config.js.map +1 -0
- package/dist/haClient/automations.d.ts +67 -0
- package/dist/haClient/automations.d.ts.map +1 -0
- package/dist/haClient/automations.js +181 -0
- package/dist/haClient/automations.js.map +1 -0
- package/dist/haClient/config.d.ts +28 -0
- package/dist/haClient/config.d.ts.map +1 -0
- package/dist/haClient/config.js +38 -0
- package/dist/haClient/config.js.map +1 -0
- package/dist/haClient/devices.d.ts +219 -0
- package/dist/haClient/devices.d.ts.map +1 -0
- package/dist/haClient/devices.js +506 -0
- package/dist/haClient/devices.js.map +1 -0
- package/dist/haClient/entities.d.ts +35 -0
- package/dist/haClient/entities.d.ts.map +1 -0
- package/dist/haClient/entities.js +97 -0
- package/dist/haClient/entities.js.map +1 -0
- package/dist/haClient/events.d.ts +138 -0
- package/dist/haClient/events.d.ts.map +1 -0
- package/dist/haClient/events.js +344 -0
- package/dist/haClient/events.js.map +1 -0
- package/dist/haClient/history.d.ts +26 -0
- package/dist/haClient/history.d.ts.map +1 -0
- package/dist/haClient/history.js +54 -0
- package/dist/haClient/history.js.map +1 -0
- package/dist/haClient/index.d.ts +153 -0
- package/dist/haClient/index.d.ts.map +1 -0
- package/dist/haClient/index.js +223 -0
- package/dist/haClient/index.js.map +1 -0
- package/dist/haClient/request.d.ts +34 -0
- package/dist/haClient/request.d.ts.map +1 -0
- package/dist/haClient/request.js +93 -0
- package/dist/haClient/request.js.map +1 -0
- package/dist/haClient/services.d.ts +22 -0
- package/dist/haClient/services.d.ts.map +1 -0
- package/dist/haClient/services.js +39 -0
- package/dist/haClient/services.js.map +1 -0
- package/dist/haClient/states.d.ts +26 -0
- package/dist/haClient/states.d.ts.map +1 -0
- package/dist/haClient/states.js +53 -0
- package/dist/haClient/states.js.map +1 -0
- package/dist/haClient/updates.d.ts +22 -0
- package/dist/haClient/updates.d.ts.map +1 -0
- package/dist/haClient/updates.js +48 -0
- package/dist/haClient/updates.js.map +1 -0
- package/dist/haClient.d.ts +97 -0
- package/dist/haClient.d.ts.map +1 -0
- package/dist/haClient.js +344 -0
- package/dist/haClient.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/localAI/index.d.ts +37 -0
- package/dist/localAI/index.d.ts.map +1 -0
- package/dist/localAI/index.js +73 -0
- package/dist/localAI/index.js.map +1 -0
- package/dist/localAI/prompts.d.ts +20 -0
- package/dist/localAI/prompts.d.ts.map +1 -0
- package/dist/localAI/prompts.js +48 -0
- package/dist/localAI/prompts.js.map +1 -0
- package/dist/localAI/providers/base.d.ts +51 -0
- package/dist/localAI/providers/base.d.ts.map +1 -0
- package/dist/localAI/providers/base.js +80 -0
- package/dist/localAI/providers/base.js.map +1 -0
- package/dist/localAI/providers/ollama.d.ts +22 -0
- package/dist/localAI/providers/ollama.d.ts.map +1 -0
- package/dist/localAI/providers/ollama.js +63 -0
- package/dist/localAI/providers/ollama.js.map +1 -0
- package/dist/localAI/providers/openai.d.ts +23 -0
- package/dist/localAI/providers/openai.d.ts.map +1 -0
- package/dist/localAI/providers/openai.js +63 -0
- package/dist/localAI/providers/openai.js.map +1 -0
- package/dist/localAI/types.d.ts +61 -0
- package/dist/localAI/types.d.ts.map +1 -0
- package/dist/localAI/types.js +6 -0
- package/dist/localAI/types.js.map +1 -0
- package/dist/ollamaClient.d.ts +37 -0
- package/dist/ollamaClient.d.ts.map +1 -0
- package/dist/ollamaClient.js +110 -0
- package/dist/ollamaClient.js.map +1 -0
- package/dist/resources/index.d.ts +11 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +238 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/server/common.d.ts +9 -0
- package/dist/server/common.d.ts.map +1 -0
- package/dist/server/common.js +27 -0
- package/dist/server/common.js.map +1 -0
- package/dist/server/eventSubscription.d.ts +53 -0
- package/dist/server/eventSubscription.d.ts.map +1 -0
- package/dist/server/eventSubscription.js +257 -0
- package/dist/server/eventSubscription.js.map +1 -0
- package/dist/server/http.d.ts +12 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +491 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/rateLimiter.d.ts +70 -0
- package/dist/server/rateLimiter.d.ts.map +1 -0
- package/dist/server/rateLimiter.js +156 -0
- package/dist/server/rateLimiter.js.map +1 -0
- package/dist/server/sse.d.ts +32 -0
- package/dist/server/sse.d.ts.map +1 -0
- package/dist/server/sse.js +76 -0
- package/dist/server/sse.js.map +1 -0
- package/dist/server/stdio.d.ts +8 -0
- package/dist/server/stdio.d.ts.map +1 -0
- package/dist/server/stdio.js +16 -0
- package/dist/server/stdio.js.map +1 -0
- package/dist/server/stream.d.ts +26 -0
- package/dist/server/stream.d.ts.map +1 -0
- package/dist/server/stream.js +81 -0
- package/dist/server/stream.js.map +1 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +30 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/callService.d.ts +8 -0
- package/dist/tools/callService.d.ts.map +1 -0
- package/dist/tools/callService.js +100 -0
- package/dist/tools/callService.js.map +1 -0
- package/dist/tools/getClient.d.ts +8 -0
- package/dist/tools/getClient.d.ts.map +1 -0
- package/dist/tools/getClient.js +94 -0
- package/dist/tools/getClient.js.map +1 -0
- package/dist/tools/getDevice.d.ts +8 -0
- package/dist/tools/getDevice.d.ts.map +1 -0
- package/dist/tools/getDevice.js +94 -0
- package/dist/tools/getDevice.js.map +1 -0
- package/dist/tools/getEntitiesByDomain.d.ts +8 -0
- package/dist/tools/getEntitiesByDomain.d.ts.map +1 -0
- package/dist/tools/getEntitiesByDomain.js +80 -0
- package/dist/tools/getEntitiesByDomain.js.map +1 -0
- package/dist/tools/getState.d.ts +8 -0
- package/dist/tools/getState.d.ts.map +1 -0
- package/dist/tools/getState.js +90 -0
- package/dist/tools/getState.js.map +1 -0
- package/dist/tools/getStates.d.ts +8 -0
- package/dist/tools/getStates.d.ts.map +1 -0
- package/dist/tools/getStates.js +70 -0
- package/dist/tools/getStates.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +9 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/listClients.d.ts +8 -0
- package/dist/tools/listClients.d.ts.map +1 -0
- package/dist/tools/listClients.js +73 -0
- package/dist/tools/listClients.js.map +1 -0
- package/dist/tools/listDevices.d.ts +8 -0
- package/dist/tools/listDevices.d.ts.map +1 -0
- package/dist/tools/listDevices.js +73 -0
- package/dist/tools/listDevices.js.map +1 -0
- package/dist/tools/listSites.d.ts +8 -0
- package/dist/tools/listSites.d.ts.map +1 -0
- package/dist/tools/listSites.js +67 -0
- package/dist/tools/listSites.js.map +1 -0
- package/dist/tools/registry.d.ts +9 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +1382 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/searchDevices.d.ts +8 -0
- package/dist/tools/searchDevices.d.ts.map +1 -0
- package/dist/tools/searchDevices.js +84 -0
- package/dist/tools/searchDevices.js.map +1 -0
- package/dist/tools/searchEntities.d.ts +8 -0
- package/dist/tools/searchEntities.d.ts.map +1 -0
- package/dist/tools/searchEntities.js +80 -0
- package/dist/tools/searchEntities.js.map +1 -0
- package/dist/types/entity.d.ts +98 -0
- package/dist/types/entity.d.ts.map +1 -0
- package/dist/types/entity.js +2 -0
- package/dist/types/entity.js.map +1 -0
- package/dist/types/errors.d.ts +16 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +32 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/haConfig.d.ts +8 -0
- package/dist/types/haConfig.d.ts.map +1 -0
- package/dist/types/haConfig.js +2 -0
- package/dist/types/haConfig.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/request.d.ts +7 -0
- package/dist/types/request.d.ts.map +1 -0
- package/dist/types/request.js +2 -0
- package/dist/types/request.js.map +1 -0
- package/dist/types/serviceCall.d.ts +18 -0
- package/dist/types/serviceCall.d.ts.map +1 -0
- package/dist/types/serviceCall.js +2 -0
- package/dist/types/serviceCall.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/config-validations.d.ts +12 -0
- package/dist/utils/config-validations.d.ts.map +1 -0
- package/dist/utils/config-validations.js +43 -0
- package/dist/utils/config-validations.js.map +1 -0
- package/dist/utils/logger.d.ts +24 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +94 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/wrapper.d.ts +8 -0
- package/dist/wrapper.d.ts.map +1 -0
- package/dist/wrapper.js +301 -0
- package/dist/wrapper.js.map +1 -0
- package/docker-compose.yml +17 -0
- package/docs/CHANGELOG.md +86 -0
- package/docs/LOCAL_CLIENT.md +268 -0
- package/docs/SSE_API.md +341 -0
- package/eslint.config.js +33 -0
- package/n8n/ai-agent-safety-monitor.json +355 -0
- package/openapi.json +166 -0
- package/package.json +51 -0
- package/src/config.ts +260 -0
- package/src/haClient/automations.ts +217 -0
- package/src/haClient/config.ts +41 -0
- package/src/haClient/devices.ts +653 -0
- package/src/haClient/entities.ts +128 -0
- package/src/haClient/events.ts +456 -0
- package/src/haClient/history.ts +68 -0
- package/src/haClient/index.ts +287 -0
- package/src/haClient/request.ts +127 -0
- package/src/haClient/services.ts +45 -0
- package/src/haClient/states.ts +60 -0
- package/src/haClient/updates.ts +58 -0
- package/src/index.ts +99 -0
- package/src/localAI/index.ts +88 -0
- package/src/localAI/prompts.ts +50 -0
- package/src/localAI/providers/base.ts +120 -0
- package/src/localAI/providers/ollama.ts +78 -0
- package/src/localAI/providers/openai.ts +86 -0
- package/src/localAI/types.ts +67 -0
- package/src/resources/index.ts +266 -0
- package/src/server/common.ts +38 -0
- package/src/server/eventSubscription.ts +318 -0
- package/src/server/http.ts +546 -0
- package/src/server/rateLimiter.ts +195 -0
- package/src/server/sse.ts +120 -0
- package/src/server/stdio.ts +22 -0
- package/src/server/stream.ts +113 -0
- package/src/tools/callService.ts +114 -0
- package/src/tools/getEntitiesByDomain.ts +92 -0
- package/src/tools/getState.ts +103 -0
- package/src/tools/getStates.ts +80 -0
- package/src/tools/index.ts +9 -0
- package/src/tools/registry.ts +1521 -0
- package/src/tools/searchEntities.ts +92 -0
- package/src/types/entity.ts +110 -0
- package/src/types/errors.ts +33 -0
- package/src/types/haConfig.ts +7 -0
- package/src/types/index.ts +5 -0
- package/src/types/request.ts +6 -0
- package/src/types/serviceCall.ts +18 -0
- package/src/types.ts +2 -0
- package/src/utils/config-validations.ts +46 -0
- package/src/utils/logger.ts +114 -0
- package/src/wrapper.ts +340 -0
- package/tests/config.test.ts +332 -0
- package/tests/haClient/automations.test.ts +133 -0
- package/tests/haClient/config.test.ts +96 -0
- package/tests/haClient/entities.test.ts +188 -0
- package/tests/haClient/history.test.ts +170 -0
- package/tests/haClient/index.test.ts +221 -0
- package/tests/haClient/request.test.ts +217 -0
- package/tests/haClient/services.test.ts +120 -0
- package/tests/haClient/states.test.ts +119 -0
- package/tests/haClient/updates.test.ts +193 -0
- package/tests/index.test.ts +164 -0
- package/tests/localAI/index.test.ts +168 -0
- package/tests/localAI/ollama.test.ts +187 -0
- package/tests/localAI/openai.test.ts +257 -0
- package/tests/server/common.test.ts +64 -0
- package/tests/server/http.test.ts +326 -0
- package/tests/server/sse.test.ts +240 -0
- package/tests/server/stdio.test.ts +102 -0
- package/tests/server/stream.test.ts +287 -0
- package/tests/tools/registry.test.ts +355 -0
- package/tests/utils/config-validations.test.ts +289 -0
- package/tests/utils/logger.test.ts +327 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +26 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate Limiter Middleware
|
|
3
|
+
* Simple token bucket rate limiter for HTTP endpoints
|
|
4
|
+
*/
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
/**
|
|
7
|
+
* Rate Limiter class
|
|
8
|
+
*/
|
|
9
|
+
export class RateLimiter {
|
|
10
|
+
windowMs;
|
|
11
|
+
maxRequests;
|
|
12
|
+
skipPaths;
|
|
13
|
+
keyGenerator;
|
|
14
|
+
entries = new Map();
|
|
15
|
+
cleanupInterval = null;
|
|
16
|
+
constructor(config = {}) {
|
|
17
|
+
this.windowMs = config.windowMs ?? 60000; // 1 minute default
|
|
18
|
+
this.maxRequests = config.maxRequests ?? 100; // 100 requests per minute
|
|
19
|
+
this.skipPaths = new Set(config.skipPaths ?? ['/health', '/healthcheck']);
|
|
20
|
+
this.keyGenerator = config.keyGenerator ?? this.defaultKeyGenerator;
|
|
21
|
+
// Start cleanup interval to prevent memory leaks
|
|
22
|
+
this.startCleanup();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Default key generator - uses IP address
|
|
26
|
+
*/
|
|
27
|
+
defaultKeyGenerator(req) {
|
|
28
|
+
// Try to get real IP from common proxy headers
|
|
29
|
+
const forwardedFor = req.headers['x-forwarded-for'];
|
|
30
|
+
if (forwardedFor) {
|
|
31
|
+
const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor;
|
|
32
|
+
return ips.split(',')[0].trim();
|
|
33
|
+
}
|
|
34
|
+
const realIp = req.headers['x-real-ip'];
|
|
35
|
+
if (realIp) {
|
|
36
|
+
return Array.isArray(realIp) ? realIp[0] : realIp;
|
|
37
|
+
}
|
|
38
|
+
// Fall back to socket remote address
|
|
39
|
+
return req.socket.remoteAddress ?? 'unknown';
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Start periodic cleanup of expired entries
|
|
43
|
+
*/
|
|
44
|
+
startCleanup() {
|
|
45
|
+
// Cleanup every window period
|
|
46
|
+
this.cleanupInterval = setInterval(() => {
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
let cleaned = 0;
|
|
49
|
+
for (const [key, entry] of this.entries) {
|
|
50
|
+
if (now - entry.windowStart > this.windowMs * 2) {
|
|
51
|
+
this.entries.delete(key);
|
|
52
|
+
cleaned++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (cleaned > 0) {
|
|
56
|
+
logger.debug('Rate limiter cleanup', { entriesRemoved: cleaned, remaining: this.entries.size });
|
|
57
|
+
}
|
|
58
|
+
}, this.windowMs);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Stop cleanup interval
|
|
62
|
+
*/
|
|
63
|
+
stop() {
|
|
64
|
+
if (this.cleanupInterval) {
|
|
65
|
+
clearInterval(this.cleanupInterval);
|
|
66
|
+
this.cleanupInterval = null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if request should be allowed
|
|
71
|
+
*/
|
|
72
|
+
check(req) {
|
|
73
|
+
const url = req.url ?? '/';
|
|
74
|
+
const path = url.split('?')[0];
|
|
75
|
+
// Skip rate limiting for certain paths
|
|
76
|
+
if (this.skipPaths.has(path)) {
|
|
77
|
+
return { allowed: true, remaining: this.maxRequests, resetTime: 0 };
|
|
78
|
+
}
|
|
79
|
+
const key = this.keyGenerator(req);
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
let entry = this.entries.get(key);
|
|
82
|
+
// Create new entry or reset if window expired
|
|
83
|
+
if (!entry || now - entry.windowStart > this.windowMs) {
|
|
84
|
+
entry = { count: 0, windowStart: now };
|
|
85
|
+
this.entries.set(key, entry);
|
|
86
|
+
}
|
|
87
|
+
// Increment count
|
|
88
|
+
entry.count++;
|
|
89
|
+
const remaining = Math.max(0, this.maxRequests - entry.count);
|
|
90
|
+
const resetTime = entry.windowStart + this.windowMs;
|
|
91
|
+
if (entry.count > this.maxRequests) {
|
|
92
|
+
logger.warn('Rate limit exceeded', {
|
|
93
|
+
key,
|
|
94
|
+
count: entry.count,
|
|
95
|
+
limit: this.maxRequests,
|
|
96
|
+
path,
|
|
97
|
+
});
|
|
98
|
+
return { allowed: false, remaining: 0, resetTime };
|
|
99
|
+
}
|
|
100
|
+
return { allowed: true, remaining, resetTime };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Middleware function for HTTP server
|
|
104
|
+
*/
|
|
105
|
+
middleware() {
|
|
106
|
+
return (req, res) => {
|
|
107
|
+
const result = this.check(req);
|
|
108
|
+
// Add rate limit headers
|
|
109
|
+
res.setHeader('X-RateLimit-Limit', this.maxRequests.toString());
|
|
110
|
+
res.setHeader('X-RateLimit-Remaining', result.remaining.toString());
|
|
111
|
+
res.setHeader('X-RateLimit-Reset', Math.ceil(result.resetTime / 1000).toString());
|
|
112
|
+
if (!result.allowed) {
|
|
113
|
+
res.setHeader('Retry-After', Math.ceil((result.resetTime - Date.now()) / 1000).toString());
|
|
114
|
+
res.writeHead(429, { 'Content-Type': 'application/json' });
|
|
115
|
+
res.end(JSON.stringify({
|
|
116
|
+
error: 'Too Many Requests',
|
|
117
|
+
message: 'Rate limit exceeded. Please try again later.',
|
|
118
|
+
retryAfter: Math.ceil((result.resetTime - Date.now()) / 1000),
|
|
119
|
+
}));
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get current stats
|
|
127
|
+
*/
|
|
128
|
+
getStats() {
|
|
129
|
+
return {
|
|
130
|
+
totalEntries: this.entries.size,
|
|
131
|
+
config: {
|
|
132
|
+
windowMs: this.windowMs,
|
|
133
|
+
maxRequests: this.maxRequests,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Reset rate limit for a specific key
|
|
139
|
+
*/
|
|
140
|
+
reset(key) {
|
|
141
|
+
return this.entries.delete(key);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Clear all rate limit entries
|
|
145
|
+
*/
|
|
146
|
+
clear() {
|
|
147
|
+
this.entries.clear();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Create a rate limiter instance with default config
|
|
152
|
+
*/
|
|
153
|
+
export function createRateLimiter(config) {
|
|
154
|
+
return new RateLimiter(config);
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=rateLimiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimiter.js","sourceRoot":"","sources":["../../src/server/rateLimiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE (Server-Sent Events) transport for MCP server
|
|
3
|
+
* Implements the legacy MCP protocol version 2024-11-05
|
|
4
|
+
*/
|
|
5
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
6
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
7
|
+
import type { EnvironmentConfig } from '../config.js';
|
|
8
|
+
import type { HaClient } from '../haClient/index.js';
|
|
9
|
+
import type { LocalAIClient } from '../localAI/index.js';
|
|
10
|
+
import { createServer } from './common.js';
|
|
11
|
+
interface SSETransportState {
|
|
12
|
+
transport: SSEServerTransport;
|
|
13
|
+
server: ReturnType<typeof createServer>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates an SSE transport for handling HTTP+SSE connections
|
|
17
|
+
*/
|
|
18
|
+
export declare function createSseTransport(client: HaClient, config: EnvironmentConfig, endpoint: string, res: ServerResponse, aiClient?: LocalAIClient): SSETransportState;
|
|
19
|
+
/**
|
|
20
|
+
* Handles SSE connection establishment (GET request)
|
|
21
|
+
*/
|
|
22
|
+
export declare function handleSseConnection(client: HaClient, config: EnvironmentConfig, endpoint: string, req: IncomingMessage, res: ServerResponse, aiClient?: LocalAIClient): Promise<SSETransportState>;
|
|
23
|
+
/**
|
|
24
|
+
* Handles SSE message POST requests
|
|
25
|
+
*/
|
|
26
|
+
export declare function handleSseMessage(transport: SSEServerTransport, req: IncomingMessage, res: ServerResponse, parsedBody?: unknown): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Returns the message endpoint path for SSE transport
|
|
29
|
+
*/
|
|
30
|
+
export declare function getSseMessagePath(): string;
|
|
31
|
+
export type { SSETransportState };
|
|
32
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/server/sse.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,UAAU,iBAAiB;IACzB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,cAAc,EACnB,QAAQ,CAAC,EAAE,aAAa,GACvB,iBAAiB,CAgBnB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,QAAQ,CAAC,EAAE,aAAa,GACvB,OAAO,CAAC,iBAAiB,CAAC,CA+B5B;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,kBAAkB,EAC7B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,UAAU,CAAC,EAAE,OAAO,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAGD,YAAY,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE (Server-Sent Events) transport for MCP server
|
|
3
|
+
* Implements the legacy MCP protocol version 2024-11-05
|
|
4
|
+
*/
|
|
5
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import { createServer } from './common.js';
|
|
8
|
+
const MESSAGE_PATH = '/messages';
|
|
9
|
+
/**
|
|
10
|
+
* Creates an SSE transport for handling HTTP+SSE connections
|
|
11
|
+
*/
|
|
12
|
+
export function createSseTransport(client, config, endpoint, res, aiClient) {
|
|
13
|
+
const mcpServer = createServer(client, aiClient);
|
|
14
|
+
const transport = new SSEServerTransport(endpoint, res, {
|
|
15
|
+
allowedOrigins: config.httpAllowedOrigins,
|
|
16
|
+
enableDnsRebindingProtection: true,
|
|
17
|
+
});
|
|
18
|
+
transport.onerror = (error) => {
|
|
19
|
+
logger.error('SSE transport error', {
|
|
20
|
+
error,
|
|
21
|
+
message: error.message,
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
return { transport, server: mcpServer };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Handles SSE connection establishment (GET request)
|
|
28
|
+
*/
|
|
29
|
+
export async function handleSseConnection(client, config, endpoint, req, res, aiClient) {
|
|
30
|
+
const originHeader = req.headers.origin;
|
|
31
|
+
const hostHeader = req.headers.host;
|
|
32
|
+
logger.info('SSE connection request received', {
|
|
33
|
+
method: req.method,
|
|
34
|
+
url: req.url,
|
|
35
|
+
origin: originHeader ?? '(not set)',
|
|
36
|
+
host: hostHeader ?? '(not set)',
|
|
37
|
+
});
|
|
38
|
+
try {
|
|
39
|
+
const { transport, server } = createSseTransport(client, config, endpoint, res, aiClient);
|
|
40
|
+
await server.connect(transport);
|
|
41
|
+
await transport.start();
|
|
42
|
+
logger.info('SSE connection established', {
|
|
43
|
+
sessionId: transport.sessionId,
|
|
44
|
+
});
|
|
45
|
+
return { transport, server };
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
logger.error('Failed to establish SSE connection', {
|
|
49
|
+
error,
|
|
50
|
+
origin: originHeader ?? '(not set)',
|
|
51
|
+
host: hostHeader ?? '(not set)',
|
|
52
|
+
allowedOrigins: config.httpAllowedOrigins,
|
|
53
|
+
});
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Handles SSE message POST requests
|
|
59
|
+
*/
|
|
60
|
+
export async function handleSseMessage(transport, req, res, parsedBody) {
|
|
61
|
+
logger.debug('SSE message received', {
|
|
62
|
+
sessionId: transport.sessionId,
|
|
63
|
+
hasBody: !!parsedBody,
|
|
64
|
+
});
|
|
65
|
+
await transport.handlePostMessage(req, res, parsedBody);
|
|
66
|
+
logger.debug('SSE message handled', {
|
|
67
|
+
sessionId: transport.sessionId,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Returns the message endpoint path for SSE transport
|
|
72
|
+
*/
|
|
73
|
+
export function getSseMessagePath() {
|
|
74
|
+
return MESSAGE_PATH;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=sse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/server/sse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stdio transport for MCP server
|
|
3
|
+
* Used for Claude Desktop and other stdio-based clients
|
|
4
|
+
*/
|
|
5
|
+
import type { HaClient } from '../haClient/index.js';
|
|
6
|
+
import type { LocalAIClient } from '../localAI/index.js';
|
|
7
|
+
export declare function startStdioServer(client: HaClient, aiClient?: LocalAIClient): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=stdio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/server/stdio.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhG"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stdio transport for MCP server
|
|
3
|
+
* Used for Claude Desktop and other stdio-based clients
|
|
4
|
+
*/
|
|
5
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import { createServer } from './common.js';
|
|
8
|
+
export async function startStdioServer(client, aiClient) {
|
|
9
|
+
logger.info('Starting stdio server');
|
|
10
|
+
const server = createServer(client, aiClient);
|
|
11
|
+
const transport = new StdioServerTransport();
|
|
12
|
+
logger.info('Connecting stdio server');
|
|
13
|
+
await server.connect(transport);
|
|
14
|
+
logger.info('Stdio server connected and ready');
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=stdio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/server/stdio.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAGjF,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAgB,EAAE,QAAwB;IAC/E,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAErC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAE7C,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACvC,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streamable HTTP transport for MCP server
|
|
3
|
+
* Implements the MCP protocol version 2025-03-26 (current/recommended)
|
|
4
|
+
*/
|
|
5
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
6
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
7
|
+
import type { EnvironmentConfig } from '../config.js';
|
|
8
|
+
import type { HaClient } from '../haClient/index.js';
|
|
9
|
+
import type { LocalAIClient } from '../localAI/index.js';
|
|
10
|
+
import { createServer } from './common.js';
|
|
11
|
+
interface StreamTransportState {
|
|
12
|
+
transport: StreamableHTTPServerTransport;
|
|
13
|
+
server: ReturnType<typeof createServer>;
|
|
14
|
+
}
|
|
15
|
+
export type { StreamTransportState };
|
|
16
|
+
/**
|
|
17
|
+
* Creates a Streamable HTTP transport
|
|
18
|
+
* This implements the MCP protocol version 2025-03-26
|
|
19
|
+
*/
|
|
20
|
+
export declare function createStreamTransport(client: HaClient, config: EnvironmentConfig, aiClient?: LocalAIClient): StreamTransportState;
|
|
21
|
+
/**
|
|
22
|
+
* Handles incoming Streamable HTTP requests (GET, POST, DELETE)
|
|
23
|
+
* For stateful mode, transports should be stored and reused by the caller
|
|
24
|
+
*/
|
|
25
|
+
export declare function handleStreamRequest(client: HaClient, config: EnvironmentConfig, req: IncomingMessage, res: ServerResponse, parsedBody?: unknown, existingTransport?: StreamTransportState, aiClient?: LocalAIClient): Promise<StreamTransportState | void>;
|
|
26
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/server/stream.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,UAAU,oBAAoB;IAC5B,SAAS,EAAE,6BAA6B,CAAC;IACzC,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;CACzC;AAGD,YAAY,EAAE,oBAAoB,EAAE,CAAC;AAErC;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,oBAAoB,CA8BjI;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,iBAAiB,EACzB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,UAAU,CAAC,EAAE,OAAO,EACpB,iBAAiB,CAAC,EAAE,oBAAoB,EACxC,QAAQ,CAAC,EAAE,aAAa,GACvB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CA0CtC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streamable HTTP transport for MCP server
|
|
3
|
+
* Implements the MCP protocol version 2025-03-26 (current/recommended)
|
|
4
|
+
*/
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
import { createServer } from './common.js';
|
|
9
|
+
/**
|
|
10
|
+
* Creates a Streamable HTTP transport
|
|
11
|
+
* This implements the MCP protocol version 2025-03-26
|
|
12
|
+
*/
|
|
13
|
+
export function createStreamTransport(client, config, aiClient) {
|
|
14
|
+
const mcpServer = createServer(client, aiClient);
|
|
15
|
+
const enableStatefulSessions = config.stateful;
|
|
16
|
+
const sessionIdGenerator = enableStatefulSessions ? () => randomUUID() : undefined;
|
|
17
|
+
if (!enableStatefulSessions) {
|
|
18
|
+
logger.info('Starting Streamable HTTP transport in stateless mode; Mcp-Session-Id headers are optional');
|
|
19
|
+
}
|
|
20
|
+
const transport = new StreamableHTTPServerTransport({
|
|
21
|
+
sessionIdGenerator,
|
|
22
|
+
allowedOrigins: config.httpAllowedOrigins,
|
|
23
|
+
enableDnsRebindingProtection: true,
|
|
24
|
+
onsessioninitialized: (sessionId) => {
|
|
25
|
+
logger.info('Session initialized', { sessionId });
|
|
26
|
+
},
|
|
27
|
+
onsessionclosed: (sessionId) => {
|
|
28
|
+
logger.info('Session closed', { sessionId });
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
transport.onerror = (error) => {
|
|
32
|
+
logger.error('Streamable HTTP transport error', {
|
|
33
|
+
error,
|
|
34
|
+
message: error.message,
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
return { transport, server: mcpServer };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Handles incoming Streamable HTTP requests (GET, POST, DELETE)
|
|
41
|
+
* For stateful mode, transports should be stored and reused by the caller
|
|
42
|
+
*/
|
|
43
|
+
export async function handleStreamRequest(client, config, req, res, parsedBody, existingTransport, aiClient) {
|
|
44
|
+
const originHeader = req.headers.origin;
|
|
45
|
+
const hostHeader = req.headers.host;
|
|
46
|
+
logger.info('Streamable HTTP request received', {
|
|
47
|
+
method: req.method,
|
|
48
|
+
url: req.url,
|
|
49
|
+
sessionId: req.headers['mcp-session-id'] ?? undefined,
|
|
50
|
+
origin: originHeader ?? '(not set)',
|
|
51
|
+
host: hostHeader ?? '(not set)',
|
|
52
|
+
});
|
|
53
|
+
// Reuse existing transport if provided, otherwise create new one
|
|
54
|
+
const state = existingTransport ?? createStreamTransport(client, config, aiClient);
|
|
55
|
+
if (!existingTransport) {
|
|
56
|
+
await state.server.connect(state.transport);
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
await state.transport.handleRequest(req, res, parsedBody);
|
|
60
|
+
logger.debug('Streamable HTTP request handled', {
|
|
61
|
+
method: req.method,
|
|
62
|
+
sessionId: req.headers['mcp-session-id'] ?? undefined,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
logger.error('Failed to handle Streamable HTTP request', {
|
|
67
|
+
error,
|
|
68
|
+
method: req.method,
|
|
69
|
+
url: req.url,
|
|
70
|
+
origin: originHeader ?? '(not set)',
|
|
71
|
+
host: hostHeader ?? '(not set)',
|
|
72
|
+
allowedOrigins: config.httpAllowedOrigins,
|
|
73
|
+
});
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
// Return state for session management if stateful
|
|
77
|
+
if (config.stateful) {
|
|
78
|
+
return state;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/server/stream.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAInG,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAU3C;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAgB,EAAE,MAAyB,EAAE,QAAwB;IACzG,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEjD,MAAM,sBAAsB,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC/C,MAAM,kBAAkB,GAAG,sBAAsB,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnF,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,2FAA2F,CAAC,CAAC;IAC3G,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;QAClD,kBAAkB;QAClB,cAAc,EAAE,MAAM,CAAC,kBAAkB;QACzC,4BAA4B,EAAE,IAAI;QAClC,oBAAoB,EAAE,CAAC,SAAiB,EAAE,EAAE;YAC1C,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,eAAe,EAAE,CAAC,SAAiB,EAAE,EAAE;YACrC,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/C,CAAC;KACF,CAAC,CAAC;IAEH,SAAS,CAAC,OAAO,GAAG,CAAC,KAAY,EAAE,EAAE;QACnC,MAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE;YAC9C,KAAK;YACL,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAgB,EAChB,MAAyB,EACzB,GAAoB,EACpB,GAAmB,EACnB,UAAoB,EACpB,iBAAwC,EACxC,QAAwB;IAExB,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;IACxC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;IAEpC,MAAM,CAAC,IAAI,CAAC,kCAAkC,EAAE;QAC9C,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,SAAS;QACrD,MAAM,EAAE,YAAY,IAAI,WAAW;QACnC,IAAI,EAAE,UAAU,IAAI,WAAW;KAChC,CAAC,CAAC;IAEH,iEAAiE;IACjE,MAAM,KAAK,GAAG,iBAAiB,IAAI,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEnF,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;QAE1D,MAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE;YAC9C,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,SAAS;SACtD,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,0CAA0C,EAAE;YACvD,KAAK;YACL,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,MAAM,EAAE,YAAY,IAAI,WAAW;YACnC,IAAI,EAAE,UAAU,IAAI,WAAW;YAC/B,cAAc,EAAE,MAAM,CAAC,kBAAkB;SAC1C,CAAC,CAAC;QACH,MAAM,KAAK,CAAC;IACd,CAAC;IAED,kDAAkD;IAClD,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server setup and initialization
|
|
3
|
+
*/
|
|
4
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
5
|
+
import type { HaClient } from './haClient.js';
|
|
6
|
+
export declare function createMcpServer(client: HaClient): Promise<Server>;
|
|
7
|
+
export declare function startStdioServer(client: HaClient): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI9C,wBAAsB,eAAe,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBvE;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAStE"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server setup and initialization
|
|
3
|
+
*/
|
|
4
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
5
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
|
+
import { registerAllTools } from './tools/registry.js';
|
|
7
|
+
import { logger } from './utils/logger.js';
|
|
8
|
+
export async function createMcpServer(client) {
|
|
9
|
+
logger.info('Creating MCP server');
|
|
10
|
+
const server = new Server({
|
|
11
|
+
name: 'ha-mcp',
|
|
12
|
+
version: '0.1.0',
|
|
13
|
+
}, {
|
|
14
|
+
capabilities: {
|
|
15
|
+
tools: {},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
// Register all tools
|
|
19
|
+
registerAllTools(server, client);
|
|
20
|
+
logger.info('MCP server created with 5 tools registered');
|
|
21
|
+
return server;
|
|
22
|
+
}
|
|
23
|
+
export async function startStdioServer(client) {
|
|
24
|
+
logger.info('Starting MCP server with stdio transport');
|
|
25
|
+
const server = await createMcpServer(client);
|
|
26
|
+
const transport = new StdioServerTransport();
|
|
27
|
+
await server.connect(transport);
|
|
28
|
+
logger.info('MCP server connected and ready');
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAgB;IACpD,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAEnC,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,OAAO;KACjB,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE;SACV;KACF,CACF,CAAC;IAEF,qBAAqB;IACrB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC,MAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IAE1D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAgB;IACrD,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IAExD,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAE7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,MAAM,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Call Service Tool
|
|
3
|
+
* Calls a Home Assistant service
|
|
4
|
+
*/
|
|
5
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
6
|
+
import type { HaClient } from '../haClient/index.js';
|
|
7
|
+
export declare function registerCallServiceTool(server: Server, client: HaClient): void;
|
|
8
|
+
//# sourceMappingURL=callService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callService.d.ts","sourceRoot":"","sources":["../../src/tools/callService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAIrD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAmG9E"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Call Service Tool
|
|
3
|
+
* Calls a Home Assistant service
|
|
4
|
+
*/
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
export function registerCallServiceTool(server, client) {
|
|
8
|
+
logger.debug('Registering callService tool');
|
|
9
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
10
|
+
return {
|
|
11
|
+
tools: [
|
|
12
|
+
{
|
|
13
|
+
name: 'callService',
|
|
14
|
+
description: 'Call a Home Assistant service (e.g., turn on a light, set temperature).',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
domain: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Service domain (e.g., "light", "switch", "climate", "notify")',
|
|
21
|
+
},
|
|
22
|
+
service: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Service name (e.g., "turn_on", "turn_off", "set_temperature")',
|
|
25
|
+
},
|
|
26
|
+
service_data: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
description: 'Optional service data (e.g., {"brightness": 255})',
|
|
29
|
+
},
|
|
30
|
+
target: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
description: 'Optional target (entity_id, device_id, or area_id)',
|
|
33
|
+
properties: {
|
|
34
|
+
entity_id: {
|
|
35
|
+
type: ['string', 'array'],
|
|
36
|
+
description: 'Entity ID or array of entity IDs',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
required: ['domain', 'service'],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
48
|
+
if (request.params.name !== 'callService') {
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: 'text',
|
|
53
|
+
text: JSON.stringify({ error: 'Unknown tool' }),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
isError: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const args = request.params.arguments;
|
|
61
|
+
if (!args.domain || !args.service) {
|
|
62
|
+
throw new Error('domain and service are required');
|
|
63
|
+
}
|
|
64
|
+
logger.info('Executing callService tool', {
|
|
65
|
+
domain: args.domain,
|
|
66
|
+
service: args.service,
|
|
67
|
+
});
|
|
68
|
+
const result = await client.callService(args);
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: 'text',
|
|
73
|
+
text: JSON.stringify({
|
|
74
|
+
success: true,
|
|
75
|
+
context: result.context,
|
|
76
|
+
}, null, 2),
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
logger.error('Failed to call service', {
|
|
83
|
+
error: error instanceof Error ? error.message : String(error),
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
content: [
|
|
87
|
+
{
|
|
88
|
+
type: 'text',
|
|
89
|
+
text: JSON.stringify({
|
|
90
|
+
error: 'Failed to call service',
|
|
91
|
+
message: error instanceof Error ? error.message : String(error),
|
|
92
|
+
}),
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
isError: true,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=callService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callService.js","sourceRoot":"","sources":["../../src/tools/callService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get Client Tool
|
|
3
|
+
* Get details for a specific client by MAC address
|
|
4
|
+
*/
|
|
5
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
6
|
+
import type { OmadaClient } from '../omadaClient.js';
|
|
7
|
+
export declare function registerGetClientTool(server: Server, client: OmadaClient): void;
|
|
8
|
+
//# sourceMappingURL=getClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getClient.d.ts","sourceRoot":"","sources":["../../src/tools/getClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAgG/E"}
|