viviscape-mcp 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -16
- package/dist/api-client.d.ts +94 -1
- package/dist/api-client.js +314 -82
- package/dist/auth/auth-service.d.ts +1 -1
- package/dist/auth/auth-service.js +4 -4
- package/dist/auth/permissions.d.ts +1 -1
- package/dist/auth/permissions.js +5 -3
- package/dist/auth/token-store.d.ts +1 -1
- package/dist/auth/token-store.js +2 -2
- package/dist/config.d.ts +2 -2
- package/dist/config.js +2 -2
- package/dist/enums.d.ts +48 -0
- package/dist/enums.js +67 -0
- package/dist/index.js +330 -57
- package/dist/projection.d.ts +39 -0
- package/dist/projection.js +116 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# viviscape-mcp
|
|
2
2
|
|
|
3
|
-
MCP server for the [ViviScape](https://viviscape.io) API
|
|
3
|
+
MCP server for the [ViviScape](https://viviscape.io) API - exposes CRM, projects, companies, tasks, time tracking, notes, and insights as tools for Claude and other MCP clients.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
No install needed
|
|
7
|
+
No install needed - run via `npx`:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npx viviscape-mcp
|
|
@@ -18,7 +18,7 @@ npm install -g viviscape-mcp
|
|
|
18
18
|
|
|
19
19
|
## Sign in
|
|
20
20
|
|
|
21
|
-
Authentication is per-user, the same browser flow the ViviScape CLI uses
|
|
21
|
+
Authentication is per-user, the same browser flow the ViviScape CLI uses - no
|
|
22
22
|
API keys. Sign in once:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
@@ -74,7 +74,7 @@ args = ["-y", "viviscape-mcp"]
|
|
|
74
74
|
|
|
75
75
|
## ChatGPT (Developer Mode / Apps SDK)
|
|
76
76
|
|
|
77
|
-
ChatGPT only accepts **remote HTTP** MCP servers
|
|
77
|
+
ChatGPT only accepts **remote HTTP** MCP servers - it does not run local stdio
|
|
78
78
|
processes. Bridge this server to an HTTP endpoint with
|
|
79
79
|
[`mcp-remote`](https://www.npmjs.com/package/mcp-remote) or
|
|
80
80
|
[`supergateway`](https://www.npmjs.com/package/supergateway), then expose it
|
|
@@ -88,11 +88,11 @@ npx -y supergateway --stdio "npx -y viviscape-mcp" --port 8000
|
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
The gateway shares the host's stored session, so everyone reaching that endpoint
|
|
91
|
-
acts as the signed-in user
|
|
91
|
+
acts as the signed-in user - put your own auth in front of it.
|
|
92
92
|
|
|
93
93
|
Expose `http://localhost:8000/sse` publicly, then in ChatGPT:
|
|
94
94
|
|
|
95
|
-
**Settings
|
|
95
|
+
**Settings -> Connectors -> Create -> Custom MCP server**
|
|
96
96
|
- **URL:** `https://your-public-host/sse`
|
|
97
97
|
- **Auth:** none (or bearer, depending on your gateway)
|
|
98
98
|
|
|
@@ -100,16 +100,56 @@ Expose `http://localhost:8000/sse` publicly, then in ChatGPT:
|
|
|
100
100
|
|
|
101
101
|
The server exposes tools across these domains:
|
|
102
102
|
|
|
103
|
-
- **Prospects**
|
|
104
|
-
- **Clients**
|
|
105
|
-
- **Companies**
|
|
106
|
-
- **Projects**
|
|
107
|
-
- **Tasks**
|
|
108
|
-
- **
|
|
109
|
-
- **
|
|
110
|
-
- **
|
|
111
|
-
- **
|
|
112
|
-
- **
|
|
103
|
+
- **Prospects** - `prospect_add`, `prospect_get`, `prospect_update`, `prospect_query`, follow-ups, notes
|
|
104
|
+
- **Clients** - `client_add`, `client_get`, `client_get_by_email`, `clients_by_company`
|
|
105
|
+
- **Companies** - `company_add`, `company_list`, `company_update`
|
|
106
|
+
- **Projects** - `project_get`, `project_list`, `project_list_active`, `project_staff`, `project_tasks`, `projects_by_company`
|
|
107
|
+
- **Tasks** - `task_add`, `task_get`, `task_update`, `tasks_open`, `tasks_pending`, `tasks_by_company`, `tasks_by_milestone`
|
|
108
|
+
- **Task comments** - `task_comments`, `task_comment_get`, `task_comment_add`, `task_comment_update`, `task_comment_remove`, `task_mark_read`
|
|
109
|
+
- **Task assignment** - `task_assignee_add`, `task_assignee_remove`, `task_set_leader`, `task_set_attention`, `task_delete`, `task_merge`
|
|
110
|
+
- **Milestones** - `milestone_list`, `milestones_active`, `milestones_by_company`, `milestones_by_user`, `milestone_get`, `milestone_add`, `milestone_update`, `milestone_clone`, `milestone_remove`
|
|
111
|
+
- **Project writes** - `project_add`, `project_update`, `project_set_status`, `project_user_add`, `project_user_remove`
|
|
112
|
+
- **Reference** - `enums` (the status, priority, and group-type values the platform accepts)
|
|
113
|
+
- **Time logs** - `timelog_add`, `timelog_update`
|
|
114
|
+
- **Notes** - `note_add`, `note_get`, `note_update`, `note_remove`, `notes_mine`, `notes_query`
|
|
115
|
+
- **Insights** - hours by person/service/project, AI summary, person stats, time totals
|
|
116
|
+
- **Account** - `account_info`, `account_services`, `account_users`
|
|
117
|
+
- **Auth** - `auth_login`, `auth_status`, `auth_logout`
|
|
118
|
+
|
|
119
|
+
### Status and priority values
|
|
120
|
+
|
|
121
|
+
Use the `enums` tool rather than guessing. The platform's vocabularies are:
|
|
122
|
+
|
|
123
|
+
| field | values |
|
|
124
|
+
|---|---|
|
|
125
|
+
| task status | backlog, new, research, inprogress, pendingreview, inreview, testing, waitingcustomer, waitingteammember, onhold, cancelled, completed |
|
|
126
|
+
| priority | low, moderate, important, urgent, critical |
|
|
127
|
+
| project status | new, inprogress, complete |
|
|
128
|
+
| prospect status | new, firstcontact, negotiation, pending, won, lost, spam |
|
|
129
|
+
|
|
130
|
+
Two traps: tasks complete as `completed` while projects complete as
|
|
131
|
+
`complete`, and priority is *not* low/medium/high. Task and project tools
|
|
132
|
+
validate these with `z.enum`, so an invalid value is rejected before it reaches
|
|
133
|
+
the API.
|
|
134
|
+
|
|
135
|
+
### Paging and field selection
|
|
136
|
+
|
|
137
|
+
List tools (`tasks_open`, `project_list_active`, `project_tasks`, and the rest)
|
|
138
|
+
return a page of trimmed rows rather than every column of every row. A task row
|
|
139
|
+
carries ~94 columns and a project row 87, most of them irrelevant to project
|
|
140
|
+
work, so each list tool accepts:
|
|
141
|
+
|
|
142
|
+
- `fields` - columns to return; omit for a curated default, or pass `["all"]`
|
|
143
|
+
- `limit` / `offset` - page window, default 50 rows
|
|
144
|
+
|
|
145
|
+
Responses are wrapped as `{ total, returned, offset, next_offset, fields, items }`
|
|
146
|
+
so a caller can tell when more rows exist. Task lists also accept
|
|
147
|
+
`company_id`, `project_id`, `assignee_id`, `status`, `priority`,
|
|
148
|
+
`due_before`, `due_after`, and `search`; `tasks_open` pushes `company_id`
|
|
149
|
+
and `only_mine` to the server and filters the rest in-process.
|
|
150
|
+
|
|
151
|
+
In practice this took `tasks_open` from 97 KB to 6.4 KB and
|
|
152
|
+
`project_list_active` from 375 KB to 19 KB.
|
|
113
153
|
|
|
114
154
|
## Development
|
|
115
155
|
|
package/dist/api-client.d.ts
CHANGED
|
@@ -52,15 +52,108 @@ export declare class ViviScapeClient {
|
|
|
52
52
|
getProjectStaff(projectId: number): Promise<unknown>;
|
|
53
53
|
getProjectsByCompany(userId: number, companyId: number): Promise<unknown>;
|
|
54
54
|
getActiveProjectsByUser(userId: number): Promise<unknown>;
|
|
55
|
+
/**
|
|
56
|
+
* Create a project (group). GEN_Group has ~40 fields; live rows for this
|
|
57
|
+
* tenant show account_id 0, platform_account_id = pid, and group type 5
|
|
58
|
+
* (PROJECT), so those are the defaults here.
|
|
59
|
+
*
|
|
60
|
+
* There is no delete route for projects -- a created project can only be
|
|
61
|
+
* archived or renamed afterward.
|
|
62
|
+
*/
|
|
63
|
+
addProject(data: {
|
|
64
|
+
company_id: number;
|
|
65
|
+
name: string;
|
|
66
|
+
short_description?: string;
|
|
67
|
+
description?: string;
|
|
68
|
+
status?: string;
|
|
69
|
+
is_public?: boolean;
|
|
70
|
+
staff?: number[];
|
|
71
|
+
}): Promise<unknown>;
|
|
72
|
+
/** Update a project by merging changes onto the row the API already has. */
|
|
73
|
+
updateProject(projectId: number, changes: Record<string, unknown>): Promise<unknown>;
|
|
74
|
+
/** Status-only update; leaves budget and approved time untouched. */
|
|
75
|
+
setProjectStatus(projectId: number, status: string, billable?: boolean): Promise<unknown>;
|
|
76
|
+
addProjectUser(projectId: number, userId: number, opts?: {
|
|
77
|
+
is_contact?: boolean;
|
|
78
|
+
is_lead?: boolean;
|
|
79
|
+
}): Promise<unknown>;
|
|
80
|
+
removeProjectUser(projectId: number, userId: number): Promise<unknown>;
|
|
55
81
|
addTask(data: Record<string, unknown>): Promise<unknown>;
|
|
56
82
|
updateTask(data: Record<string, unknown>): Promise<unknown>;
|
|
57
83
|
getProjectTasks(projectId: number): Promise<unknown>;
|
|
58
|
-
|
|
84
|
+
/**
|
|
85
|
+
* The account/tasks/open route filters server-side on company, ownership, and
|
|
86
|
+
* a date window; pass them through instead of hardcoding "everything".
|
|
87
|
+
*/
|
|
88
|
+
getOpenTasks(opts?: {
|
|
89
|
+
company_id?: number;
|
|
90
|
+
only_mine?: boolean;
|
|
91
|
+
team?: string;
|
|
92
|
+
start?: string;
|
|
93
|
+
end?: string;
|
|
94
|
+
}): Promise<unknown>;
|
|
59
95
|
getTask(taskId: number): Promise<unknown>;
|
|
60
96
|
getPendingTasks(userId: number): Promise<unknown>;
|
|
61
97
|
getTasksByMilestone(milestoneId: number): Promise<unknown>;
|
|
62
98
|
getTasksByGroupAndUser(groupId: number, userId: number): Promise<unknown>;
|
|
63
99
|
getCompanyTasks(companyId: number): Promise<unknown>;
|
|
100
|
+
getTaskComments(taskId: number): Promise<unknown>;
|
|
101
|
+
getTaskComment(commentId: string): Promise<unknown>;
|
|
102
|
+
/**
|
|
103
|
+
* NOTE: on platform account 1, the backend fires a client-notification webhook
|
|
104
|
+
* for comments on tasks whose client_id > 0. Adding a comment can therefore
|
|
105
|
+
* reach the customer -- the tool description says so too.
|
|
106
|
+
*/
|
|
107
|
+
addTaskComment(data: {
|
|
108
|
+
task_id: number;
|
|
109
|
+
comment: string;
|
|
110
|
+
client_id?: number;
|
|
111
|
+
source?: string;
|
|
112
|
+
}): Promise<unknown>;
|
|
113
|
+
updateTaskComment(data: {
|
|
114
|
+
comment_id: string;
|
|
115
|
+
task_id: number;
|
|
116
|
+
comment: string;
|
|
117
|
+
}): Promise<unknown>;
|
|
118
|
+
/**
|
|
119
|
+
* The backend's remove proxy answers 500 even on success: it returns 400 when
|
|
120
|
+
* the upstream delete fails, so a 500 means it crashed deserializing an empty
|
|
121
|
+
* success body (res.data.ToString() on null). Verify by re-reading the thread
|
|
122
|
+
* rather than reporting a failure that did not happen.
|
|
123
|
+
*/
|
|
124
|
+
removeTaskComment(commentId: string, taskId?: number): Promise<unknown>;
|
|
125
|
+
markTaskRead(taskId: number): Promise<unknown>;
|
|
126
|
+
addTaskAssignee(data: {
|
|
127
|
+
task_id: number;
|
|
128
|
+
user_id: number;
|
|
129
|
+
leader?: boolean;
|
|
130
|
+
attention?: boolean;
|
|
131
|
+
notify?: boolean;
|
|
132
|
+
}): Promise<unknown>;
|
|
133
|
+
/** Takes the assignee_id from the task's staff_assignees, not a user_id. */
|
|
134
|
+
removeTaskAssignee(assigneeId: number): Promise<unknown>;
|
|
135
|
+
setTaskLeader(taskId: number, userId: number): Promise<unknown>;
|
|
136
|
+
setTaskAttention(taskId: number, userId: number, flag: boolean): Promise<unknown>;
|
|
137
|
+
deleteTask(taskId: number): Promise<unknown>;
|
|
138
|
+
/** Fold one task into another; the source task is consumed. */
|
|
139
|
+
mergeTasks(fromTaskId: number, toTaskId: number): Promise<unknown>;
|
|
140
|
+
getMilestones(projectId: number): Promise<unknown>;
|
|
141
|
+
getActiveMilestones(): Promise<unknown>;
|
|
142
|
+
getMilestonesByCompany(companyId: number): Promise<unknown>;
|
|
143
|
+
getMilestonesByUser(userId: number): Promise<unknown>;
|
|
144
|
+
getMilestone(milestoneId: number): Promise<unknown>;
|
|
145
|
+
addMilestone(data: Record<string, unknown>): Promise<unknown>;
|
|
146
|
+
updateMilestone(data: Record<string, unknown>): Promise<unknown>;
|
|
147
|
+
/** Copy a milestone (and its task template) into a project. */
|
|
148
|
+
cloneMilestone(milestoneId: number, projectId: number, title?: string): Promise<unknown>;
|
|
149
|
+
/**
|
|
150
|
+
* Known broken upstream: milestone/remove answers 500 and the milestone
|
|
151
|
+
* survives. The proxy returns 400 with detail when the upstream delete fails,
|
|
152
|
+
* so the 500 is a crash in its own success branch (Convert.ToBoolean on a
|
|
153
|
+
* non-boolean data payload) -- but unlike the comment-remove case the record
|
|
154
|
+
* is genuinely still there. Verify and say so plainly instead of pretending.
|
|
155
|
+
*/
|
|
156
|
+
removeMilestone(milestoneId: number): Promise<unknown>;
|
|
64
157
|
addTimeLog(data: Record<string, unknown>): Promise<unknown>;
|
|
65
158
|
updateTimeLog(data: Record<string, unknown>): Promise<unknown>;
|
|
66
159
|
getMyNotes(): Promise<unknown>;
|