# For developers

> Build an MCP client or an agent on Taskadence — Streamable HTTP, token or OAuth sign-in, tool annotations, rate limits and errors.

**What this is:** the protocol details for your own client or agent. **When you need it:** you are building on the server rather than using an off-the-shelf client.

**Python**


```python
import asyncio, os
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def main():
    headers = {"Authorization": f"Bearer {os.environ['TASKADENCE_TOKEN']}"}
    async with streamablehttp_client("https://api.taskadence.com/mcp", headers=headers) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print([t.name for t in tools.tools])

asyncio.run(main())
```

**TypeScript**


```ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const transport = new StreamableHTTPClientTransport(new URL("https://api.taskadence.com/mcp"), {
  requestInit: { headers: { Authorization: `Bearer ${process.env.TASKADENCE_TOKEN}` } },
});
const client = new Client({ name: "my-agent", version: "0.1.0" });
await client.connect(transport);
console.log((await client.listTools()).tools.map((t) => t.name));
```

## Transport

Streamable HTTP, one URL for every organization, stateless: each POST carries one JSON-RPC message and gets a JSON answer — no session id, no server-sent stream. `?readonly=1` and `?groups=` narrow the tools on the server ([choosing groups](https://docs.taskadence.com/mcp/tools/#choosing-groups)). The stdio packages expose the same tools.

## Signing in

-   **An access token** as a bearer — for your own agents and service accounts. Mint it with the scopes the agent needs; see [authentication](https://docs.taskadence.com/guides/authentication/).
-   **OAuth 2.1** — for a client acting for people who sign in: server metadata, a client ID metadata document or dynamic registration, PKCE and refresh tokens. The consent screen lists the scopes; the token acts as the person who approved it.

## Discovery and registration

A client needs only the server URL: the `401` points at the metadata. The client then uses a **client ID metadata document** (Claude does) or registers itself.

| Step | URL |
| --- | --- |
| Protected-resource metadata | `https://api.taskadence.com/.well-known/oauth-protected-resource/mcp` |
| Authorization-server metadata | `https://api.taskadence.com/.well-known/oauth-authorization-server` |
| Registration | `https://api.taskadence.com/oauth/register` |
| Authorization | `https://api.taskadence.com/oauth/authorize` |
| Token | `https://api.taskadence.com/oauth/token` |
| Revocation | `https://api.taskadence.com/oauth/revoke` |

### Client ID metadata document

Your `client_id` is your JSON document’s `https://` URL:

**client.json**

```json
{
  "client_id": "https://agent.example.com/oauth/client.json",
  "client_name": "My agent",
  "client_uri": "https://agent.example.com",
  "redirect_uris": ["http://localhost/callback", "http://127.0.0.1/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}
```

| Rule |  |
| --- | --- |
| Advertised | `client_id_metadata_document_supported: true`, with `none` in `token_endpoint_auth_methods_supported`. Taskadence fetches the document when a person authorizes. |
| The URL | `https://`, a path, no fragment or credentials. The document’s `client_id` must equal it exactly. |
| The document | `application/json`, at most 64 KiB, served without a redirect from a public address, answering within 5 seconds. `token_endpoint_auth_method` must be `none`; no secret. The other fields follow the registration rules below. |
| Refresh | Taskadence keeps a copy for a day, and fetches it again sooner when a request names a redirect URI the copy lacks. |
| Consent | The screen shows your `client_name` with the document’s host — “My agent — agent.example.com”. |
| Errors | A document that breaks a rule fails the authorization with `invalid_client`. |

### Dynamic client registration

Register once — there is no secret:

**Register a client**

```bash
curl -s https://api.taskadence.com/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My agent",
    "redirect_uris": ["http://localhost:8765/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "token_endpoint_auth_method": "none"
  }'
```

### Rules

| Rule |  |
| --- | --- |
| Client type | Public: `token_endpoint_auth_method` must be `none` (a secret method is `invalid_client_metadata`); PKCE with `S256` on every authorization. |
| Redirect URIs | 1 to 10, exact strings: `https://…`, `http://localhost` or `http://127.0.0.1`, `cursor://`, `vscode://`, `vscode-insiders://`, `claude://`. A loopback URI without a port, such as `http://localhost/callback`, matches that URI on any port. |
| Scopes | `scope` in the registration or document, else `tasks:read` `tasks:write` `projects:read` `teams:read` `members:read` — 37 tools on the default URL. An authorization without `scope` asks for all of them; the token response’s `scope` is what the person approved. |
| Consent | Once per client and organization; `prompt=consent` shows the screen anyway. |
| Limits | Registrations, and new metadata documents, are limited per address; a client no one connects within 30 days is revoked. No read-back or update of a registration — register again. |
| Standards | OAuth 2.1, PKCE (RFC 7636), server metadata (RFC 8414), resource metadata (RFC 9728), registration (RFC 7591), revocation (RFC 7009), client ID metadata documents, the MCP authorization specification. |

## Tool annotations

Every tool has a `title` and all four hints — `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`. Use them to confirm changes with your user; destructive tools are the **admin** group, which also requires `admin`. The [tools reference](https://docs.taskadence.com/mcp/tools/) has each tool’s input schema.

## Rate limits

Tool calls go through the API with your token, so its per-token limits apply — 600 requests a minute for a live token, 60 for a test one. See [rate limits](https://docs.taskadence.com/guides/rate-limits/).

## Errors

A failed tool call is a result with `isError: true`. Its text reads `<title>: <detail> (ref <request_id>)`; its structured content is the API’s problem — `type`, `status`, `title`, `detail`, `request_id` and, for a validation error, `errors` — the same [problem types](https://docs.taskadence.com/guides/errors/) as the REST API. A rate-limited call adds `retry_after_seconds`; the server never retries. A missing or refused token is an HTTP `401` (above), not a tool result.

## MCP or the SDK?

The [Python SDK](https://docs.taskadence.com/sdks/python/) calls the REST API: use it when your code decides which calls to make. Use MCP when a model decides.

## Acting for your product’s users

A “Connect Taskadence” button in another product is an OAuth app: register it under **Developers → OAuth apps** ([`oauth-clients.create`](https://docs.taskadence.com/reference/operations/oauth-clientscreate/)) and run the same flow; the token acts for the person who consented. Server to server, use a service account’s token.

---
Source: https://docs.taskadence.com/mcp/for-developers/
