> ## Documentation Index
> Fetch the complete documentation index at: https://developer.kodexa.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Kodexa Workflow MCP Connector

> Connect claude.ai to your Kodexa workflow — activities, tasks, and task-groups — as an OAuth-secured MCP connector, and drive assignment, status, and membership actions in natural language.

kodexa-api exposes its workflow surface — activities, tasks, and task-groups, plus assignment, status, and membership actions — as a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) connector. You add the endpoint to claude.ai as a remote OAuth connector, and Claude can then query and manage your workflow in natural language, acting as you.

<Info>
  The connector is served by the Kodexa platform itself over HTTP — there is nothing to install on your machine. Every call is authenticated and every result is scoped to what the calling user is permitted to see, exactly like the REST API.
</Info>

## What The Connector Exposes

The MCP server registers as **`kodexa-workflow`** and presents 19 tools:

* **Discovery** — resolve who you are and the organizations, projects, teams, and members you can act on.
* **Workflow queries** — list and read activities, tasks, task-groups, and task statuses (read-only).
* **Workflow actions** — assign and unassign tasks and task-groups, move tasks between statuses, and add or remove tasks from a group.

Reads reuse the platform's registry handlers; writes replay through the existing REST handlers. As a result **access control (FGAC), optimistic locking, task-group cascades, audit, and domain events are identical to the REST surface** — no logic is duplicated.

<Note>
  **Workflow-model caveats the tools enforce:**

  * **Activities are read-only over MCP.** You can list and inspect them, but not mutate them.
  * **A grouped task's assignee is managed via its group.** `assign_task` / `unassign_task` targeting the **user** (assignee) fail on a task that belongs to a task-group — assign the group with `assign_task_group` instead, and the assignment cascades to every member task. (A **team-only** assignment through `assign_task` is *not* blocked for grouped tasks, since a group owns the assignee, not the team.)
  * **Access is FGAC-scoped to the calling user.** Claude sees and changes only what you could see and change yourself.
</Note>

## Enabling The Connector

The connector is off by default. A platform administrator enables it in the platform configuration:

| Setting         | Purpose                                                                                                                                                                                               |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mcp.enabled`   | Turns the connector on. When false, the `/mcp` route is not mounted.                                                                                                                                  |
| `mcp.publicUrl` | The canonical, publicly reachable URL of the MCP endpoint. This value **must equal the Auth0 API identifier (audience)** for the OAuth flow, and it anchors the protected-resource metadata document. |

For the claude.ai OAuth flow, `api.security.auth0.domain` must also be set. On startup the platform builds an Auth0 token validator bound to `mcp.publicUrl` as the audience.

<Warning>
  If `mcp.enabled` is set but `mcp.publicUrl` is empty, the connector is disabled. If the Auth0 domain is not configured (or the validator fails to initialize), the connector still works with the `X-API-Key` fallback but does **not** advertise the claude.ai OAuth flow.
</Warning>

The connector is mounted at `<basePath>/mcp` over the Streamable HTTP transport (stateless — each request is a self-contained, independently authenticated JSON-RPC message).

## Adding It To claude.ai

Add the MCP endpoint (`https://<your-mcp-publicUrl>/mcp`) as a **remote connector** in claude.ai. The OAuth handshake is automatic:

1. claude.ai calls the endpoint with no token and receives an RFC 9728 `401` with a `WWW-Authenticate: Bearer resource_metadata="…"` header.
2. It fetches `/.well-known/oauth-protected-resource` from the endpoint's origin, which points at your Auth0 authorization server.
3. It performs Dynamic Client Registration (DCR) and an OAuth authorization-code + PKCE flow against Auth0, obtaining an access token whose audience matches `mcp.publicUrl`.
4. Every subsequent MCP call carries that bearer token, which the platform validates against Auth0's JWKS (signature, issuer, expiry, audience) before landing an authenticated user in context.

The protected-resource metadata advertises the `openid`, `profile`, `email`, and `offline_access` scopes.

### Programmatic And Local Clients

Clients that cannot run the OAuth flow can authenticate with an API key by sending it in the `X-API-Key` header instead of a bearer token. This is the fallback path used for local development and programmatic access.

## Tool Reference

All list tools share a common set of optional parameters: `filter` (an advanced SpringFilter expression, ANDed with the other filters), `query` (free-text search), `sort` (e.g. `created_on:desc`), `page` (1-indexed, default 1), and `pageSize` (default 20, max 1000).

### Discovery

| Tool                        | Purpose                                                                                          | Key arguments               |
| --------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------- |
| `whoami`                    | Return the authenticated user's identity (id, email, roles, groups, job title). Call this first. | —                           |
| `list_organizations`        | Organizations you can access.                                                                    | list params                 |
| `list_projects`             | Projects, optionally scoped to an organization.                                                  | `organizationId`            |
| `list_teams`                | Teams in an organization (assignment targets).                                                   | `organizationId`            |
| `list_organization_members` | Members of an organization (assignment targets).                                                 | `organizationId` (required) |

### Workflow Queries (read-only)

| Tool                 | Purpose                                                                                                         | Key arguments                                                                                                              |
| -------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `list_activities`    | Activities (runtime instances of activity plans).                                                               | `projectId`, `lifecycleState` (`PENDING`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED`, `REPLANNED`)                      |
| `get_activity`       | A single activity, including its materialized steps.                                                            | `id` (required)                                                                                                            |
| `list_tasks`         | Tasks, with rich filtering.                                                                                     | `projectId`, `assigneeId`, `unassigned`, `statusType` (`OPEN`, `IN_PROGRESS`, `DONE`, `BLOCKED`, `PENDING`), `taskGroupId` |
| `get_task`           | A single task.                                                                                                  | `id` (required)                                                                                                            |
| `list_task_groups`   | Task-groups (batch tasks for shared assignment and lifecycle).                                                  | `projectId`, `teamId`, `assigneeId`, `statusType`                                                                          |
| `get_task_group`     | A single task-group.                                                                                            | `id` (required)                                                                                                            |
| `list_task_statuses` | Task statuses for an organization (optionally bound to a project). Use a status slug with `update_task_status`. | `organizationId`, `projectId`                                                                                              |

### Workflow Actions (write)

| Tool                     | Purpose                                                                                                                                                                                                        | Key arguments                                                                                      |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `assign_task`            | Assign a task to a user and/or team. A **user** assignment fails if the task belongs to a task-group (assign the group instead) or the task is locked; a **team-only** assignment is allowed on grouped tasks. | `taskId` (required), `userId`, `teamId`, `changeSequence`                                          |
| `unassign_task`          | Clear a task's user and/or team assignment. Clearing the **user** fails if the task belongs to a task-group.                                                                                                   | `taskId` (required), `target` (`user`, `team`, or `both`; default `user`)                          |
| `assign_task_group`      | Assign a task-group to a user; cascades to every member task. Fails if any member is locked.                                                                                                                   | `taskGroupId` (required), `userId` (required), `changeSequence`                                    |
| `unassign_task_group`    | Clear a task-group's user assignment, cascading the clear to all members.                                                                                                                                      | `taskGroupId` (required)                                                                           |
| `update_task_status`     | Move a task to a status by slug. Applies the status's lock behavior and any group auto-complete/reopen.                                                                                                        | `taskId` (required), `status` (slug, required), `changeSequence`, `lockTask`, `lockDocumentFamily` |
| `add_tasks_to_group`     | Add tasks to a group; the group's assignee (if set) cascades to the new members.                                                                                                                               | `taskGroupId` (required), `taskIds` (required)                                                     |
| `remove_task_from_group` | Remove a single task from a group, ungrouping it.                                                                                                                                                              | `taskGroupId` (required), `taskId` (required)                                                      |

### Optimistic Concurrency

Tasks carry a `changeSequence`. The write tools that accept a `changeSequence` argument (`assign_task`, `assign_task_group`, `update_task_status`) use it for optimistic-concurrency safety: pass the sequence you last read, and the write fails cleanly if the task changed underneath you. It is optional — omit it to skip the check.

## A Typical Session

A natural place to start is discovery, then narrow to the work you care about, then act:

```text theme={null}
Who am I, and which projects can I see?
```

```text theme={null}
Show me the unassigned tasks in the Invoices project that are still OPEN.
```

```text theme={null}
Assign the "Review BoL #1428" task to me.
```

Claude calls `whoami` and `list_projects`, then `list_tasks` with `projectId`, `unassigned: true`, and `statusType: OPEN`, then resolves your user id via `list_organization_members` and calls `assign_task`. Because everything runs through the platform's normal access-control path, it can only ever do what you are permitted to do.
