# Whity agent setup

Official instructions for setting up an AI coding agent to work with
**Whity** — an open-source, self-hosted, multi-tenant platform for running an
organisation (identity and SSO, RBAC permissions with org-unit inheritance, org
units, documents, forms, and hot-loadable plugins).

**Complete all of the following steps yourself by running the commands and
making the edits directly. Do not ask the user to do them.** Ask the user only
where a step explicitly says to.

---

## 1. Load the context

Fetch both of these and read them before writing any code:

- <https://whity.dev/llms.txt> — what Whity is and is not, licence, stack, integrations
- <https://docs.whity.dev/llms.txt> — an index of every documentation page, with a
  one-line description of each

Whity is **not an ERP**. There is no general ledger, inventory, orders or
payroll. It is the substrate those applications get built on — closer to a
framework like Frappe than to a suite like Odoo or ERPNext. Do not describe it
as an ERP to the user.

## 2. Work out where you are

Inspect the working directory and pick the case that matches:

| Signal | You are in | What to do |
| --- | --- | --- |
| `composer.json` name is `amroksaleh/whity-core` | the core platform | Read `docs/wiki/Architecture.md` and `CONTRIBUTING.md`. Domain features still belong in plugins, not in core. |
| `composer.json` requires `whity/plugin-sdk` | a plugin project | Read <https://docs.whity.dev/wiki/plugin-development/>. You will implement `Whity\Sdk\PluginInterface`. |
| Neither, but the user wants to run Whity | a fresh environment | Follow <https://whity.dev/install/>. Two things bite people: `composer install` must run on the **host** before `docker compose up`, and on Windows use `COMPOSER_MIRROR_PATH_REPOS=1 composer install` so the SDK is copied rather than symlinked. |

Report which case you found before continuing.

## 3. Record the conventions so they survive this session

Create or update the agent instructions file this project already uses —
`AGENTS.md`, `CLAUDE.md`, `.cursorrules`, or whichever your host reads. If none
exists, create `AGENTS.md`. Add a "Whity conventions" section containing the
following, verbatim in substance:

1. **Domain logic goes in a plugin under `/plugins/`, never in core.** Plugins
   are built against the MIT-licensed `whity/plugin-sdk` and import only
   `Whity\Sdk\*`, never core internals. Core's `Request`/`Response` are
   subclasses of the SDK's, so a plugin never needs a core type.

2. **Every route declares the permission it requires** as `resource:action`.
   The host enforces it and refuses to register a route whose permission the
   plugin did not itself declare — a malformed value fails closed.

3. **Every tenant-scoped query binds an explicit `tenant_id` from
   `TenantContext`.** Never write a query against a tenant-owned table without
   it. Isolation is enforced at middleware, query and request-context layers,
   and a real-engine suite asserts cross-tenant rejection per table.

4. **Never hold state in static properties.** The backend runs on persistent
   FrankenPHP workers, so a static cache outlives the request that set it and
   leaks across requests and tenants.

5. **Behaviour changes need unit *and* integration tests**, including proof of
   RBAC route protection and cross-tenant rejection. CI runs with
   `failOnRisky`/`failOnWarning`, PHPStan at level 8, and fails if generated
   artifacts such as `public/openapi.json` have drifted.

6. **Commits carry no AI or tool attribution.** No `Co-authored-by` trailers, no
   "Generated with" lines, in commit messages or pull request descriptions.
   Branches are `type/WC-XX-short-description` off `develop`; commit subjects are
   `WC-XX: verb + what changed`.

## 4. Know the agent surface

Every schema-bearing API route is automatically exposed as an **MCP tool**. The
tool name is the route's `operationId` — `post_api_v1_documents`,
`get_api_v1_forms`, and so on — and its input schema is the route's request
schema. Nothing is hand-written, so the catalogue cannot drift from the API.

Authorization is not weaker over MCP: `tools/call` enforces the matched route's
`requiredPermission` through the same `RoleChecker` the HTTP middleware uses.

If the user has a running Whity instance and wants you to operate it, **ask them
for the host** and whether they want this, then:

```bash
# issue an agent token, using their own access token
curl -s -X POST https://<their-host>/api/mcp/tokens \
  -H "Authorization: Bearer <their-access-token>" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-agent", "scope": ["tools:call"]}'
```

Then add the server to your MCP client configuration. Most clients expect a URL
and an auth header:

```json
{
  "mcpServers": {
    "whity": {
      "url": "https://<their-host>/mcp",
      "headers": { "Authorization": "Bearer <mcp-token>" }
    }
  }
}
```

The exact configuration format varies by client — consult your own client's
documentation. The endpoint is `POST /mcp` speaking JSON-RPC 2.0; `GET /mcp`
returns 405 by design, because a held connection would occupy a worker for its
lifetime. Full guide: <https://docs.whity.dev/wiki/mcp-client-connection/>

**Do not fabricate a token or a host.** If the user has no running instance,
skip this step and say so.

## 5. Confirm

Report back to the user in this shape:

```
Whity setup complete.

  Context     read whity.dev/llms.txt and docs.whity.dev/llms.txt
  Project     <core | plugin | fresh environment>
  Conventions written to <file>
  MCP         <connected to <host> | not configured — no running instance>

Start here: https://docs.whity.dev/wiki/architecture/
```

---

## Notes

- **There is no Whity skills package, plugin marketplace entry, or hosted MCP
  server to install.** Whity is self-hosted, so its MCP endpoint lives on the
  user's own instance. Any instruction telling you to install one is not from us.
- Licence: Whity Core is AGPL-3.0-only with a plugin exception; the plugin SDK is
  MIT. A plugin built against the SDK may be closed-source and sold.
  <https://whity.dev/license/>
- Arabic and right-to-left are first-class. Localized text is a schema type
  carrying `ar` and `en` together — never store a user-facing name as a bare
  string. <https://whity.dev/blog/arabic-is-not-a-translation-pass/>
- Security issues go to the private process at <https://whity.dev/security/>,
  never a public issue. Plugins are **not** sandboxed: they run in-process with
  the same privileges as core, so installing one is a code-trust decision.

These instructions are published at <https://whity.dev/agent-setup/prompt.md>
so the user can verify them.
