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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
157 lines
8.2 KiB
Markdown
157 lines
8.2 KiB
Markdown
# 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.
|