chore: add MCP integration and refactor documentation into modular how-to guides
- Moved GraphQL query samples into a new `docs/queries` directory for better organization. - Added new queries and mutations, including `createHost.graphql` and `GetApiVersion.graphql`. - Introduced `mcp-config.yaml` and updated `docker-compose.yml` for MCP integration. - Updated IntelliJ `.idea/workspace.xml` settings to reflect project changes. - Added new how-to guides (`docs/howtos`) for permissions, tags, MCP integration, and schema usage. - Enhanced tests by updating file paths and improving sample data locations. - Refined permissions and host group structures in `zabbix-hostgroups.ts` and `resolvers.ts`.
This commit is contained in:
parent
2a82fe6cf2
commit
4ec61ffba1
33 changed files with 439 additions and 165 deletions
65
.idea/workspace.xml
generated
65
.idea/workspace.xml
generated
|
|
@ -4,9 +4,30 @@
|
||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="d7a71994-2699-4ae4-9fd2-ee13b7f33d35" name="Changes" comment="chore: add tests for schema and API config mocking - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - Optimized imports on all files and include this within a new .junie/guidelines.md file">
|
<list default="true" id="d7a71994-2699-4ae4-9fd2-ee13b7f33d35" name="Changes" comment="chore: update `.idea/workspace.xml` and project guidelines - Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. - Renamed and expanded `.junie/guidelines.md` to reflect updated best practices, project structure, and development workflows.">
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.junie/guidelines.md" beforeDir="false" afterPath="$PROJECT_DIR$/.junie/guidelines.md" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.junie/guidelines.md" beforeDir="false" afterPath="$PROJECT_DIR$/.junie/guidelines.md" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_all_devices_query.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_all_host_groups_query.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_all_hosts_query.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_all_template_groups_query.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_delete_template_groups_mutation.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_delete_templates_mutation.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_export_user_rights_query.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_import_distance_tracker_template.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_import_host_groups_mutation.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_import_host_template_groups_mutation.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_import_hosts_mutation.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_import_permissions_template_groups_mutation.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_import_templates_mutation.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_import_user_rights_mutation.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/docs/sample_templates_query.graphql" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/api/resolvers.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/api/resolvers.ts" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/datasources/zabbix-hostgroups.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/datasources/zabbix-hostgroups.ts" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/test/host_integration.test.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/host_integration.test.ts" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/test/template_integration.test.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/template_integration.test.ts" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/test/user_rights_integration.test.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/user_rights_integration.test.ts" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
|
@ -17,7 +38,7 @@
|
||||||
<execution />
|
<execution />
|
||||||
</component>
|
</component>
|
||||||
<component name="EmbeddingIndexingInfo">
|
<component name="EmbeddingIndexingInfo">
|
||||||
<option name="cachedIndexableFilesCount" value="70" />
|
<option name="cachedIndexableFilesCount" value="106" />
|
||||||
<option name="fileBasedEmbeddingIndicesEnabled" value="true" />
|
<option name="fileBasedEmbeddingIndicesEnabled" value="true" />
|
||||||
</component>
|
</component>
|
||||||
<component name="Git.Settings">
|
<component name="Git.Settings">
|
||||||
|
|
@ -29,6 +50,7 @@
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
<option name="RESET_MODE" value="HARD" />
|
<option name="RESET_MODE" value="HARD" />
|
||||||
<option name="SWAP_SIDES_IN_COMPARE_BRANCHES" value="true" />
|
<option name="SWAP_SIDES_IN_COMPARE_BRANCHES" value="true" />
|
||||||
|
<option name="UPDATE_TYPE" value="REBASE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="GitRewordedCommitMessages">
|
<component name="GitRewordedCommitMessages">
|
||||||
<option name="commitMessagesMapping">
|
<option name="commitMessagesMapping">
|
||||||
|
|
@ -90,8 +112,8 @@
|
||||||
"npm.copy-schema.executor": "Run",
|
"npm.copy-schema.executor": "Run",
|
||||||
"npm.prod.executor": "Run",
|
"npm.prod.executor": "Run",
|
||||||
"npm.test.executor": "Run",
|
"npm.test.executor": "Run",
|
||||||
"settings.editor.selected.configurable": "ml.llm.LLMConfigurable",
|
"settings.editor.selected.configurable": "junie.mcp",
|
||||||
"settings.editor.splitter.proportion": "0.3839406",
|
"settings.editor.splitter.proportion": "0.28812414",
|
||||||
"to.speed.mode.migration.done": "true",
|
"to.speed.mode.migration.done": "true",
|
||||||
"ts.external.directory.path": "\\\\wsl.localhost\\Ubuntu\\home\\ahilbig\\git\\vcr\\zabbix-graphql-api\\node_modules\\typescript\\lib",
|
"ts.external.directory.path": "\\\\wsl.localhost\\Ubuntu\\home\\ahilbig\\git\\vcr\\zabbix-graphql-api\\node_modules\\typescript\\lib",
|
||||||
"vue.rearranger.settings.migration": "true"
|
"vue.rearranger.settings.migration": "true"
|
||||||
|
|
@ -116,7 +138,7 @@
|
||||||
<recent name="\\wsl.localhost\Ubuntu\home\ahilbig\git\vcr\zabbix-graphql-api\schema" />
|
<recent name="\\wsl.localhost\Ubuntu\home\ahilbig\git\vcr\zabbix-graphql-api\schema" />
|
||||||
</key>
|
</key>
|
||||||
</component>
|
</component>
|
||||||
<component name="RunManager" selected="npm.test">
|
<component name="RunManager" selected="Node.js.index.ts">
|
||||||
<configuration name="copy-schema" type="js.build_tools.npm" temporary="true" nameIsGenerated="true">
|
<configuration name="copy-schema" type="js.build_tools.npm" temporary="true" nameIsGenerated="true">
|
||||||
<package-json value="$PROJECT_DIR$/package.json" />
|
<package-json value="$PROJECT_DIR$/package.json" />
|
||||||
<command value="run" />
|
<command value="run" />
|
||||||
|
|
@ -179,7 +201,7 @@
|
||||||
<component name="SharedIndexes">
|
<component name="SharedIndexes">
|
||||||
<attachedChunks>
|
<attachedChunks>
|
||||||
<set>
|
<set>
|
||||||
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-WS-253.29346.242" />
|
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-WS-253.30387.83" />
|
||||||
</set>
|
</set>
|
||||||
</attachedChunks>
|
</attachedChunks>
|
||||||
</component>
|
</component>
|
||||||
|
|
@ -196,6 +218,9 @@
|
||||||
<workItem from="1768913192173" duration="14627000" />
|
<workItem from="1768913192173" duration="14627000" />
|
||||||
<workItem from="1769095609607" duration="1390000" />
|
<workItem from="1769095609607" duration="1390000" />
|
||||||
<workItem from="1769256682556" duration="8928000" />
|
<workItem from="1769256682556" duration="8928000" />
|
||||||
|
<workItem from="1769699975260" duration="75000" />
|
||||||
|
<workItem from="1769700092648" duration="5212000" />
|
||||||
|
<workItem from="1769724930397" duration="3476000" />
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00001" summary="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment">
|
<task id="LOCAL-00001" summary="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
|
|
@ -373,7 +398,15 @@
|
||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1769582068426</updated>
|
<updated>1769582068426</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="23" />
|
<task id="LOCAL-00023" summary="chore: update `.idea/workspace.xml` and project guidelines - Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. - Renamed and expanded `.junie/guidelines.md` to reflect updated best practices, project structure, and development workflows.">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1769597550348</created>
|
||||||
|
<option name="number" value="00023" />
|
||||||
|
<option name="presentableId" value="LOCAL-00023" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1769597550348</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="24" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
|
@ -384,14 +417,25 @@
|
||||||
<map>
|
<map>
|
||||||
<entry key="MAIN">
|
<entry key="MAIN">
|
||||||
<value>
|
<value>
|
||||||
<State />
|
<State>
|
||||||
|
<option name="FILTERS">
|
||||||
|
<map>
|
||||||
|
<entry key="branch">
|
||||||
|
<value>
|
||||||
|
<list>
|
||||||
|
<option value="public/main" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</State>
|
||||||
</value>
|
</value>
|
||||||
</entry>
|
</entry>
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="VcsManagerConfiguration">
|
<component name="VcsManagerConfiguration">
|
||||||
<MESSAGE value="refactor!: Update Node.js version to 24.12.0, enhance GraphQL schema structure, and improve dynamic schema loading logic" />
|
|
||||||
<MESSAGE value="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment" />
|
<MESSAGE value="chore: Update IntelliJ workspace settings and add GitHub Actions workflow for Docker deployment" />
|
||||||
<MESSAGE value="chore: Add missing ".js" extensions to imports and improve Node.js compatibility for dynamic schema loading" />
|
<MESSAGE value="chore: Add missing ".js" extensions to imports and improve Node.js compatibility for dynamic schema loading" />
|
||||||
<MESSAGE value="chore: Update IntelliJ workspace and fix Docker image tag in workflow" />
|
<MESSAGE value="chore: Update IntelliJ workspace and fix Docker image tag in workflow" />
|
||||||
|
|
@ -416,7 +460,8 @@
|
||||||
<MESSAGE value="chore: add default filters for host and host group queries - Introduced `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` constants in the `Config` class. - Updated resolvers to use these defaults when `tag_hostType` or `search_name` arguments are not provided. - Added corresponding tests to verify default behavior in host and host group queries. - Added documentation on overriding 'HOST_GROUP_FILTER_DEFAULT' by explicitly setting the 'search_name' argument in the 'allHostGroups' query. - Explained the usage of the '*' wildcard in 'search_name' with a concrete example for subgroup matching." />
|
<MESSAGE value="chore: add default filters for host and host group queries - Introduced `HOST_TYPE_FILTER_DEFAULT` and `HOST_GROUP_FILTER_DEFAULT` constants in the `Config` class. - Updated resolvers to use these defaults when `tag_hostType` or `search_name` arguments are not provided. - Added corresponding tests to verify default behavior in host and host group queries. - Added documentation on overriding 'HOST_GROUP_FILTER_DEFAULT' by explicitly setting the 'search_name' argument in the 'allHostGroups' query. - Explained the usage of the '*' wildcard in 'search_name' with a concrete example for subgroup matching." />
|
||||||
<MESSAGE value="chore: add tests for schema and API config mocking - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - Optimized imports on all files" />
|
<MESSAGE value="chore: add tests for schema and API config mocking - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - Optimized imports on all files" />
|
||||||
<MESSAGE value="chore: add tests for schema and API config mocking - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - Optimized imports on all files and include this within a new .junie/guidelines.md file" />
|
<MESSAGE value="chore: add tests for schema and API config mocking - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - Optimized imports on all files and include this within a new .junie/guidelines.md file" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="chore: add tests for schema and API config mocking - Added unit tests for schema loader, mocking Config variables and resolvers. - Added unit tests for Zabbix API configuration, verifying constants derived from Config. - Mocked relevant modules and filesystem behaviors to enable isolated testing. - Optimized imports on all files and include this within a new .junie/guidelines.md file" />
|
<MESSAGE value="chore: update `.idea/workspace.xml` and project guidelines - Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. - Renamed and expanded `.junie/guidelines.md` to reflect updated best practices, project structure, and development workflows." />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="chore: update `.idea/workspace.xml` and project guidelines - Refined IntelliJ IDEA settings in `.idea/workspace.xml`, including updates to `ProblemsViewState` and `PropertiesComponent` for project consistency. - Renamed and expanded `.junie/guidelines.md` to reflect updated best practices, project structure, and development workflows." />
|
||||||
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
|
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XDebuggerManager">
|
<component name="XDebuggerManager">
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
This document provides concise information and best practices for developers working on the Zabbix GraphQL API project.
|
This document provides concise information and best practices for developers working on the Zabbix GraphQL API project.
|
||||||
|
|
||||||
|
### Environment
|
||||||
|
- **Operating System:** Windows with WSL + Ubuntu installed.
|
||||||
|
- **Commands:** Always execute Linux commands (e.g., use `ls` instead of `dir`).
|
||||||
|
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
- **Runtime**: Node.js (v18+)
|
- **Runtime**: Node.js (v18+)
|
||||||
- **Language**: TypeScript (ESM)
|
- **Language**: TypeScript (ESM)
|
||||||
|
|
@ -35,3 +40,6 @@ This document provides concise information and best practices for developers wor
|
||||||
- **Modular Datasources**: When adding support for new Zabbix features, create a new datasource class in `src/datasources/` extending `ZabbixRESTDataSource`.
|
- **Modular Datasources**: When adding support for new Zabbix features, create a new datasource class in `src/datasources/` extending `ZabbixRESTDataSource`.
|
||||||
- **Schema Organization**: Place GraphQL SDL files in the `schema/` directory. Use descriptive comments in SDL as they are used for API documentation.
|
- **Schema Organization**: Place GraphQL SDL files in the `schema/` directory. Use descriptive comments in SDL as they are used for API documentation.
|
||||||
- **Testing**: Write reproduction tests for bugs and cover new features with both unit and integration tests in `src/test/`.
|
- **Testing**: Write reproduction tests for bugs and cover new features with both unit and integration tests in `src/test/`.
|
||||||
|
|
||||||
|
### Git Standards
|
||||||
|
- **Commit Messages:** Use [Conventional Commits](https://www.conventionalcommits.org/) (e.g., `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `style:`).
|
||||||
|
|
|
||||||
157
README.md
157
README.md
|
|
@ -25,8 +25,21 @@ Compared to the original Zabbix API, this GraphQL API provides several key enhan
|
||||||
* **Strongly Typed Schema**: Leverages GraphQL's type system for clear API documentation and client-side code generation.
|
* **Strongly Typed Schema**: Leverages GraphQL's type system for clear API documentation and client-side code generation.
|
||||||
* **Dynamic Schema Extensibility**: Easily extend the API with custom schema snippets and dynamic resolvers for specialized device types without modifying the core code.
|
* **Dynamic Schema Extensibility**: Easily extend the API with custom schema snippets and dynamic resolvers for specialized device types without modifying the core code.
|
||||||
* **CI/CD Integration**: Includes a ready-to-use Forgejo/Gitea/GitHub Actions workflow for automated building, testing, and deployment.
|
* **CI/CD Integration**: Includes a ready-to-use Forgejo/Gitea/GitHub Actions workflow for automated building, testing, and deployment.
|
||||||
|
* **MCP Integration**: Native support for the **Model Context Protocol (MCP)**, enabled by GraphQL's introspectable schema, allowing LLMs to seamlessly interact with Zabbix data.
|
||||||
* **Sample Application (VCR)**: Designed to power the **Virtual Control Room**, a professional cockpit for managing thousands of IoT/Edge devices.
|
* **Sample Application (VCR)**: Designed to power the **Virtual Control Room**, a professional cockpit for managing thousands of IoT/Edge devices.
|
||||||
|
|
||||||
|
## How-To Guides
|
||||||
|
|
||||||
|
For detailed information on specific topics, please refer to our how-to guides:
|
||||||
|
|
||||||
|
* [**Schema & Extension Overview**](./docs/howtos/schema.md): Detailed explanation of the schema structure and extension mechanism.
|
||||||
|
* [**Hierarchical Data Mapping**](./docs/howtos/hierarchical_data_mapping.md): How Zabbix items are mapped to nested GraphQL fields.
|
||||||
|
* [**Roles & Permissions**](./docs/howtos/permissions.md): Managing user rights through Zabbix template groups.
|
||||||
|
* [**Zabbix Tags Usage**](./docs/howtos/tags.md): Using tags for classification and metadata.
|
||||||
|
* [**MCP Integration**](./docs/howtos/mcp.md): Connecting LLMs to Zabbix via Model Context Protocol.
|
||||||
|
|
||||||
|
See the [How-To Overview](./docs/howtos/README.md) for a complete list of documentation.
|
||||||
|
|
||||||
## How to Install and Start
|
## How to Install and Start
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
@ -117,126 +130,6 @@ If you prefer to build the image yourself using the provided `Dockerfile`:
|
||||||
zabbix-graphql-api
|
zabbix-graphql-api
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extending the Schema
|
|
||||||
|
|
||||||
The Zabbix GraphQL API is designed to be highly extensible. You can add your own GraphQL schema snippets and have resolvers dynamically created for them.
|
|
||||||
|
|
||||||
### Dynamic Resolvers with `createHierarchicalValueFieldResolver`
|
|
||||||
|
|
||||||
The function `createHierarchicalValueFieldResolver` (found in `src/api/resolver_helpers.ts`) allows for the automatic creation of resolvers that map Zabbix items or tags to a hierarchical GraphQL structure. It uses field names and Zabbix item keys (dot-separated) to automatically resolve nested objects.
|
|
||||||
|
|
||||||
### Zabbix Preconditions for Hierarchical Mapping
|
|
||||||
|
|
||||||
In order for the dynamic resolvers to correctly map Zabbix data to your GraphQL schema, the following preconditions must be met in your Zabbix templates:
|
|
||||||
|
|
||||||
* **Key Naming**: Zabbix item keys (or tags) must match the GraphQL field names.
|
|
||||||
* **Dot Separation**: Use a dot (`.`) as a separator to represent nested object structures. For example, a Zabbix item with the key `state.current.values.temperature` will be automatically mapped to the `temperature` field within the nested structure: `state` -> `current` -> `values` -> `temperature`.
|
|
||||||
* **Type Hinting**: You can guide the type conversion by prepending a type hint and an underscore to the last token of the key:
|
|
||||||
* `json_`: Parses the value as a JSON object (useful for complex types).
|
|
||||||
* `str_`: Forces the value to be treated as a string.
|
|
||||||
* `bool_`: Forces the value to be treated as a boolean.
|
|
||||||
* `float_`: Forces the value to be treated as a number.
|
|
||||||
|
|
||||||
For a complete example of a Zabbix template designed for schema extension, see the [Distance Tracker Import Sample](docs/sample_import_distance_tracker_template.graphql).
|
|
||||||
|
|
||||||
### No-Code Extension via Environment Variables
|
|
||||||
|
|
||||||
You can extend the schema and add resolvers without writing any TypeScript code by using the following environment variables:
|
|
||||||
|
|
||||||
* **`ADDITIONAL_SCHEMAS`**: A comma-separated list of paths to additional `.graphql` files.
|
|
||||||
* **`ADDITIONAL_RESOLVERS`**: A comma-separated list of GraphQL Type names for which dynamic hierarchical resolvers should be created.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
Suppose you have custom device definitions in `schema/extensions/`. You can load them and enable dynamic resolution by setting:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql,./schema/extensions/location_tracker_commons.graphql
|
|
||||||
ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice
|
|
||||||
```
|
|
||||||
|
|
||||||
The API will:
|
|
||||||
1. Load all provided schema files.
|
|
||||||
2. For each type listed in `ADDITIONAL_RESOLVERS`, it will automatically create a resolver that maps Zabbix items (e.g., an item with key `state.current.values.temperature`) to the corresponding GraphQL fields.
|
|
||||||
|
|
||||||
## User Permissions & `hasPermission`
|
|
||||||
|
|
||||||
The Zabbix GraphQL API provides a sophisticated way to manage and check application-level permissions using Zabbix's built-in user group and template group mechanisms.
|
|
||||||
|
|
||||||
### Modeling Permissions with Template Groups
|
|
||||||
|
|
||||||
Permissions can be modeled as **empty template groups** (groups with no templates or hosts attached) organized in a hierarchical structure. By convention, these groups start with a configurable prefix (default: `Permissions/`).
|
|
||||||
|
|
||||||
#### Example Hierarchy:
|
|
||||||
* `Permissions/ConstructionSite`: General access to construction site data.
|
|
||||||
* `Permissions/Automatism`: Access to automation features.
|
|
||||||
* `Permissions/Automatism/Status`: Permission to view automation status.
|
|
||||||
|
|
||||||
### Zabbix Preconditions
|
|
||||||
|
|
||||||
1. **Template Groups**: Create template groups for each permission you want to manage (e.g., `Permissions/App1/FeatureA`).
|
|
||||||
2. **User Groups**: In Zabbix, assign these template groups to Zabbix User Groups with specific permission levels (`READ`, `READ_WRITE`, or `DENY`).
|
|
||||||
3. **Authentication**: The GraphQL API will check the permissions of the authenticated user (via token or session cookie) against these Zabbix assignments.
|
|
||||||
|
|
||||||
### Using `hasPermission` and `userPermissions`
|
|
||||||
|
|
||||||
The API provides two main queries for permission checking:
|
|
||||||
|
|
||||||
* **`userPermissions`**: Returns a list of all permissions assigned to the current user.
|
|
||||||
* **`hasPermissions`**: Checks if the user has a specific set of required permissions (e.g., "Does the user have `READ_WRITE` access to `Automatism/Status`?").
|
|
||||||
|
|
||||||
This allows for fine-grained access control in your frontend or external applications, using Zabbix as the central authorization authority.
|
|
||||||
|
|
||||||
For a complete example of how to import these permission groups, see the [Permissions Template Groups Import Sample](docs/sample_import_permissions_template_groups_mutation.graphql).
|
|
||||||
|
|
||||||
## Host Classification & Filtering
|
|
||||||
|
|
||||||
The API leverages Zabbix tags to classify hosts and devices, enabling efficient filtering and multi-tenancy support.
|
|
||||||
|
|
||||||
### The `hostType` Tag
|
|
||||||
|
|
||||||
The `hostType` tag is used to categorize hosts and templates. This allows the API to provide default filters for specific application domains or device categories.
|
|
||||||
|
|
||||||
#### How to set the Host Type in Zabbix:
|
|
||||||
|
|
||||||
To classify a host or a template, simply add a tag in the Zabbix UI or via the API:
|
|
||||||
* **Tag Name**: `hostType`
|
|
||||||
* **Tag Value**: A string representing the category (e.g., `Roadwork/Devices`, `SmartCity/Sensors`).
|
|
||||||
|
|
||||||
This tag can be defined:
|
|
||||||
1. **Directly on the Host**: Specific to that individual device.
|
|
||||||
2. **On a Template**: All hosts linked to this template will inherit the classification.
|
|
||||||
|
|
||||||
### Default Filtering with `HOST_TYPE_FILTER_DEFAULT`
|
|
||||||
|
|
||||||
By configuring the `HOST_TYPE_FILTER_DEFAULT` environment variable, you can set a global default for the `allHosts` and `allDevices` queries.
|
|
||||||
|
|
||||||
* If `HOST_TYPE_FILTER_DEFAULT=Roadwork/Devices` is set, a query like `allHosts { host }` will only return hosts that have the `hostType` tag set to `Roadwork/Devices`.
|
|
||||||
* This default can always be overridden in the GraphQL query by explicitly passing the `tag_hostType` argument.
|
|
||||||
|
|
||||||
### Search Filtering with `HOST_GROUP_FILTER_DEFAULT`
|
|
||||||
|
|
||||||
The `HOST_GROUP_FILTER_DEFAULT` variable provides a default search pattern for the `allHostGroups` query. This is particularly useful for restricting the visible host group hierarchy to a specific subtree by default.
|
|
||||||
|
|
||||||
#### Overriding the Default Filter
|
|
||||||
|
|
||||||
The default filter can be overridden by explicitly providing the `search_name` argument in the `allHostGroups` query. When `search_name` is present, the environment variable is ignored.
|
|
||||||
|
|
||||||
#### Using Wildcards
|
|
||||||
|
|
||||||
The `search_name` parameter supports the `*` wildcard (enabled via the Zabbix API's `searchWildcardsEnabled` feature). This allows you to search for all subgroups within a specific path.
|
|
||||||
|
|
||||||
**Example**: To find all subgroups of `Roadwork/Devices/`, use the following query:
|
|
||||||
|
|
||||||
```graphql
|
|
||||||
query {
|
|
||||||
allHostGroups(search_name: "Roadwork/Devices/*") {
|
|
||||||
groupid
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Sample Application: Virtual Control Room (VCR)
|
## Sample Application: Virtual Control Room (VCR)
|
||||||
|
|
||||||
The **Virtual Control Room (VCR)** is a professional cockpit and control center application designed for monitoring and managing large-scale deployments of IoT and Edge devices, such as traffic management systems, roadwork safety equipment, and environmental sensors.
|
The **Virtual Control Room (VCR)** is a professional cockpit and control center application designed for monitoring and managing large-scale deployments of IoT and Edge devices, such as traffic management systems, roadwork safety equipment, and environmental sensors.
|
||||||
|
|
@ -277,23 +170,23 @@ ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice
|
||||||
|
|
||||||
## Usage Samples
|
## Usage Samples
|
||||||
|
|
||||||
The `docs` directory contains several sample GraphQL queries and mutations to help you get started:
|
The `docs/queries` directory contains several sample GraphQL queries and mutations to help you get started:
|
||||||
|
|
||||||
* **Hosts**:
|
* **Hosts**:
|
||||||
* [Query All Hosts](docs/sample_all_hosts_query.graphql)
|
* [Query All Hosts](docs/queries/sample_all_hosts_query.graphql)
|
||||||
* [Import Hosts](docs/sample_import_hosts_mutation.graphql)
|
* [Import Hosts](docs/queries/sample_import_hosts_mutation.graphql)
|
||||||
* **Templates**:
|
* **Templates**:
|
||||||
* [Query Templates](docs/sample_templates_query.graphql)
|
* [Query Templates](docs/queries/sample_templates_query.graphql)
|
||||||
* [Import Templates](docs/sample_import_templates_mutation.graphql)
|
* [Import Templates](docs/queries/sample_import_templates_mutation.graphql)
|
||||||
* [Import Distance Tracker Template](docs/sample_import_distance_tracker_template.graphql) (Schema Extension Example)
|
* [Import Distance Tracker Template](docs/queries/sample_import_distance_tracker_template.graphql) (Schema Extension Example)
|
||||||
* [Delete Templates](docs/sample_delete_templates_mutation.graphql)
|
* [Delete Templates](docs/queries/sample_delete_templates_mutation.graphql)
|
||||||
* **Template Groups**:
|
* **Template Groups**:
|
||||||
* [Import Host Template Groups](docs/sample_import_host_template_groups_mutation.graphql)
|
* [Import Host Template Groups](docs/queries/sample_import_host_template_groups_mutation.graphql)
|
||||||
* [Import Permissions Template Groups](docs/sample_import_permissions_template_groups_mutation.graphql)
|
* [Import Permissions Template Groups](docs/queries/sample_import_permissions_template_groups_mutation.graphql)
|
||||||
* [Delete Template Groups](docs/sample_delete_template_groups_mutation.graphql)
|
* [Delete Template Groups](docs/queries/sample_delete_template_groups_mutation.graphql)
|
||||||
* **User Rights**:
|
* **User Rights**:
|
||||||
* [Export User Rights](docs/sample_export_user_rights_query.graphql)
|
* [Export User Rights](docs/queries/sample_export_user_rights_query.graphql)
|
||||||
* [Import User Rights](docs/sample_import_user_rights_mutation.graphql)
|
* [Import User Rights](docs/queries/sample_import_user_rights_mutation.graphql)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
27
docker-compose.yml
Normal file
27
docker-compose.yml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
services:
|
||||||
|
zabbix-graphql-api:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
args:
|
||||||
|
- API_VERSION=1.0.0
|
||||||
|
ports:
|
||||||
|
- "4001:4000"
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- SCHEMA_PATH=/usr/app/dist/schema/
|
||||||
|
- ZABBIX_AUTH_TOKEN_FOR_REQUESTS=${ZABBIX_AUTH_TOKEN_FOR_REQUESTS}
|
||||||
|
|
||||||
|
apollo-mcp-server:
|
||||||
|
image: ghcr.io/apollographql/apollo-mcp-server:latest
|
||||||
|
ports:
|
||||||
|
- "3000:8000"
|
||||||
|
volumes:
|
||||||
|
- ./mcp-config.yaml:/mcp-config.yaml
|
||||||
|
- ./schema.graphql:/schema.graphql
|
||||||
|
- ./mcp/operations:/mcp/operations
|
||||||
|
command: /mcp-config.yaml
|
||||||
|
environment:
|
||||||
|
- APOLLO_GRAPH_REF=local@main
|
||||||
|
depends_on:
|
||||||
|
- zabbix-graphql-api
|
||||||
24
docs/howtos/README.md
Normal file
24
docs/howtos/README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# How-To Guides
|
||||||
|
|
||||||
|
This directory contains detailed guides on how to use and extend the Zabbix GraphQL API.
|
||||||
|
|
||||||
|
## Available Guides
|
||||||
|
|
||||||
|
### 📊 [Schema and Schema Extension](./schema.md)
|
||||||
|
Learn about the GraphQL schema structure, how Zabbix entities map to GraphQL types, and how to use the dynamic schema extension system.
|
||||||
|
|
||||||
|
### 🗂️ [Hierarchical Data Mapping](./hierarchical_data_mapping.md)
|
||||||
|
Understand how the API automatically maps flat Zabbix item keys into nested GraphQL objects using hierarchical resolvers and type hinting.
|
||||||
|
|
||||||
|
### 🔐 [Roles and Permissions Extension](./permissions.md)
|
||||||
|
Discover how the permission system works, how to define permission levels using Zabbix template groups, and how to query user permissions.
|
||||||
|
|
||||||
|
### 🏷️ [Zabbix Tags Usage](./tags.md)
|
||||||
|
Learn how Zabbix tags are used for device classification, host categorization, and as metadata within the GraphQL API.
|
||||||
|
|
||||||
|
### 🤖 [MCP Integration](./mcp.md)
|
||||||
|
Discover how to integrate the Zabbix GraphQL API with the Model Context Protocol (MCP) to enable LLMs to interact with your Zabbix data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For practical examples of GraphQL operations, check the [Sample Queries](../queries/) directory.
|
||||||
41
docs/howtos/hierarchical_data_mapping.md
Normal file
41
docs/howtos/hierarchical_data_mapping.md
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
## 🗂️ Hierarchical Data Mapping
|
||||||
|
|
||||||
|
The API automatically maps Zabbix items to nested GraphQL objects using the `createHierarchicalValueFieldResolver` function.
|
||||||
|
|
||||||
|
### How it Works
|
||||||
|
|
||||||
|
Zabbix item keys are used to define the structure in GraphQL. A dot (`.`) acts as a separator to represent nested object structures.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
Zabbix item key `state.current.values.temperature` is automatically mapped to:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"state": {
|
||||||
|
"current": {
|
||||||
|
"values": {
|
||||||
|
"temperature": 25.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type Hinting
|
||||||
|
|
||||||
|
You can guide the type conversion by prepending a type hint and an underscore to the last token of the Zabbix item key:
|
||||||
|
|
||||||
|
* `json_`: Parses the value as a JSON object (useful for complex types).
|
||||||
|
* `str_`: Forces the value to be treated as a string.
|
||||||
|
* `bool_`: Forces the value to be treated as a boolean.
|
||||||
|
* `float_`: Forces the value to be treated as a number.
|
||||||
|
|
||||||
|
### Preconditions
|
||||||
|
|
||||||
|
1. **Key Naming**: Zabbix item keys (or tags) must match the GraphQL field names.
|
||||||
|
2. **Dot Separation**: Use dots to represent the desired hierarchy.
|
||||||
|
|
||||||
|
The `createHierarchicalValueFieldResolver` function (found in `../../src/api/resolver_helpers.ts`) dynamically creates these resolvers, eliminating the need for manual resolver definitions for each hierarchical field.
|
||||||
|
|
||||||
|
For more information, see the comments in `../../schema/device_value_commons.graphql` and `../../src/api/resolver_helpers.ts`.
|
||||||
|
|
||||||
|
See `../queries/sample_all_devices_query.graphql` for examples of hierarchical data in query results.
|
||||||
65
docs/howtos/mcp.md
Normal file
65
docs/howtos/mcp.md
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
## 🤖 Model Context Protocol (MCP) Integration
|
||||||
|
|
||||||
|
The Zabbix GraphQL API supports the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/), enabling Large Language Models (LLMs) to interact directly with your Zabbix data through a standardized interface.
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
By leveraging GraphQL, the API provides a strongly-typed and introspectable interface that is ideal for MCP. This allows LLMs to:
|
||||||
|
- Discover available queries and mutations.
|
||||||
|
- Understand the data structures (hosts, items, templates, etc.).
|
||||||
|
- Execute operations to retrieve or modify Zabbix data based on natural language prompts.
|
||||||
|
|
||||||
|
### Running Apollo MCP Server with Docker Compose
|
||||||
|
|
||||||
|
You can start both the Zabbix GraphQL API and the Apollo MCP Server using Docker Compose. This setup uses a local `mcp-config.yaml` and a generated `schema.graphql`.
|
||||||
|
|
||||||
|
1. **Prerequisites**: Ensure you have a `.env` file with the required Zabbix connection details.
|
||||||
|
2. **Generate Schema**: Generate the combined schema file required by the MCP server:
|
||||||
|
```bash
|
||||||
|
cat schema/*.graphql > schema.graphql
|
||||||
|
```
|
||||||
|
3. **Prepare Operations**: Create the operations directory if it doesn't exist:
|
||||||
|
```bash
|
||||||
|
mkdir -p mcp/operations
|
||||||
|
```
|
||||||
|
4. **Start Services**:
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
- Start the `zabbix-graphql-api` on `http://localhost:4001/graphql` (internal port 4000).
|
||||||
|
- Start the `apollo-mcp-server` on `http://localhost:3000/mcp` (mapped from internal port 8000), configured to connect to the local API via `mcp-config.yaml`.
|
||||||
|
|
||||||
|
### Using with Claude Desktop
|
||||||
|
|
||||||
|
To use this integration with Claude Desktop, add the following configuration to your Claude Desktop config file (typically `appflowy.json` or similar depending on OS, but usually `claude_desktop_config.json`):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"zabbix-graphql": {
|
||||||
|
"command": "docker",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"-i",
|
||||||
|
"--rm",
|
||||||
|
"-v", "/path/to/your/project/mcp-config.yaml:/mcp-config.yaml",
|
||||||
|
"-v", "/path/to/your/project/schema.graphql:/schema.graphql",
|
||||||
|
"-v", "/path/to/your/project/mcp/operations:/mcp/operations",
|
||||||
|
"-e", "APOLLO_GRAPH_REF=local@main",
|
||||||
|
"ghcr.io/apollographql/apollo-mcp-server:latest",
|
||||||
|
"/mcp-config.yaml"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: Ensure the `zabbix-graphql-api` is running and accessible. If running locally, you might need to use `host.docker.internal:4001/graphql` in your `mcp-config.yaml` to allow the containerized MCP server to reach your host.
|
||||||
|
|
||||||
|
### Benefits of GraphQL-enabled MCP
|
||||||
|
|
||||||
|
- **Self-Documenting**: The GraphQL schema provides all necessary metadata for the LLM to understand how to use the tools.
|
||||||
|
- **Efficient**: LLMs can request exactly the data they need, reducing token usage and improving response speed.
|
||||||
|
- **Secure**: Uses the same authentication and permission model as the rest of the API.
|
||||||
49
docs/howtos/permissions.md
Normal file
49
docs/howtos/permissions.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
## 🔐 Roles and Permissions Extension
|
||||||
|
|
||||||
|
The API implements a permission system using Zabbix template groups:
|
||||||
|
|
||||||
|
### Permission Template Groups
|
||||||
|
|
||||||
|
- Template groups with prefix `Permissions/` (configurable via `ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX`) are used for permissions
|
||||||
|
- Users gain permissions by being assigned to user groups that have access to these permission template groups
|
||||||
|
|
||||||
|
### Available Permissions
|
||||||
|
|
||||||
|
The system supports three permission levels defined in `../../schema/api_commons.graphql`:
|
||||||
|
|
||||||
|
- `DENY`: Explicitly denies access (supersedes other permissions)
|
||||||
|
- `READ`: Allows viewing/reading access
|
||||||
|
- `READ_WRITE`: Allows both reading and writing (implies READ permission)
|
||||||
|
|
||||||
|
### Permission Object Names
|
||||||
|
|
||||||
|
Permission object names map to Zabbix template group paths: `Permissions/{objectName}`
|
||||||
|
|
||||||
|
### GraphQL Permission Queries
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
# Check if current user has specific permissions
|
||||||
|
query HasPermissions {
|
||||||
|
hasPermissions(permissions: [
|
||||||
|
{ objectName: "hosts", permission: READ },
|
||||||
|
{ objectName: "templates", permission: READ_WRITE }
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get all user permissions
|
||||||
|
query GetUserPermissions {
|
||||||
|
userPermissions(objectNames: ["hosts", "templates"])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setting Up Permissions
|
||||||
|
|
||||||
|
1. Create template groups with the prefix `Permissions/` (e.g., `Permissions/hosts`, `Permissions/templates`)
|
||||||
|
2. Assign these template groups to user groups in Zabbix with appropriate permission levels
|
||||||
|
3. Users in those user groups will inherit the permissions
|
||||||
|
|
||||||
|
### Detailed Permission Usage Examples
|
||||||
|
|
||||||
|
For comprehensive examples of permission usage patterns, see `../../schema/api_commons.graphql` which contains detailed documentation in the `PermissionRequest` input type comments, including real-world examples of how to model permissions for buttons, status controls, and application features.
|
||||||
|
|
||||||
|
See also `../queries/sample_import_permissions_template_groups_mutation.graphql` for examples of importing permission template groups.
|
||||||
62
docs/howtos/schema.md
Normal file
62
docs/howtos/schema.md
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
## 📊 Schema and Schema Extension
|
||||||
|
|
||||||
|
### Main Schema Structure
|
||||||
|
|
||||||
|
The GraphQL schema is located in the `../../schema/` directory and consists of:
|
||||||
|
|
||||||
|
- `queries.graphql` - Query definitions (see detailed documentation in file comments)
|
||||||
|
- `mutations.graphql` - Mutation definitions (see detailed documentation in file comments)
|
||||||
|
- `devices.graphql` - Device-related types (see detailed documentation in file comments)
|
||||||
|
- `zabbix.graphql` - Zabbix-specific types (see detailed documentation in file comments)
|
||||||
|
- `device_value_commons.graphql` - Common value types (see detailed documentation in file comments)
|
||||||
|
- `api_commons.graphql` - Common API types and permission system (see detailed documentation in file comments)
|
||||||
|
- `extensions/` - Custom device type extensions
|
||||||
|
|
||||||
|
For comprehensive understanding of each operation, read the detailed comments in the respective schema files.
|
||||||
|
|
||||||
|
### Zabbix to GraphQL Mapping
|
||||||
|
|
||||||
|
The API maps Zabbix entities to GraphQL types as follows:
|
||||||
|
|
||||||
|
| Zabbix Entity | GraphQL Type | Description |
|
||||||
|
|---------------|--------------|-------------|
|
||||||
|
| Host | `Host` / `Device` | Represents a Zabbix host; `Device` is a specialized `Host` with a `deviceType` tag |
|
||||||
|
| Host Group | `HostGroup` | Represents a Zabbix host group |
|
||||||
|
| Template | `Template` | Represents a Zabbix template |
|
||||||
|
| Template Group | `HostGroup` | Represents a Zabbix template group |
|
||||||
|
| Item | Nested fields in `Device` | Zabbix items become nested fields in the device based on their key names |
|
||||||
|
| Tag | `Tag` | Represents a Zabbix tag associated with a host or template |
|
||||||
|
| Inventory | `Location` | Host inventory information maps to location data |
|
||||||
|
|
||||||
|
### Zabbix Entity Relationships
|
||||||
|
|
||||||
|
- **Host Groups**: Organize hosts and templates hierarchically; represented as `HostGroup` objects in GraphQL
|
||||||
|
- **Templates**: Contain items and other configuration that can be applied to hosts; linked via template groups
|
||||||
|
- **Items**: Individual metrics collected from hosts; automatically mapped to nested GraphQL fields based on their key names
|
||||||
|
- **Tags**: Metadata associated with hosts/templates; used for classification and filtering
|
||||||
|
|
||||||
|
### Location Type Usage
|
||||||
|
|
||||||
|
The `Location` type represents geographical information from Zabbix host inventory:
|
||||||
|
|
||||||
|
- **Fields**: Includes `name`, `location_lat`, `location_lon`, and other inventory attributes
|
||||||
|
- **Usage**: Available through the `locations` query and as part of host/device objects
|
||||||
|
- **Access**: Retrieved via the `getLocations` method in the Zabbix API datasource
|
||||||
|
|
||||||
|
### Dynamic Schema Extension
|
||||||
|
|
||||||
|
Extend the schema without code changes using environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ADDITIONAL_SCHEMAS=./schema/extensions/display_devices.graphql,./schema/extensions/location_tracker_devices.graphql
|
||||||
|
ADDITIONAL_RESOLVERS=SinglePanelDevice,FourPanelDevice,DistanceTrackerDevice
|
||||||
|
```
|
||||||
|
|
||||||
|
This enables runtime schema extension for custom device types without modifying the core code.
|
||||||
|
|
||||||
|
### Sample Operations
|
||||||
|
|
||||||
|
For practical examples of schema usage, see the sample files in the `../queries/` directory:
|
||||||
|
- `../queries/sample_all_devices_query.graphql` - Example of querying all devices
|
||||||
|
- `../queries/sample_import_templates_mutation.graphql` - Example of importing templates
|
||||||
|
- `../queries/sample_import_host_groups_mutation.graphql` - Example of importing host groups
|
||||||
36
docs/howtos/tags.md
Normal file
36
docs/howtos/tags.md
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
## 🏷️ Zabbix Tags Usage
|
||||||
|
|
||||||
|
Zabbix tags are used for:
|
||||||
|
|
||||||
|
- Device classification (`deviceType` tag)
|
||||||
|
- Host categorization (`hostType` tag)
|
||||||
|
- Custom metadata storage
|
||||||
|
- Permission assignment through template groups
|
||||||
|
|
||||||
|
### The `hostType` Tag
|
||||||
|
|
||||||
|
The `hostType` tag is used to categorize hosts and templates. This allows the API to provide default filters for specific application domains or device categories.
|
||||||
|
|
||||||
|
To classify a host or a template, add a tag in Zabbix:
|
||||||
|
* **Tag Name**: `hostType`
|
||||||
|
* **Tag Value**: A string representing the category (e.g., `Roadwork/Devices`, `SmartCity/Sensors`).
|
||||||
|
|
||||||
|
This tag can be defined directly on the host or on a template (where linked hosts will inherit it).
|
||||||
|
|
||||||
|
### Default Filtering with `HOST_TYPE_FILTER_DEFAULT`
|
||||||
|
|
||||||
|
By configuring the `HOST_TYPE_FILTER_DEFAULT` environment variable, you can set a global default for the `allHosts` and `allDevices` queries.
|
||||||
|
|
||||||
|
* If `HOST_TYPE_FILTER_DEFAULT=Roadwork/Devices` is set, `allHosts` will only return hosts with that tag value.
|
||||||
|
* This default can be overridden in the GraphQL query by passing the `tag_hostType` argument.
|
||||||
|
|
||||||
|
### Search Filtering with `HOST_GROUP_FILTER_DEFAULT`
|
||||||
|
|
||||||
|
The `HOST_GROUP_FILTER_DEFAULT` variable provides a default search pattern for the `allHostGroups` query, useful for restricting the visible host group hierarchy.
|
||||||
|
|
||||||
|
* **Overriding**: Providing the `search_name` argument in the `allHostGroups` query overrides this default.
|
||||||
|
* **Wildcards**: The `search_name` parameter supports the `*` wildcard. For example, `Roadwork/Devices/*` finds all subgroups within that path.
|
||||||
|
|
||||||
|
For more information, see the comments in `../../schema/devices.graphql` and `../../schema/zabbix.graphql`.
|
||||||
|
|
||||||
|
See `../queries/sample_all_hosts_query.graphql` and `../queries/sample_all_devices_query.graphql` for examples.
|
||||||
20
mcp-config.yaml
Normal file
20
mcp-config.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
endpoint: http://zabbix-graphql-api:4000/graphql
|
||||||
|
overrides:
|
||||||
|
mutation_mode: all
|
||||||
|
transport:
|
||||||
|
type: streamable_http
|
||||||
|
stateful_mode: false
|
||||||
|
operations:
|
||||||
|
source: local
|
||||||
|
paths:
|
||||||
|
- /mcp/operations
|
||||||
|
schema:
|
||||||
|
source: local
|
||||||
|
path: /schema.graphql
|
||||||
|
introspection:
|
||||||
|
execute:
|
||||||
|
enabled: true
|
||||||
|
introspect:
|
||||||
|
enabled: true
|
||||||
|
search:
|
||||||
|
enabled: true
|
||||||
3
mcp/operations/apiVersion.graphql
Normal file
3
mcp/operations/apiVersion.graphql
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
query GetApiVersion {
|
||||||
|
apiVersion
|
||||||
|
}
|
||||||
6
mcp/operations/createHost.graphql
Normal file
6
mcp/operations/createHost.graphql
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
mutation CreateHost($host: String!, $hostgroupids: [Int!]!, $templateids: [Int!]) {
|
||||||
|
createHost(host: $host, hostgroupids: $hostgroupids, templateids: $templateids) {
|
||||||
|
hostids
|
||||||
|
error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,14 +4,15 @@ import {
|
||||||
DeviceStatus,
|
DeviceStatus,
|
||||||
Host,
|
Host,
|
||||||
MutationCreateHostArgs,
|
MutationCreateHostArgs,
|
||||||
|
MutationDeleteTemplateGroupsArgs,
|
||||||
|
MutationDeleteTemplatesArgs,
|
||||||
MutationImportHostGroupsArgs,
|
MutationImportHostGroupsArgs,
|
||||||
MutationImportHostsArgs,
|
MutationImportHostsArgs,
|
||||||
MutationImportTemplateGroupsArgs,
|
MutationImportTemplateGroupsArgs,
|
||||||
MutationImportTemplatesArgs,
|
MutationImportTemplatesArgs,
|
||||||
MutationDeleteTemplatesArgs,
|
|
||||||
MutationDeleteTemplateGroupsArgs,
|
|
||||||
MutationImportUserRightsArgs,
|
MutationImportUserRightsArgs,
|
||||||
Permission, QueryAllDevicesArgs,
|
Permission,
|
||||||
|
QueryAllDevicesArgs,
|
||||||
QueryAllHostGroupsArgs,
|
QueryAllHostGroupsArgs,
|
||||||
QueryAllHostsArgs,
|
QueryAllHostsArgs,
|
||||||
QueryExportHostValueHistoryArgs,
|
QueryExportHostValueHistoryArgs,
|
||||||
|
|
@ -31,7 +32,8 @@ import {logger} from "../logging/logger.js";
|
||||||
import {ParsedArgs, ZabbixRequest} from "../datasources/zabbix-request.js";
|
import {ParsedArgs, ZabbixRequest} from "../datasources/zabbix-request.js";
|
||||||
import {
|
import {
|
||||||
ZabbixCreateHostRequest,
|
ZabbixCreateHostRequest,
|
||||||
ZabbixQueryDevices, ZabbixQueryDevicesArgs,
|
ZabbixQueryDevices,
|
||||||
|
ZabbixQueryDevicesArgs,
|
||||||
ZabbixQueryHostsRequestWithItemsAndInventory,
|
ZabbixQueryHostsRequestWithItemsAndInventory,
|
||||||
} from "../datasources/zabbix-hosts.js";
|
} from "../datasources/zabbix-hosts.js";
|
||||||
import {ZabbixQueryHostgroupsParams, ZabbixQueryHostgroupsRequest} from "../datasources/zabbix-hostgroups.js";
|
import {ZabbixQueryHostgroupsParams, ZabbixQueryHostgroupsRequest} from "../datasources/zabbix-hostgroups.js";
|
||||||
|
|
@ -46,15 +48,8 @@ import {
|
||||||
ZabbixImportUserRolesRequest,
|
ZabbixImportUserRolesRequest,
|
||||||
ZabbixQueryUserRolesRequest
|
ZabbixQueryUserRolesRequest
|
||||||
} from "../datasources/zabbix-userroles.js";
|
} from "../datasources/zabbix-userroles.js";
|
||||||
import {
|
import {ZabbixQueryTemplateGroupRequest, ZabbixQueryTemplatesRequest} from "../datasources/zabbix-templates.js";
|
||||||
ZabbixCreateItemRequest,
|
import {zabbixAPI} from "../datasources/zabbix-api.js";
|
||||||
ZabbixCreateTemplateGroupRequest,
|
|
||||||
ZabbixCreateTemplateRequest,
|
|
||||||
ZabbixQueryItemRequest,
|
|
||||||
ZabbixQueryTemplateGroupRequest,
|
|
||||||
ZabbixQueryTemplatesRequest
|
|
||||||
} from "../datasources/zabbix-templates.js";
|
|
||||||
import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI} from "../datasources/zabbix-api.js";
|
|
||||||
import {GraphQLInterfaceType, GraphQLList} from "graphql/type/index.js";
|
import {GraphQLInterfaceType, GraphQLList} from "graphql/type/index.js";
|
||||||
import {isDevice} from "./resolver_helpers.js";
|
import {isDevice} from "./resolver_helpers.js";
|
||||||
import {ZabbixPermissionsHelper} from "../datasources/zabbix-permissions.js";
|
import {ZabbixPermissionsHelper} from "../datasources/zabbix-permissions.js";
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export interface CreateHostGroupResult {
|
||||||
const hostGroupReadWritePermissions = {
|
const hostGroupReadWritePermissions = {
|
||||||
permissions: [
|
permissions: [
|
||||||
{
|
{
|
||||||
objectName: "Hostgroup/ConstructionSite",
|
objectName: "Hostgroup",
|
||||||
permission: Permission.ReadWrite
|
permission: Permission.ReadWrite
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ describe("Host Integration Tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Query allHosts using sample", async () => {
|
test("Query allHosts using sample", async () => {
|
||||||
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_all_hosts_query.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_all_hosts_query.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
||||||
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
|
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@ describe("Host Integration Tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Import hosts using sample", async () => {
|
test("Import hosts using sample", async () => {
|
||||||
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_import_hosts_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_hosts_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
||||||
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
|
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ describe("Template Integration Tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Import templates using sample query and variables", async () => {
|
test("Import templates using sample query and variables", async () => {
|
||||||
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_import_templates_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_templates_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
|
|
||||||
// Extract mutation and variables from the doc file
|
// Extract mutation and variables from the doc file
|
||||||
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
||||||
|
|
@ -65,7 +65,7 @@ describe("Template Integration Tests", () => {
|
||||||
|
|
||||||
test("Import and Export templates comparison", async () => {
|
test("Import and Export templates comparison", async () => {
|
||||||
// 1. Import
|
// 1. Import
|
||||||
const importSample = readFileSync(join(process.cwd(), 'docs', 'sample_import_templates_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
const importSample = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_templates_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
const importMutation = importSample.match(/```graphql\n([\s\S]*?)\n```/)![1];
|
const importMutation = importSample.match(/```graphql\n([\s\S]*?)\n```/)![1];
|
||||||
const importVariables = JSON.parse(importSample.match(/```json\n([\s\S]*?)\n```/)![1]);
|
const importVariables = JSON.parse(importSample.match(/```json\n([\s\S]*?)\n```/)![1]);
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ describe("Template Integration Tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Export (Query)
|
// 2. Export (Query)
|
||||||
const querySample = readFileSync(join(process.cwd(), 'docs', 'sample_templates_query.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
const querySample = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_templates_query.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
const query = querySample.match(/```graphql\n([\s\S]*?)\n```/)![1];
|
const query = querySample.match(/```graphql\n([\s\S]*?)\n```/)![1];
|
||||||
const queryVariables = JSON.parse(querySample.match(/```json\n([\s\S]*?)\n```/)![1]);
|
const queryVariables = JSON.parse(querySample.match(/```json\n([\s\S]*?)\n```/)![1]);
|
||||||
|
|
||||||
|
|
@ -137,7 +137,7 @@ describe("Template Integration Tests", () => {
|
||||||
|
|
||||||
test("Import and Export template groups comparison", async () => {
|
test("Import and Export template groups comparison", async () => {
|
||||||
// 1. Import (Host Template Groups)
|
// 1. Import (Host Template Groups)
|
||||||
const importSample = readFileSync(join(process.cwd(), 'docs', 'sample_import_host_template_groups_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
const importSample = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_host_template_groups_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
const importMutation = importSample.match(/```graphql\n([\s\S]*?)\n```/)![1];
|
const importMutation = importSample.match(/```graphql\n([\s\S]*?)\n```/)![1];
|
||||||
const importVariables = JSON.parse(importSample.match(/```json\n([\s\S]*?)\n```/)![1]);
|
const importVariables = JSON.parse(importSample.match(/```json\n([\s\S]*?)\n```/)![1]);
|
||||||
|
|
||||||
|
|
@ -164,7 +164,7 @@ describe("Template Integration Tests", () => {
|
||||||
expect(importResult.data.importTemplateGroups).toHaveLength(importVariables.templateGroups.length);
|
expect(importResult.data.importTemplateGroups).toHaveLength(importVariables.templateGroups.length);
|
||||||
|
|
||||||
// 2. Import (Permissions Template Groups)
|
// 2. Import (Permissions Template Groups)
|
||||||
const permImportSample = readFileSync(join(process.cwd(), 'docs', 'sample_import_permissions_template_groups_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
const permImportSample = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_permissions_template_groups_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
const permImportMutation = permImportSample.match(/```graphql\n([\s\S]*?)\n```/)![1];
|
const permImportMutation = permImportSample.match(/```graphql\n([\s\S]*?)\n```/)![1];
|
||||||
const permImportVariables = JSON.parse(permImportSample.match(/```json\n([\s\S]*?)\n```/)![1]);
|
const permImportVariables = JSON.parse(permImportSample.match(/```json\n([\s\S]*?)\n```/)![1]);
|
||||||
|
|
||||||
|
|
@ -189,7 +189,7 @@ describe("Template Integration Tests", () => {
|
||||||
expect(permImportResult.data.importTemplateGroups).toHaveLength(permImportVariables.templateGroups.length);
|
expect(permImportResult.data.importTemplateGroups).toHaveLength(permImportVariables.templateGroups.length);
|
||||||
|
|
||||||
// 3. Export (Query)
|
// 3. Export (Query)
|
||||||
const querySample = readFileSync(join(process.cwd(), 'docs', 'sample_all_template_groups_query.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
const querySample = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_all_template_groups_query.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
const query = querySample.match(/```graphql\n([\s\S]*?)\n```/)![1];
|
const query = querySample.match(/```graphql\n([\s\S]*?)\n```/)![1];
|
||||||
const queryVariables = JSON.parse(querySample.match(/```json\n([\s\S]*?)\n```/)![1]);
|
const queryVariables = JSON.parse(querySample.match(/```json\n([\s\S]*?)\n```/)![1]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ describe("User Rights Integration Tests", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Import user rights using sample", async () => {
|
test("Import user rights using sample", async () => {
|
||||||
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_import_user_rights_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'queries', 'sample_import_user_rights_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
||||||
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
|
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue