# AlgaPSA MCP Server AlgaPSA exposes its functionality to AI agents over the **Model Context Protocol (MCP)** through a small, constant **3-tool** surface (progressive disclosure) rather than one tool per API endpoint: | Tool | Purpose | |------|---------| | `search_api_registry` | Find the right API endpoint by natural-language query | | `search_business_data` | Find tenant records (tickets, clients, …), ACL-scoped | | `call_api_endpoint` | Execute a chosen endpoint | There are two delivery forms. ## 1. Local connector (CE / free) A workstation tool the user runs alongside an MCP client (Claude Desktop, Cursor). It calls the AlgaPSA API under the **user's own** API token, inheriting their RBAC/ABAC. No agent governance — the user's MCP client is the human-in-the-loop. **Setup:** 1. AlgaPSA → **Settings → API Keys** → create a key. 2. Add to the MCP client config: ```json { "mcpServers": { "alga-psa": { "command": "npx", "args": ["-y", "@alga-psa/mcp-connector"], "env": { "ALGA_INSTANCE_URL": "https://alga.example.com", "ALGA_API_TOKEN": "your-api-key" } } } } ``` Full details: [`packages/alga-mcp-connector/README.md`](../packages/alga-mcp-connector/README.md). ## 2. Remote governed server (EE) A networked MCP endpoint (`POST /api/mcp`, Streamable HTTP / JSON-RPC) that authenticates **agents** via the tenant's identity provider and enforces governance: distinct agent identity, RBAC, and an exportable audit trail. Enterprise-only. > **Dark release.** The admin UI (**Settings → MCP Server**) is gated behind the > `mcp-server` PostHog feature flag and is **off by default** — it appears only when the > tenant is Enterprise *and* the flag is enabled for them. This is a UI-only gate: the > server endpoints below stay live regardless, so an operator can configure agents via the > admin API even before the tab is rolled out. See `docs/features/feature-flags.md`. ### Auth model — OAuth 2.1 resource server (IdP delegation) AlgaPSA is an OAuth 2.1 **resource server** only; it does **not** issue tokens. Tokens come from the **tenant's IdP** (Entra / Google / Keycloak). On an unauthenticated request the server returns `401` + `WWW-Authenticate` pointing at the Protected Resource Metadata (`/.well-known/oauth-protected-resource`, RFC 9728), which names the trusted IdP(s). The server validates the bearer JWT (issuer, audience/resource, signature via JWKS) and maps the configured **subject claim** to a provisioned agent. > **Requirement:** a remote MCP server needs the tenant to have an IdP that can issue tokens > to a machine agent (client-credentials / service principal). A bare appliance with no IdP > can still run the free local connector. ### The easy path — provider presets, reuse, and hosted built-ins Registering an IdP used to mean typing a raw issuer + JWKS URL + audience. Three layers now remove that friction, mirroring how AlgaPSA's own Google/Microsoft SSO works. **1. Provider presets (any edition).** In **Settings → MCP Server** the trusted-IdP form offers **Microsoft Entra**, **Google**, or **Custom** instead of free text: - **Google** — nothing to enter. Issuer is fixed (`https://accounts.google.com`), the JWKS is fetched via OIDC discovery, and the subject claim defaults to `sub`. - **Microsoft Entra** — enter only the **Entra tenant id**. The issuer (`https://login.microsoftonline.com/{tid}/v2.0`) and JWKS are derived via discovery; the subject claim defaults to `azp` (app-only tokens). Use the concrete tenant id, **not** `common` — tokens are issued with the concrete `tid`, so `common` won't match on verify. - **Custom** — the original raw issuer / JWKS / audience / claim path, unchanged. `POST /api/v1/mcp/idp-providers` accepts `{ kind: 'google'|'microsoft'|'custom', entraTenantId? }` and resolves issuer + JWKS server-side. **2. Reuse an existing connection.** If the tenant already linked Microsoft (SSO / email / Teams), the form shows **"You're already connected to Microsoft — enable agent access?"** and one-click pre-fills the Entra preset with the known tenant id (read from `microsoft_profiles`). **3. Hosted built-ins (SaaS, near-zero-config).** On Nine Minds–hosted AlgaPSA, Google and Microsoft are **pre-trusted** using the shared OAuth apps that already back SSO — so a hosted tenant can provision agents with **no IdP registration at all**. When the shared-app secrets are present, the built-in issuers are advertised in the PRM and accepted at token validation exactly like a registered `agent_idp_providers` row. This covers **interactive / human-delegated** agents (auth-code + PKCE through the shared app); the customer configures nothing in their own directory. ### Admin setup (the journey) Either from the UI (**Settings → MCP Server**, EE only) or the admin API: 1. **Register the trusted IdP** — pick **Google** / **Microsoft Entra** (preset) or **Custom**. On hosted SaaS with built-ins, skip this entirely. `POST /api/v1/mcp/idp-providers` 2. **Give the agent a directory identity** — see the distinction below. Interactive agents reuse the human's IdP login; unattended agents need their own client-credentials principal. 3. **Provision the agent** — name, IdP issuer + subject, and the RBAC roles it may use. One agent per `(issuer, subject)` — re-binding an identity already claimed by an active agent returns a friendly `409`. `POST /api/v1/mcp/agents` 4. The agent's MCP client connects to `/api/mcp`; on `401` it reads the PRM, gets a token from the IdP (resource = the `/api/mcp` URL), and connects. 5. **Review/export audit** — `GET /api/v1/mcp/audit?agentId=…` or the UI audit viewer. Agents operate **only within their assigned roles** (an agent without a permission is denied), and **every tool call is audited** (identity, tool, inputs, decision, status). ### The key distinction — interactive vs unattended agents The easy path makes **interactive, human-delegated** agents zero-config: the agent acts as a signed-in human, so it rides the existing Google/Microsoft login (or the hosted built-ins) and needs no new directory object. **Unattended machine agents** (client-credentials, no human in the loop) are the irreducible exception: a token with no user behind it must come from a **directory identity the customer owns** — an **Entra app registration** or a **Google service account**. No preset removes that; it's a property of OAuth, not of AlgaPSA. For these, create the app/service-account in your own directory, then provision the agent with its `client_id` (Entra `azp`/`appid`) or service-account `sub` (Google) as the subject. The subject-claim guidance in the form names the right claim per provider. ### Admin API reference (EE, session-admin or API-key auth) | Method | Path | | |--------|------|--| | GET/POST | `/api/v1/mcp/idp-providers` | list / add trusted IdPs (POST takes `kind`/`entraTenantId` presets or raw custom fields) | | GET | `/api/v1/mcp/idp-suggestions` | suggested IdP from an existing connection (e.g. linked Microsoft → Entra tenant id) | | GET/POST | `/api/v1/mcp/agents` | list / provision agents (duplicate `(issuer, subject)` → `409`) | | GET | `/api/v1/mcp/roles` | assignable MSP roles | | GET | `/api/v1/mcp/audit` | export agent audit (`?agentId=`, `?limit=`) | ## Edition matrix | Capability | CE | EE | |---|:--:|:--:| | Local stdio connector (user-scoped) | ✓ | ✓ | | Registry endpoint (`/api/v1/meta/mcp-registry`) | ✓ | ✓ | | Remote MCP server (`/api/mcp`) + agent identity + IdP auth + audit | — | ✓ | ## Known MVP limitations - **PRM is instance-wide.** `/.well-known/oauth-protected-resource` lists all trusted issuers across tenants. Correct for a **single-tenant appliance**; a multi-tenant SaaS needs a per-tenant PRM (tenant hint via host/path) — tracked as a follow-up. - **Agent session keys** are short-lived (5 min) and swept opportunistically on each agent request; a periodic sweep job (`cleanupExpiredAgentKeys`) is also available. - **Dispatch** runs as a short-lived agent-scoped key against `/api/v1` (kernel-enforced); in-process dispatch is a later optimization. - **Not yet in MVP** (Phase 3): agent-specific ABAC policy, approval gates (human-in-the-loop), and quotas/rate-limits.