@coffeerunhobby/mcp-ha-connect 2.2.1 → 2.4.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/.env.example +1 -1
- package/README.md +24 -2
- package/dist/omadaClient/accessControl.d.ts +290 -0
- package/dist/omadaClient/accessControl.js +509 -0
- package/dist/omadaClient/accessControl.js.map +1 -0
- 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 +9 -0
- package/dist/omadaClient/index.js +19 -0
- package/dist/omadaClient/index.js.map +1 -1
- package/dist/omadaClient/macGroups.d.ts +58 -0
- package/dist/omadaClient/macGroups.js +110 -0
- package/dist/omadaClient/macGroups.js.map +1 -0
- package/dist/omadaClient/serialize.d.ts +13 -0
- package/dist/omadaClient/serialize.js +29 -0
- package/dist/omadaClient/serialize.js.map +1 -0
- package/dist/omadaClient/timeRange.d.ts +41 -0
- package/dist/omadaClient/timeRange.js +109 -0
- package/dist/omadaClient/timeRange.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/accessControl.d.ts +151 -0
- package/dist/tools/omada/accessControl.js +158 -0
- package/dist/tools/omada/accessControl.js.map +1 -0
- package/dist/tools/omada/index.js +9 -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 +16 -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,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* System tools - Restart, logs, updates
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
6
|
-
import type { HaClient } from '../../haClient/index.js';
|
|
7
|
-
import { emptySchema, systemLogSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
8
|
-
import type { z } from 'zod';
|
|
9
|
-
|
|
10
|
-
type SystemLogArgs = z.infer<typeof systemLogSchema>;
|
|
11
|
-
|
|
12
|
-
export function registerRestartHomeAssistantTool(server: McpServer, client: HaClient): void {
|
|
13
|
-
server.registerTool(
|
|
14
|
-
'restartHomeAssistant',
|
|
15
|
-
{
|
|
16
|
-
description: 'Restart the Home Assistant server. Use with caution.',
|
|
17
|
-
inputSchema: emptySchema,
|
|
18
|
-
},
|
|
19
|
-
wrapToolHandler('restartHomeAssistant', async () => {
|
|
20
|
-
await client.restartServer();
|
|
21
|
-
return toToolResult({ success: true, message: 'Home Assistant restart initiated' });
|
|
22
|
-
}, Permission.ADMIN)
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function registerGetSystemLogTool(server: McpServer, client: HaClient): void {
|
|
27
|
-
server.registerTool(
|
|
28
|
-
'getSystemLog',
|
|
29
|
-
{
|
|
30
|
-
description: 'Get system log entries from the Home Assistant logbook. Shows recent events and state changes. Default: last 24h, 100 entries.',
|
|
31
|
-
inputSchema: systemLogSchema,
|
|
32
|
-
},
|
|
33
|
-
wrapToolHandler('getSystemLog', async ({ hours = 24, entity_id, limit = 100 }: SystemLogArgs) => {
|
|
34
|
-
const log = await client.getSystemLog({ hours, entity_id, limit });
|
|
35
|
-
return toToolResult({ hours, limit, entity_id: entity_id || null, entries: log });
|
|
36
|
-
}, Permission.ADMIN)
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function registerCheckUpdatesTool(server: McpServer, client: HaClient): void {
|
|
41
|
-
server.registerTool(
|
|
42
|
-
'checkUpdates',
|
|
43
|
-
{
|
|
44
|
-
description: 'Check for available updates for Home Assistant Core, Supervisor, and OS. Requires Home Assistant OS or Supervised installation.',
|
|
45
|
-
inputSchema: emptySchema,
|
|
46
|
-
},
|
|
47
|
-
wrapToolHandler('checkUpdates', async () => {
|
|
48
|
-
const updates = await client.getAvailableUpdates();
|
|
49
|
-
return toToolResult(updates);
|
|
50
|
-
}, Permission.ADMIN)
|
|
51
|
-
);
|
|
52
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* triggerAutomation tool - Manually trigger an automation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
6
|
-
import type { HaClient } from '../../haClient/index.js';
|
|
7
|
-
import { triggerAutomationSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
8
|
-
import type { z } from 'zod';
|
|
9
|
-
|
|
10
|
-
type TriggerAutomationArgs = z.infer<typeof triggerAutomationSchema>;
|
|
11
|
-
|
|
12
|
-
export function registerTriggerAutomationTool(server: McpServer, client: HaClient): void {
|
|
13
|
-
server.registerTool(
|
|
14
|
-
'triggerAutomation',
|
|
15
|
-
{
|
|
16
|
-
description: 'Manually trigger a Home Assistant automation. Can optionally pass variables to the automation.',
|
|
17
|
-
inputSchema: triggerAutomationSchema,
|
|
18
|
-
},
|
|
19
|
-
wrapToolHandler('triggerAutomation', async ({ entity_id, variables }: TriggerAutomationArgs) => {
|
|
20
|
-
const result = await client.triggerAutomation(entity_id, variables as Record<string, unknown> | undefined);
|
|
21
|
-
return toToolResult({ success: true, entity_id, message: 'Automation triggered successfully', context: result.context });
|
|
22
|
-
}, Permission.CONFIGURE)
|
|
23
|
-
);
|
|
24
|
-
}
|
package/src/tools/index.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tools index - registers all MCP tools organized by domain
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
6
|
-
import type { HaClient } from '../haClient/index.js';
|
|
7
|
-
import type { LocalAIClient } from '../localAI/index.js';
|
|
8
|
-
import type { OmadaClient } from '../omadaClient/index.js';
|
|
9
|
-
import { logger } from '../utils/logger.js';
|
|
10
|
-
|
|
11
|
-
// Tool registration functions by domain
|
|
12
|
-
import { registerHomeAssistantTools } from './homeassistant/index.js';
|
|
13
|
-
import { registerOmadaTools, type OmadaRegistrationMode } from './omada/index.js';
|
|
14
|
-
import { registerAITools } from './ai/index.js';
|
|
15
|
-
import { registerInfraTools, type RestAction } from './infra/index.js';
|
|
16
|
-
import { registerOwnerQuestionTools } from './homeassistant/askOwner.js';
|
|
17
|
-
import type { OwnerQuestionService } from '../ownerQuestions/service.js';
|
|
18
|
-
|
|
19
|
-
export interface RegisterToolsOptions {
|
|
20
|
-
server: McpServer;
|
|
21
|
-
haClient?: HaClient;
|
|
22
|
-
omadaClient?: OmadaClient;
|
|
23
|
-
aiClient?: LocalAIClient;
|
|
24
|
-
/** Pre-registered REST actions for invokeAction (empty/undefined = tool not registered). */
|
|
25
|
-
restActions?: Record<string, RestAction>;
|
|
26
|
-
/** Tool registration strategy for the Omada plugin (default 'eager'). */
|
|
27
|
-
toolRegistrationMode?: OmadaRegistrationMode;
|
|
28
|
-
/** Owner questions (askOwner & co.); registered only when the service is running. */
|
|
29
|
-
ownerQuestions?: OwnerQuestionService;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Register all available tools based on configured clients
|
|
34
|
-
*/
|
|
35
|
-
export function registerAllTools(options: RegisterToolsOptions): void {
|
|
36
|
-
const { server, haClient, omadaClient, aiClient, restActions, toolRegistrationMode, ownerQuestions } = options;
|
|
37
|
-
logger.debug('Registering all tools');
|
|
38
|
-
|
|
39
|
-
let totalTools = 0;
|
|
40
|
-
|
|
41
|
-
// Home Assistant tools (if client provided)
|
|
42
|
-
if (haClient) {
|
|
43
|
-
const haToolCount = registerHomeAssistantTools(server, haClient);
|
|
44
|
-
totalTools += haToolCount;
|
|
45
|
-
if (ownerQuestions) {
|
|
46
|
-
totalTools += registerOwnerQuestionTools(server, ownerQuestions);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Omada tools (if client provided)
|
|
51
|
-
if (omadaClient) {
|
|
52
|
-
const omadaToolCount = registerOmadaTools(server, omadaClient, toolRegistrationMode);
|
|
53
|
-
totalTools += omadaToolCount;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// AI tools (always register, but may be disabled if no aiClient)
|
|
57
|
-
const aiToolCount = registerAITools(server, aiClient);
|
|
58
|
-
totalTools += aiToolCount;
|
|
59
|
-
|
|
60
|
-
// Infra tools (only when REST actions are configured)
|
|
61
|
-
totalTools += registerInfraTools(server, restActions ?? {});
|
|
62
|
-
|
|
63
|
-
logger.info('All tools registered successfully', {
|
|
64
|
-
totalTools,
|
|
65
|
-
haEnabled: !!haClient,
|
|
66
|
-
omadaEnabled: !!omadaClient,
|
|
67
|
-
aiEnabled: !!aiClient,
|
|
68
|
-
infraEnabled: Object.keys(restActions ?? {}).length > 0,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Re-export common utilities
|
|
73
|
-
export { toToolResult, wrapToolHandler, Permission } from './common.js';
|
|
74
|
-
|
|
75
|
-
// Re-export domain-specific registration functions for selective use
|
|
76
|
-
export { registerHomeAssistantTools } from './homeassistant/index.js';
|
|
77
|
-
export { registerOmadaTools } from './omada/index.js';
|
|
78
|
-
export { registerAITools } from './ai/index.js';
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A pre-registered REST action the `invokeAction` tool may fire.
|
|
5
|
-
*
|
|
6
|
-
* SECURITY MODEL: the LLM caller supplies ONE thing — an action NAME — which is
|
|
7
|
-
* exact-match looked up here. Method, URL, auth, and timeout all come from this
|
|
8
|
-
* operator-controlled config; no model-supplied string is ever interpolated into
|
|
9
|
-
* any part of the request. A malformed/hostile name can only fail the lookup.
|
|
10
|
-
*/
|
|
11
|
-
export interface RestAction {
|
|
12
|
-
/** Human hint surfaced in the tool description so the agent picks well. */
|
|
13
|
-
description?: string;
|
|
14
|
-
method: 'GET' | 'POST';
|
|
15
|
-
url: string;
|
|
16
|
-
/** Optional Authorization: Bearer <token> (e.g. watchtower's HTTP-API token). */
|
|
17
|
-
bearerToken?: string;
|
|
18
|
-
/** Per-action timeout; some actions (watchtower update = image pulls) run long. */
|
|
19
|
-
timeoutMs?: number;
|
|
20
|
-
/**
|
|
21
|
-
* Minimum interval between firings of THIS action (rate limit). Defaults to
|
|
22
|
-
* 60s when unset; 0 disables. Caps the blast radius of a prompt-injected
|
|
23
|
-
* model looping a legitimate action (deploy storms / endpoint DoS) — the
|
|
24
|
-
* name-only lookup already stops forged actions; this stops repeated real ones.
|
|
25
|
-
*/
|
|
26
|
-
cooldownMs?: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Action names are deliberately boring: lowercase alnum with -/_ separators,
|
|
31
|
-
* 1–64 chars, must start alphanumeric. This rejects prototype-pollution shaped
|
|
32
|
-
* keys (`__proto__`), path/URL fragments, and whitespace tricks at CONFIG time,
|
|
33
|
-
* so the runtime lookup only ever sees tame identifiers.
|
|
34
|
-
*/
|
|
35
|
-
const ACTION_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
36
|
-
|
|
37
|
-
const actionSchema = z.object({
|
|
38
|
-
description: z.string().max(200).optional(),
|
|
39
|
-
method: z.enum(['GET', 'POST']).default('POST'),
|
|
40
|
-
url: z.string().url({ message: 'each action needs a valid http(s) url' }),
|
|
41
|
-
bearerToken: z.string().min(1).optional(),
|
|
42
|
-
timeoutMs: z.number().int().min(1000).max(300_000).optional(),
|
|
43
|
-
// 0 = explicitly disabled; capped at 24h so a config typo can't brick an action.
|
|
44
|
-
cooldownMs: z.number().int().min(0).max(86_400_000).optional(),
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Parse MCP_REST_ACTIONS (JSON):
|
|
49
|
-
* {"update-node-u2":{"method":"POST","url":"http://192.168.0.9:8425/v1/update","bearerToken":"…","description":"deploy latest images on node-u2"}}
|
|
50
|
-
*
|
|
51
|
-
* Returns an empty map when unset; throws on malformed input so a config typo
|
|
52
|
-
* fails loudly at startup instead of silently disabling the tool.
|
|
53
|
-
*/
|
|
54
|
-
export function parseRestActions(raw: string | undefined): Record<string, RestAction> {
|
|
55
|
-
if (!raw || raw.trim() === '') {
|
|
56
|
-
return {};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let json: unknown;
|
|
60
|
-
try {
|
|
61
|
-
json = JSON.parse(raw);
|
|
62
|
-
} catch {
|
|
63
|
-
throw new Error(
|
|
64
|
-
'MCP_REST_ACTIONS must be valid JSON: {"name":{"method":"POST","url":"http://host/path","bearerToken":"..."}}'
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (typeof json !== 'object' || json === null || Array.isArray(json)) {
|
|
69
|
-
throw new Error('MCP_REST_ACTIONS must be a JSON object keyed by action name');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Null-prototype output: even a hostile config key can never shadow or reach
|
|
73
|
-
// Object.prototype members, and runtime lookups stay own-property-only.
|
|
74
|
-
const actions: Record<string, RestAction> = Object.create(null) as Record<string, RestAction>;
|
|
75
|
-
|
|
76
|
-
for (const [name, value] of Object.entries(json)) {
|
|
77
|
-
if (!ACTION_NAME_PATTERN.test(name)) {
|
|
78
|
-
throw new Error(
|
|
79
|
-
`Invalid MCP_REST_ACTIONS action name '${name}': must match ${ACTION_NAME_PATTERN.source}`
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
const parsed = actionSchema.safeParse(value);
|
|
83
|
-
if (!parsed.success) {
|
|
84
|
-
const messages = parsed.error.issues.map((issue) => issue.message);
|
|
85
|
-
throw new Error(`Invalid MCP_REST_ACTIONS entry '${name}':\n${messages.join('\n')}`);
|
|
86
|
-
}
|
|
87
|
-
const url = new URL(parsed.data.url);
|
|
88
|
-
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
89
|
-
throw new Error(`Invalid MCP_REST_ACTIONS entry '${name}': only http(s) urls are allowed`);
|
|
90
|
-
}
|
|
91
|
-
actions[name] = parsed.data;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return actions;
|
|
95
|
-
}
|
package/src/tools/infra/index.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Infra tools — pre-registered REST actions ("actuator rails for HTTP").
|
|
3
|
-
*
|
|
4
|
-
* Design rules:
|
|
5
|
-
* - The healer lives OUTSIDE the patient (2026-07-10 v1.5.5 outage lesson):
|
|
6
|
-
* this server never holds a docker socket or update logic; it only pokes
|
|
7
|
-
* separately-running endpoints (watchtower HTTP API, webhooks) that keep
|
|
8
|
-
* their own privileges.
|
|
9
|
-
* - The model can only NAME an action, never shape a request: exact-match,
|
|
10
|
-
* own-property lookup into operator config. No model string reaches the
|
|
11
|
-
* URL, method, headers, or body. Redirects are refused; responses are
|
|
12
|
-
* truncated before they re-enter the context window.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
16
|
-
import { z } from 'zod';
|
|
17
|
-
|
|
18
|
-
import { toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
19
|
-
import { logger } from '../../utils/logger.js';
|
|
20
|
-
|
|
21
|
-
import type { RestAction } from './actions.js';
|
|
22
|
-
|
|
23
|
-
export type { RestAction } from './actions.js';
|
|
24
|
-
export { parseRestActions } from './actions.js';
|
|
25
|
-
|
|
26
|
-
/** Default per-action timeout; watchtower update cycles pull images. */
|
|
27
|
-
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
28
|
-
/** Cap on response bytes surfaced to the caller (anti context-flooding). */
|
|
29
|
-
const MAX_RESPONSE_CHARS = 500;
|
|
30
|
-
/** Default per-action cooldown (override per action via `cooldownMs`; 0 disables). */
|
|
31
|
-
const DEFAULT_COOLDOWN_MS = 60_000;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Last-fired timestamps per action name — the rate limiter's whole state.
|
|
35
|
-
*
|
|
36
|
-
* MODULE-level on purpose: the HTTP server registers tools per SESSION, so a
|
|
37
|
-
* closure-scoped map would give every new session a fresh cooldown (trivially
|
|
38
|
-
* bypassed by reconnecting). One process = one store. In-memory is correct for
|
|
39
|
-
* this single-process server; the check-and-claim below runs synchronously
|
|
40
|
-
* before any await, so two racing calls cannot both pass (no TOCTOU on the
|
|
41
|
-
* event loop). Resets on restart — acceptable: a restart itself takes longer
|
|
42
|
-
* than the default cooldown.
|
|
43
|
-
*/
|
|
44
|
-
const lastFired = new Map<string, number>();
|
|
45
|
-
|
|
46
|
-
/** Test hook: clear the process-wide cooldown state between test cases. */
|
|
47
|
-
export function resetActionCooldowns(): void {
|
|
48
|
-
lastFired.clear();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function registerInfraTools(server: McpServer, actions: Record<string, RestAction>): number {
|
|
52
|
-
const names = Object.keys(actions);
|
|
53
|
-
if (names.length === 0) {
|
|
54
|
-
return 0;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const catalogue = names
|
|
58
|
-
.map((n) => {
|
|
59
|
-
const description = actions[n].description;
|
|
60
|
-
return description ? `${n} (${description})` : n;
|
|
61
|
-
})
|
|
62
|
-
.join('; ');
|
|
63
|
-
|
|
64
|
-
const inputSchema = z.object({
|
|
65
|
-
action: z
|
|
66
|
-
.string()
|
|
67
|
-
.min(1)
|
|
68
|
-
.max(64, 'action names are at most 64 chars')
|
|
69
|
-
.describe(`Name of the pre-registered action to invoke — one of: ${names.join(', ')}`),
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
server.registerTool(
|
|
73
|
-
'invokeAction',
|
|
74
|
-
{
|
|
75
|
-
description:
|
|
76
|
-
'Invoke a pre-registered REST action by name. Actions are fixed, operator-configured HTTP calls ' +
|
|
77
|
-
'(method, URL, and credentials come from server config — only the name is chosen here). ' +
|
|
78
|
-
`Available actions: ${catalogue}. ` +
|
|
79
|
-
'Actions have real-world effects (deploys, restarts, webhooks) - confirm with the user before applying.',
|
|
80
|
-
inputSchema: inputSchema,
|
|
81
|
-
},
|
|
82
|
-
wrapToolHandler('invokeAction', async ({ action }) => {
|
|
83
|
-
// Own-property, exact-match lookup. The registry has a null prototype,
|
|
84
|
-
// so names like 'constructor' or '__proto__' can never resolve to
|
|
85
|
-
// anything but a genuinely configured action.
|
|
86
|
-
if (!Object.hasOwn(actions, action)) {
|
|
87
|
-
throw new Error(`Unknown action '${action}'. Available actions: ${names.join(', ')}`);
|
|
88
|
-
}
|
|
89
|
-
const spec = actions[action];
|
|
90
|
-
|
|
91
|
-
// Rate limit: check-and-CLAIM synchronously before the fetch, so the
|
|
92
|
-
// slot is consumed even when the call fails (no hammering a dying
|
|
93
|
-
// endpoint), and two racing calls can't both pass.
|
|
94
|
-
const cooldownMs = spec.cooldownMs ?? DEFAULT_COOLDOWN_MS;
|
|
95
|
-
if (cooldownMs > 0) {
|
|
96
|
-
const now = Date.now();
|
|
97
|
-
const elapsed = now - (lastFired.get(action) ?? Number.NEGATIVE_INFINITY);
|
|
98
|
-
if (elapsed < cooldownMs) {
|
|
99
|
-
const retryInS = Math.ceil((cooldownMs - elapsed) / 1000);
|
|
100
|
-
throw new Error(
|
|
101
|
-
`Action '${action}' is cooling down — try again in ~${String(retryInS)}s. ` +
|
|
102
|
-
'Repeated firing of infrastructure actions is rate-limited by design.'
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
lastFired.set(action, now);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
logger.info('Invoking registered REST action', { action, method: spec.method, url: spec.url });
|
|
109
|
-
|
|
110
|
-
const response = await fetch(spec.url, {
|
|
111
|
-
method: spec.method,
|
|
112
|
-
headers: spec.bearerToken ? { Authorization: `Bearer ${spec.bearerToken}` } : undefined,
|
|
113
|
-
// A registered endpoint that suddenly redirects is a surprise, not
|
|
114
|
-
// a convenience — fail closed rather than follow.
|
|
115
|
-
redirect: 'error',
|
|
116
|
-
signal: AbortSignal.timeout(spec.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const body = (await response.text().catch(() => '')).slice(0, MAX_RESPONSE_CHARS);
|
|
120
|
-
|
|
121
|
-
if (!response.ok) {
|
|
122
|
-
throw new Error(`Action '${action}' returned HTTP ${String(response.status)}${body ? `: ${body}` : ''}`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return toToolResult({
|
|
126
|
-
action,
|
|
127
|
-
status: 'completed',
|
|
128
|
-
httpStatus: response.status,
|
|
129
|
-
response: body || undefined,
|
|
130
|
-
});
|
|
131
|
-
},
|
|
132
|
-
Permission.ADMIN
|
|
133
|
-
)
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
logger.info('Infra tools registered', { toolCount: 1, actions: names });
|
|
137
|
-
return 1;
|
|
138
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
5
|
-
import { toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
6
|
-
|
|
7
|
-
export function registerBlockClientTool(server: McpServer, client: OmadaClient): void {
|
|
8
|
-
const inputSchema = z.object({
|
|
9
|
-
clientMac: z.string().min(1, 'clientMac (MAC address) is required'),
|
|
10
|
-
siteId: z.string().min(1).optional(),
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
server.registerTool(
|
|
14
|
-
'omada_blockClient',
|
|
15
|
-
{
|
|
16
|
-
description:
|
|
17
|
-
'Block a client from the network by MAC address. The client is denied all network access until unblocked. ' +
|
|
18
|
-
'Use omada_listClients first to find the exact MAC. This affects real network connectivity - confirm with the user before applying. ' +
|
|
19
|
-
"Caution: a blocked device cannot reconnect, and once the controller stops treating it as current, Omada's API can no longer unblock it. " +
|
|
20
|
-
'For recurring schedules (e.g. a curfew) prefer an Omada time-range access rule on the controller over daily block/unblock calls.',
|
|
21
|
-
inputSchema: inputSchema,
|
|
22
|
-
},
|
|
23
|
-
wrapToolHandler('omada_blockClient', async ({ clientMac, siteId }) =>
|
|
24
|
-
toToolResult(await client.blockClient(clientMac, siteId)),
|
|
25
|
-
Permission.CONTROL
|
|
26
|
-
)
|
|
27
|
-
);
|
|
28
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
5
|
-
import { toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
6
|
-
|
|
7
|
-
export function registerCyclePoePortTool(server: McpServer, client: OmadaClient): void {
|
|
8
|
-
const inputSchema = z.object({
|
|
9
|
-
switchMac: z.string().min(1, 'switchMac (MAC address of the Omada switch) is required'),
|
|
10
|
-
ports: z.array(z.number().int().positive()).min(1, 'at least one port number is required'),
|
|
11
|
-
siteId: z.string().min(1).optional(),
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
server.registerTool(
|
|
15
|
-
'omada_cyclePoePort',
|
|
16
|
-
{
|
|
17
|
-
description:
|
|
18
|
-
'Power-cycle PoE on one or more switch ports — a remote hard-reboot for whatever the ports power ' +
|
|
19
|
-
'(access points, cameras, PoE sensors). The switch briefly cuts and restores power on ONLY the given ' +
|
|
20
|
-
'ports; the switch itself and other ports are unaffected. Use omada_listDevices to find the switch MAC ' +
|
|
21
|
-
'and identify which port powers which device first. The powered device goes down for the cycle - ' +
|
|
22
|
-
'confirm with the user before applying.',
|
|
23
|
-
inputSchema: inputSchema,
|
|
24
|
-
},
|
|
25
|
-
wrapToolHandler('omada_cyclePoePort', async ({ switchMac, ports, siteId }) => {
|
|
26
|
-
await client.cyclePoePorts(switchMac, ports, siteId);
|
|
27
|
-
return toToolResult({ switchMac, ports, status: 'poe-recovery triggered' });
|
|
28
|
-
},
|
|
29
|
-
Permission.CONTROL
|
|
30
|
-
)
|
|
31
|
-
);
|
|
32
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
5
|
-
import { toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
6
|
-
|
|
7
|
-
export function registerDeleteClientTool(server: McpServer, client: OmadaClient): void {
|
|
8
|
-
const inputSchema = z.object({
|
|
9
|
-
clientMac: z.string().min(1, 'clientMac (MAC address) is required'),
|
|
10
|
-
siteId: z.string().min(1).optional(),
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
server.registerTool(
|
|
14
|
-
'omada_deleteClient',
|
|
15
|
-
{
|
|
16
|
-
description:
|
|
17
|
-
"Delete the controller's record of a client by MAC: its name, history and block state. " +
|
|
18
|
-
'Use it to clear a block that omada_unblockClient can no longer reach (the device stays refused by the access points although Omada lists nothing). ' +
|
|
19
|
-
'The device is treated as new when it reconnects. Irreversible for that history - confirm with the user before applying.',
|
|
20
|
-
inputSchema,
|
|
21
|
-
},
|
|
22
|
-
wrapToolHandler('omada_deleteClient', async ({ clientMac, siteId }: z.infer<typeof inputSchema>) =>
|
|
23
|
-
toToolResult(await client.deleteClient(clientMac, siteId)),
|
|
24
|
-
Permission.CONFIGURE
|
|
25
|
-
)
|
|
26
|
-
);
|
|
27
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
4
|
-
import { toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
5
|
-
|
|
6
|
-
export function registerDisableClientRateLimitTool(server: McpServer, client: OmadaClient): void {
|
|
7
|
-
const inputSchema = z.object({
|
|
8
|
-
clientMac: z.string().min(1, 'clientMac (MAC address) is required'),
|
|
9
|
-
siteId: z.string().min(1).optional(),
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
server.registerTool(
|
|
13
|
-
'omada_disableClientRateLimit',
|
|
14
|
-
{
|
|
15
|
-
description: 'Remove bandwidth limits from a client',
|
|
16
|
-
inputSchema: inputSchema,
|
|
17
|
-
},
|
|
18
|
-
wrapToolHandler('omada_disableClientRateLimit', async ({ clientMac, siteId }) =>
|
|
19
|
-
toToolResult(await client.disableClientRateLimit(clientMac, siteId)),
|
|
20
|
-
Permission.CONTROL
|
|
21
|
-
)
|
|
22
|
-
);
|
|
23
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
3
|
-
import { clientIdSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
4
|
-
|
|
5
|
-
export function registerGetClientTool(server: McpServer, client: OmadaClient): void {
|
|
6
|
-
server.registerTool(
|
|
7
|
-
'omada_getClient',
|
|
8
|
-
{
|
|
9
|
-
description: 'Get detailed information about a specific connected client',
|
|
10
|
-
inputSchema: clientIdSchema,
|
|
11
|
-
},
|
|
12
|
-
wrapToolHandler('omada_getClient', async ({ clientId, siteId }) => toToolResult(await client.getClient(clientId, siteId)), Permission.QUERY)
|
|
13
|
-
);
|
|
14
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
3
|
-
import { deviceIdSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
4
|
-
|
|
5
|
-
export function registerGetDeviceTool(server: McpServer, client: OmadaClient): void {
|
|
6
|
-
server.registerTool(
|
|
7
|
-
'omada_getDevice',
|
|
8
|
-
{
|
|
9
|
-
description: 'Get detailed information about a specific network device',
|
|
10
|
-
inputSchema: deviceIdSchema,
|
|
11
|
-
},
|
|
12
|
-
wrapToolHandler('omada_getDevice', async ({ deviceId, siteId }) => toToolResult(await client.getDevice(deviceId, siteId)), Permission.QUERY)
|
|
13
|
-
);
|
|
14
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
3
|
-
import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
4
|
-
|
|
5
|
-
export function registerGetFirewallSettingTool(server: McpServer, client: OmadaClient): void {
|
|
6
|
-
server.registerTool(
|
|
7
|
-
'omada_getFirewallSetting',
|
|
8
|
-
{
|
|
9
|
-
description: 'Get firewall configuration for a site',
|
|
10
|
-
inputSchema: siteInputSchema,
|
|
11
|
-
},
|
|
12
|
-
wrapToolHandler('omada_getFirewallSetting', async ({ siteId }) => toToolResult(await client.getFirewallSetting(siteId)), Permission.QUERY)
|
|
13
|
-
);
|
|
14
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
3
|
-
import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
4
|
-
|
|
5
|
-
export function registerGetInternetInfoTool(server: McpServer, client: OmadaClient): void {
|
|
6
|
-
server.registerTool(
|
|
7
|
-
'omada_getInternetInfo',
|
|
8
|
-
{
|
|
9
|
-
description: 'Get internet connection configuration for a site',
|
|
10
|
-
inputSchema: siteInputSchema,
|
|
11
|
-
},
|
|
12
|
-
wrapToolHandler('omada_getInternetInfo', async ({ siteId }) => toToolResult(await client.getInternetInfo(siteId)), Permission.QUERY)
|
|
13
|
-
);
|
|
14
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
3
|
-
import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
4
|
-
|
|
5
|
-
export function registerGetLanNetworkListTool(server: McpServer, client: OmadaClient): void {
|
|
6
|
-
server.registerTool(
|
|
7
|
-
'omada_getLanNetworkList',
|
|
8
|
-
{
|
|
9
|
-
description: 'Get LAN network configuration list',
|
|
10
|
-
inputSchema: siteInputSchema,
|
|
11
|
-
},
|
|
12
|
-
wrapToolHandler('omada_getLanNetworkList', async ({ siteId }) => toToolResult(await client.getLanNetworkList(siteId)), Permission.QUERY)
|
|
13
|
-
);
|
|
14
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
3
|
-
import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
4
|
-
|
|
5
|
-
export function registerGetLanProfileListTool(server: McpServer, client: OmadaClient): void {
|
|
6
|
-
server.registerTool(
|
|
7
|
-
'omada_getLanProfileList',
|
|
8
|
-
{
|
|
9
|
-
description: 'Get LAN profile configuration list',
|
|
10
|
-
inputSchema: siteInputSchema,
|
|
11
|
-
},
|
|
12
|
-
wrapToolHandler('omada_getLanProfileList', async ({ siteId }) => toToolResult(await client.getLanProfileList(siteId)), Permission.QUERY)
|
|
13
|
-
);
|
|
14
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
5
|
-
import { toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
6
|
-
import { createPaginationSchema } from '../utils/pagination-schema.js';
|
|
7
|
-
|
|
8
|
-
const portForwardingSchema = z.object({
|
|
9
|
-
type: z.enum(['User', 'UPnP']).describe('Port forwarding type: User (manually configured) or UPnP (automatically configured)'),
|
|
10
|
-
siteId: z.string().min(1).describe('Site ID (required)'),
|
|
11
|
-
...createPaginationSchema(10),
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
export function registerGetPortForwardingStatusTool(server: McpServer, client: OmadaClient): void {
|
|
15
|
-
server.registerTool(
|
|
16
|
-
'omada_getPortForwardingStatus',
|
|
17
|
-
{
|
|
18
|
-
description: 'Get port forwarding rules (User or UPnP)',
|
|
19
|
-
inputSchema: portForwardingSchema,
|
|
20
|
-
},
|
|
21
|
-
wrapToolHandler('omada_getPortForwardingStatus', async ({ type, siteId, page = 1, pageSize = 10 }) =>
|
|
22
|
-
toToolResult(await client.getPortForwardingStatus(type, siteId, page, pageSize)),
|
|
23
|
-
Permission.QUERY
|
|
24
|
-
)
|
|
25
|
-
);
|
|
26
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
3
|
-
import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
4
|
-
|
|
5
|
-
export function registerGetRateLimitProfilesTool(server: McpServer, client: OmadaClient): void {
|
|
6
|
-
server.registerTool(
|
|
7
|
-
'omada_getRateLimitProfiles',
|
|
8
|
-
{
|
|
9
|
-
description: 'Get available rate limit profiles for a site',
|
|
10
|
-
inputSchema: siteInputSchema,
|
|
11
|
-
},
|
|
12
|
-
wrapToolHandler('omada_getRateLimitProfiles', async ({ siteId }) => toToolResult(await client.getRateLimitProfiles(siteId)), Permission.QUERY)
|
|
13
|
-
);
|
|
14
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
-
import type { OmadaClient } from '../../omadaClient/index.js';
|
|
3
|
-
import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
|
|
4
|
-
|
|
5
|
-
export function registerGetSiteNtpStatusTool(server: McpServer, client: OmadaClient): void {
|
|
6
|
-
server.registerTool(
|
|
7
|
-
'omada_getSiteNtpStatus',
|
|
8
|
-
{
|
|
9
|
-
description: 'Get the NTP (network time) server configuration and status for a site',
|
|
10
|
-
inputSchema: siteInputSchema,
|
|
11
|
-
},
|
|
12
|
-
wrapToolHandler('omada_getSiteNtpStatus', async ({ siteId }) => toToolResult(await client.getSiteNtpStatus(siteId)), Permission.QUERY)
|
|
13
|
-
);
|
|
14
|
-
}
|