# Archer MCP Server v1.0.0

REST-only MCP server for the Archer GRC platform at `sonymusic-dev.archerirm.us`.
All API calls go through `/platformapi/core/` with token-based auth.

## What's New in v1.0.0

- **Archer 2026 API compatibility**: `/content/fieldcontent` now uses plain POST with `ContentIds` + `FieldIds` (no GET override, no ModuleId-based search)
- **Session auto-refresh**: On 401/403, re-authenticates and retries (requires username/password credentials)
- **Better error context**: `ArcherApiError` includes HTTP status, method, endpoint, and response body
- **10 smart composite tools**: Higher-level operations that combine multiple API calls

## Available MCP Tools (49 tools)

### Generic API Access

| Tool           | Purpose                                                                                                                                                       |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `raw_api_call` | Call ANY Archer REST endpoint directly. Use for endpoints not covered by specific tools. Supports GET/POST/PUT/DELETE and the X-Http-Method-Override pattern. |

### Content / Record Management

| Tool                  | Purpose                                                                      |
| --------------------- | ---------------------------------------------------------------------------- |
| `search_records`      | Get records by content IDs + field IDs (Archer 2026: no module-based search) |
| `get_record`          | Get a single record by content ID                                            |
| `get_records_by_ids`  | Get field content for multiple records by content IDs (bulk)                 |
| `get_related_records` | Get records related via a cross-reference/related field                      |
| `get_record_history`  | Get the change history log for a record                                      |
| `create_record`       | Create a new record                                                          |
| `update_record`       | Update an existing record                                                    |
| `delete_record`       | Delete a record by content ID                                                |

### Metadata & Schema

| Tool                   | Purpose                                                              |
| ---------------------- | -------------------------------------------------------------------- |
| `get_modules`          | List all applications/modules                                        |
| `get_application`      | Get detailed metadata for a single module                            |
| `get_levels`           | Get levels for a module (needed to find LevelId for record creation) |
| `get_level`            | Get details for a specific level                                     |
| `get_level_layout`     | Get form layout for a level                                          |
| `get_fields`           | Get field definitions by application ID                              |
| `get_fields_by_level`  | Get field definitions by level ID                                    |
| `get_field_definition` | Get a single field definition by field ID                            |
| `get_value_list`       | Get dropdown options by values list ID                               |
| `get_subform`          | Get subform definition by ID                                         |

### Attachments

| Tool                | Purpose                          |
| ------------------- | -------------------------------- |
| `get_attachment`    | Download attachment by ID        |
| `upload_attachment` | Upload base64-encoded attachment |

### User Management

| Tool              | Purpose                         |
| ----------------- | ------------------------------- |
| `get_users`       | List users with OData filtering |
| `get_user`        | Get user details by ID          |
| `create_user`     | Create a new user               |
| `update_user`     | Update an existing user         |
| `activate_user`   | Activate a user account         |
| `deactivate_user` | Deactivate a user account       |

### Groups & Roles

| Tool         | Purpose                 |
| ------------ | ----------------------- |
| `get_groups` | List all groups         |
| `get_group`  | Get group details by ID |
| `get_roles`  | List all access roles   |

### Workflow

| Tool                      | Purpose                                         |
| ------------------------- | ----------------------------------------------- |
| `get_workflow_actions`    | Get available workflow transitions for a record |
| `perform_workflow_action` | Execute a workflow transition on a record       |

### Data Feeds

| Tool                    | Purpose                               |
| ----------------------- | ------------------------------------- |
| `execute_data_feed`     | Trigger a data feed by GUID           |
| `get_data_feed_history` | Get execution history for a data feed |

### Questionnaires

| Tool                 | Purpose                         |
| -------------------- | ------------------------------- |
| `get_questionnaires` | List all questionnaires         |
| `get_questionnaire`  | Get questionnaire details by ID |

### System / Platform Info

| Tool                      | Purpose                                                   |
| ------------------------- | --------------------------------------------------------- |
| `get_archer_version`      | Get the Archer platform version                           |
| `get_security_parameters` | Get password policies, lockout settings, session timeouts |
| `get_user_tasks`          | Get tasks assigned to user with OData filtering           |

### Smart Composite Tools (v1.0.0)

| Tool                      | Purpose                                                                              |
| ------------------------- | ------------------------------------------------------------------------------------ |
| `lookup_user`             | Search users by name/username — no OData needed                                      |
| `decode_record`           | Fetch a record with resolved field names + dropdown values in one call               |
| `search_records_filtered` | Get records by content IDs with OData $filter/$orderby                               |
| `get_records_by_module`   | Fetch records by content IDs with auto-discovered field names                        |
| `batch_resolve_values`    | Resolve up to 50 dropdown values lists in parallel                                   |
| `lookup_field`            | Search field definitions by name (case-insensitive)                                  |
| `clone_record`            | Clone a record, stripping read-only fields, with optional overrides                  |
| `bulk_create_records`     | Create up to 50 records sequentially with per-record error reporting                 |
| `compare_records`         | Compare two records field by field with diff flags                                   |
| `export_module`           | Export records by content IDs with field name resolution + optional dropdown resolve |

## Key Patterns

### Creating Records

The `create_record` tool requires a `Content` wrapper:

```json
{
  "Content": {
    "LevelId": <levelId from get_levels>,
    "FieldContents": {
      "<fieldId>": { "Type": <fieldType>, "Value": <value>, "FieldId": <fieldId> }
    }
  }
}
```

### Field Types

| Type | Name                   | Value Format                                |
| ---- | ---------------------- | ------------------------------------------- |
| 1    | Text                   | `"Value": "string"`                         |
| 2    | Numeric                | `"Value": 123`                              |
| 3    | Date                   | `"Value": "2025-01-15T00:00:00"`            |
| 4    | Values List (dropdown) | `"Value": {"ValuesListIds": [<id>]}`        |
| 6    | Tracking ID            | Read-only, auto-generated                   |
| 8    | Users/Groups           | `"Value": {"UserList": [{"Id": <userId>}]}` |
| 9    | Cross-Reference        | `"Value": {"ContentIds": [<contentId>]}`    |
| 11   | Attachment             | Managed via attachment tools                |
| 12   | Image                  | Managed via attachment tools                |
| 21   | First Published        | Read-only                                   |
| 22   | Last Updated           | Read-only                                   |
| 23   | Related Records        | Read-only reverse relationship              |
| 24   | Sub-Form               | Nested records                              |
| 25   | History Log            | Read-only                                   |

### Updating Records

The `update_record` tool expects the same `Content` wrapper, but with `Id` and `Version`:

```json
{
  "Content": {
    "Id": <contentId>,
    "LevelId": <levelId>,
    "FieldContents": { ... },
    "Version": "<version string from get_record>"
  }
}
```

### Workflow: Creating a Record in an Unknown Module

1. `get_modules` — find the module ID and name
2. `get_levels(moduleId)` — find the LevelId for the module
3. `get_fields(moduleId)` — find required fields (IsRequired=true) and their types
4. For Type 4 (dropdown) fields, note `RelatedValuesListId` from the field definition
5. `get_value_list(valuesListId)` — get valid value IDs for each dropdown
6. `create_record` with all required fields populated

### Workflow: Looking Up Values for Dropdowns

Field definitions for Type 4 fields include `RelatedValuesListId`. Use that ID (NOT the field ID) with `get_value_list`. Each value has an `Id` that goes into `ValuesListIds` array.

### Workflow: Advancing a Record Through Workflow

1. `get_workflow_actions(contentId)` — see current node and available transitions
2. `perform_workflow_action(contentId, workflowNodeId, completionCode)` — execute the transition

### Workflow: Resolving Unknown Field Names

When you have a record with raw field IDs but no names:

1. Check the record's `LevelId`
2. `get_fields_by_level(levelId)` — get field definitions including Name, Type, IsRequired
3. Map field IDs to human-readable names

**Or use `decode_record(contentId)` — does all of this in one call.**

### Workflow: Quick Record Inspection (v1.0.0)

Use `decode_record(contentId)` instead of the 6-step manual process. It:

1. Fetches the record
2. Resolves all field names from the LevelId
3. Resolves all dropdown values to display names
4. Returns `{fieldName: resolvedValue}` in one response

### Workflow: Exploring a Module's Data (v1.0.0)

Use `get_records_by_module(moduleId)` to get records with field names in one call.
Use `export_module(moduleId, resolveDropdowns=true)` for a full export with resolved dropdowns.

### Workflow: Finding Fields (v1.0.0)

Use `lookup_field(moduleId, "Status")` instead of manually scanning `get_fields` output.

### Workflow: Finding Users (v1.0.0)

Use `lookup_user("john")` instead of constructing OData filters manually.

### Workflow: Cloning Records (v1.0.0)

Use `clone_record(sourceContentId, overrides)` to copy a record with modifications.
Read-only, calculated, and sub-form fields are automatically stripped.

### Using raw_api_call for Uncovered Endpoints

The `raw_api_call` tool can hit any endpoint under `/platformapi/core/`. Key tips:

- Many Archer GET operations require `useGetOverride: true` (sends POST with X-Http-Method-Override: GET)
- Set `method` to the logical HTTP verb (GET, POST, PUT, DELETE)
- Pass `body` for POST/PUT operations
- Pass `queryParams` for OData filtering (`$filter`, `$top`, `$skip`, `$orderby`, `$select`)

Example — get group hierarchy:

```
raw_api_call(endpoint="/system/grouphierarchy", method="GET")
```

Example — change user password:

```
raw_api_call(endpoint="/system/userpassword", method="PUT", body={"UserId": 123, "NewPassword": "..."})
```

## Known Modules (as of 2025)

| ID  | Name                             | LevelId |
| --- | -------------------------------- | ------- |
| 68  | Business Processes               | -       |
| 83  | Exception Requests               | -       |
| 84  | Contacts                         | 44      |
| 149 | Business Unit                    | -       |
| 152 | Vendors & 3rd Party Applications | -       |
| 153 | Control Procedures               | -       |
| 167 | Findings & Risks                 | -       |
| 169 | Question Library                 | -       |
| 189 | Consumer Websites                | -       |
| 191 | Company                          | -       |
| 192 | Division                         | -       |
| 250 | Assessments                      | -       |
| 318 | Information                      | -       |
| 352 | Platforms                        | -       |
| 368 | Corporate Applications           | 49      |
| 379 | Mobile Applications              | -       |
| 392 | Social Media Accounts            | -       |

## Notes

- **CRITICAL — Archer 2026 API change**: The `/content/fieldcontent` endpoint NO LONGER supports `ModuleId`-based search. It only accepts `{"ContentIds": [...], "FieldIds": [...]}` as plain POST (no X-Http-Method-Override). To get records, you must already know the content IDs. Use `get_record` to fetch individual records, or `decode_record` for human-readable output.
- SOAP endpoints are blocked on this instance (302 redirect). All operations are REST-only.
- The `get_value_list` tool takes `valuesListId`, NOT `fieldId`. Get the values list ID from the field definition's `RelatedValuesListId`.
- Read-only fields (IsContentReadOnly=true, IsCalculated=true) cannot be set during create/update.
- The current user does NOT have write access to Contacts (module 84). Use Corporate Applications (368) for testing.
- Use `get_levels` to programmatically find the LevelId instead of hard-coding it.
- The `raw_api_call` tool is a fallback for any endpoint not covered by specific tools — group CRUD, role management, user password changes, data gateway operations, etc.
- Session auto-refresh requires ARCHER_USERNAME + ARCHER_PASSWORD. Token-only mode (ARCHER_TOKEN) will throw a clear error on session expiry.
- Smart tools (`decode_record`, `export_module`, etc.) make multiple API calls internally. Large exports may take several seconds.

## API Reference

Official API docs: https://developer.archerirm.com/
Instance: https://sonymusic-dev.archerirm.us/platformapi/core/
