Skip to main content
Glama
Gnidreve

Genesis-World-MCP

by Gnidreve
README.md
<p align="center">
  <img src="https://www.acp-gruppe.com/hs-fs/hubfs/cas_genesis_world-1.png?width=1600&height=600&name=cas_genesis_world-1.png" alt="CAS genesisWorld" width="100%" />
</p>

# cas-genesisworld-mcp

[![npm](https://img.shields.io/npm/v/cas-genesis-world-mcp?logo=npm&label=npm)](https://www.npmjs.com/package/cas-genesis-world-mcp)
[![Docker](https://img.shields.io/docker/v/vaatu/cas-genesis-world-mcp?logo=docker&label=docker)](https://hub.docker.com/r/vaatu/cas-genesis-world-mcp)
[![CI](https://github.com/Gnidreve/Genesis-World-MCP/actions/workflows/ci.yml/badge.svg)](https://github.com/Gnidreve/Genesis-World-MCP/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/github/license/Gnidreve/Genesis-World-MCP)](./LICENSE)

Connect Claude, Cursor, or any [MCP](https://modelcontextprotocol.io)-compatible
AI agent to your **CAS genesisWorld** CRM. Search contacts, manage tasks and
appointments, and read or write records — through natural language, without
writing a single API call.

- **69 tools**, including **7 native flows** that bundle multi-step CRM
  operations (like checking for scheduling conflicts before booking a
  meeting, or checking for duplicates before creating a contact) into a
  single call.
- **Full read/write access** to tasks, contacts, appointments, documents,
  distribution lists, and more — plus generic access to any custom object
  type your installation defines.
- **Read-only mode** available for safe, exploratory use.
- Ships as a ready-made **Docker image** or **npm package**.

## Quick start

Add this to your MCP client's config (e.g. Claude Desktop's
`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "cas-genesisworld": {
      "command": "npx",
      "args": ["-y", "cas-genesis-world-mcp"],
      "env": {
        "GENESISWORLD_BASE_URL": "http://your-genesisworld-server/genesisrest.svc",
        "GENESISWORLD_PRODUCT_KEY": "your-product-key",
        "GENESISWORLD_USERNAME": "your-username",
        "GENESISWORLD_PASSWORD": "your-password"
      }
    }
  }
}
```

Restart your client — the tools show up automatically. Ask your agent to
find a contact, list your open tasks, or book a meeting, and it takes it
from there.

Prefer a persistent, self-hosted server instead of a per-client process?
See [Self-hosting with Docker](#self-hosting-with-docker) below.

## What you can ask it

Once connected, your agent can handle requests like:

- *"Find the contact info for Jane Doe and show me her open tasks."*
- *"Create a follow-up task for this lead and link it to their contact record."*
- *"Book a meeting with the sales team next Tuesday at 10am — check everyone's
  calendar for conflicts first."*
- *"Check for possible duplicates before creating a new contact for Acme Corp."*
- *"Generate a report for this opportunity."*

Requests like these are answered by **flows** — single tool calls that
bundle the multi-step sequence of API requests a human would otherwise have
to script by hand.

## Tools & flows

### Flows — compound actions, one call each

| Flow            | Mode  | What it does |
|-----------------|-------|--------------|
| `my_open_tasks` | read  | Current user + their task list (due window, saved view, or full-text filter) in one call |
| `task_overview` | read  | Task record + links + tags, fetched in parallel |
| `create_task`   | write | Create a task and optionally link it to another object (e.g. a contact) |
| `find_contact`  | read  | Contact search by name and/or phone number, in parallel |
| `contact_360`   | read  | Contact + collection dossier + tags + links, fetched in parallel |
| `create_address_safe` | write | Duplicate check first — creates only when no candidates are found |
| `create_appointment_safe` | write | Optional conflict check → create → add participants, in one call |

<details>
<summary><strong>Full tool reference (62 atomic tools)</strong></summary>

#### Read (39)

| Tool                               | Endpoint                                                          |
|-------------------------------------|-------------------------------------------------------------------|
| `smart_search`                     | `GET /v7.0/smartsearch`                                           |
| `get_data_object`                  | `GET /v7.0/type/{dataObjectType}/{dataObjectGGUID}`               |
| `get_dossier`                      | `GET /v7.0/type/{dataObjectType}/{dataObjectGGUID}/dossier/full`  |
| `list_data_objects`                | `GET /v7.0/type/{dataObjectType}/list`                            |
| `list_views`                       | `GET /v7.0/type/{dataObjectType}/view/list`                       |
| `list_data_objects_by_view`        | `GET /v7.0/type/{dataObjectType}/view/{viewID}/list`              |
| `list_available_data_object_types` | `GET /v7.0/user/self/dataobjecttypepermission/list`               |
| `get_data_object_types_metadata`   | `GET /v7.0/metadata`                                              |
| `list_links`                       | `GET /v7.0/type/{dataObjectType}/{dataObjectGGUID}/link/list`     |
| `list_recent_data_objects`         | `GET /v7.0/type/{dataObjectType}/recent/list`                     |
| `get_available_products`           | `GET /v7.0/type/gwopportunity/availableproducts`                  |
| `get_data_object_count`            | `GET /v7.0/type/{dataObjectType}/count`                           |
| `get_primary_link_parents`         | `GET /v7.0/type/{dataObjectType}/{dataObjectGGUID}/primarylinkparents` |
| `list_users`                       | `GET /v7.0/user/list`                                             |
| `get_user_self`                    | `GET /v7.0/user/self`                                             |
| `get_view`                         | `GET /v7.0/type/{dataObjectType}/view/{viewID}`                   |
| `list_tags`                        | `GET /v7.0/tags`                                                  |
| `get_object_tags`                  | `GET /v7.0/type/{dataObjectType}/{dataObjectGGUID}/tags`          |
| `get_full_data_objects`            | `GET /v7.0/type/{dataObjectType}/full`                            |
| `list_data_objects_by_view_full`   | `GET /v7.0/type/{dataObjectType}/view/{viewID}/full`              |
| `get_data_objects_bulk`            | `POST /v7.0/type/{dataObjectType}/records` (read despite POST)    |
| `get_ticket_service_agreements`    | `GET /v7.0/type/task/ticket/serviceagreements`                    |
| `get_vcard`                        | `GET /v7.0/type/address/{dataObjectGGUID}/vcard`                  |
| `get_salutation`                   | `POST /v7.0/type/address/salutation` (read despite POST)          |
| `format_phone_number`              | `POST /v7.0/type/address/formatphonenumber` (read despite POST)   |
| `check_appointment_conflicts`      | `GET /v7.0/type/appointment/conflicts`                            |
| `get_participant_summary`          | `GET /v7.0/type/appointment/{gguid}/participant/summary`          |
| `list_appointment_participants`    | `GET /v7.0/type/appointment/{gguid}/participant/full`             |
| `get_document_file`                | `GET /v7.0/type/document/{gguid}/file` (never locks)              |
| `list_document_versions`           | `GET /v7.0/type/document/{gguid}/file/version/list`               |
| `list_email_attachments`           | `GET /v7.0/type/emailstore/{gguid}/attachment/list`               |
| `get_email_attachment`             | `GET /v7.0/type/emailstore/{gguid}/attachment/{attachmentId}`     |
| `get_email_file`                   | `GET /v7.0/type/emailstore/{gguid}/file`                          |
| `list_object_permissions`          | `GET /v7.0/type/{t}/{gguid}/permission/full`                      |
| `list_distributions`               | `GET /v7.0/type/gwdistribution/list`                              |
| `list_distribution_addresses`      | `GET /v7.0/type/gwdistribution/{distributionGuid}/address/list`   |
| `list_report_templates`            | `GET /v7.0/type/report/template/{templateType}`                   |
| `generate_report`                  | `POST /v7.0/type/report/template/{templateGGUID}` (read despite POST — renders, doesn't mutate) |
| `readme` (tool form)                | server-local static orientation document                          |

#### Write (23 — hidden in read-only mode, along with the write flows above)

| Tool                    | Endpoint                                                             |
|-------------------------|------------------------------------------------------------------------|
| `create_data_object`    | `POST /v7.0/type/{dataObjectType}`                                   |
| `update_data_object`    | `PUT /v7.0/type/{dataObjectType}/{dataObjectGGUID}`                  |
| `delete_data_object`    | `DELETE /v7.0/type/{dataObjectType}/{dataObjectGGUID}`               |
| `restore_data_object`   | `POST /v7.0/type/{dataObjectType}/rbin/undelete`                     |
| `create_link`           | `POST /v7.0/type/{dataObjectType}/{dataObjectGGUID}/link`            |
| `delete_link`           | `DELETE /v7.0/type/{t}/{gguid}/link/{objecttype2}/{guid2}/{attribute}` |
| `set_object_tags`       | `POST /v7.0/type/{dataObjectType}/{dataObjectGGUID}/tags/user`       |
| `append_notes`          | `POST /v7.0/type/{t}/{gguid}/notes/{fieldName}`                      |
| `create_dossier_entry`  | `POST /v7.0/type/{dataObjectType}/{dataObjectGGUID}/dossier`         |
| `delete_dossier_entry`  | `DELETE /v7.0/type/{t}/{gguid}/dossier/{dossierEntryGGUID}`          |
| `set_contact_persons_active` | `POST /v7.0/type/address/{gguid}/contactperson/activate\|deactivate` |
| `add_appointment_participant`    | `POST /v7.0/type/appointment/{gguid}/participant`                |
| `remove_appointment_participant` | `DELETE /v7.0/type/appointment/{gguid}/participant/{participantGGUID}` |
| `set_recurrence`        | `POST /v7.0/type/{t}/recurrence` / `PUT …/recurrence/{periodGuid}`   |
| `delete_recurrence`     | `DELETE /v7.0/type/{t}/recurrence/{periodGuid}`                      |
| `set_alarm`             | `PUT /v7.0/type/{t}/{gguid}/alarm/self`                              |
| `delete_alarm`          | `DELETE /v7.0/type/{t}/{gguid}/alarm/self`                           |
| `set_object_permission` | `POST /v7.0/type/{t}/{gguid}/permission`                             |
| `delete_object_permission` | `DELETE /v7.0/type/{t}/{gguid}/permission/{permissionGGUID}`      |
| `add_distribution_addresses`  | `POST /v7.0/type/gwdistribution/{distributionGuid}/address`    |
| `remove_distribution_address` | `DELETE /v7.0/type/gwdistribution/{distributionGuid}/address/{addressGGUID}` |
| `convert_lead`          | `POST /v7.0/type/gwsllead/{dataObjectGGUID}/convert`                 |
| `recalculate_opportunity_positions` | `PUT /v7.0/type/gwopportunitypos/recalculatevalues`      |

The full upstream API this is built on is committed as
[`swagger.json`](./swagger.json).

</details>

## Resources

Beyond tools, the server exposes MCP resources for data that rarely
changes, so your agent doesn't burn tool calls rediscovering it every
session:

- `genesisworld://readme` — static orientation document for the agent
  itself (domain model, navigation patterns, efficiency rules).
- `genesisworld://types` — data-object types accessible to the user, with
  permissions. Cached 15 min.
- `genesisworld://metadata/{objectType}` — field/relationship schema of one
  type, e.g. `genesisworld://metadata/ADDRESS`. Cached 15 min.
- `genesisworld://views/{objectType}` — saved views of one type, e.g.
  `genesisworld://views/TASK`. Cached 15 min.

## Self-hosting with Docker

For a persistent server multiple clients can point at (instead of one
`npx` process per client):

```bash
docker run -d --name cas-genesisworld-mcp -p 8084:3000 \
  -e GENESISWORLD_BASE_URL="http://your-genesisworld-server/genesisrest.svc" \
  -e GENESISWORLD_PRODUCT_KEY="your-product-key" \
  -e GENESISWORLD_USERNAME="your-username" \
  -e GENESISWORLD_PASSWORD="your-password" \
  vaatu/cas-genesis-world-mcp

# Read-only mode: append --read-only after the image name
docker run -d --name cas-genesisworld-mcp -p 8084:3000 \
  -e GENESISWORLD_BASE_URL="http://your-genesisworld-server/genesisrest.svc" \
  -e GENESISWORLD_PRODUCT_KEY="your-product-key" \
  -e GENESISWORLD_USERNAME="your-username" \
  -e GENESISWORLD_PASSWORD="your-password" \
  vaatu/cas-genesis-world-mcp --read-only
```

Or with `docker compose`, using [`docker-compose.yml`](./docker-compose.yml)
(repo root) and [`.env.example`](./.env.example):

```bash
cp .env.example .env   # fill in your values — .env is gitignored
docker compose up -d
```

For `--read-only`, uncomment the matching `command:` line in
`docker-compose.yml` — launch options are CLI flags only, never `.env`
entries, so mode selection stays in the compose file rather than `.env`.
For `--client-credentials`, use
[`docker-compose.client-credentials.yml`](./docker-compose.client-credentials.yml)
instead (see "Multi-tenant" below) — it has that flag active by default,
rather than making you uncomment it in the primary file.

### Multi-tenant: one server, many genesisWorld identities

By default the container has one fixed genesisWorld *user* for every
client that connects. With `--client-credentials`, it has none — each
client authenticates as itself, so one server can safely serve several
people/teams with different genesisWorld logins. The product key stays
server-side either way — it identifies your product license, not a user,
so it's not something a client ever supplies:

```bash
docker run -d --name cas-genesisworld-mcp -p 8084:3000 \
  -e GENESISWORLD_BASE_URL="http://your-genesisworld-server/genesisrest.svc" \
  -e GENESISWORLD_PRODUCT_KEY="your-product-key" \
  vaatu/cas-genesis-world-mcp --client-credentials
```

Or with `docker compose`, using
[`docker-compose.client-credentials.yml`](./docker-compose.client-credentials.yml)
(only `GENESISWORLD_BASE_URL`/`GENESISWORLD_PRODUCT_KEY` need values in
`.env` here — leave `GENESISWORLD_USERNAME`/`PASSWORD` unset, each client
brings its own instead):

```bash
cp .env.example .env   # fill in your values — .env is gitignored
docker compose -f docker-compose.client-credentials.yml up -d
```

Each client then sends its own credentials when connecting — two ways,
whichever your MCP client supports:

**Standard `Authorization: Basic` header** (preferred — works with
Claude's built-in "Add custom connector" flow, which rejects arbitrary
custom header names unless Anthropic pre-approves them):

```json
{
  "mcpServers": {
    "cas-genesisworld": {
      "url": "http://localhost:8084/mcp",
      "headers": {
        "Authorization": "Basic base64(your-username:your-password)"
      }
    }
  }
}
```

**Or custom headers**, for clients that support arbitrary headers but have
no first-class "Basic Auth" option (if `Authorization: Basic` is present,
it wins — these are only used as a fallback):

```json
{
  "mcpServers": {
    "cas-genesisworld": {
      "url": "http://localhost:8084/mcp",
      "headers": {
        "X-GenesisWorld-Username": "your-username",
        "X-GenesisWorld-Password": "your-password"
      }
    }
  }
}
```

There is no product-key header (and no product-key slot in the
`Authorization` value either) — `GENESISWORLD_PRODUCT_KEY` on the server
is used for every client, always; clients can't set or override it. HTTP
transport only (there's no per-request header channel on stdio); a
request missing valid credentials via either path is rejected outright
(HTTP 401), it never falls back to a shared identity.

The MCP endpoint is now at `http://localhost:8084/mcp`. Point your client
at it:

```json
{
  "mcpServers": {
    "cas-genesisworld": {
      "url": "http://localhost:8084/mcp"
    }
  }
}
```

## Configuration

Two kinds of settings, kept strictly separate — no setting is both:
**Environments** configure the deployment (where the API lives, who
connects); **launch options** toggle behavior at startup.

### Environments

| Variable                   | Required | Purpose                                          |
| --------------------------- | -------- | ------------------------------------------------ |
| `GENESISWORLD_BASE_URL`    | **yes**  | Base URL of the REST service, e.g. `http://demo.cas.de/genesisrest.svc` |
| `GENESISWORLD_PRODUCT_KEY` | **yes**  | Sent as `X-CAS-PRODUCT-KEY` on every request. Always required — including in `--client-credentials` mode, where it identifies your product license and stays server-side, never client-suppliable |
| `GENESISWORLD_USERNAME`    | yes\*    | Basic Auth user                                   |
| `GENESISWORLD_PASSWORD`    | yes\*    | Basic Auth password                               |
| `MCP_TRANSPORT`            | no       | `http` (default in Docker) or `stdio`             |
| `MCP_HOST` / `MCP_PORT`    | no       | Bind address for HTTP mode (default `0.0.0.0:3000`) |
| `GENESISWORLD_MAX_RESULT_CHARS` | no  | Truncate oversized responses (default 60000 chars; `0` disables) |
| `GENESISWORLD_QUIET`       | no       | `true` disables per-request stderr logging        |

\* Required in practice for any real request to succeed — except in
`--client-credentials` mode (see below), where the server has no fixed
*user* identity and these two are not required (each client brings its
own instead).

### Launch options

| Flag                    | Required | Purpose                                          |
| ------------------------ | -------- | ------------------------------------------------ |
| `--read-only`            | no       | Registers only read tools for that session — mutating tools are not merely blocked, they don't exist |
| `--client-credentials`   | no       | Server holds no fixed genesisWorld *user* identity; each HTTP client authenticates itself via request headers (see "Multi-tenant" above). Product key stays server-side. HTTP transport only |

Pass launch options on the command line, or after the image name in
`docker run` (as shown above). None of them have an environment-variable
equivalent, by design.

## License

[MIT](./LICENSE)

---

Want to add a tool or understand how this is built? See
[AGENTS.md](./AGENTS.md) for architecture and contributor docs, and
[ROADMAP.md](./ROADMAP.md) for the project plan.