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 @@ - @@ -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```/);