# Tag groups

## Search Tag Groups

`TOOL discourse_search_tag_groups`

Public visibility-filtered tag-group search. q and names combine with AND semantics; upstream treats % and _ in q as SQL LIKE wildcards. Results intentionally omit group IDs, parent tags, and permissions and are not authoritative staff inventory. Exact case-insensitive group name is the supported correlation key. Always sends an explicit limit and reports possible truncation.

Authentication: no security requirement declared for this operation.

Parameters:

- `q` (argument, string, optional)
  Type: string
  
  minLength: 1
  
  maxLength: 250

- `names` (argument, Array<string>, optional)
  Type: Array<string>
  
  minItems: 1
  
  maxItems: 100
  
  Items:
    Type: string
    
    minLength: 1
    
    maxLength: 100

- `limit` (argument, integer, optional)
  Type: integer
  
  default: 100
  
  minimum: 1
  
  maximum: 1000

Returns:

Type: object

- `results` (required)
  Type: Array<object>
  
  Items:
    Type: object
    
    - `name` (required)
      Type: string
      
      minLength: 1
    
    - `tags` (required)
      Type: Array<object>
      
      Items:
        Type: object
        
        - `id` (required)
          Type: integer
          
          exclusiveMinimum: 0
        
        - `name` (required)
          Type: string
          
          minLength: 1
        
        - `slug` (required)
          Type: string
          
          minLength: 1
        
        Additional properties: true
    
    Additional properties: true

- `meta` (required)
  Type: object
  
  - `limit` (required)
    Type: integer
    
    minimum: 1
    
    maximum: 1000
  
  - `returned` (required)
    Type: integer
    
    minimum: 0
  
  - `truncated` (required)
    Type: boolean
  
  Additional properties: false

Additional properties: false

Declared MCP annotations:

```json
{
  "readOnlyHint": true,
  "destructiveHint": false,
  "idempotentHint": true,
  "openWorldHint": true
}
```

## Examples

### JSON-RPC

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "discourse_search_tag_groups",
    "arguments": {
        "q": "string",
        "names": [
          "string"
        ],
        "limit": 100
      }
  }
}
```

### TypeScript

```typescript
const result = await client.callTool({
  "name": "discourse_search_tag_groups",
  "arguments": {
    "q": "string",
    "names": [
      "string"
    ],
    "limit": 100
  }
});
```

### Python

```python
result = await session.call_tool("discourse_search_tag_groups", arguments={
  "q": "string",
  "names": [
    "string"
  ],
  "limit": 100
})
```


## List Tag Groups

`TOOL discourse_list_tag_groups`

Authoritative unpaginated tag-group inventory ordered by name. Requires a locally configured API credential and upstream staff authority; Discourse returns a privacy-preserving 404 to non-staff. Staff reads can work when tagging is disabled. Returns canonical tags, parent, one-per-topic, explicit permissions entries ({group_id, access}; group_id 0 is everyone), and optimistic state hashes.

Authentication: no security requirement declared for this operation.

Returns:

Type: object

- `tag_groups` (required)
  Type: Array<object>
  
  Items:
    Type: object
    
    - `id` (required)
      Type: integer
      
      exclusiveMinimum: 0
    
    - `name` (required)
      Type: string
      
      minLength: 1
    
    - `tags` (required)
      Type: Array<object>
      
      Items:
        Type: object
        
        - `id` (required)
          Type: integer
          
          exclusiveMinimum: 0
        
        - `name` (required)
          Type: string
          
          minLength: 1
        
        - `slug` (required)
          Type: string
          
          minLength: 1
        
        Additional properties: true
    
    - `parent_tag` (required)
      Type: object | null
      
      anyOf:
        Type: object
        
        - `id` (required)
          Type: integer
          
          exclusiveMinimum: 0
        
        - `name` (required)
          Type: string
          
          minLength: 1
        
        - `slug` (required)
          Type: string
          
          minLength: 1
        
        Additional properties: true
      
        Type: null
    
    - `one_per_topic` (required)
      Type: boolean
    
    - `permissions` (required)
      Type: Array<object>
      
      Complete tag-group permission list. Use [{"group_id":0,"access":"full"}] for everyone with full access.
      
      minItems: 1
      
      maxItems: 1000
      
      Items:
        Type: object
        
        - `group_id` (required)
          Type: integer
          
          Numeric Discourse group ID. Use 0 for the built-in everyone group.
          
          minimum: 0
        
        - `access` (required)
          Type: string
          
          Tag permission for this group: full allows use; readonly allows visibility without use.
          
          enum: ["full","readonly"]
        
        Additional properties: false
    
    - `state_hash` (required)
      Type: string
      
      pattern: "^[a-f0-9]{64}$"
    
    Additional properties: true

Additional properties: false

Declared MCP annotations:

```json
{
  "readOnlyHint": true,
  "destructiveHint": false,
  "idempotentHint": true,
  "openWorldHint": true
}
```

## Examples

### JSON-RPC

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "discourse_list_tag_groups",
    "arguments": {}
  }
}
```

### TypeScript

```typescript
const result = await client.callTool({
  "name": "discourse_list_tag_groups",
  "arguments": {}
});
```

### Python

```python
result = await session.call_tool("discourse_list_tag_groups", arguments={})
```


## Get Tag Group

`TOOL discourse_get_tag_group`

Get authoritative tag-group state by ID. Requires a locally configured API credential and upstream staff authority. The deterministic state_hash is an MCP optimistic precondition, not an upstream atomic lock; a race remains possible after preflight.

Authentication: no security requirement declared for this operation.

Parameters:

- `id` (argument, integer, required)
  Type: integer
  
  exclusiveMinimum: 0

Returns:

Type: object

- `tag_group` (required)
  Type: object
  
  - `id` (required)
    Type: integer
    
    exclusiveMinimum: 0
  
  - `name` (required)
    Type: string
    
    minLength: 1
  
  - `tags` (required)
    Type: Array<object>
    
    Items:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      - `name` (required)
        Type: string
        
        minLength: 1
      
      - `slug` (required)
        Type: string
        
        minLength: 1
      
      Additional properties: true
  
  - `parent_tag` (required)
    Type: object | null
    
    anyOf:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      - `name` (required)
        Type: string
        
        minLength: 1
      
      - `slug` (required)
        Type: string
        
        minLength: 1
      
      Additional properties: true
    
      Type: null
  
  - `one_per_topic` (required)
    Type: boolean
  
  - `permissions` (required)
    Type: Array<object>
    
    Complete tag-group permission list. Use [{"group_id":0,"access":"full"}] for everyone with full access.
    
    minItems: 1
    
    maxItems: 1000
    
    Items:
      Type: object
      
      - `group_id` (required)
        Type: integer
        
        Numeric Discourse group ID. Use 0 for the built-in everyone group.
        
        minimum: 0
      
      - `access` (required)
        Type: string
        
        Tag permission for this group: full allows use; readonly allows visibility without use.
        
        enum: ["full","readonly"]
      
      Additional properties: false
  
  - `state_hash` (required)
    Type: string
    
    pattern: "^[a-f0-9]{64}$"
  
  Additional properties: true

- `warnings` (optional)
  Type: Array<string>
  
  Items:
    Type: string

Additional properties: false

Declared MCP annotations:

```json
{
  "readOnlyHint": true,
  "destructiveHint": false,
  "idempotentHint": true,
  "openWorldHint": true
}
```

## Examples

### JSON-RPC

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "discourse_get_tag_group",
    "arguments": {
        "id": 0
      }
  }
}
```

### TypeScript

```typescript
const result = await client.callTool({
  "name": "discourse_get_tag_group",
  "arguments": {
    "id": 0
  }
});
```

### Python

```python
result = await session.call_tool("discourse_get_tag_group", arguments={
  "id": 0
})
```


## Create Tag Group

`TOOL discourse_create_tag_group`

Create a tag group with an explicit complete permission list such as permissions: [{group_id: 0, access: "full"}] for everyone, nonempty members, an optional parent selector, and one-per-topic policy. Omit parent_tag or use null when there is no parent; blank placeholders are safely treated as omitted. New tag names require allow_tag_creation=true because they create persistent tags and trigger normal indexing/plugin hooks. Duplicate-name and ID checks are advisory; upstream staff authority, tagging_enabled, and constraints remain authoritative.

Authentication: no security requirement declared for this operation.

Request body: application/json (required)

Type: object

- `name` (required)
  Type: string
  
  minLength: 1
  
  maxLength: 100

- `tags` (required)
  Type: Array<object>
  
  minItems: 1
  
  maxItems: 1000
  
  Items:
    Type: object
    
    anyOf:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      Additional properties: false
    
      Type: object
      
      - `name` (required)
        Type: string
        
        minLength: 1
        
        maxLength: 100
      
      Additional properties: false

- `parent_tag` (optional)
  Type: object | null
  
  Optional parent tag selector. Omit or use null when there is no parent; blank string placeholders are treated as omitted.
  
  anyOf:
    Type: object
    
    anyOf:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      Additional properties: false
    
      Type: object
      
      - `name` (required)
        Type: string
        
        minLength: 1
        
        maxLength: 100
      
      Additional properties: false
  
    Type: null

- `one_per_topic` (optional)
  Type: boolean
  
  default: false

- `permissions` (required)
  Type: Array<object>
  
  Complete tag-group permission list. Use [{"group_id":0,"access":"full"}] for everyone with full access.
  
  minItems: 1
  
  maxItems: 1000
  
  Items:
    Type: object
    
    - `group_id` (required)
      Type: integer
      
      Numeric Discourse group ID. Use 0 for the built-in everyone group.
      
      minimum: 0
    
    - `access` (required)
      Type: string
      
      Tag permission for this group: full allows use; readonly allows visibility without use.
      
      enum: ["full","readonly"]
    
    Additional properties: false

- `allow_tag_creation` (optional)
  Type: boolean
  
  default: false

Additional properties: false

Returns:

Type: object

- `tag_group` (required)
  Type: object
  
  - `id` (required)
    Type: integer
    
    exclusiveMinimum: 0
  
  - `name` (required)
    Type: string
    
    minLength: 1
  
  - `tags` (required)
    Type: Array<object>
    
    Items:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      - `name` (required)
        Type: string
        
        minLength: 1
      
      - `slug` (required)
        Type: string
        
        minLength: 1
      
      Additional properties: true
  
  - `parent_tag` (required)
    Type: object | null
    
    anyOf:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      - `name` (required)
        Type: string
        
        minLength: 1
      
      - `slug` (required)
        Type: string
        
        minLength: 1
      
      Additional properties: true
    
      Type: null
  
  - `one_per_topic` (required)
    Type: boolean
  
  - `permissions` (required)
    Type: Array<object>
    
    Complete tag-group permission list. Use [{"group_id":0,"access":"full"}] for everyone with full access.
    
    minItems: 1
    
    maxItems: 1000
    
    Items:
      Type: object
      
      - `group_id` (required)
        Type: integer
        
        Numeric Discourse group ID. Use 0 for the built-in everyone group.
        
        minimum: 0
      
      - `access` (required)
        Type: string
        
        Tag permission for this group: full allows use; readonly allows visibility without use.
        
        enum: ["full","readonly"]
      
      Additional properties: false
  
  - `state_hash` (required)
    Type: string
    
    pattern: "^[a-f0-9]{64}$"
  
  Additional properties: true

- `warnings` (optional)
  Type: Array<string>
  
  Items:
    Type: string

Additional properties: false

Declared MCP annotations:

```json
{
  "readOnlyHint": false,
  "destructiveHint": false,
  "idempotentHint": false,
  "openWorldHint": true
}
```

## Examples

### JSON-RPC

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "discourse_create_tag_group",
    "arguments": {
        "name": "string",
        "tags": [
          {
            "id": 0
          }
        ],
        "parent_tag": {
          "id": 0
        },
        "one_per_topic": false,
        "permissions": [
          {
            "group_id": 0,
            "access": "full"
          }
        ],
        "allow_tag_creation": false
      }
  }
}
```

### TypeScript

```typescript
const result = await client.callTool({
  "name": "discourse_create_tag_group",
  "arguments": {
    "name": "string",
    "tags": [
      {
        "id": 0
      }
    ],
    "parent_tag": {
      "id": 0
    },
    "one_per_topic": false,
    "permissions": [
      {
        "group_id": 0,
        "access": "full"
      }
    ],
    "allow_tag_creation": false
  }
});
```

### Python

```python
result = await session.call_tool("discourse_create_tag_group", arguments={
  "name": "string",
  "tags": [
    {
      "id": 0
    }
  ],
  "parent_tag": {
    "id": 0
  },
  "one_per_topic": false,
  "permissions": [
    {
      "group_id": 0,
      "access": "full"
    }
  ],
  "allow_tag_creation": false
})
```


## Update Tag Group

`TOOL discourse_update_tag_group`

Safely replace complete tag-group state after a fresh expected_state_hash preflight. Omitted mutable fields are preserved locally, but Discourse receives full tags, parent, one-per-topic, and permissions because partial upstream bodies clear state. For parent_tag, omit or pass a blank placeholder to preserve the current parent; pass null to clear it. Permissions use explicit {group_id, access} entries; group_id 0 means everyone. Tag/parent removals, permission replacement, and possible materialization of serializer-synthesized everyone/full permissions require explicit acknowledgements. The hash is advisory rather than an upstream atomic lock; uncertain post-dispatch outcomes must not be retried blindly.

Authentication: no security requirement declared for this operation.

Request body: application/json (required)

Type: object

- `id` (required)
  Type: integer
  
  exclusiveMinimum: 0

- `expected_state_hash` (required)
  Type: string
  
  pattern: "^[a-f0-9]{64}$"

- `name` (optional)
  Type: string
  
  minLength: 1
  
  maxLength: 100

- `tags` (optional)
  Type: Array<object>
  
  minItems: 1
  
  maxItems: 1000
  
  Items:
    Type: object
    
    anyOf:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      Additional properties: false
    
      Type: object
      
      - `name` (required)
        Type: string
        
        minLength: 1
        
        maxLength: 100
      
      Additional properties: false

- `parent_tag` (optional)
  Type: object | null
  
  Optional parent tag selector. Omit or use null when there is no parent; blank string placeholders are treated as omitted.
  
  anyOf:
    Type: object
    
    anyOf:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      Additional properties: false
    
      Type: object
      
      - `name` (required)
        Type: string
        
        minLength: 1
        
        maxLength: 100
      
      Additional properties: false
  
    Type: null

- `one_per_topic` (optional)
  Type: boolean

- `permissions` (optional)
  Type: Array<object>
  
  Complete tag-group permission list. Use [{"group_id":0,"access":"full"}] for everyone with full access.
  
  minItems: 1
  
  maxItems: 1000
  
  Items:
    Type: object
    
    - `group_id` (required)
      Type: integer
      
      Numeric Discourse group ID. Use 0 for the built-in everyone group.
      
      minimum: 0
    
    - `access` (required)
      Type: string
      
      Tag permission for this group: full allows use; readonly allows visibility without use.
      
      enum: ["full","readonly"]
    
    Additional properties: false

- `allow_tag_creation` (optional)
  Type: boolean
  
  default: false

- `confirm_tag_removal` (optional)
  Type: boolean
  
  default: false

- `confirm_parent_removal` (optional)
  Type: boolean
  
  default: false

- `confirm_permission_replacement` (optional)
  Type: boolean
  
  default: false

- `acknowledge_possible_synthetic_permission_materialization` (optional)
  Type: boolean
  
  default: false

Additional properties: false

Returns:

Type: object

- `tag_group` (required)
  Type: object
  
  - `id` (required)
    Type: integer
    
    exclusiveMinimum: 0
  
  - `name` (required)
    Type: string
    
    minLength: 1
  
  - `tags` (required)
    Type: Array<object>
    
    Items:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      - `name` (required)
        Type: string
        
        minLength: 1
      
      - `slug` (required)
        Type: string
        
        minLength: 1
      
      Additional properties: true
  
  - `parent_tag` (required)
    Type: object | null
    
    anyOf:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      - `name` (required)
        Type: string
        
        minLength: 1
      
      - `slug` (required)
        Type: string
        
        minLength: 1
      
      Additional properties: true
    
      Type: null
  
  - `one_per_topic` (required)
    Type: boolean
  
  - `permissions` (required)
    Type: Array<object>
    
    Complete tag-group permission list. Use [{"group_id":0,"access":"full"}] for everyone with full access.
    
    minItems: 1
    
    maxItems: 1000
    
    Items:
      Type: object
      
      - `group_id` (required)
        Type: integer
        
        Numeric Discourse group ID. Use 0 for the built-in everyone group.
        
        minimum: 0
      
      - `access` (required)
        Type: string
        
        Tag permission for this group: full allows use; readonly allows visibility without use.
        
        enum: ["full","readonly"]
      
      Additional properties: false
  
  - `state_hash` (required)
    Type: string
    
    pattern: "^[a-f0-9]{64}$"
  
  Additional properties: true

- `warnings` (optional)
  Type: Array<string>
  
  Items:
    Type: string

Additional properties: false

Declared MCP annotations:

```json
{
  "readOnlyHint": false,
  "destructiveHint": true,
  "idempotentHint": false,
  "openWorldHint": true
}
```

## Examples

### JSON-RPC

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "discourse_update_tag_group",
    "arguments": {
        "id": 0,
        "expected_state_hash": "string",
        "name": "string",
        "tags": [
          {
            "id": 0
          }
        ],
        "parent_tag": {
          "id": 0
        },
        "one_per_topic": true,
        "permissions": [
          {
            "group_id": 0,
            "access": "full"
          }
        ],
        "allow_tag_creation": false,
        "confirm_tag_removal": false,
        "confirm_parent_removal": false,
        "confirm_permission_replacement": false,
        "acknowledge_possible_synthetic_permission_materialization": false
      }
  }
}
```

### TypeScript

```typescript
const result = await client.callTool({
  "name": "discourse_update_tag_group",
  "arguments": {
    "id": 0,
    "expected_state_hash": "string",
    "name": "string",
    "tags": [
      {
        "id": 0
      }
    ],
    "parent_tag": {
      "id": 0
    },
    "one_per_topic": true,
    "permissions": [
      {
        "group_id": 0,
        "access": "full"
      }
    ],
    "allow_tag_creation": false,
    "confirm_tag_removal": false,
    "confirm_parent_removal": false,
    "confirm_permission_replacement": false,
    "acknowledge_possible_synthetic_permission_materialization": false
  }
});
```

### Python

```python
result = await session.call_tool("discourse_update_tag_group", arguments={
  "id": 0,
  "expected_state_hash": "string",
  "name": "string",
  "tags": [
    {
      "id": 0
    }
  ],
  "parent_tag": {
    "id": 0
  },
  "one_per_topic": true,
  "permissions": [
    {
      "group_id": 0,
      "access": "full"
    }
  ],
  "allow_tag_creation": false,
  "confirm_tag_removal": false,
  "confirm_parent_removal": false,
  "confirm_permission_replacement": false,
  "acknowledge_possible_synthetic_permission_materialization": false
})
```


## Delete Tag Group

`TOOL discourse_delete_tag_group`

Permanently delete one exact tag group after fresh name/state-hash checks and explicit acknowledgements for category relationship cascades and unresolved plugin dependencies. Deletion removes memberships, permissions, and category allowed/required relationships, but not tags or topic-tag rows. Dependency discovery is not exhaustive (plugins such as RSS polling may depend on groups). Scoped API keys may not include this delete route. A 200 response is only dispatch acknowledgement; absence is verified before success.

Authentication: no security requirement declared for this operation.

Parameters:

- `id` (argument, integer, required)
  Type: integer
  
  exclusiveMinimum: 0

- `name` (argument, string, required)
  Type: string
  
  minLength: 1

- `expected_state_hash` (argument, string, required)
  Type: string
  
  pattern: "^[a-f0-9]{64}$"

- `confirm_delete` (argument, boolean, required)
  Type: boolean
  
  const: true

- `acknowledge_category_relationship_removal` (argument, boolean, required)
  Type: boolean
  
  const: true

- `acknowledge_unresolved_plugin_dependencies` (argument, boolean, required)
  Type: boolean
  
  const: true

Returns:

Type: object

- `deleted` (required)
  Type: boolean
  
  const: true

- `id` (required)
  Type: integer
  
  exclusiveMinimum: 0

- `name` (required)
  Type: string
  
  minLength: 1

- `impact` (required)
  Type: object
  
  - `member_tags` (required)
    Type: Array<object>
    
    Items:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      - `name` (required)
        Type: string
        
        minLength: 1
      
      - `slug` (required)
        Type: string
        
        minLength: 1
      
      Additional properties: true
  
  - `parent_tag` (required)
    Type: object | null
    
    anyOf:
      Type: object
      
      - `id` (required)
        Type: integer
        
        exclusiveMinimum: 0
      
      - `name` (required)
        Type: string
        
        minLength: 1
      
      - `slug` (required)
        Type: string
        
        minLength: 1
      
      Additional properties: true
    
      Type: null
  
  - `permissions` (required)
    Type: Array<object>
    
    Complete tag-group permission list. Use [{"group_id":0,"access":"full"}] for everyone with full access.
    
    minItems: 1
    
    maxItems: 1000
    
    Items:
      Type: object
      
      - `group_id` (required)
        Type: integer
        
        Numeric Discourse group ID. Use 0 for the built-in everyone group.
        
        minimum: 0
      
      - `access` (required)
        Type: string
        
        Tag permission for this group: full allows use; readonly allows visibility without use.
        
        enum: ["full","readonly"]
      
      Additional properties: false
  
  - `category_relationships` (required)
    Type: object
    
    - `included` (required)
      Type: boolean
      
      const: false
    
    - `reason` (required)
      Type: string
    
    Additional properties: false
  
  - `dependency_discovery_exhaustive` (required)
    Type: boolean
    
    const: false
  
  Additional properties: false

Additional properties: false

Declared MCP annotations:

```json
{
  "readOnlyHint": false,
  "destructiveHint": true,
  "idempotentHint": false,
  "openWorldHint": true
}
```

## Examples

### JSON-RPC

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "discourse_delete_tag_group",
    "arguments": {
        "id": 0,
        "name": "string",
        "expected_state_hash": "string",
        "confirm_delete": true,
        "acknowledge_category_relationship_removal": true,
        "acknowledge_unresolved_plugin_dependencies": true
      }
  }
}
```

### TypeScript

```typescript
const result = await client.callTool({
  "name": "discourse_delete_tag_group",
  "arguments": {
    "id": 0,
    "name": "string",
    "expected_state_hash": "string",
    "confirm_delete": true,
    "acknowledge_category_relationship_removal": true,
    "acknowledge_unresolved_plugin_dependencies": true
  }
});
```

### Python

```python
result = await session.call_tool("discourse_delete_tag_group", arguments={
  "id": 0,
  "name": "string",
  "expected_state_hash": "string",
  "confirm_delete": true,
  "acknowledge_category_relationship_removal": true,
  "acknowledge_unresolved_plugin_dependencies": true
})
```
