# Tools

De vier tools van de MCP-server van plnnrly, wat elke tool accepteert en teruggeeft, en welke limieten er gelden.

De MCP-server heeft geen tool per functie. Via drie algemene tools bereikt een AI-client elke operatie van de [REST API (Engels)](https://developers.plnnrly.com/en/docs/api/v2/), behalve bestanden uploaden, altijd namens jou en met jouw rechten. Een vierde tool geeft je eigen profiel terug. Krijgt de API er een functie bij, dan kunnen clients die direct gebruiken.

| Tool                    | Titel in clients  | Wat hij doet                           |
| ----------------------- | ----------------- | -------------------------------------- |
| `search_api`            | Search the API    | Zoekt API-operaties en beschrijft ze   |
| `api_read`              | Read from the API | Stuurt een `GET` naar een API-operatie |
| `api_write`             | Write to the API  | Stuurt een `POST`, `PATCH` of `DELETE` |
| `get-current-user-tool` | Get current user  | Geeft je profiel terug                 |

Drie tools lezen alleen. **Write to the API** is gemarkeerd als een tool die gegevens kan wijzigen, dus clients vragen standaard om je goedkeuring. Bij de meeste clients kun je hem toestaan voor één gesprek of voorgoed. [Rechten en veiligheid](https://developers.plnnrly.com/nl/docs/mcp/permissions-and-safety/) legt uit wat een client mag.

## Zo gebruikt een client de tools

Elke client krijgt van de server ook korte instructies. Die beschrijven drie stappen: zoek de operatie met `search_api`, lees wat die accepteert, en roep die dan aan met `api_read` of `api_write`. Ze vertellen de client ook dat:

- paden voor werkitems sleutels accepteren, zoals `ACM-12`
- gebruikersfilters `me` accepteren
- verwijderen `confirm: true` nodig heeft, dat de client pas meestuurt nadat jij akkoord bent gegaan, en alleen werkt als je organisatie het toestaat
- tekst aan een Drive-document wordt toegevoegd met `mode: append`, omdat anders het hele document wordt vervangen

## search_api

Zoekt API-operaties en beschrijft ze. Hij geeft alleen de eigen documentatie van de API terug, nooit gegevens uit je organisatie.

| Invoer         | Type   | Verplicht | Omschrijving                                                    |
| -------------- | ------ | --------- | --------------------------------------------------------------- |
| `query`        | string | nee       | Trefwoorden, zoals `link work items` of `log time`.             |
| `operation_id` | string | nee       | Een `id` uit een zoekresultaat, zoals `work-items.links.store`. |

- Met `query` geeft hij maximaal 12 treffers terug als `{id, method, path, summary}`, plus een `hint` voor de volgende stap.
- Met `operation_id` beschrijft hij die operatie: de beschrijving, het benodigde recht, padparameters, queryparameters, filters, sorteringen, includes, bodyvelden, de functie waar de operatie bij hoort, en een of twee voorbeelden. De id werkt met en zonder het voorvoegsel `api.v2.`.
- Zonder invoer somt hij elke operatie-id op, gegroepeerd per onderdeel.

Operaties die je organisatie niet kan gebruiken, worden weggelaten: die van onderdelen die je organisatie niet gebruikt, verwijderoperaties zolang **Destructieve acties toestaan** uitstaat, en bestandsuploads.

## api_read

Stuurt een `GET` naar elke API-operatie.

| Invoer  | Type   | Verplicht | Omschrijving                                                                                        |
| ------- | ------ | --------- | --------------------------------------------------------------------------------------------------- |
| `path`  | string | ja        | Het pad onder `/api/v2`, met de placeholders ingevuld, zoals `work-items/ACM-12/comments`.          |
| `query` | object | nee       | Queryparameters, zoals `{"filter": {"assignee_id": "me", "resolved": false}, "include": "labels"}`. |

### Paden en query

- Een leeg pad somt de resources op die de API aanbiedt.
- Het pad mag met `/api/v2` beginnen; dat voorvoegsel wordt genegeerd.
- Het hele pad is maximaal 256 tekens. De segmenten bevatten alleen letters, cijfers, `-` en `_`, en elke placeholder moet zijn ingevuld.
- Paden voor werkitems accepteren sleutels, zoals `work-items/ACM-12`. Gebruikersfilters accepteren `me`.
- Queryobjecten zijn maximaal drie niveaus diep, zoals `filter[due_date][gte]`, met in totaal maximaal 60 parameters.
- Null-waarden worden weggelaten. Lijsten worden omgezet in kommagescheiden waarden.

### Lijsten en pagina's

Lijsten komen terug in pagina's van 10 in de weergave `summary`, tenzij de aanroep om iets anders vraagt. Voor de volgende pagina geef je de `meta.page.next_cursor` van het vorige antwoord mee als `cursor`. Is `next_cursor` gelijk aan `null`, dan zijn er geen pagina's meer.

Sommige lijsten accepteren ook een paginanummer. Hun `meta.page` heeft altijd `current_page` en `last_page`, die `null` zijn tenzij de aanroep `page` meestuurt. Een aanroep met `page` krijgt altijd `next_cursor: null`. De laatste pagina is die waarop `current_page` gelijk is aan `last_page`.

## api_write

Stuurt een `POST`, `PATCH` of `DELETE` naar elke API-operatie.

| Invoer    | Type    | Verplicht | Omschrijving                                                                                      |
| --------- | ------- | --------- | ------------------------------------------------------------------------------------------------- |
| `method`  | string  | ja        | `POST`, `PATCH` of `DELETE`.                                                                      |
| `path`    | string  | ja        | Het pad onder `/api/v2`, zoals `work-items/ACM-12/links`.                                         |
| `query`   | object  | nee       | Queryparameters, zoals `{"view": "detail"}`.                                                      |
| `body`    | object  | nee       | De JSON-body, zoals `{"link_type": "blocks", "target_work_item_id": 42}`.                         |
| `confirm` | boolean | nee       | `true` bij een verwijdering of een overschrijving, en alleen nadat jij ermee akkoord bent gegaan. |

De regels voor pad en query van `api_read` gelden hier ook. De body wordt precies zo gecontroleerd als de REST API dat doet: een veld dat de operatie niet accepteert, wordt geweigerd met vermelding van de naam, en nooit genegeerd.

Verwijderen vraagt twee dingen: **Destructieve acties toestaan** moet aanstaan voor je organisatie, en de aanroep moet `confirm: true` meesturen. Ontbreekt een van beide, dan wordt er niets verwijderd. [Wat telt als verwijderen](https://developers.plnnrly.com/nl/docs/mcp/permissions-and-safety/#destructive) somt de operaties op waarvoor dit geldt. Bij `DELETE /projects/{project}` verwijdert de query `{"force": true}` het project definitief, in plaats van een verwijdering die je kunt terugdraaien.

De hele tekst van een Drive-document vervangen vraagt ook `confirm: true`. Daarmee verdwijnen namelijk de opmaak en alles wat anderen hebben getypt sinds de client het document las. Tekst toevoegen met `{"mode": "append"}` in de body vraagt geen bevestiging.

`api_write` weigert deze aanroepen voordat er iets gebeurt:

| Weigering                                                          | Probleemtype          |
| ------------------------------------------------------------------ | --------------------- |
| Een verwijdering terwijl **Destructieve acties toestaan** uitstaat | `forbidden-by-policy` |
| Een verwijdering of overschrijving zonder `confirm: true`          | `validation-failed`   |
| Een bestandsupload                                                 | `endpoint-not-found`  |
| Een methode die de operatie niet accepteert                        | `method-not-allowed`  |

Bij de eerste twee wordt het record niet gelezen voordat de aanroep wordt geweigerd, dus een weigering verraadt nooit of het record bestaat.

Schrijfacties via MCP zijn niet beschermd tegen dubbele uitvoering. Een `Idempotency-Key` is niet nodig en wordt niet gebruikt. Weet een client niet zeker of een schrijfactie is gelukt, dan moet hij eerst lezen voordat hij het opnieuw probeert.

Vroeg een app bij het inloggen ook om tokenrechten voor de REST API, dan is hij via MCP ook aan die tokenrechten gebonden. Tokenrechten die bij het inloggen worden gevraagd, omvatten nooit verwijderen, dus zo'n app kan via MCP niets verwijderen.

## get-current-user-tool

Geeft je eigen profiel terug. Deze tool heeft geen invoer.

```json
{
  "id": 34,
  "name": "Jamie Doe",
  "email": "jamie@acme.example",
  "email_verified_at": "2026-02-03T09:15:00+00:00",
  "timezone": "Europe/Amsterdam",
  "locale": "en",
  "job_title": "Project manager",
  "date_employed": "2025-09-01",
  "roles": ["Project manager"],
  "created_at": "2025-08-28T14:02:11+00:00"
}
```

De tijdstempels vermelden de tijdzone als afwijking van UTC. `date_employed` is een datum (`YYYY-MM-DD`). Velden zonder waarde zijn `null`.

## Antwoorden

Een geslaagd antwoord is de body die de API zelf teruggeeft, met twee wijzigingen:

- Vooraan staat een sleutel `status`, met de HTTP-status waarmee de API antwoordde, zoals `200` of `201`.
- Elk `links`-object is verwijderd. Een client bladert verder in een lijst met `meta.page.next_cursor`, en roept andere records aan via hun pad.

Een verwijdering antwoordt zonder body, dus de tool geeft `{"status": 204}` terug.

Een antwoord mag ongeveer 30.000 tekens bevatten. De limiet telt bytes, dus tekst met accenten of emoji past er iets minder in. Een langer antwoord wordt ingekort, nooit ongemerkt: `meta.mcp_truncated` zegt wat er is weggelaten. Zijn er rijen uit een lijst weggelaten, dan wordt `next_cursor` leeggemaakt, omdat je die rijen anders zou overslaan. De hint zegt hoe je zonder gaten verder bladert. Dit is het `meta`-object van een ingekorte lijst:

```json
{
  "page": {
    "per_page": 10,
    "next_cursor": null,
    "prev_cursor": null,
    "total": null
  },
  "mcp_truncated": {
    "reason": "response_budget",
    "returned": 6,
    "omitted": 4,
    "hint": "Only the first 6 of 10 rows fit. Repeat this request with per_page=6 (and the same cursor, if any) to page through without gaps."
  }
}
```

Is één lange tekst het probleem, dan somt `mcp_truncated` in plaats daarvan de ingekorte velden op, elk met zijn `pointer`, het aantal behouden tekens (`kept`) en de oorspronkelijke lengte (`length`).

## Fouten

Een geweigerde aanroep komt terug als toolfout. Die bevat hetzelfde foutdocument als de REST API teruggeeft, met dezelfde status en hetzelfde type; zie [Fouten en opnieuw proberen (Engels)](https://developers.plnnrly.com/en/docs/api/v2/guides/errors/). Iets wat je niet mag zien, geeft `404`, precies zoals via HTTP.

```json
{
  "type": "https://developers.plnnrly.com/api/v2/problems/forbidden-by-policy",
  "title": "Forbidden by policy",
  "status": 403,
  "detail": "This organisation does not allow AI clients to delete data. An administrator can turn on Allow destructive actions under Settings › Administration › MCP & API.",
  "instance": "/api/v2/work-items/ACM-12",
  "request_id": "01M4WVYY50K7T2B9QH3D6N8RXP"
}
```

Een pad of query die de tools niet kunnen gebruiken, geeft [malformed-request](https://developers.plnnrly.com/en/docs/api/v2/guides/errors/#malformed-request). De `errors[].code` daarvan is `invalid` (een pad of waarde die niet is toegestaan), `too_many` (meer dan 60 queryparameters) of `too_deep` (meer dan drie niveaus diep).

## Limieten

- Elke gebruiker mag 60 MCP-verzoeken per minuut sturen. Dat geldt voor elk verzoek dat de client naar de server stuurt, ook voor de verzoeken die de verbinding opzetten en de tools opvragen, niet alleen voor toolaanroepen. Een client boven de limiet krijgt `429`.
- De eigen rate limits en rate units van de REST API gelden niet voor aanroepen via MCP.
- Een antwoord bevat maximaal ongeveer 30.000 tekens.
- De client vernieuwt zijn inlog elk uur. Een client die 30 dagen niet is gebruikt, moet opnieuw inloggen.

[Limieten in één oogopslag (Engels)](https://developers.plnnrly.com/en/docs/api/v2/guides/limits/) somt elke limiet van de REST API op.

## Wat MCP niet kan

- **Bestanden uploaden.** Een bijlage of Drive-bestand toevoegen vraagt een upload van bytes, en die kunnen MCP-clients niet doen. Gebruik de REST API met een API-token; zie [Bestanden uploaden en downloaden (Engels)](https://developers.plnnrly.com/en/docs/api/v2/guides/uploads/).
- **Verwijderen zolang de schakelaar uitstaat.** Verwijderen werkt alleen zolang je organisatie destructieve acties toestaat.

## Zie ook

- [Wat je kunt vragen](https://developers.plnnrly.com/nl/docs/mcp/examples/)
- [Rechten en veiligheid](https://developers.plnnrly.com/nl/docs/mcp/permissions-and-safety/)
- [Problemen oplossen](https://developers.plnnrly.com/nl/docs/mcp/troubleshooting/)
- [Fouten en opnieuw proberen (Engels)](https://developers.plnnrly.com/en/docs/api/v2/guides/errors/)
