Skip to main content
Glama
alga-psa-mcp

alga-psa-mcp

Official
by WYRE-AI

@wyre-ai/alga-psa-mcp

A read-only Apache-2.0 MCP server for Alga PSA, maintained by WYRE AI. It exposes 42 curated, hand-written read tools over stdio or stateless Streamable HTTP. Every tool is a plain GET in the read tier; the server ships no tool that creates, changes or deletes anything in Alga, and its HTTP client cannot send any other method or a request body.

The tools and schemas in this repository are hand-written. No generated API specification or Alga implementation code is included.

Least-privilege setup in Alga

  1. Create a dedicated internal user, for example ai-assistant.

  2. Assign a custom role with only the read permissions needed for tickets, clients, contacts, time, projects, assets, invoices and knowledge articles, plus read access to the lookup resources (boards, statuses, priorities, categories, services, contracts, users, teams) your tools use.

  3. Under that user's Profile → API Keys, create an API key with an expiry. Keys get that user's role permissions. We haven't confirmed whether enterprise-edition agent keys behave differently, so treat the key as having everything its user can do.

  4. Rotate by creating a new key, updating the deployment, and revoking the old key.

The server only reads, but Alga's role permissions still decide which records the user can see, so keep the role read-only too.

Related MCP server: servicenow-mcp

Connection and credentials

The REST client targets {instance-origin}/api/v1. Supply an origin such as https://psa.example.com or https://psa.example.com:8443, with an optional /api/v1 suffix (normalized to the origin), and no other path, userinfo, query or fragment.

Gateway HTTP

AUTH_MODE defaults to gateway for HTTP (unset or empty means gateway), and only gateway or env is accepted; any other value refuses to start. Send these headers on every request to /mcp:

Header

Required

Meaning

X-Alga-Instance-Url

Yes

The https:// address you use to sign in to Alga, for example https://psa.example.com. A trailing / or /api/v1 is fine; leave off any other path. Works for Nine Minds–hosted and self-hosted instances. Self-hosted instances on a public https:// URL are supported; instances on private networks are not.

X-Alga-Api-Key

Yes

API key for the Alga user this connection acts as. Create it in Alga under Profile → API Keys. The key gets that user's permissions, so use a dedicated user with a limited role.

X-Alga-Tenant-Id

No

Optional. Leave blank unless Alga support asks for it. A wrong value makes every request fail.

X-Gateway-S2S

Yes, except insecure local dev

Timestamped gateway proof

X-Conduit-Tool-Tiers

No

Comma-separated tiers for this request. read is the only tier this server has. The header only narrows the tiers the container serves and never widens them. Absent means no narrowing; present but empty (or naming no served tier) means no tools at all.

Gateway mode never falls back to environment credentials, including the tenant ID. Missing required credential headers return HTTP 401 JSON {error: ...}; invalid or disallowed instance URLs return HTTP 400 JSON before MCP handling. A tenant ID, when supplied, must be a UUID. Empty tenant values are ignored. Credentials are isolated using AsyncLocalStorage. Each request gets its own MCP server and stateless transport; no credential-bearing sessions persist. /mcp accepts only POST: after the S2S check, any other method (including a GET for a standalone SSE stream, or DELETE) gets HTTP 405 with Allow: POST.

Set a real CONDUIT_S2S_SECRET of at least 32 characters to start HTTP (generate one with openssl rand -hex 32). The gateway proof has the form t=<unix-seconds>,v1=<hex-HMAC-SHA256>, signing the literal t=<unix-seconds> with the configured secret. Proofs outside a five-minute window are refused. Deploy the vendor-specific derived S2S secret, not a shared gateway master secret. Empty or shorter secrets fail startup, and common placeholders such as changeme, secret and replace-with-a-real-secret are refused even in insecure dev mode. Values that look like a placeholder, repeat a short pattern (aaaa…, abcabc…) or use fewer than 8 distinct characters are refused too. The secret is never echoed in these errors.

GET /health is unauthenticated process liveness. It returns no credentials and never queries Alga. All /mcp requests require S2S before credential extraction or body-size handling. HTTP has no browser CORS allowance; use a trusted gateway.

Standalone environment

Stdio always uses environment credentials. For standalone HTTP, set AUTH_MODE=env; HTTP still requires S2S unless insecure development is enabled. In AUTH_MODE=env, HTTP refuses to start unless ALGA_INSTANCE_URL is a valid instance URL and ALGA_API_KEY is set. The server does not automatically load .env files.

Variable

Default

Purpose

ALGA_INSTANCE_URL

Required in standalone

The https:// address you use to sign in to Alga, for example https://psa.example.com. A trailing / or /api/v1 is fine; leave off any other path. Works for Nine Minds–hosted and self-hosted instances. Self-hosted instances on a public https:// URL are supported; instances on private networks are not.

ALGA_API_KEY

Required in standalone

API key for the Alga user this connection acts as. Create it in Alga under Profile → API Keys. The key gets that user's permissions, so use a dedicated user with a limited role.

ALGA_TENANT_ID

Unset

Optional. Leave blank unless Alga support asks for it. A wrong value makes every request fail.

AUTH_MODE

HTTP: gateway; stdio: env credentials

HTTP only: gateway or env, anything else refuses to start. Stdio always uses env credentials

MCP_TRANSPORT

stdio (Docker: http)

Transport when using the HTTP entry point

MCP_HTTP_PORT

8080

HTTP listener port

MCP_HTTP_HOST

0.0.0.0 with S2S

Bind host; insecure dev forces 127.0.0.1

MCP_ALLOWED_HOSTS

Unset (insecure dev: loopback names)

Optional comma-separated Host header allowlist for /mcp (with or without port); any other Host gets 403. Unset accepts any Host, except in insecure dev, where only 127.0.0.1, localhost and [::1] are accepted, so a DNS-rebinding page cannot reach it

LOG_LEVEL

info

debug, info, warn, error; logs use stderr

Flags

Variable

Default

Behavior

ALGA_TOOL_TIERS

read

Comma-separated tiers the container serves (the fleet's <VENDOR>_TOOL_TIERS contract). read is the only valid value; anything else refuses to start

ALGA_ALLOW_PRIVATE_INSTANCE

Off

Exactly true allows private/loopback destinations and HTTP, for stdio and AUTH_MODE=env sandboxes only. HTTP refuses to start with it in AUTH_MODE=gateway, and gateway mode never honours it

CONDUIT_S2S_SECRET

Unset

Required for HTTP, at least 32 characters; never sent to Alga

MCP_ALLOW_INSECURE_DEV

Off

Exactly 1 permits HTTP without S2S only on loopback, and only with NODE_ENV=development (any other non-empty value other than 0 or false refuses to start without it); cannot bypass placeholder refusal

A tool removed for one request by X-Conduit-Tool-Tiers, or a name the server does not ship, is refused with the same unknown-tool error. Every tool carries readOnlyHint: true, destructiveHint: false.

SSRF policy and request safety

By default the instance must use HTTPS, with a hostname, optional port, and no userinfo, paths other than /api/v1 (with optional trailing slash), query or fragment. Private and special-use IP literals are refused, as are special-use and internal names (localhost, internal, local, test, invalid, example and their subdomains, and *.home.arpa), before any DNS lookup. DNS resolution checks every returned address. It blocks loopback, private, link-local (including metadata IPs), CGNAT, unspecified, multicast, reserved and broadcast destinations; the Azure platform address 168.63.129.16; IPv6 local/multicast ranges; IPv4-mapped and compatible IPv6; and NAT64/6to4 addresses embedding blocked IPv4 destinations.

An undici Agent resolves again when opening each socket and passes only checked numeric addresses to the connector, preventing DNS rebinding between validation and connection. TLS continues to verify the original hostname. Redirects are never followed, including redirects to another public origin. The sandbox-only ALGA_ALLOW_PRIVATE_INSTANCE=true override permits private networks and HTTP, retains URL structural restrictions, and logs a loud warning once at startup. It only works for stdio and AUTH_MODE=env: gateway mode ignores it, and HTTP in gateway mode refuses to start while it is set. No request header can enable it.

Each tool call has one 30-second deadline that covers every attempt, wait and body read, and it stops early if the MCP request is cancelled. HTTP 429 and 502/503/504 retry at most three times, honoring seconds or HTTP-date Retry-After values up to 5 seconds; a longer Retry-After, or a retry whose wait would pass the deadline, returns the error at once. Upstream responses are read as a stream and capped at 1 MiB; a larger one is aborted with an error asking you to narrow the request. One process runs at most 16 Alga calls at once, and one connection (API key) at most 4; a call beyond either limit fails at once with a retryable error, so a slow or hostile instance cannot exhaust the shared container's memory or hold every slot while other tenants wait. HTTP /mcp refuses JSON-RPC batch bodies (the 2025-06 protocol has none), so one request is at most one tool call. Other failures become concise tool errors. From an Alga error we keep only its code (for example NOT_FOUND) and the x-request-id response header, and only when each is a plain token; Alga's error message, details and network exception text are not returned.

Every path parameter is checked again just before it goes into the URL (no empty value, ., .., /, \, encoded separators or dots, or control characters) and is percent-encoded. The built URL must land on exactly {instance-origin}/api/v1{path}. Query values are single scalars: Alga keeps one value per query key, so an array is refused rather than sent as repeated or key[] parameters. Where Alga takes a list (for example client_ids on ticket search or types on alga_search), the tool takes one comma-separated string, which Alga splits.

Tool input uses closed schemas and bounded identifiers. HTTP bodies are capped at 256 KiB, including chunked bodies; a body that passes the cap gets HTTP 413 and the connection is closed without reading the rest. Tool arguments are capped at 10,000 elements. Responses are compact JSON retaining data and pagination, capped at 100 KiB. A list over the cap keeps its pagination and other top-level fields and drops rows from the end of data (for alga_search, from data.results, keeping groups, totalCount and nextCursor); it stays valid JSON and adds truncated: true, returned_items, total_items_in_page and a truncation_hint built from the tool's own arguments. Paginated tools and alga_search say to repeat the request with a smaller limit, because the next page or cursor starts after the rows left out; alga_tickets_comments_list says which offset to continue from; tools that return everything at once say to narrow the request. Any other response over the cap is cut as text and ends with a truncation note, so narrow the query before parsing it as JSON. Known API keys and S2S secrets are redacted from output. Logs go to stderr and are redacted, including the per-request gateway API key.

Untrusted content

Ticket subjects and comments, notes, names, descriptions and knowledge base text can be written by clients and outside senders. Every successful tool result is wrapped as <untrusted-content nonce="...">...</untrusted-content nonce="...">, preceded by a notice that it is untrusted data and not instructions. The nonce is random per response. The data inside is left unchanged (no HTML escaping), except that anything resembling an untrusted-content marker is defused with a ‹ so it cannot forge a closing tag. The truncation note, when present, stays inside the envelope. Error messages are always our own fixed text plus, at most, Alga's error code and request ID tokens; Alga's error messages are never forwarded.

Running locally

Node 22 or newer is required; CI runs Node 22 and 24, and Docker uses Node 22.

npm ci --ignore-scripts
npm run lint              # ESLint + destructive-warning lint
npm run typecheck
npm run build
npm test                  # offline; DNS and fetch are mocked
npm run smoke             # spawns dist/index.js over stdio; no network

For stdio, set ALGA_INSTANCE_URL, ALGA_API_KEY and optional ALGA_TENANT_ID in your MCP client's environment, then configure the command:

{
  "mcpServers": {
    "alga": {
      "command": "node",
      "args": ["/absolute/path/to/alga-psa-mcp/dist/index.js"],
      "env": {
        "ALGA_INSTANCE_URL": "https://psa.example.com",
        "ALGA_API_KEY": "<service-user-api-key>"
      }
    }
  }
}

For local HTTP with credentials already exported in your shell:

NODE_ENV=development AUTH_MODE=env MCP_ALLOW_INSECURE_DEV=1 MCP_HTTP_HOST=127.0.0.1 npm run start:http

Leave CONDUIT_S2S_SECRET unset for this local-only bypass. Production must use a real secret, a trusted gateway and TLS at the gateway ingress.

Docker

docker build -t alga-psa-mcp:test .
# Export a securely generated CONDUIT_S2S_SECRET before starting.
docker run --rm -p 127.0.0.1:8080:8080 \
  -e CONDUIT_S2S_SECRET alga-psa-mcp:test

The image uses node:22-alpine pinned by digest, installs with --ignore-scripts, runs as the non-root alga user, defaults to AUTH_MODE=gateway and ALGA_TOOL_TIERS=read, and exposes port 8080. It refuses to start without valid S2S configuration. Inject Alga credentials only via the per-request headers, and restrict network ingress to the trusted gateway. The healthcheck uses /health.

To use the image over stdio, override its HTTP defaults:

docker run --rm -i -e MCP_TRANSPORT=stdio -e AUTH_MODE=env \
  -e ALGA_INSTANCE_URL -e ALGA_API_KEY -e ALGA_TENANT_ID alga-psa-mcp:test

Tool catalog

Paginated list tools accept page (default 1) and limit (default 25, 1–100, Alga's maximum). page is passed through as given (1 or more; Alga has no page maximum). Every limit is bounded by its own tool's schema and sent unchanged. Each call makes one request; the server never fetches further pages on its own. alga_ticket_categories_list also takes page, but Alga's category service pages by offset only, so the server sends offset = (page - 1) × limit with it, and it sorts with sort_by/sort_order. sort is always an enum of the columns that route's Alga service can order by; alga_assets_list offers only order, because Alga's asset sort is not safe to drive by column name. fields (columns to return) is offered only on alga_tickets_list, the one list Alga applies it to, as an enum of Alga's allowed ticket fields. Date filters are typed: YYYY-MM-DD for calendar dates and a UTC date-time such as 2026-03-01T00:00:00Z for timestamps.

Search tools (alga_tickets_search, alga_contacts_search, alga_assets_search) are not paginated: they return up to limit matches. alga_tickets_comments_list uses Alga's limit (up to 200) and offset. alga_search uses Alga's cursor pagination: pass the returned nextCursor as cursor with the same query, types and sort. alga_schedules_list calls GET /schedules/search, the paginated twin of GET /schedules, which returns every entry at once.

Identifiers are strings, at most 128 characters, matching ^[A-Za-z0-9_\-:.]+$; standalone . and .. identifiers are refused. IDs from other systems (external_id, external_parent_id) are only sent as query values and accept any text up to 1,000 characters. Tool inputs expose path identifiers as id except project phase lists, task lists and task status lists use project_id, phase selection uses phase_id, and task get uses task_id.

Each tool's query keys are checked against the keys its Alga route actually reads (src/__tests__/alga-contract.test.ts, vendored from Alga's schemas, controllers and the service each controller calls), so a filter Alga would silently drop fails the build. The same file checks how each list pages (page or offset), every sort allowlist, and that each tool's maximum limit reaches Alga unchanged. Tests use mocked responses and don't contact a live Alga instance.

Three Alga read endpoints are deliberately not separate tools. GET /api/v1/tickets/statuses and /tickets/priorities overlap alga_statuses_list and alga_priorities_list. GET /api/v1/assets/{id}/summary is left out because its route handler never authenticates the API key (it calls a session-based server action), so it is not expected to work with an API key.

Project phase lists, task lists and task status lists are not paginated. alga_projects_tasks_list takes project_id and optional phase_id.

Read — 42 tools

Tool

Route

Returns / key filters

alga_assets_get

GET /assets/{id}

Get one asset by ID. Use after a list or search to see full details.

alga_assets_list

GET /assets

List assets, one page at a time. Use to browse or filter by client, type, status or location; use alga_assets_search to find assets by text.

alga_assets_search

GET /assets/search

Search assets by text and return up to limit matches (not paginated). To filter by client, type or status, use alga_assets_list instead.

alga_boards_list

GET /boards

List ticket boards, optionally filtered by name search, one page at a time. Inactive boards are left out unless include_inactive is true.

alga_clients_contacts_list

GET /clients/{id}/contacts

List contacts belonging to one client, one page at a time. Use alga_contacts_list to browse across clients or alga_contacts_search to find a person by name or email.

alga_clients_get

GET /clients/{id}

Get one client by ID. Use after a list or search to see full details.

alga_clients_list

GET /clients

List clients, one page at a time. Use to browse or filter by name search, type or inactive state.

alga_clients_locations_list

GET /clients/{id}/locations

List all locations belonging to a client (not paginated).

alga_contacts_get

GET /contacts/{id}

Get one contact by ID. Use after a list or search to see full details.

alga_contacts_list

GET /contacts

List contacts, one page at a time. Use to browse or filter by client or inactive state; use alga_contacts_search to find a person by name or email.

alga_contacts_search

GET /contacts/search

Search contacts by name, email or other columns and return up to limit matches (not paginated). Optionally limit to one client or include inactive contacts.

alga_contracts_list

GET /contracts

List client contracts, one page at a time (each has owner_client_id and owner_client_name); templates are not included. Filter by contract name, active state or whether a contract has plans. There is no per-client filter here.

alga_invoices_get

GET /invoices/{id}

Get one invoice by ID. Use after a list or search to see full details.

alga_invoices_items_list

GET /invoices/{id}/items

List all line items on an invoice (not paginated).

alga_invoices_list

GET /invoices

List invoices, one page at a time. Filter by client, status, invoice date range or due date range (YYYY-MM-DD, inclusive).

alga_kb_articles_get

GET /kb-articles/{id}

Get one knowledge article by ID. Use after a list or search to see full details.

alga_kb_articles_list

GET /kb-articles

List knowledge articles, one page at a time. Use to browse or filter by search, category, type, audience or status.

alga_priorities_list

GET /priorities

List priorities, one page at a time. Use to browse or filter by priority name search or by what they apply to (item_type).

alga_projects_get

GET /projects/{id}

Get one project by ID. Use after a list or search to see full details.

alga_projects_list

GET /projects

List projects, one page at a time. Use to browse or filter by client, status or search.

alga_projects_phases_list

GET /projects/{project_id}/phases

List all phases in a project. This list is not paginated.

alga_projects_task_status_mappings_list

GET /projects/{project_id}/task-status-mappings

List the task statuses a project uses, in the order Alga shows them, with the ID Alga uses for each (project_status_mapping_id). Pass phase_id to get the statuses for one phase; if that phase has no statuses of its own, you get the project's.

alga_projects_tasks_get

GET /projects/tasks/{task_id}

Get one project task by ID. Use after a list or search to see full details.

alga_projects_tasks_list

GET /projects/{project_id}/tasks

List all tasks in a project. Pass phase_id to list only one phase; get phase IDs from alga_projects_phases_list. These lists are not paginated.

alga_schedules_list

GET /schedules/search

List schedule entries, one page at a time. Filter by user or date range.

alga_search

GET /search

Search across record types at once. Use when you don't know the record type; pass the returned nextCursor as cursor to get more results.

alga_services_list

GET /services

List services, one page at a time. Use to browse or filter by name search or category.

alga_statuses_list

GET /statuses

List statuses, one page at a time. Filter by type (ticket, project, project_task or interaction) or name search. Ticket statuses belong to a board, so type=ticket also needs board_id.

alga_teams_list

GET /teams

List teams of users, one page at a time. Use to browse or filter by team name, manager or whether a team has a manager.

alga_ticket_categories_list

GET /categories/ticket

List ticket categories, one page at a time. Use to browse or filter by board, parent category or name search.

alga_tickets_assets_list

GET /tickets/{id}/assets

List all assets linked to a ticket (not paginated).

alga_tickets_comments_list

GET /tickets/{id}/comments

List comments on a ticket. Internal notes are included for internal users; each comment's is_internal flag says which. Use limit (up to 200) and offset to read long threads in parts.

alga_tickets_get

GET /tickets/{id}

Get one ticket by ID. Use after a list or search to see full details.

alga_tickets_get_by_external_link

GET /tickets/by-external-link

Get the ticket linked to a record in another system, using that system's name and the record's ID there.

alga_tickets_list

GET /tickets

List tickets, one page at a time. Use to browse or filter by client, board, status, priority, assignee, open or closed, dates or external link.

alga_tickets_search

GET /tickets/search

Search tickets by text and return up to limit matches (not paginated). Closed tickets are left out unless include_closed is true. Narrow with comma-separated client, status, priority or assignee IDs.

alga_tickets_time_entries_list

GET /tickets/{id}/time-entries

Get the time logged on a ticket (not paginated). Users who can read every time sheet see all entries; others see their own entries plus a total for everyone else's.

alga_time_entries_get

GET /time-entries/{id}

Get one time entry by ID. Use after a list or search to see full details.

alga_time_entries_list

GET /time-entries

List time entries, one page at a time. Use to browse or filter by user, work item, dates or billable state.

alga_time_sheets_list

GET /time-sheets

List time sheets, one page at a time. Use to browse or filter by user, period or approval state.

alga_users_get

GET /users/{id}

Get one user profile by ID. Use after a list or search to see full details.

alga_users_list

GET /users

List users, one page at a time. Use to browse or filter by search or inactive state.

Development and licensing

src/tools/<domain>.ts contains the hand-written tool definitions and closed JSON schemas. src/tools/index.ts holds the single tool inventory. Tests assert the read-only contract (42 tools, every one a GET in the read tier), annotations, excluded routes, tool-list size, each route's query keys against Alga's own contract, wire requests, input limits, S2S behavior, SSRF protection, the retry deadline and the template security contract (src/__tests__/contract.test.ts). No test contacts Alga. CI (ci.yml) runs npm ci --ignore-scripts, lint, typecheck, build, the destructive-warning lint, a process.env credential-mutation guard, tests and the stdio smoke test on Node 22 and 24, plus a Docker build. mcp-assert.yml runs the fleet baseline plus evals/, and gitleaks.yml scans history.

release.yml calls the pinned fleet release pipeline. On pull requests and manual dispatch it runs only the verify job; a push to main cuts a release, pushes ghcr.io/wyre-ai/alga-psa-mcp and gates it on a HIGH/CRITICAL Trivy scan. server.json names that OCI image with 0.0.0 placeholders, which the pipeline fills in.

Apache-2.0; see LICENSE and THIRD_PARTY_NOTICES.md. No Alga implementation code is included. Alga PSA is a trademark of Nine Minds. This project is not affiliated with Nine Minds.

Available Tools

42 tools
alga_assets_getA
Read-only

Get one asset by ID. Use after a list or search to see full details.

Inputs: id (required).

alga_assets_listA
Read-only

List assets, one page at a time. Use to browse or filter by client, type, status or location; use alga_assets_search to find assets by text.

Inputs: page, limit, order, search, +4 more.

alga_boards_listA
Read-only

List ticket boards, optionally filtered by name search, one page at a time. Inactive boards are left out unless include_inactive is true.

Inputs: page, sort, limit, order, +2 more.

alga_clients_contacts_listA
Read-only

List contacts belonging to one client, one page at a time. Use alga_contacts_list to browse across clients or alga_contacts_search to find a person by name or email.

Inputs: id (required), page, limit, search, +1 more.

alga_clients_getA
Read-only

Get one client by ID. Use after a list or search to see full details.

Inputs: id (required).

alga_clients_listA
Read-only

List clients, one page at a time. Use to browse or filter by name search, type or inactive state.

Inputs: page, sort, limit, order, +3 more.

alga_clients_locations_listA
Read-only

List all locations belonging to a client (not paginated).

Inputs: id (required).

alga_contacts_getA
Read-only

Get one contact by ID. Use after a list or search to see full details.

Inputs: id (required).

alga_contacts_listA
Read-only

List contacts, one page at a time. Use to browse or filter by client or inactive state; use alga_contacts_search to find a person by name or email.

Inputs: page, sort, limit, order, +3 more.

alga_contracts_listA
Read-only

List client contracts, one page at a time (each has owner_client_id and owner_client_name); templates are not included. Filter by contract name, active state or whether a contract has plans. There is no per-client filter here.

Inputs: page, sort, limit, order, +3 more.

alga_invoices_getA
Read-only

Get one invoice by ID. Use after a list or search to see full details.

Inputs: id (required).

alga_invoices_items_listA
Read-only

List all line items on an invoice (not paginated).

Inputs: id (required).

alga_invoices_listA
Read-only

List invoices, one page at a time. Filter by client, status, invoice date range or due date range (YYYY-MM-DD, inclusive).

Inputs: page, sort, limit, order, +6 more.

alga_kb_articles_getA
Read-only

Get one knowledge article by ID. Use after a list or search to see full details.

Inputs: id (required).

alga_kb_articles_listB
Read-only

List knowledge articles, one page at a time. Use to browse or filter by search, category, type, audience or status.

Inputs: page, sort, limit, order, +5 more.

alga_priorities_listA
Read-only

List priorities, one page at a time. Use to browse or filter by priority name search or by what they apply to (item_type).

Inputs: page, sort, limit, order, +2 more.

alga_projects_getA
Read-only

Get one project by ID. Use after a list or search to see full details.

Inputs: id (required).

alga_projects_listA
Read-only

List projects, one page at a time. Use to browse or filter by client, status or search.

Inputs: page, sort, limit, order, +3 more.

alga_projects_phases_listB
Read-only

List all phases in a project. This list is not paginated.

Inputs: project_id (required).

alga_projects_tasks_getA
Read-only

Get one project task by ID. Use after a list or search to see full details.

Inputs: task_id (required).

alga_projects_tasks_listA
Read-only

List all tasks in a project. Pass phase_id to list only one phase; get phase IDs from alga_projects_phases_list. These lists are not paginated.

Inputs: project_id (required), phase_id.

alga_projects_task_status_mappings_listA
Read-only

List the task statuses a project uses, in the order Alga shows them, with the ID Alga uses for each (project_status_mapping_id). Pass phase_id to get the statuses for one phase; if that phase has no statuses of its own, you get the project's.

Inputs: project_id (required), phase_id.

alga_schedules_listB
Read-only

List schedule entries, one page at a time. Filter by user or date range.

Inputs: page, limit, user_id, end_date, +1 more.

alga_services_listA
Read-only

List services, one page at a time. Use to browse or filter by name search or category.

Inputs: page, sort, limit, order, +2 more.

alga_statuses_listA
Read-only

List statuses, one page at a time. Filter by type (ticket, project, project_task or interaction) or name search. Ticket statuses belong to a board, so type=ticket also needs board_id.

Inputs: page, sort, type, limit, +3 more.

alga_teams_listA
Read-only

List teams of users, one page at a time. Use to browse or filter by team name, manager or whether a team has a manager.

Inputs: page, sort, limit, order, +3 more.

alga_ticket_categories_listA
Read-only

List ticket categories, one page at a time. Use to browse or filter by board, parent category or name search.

Inputs: page, limit, search, sort_by, +3 more.

alga_tickets_assets_listB
Read-only

List all assets linked to a ticket (not paginated).

Inputs: id (required).

alga_tickets_comments_listA
Read-only

List comments on a ticket. Internal notes are included for internal users; each comment's is_internal flag says which. Use limit (up to 200) and offset to read long threads in parts.

Inputs: id (required), limit, order, offset, +1 more.

alga_tickets_getA
Read-only

Get one ticket by ID. Use after a list or search to see full details.

Inputs: id (required).

alga_tickets_listB
Read-only

List tickets, one page at a time. Use to browse or filter by client, board, status, priority, assignee, open or closed, dates or external link.

Inputs: page, sort, limit, order, +15 more.

alga_tickets_time_entries_listB
Read-only

Get the time logged on a ticket (not paginated). Users who can read every time sheet see all entries; others see their own entries plus a total for everyone else's.

Inputs: id (required).

alga_time_entries_getA
Read-only

Get one time entry by ID. Use after a list or search to see full details.

Inputs: id (required).

alga_time_entries_listA
Read-only

List time entries, one page at a time. Use to browse or filter by user, work item, dates or billable state.

Inputs: page, sort, limit, order, +12 more.

alga_time_sheets_listA
Read-only

List time sheets, one page at a time. Use to browse or filter by user, period or approval state.

Inputs: page, sort, limit, order, +3 more.

alga_users_getA
Read-only

Get one user profile by ID. Use after a list or search to see full details.

Inputs: id (required).

alga_users_listB
Read-only

List users, one page at a time. Use to browse or filter by search or inactive state.

Inputs: page, sort, limit, order, +2 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.4/5.0

    Scored across 42 tools

    Disambiguation4/5

    Most tools target clearly distinct resource/action pairs, and descriptions explicitly distinguish list vs search vs get and entity-scoped vs global listings. Minor overlap remains between alga_search and the per-entity search tools, and between nested list tools such as alga_clients_contacts_list and alga_contacts_list, but the descriptions make the intended use reasonably clear.

    Naming Consistency5/5

    Tool names consistently use the alga_ prefix followed by snake_case resource and action names, including nested resources like alga_tickets_comments_list and alga_projects_tasks_get. The pattern is predictable throughout the set despite some longer names.

    Tool Count2/5

    The server exposes 42 tools, which is well above the range where a tool set remains easy to navigate. While the PSA domain is broad, the surface is heavy and likely to overwhelm agents with many similar list/get/search variants.

    Completeness2/5

    The set provides extensive read coverage across tickets, clients, contacts, projects, invoices, assets, users, and reference data, but it appears to be read-only. There are no create, update, delete, or mutation tools for core PSA workflows such as creating tickets, adding comments, logging time, or updating invoice/client records, which are significant gaps for a PSA server.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      D
      maintenance
      A read-only MCP server that connects AI assistants to Freshservice Service Desk for ticket triage, asset analysis, and support history summarization.
      23
      ISC
    • A
      license
      A
      quality
      D
      maintenance
      A read-only MCP server that enables AI assistants to query ServiceNow instances—incidents, changes, users, CMDB—with malformed query linting and injection protection.
      7
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      A read-only MCP server for the NuMetric.work accounting/POS/ERP platform, exposing 38 tools to query live business data such as financial statements, invoices, taxes, projects, inventory, and documents. It enables AI assistants to answer from real accounting data without any create, edit, or delete capabilities.
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      Read-only MCP server for Dynamics 365 Business Central, enabling AI clients to query live ERP data like customers, items, orders, and invoices via GET-only APIs with dual authentication and stdio/HTTP transports.
      10
      MIT