PSA/docs/mcp-server.md
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

157 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 Mindshosted 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.