diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index a135d93..2f3b3b9 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,9 +4,30 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -17,7 +38,7 @@
-
+
@@ -29,6 +50,7 @@
+
-
+
@@ -179,7 +201,7 @@
-
+
@@ -196,6 +218,9 @@
+
+
+
@@ -373,7 +398,15 @@
1769582068426
-
+
+
+ 1769597550348
+
+
+
+ 1769597550348
+
+
@@ -384,14 +417,25 @@
-
@@ -416,7 +460,8 @@
-
+
+
diff --git a/.junie/guidelines.md b/.junie/guidelines.md
index da28a59..820f2d4 100644
--- a/.junie/guidelines.md
+++ b/.junie/guidelines.md
@@ -2,6 +2,11 @@
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
- **Runtime**: Node.js (v18+)
- **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`.
- **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/`.
+
+### Git Standards
+- **Commit Messages:** Use [Conventional Commits](https://www.conventionalcommits.org/) (e.g., `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `style:`).
diff --git a/README.md b/README.md
index 71b50a7..e6d55af 100644
--- a/README.md
+++ b/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.
* **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.
+* **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.
+## 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
### Prerequisites
@@ -117,126 +130,6 @@ If you prefer to build the image yourself using the provided `Dockerfile`:
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)
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
-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**:
- * [Query All Hosts](docs/sample_all_hosts_query.graphql)
- * [Import Hosts](docs/sample_import_hosts_mutation.graphql)
+ * [Query All Hosts](docs/queries/sample_all_hosts_query.graphql)
+ * [Import Hosts](docs/queries/sample_import_hosts_mutation.graphql)
* **Templates**:
- * [Query Templates](docs/sample_templates_query.graphql)
- * [Import Templates](docs/sample_import_templates_mutation.graphql)
- * [Import Distance Tracker Template](docs/sample_import_distance_tracker_template.graphql) (Schema Extension Example)
- * [Delete Templates](docs/sample_delete_templates_mutation.graphql)
+ * [Query Templates](docs/queries/sample_templates_query.graphql)
+ * [Import Templates](docs/queries/sample_import_templates_mutation.graphql)
+ * [Import Distance Tracker Template](docs/queries/sample_import_distance_tracker_template.graphql) (Schema Extension Example)
+ * [Delete Templates](docs/queries/sample_delete_templates_mutation.graphql)
* **Template Groups**:
- * [Import Host Template Groups](docs/sample_import_host_template_groups_mutation.graphql)
- * [Import Permissions Template Groups](docs/sample_import_permissions_template_groups_mutation.graphql)
- * [Delete Template Groups](docs/sample_delete_template_groups_mutation.graphql)
+ * [Import Host Template Groups](docs/queries/sample_import_host_template_groups_mutation.graphql)
+ * [Import Permissions Template Groups](docs/queries/sample_import_permissions_template_groups_mutation.graphql)
+ * [Delete Template Groups](docs/queries/sample_delete_template_groups_mutation.graphql)
* **User Rights**:
- * [Export User Rights](docs/sample_export_user_rights_query.graphql)
- * [Import User Rights](docs/sample_import_user_rights_mutation.graphql)
+ * [Export User Rights](docs/queries/sample_export_user_rights_query.graphql)
+ * [Import User Rights](docs/queries/sample_import_user_rights_mutation.graphql)
## License
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..1b7d32e
--- /dev/null
+++ b/docker-compose.yml
@@ -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
diff --git a/docs/howtos/README.md b/docs/howtos/README.md
new file mode 100644
index 0000000..978d1fb
--- /dev/null
+++ b/docs/howtos/README.md
@@ -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.
diff --git a/docs/howtos/hierarchical_data_mapping.md b/docs/howtos/hierarchical_data_mapping.md
new file mode 100644
index 0000000..c722652
--- /dev/null
+++ b/docs/howtos/hierarchical_data_mapping.md
@@ -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.
diff --git a/docs/howtos/mcp.md b/docs/howtos/mcp.md
new file mode 100644
index 0000000..6943e5d
--- /dev/null
+++ b/docs/howtos/mcp.md
@@ -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.
diff --git a/docs/howtos/permissions.md b/docs/howtos/permissions.md
new file mode 100644
index 0000000..d521dc0
--- /dev/null
+++ b/docs/howtos/permissions.md
@@ -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.
diff --git a/docs/howtos/schema.md b/docs/howtos/schema.md
new file mode 100644
index 0000000..838eb97
--- /dev/null
+++ b/docs/howtos/schema.md
@@ -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
diff --git a/docs/howtos/tags.md b/docs/howtos/tags.md
new file mode 100644
index 0000000..6b8e9a3
--- /dev/null
+++ b/docs/howtos/tags.md
@@ -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.
diff --git a/docs/sample_all_devices_query.graphql b/docs/queries/sample_all_devices_query.graphql
similarity index 100%
rename from docs/sample_all_devices_query.graphql
rename to docs/queries/sample_all_devices_query.graphql
diff --git a/docs/sample_all_host_groups_query.graphql b/docs/queries/sample_all_host_groups_query.graphql
similarity index 100%
rename from docs/sample_all_host_groups_query.graphql
rename to docs/queries/sample_all_host_groups_query.graphql
diff --git a/docs/sample_all_hosts_query.graphql b/docs/queries/sample_all_hosts_query.graphql
similarity index 100%
rename from docs/sample_all_hosts_query.graphql
rename to docs/queries/sample_all_hosts_query.graphql
diff --git a/docs/sample_all_template_groups_query.graphql b/docs/queries/sample_all_template_groups_query.graphql
similarity index 100%
rename from docs/sample_all_template_groups_query.graphql
rename to docs/queries/sample_all_template_groups_query.graphql
diff --git a/docs/sample_delete_template_groups_mutation.graphql b/docs/queries/sample_delete_template_groups_mutation.graphql
similarity index 100%
rename from docs/sample_delete_template_groups_mutation.graphql
rename to docs/queries/sample_delete_template_groups_mutation.graphql
diff --git a/docs/sample_delete_templates_mutation.graphql b/docs/queries/sample_delete_templates_mutation.graphql
similarity index 100%
rename from docs/sample_delete_templates_mutation.graphql
rename to docs/queries/sample_delete_templates_mutation.graphql
diff --git a/docs/sample_export_user_rights_query.graphql b/docs/queries/sample_export_user_rights_query.graphql
similarity index 100%
rename from docs/sample_export_user_rights_query.graphql
rename to docs/queries/sample_export_user_rights_query.graphql
diff --git a/docs/sample_import_distance_tracker_template.graphql b/docs/queries/sample_import_distance_tracker_template.graphql
similarity index 100%
rename from docs/sample_import_distance_tracker_template.graphql
rename to docs/queries/sample_import_distance_tracker_template.graphql
diff --git a/docs/sample_import_host_groups_mutation.graphql b/docs/queries/sample_import_host_groups_mutation.graphql
similarity index 100%
rename from docs/sample_import_host_groups_mutation.graphql
rename to docs/queries/sample_import_host_groups_mutation.graphql
diff --git a/docs/sample_import_host_template_groups_mutation.graphql b/docs/queries/sample_import_host_template_groups_mutation.graphql
similarity index 100%
rename from docs/sample_import_host_template_groups_mutation.graphql
rename to docs/queries/sample_import_host_template_groups_mutation.graphql
diff --git a/docs/sample_import_hosts_mutation.graphql b/docs/queries/sample_import_hosts_mutation.graphql
similarity index 100%
rename from docs/sample_import_hosts_mutation.graphql
rename to docs/queries/sample_import_hosts_mutation.graphql
diff --git a/docs/sample_import_permissions_template_groups_mutation.graphql b/docs/queries/sample_import_permissions_template_groups_mutation.graphql
similarity index 100%
rename from docs/sample_import_permissions_template_groups_mutation.graphql
rename to docs/queries/sample_import_permissions_template_groups_mutation.graphql
diff --git a/docs/sample_import_templates_mutation.graphql b/docs/queries/sample_import_templates_mutation.graphql
similarity index 100%
rename from docs/sample_import_templates_mutation.graphql
rename to docs/queries/sample_import_templates_mutation.graphql
diff --git a/docs/sample_import_user_rights_mutation.graphql b/docs/queries/sample_import_user_rights_mutation.graphql
similarity index 100%
rename from docs/sample_import_user_rights_mutation.graphql
rename to docs/queries/sample_import_user_rights_mutation.graphql
diff --git a/docs/sample_templates_query.graphql b/docs/queries/sample_templates_query.graphql
similarity index 100%
rename from docs/sample_templates_query.graphql
rename to docs/queries/sample_templates_query.graphql
diff --git a/mcp-config.yaml b/mcp-config.yaml
new file mode 100644
index 0000000..389a203
--- /dev/null
+++ b/mcp-config.yaml
@@ -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
diff --git a/mcp/operations/apiVersion.graphql b/mcp/operations/apiVersion.graphql
new file mode 100644
index 0000000..d46a89a
--- /dev/null
+++ b/mcp/operations/apiVersion.graphql
@@ -0,0 +1,3 @@
+query GetApiVersion {
+ apiVersion
+}
diff --git a/mcp/operations/createHost.graphql b/mcp/operations/createHost.graphql
new file mode 100644
index 0000000..c078a0f
--- /dev/null
+++ b/mcp/operations/createHost.graphql
@@ -0,0 +1,6 @@
+mutation CreateHost($host: String!, $hostgroupids: [Int!]!, $templateids: [Int!]) {
+ createHost(host: $host, hostgroupids: $hostgroupids, templateids: $templateids) {
+ hostids
+ error
+ }
+}
diff --git a/src/api/resolvers.ts b/src/api/resolvers.ts
index e55c45a..ed83dc2 100644
--- a/src/api/resolvers.ts
+++ b/src/api/resolvers.ts
@@ -4,14 +4,15 @@ import {
DeviceStatus,
Host,
MutationCreateHostArgs,
+ MutationDeleteTemplateGroupsArgs,
+ MutationDeleteTemplatesArgs,
MutationImportHostGroupsArgs,
MutationImportHostsArgs,
MutationImportTemplateGroupsArgs,
MutationImportTemplatesArgs,
- MutationDeleteTemplatesArgs,
- MutationDeleteTemplateGroupsArgs,
MutationImportUserRightsArgs,
- Permission, QueryAllDevicesArgs,
+ Permission,
+ QueryAllDevicesArgs,
QueryAllHostGroupsArgs,
QueryAllHostsArgs,
QueryExportHostValueHistoryArgs,
@@ -31,7 +32,8 @@ import {logger} from "../logging/logger.js";
import {ParsedArgs, ZabbixRequest} from "../datasources/zabbix-request.js";
import {
ZabbixCreateHostRequest,
- ZabbixQueryDevices, ZabbixQueryDevicesArgs,
+ ZabbixQueryDevices,
+ ZabbixQueryDevicesArgs,
ZabbixQueryHostsRequestWithItemsAndInventory,
} from "../datasources/zabbix-hosts.js";
import {ZabbixQueryHostgroupsParams, ZabbixQueryHostgroupsRequest} from "../datasources/zabbix-hostgroups.js";
@@ -46,15 +48,8 @@ import {
ZabbixImportUserRolesRequest,
ZabbixQueryUserRolesRequest
} from "../datasources/zabbix-userroles.js";
-import {
- ZabbixCreateItemRequest,
- ZabbixCreateTemplateGroupRequest,
- ZabbixCreateTemplateRequest,
- ZabbixQueryItemRequest,
- ZabbixQueryTemplateGroupRequest,
- ZabbixQueryTemplatesRequest
-} from "../datasources/zabbix-templates.js";
-import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI} from "../datasources/zabbix-api.js";
+import {ZabbixQueryTemplateGroupRequest, ZabbixQueryTemplatesRequest} from "../datasources/zabbix-templates.js";
+import {zabbixAPI} from "../datasources/zabbix-api.js";
import {GraphQLInterfaceType, GraphQLList} from "graphql/type/index.js";
import {isDevice} from "./resolver_helpers.js";
import {ZabbixPermissionsHelper} from "../datasources/zabbix-permissions.js";
diff --git a/src/datasources/zabbix-hostgroups.ts b/src/datasources/zabbix-hostgroups.ts
index 03d0664..65e9c6d 100644
--- a/src/datasources/zabbix-hostgroups.ts
+++ b/src/datasources/zabbix-hostgroups.ts
@@ -16,7 +16,7 @@ export interface CreateHostGroupResult {
const hostGroupReadWritePermissions = {
permissions: [
{
- objectName: "Hostgroup/ConstructionSite",
+ objectName: "Hostgroup",
permission: Permission.ReadWrite
}]
}
diff --git a/src/test/host_integration.test.ts b/src/test/host_integration.test.ts
index 963df37..b7be340 100644
--- a/src/test/host_integration.test.ts
+++ b/src/test/host_integration.test.ts
@@ -27,7 +27,7 @@ describe("Host Integration Tests", () => {
});
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 variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
@@ -51,7 +51,7 @@ describe("Host Integration Tests", () => {
});
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 variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
diff --git a/src/test/template_integration.test.ts b/src/test/template_integration.test.ts
index 58b2571..22858bb 100644
--- a/src/test/template_integration.test.ts
+++ b/src/test/template_integration.test.ts
@@ -24,7 +24,7 @@ describe("Template Integration Tests", () => {
});
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
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
@@ -65,7 +65,7 @@ describe("Template Integration Tests", () => {
test("Import and Export templates comparison", async () => {
// 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 importVariables = JSON.parse(importSample.match(/```json\n([\s\S]*?)\n```/)![1]);
@@ -84,7 +84,7 @@ describe("Template Integration Tests", () => {
});
// 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 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 () => {
// 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 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);
// 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 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);
// 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 queryVariables = JSON.parse(querySample.match(/```json\n([\s\S]*?)\n```/)![1]);
diff --git a/src/test/user_rights_integration.test.ts b/src/test/user_rights_integration.test.ts
index afb9663..a7e401c 100644
--- a/src/test/user_rights_integration.test.ts
+++ b/src/test/user_rights_integration.test.ts
@@ -26,7 +26,7 @@ describe("User Rights Integration Tests", () => {
});
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 variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);