Skip to main content
Glama
upservice-mcp

upservice-mcp

by alexherbaly

Upservice MCP Server

Читать на русском

An MCP (Model Context Protocol) server that wraps the Upservice Public API so Claude (or any MCP client) can manage employees, projects, sprints, tasks, tags, directories, contacts, sales pipelines, and channel messages directly.

Built from the OpenAPI spec at https://public.upservice.io/openapi.json, verified against the live API.

What it can do

68 tools covering:

  • Employees — list employees

  • Projects — list, create, get, update, delete, set managers/members, mark completed

  • Sprints — list, create, get, update, delete, complete, activate, add tasks

  • Tags — list, create, update, delete, assign/unassign to tasks/chats/assets/contacts/attachments, list the tags on one entity

  • Tasks — list/search, create, get, update, delete, attachments, status changes, effort estimation, worklog (read entries / log effort), agreement (approval) workflow actions, co-responsibles, agreement/acquaintance sheets

  • Directories (custom reference catalogs) — list/create/get/update/delete directories and their records, plus bulk relation management (linking records to tasks, projects, orders, etc.)

  • Contacts — list/search with filters (status, responsible, company, channel, tags, creation date, etc.), get, create, partially update, archive, restore. As with task filters, the live API applies only the last of several id/status/responsible/company/channel/channel_kind values, so the tool takes one value per call (tags_ids accepts several)

  • Pipelines (sales funnels) — list, get; each pipeline carries its statuses (stages)

  • Channels & files — load/send chat messages, send channel messages, upload files, get file download URLs

Every write tool exposes the well-documented fields explicitly (validated), and most also accept an extra_fields dict for advanced/uncommon fields documented in the Upservice API but not modeled individually.

Mentioning employees: Upservice only turns @Name into a real, notifying mention if it's written as @[Name](employee_id). Tools that write text (upservice_send_chat_message, upservice_send_channel_message, upservice_create_task, upservice_update_task) accept a mentions: [{employee_id, display_name}] field — put a {{employee_id}} placeholder in your text and the server substitutes the correct syntax for you.

Task filters: upservice_list_tasks filters server-side by status, sprint_id, and without_sprint (true — only tasks with no sprint, false — only tasks with one). The API documents several status/sprint_id values as OR, but the live API applies only the last one, so the tool accepts a single value per call. without_sprint=true together with sprint_id is rejected by the tool before any request is sent.

Related MCP server: Unipile Universal MCP Server

Setup

Get your Upservice API key first: in your Upservice account, go to account settings → API key. Keys look like UPS-XXXX-XXXX-XXXX-XXXX.

Option A — uvx (recommended, no manual install)

Requires uv installed (brew install uv or see their docs). git is not needed.

  1. Download the project as a ZIP: open the repository on GitHub and click Code → Download ZIP.

  2. Unpack it wherever you like (e.g. ~/upservice-mcp). The folder you point at below is the one that contains pyproject.toml.

  3. Add to your MCP client config (e.g. claude_desktop_config.json), using the absolute path to that folder:

{
  "mcpServers": {
    "upservice": {
      "command": "uvx",
      "args": ["--from", "/absolute/path/to/upservice-mcp", "upservice-mcp"],
      "env": {
        "UPSERVICE_API_KEY": "UPS-XXXX-XXXX-XXXX-XXXX"
      }
    }
  }
}

uv builds and caches an isolated environment on first run; nothing is installed system-wide.

Updating: there is no auto-update. To get a newer version, download a fresh ZIP, replace the folder's contents, and restart your MCP client — uv rebuilds the environment from the changed source.

Option B — plain venv + pip

cd upservice_mcp
python3 -m venv venv
venv/bin/pip install -r requirements.txt
{
  "mcpServers": {
    "upservice": {
      "command": "/absolute/path/to/upservice_mcp/venv/bin/python3",
      "args": ["/absolute/path/to/upservice_mcp/src/upservice_mcp/server.py"],
      "env": {
        "UPSERVICE_API_KEY": "UPS-XXXX-XXXX-XXXX-XXXX"
      }
    }
  }
}

Restart Claude Desktop afterwards. The tools will appear prefixed with upservice_.

Optional: custom base URL

If Upservice ever changes the API host, or you use a private/on-prem instance, override it:

"env": {
  "UPSERVICE_API_KEY": "UPS-XXXX-XXXX-XXXX-XXXX",
  "UPSERVICE_API_BASE_URL": "https://public.upservice.io"
}

Testing locally

# uvx
UPSERVICE_API_KEY=UPS-XXXX uvx --from . upservice-mcp

# venv
UPSERVICE_API_KEY=UPS-XXXX venv/bin/python3 src/upservice_mcp/server.py

Or test interactively with the MCP Inspector:

npx @modelcontextprotocol/inspector uvx --from . upservice-mcp

Notes

  • Authentication uses a raw API key in the Authorization header (not a Bearer token) — this server handles that for you.

  • Destructive tools (delete_*, unassign_tag) are annotated with destructiveHint: true so MCP clients can warn users appropriately.

  • Some request bodies documented by Upservice have many optional fields; where a tool doesn't model a field explicitly, pass it via extra_fields (a plain JSON object) and it will be merged into the request body.

  • Never share your own UPSERVICE_API_KEY — each person should generate their own from their Upservice account settings.

Available Tools

58 tools
upservice_activate_sprintB
Idempotent

Activate a sprint (start it).

Args: params (SprintIdInput): sprint_id (int)

Returns: str: JSON of the updated sprint.

Inputs: params (required). Returns: result

upservice_add_tasks_to_sprintA

Add one or more existing tasks to a sprint.

Args: params (AddTasksToSprintInput): sprint_id (int), tasks (List[int] of task IDs)

Returns: str: JSON list of task IDs now in the sprint.

Inputs: params (required). Returns: result

upservice_assign_tagA
Idempotent

Assign (attach) a tag to an entity such as a task, chat, asset, contact, or attachment.

Args: params (AssignTagInput): tag_id (UUID), entity_id (UUID or int), entity_type (enum)

Returns: str: JSON confirmation of the assignment.

Inputs: params (required). Returns: result

upservice_bulk_update_directory_relationsA

Add or remove multiple links between a directory record and other entities (tasks, projects, orders, etc.) in one call.

Args: params (BulkUpdateRelationsInput): record_id (int), relations (list of {rel_type, relation_ids, is_delete})

Returns: str: JSON confirmation of the applied relation changes.

Inputs: params (required). Returns: result

upservice_complete_projectA
Idempotent

Mark a project as completed.

Args: params (ProjectIdInput): project_id (int)

Returns: str: JSON of the updated project record.

Inputs: params (required). Returns: result

upservice_complete_sprintB
Idempotent

Mark a sprint as completed.

Args: params (SprintIdInput): sprint_id (int)

Returns: str: JSON of the updated sprint.

Inputs: params (required). Returns: result

upservice_create_directoryB

Create a new custom directory (reference catalog).

Args: params (CreateDirectoryInput): title, manager_id (int), parent_id (optional int)

Returns: str: JSON of the created directory.

Inputs: params (required). Returns: result

upservice_create_directory_recordA

Create a new record within a directory (e.g. an asset or contact entry).

Args: params (CreateDirectoryRecordInput): title, category (directory ID), responsible (employee ID), description (optional), inventory_number (optional), extra_fields (optional dict)

Returns: str: JSON of the created record.

Inputs: params (required). Returns: result

upservice_create_external_messageB

Create a new external/inbound message (e.g. from an external channel integration).

Args: params (SendMessageInput): content (str), message_id (optional UUID str), mentions (optional)

Returns: str: JSON of the created message.

Inputs: params (required). Returns: result

upservice_create_projectB

Create a new project in Upservice.

Args: params (CreateProjectInput): - title (str): Project title - managers (List[int]): Employee IDs to assign as project managers - members (List[int]): Employee IDs to assign as project members - extra_fields (Optional[dict]): Extra raw JSON fields to merge into the body

Returns: str: JSON of the created project record.

Inputs: params (required). Returns: result

upservice_create_sprintA

Create a new sprint under a project.

Args: params (CreateSprintInput): title, project (int), date_start (YYYY-MM-DD), date_end (YYYY-MM-DD), extra_fields (optional dict)

Returns: str: JSON of the created sprint.

Inputs: params (required). Returns: result

upservice_create_tagB

Create a new tag definition.

Args: params (CreateTagInput): name (str), color (optional str), type (optional: text|number|address|date|richard)

Returns: str: JSON of the created tag, including its UUID.

Inputs: params (required). Returns: result

upservice_create_taskA

Create a new task (or meeting/agreement/ticket, depending on kind).

Args: params (CreateTaskInput): title, kind, description, project, sprint, responsible, responsible_departments, estimation (minutes), date_start, date_end, file_list, mentions, extra_fields

Returns: str: JSON of the created task record, including its ID.

Examples: - "Create a task 'Fix login bug' in project 42, due 2026-08-01" -> title="Fix login bug", project=42, date_end="2026-08-01T00:00:00Z" - "Create a task and mention Ivan (employee_id 115768) in the description" -> description="cc {{115768}}", mentions=[{"employee_id": 115768, "display_name": "Ivan Ivanov"}]

Inputs: params (required). Returns: result

upservice_delete_directoryA
DestructiveIdempotent

Permanently delete a directory (and, depending on account settings, its records). Destructive operation.

Args: params (DirectoryIdInput): directory_id (int)

Returns: str: JSON confirmation, or "Error: ..." on failure.

Inputs: params (required). Returns: result

upservice_delete_directory_recordA
DestructiveIdempotent

Permanently delete a directory record. Destructive operation.

Args: params (RecordIdInput): record_id (int)

Returns: str: JSON confirmation, or "Error: ..." on failure.

Inputs: params (required). Returns: result

upservice_delete_projectA
DestructiveIdempotent

Permanently delete a project. This is a destructive operation and cannot be undone.

Args: params (ProjectIdInput): project_id (int)

Returns: str: JSON confirmation, or "Error: ..." on failure.

Inputs: params (required). Returns: result

upservice_delete_sprintA
DestructiveIdempotent

Permanently delete a sprint. This is a destructive operation.

Args: params (SprintIdInput): sprint_id (int)

Returns: str: JSON confirmation, or "Error: ..." on failure.

Inputs: params (required). Returns: result

upservice_delete_tagA
DestructiveIdempotent

Permanently delete a tag definition (removing it from all entities it was applied to).

Args: params (UpdateTagInput): only tag_id is used (UUID str)

Returns: str: JSON confirmation, or "Error: ..." on failure.

Inputs: params (required). Returns: result

upservice_delete_taskA
DestructiveIdempotent

Permanently delete a task. This is a destructive operation and cannot be undone.

Args: params (TaskIdInput): task_id (int)

Returns: str: JSON confirmation, or "Error: ..." on failure.

Inputs: params (required). Returns: result

upservice_get_acquaintance_sheetA
Read-onlyIdempotent

Get the acquaintance sheet (who has read/acknowledged) for a task of kind 'acquaintance'.

Args: params (TaskIdInput): task_id (int)

Returns: str: JSON with acknowledgement status per employee.

Inputs: params (required). Returns: result

upservice_get_agreement_sheetA
Read-onlyIdempotent

Get the agreement sheet (approval status and attachments) for a task.

Args: params (TaskIdInput): task_id (int)

Returns: str: JSON with status and attachments for the agreement.

Inputs: params (required). Returns: result

upservice_get_agreement_stepsB
Read-onlyIdempotent

List the approval/agreement steps and their statuses for a task.

Args: params (TaskIdInput): task_id (int)

Returns: str: JSON list of agreement steps.

Inputs: params (required). Returns: result

upservice_get_channel_file_urlA
Read-onlyIdempotent

Get a fresh, time-limited download URL for a file/attachment scoped to an external channel.

Args: params (ChannelFileIdInput): channel_unique_identifier (str), file_id (str)

Returns: str: JSON containing the file's download URL.

Inputs: params (required). Returns: result

upservice_get_chat_messagesB
Read-onlyIdempotent

Get messages for a specific chat room within a specific channel.

Args: params (GetChatMessagesInput): channel_unique_identifier (str), room_uuid (str), limit, offset

Returns: str: JSON list of messages in that room.

Inputs: params (required). Returns: result

upservice_get_directoryA
Read-onlyIdempotent

Retrieve a single directory by ID.

Args: params (DirectoryIdInput): directory_id (int)

Returns: str: JSON of the directory record.

Inputs: params (required). Returns: result

upservice_get_directory_recordB
Read-onlyIdempotent

Retrieve a single directory record by ID.

Args: params (RecordIdInput): record_id (int)

Returns: str: JSON of the record.

Inputs: params (required). Returns: result

upservice_get_file_urlB
Read-onlyIdempotent

Get a fresh, time-limited download URL for a file/attachment.

Args: params (FileIdInput): file_id (str)

Returns: str: JSON containing the file's download URL.

Inputs: params (required). Returns: result

upservice_get_projectA
Read-onlyIdempotent

Retrieve a single project by ID.

Args: params (ProjectIdInput): project_id (int)

Returns: str: JSON of the project record, or "Error: Resource not found (404)" if it doesn't exist.

Inputs: params (required). Returns: result

upservice_get_sprintB
Read-onlyIdempotent

Retrieve a single sprint by ID.

Args: params (SprintIdInput): sprint_id (int)

Returns: str: JSON of the sprint record.

Inputs: params (required). Returns: result

upservice_get_taskA
Read-onlyIdempotent

Retrieve full details for a single task by ID.

Args: params (TaskIdInput): task_id (int)

Returns: str: JSON of the task record.

Inputs: params (required). Returns: result

upservice_get_task_attachmentsA
Read-onlyIdempotent

List file attachments on a task.

Args: params (TaskIdInput): task_id (int)

Returns: str: JSON list of attachments (id, filename, url, etc. - use upservice_get_file_url for a fresh download link).

Inputs: params (required). Returns: result

upservice_get_task_co_responsiblesA
Read-onlyIdempotent

List the co-responsible employees assigned to a task.

Args: params (TaskIdInput): task_id (int)

Returns: str: JSON list of co-responsible employees.

Inputs: params (required). Returns: result

upservice_list_chat_messagesB
Read-onlyIdempotent

Load messages from one or more Upservice chat rooms.

Args: params (ListChatMessagesInput): chat_uuids (required list), channel_id (required), limit, offset, thread_ids, language, sender_id, message_kind (all optional)

Returns: str: JSON list of messages.

Inputs: params (required). Returns: result

upservice_list_directoriesA
Read-onlyIdempotent

List custom directories (reference catalogs, e.g. assets, contacts) defined in the account.

Note: this endpoint has no pagination in the Upservice API; it returns all matching directories.

Args: params (ListDirectoriesInput): parent, search, id, manager (all optional)

Returns: str: JSON list of directories.

Inputs: params (required). Returns: result

upservice_list_directory_record_relationsA
Read-onlyIdempotent

List the entities (tasks, orders, projects, etc.) linked/related to a directory record.

Args: params (RecordIdInput): record_id (int)

Returns: str: JSON list of related entities, grouped by relation type.

Inputs: params (required). Returns: result

upservice_list_directory_recordsA
Read-onlyIdempotent

List records within directories, optionally filtered to one or more directories.

Args: params (ListDirectoryRecordsInput): limit, offset, category, responsible, creator, search, id, tags_ids, tags_condition, date_end_gte/lte, is_subscribed (all optional)

Returns: str: JSON list of directory records.

Inputs: params (required). Returns: result

upservice_list_employeesA
Read-onlyIdempotent

List employees in the Upservice account.

Args: params (ListEmployeesInput): - limit (Optional[int]): Page size, 1-100 (default 25) - offset (Optional[int]): Items to skip for pagination (default 0)

Returns: str: JSON array/object of employees as returned by the Upservice API (fields typically include id, first_name, last_name, email, position, department).

Error Handling: Returns "Error: ..." with an actionable message on failure (see error codes below).

Inputs: params (required). Returns: result

upservice_list_projectsA
Read-onlyIdempotent

List projects in the Upservice account.

Args: params (ListProjectsInput): limit (1-100, default 25), offset (default 0), status, tags_condition, tags_ids

Returns: str: JSON list of projects (id, title, managers, members, completed, etc.)

Inputs: params (required). Returns: result

upservice_list_sprintsB
Read-onlyIdempotent

List sprints, optionally filtered by project.

Args: params (ListSprintsInput): limit, offset, project (optional list of IDs), status (optional list), is_lag (optional bool)

Returns: str: JSON list of sprints.

Inputs: params (required). Returns: result

upservice_list_tagsA
Read-onlyIdempotent

List tags defined in the Upservice account, optionally filtered by a search query.

Args: params (ListTagsInput): limit, offset, query (optional str)

Returns: str: JSON list of tags (id, name, color, type).

Inputs: params (required). Returns: result

upservice_list_tasksA
Read-onlyIdempotent

List/search tasks with optional filters for kind, project, author, responsible, date ranges, and tags.

CONFIRMED LIMITATION (verified against the live API, not just the OpenAPI spec): this endpoint has no status/status_in/is_completed/completed/query filter. Passing any of those as extra query params is silently ignored server-side (no error, no effect on results) — status filtering must be done client-side on the returned status field. There is no workaround on the Upservice API today; this has been reported to Upservice as a feature request. Until it lands, narrow results with date_end_gte/date_end_lte (due-date range) plus project/author/responsible BEFORE paginating and filtering by status client-side — e.g. for "open overdue tasks", pass date_end_lte=<now, ISO 8601> together with project/responsible to get a small candidate set, then drop any whose status is completed/cancelled/rejected/deleted. Do not call this with only author (or no filters) and try to page through everything — accounts can have 100k+ tasks and completed_at/date filters are the only server-side way to keep that bounded.

Args: params (ListTasksInput): limit, offset, kind, project, author, responsible, created_at_gte/lte, completed_at_gte/lte, date_start_gte/lte, date_end_gte/lte, tags_ids, tags_condition (all optional except pagination defaults)

Returns: str: JSON list of tasks matching the filters.

Inputs: params (required). Returns: result

upservice_send_channel_messageA

Create a new message directly in a channel (not tied to a specific chat room).

Args: params (SendChannelMessageInput): channel_unique_identifier (str), content (str), message_id, first_name, last_name, email, phone, mentions (all optional), extra_fields (optional dict)

Returns: str: JSON of the created message.

Inputs: params (required). Returns: result

upservice_send_chat_messageB

Send a message into a specific chat room within a channel.

Args: params (SendChatMessageInput): channel_id (str), room_uuid (str), content (str), message_id (optional), mentions (optional)

Returns: str: JSON of the sent message.

Inputs: params (required). Returns: result

upservice_set_project_managersA
Idempotent

Replace the full set of managers for a project.

Note: this REPLACES the existing manager list, it does not append to it.

Args: params (SetProjectEmployeesInput): project_id (int), employees (List[int], the new full manager list)

Returns: str: JSON confirmation/updated manager list.

Inputs: params (required). Returns: result

upservice_set_project_membersA
Idempotent

Replace the full set of members/guests for a project.

Note: this REPLACES the existing membership list, it does not append to it.

Args: params (SetProjectEmployeesInput): project_id (int), employees (List[int], the new full member list)

Returns: str: JSON confirmation/updated member list.

Inputs: params (required). Returns: result

upservice_task_agreement_actionA

Advance an agreement/approval workflow step on a task: progress it, approve it, or reject it.

Args: params (AgreementActionInput): task_id (int), action (progress|approved|rejected), rejection_reason (required if action='rejected'), date_end (optional, used with 'progress')

Returns: str: JSON of the updated agreement state.

Inputs: params (required). Returns: result

upservice_unassign_tagA
DestructiveIdempotent

Remove (detach) a tag from an entity.

Args: params (AssignTagInput): tag_id (UUID), entity_id (UUID or int), entity_type (enum)

Returns: str: JSON confirmation, or "Error: ..." on failure.

Inputs: params (required). Returns: result

upservice_update_directoryA
Idempotent

Update a directory's title or manager.

Args: params (UpdateDirectoryInput): directory_id (int), title (optional), manager_id (optional)

Returns: str: JSON of the updated directory.

Inputs: params (required). Returns: result

upservice_update_directory_recordA
Idempotent

Partially update a directory record's fields.

Only fields you explicitly pass are sent — an omitted field is left unchanged, while an explicit null (e.g. responsible=null) is sent as-is and clears that field server-side.

Args: params (UpdateDirectoryRecordInput): record_id (int) plus any fields to change, extra_fields for the rest

Returns: str: JSON of the updated record.

Inputs: params (required). Returns: result

upservice_update_projectA
Idempotent

Update an existing project's fields (e.g. title).

Args: params (UpdateProjectInput): project_id (int), title (optional str), extra_fields (optional dict)

Returns: str: JSON of the updated project record.

Inputs: params (required). Returns: result

upservice_update_sprintA
Idempotent

Update a sprint's title or dates.

Args: params (UpdateSprintInput): sprint_id (int), title/date_start/date_end (all optional), extra_fields (optional dict)

Returns: str: JSON of the updated sprint.

Inputs: params (required). Returns: result

upservice_update_tagB
Idempotent

Update an existing tag's name or color.

Args: params (UpdateTagInput): tag_id (UUID str), name (optional), color (optional)

Returns: str: JSON of the updated tag.

Inputs: params (required). Returns: result

upservice_update_taskA
Idempotent

Partially update a task's fields (title, description, dates, responsible, etc).

Only fields you provide are changed; omitted fields are left unchanged.

Args: params (UpdateTaskInput): task_id (int) plus any fields to change, mentions (for description), and extra_fields for anything not explicitly modeled

Returns: str: JSON of the updated task record.

Inputs: params (required). Returns: result

upservice_update_task_estimationA
Idempotent

Set the planned-effort estimate for a task, in minutes.

Args: params (UpdateTaskEstimationInput): task_id (int), estimation (int, minutes)

Returns: str: JSON of the updated task.

Inputs: params (required). Returns: result

upservice_update_task_statusA
Idempotent

Change a task's status (e.g. mark as completed, cancelled, in progress).

Args: params (UpdateTaskStatusInput): task_id (int), status (enum), reason (optional str, needed for cancel/reject)

Returns: str: JSON of the updated task.

Inputs: params (required). Returns: result

upservice_update_task_worklogA

Log actual effort (worklog) spent on a task, in minutes.

Args: params (UpdateTaskWorklogInput): task_id (int), value (int, minutes of actual effort)

Returns: str: JSON of the updated task/worklog.

Inputs: params (required). Returns: result

upservice_upload_fileA

Upload a local file to Upservice (e.g. to attach it to a task via its returned file ID).

Args: params (UploadFileInput): file_path (absolute local path)

Returns: str: JSON of the uploaded file record, including its ID.

Inputs: params (required). Returns: result

upservice_upload_file_to_channelB

Upload a local file to an external channel in Upservice.

Args: params (UploadFileToChannelInput): channel_unique_identifier (str), file_path (absolute local path)

Returns: str: JSON of the uploaded file record, including its ID.

Inputs: params (required). Returns: result

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    B3.3/5.0

    Scored across 58 tools

    Disambiguation2/5

    The messaging tools overlap heavily: list_chat_messages vs get_chat_messages are nearly indistinguishable from their descriptions, and create_external_message vs send_channel_message vs send_chat_message blur the boundary between a channel, a room, and an external message. Outside messaging, most task/project/sprint/directory tools are clearly distinct, but the chat subset creates real misselection risk.

    Naming Consistency4/5

    Names overwhelmingly follow the upservice_<verb>_<noun> pattern, which is a strong and predictable convention. The main deviations are upservice_task_agreement_action (noun-first instead of verb-first) and the mixed create_/send_ verbs across the message tools, but these are minor against the overall consistency.

    Tool Count1/5

    58 tools is well past the 50+ threshold and is extreme for a single MCP server, even for a broad project-management API. The agent-facing surface is bloated and would be far more navigable if split into focused servers by domain (tasks, projects, directories, chat).

    Completeness4/5

    The server provides solid CRUD coverage for tasks, projects, sprints, tags, directories, and directory records, plus file upload/download and task workflow operations. Notable gaps include no way to discover channels/chat rooms, no remove-tasks-from-sprint operation, and no update/delete for messages, which agents may need to work around.

    Maintenance

    ActivityMaintained
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers