@coffeerunhobby/mcp-ha-connect 2.2.1 → 2.3.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.
- package/.env.example +1 -1
- package/README.md +11 -2
- package/dist/omadaClient/client.d.ts +6 -1
- package/dist/omadaClient/client.js +12 -1
- package/dist/omadaClient/client.js.map +1 -1
- package/dist/omadaClient/diagnose.d.ts +68 -0
- package/dist/omadaClient/diagnose.js +164 -0
- package/dist/omadaClient/diagnose.js.map +1 -0
- package/dist/omadaClient/index.d.ts +6 -0
- package/dist/omadaClient/index.js +15 -0
- package/dist/omadaClient/index.js.map +1 -1
- package/dist/omadaClient/macGroups.d.ts +58 -0
- package/dist/omadaClient/macGroups.js +124 -0
- package/dist/omadaClient/macGroups.js.map +1 -0
- package/dist/tools/infra/actions.d.ts +1 -1
- package/dist/tools/infra/actions.js +1 -1
- package/dist/tools/omada/index.js +6 -0
- package/dist/tools/omada/index.js.map +1 -1
- package/dist/tools/omada/macGroups.d.ts +19 -0
- package/dist/tools/omada/macGroups.js +77 -0
- package/dist/tools/omada/macGroups.js.map +1 -0
- package/dist/tools/omada/namespace.js +8 -0
- package/dist/tools/omada/namespace.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
- package/.codex/review-rules.md +0 -16
- package/.dockerignore +0 -8
- package/.gitattributes +0 -30
- package/.github/workflows/ci.yml +0 -193
- package/AGENTS.md +0 -389
- package/Dockerfile +0 -50
- package/docker-compose.yml +0 -23
- package/docs/CHANGELOG.md +0 -341
- package/docs/COVERAGE.md +0 -219
- package/docs/QUICK_START.md +0 -240
- package/eslint.config.js +0 -33
- package/n8n/ai-agent-safety-monitor.json +0 -355
- package/openapi.json +0 -1259
- package/scripts/check-version-sync.mjs +0 -69
- package/scripts/generate-jwt.ts +0 -121
- package/src/config.ts +0 -440
- package/src/haClient/automations.ts +0 -261
- package/src/haClient/calendars.ts +0 -94
- package/src/haClient/config.ts +0 -52
- package/src/haClient/devices.ts +0 -653
- package/src/haClient/entities.ts +0 -128
- package/src/haClient/events.ts +0 -689
- package/src/haClient/history.ts +0 -73
- package/src/haClient/index.ts +0 -336
- package/src/haClient/request.ts +0 -142
- package/src/haClient/services.ts +0 -45
- package/src/haClient/states.ts +0 -60
- package/src/haClient/updates.ts +0 -58
- package/src/haClient/wsCommand.ts +0 -44
- package/src/index.ts +0 -164
- package/src/localAI/index.ts +0 -88
- package/src/localAI/prompts.ts +0 -50
- package/src/localAI/providers/base.ts +0 -120
- package/src/localAI/providers/ollama.ts +0 -78
- package/src/localAI/providers/openai.ts +0 -86
- package/src/localAI/types.ts +0 -67
- package/src/omadaClient/auth.ts +0 -178
- package/src/omadaClient/client.ts +0 -337
- package/src/omadaClient/device.ts +0 -118
- package/src/omadaClient/index.ts +0 -310
- package/src/omadaClient/network.ts +0 -284
- package/src/omadaClient/request.ts +0 -365
- package/src/omadaClient/security.ts +0 -51
- package/src/omadaClient/site.ts +0 -344
- package/src/ownerQuestions/service.ts +0 -536
- package/src/permissions/index.ts +0 -143
- package/src/resources/index.ts +0 -213
- package/src/server/auth.ts +0 -175
- package/src/server/chatFace.ts +0 -789
- package/src/server/chatSlice.ts +0 -57
- package/src/server/common.ts +0 -170
- package/src/server/eventSubscription.ts +0 -343
- package/src/server/http.ts +0 -626
- package/src/server/instructions.ts +0 -97
- package/src/server/rateLimiter.ts +0 -227
- package/src/server/stdio.ts +0 -41
- package/src/server/stream.ts +0 -205
- package/src/tools/ai/analyzeSensors.ts +0 -33
- package/src/tools/ai/index.ts +0 -20
- package/src/tools/common.ts +0 -442
- package/src/tools/homeassistant/askOwner.ts +0 -133
- package/src/tools/homeassistant/automationControls.ts +0 -66
- package/src/tools/homeassistant/calendar.ts +0 -97
- package/src/tools/homeassistant/callService.ts +0 -25
- package/src/tools/homeassistant/controlClimate.ts +0 -68
- package/src/tools/homeassistant/controlCover.ts +0 -48
- package/src/tools/homeassistant/controlFan.ts +0 -44
- package/src/tools/homeassistant/controlLight.ts +0 -38
- package/src/tools/homeassistant/controlMediaPlayer.ts +0 -50
- package/src/tools/homeassistant/createAutomation.ts +0 -35
- package/src/tools/homeassistant/deleteAutomation.ts +0 -24
- package/src/tools/homeassistant/entityAction.ts +0 -35
- package/src/tools/homeassistant/entityGuard.ts +0 -28
- package/src/tools/homeassistant/getAllSensors.ts +0 -46
- package/src/tools/homeassistant/getAutomationTrace.ts +0 -30
- package/src/tools/homeassistant/getDomainSummary.ts +0 -29
- package/src/tools/homeassistant/getEntitiesByDomain.ts +0 -56
- package/src/tools/homeassistant/getHistory.ts +0 -24
- package/src/tools/homeassistant/getState.ts +0 -24
- package/src/tools/homeassistant/getStates.ts +0 -46
- package/src/tools/homeassistant/getVersion.ts +0 -21
- package/src/tools/homeassistant/index.ts +0 -121
- package/src/tools/homeassistant/listAutomations.ts +0 -35
- package/src/tools/homeassistant/listEntities.ts +0 -44
- package/src/tools/homeassistant/listPersons.ts +0 -46
- package/src/tools/homeassistant/sceneAndScript.ts +0 -53
- package/src/tools/homeassistant/searchEntities.ts +0 -58
- package/src/tools/homeassistant/sendNotification.ts +0 -179
- package/src/tools/homeassistant/systemTools.ts +0 -52
- package/src/tools/homeassistant/triggerAutomation.ts +0 -24
- package/src/tools/index.ts +0 -78
- package/src/tools/infra/actions.ts +0 -95
- package/src/tools/infra/index.ts +0 -138
- package/src/tools/omada/blockClient.ts +0 -28
- package/src/tools/omada/cyclePoePort.ts +0 -32
- package/src/tools/omada/deleteClient.ts +0 -27
- package/src/tools/omada/disableClientRateLimit.ts +0 -23
- package/src/tools/omada/getClient.ts +0 -14
- package/src/tools/omada/getDevice.ts +0 -14
- package/src/tools/omada/getFirewallSetting.ts +0 -14
- package/src/tools/omada/getInternetInfo.ts +0 -14
- package/src/tools/omada/getLanNetworkList.ts +0 -14
- package/src/tools/omada/getLanProfileList.ts +0 -14
- package/src/tools/omada/getPortForwardingStatus.ts +0 -26
- package/src/tools/omada/getRateLimitProfiles.ts +0 -14
- package/src/tools/omada/getSiteNtpStatus.ts +0 -14
- package/src/tools/omada/getSsidDetail.ts +0 -22
- package/src/tools/omada/getSsidList.ts +0 -21
- package/src/tools/omada/getSwitchStackDetail.ts +0 -14
- package/src/tools/omada/getThreatList.ts +0 -42
- package/src/tools/omada/getWlanGroupList.ts +0 -14
- package/src/tools/omada/graph.ts +0 -187
- package/src/tools/omada/index.ts +0 -153
- package/src/tools/omada/listBlockedClients.ts +0 -17
- package/src/tools/omada/listClients.ts +0 -14
- package/src/tools/omada/listClientsActivity.ts +0 -25
- package/src/tools/omada/listClientsPastConnections.ts +0 -44
- package/src/tools/omada/listDevices.ts +0 -14
- package/src/tools/omada/listDevicesStats.ts +0 -41
- package/src/tools/omada/listMostActiveClients.ts +0 -14
- package/src/tools/omada/listSites.ts +0 -18
- package/src/tools/omada/namespace.ts +0 -1164
- package/src/tools/omada/searchDevices.ts +0 -20
- package/src/tools/omada/setClientRateLimit.ts +0 -26
- package/src/tools/omada/setClientRateLimitProfile.ts +0 -25
- package/src/tools/omada/setSiteNtpServers.ts +0 -36
- package/src/tools/omada/setSsidEnabled.ts +0 -38
- package/src/tools/omada/unblockClient.ts +0 -26
- package/src/tools/types.ts +0 -11
- package/src/tools/utils/pagination-schema.ts +0 -27
- package/src/types/ai/index.ts +0 -11
- package/src/types/ai/types.ts +0 -67
- package/src/types/homeassistant/automations.ts +0 -43
- package/src/types/homeassistant/calendars.ts +0 -26
- package/src/types/homeassistant/config.ts +0 -14
- package/src/types/homeassistant/entities.ts +0 -14
- package/src/types/homeassistant/errors.ts +0 -33
- package/src/types/homeassistant/haConfig.ts +0 -7
- package/src/types/homeassistant/history.ts +0 -13
- package/src/types/homeassistant/index.ts +0 -46
- package/src/types/homeassistant/request.ts +0 -6
- package/src/types/homeassistant/serviceCall.ts +0 -18
- package/src/types/homeassistant/states.ts +0 -58
- package/src/types/homeassistant/updates.ts +0 -28
- package/src/types/index.ts +0 -12
- package/src/types/omada/activeClientInfo.ts +0 -17
- package/src/types/omada/clientActivity.ts +0 -32
- package/src/types/omada/clientBlockStatus.ts +0 -11
- package/src/types/omada/clientPastConnection.ts +0 -82
- package/src/types/omada/index.ts +0 -14
- package/src/types/omada/omadaApiResponse.ts +0 -5
- package/src/types/omada/omadaClientInfo.ts +0 -11
- package/src/types/omada/omadaDeviceInfo.ts +0 -11
- package/src/types/omada/omadaDeviceStats.ts +0 -31
- package/src/types/omada/omadaSiteSummary.ts +0 -5
- package/src/types/omada/oswStackDetail.ts +0 -11
- package/src/types/omada/paginatedResult.ts +0 -6
- package/src/types/omada/rateLimitProfile.ts +0 -58
- package/src/types/omada/threatInfo.ts +0 -38
- package/src/types/omada/tokenResult.ts +0 -6
- package/src/types.ts +0 -2
- package/src/utils/config-validations.ts +0 -68
- package/src/utils/jwt.ts +0 -112
- package/src/utils/logger.ts +0 -121
- package/src/utils/plainText.ts +0 -48
- package/src/utils/redact.ts +0 -59
- package/src/utils/sanitizeError.ts +0 -18
- package/src/utils/tlsDispatcher.ts +0 -69
- package/src/version.ts +0 -5
- package/tests/bdd/automation-creation.spec.ts +0 -214
- package/tests/bdd/automation-management.spec.ts +0 -267
- package/tests/bdd/calendar.spec.ts +0 -190
- package/tests/bdd/configuration.spec.ts +0 -283
- package/tests/bdd/device-control.spec.ts +0 -344
- package/tests/bdd/entity-listing.spec.ts +0 -208
- package/tests/bdd/entity-search.spec.ts +0 -183
- package/tests/bdd/history-retrieval.spec.ts +0 -195
- package/tests/bdd/notifications.spec.ts +0 -238
- package/tests/bdd/service-calls.spec.ts +0 -144
- package/tests/bdd/state-retrieval.spec.ts +0 -204
- package/tests/bench/hotpaths.bench.ts +0 -68
- package/tests/config.test.ts +0 -355
- package/tests/features/automation-creation.feature +0 -36
- package/tests/features/automation-management.feature +0 -54
- package/tests/features/calendar.feature +0 -37
- package/tests/features/configuration.feature +0 -70
- package/tests/features/device-control.feature +0 -100
- package/tests/features/entity-listing.feature +0 -51
- package/tests/features/entity-search.feature +0 -44
- package/tests/features/history-retrieval.feature +0 -38
- package/tests/features/notifications.feature +0 -37
- package/tests/features/service-calls.feature +0 -35
- package/tests/features/state-retrieval.feature +0 -37
- package/tests/fixtures/openapi-default-slice.json +0 -290
- package/tests/haClient/automationTrace.test.ts +0 -59
- package/tests/haClient/automations.test.ts +0 -149
- package/tests/haClient/config.test.ts +0 -117
- package/tests/haClient/entities.test.ts +0 -188
- package/tests/haClient/events.test.ts +0 -455
- package/tests/haClient/eventsTls.test.ts +0 -52
- package/tests/haClient/history.test.ts +0 -228
- package/tests/haClient/index.test.ts +0 -243
- package/tests/haClient/request.test.ts +0 -217
- package/tests/haClient/services.test.ts +0 -120
- package/tests/haClient/states.test.ts +0 -119
- package/tests/haClient/updates.test.ts +0 -193
- package/tests/index.test.ts +0 -160
- package/tests/integration/homeassistant.test.ts +0 -144
- package/tests/integration/omada.test.ts +0 -167
- package/tests/integration/server.test.ts +0 -151
- package/tests/localAI/index.test.ts +0 -177
- package/tests/localAI/ollama.test.ts +0 -187
- package/tests/localAI/openai.test.ts +0 -257
- package/tests/omadaClient/actions.test.ts +0 -161
- package/tests/omadaClient/clientBlocks.test.ts +0 -170
- package/tests/omadaClient/ntp.test.ts +0 -381
- package/tests/omadaClient/site.test.ts +0 -37
- package/tests/ownerQuestions/service.test.ts +0 -499
- package/tests/ownerQuestions/tools.test.ts +0 -186
- package/tests/permissions/index.test.ts +0 -217
- package/tests/resources/index.test.ts +0 -288
- package/tests/scripts/generateJwt.test.ts +0 -44
- package/tests/security/accessControl.test.ts +0 -223
- package/tests/security/authentication.test.ts +0 -117
- package/tests/security/configuration.test.ts +0 -170
- package/tests/security/cryptoTransport.test.ts +0 -172
- package/tests/security/dependencies.test.ts +0 -152
- package/tests/security/injection.test.ts +0 -117
- package/tests/security/insecureDesign.test.ts +0 -121
- package/tests/security/logging.test.ts +0 -131
- package/tests/security/resourceConsumption.test.ts +0 -142
- package/tests/server/auth.test.ts +0 -249
- package/tests/server/chatFace.test.ts +0 -320
- package/tests/server/common.test.ts +0 -84
- package/tests/server/http.test.ts +0 -561
- package/tests/server/instructions.test.ts +0 -152
- package/tests/server/memoizeJsonSchema.test.ts +0 -71
- package/tests/server/stdio.test.ts +0 -99
- package/tests/server/stream.test.ts +0 -285
- package/tests/server/toolVisibility.http.test.ts +0 -111
- package/tests/server/toolVisibility.test.ts +0 -129
- package/tests/tools/ai/analyzeSensors.test.ts +0 -227
- package/tests/tools/common.test.ts +0 -123
- package/tests/tools/homeassistant/handlers.test.ts +0 -872
- package/tests/tools/infra/invokeAction.test.ts +0 -280
- package/tests/tools/omada/graph.test.ts +0 -385
- package/tests/tools/omada/handlers.test.ts +0 -897
- package/tests/tools/omada/namespace.test.ts +0 -483
- package/tests/tools/registry.test.ts +0 -168
- package/tests/utils/config-validations.test.ts +0 -352
- package/tests/utils/jwt.test.ts +0 -115
- package/tests/utils/logger.test.ts +0 -327
- package/tests/utils/redact.test.ts +0 -105
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -38
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Server Instructions
|
|
3
|
-
*
|
|
4
|
-
* Provides guidance to LLMs on how to optimally use the server's tools.
|
|
5
|
-
* These instructions are sent during initialization and help LLMs understand
|
|
6
|
-
* tool relationships, best practices, and constraints.
|
|
7
|
-
*
|
|
8
|
-
* @see https://blog.modelcontextprotocol.io/posts/2025-11-03-using-server-instructions/
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export interface InstructionsConfig {
|
|
12
|
-
haEnabled: boolean;
|
|
13
|
-
omadaEnabled: boolean;
|
|
14
|
-
aiEnabled: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Generates server instructions based on enabled plugins.
|
|
19
|
-
* Instructions are kept concise and actionable per MCP best practices.
|
|
20
|
-
*/
|
|
21
|
-
export function generateInstructions(config: InstructionsConfig): string {
|
|
22
|
-
const sections: string[] = [];
|
|
23
|
-
|
|
24
|
-
// Base instructions
|
|
25
|
-
sections.push(
|
|
26
|
-
'MCP-HA-Connect: Smart home control via Home Assistant with network management via TP-Link Omada.'
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
if (config.haEnabled) {
|
|
30
|
-
sections.push(getHomeAssistantInstructions());
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (config.omadaEnabled) {
|
|
34
|
-
sections.push(getOmadaInstructions());
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (config.aiEnabled) {
|
|
38
|
-
sections.push(getAIInstructions());
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Cross-plugin relationships
|
|
42
|
-
if (config.haEnabled && config.omadaEnabled) {
|
|
43
|
-
sections.push(getCrossPluginInstructions());
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return sections.join(' ');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getHomeAssistantInstructions(): string {
|
|
50
|
-
return [
|
|
51
|
-
// Entity queries
|
|
52
|
-
'ENTITY QUERIES: Entity tools return paginated results (50/page default). Use includeAttributes=true only when full attribute data is needed. For people/family queries, use listPersons instead of searchEntities.',
|
|
53
|
-
|
|
54
|
-
// State management
|
|
55
|
-
'STATE: getState returns single entity; getStates returns all entities paginated. Use getDomainSummary for quick counts by domain.',
|
|
56
|
-
|
|
57
|
-
// Device control
|
|
58
|
-
'CONTROL: Use specialized control tools (controlLight, controlClimate, controlFan, controlCover, controlMediaPlayer) instead of generic callService when available - they provide better validation and defaults.',
|
|
59
|
-
|
|
60
|
-
// Automations
|
|
61
|
-
'AUTOMATIONS: After createAutomation, automations reload automatically. Use listAutomations to verify. triggerAutomation can pass variables to test automations safely.',
|
|
62
|
-
|
|
63
|
-
// Notifications
|
|
64
|
-
'NOTIFICATIONS: Use listNotificationTargets to discover available mobile devices before sendNotification. Target format is usually mobile_app_<device_name>.',
|
|
65
|
-
].join(' ');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function getOmadaInstructions(): string {
|
|
69
|
-
return [
|
|
70
|
-
// Site context
|
|
71
|
-
'OMADA: Most Omada tools require siteId. Use omada_listSites first to get available sites if siteId is unknown.',
|
|
72
|
-
|
|
73
|
-
// Client vs Device
|
|
74
|
-
'CLIENTS vs DEVICES: Clients are connected users/devices (phones, laptops). Devices are network infrastructure (APs, switches, gateways). Use omada_listClients for user devices, omada_listDevices for infrastructure.',
|
|
75
|
-
|
|
76
|
-
// Rate limiting
|
|
77
|
-
'RATE LIMITS: Use omada_getRateLimitProfiles before setting limits. omada_setClientRateLimitProfile applies predefined profiles; omada_setClientRateLimit sets custom Kbps values.',
|
|
78
|
-
|
|
79
|
-
// Remote-hands actions (v1.6)
|
|
80
|
-
'ACTIONS: omada_cyclePoePort power-cycles PoE ports (remote-reboots APs/cameras; switch MAC via omada_listDevices). omada_setSsidEnabled toggles an SSID by name (guest WiFi on/off). omada_setSiteNtpServers sets site NTP.',
|
|
81
|
-
|
|
82
|
-
// Read-only preference
|
|
83
|
-
'SAFETY: Prefer read-only Omada tools. Rate limits, PoE cycling, and SSID toggles affect real availability - confirm with user before applying.',
|
|
84
|
-
].join(' ');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function getAIInstructions(): string {
|
|
88
|
-
return [
|
|
89
|
-
'AI ANALYSIS: analyzeSensors uses local Ollama for privacy-preserving sensor analysis. Pass sensor data as key-value pairs. Useful for anomaly detection and recommendations.',
|
|
90
|
-
].join(' ');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function getCrossPluginInstructions(): string {
|
|
94
|
-
return [
|
|
95
|
-
'INTEGRATION: Network clients from Omada can correlate with HA device_trackers for presence detection. HA person entities link to device_trackers which may correspond to Omada clients.',
|
|
96
|
-
].join(' ');
|
|
97
|
-
}
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rate Limiter Middleware
|
|
3
|
-
* Simple token bucket rate limiter for HTTP endpoints
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
7
|
-
import { logger } from '../utils/logger.js';
|
|
8
|
-
|
|
9
|
-
export interface RateLimitConfig {
|
|
10
|
-
windowMs?: number; // Time window in milliseconds (default: 60000 = 1 minute)
|
|
11
|
-
maxRequests?: number; // Maximum requests per window (default: 100)
|
|
12
|
-
skipPaths?: string[]; // Paths to skip rate limiting (e.g., health checks)
|
|
13
|
-
keyGenerator?: (req: IncomingMessage) => string; // Custom key generator
|
|
14
|
-
/**
|
|
15
|
-
* Immediate-peer IPs whose forwarding headers (CF-Connecting-IP / X-Forwarded-For /
|
|
16
|
-
* X-Real-IP) may be trusted to identify the real client (M4). Empty by default —
|
|
17
|
-
* with no trusted proxy, client-supplied headers are IGNORED and the socket
|
|
18
|
-
* address is used, so a forged header cannot evade limits or grow the bucket map.
|
|
19
|
-
*/
|
|
20
|
-
trustedProxies?: string[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface RateLimitEntry {
|
|
24
|
-
count: number;
|
|
25
|
-
windowStart: number;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Rate Limiter class
|
|
30
|
-
*/
|
|
31
|
-
export class RateLimiter {
|
|
32
|
-
private readonly windowMs: number;
|
|
33
|
-
private readonly maxRequests: number;
|
|
34
|
-
private readonly skipPaths: Set<string>;
|
|
35
|
-
private readonly keyGenerator: (req: IncomingMessage) => string;
|
|
36
|
-
private readonly trustedProxies: Set<string>;
|
|
37
|
-
private readonly entries = new Map<string, RateLimitEntry>();
|
|
38
|
-
private cleanupInterval: ReturnType<typeof setInterval> | null = null;
|
|
39
|
-
|
|
40
|
-
constructor(config: RateLimitConfig = {}) {
|
|
41
|
-
this.windowMs = config.windowMs ?? 60000; // 1 minute default
|
|
42
|
-
this.maxRequests = config.maxRequests ?? 100; // 100 requests per minute
|
|
43
|
-
this.skipPaths = new Set(config.skipPaths ?? ['/health', '/healthcheck']);
|
|
44
|
-
this.trustedProxies = new Set(config.trustedProxies ?? []);
|
|
45
|
-
// Bind so a custom generator OR the default both see `this`.
|
|
46
|
-
this.keyGenerator = config.keyGenerator ?? ((req) => this.defaultKeyGenerator(req));
|
|
47
|
-
|
|
48
|
-
// Start cleanup interval to prevent memory leaks
|
|
49
|
-
this.startCleanup();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** First value of a possibly-array header, trimmed; undefined if absent/empty. */
|
|
53
|
-
private firstHeader(value: string | string[] | undefined): string | undefined {
|
|
54
|
-
const v = Array.isArray(value) ? value[0] : value;
|
|
55
|
-
const trimmed = v?.trim();
|
|
56
|
-
return trimmed ? trimmed : undefined;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Default key generator (M4 / OWASP API4:2023).
|
|
61
|
-
*
|
|
62
|
-
* Client-supplied forwarding headers are honored ONLY when the immediate peer
|
|
63
|
-
* (`socket.remoteAddress`) is a configured trusted proxy. Otherwise we key on the
|
|
64
|
-
* socket address — an attacker cannot forge that, so they can neither evade the
|
|
65
|
-
* limit by rotating X-Forwarded-For nor exhaust memory with unbounded forged keys.
|
|
66
|
-
*/
|
|
67
|
-
private defaultKeyGenerator(req: IncomingMessage): string {
|
|
68
|
-
const socketAddr = req.socket?.remoteAddress ?? 'unknown';
|
|
69
|
-
|
|
70
|
-
if (!this.trustedProxies.has(socketAddr)) {
|
|
71
|
-
return socketAddr;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Behind a trusted proxy: prefer Cloudflare's single-client CF-Connecting-IP,
|
|
75
|
-
// then the left-most X-Forwarded-For hop, then X-Real-IP.
|
|
76
|
-
const cf = this.firstHeader(req.headers['cf-connecting-ip']);
|
|
77
|
-
if (cf) {
|
|
78
|
-
return cf;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const xff = this.firstHeader(req.headers['x-forwarded-for']);
|
|
82
|
-
if (xff) {
|
|
83
|
-
return xff.split(',')[0].trim();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const realIp = this.firstHeader(req.headers['x-real-ip']);
|
|
87
|
-
if (realIp) {
|
|
88
|
-
return realIp;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return socketAddr;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Start periodic cleanup of expired entries
|
|
96
|
-
*/
|
|
97
|
-
private startCleanup(): void {
|
|
98
|
-
// Cleanup every window period
|
|
99
|
-
this.cleanupInterval = setInterval(() => {
|
|
100
|
-
const now = Date.now();
|
|
101
|
-
let cleaned = 0;
|
|
102
|
-
|
|
103
|
-
for (const [key, entry] of this.entries) {
|
|
104
|
-
if (now - entry.windowStart > this.windowMs * 2) {
|
|
105
|
-
this.entries.delete(key);
|
|
106
|
-
cleaned++;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (cleaned > 0) {
|
|
111
|
-
logger.debug('Rate limiter cleanup', { entriesRemoved: cleaned, remaining: this.entries.size });
|
|
112
|
-
}
|
|
113
|
-
}, this.windowMs);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Stop cleanup interval
|
|
118
|
-
*/
|
|
119
|
-
stop(): void {
|
|
120
|
-
if (this.cleanupInterval) {
|
|
121
|
-
clearInterval(this.cleanupInterval);
|
|
122
|
-
this.cleanupInterval = null;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Check if request should be allowed
|
|
128
|
-
*/
|
|
129
|
-
check(req: IncomingMessage): { allowed: boolean; remaining: number; resetTime: number } {
|
|
130
|
-
const url = req.url ?? '/';
|
|
131
|
-
const path = url.split('?')[0];
|
|
132
|
-
|
|
133
|
-
// Skip rate limiting for certain paths
|
|
134
|
-
if (this.skipPaths.has(path)) {
|
|
135
|
-
return { allowed: true, remaining: this.maxRequests, resetTime: 0 };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const key = this.keyGenerator(req);
|
|
139
|
-
const now = Date.now();
|
|
140
|
-
let entry = this.entries.get(key);
|
|
141
|
-
|
|
142
|
-
// Create new entry or reset if window expired
|
|
143
|
-
if (!entry || now - entry.windowStart > this.windowMs) {
|
|
144
|
-
entry = { count: 0, windowStart: now };
|
|
145
|
-
this.entries.set(key, entry);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Increment count
|
|
149
|
-
entry.count++;
|
|
150
|
-
|
|
151
|
-
const remaining = Math.max(0, this.maxRequests - entry.count);
|
|
152
|
-
const resetTime = entry.windowStart + this.windowMs;
|
|
153
|
-
|
|
154
|
-
if (entry.count > this.maxRequests) {
|
|
155
|
-
logger.warn('Rate limit exceeded', {
|
|
156
|
-
key,
|
|
157
|
-
count: entry.count,
|
|
158
|
-
limit: this.maxRequests,
|
|
159
|
-
path,
|
|
160
|
-
});
|
|
161
|
-
return { allowed: false, remaining: 0, resetTime };
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return { allowed: true, remaining, resetTime };
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Middleware function for HTTP server
|
|
169
|
-
*/
|
|
170
|
-
middleware(): (req: IncomingMessage, res: ServerResponse) => boolean {
|
|
171
|
-
return (req: IncomingMessage, res: ServerResponse): boolean => {
|
|
172
|
-
const result = this.check(req);
|
|
173
|
-
|
|
174
|
-
// Add rate limit headers
|
|
175
|
-
res.setHeader('X-RateLimit-Limit', this.maxRequests.toString());
|
|
176
|
-
res.setHeader('X-RateLimit-Remaining', result.remaining.toString());
|
|
177
|
-
res.setHeader('X-RateLimit-Reset', Math.ceil(result.resetTime / 1000).toString());
|
|
178
|
-
|
|
179
|
-
if (!result.allowed) {
|
|
180
|
-
res.setHeader('Retry-After', Math.ceil((result.resetTime - Date.now()) / 1000).toString());
|
|
181
|
-
res.writeHead(429, { 'Content-Type': 'application/json' });
|
|
182
|
-
res.end(JSON.stringify({
|
|
183
|
-
error: 'Too Many Requests',
|
|
184
|
-
message: 'Rate limit exceeded. Please try again later.',
|
|
185
|
-
retryAfter: Math.ceil((result.resetTime - Date.now()) / 1000),
|
|
186
|
-
}));
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return true;
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Get current stats
|
|
196
|
-
*/
|
|
197
|
-
getStats(): { totalEntries: number; config: { windowMs: number; maxRequests: number } } {
|
|
198
|
-
return {
|
|
199
|
-
totalEntries: this.entries.size,
|
|
200
|
-
config: {
|
|
201
|
-
windowMs: this.windowMs,
|
|
202
|
-
maxRequests: this.maxRequests,
|
|
203
|
-
},
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Reset rate limit for a specific key
|
|
209
|
-
*/
|
|
210
|
-
reset(key: string): boolean {
|
|
211
|
-
return this.entries.delete(key);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Clear all rate limit entries
|
|
216
|
-
*/
|
|
217
|
-
clear(): void {
|
|
218
|
-
this.entries.clear();
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Create a rate limiter instance with default config
|
|
224
|
-
*/
|
|
225
|
-
export function createRateLimiter(config?: RateLimitConfig): RateLimiter {
|
|
226
|
-
return new RateLimiter(config);
|
|
227
|
-
}
|
package/src/server/stdio.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stdio transport for MCP server
|
|
3
|
-
* Used for Claude Desktop and other stdio-based clients
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
|
|
7
|
-
import type { HaClient } from '../haClient/index.js';
|
|
8
|
-
import type { LocalAIClient } from '../localAI/index.js';
|
|
9
|
-
import type { OmadaClient } from '../omadaClient/index.js';
|
|
10
|
-
import { logger } from '../utils/logger.js';
|
|
11
|
-
import { setLocalFullTrust } from '../tools/common.js';
|
|
12
|
-
import type { OmadaRegistrationMode } from '../tools/omada/index.js';
|
|
13
|
-
import type { RestAction } from '../tools/infra/index.js';
|
|
14
|
-
import { createServer } from './common.js';
|
|
15
|
-
|
|
16
|
-
export interface StdioServerOptions {
|
|
17
|
-
haClient?: HaClient;
|
|
18
|
-
omadaClient?: OmadaClient;
|
|
19
|
-
aiClient?: LocalAIClient;
|
|
20
|
-
/** Pre-registered REST actions for invokeAction (empty/undefined = tool not registered). */
|
|
21
|
-
restActions?: Record<string, RestAction>;
|
|
22
|
-
/** Tool registration strategy for the Omada plugin (default 'eager'). */
|
|
23
|
-
toolRegistrationMode?: OmadaRegistrationMode;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function startStdioServer(options: StdioServerOptions): Promise<void> {
|
|
27
|
-
logger.info('Starting stdio server');
|
|
28
|
-
|
|
29
|
-
// L3: stdio has no per-request auth; the local process owner is trusted. Grant
|
|
30
|
-
// full permissions to tool calls that arrive without an explicit mask. HTTP
|
|
31
|
-
// transports never enable this — they fail closed on a missing mask.
|
|
32
|
-
setLocalFullTrust(true);
|
|
33
|
-
|
|
34
|
-
const server = createServer(options);
|
|
35
|
-
const transport = new StdioServerTransport();
|
|
36
|
-
|
|
37
|
-
logger.info('Connecting stdio server');
|
|
38
|
-
await server.connect(transport);
|
|
39
|
-
|
|
40
|
-
logger.info('Stdio server connected and ready');
|
|
41
|
-
}
|
package/src/server/stream.ts
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Streamable HTTP transport for MCP server
|
|
3
|
-
* Implements the MCP protocol version 2025-03-26 (current/recommended)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { randomUUID } from 'node:crypto';
|
|
7
|
-
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
8
|
-
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
|
|
9
|
-
import type { EnvironmentConfig } from '../config.js';
|
|
10
|
-
import type { HaClient } from '../haClient/index.js';
|
|
11
|
-
import type { LocalAIClient } from '../localAI/index.js';
|
|
12
|
-
import type { OmadaClient } from '../omadaClient/index.js';
|
|
13
|
-
import { logger } from '../utils/logger.js';
|
|
14
|
-
import { createServer } from './common.js';
|
|
15
|
-
import type { OwnerQuestionService } from '../ownerQuestions/service.js';
|
|
16
|
-
import type { AuthenticatedRequest } from './auth.js';
|
|
17
|
-
|
|
18
|
-
interface StreamTransportState {
|
|
19
|
-
transport: NodeStreamableHTTPServerTransport;
|
|
20
|
-
server: ReturnType<typeof createServer>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Export the type for use in http.ts
|
|
24
|
-
export type { StreamTransportState };
|
|
25
|
-
|
|
26
|
-
export interface StreamTransportOptions {
|
|
27
|
-
haClient?: HaClient;
|
|
28
|
-
omadaClient?: OmadaClient;
|
|
29
|
-
aiClient?: LocalAIClient;
|
|
30
|
-
config: EnvironmentConfig;
|
|
31
|
-
ownerQuestions?: OwnerQuestionService;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface TransportSecurityOptions {
|
|
35
|
-
allowedOrigins: string[];
|
|
36
|
-
allowedHosts: string[];
|
|
37
|
-
enableDnsRebindingProtection: boolean;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Build the DNS-rebinding protection options for the Streamable HTTP transport
|
|
42
|
-
* (M3 / OWASP A05:2021).
|
|
43
|
-
*
|
|
44
|
-
* Host-header validation is **opt-in**. The SDK only validates the `Host` header
|
|
45
|
-
* when `allowedHosts` is non-empty (EXACT string match), and only validates
|
|
46
|
-
* `Origin` when `allowedOrigins` is non-empty. CORS/Origin enforcement already
|
|
47
|
-
* lives in the HTTP layer, so this helper governs ONLY the Host check and leaves
|
|
48
|
-
* `allowedOrigins` empty to avoid double-enforcing origins at the transport.
|
|
49
|
-
*
|
|
50
|
-
* - `MCP_HTTP_ALLOWED_HOSTS` UNSET → Host validation OFF (protection disabled).
|
|
51
|
-
* This preserves pre-1.3.0 behavior: an upgrade never silently 403s clients
|
|
52
|
-
* that reach the server by a host not listed in the (CORS) origins. The
|
|
53
|
-
* server's own reachable host and its allowed CORS origins are frequently
|
|
54
|
-
* different addresses, so deriving hosts from origins would break real setups.
|
|
55
|
-
* - `MCP_HTTP_ALLOWED_HOSTS` SET → enforce exactly those host[:port] values, plus
|
|
56
|
-
* loopback/bind-address conveniences so on-box health checks and local tools
|
|
57
|
-
* keep working. Any other `Host` header is rejected with a 403.
|
|
58
|
-
*/
|
|
59
|
-
export function buildTransportSecurityOptions(config: EnvironmentConfig): TransportSecurityOptions {
|
|
60
|
-
const explicitHosts = config.httpAllowedHosts ?? [];
|
|
61
|
-
|
|
62
|
-
// Opt-out by default: no explicit hosts ⇒ no Host-header validation.
|
|
63
|
-
if (explicitHosts.length === 0) {
|
|
64
|
-
return { allowedOrigins: [], allowedHosts: [], enableDnsRebindingProtection: false };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const port = config.httpPort ?? 3000;
|
|
68
|
-
const bindAddr = config.httpBindAddr ?? '127.0.0.1';
|
|
69
|
-
const hosts = new Set<string>();
|
|
70
|
-
|
|
71
|
-
for (const entry of explicitHosts) {
|
|
72
|
-
try {
|
|
73
|
-
// Accept a full URL form (e.g. https://example.com) → host header form.
|
|
74
|
-
hosts.add(new URL(entry).host);
|
|
75
|
-
} catch {
|
|
76
|
-
// Bare hostname / IP[:port] already in host-header form.
|
|
77
|
-
hosts.add(entry);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Always permit direct loopback / bind-address access on the listen port.
|
|
82
|
-
for (const host of [bindAddr, 'localhost', '127.0.0.1']) {
|
|
83
|
-
hosts.add(host);
|
|
84
|
-
hosts.add(`${host}:${port}`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
allowedOrigins: [],
|
|
89
|
-
allowedHosts: [...hosts],
|
|
90
|
-
enableDnsRebindingProtection: true,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Creates a Streamable HTTP transport
|
|
96
|
-
* This implements the MCP protocol version 2025-03-26
|
|
97
|
-
*/
|
|
98
|
-
export function createStreamTransport(options: StreamTransportOptions, callerPermissions?: number): StreamTransportState {
|
|
99
|
-
const { haClient, omadaClient, aiClient, config, ownerQuestions } = options;
|
|
100
|
-
const mcpServer = createServer({
|
|
101
|
-
haClient,
|
|
102
|
-
omadaClient,
|
|
103
|
-
aiClient,
|
|
104
|
-
ownerQuestions,
|
|
105
|
-
restActions: config.restActions,
|
|
106
|
-
toolRegistrationMode: config.toolRegistrationMode,
|
|
107
|
-
// Hide tools this caller can never use from tools/list (RBAC still enforced per call).
|
|
108
|
-
callerPermissions,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const enableStatefulSessions = config.stateful;
|
|
112
|
-
const sessionIdGenerator = enableStatefulSessions ? () => randomUUID() : undefined;
|
|
113
|
-
|
|
114
|
-
if (!enableStatefulSessions) {
|
|
115
|
-
logger.info('Starting Streamable HTTP transport in stateless mode; Mcp-Session-Id headers are optional');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const security = buildTransportSecurityOptions(config);
|
|
119
|
-
|
|
120
|
-
const transport = new NodeStreamableHTTPServerTransport({
|
|
121
|
-
sessionIdGenerator,
|
|
122
|
-
allowedOrigins: security.allowedOrigins,
|
|
123
|
-
allowedHosts: security.allowedHosts,
|
|
124
|
-
enableDnsRebindingProtection: security.enableDnsRebindingProtection,
|
|
125
|
-
onsessioninitialized: (sessionId: string) => {
|
|
126
|
-
logger.info('Session initialized', { sessionId });
|
|
127
|
-
},
|
|
128
|
-
onsessionclosed: (sessionId: string) => {
|
|
129
|
-
logger.info('Session closed', { sessionId });
|
|
130
|
-
},
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
transport.onerror = (error: Error) => {
|
|
134
|
-
logger.error('Streamable HTTP transport error', {
|
|
135
|
-
error,
|
|
136
|
-
message: error.message,
|
|
137
|
-
});
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
return { transport, server: mcpServer };
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Handles incoming Streamable HTTP requests (GET, POST, DELETE)
|
|
145
|
-
* For stateful mode, transports should be stored and reused by the caller
|
|
146
|
-
*/
|
|
147
|
-
export async function handleStreamRequest(
|
|
148
|
-
options: StreamTransportOptions,
|
|
149
|
-
req: IncomingMessage,
|
|
150
|
-
res: ServerResponse,
|
|
151
|
-
parsedBody?: unknown,
|
|
152
|
-
existingTransport?: StreamTransportState
|
|
153
|
-
): Promise<StreamTransportState | void> {
|
|
154
|
-
const { config } = options;
|
|
155
|
-
const originHeader = req.headers.origin;
|
|
156
|
-
const hostHeader = req.headers.host;
|
|
157
|
-
|
|
158
|
-
logger.info('Streamable HTTP request received', {
|
|
159
|
-
method: req.method,
|
|
160
|
-
url: req.url,
|
|
161
|
-
sessionId: req.headers['mcp-session-id'] ?? undefined,
|
|
162
|
-
origin: originHeader ?? '(not set)',
|
|
163
|
-
host: hostHeader ?? '(not set)',
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Reuse existing transport if provided, otherwise create new one
|
|
167
|
-
// The auth middleware attached the caller's mask; a new server lists only the tools it permits.
|
|
168
|
-
const callerPermissions = (req as AuthenticatedRequest).auth?.extra?.permissions as number | undefined;
|
|
169
|
-
const state = existingTransport ?? createStreamTransport(options, callerPermissions);
|
|
170
|
-
|
|
171
|
-
if (!existingTransport) {
|
|
172
|
-
await state.server.connect(state.transport);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Ensure Accept header includes required MIME types for SDK compatibility
|
|
176
|
-
// Some MCP clients (like n8n) don't send the full Accept header
|
|
177
|
-
const acceptHeader = req.headers.accept ?? '';
|
|
178
|
-
if (!acceptHeader.includes('text/event-stream') || !acceptHeader.includes('application/json')) {
|
|
179
|
-
req.headers.accept = 'application/json, text/event-stream';
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
await state.transport.handleRequest(req, res, parsedBody);
|
|
184
|
-
|
|
185
|
-
logger.debug('Streamable HTTP request handled', {
|
|
186
|
-
method: req.method,
|
|
187
|
-
sessionId: req.headers['mcp-session-id'] ?? undefined,
|
|
188
|
-
});
|
|
189
|
-
} catch (error) {
|
|
190
|
-
logger.error('Failed to handle Streamable HTTP request', {
|
|
191
|
-
error,
|
|
192
|
-
method: req.method,
|
|
193
|
-
url: req.url,
|
|
194
|
-
origin: originHeader ?? '(not set)',
|
|
195
|
-
host: hostHeader ?? '(not set)',
|
|
196
|
-
allowedOrigins: config.httpAllowedOrigins,
|
|
197
|
-
});
|
|
198
|
-
throw error;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Return state for session management if stateful
|
|
202
|
-
if (config.stateful) {
|
|
203
|
-
return state;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* analyzeSensors tool - AI-powered sensor analysis
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
6
|
-
import type { LocalAIClient, SensorData } from '../../localAI/index.js';
|
|
7
|
-
import { analyzeSensorsSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
8
|
-
import type { z } from 'zod';
|
|
9
|
-
|
|
10
|
-
type AnalyzeSensorsArgs = z.infer<typeof analyzeSensorsSchema>;
|
|
11
|
-
|
|
12
|
-
export function registerAnalyzeSensorsTool(server: McpServer, aiClient?: LocalAIClient): void {
|
|
13
|
-
server.registerTool(
|
|
14
|
-
'analyzeSensors',
|
|
15
|
-
{
|
|
16
|
-
description: 'Analyze sensor data using AI (Ollama) to detect issues and provide recommendations.',
|
|
17
|
-
inputSchema: analyzeSensorsSchema,
|
|
18
|
-
},
|
|
19
|
-
wrapToolHandler('analyzeSensors', async ({ sensors }: AnalyzeSensorsArgs) => {
|
|
20
|
-
if (!aiClient) {
|
|
21
|
-
return toToolResult({
|
|
22
|
-
error: 'AI client not configured',
|
|
23
|
-
message: 'AI analysis requires AI_URL to be set (or legacy OLLAMA_URL)',
|
|
24
|
-
}, true);
|
|
25
|
-
}
|
|
26
|
-
if (!sensors || typeof sensors !== 'object') {
|
|
27
|
-
return toToolResult({ error: 'sensors object is required' }, true);
|
|
28
|
-
}
|
|
29
|
-
const analysis = await aiClient.analyzeSensors(sensors as SensorData);
|
|
30
|
-
return toToolResult(analysis);
|
|
31
|
-
}, Permission.AI)
|
|
32
|
-
);
|
|
33
|
-
}
|
package/src/tools/ai/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AI tools index - registers all AI-powered MCP tools
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
6
|
-
import type { LocalAIClient } from '../../localAI/index.js';
|
|
7
|
-
import { logger } from '../../utils/logger.js';
|
|
8
|
-
|
|
9
|
-
import { registerAnalyzeSensorsTool } from './analyzeSensors.js';
|
|
10
|
-
|
|
11
|
-
export function registerAITools(server: McpServer, aiClient?: LocalAIClient): number {
|
|
12
|
-
logger.debug('Registering AI tools');
|
|
13
|
-
let toolCount = 0;
|
|
14
|
-
|
|
15
|
-
registerAnalyzeSensorsTool(server, aiClient);
|
|
16
|
-
toolCount += 1;
|
|
17
|
-
|
|
18
|
-
logger.info('AI tools registered', { toolCount, aiEnabled: !!aiClient });
|
|
19
|
-
return toolCount;
|
|
20
|
-
}
|