alga-psa-mcp
OfficialClick on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@alga-psa-mcp show me all open tickets for Acme Corp"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@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
Create a dedicated internal user, for example
ai-assistant.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.
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.
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 |
| 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. |
| 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. |
| No | Optional. Leave blank unless Alga support asks for it. A wrong value makes every request fail. |
| Yes, except insecure local dev | Timestamped gateway proof |
| No | Comma-separated tiers for this request. |
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 |
| 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. |
| 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. |
| Unset | Optional. Leave blank unless Alga support asks for it. A wrong value makes every request fail. |
| HTTP: | HTTP only: |
|
| Transport when using the HTTP entry point |
|
| HTTP listener port |
|
| Bind host; insecure dev forces |
| Unset (insecure dev: loopback names) | Optional comma-separated |
|
|
|
Flags
Variable | Default | Behavior |
|
| Comma-separated tiers the container serves (the fleet's |
| Off | Exactly |
| Unset | Required for HTTP, at least 32 characters; never sent to Alga |
| Off | Exactly |
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 networkFor 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:httpLeave 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:testThe 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:testTool 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 |
|
| Get one asset by ID. Use after a list or search to see full details. |
|
| 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. |
|
| Search assets by text and return up to limit matches (not paginated). To filter by client, type or status, use alga_assets_list instead. |
|
| List ticket boards, optionally filtered by name search, one page at a time. Inactive boards are left out unless include_inactive is true. |
|
| 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. |
|
| Get one client by ID. Use after a list or search to see full details. |
|
| List clients, one page at a time. Use to browse or filter by name search, type or inactive state. |
|
| List all locations belonging to a client (not paginated). |
|
| Get one contact by ID. Use after a list or search to see full details. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| Get one invoice by ID. Use after a list or search to see full details. |
|
| List all line items on an invoice (not paginated). |
|
| List invoices, one page at a time. Filter by client, status, invoice date range or due date range (YYYY-MM-DD, inclusive). |
|
| Get one knowledge article by ID. Use after a list or search to see full details. |
|
| List knowledge articles, one page at a time. Use to browse or filter by search, category, type, audience or status. |
|
| List priorities, one page at a time. Use to browse or filter by priority name search or by what they apply to (item_type). |
|
| Get one project by ID. Use after a list or search to see full details. |
|
| List projects, one page at a time. Use to browse or filter by client, status or search. |
|
| List all phases in a project. This list is not paginated. |
|
| 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. |
|
| Get one project task by ID. Use after a list or search to see full details. |
|
| 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. |
|
| List schedule entries, one page at a time. Filter by user or date range. |
|
| 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. |
|
| List services, one page at a time. Use to browse or filter by name search or category. |
|
| 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. |
|
| 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. |
|
| List ticket categories, one page at a time. Use to browse or filter by board, parent category or name search. |
|
| List all assets linked to a ticket (not paginated). |
|
| 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. |
|
| Get one ticket by ID. Use after a list or search to see full details. |
|
| Get the ticket linked to a record in another system, using that system's name and the record's ID there. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| Get one time entry by ID. Use after a list or search to see full details. |
|
| List time entries, one page at a time. Use to browse or filter by user, work item, dates or billable state. |
|
| List time sheets, one page at a time. Use to browse or filter by user, period or approval state. |
|
| Get one user profile by ID. Use after a list or search to see full details. |
|
| 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 toolsalga_assets_getARead-only
Get one asset by ID. Use after a list or search to see full details.
Inputs: id (required).
alga_assets_listARead-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_assets_searchARead-only
Search assets by text and return up to limit matches (not paginated). To filter by client, type or status, use alga_assets_list instead.
Inputs: query (required), limit, include_extension_data.
alga_boards_listARead-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_listARead-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_getARead-only
Get one client by ID. Use after a list or search to see full details.
Inputs: id (required).
alga_clients_listARead-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_listARead-only
List all locations belonging to a client (not paginated).
Inputs: id (required).
alga_contacts_getARead-only
Get one contact by ID. Use after a list or search to see full details.
Inputs: id (required).
alga_contacts_listARead-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_contacts_searchARead-only
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.
Inputs: query (required), limit, fields, client_id, +1 more.
alga_contracts_listARead-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_getARead-only
Get one invoice by ID. Use after a list or search to see full details.
Inputs: id (required).
alga_invoices_items_listARead-only
List all line items on an invoice (not paginated).
Inputs: id (required).
alga_invoices_listARead-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_getARead-only
Get one knowledge article by ID. Use after a list or search to see full details.
Inputs: id (required).
alga_kb_articles_listBRead-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_listARead-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_getARead-only
Get one project by ID. Use after a list or search to see full details.
Inputs: id (required).
alga_projects_listARead-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_listBRead-only
List all phases in a project. This list is not paginated.
Inputs: project_id (required).
alga_projects_tasks_getARead-only
Get one project task by ID. Use after a list or search to see full details.
Inputs: task_id (required).
alga_projects_tasks_listARead-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_listARead-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_listBRead-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_searchARead-only
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.
Inputs: query (required), sort, limit, types, +1 more.
alga_services_listARead-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_listARead-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_listARead-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_listARead-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_listBRead-only
List all assets linked to a ticket (not paginated).
Inputs: id (required).
alga_tickets_comments_listARead-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_getARead-only
Get one ticket by ID. Use after a list or search to see full details.
Inputs: id (required).
alga_tickets_get_by_external_linkARead-only
Get the ticket linked to a record in another system, using that system's name and the record's ID there.
Inputs: system (required), external_id (required), external_parent_id.
alga_tickets_listBRead-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_searchARead-only
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.
Inputs: query (required), limit, fields, client_ids, +4 more.
alga_tickets_time_entries_listBRead-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_getARead-only
Get one time entry by ID. Use after a list or search to see full details.
Inputs: id (required).
alga_time_entries_listARead-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_listARead-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_getARead-only
Get one user profile by ID. Use after a list or search to see full details.
Inputs: id (required).
alga_users_listBRead-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
Scored across 42 tools
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.
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.
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.
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
Related MCP Connectors
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Unofficial remote MCP server for Less Annoying CRM: OAuth 2.1, read-only grants, 10 tools.
1Read-only MCP server for ClassQuill, a tutoring-business-management platform.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Related MCP Servers
- AlicenseBqualityDmaintenanceA read-only MCP server that connects AI assistants to Freshservice Service Desk for ticket triage, asset analysis, and support history summarization.23ISC
- AlicenseAqualityDmaintenanceA read-only MCP server that enables AI assistants to query ServiceNow instances—incidents, changes, users, CMDB—with malformed query linting and injection protection.7MIT
- AlicenseNot gradedqualityCmaintenanceA 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
- AlicenseAqualityDmaintenanceRead-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.10MIT