@vantageos/vantage-crm-mcp 0.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.
Files changed (48) hide show
  1. package/README.md +260 -0
  2. package/dist/convex/crm/_helpers.js +24 -0
  3. package/dist/convex/crm/activities.js +220 -0
  4. package/dist/convex/crm/briefing.js +198 -0
  5. package/dist/convex/crm/calendarCron.js +92 -0
  6. package/dist/convex/crm/calendarCronDispatch.js +83 -0
  7. package/dist/convex/crm/calendarSync.js +294 -0
  8. package/dist/convex/crm/companies.js +323 -0
  9. package/dist/convex/crm/contacts.js +346 -0
  10. package/dist/convex/crm/deals.js +481 -0
  11. package/dist/convex/crm/emailActions.js +158 -0
  12. package/dist/convex/crm/emailCron.js +210 -0
  13. package/dist/convex/crm/emailCronDispatch.js +76 -0
  14. package/dist/convex/crm/emailSync.js +260 -0
  15. package/dist/convex/crm/onboarding.js +185 -0
  16. package/dist/convex/crm/stats.js +75 -0
  17. package/dist/convex/crm/tasks.js +109 -0
  18. package/dist/convex/crons.js +25 -0
  19. package/dist/convex/integrations.js +183 -0
  20. package/dist/convex/lib/auditLog.js +109 -0
  21. package/dist/convex/lib/auth.js +372 -0
  22. package/dist/convex/lib/rbac.js +123 -0
  23. package/dist/convex/lib/workspace.js +171 -0
  24. package/dist/convex/organizations.js +192 -0
  25. package/dist/convex/schema.js +690 -0
  26. package/dist/convex/users.js +217 -0
  27. package/dist/convex/workspaces.js +603 -0
  28. package/dist/mcp-server/lib/convexClient.js +50 -0
  29. package/dist/mcp-server/lib/scopeEnforcement.js +76 -0
  30. package/dist/mcp-server/registry.js +116 -0
  31. package/dist/mcp-server/server.js +97 -0
  32. package/dist/mcp-server/tests/registry.test.js +163 -0
  33. package/dist/mcp-server/tests/scopeEnforcement.test.js +137 -0
  34. package/dist/mcp-server/tests/security.test.js +257 -0
  35. package/dist/mcp-server/tests/tools.test.js +272 -0
  36. package/dist/mcp-server/tools/activities.js +207 -0
  37. package/dist/mcp-server/tools/admin.js +190 -0
  38. package/dist/mcp-server/tools/companies.js +233 -0
  39. package/dist/mcp-server/tools/contacts.js +306 -0
  40. package/dist/mcp-server/tools/customFields.js +222 -0
  41. package/dist/mcp-server/tools/customObjects.js +235 -0
  42. package/dist/mcp-server/tools/deals.js +297 -0
  43. package/dist/mcp-server/tools/rbac.js +177 -0
  44. package/dist/mcp-server/tools/search.js +155 -0
  45. package/dist/mcp-server/tools/workflows.js +234 -0
  46. package/dist/mcp-server/transport/http.js +257 -0
  47. package/dist/mcp-server/transport/stdio.js +90 -0
  48. package/package.json +45 -0
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ /**
3
+ * User management functions
4
+ *
5
+ * Handles syncing Clerk users to Convex users table
6
+ * Enhanced with organization/workspace context support
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getByClerkId = exports.updateSettings = exports.getCurrentUser = exports.syncUser = void 0;
10
+ const values_1 = require("convex/values");
11
+ const server_1 = require("./_generated/server");
12
+ const auth_1 = require("./lib/auth");
13
+ /**
14
+ * Sync current Clerk user to Convex users table
15
+ *
16
+ * This should be called from the frontend after Clerk authentication
17
+ * to ensure the user exists in the Convex users table.
18
+ * Also syncs organization if user is in one, and ensures default workspace exists.
19
+ */
20
+ exports.syncUser = (0, server_1.mutation)({
21
+ args: {},
22
+ handler: async (ctx) => {
23
+ const identity = await ctx.auth.getUserIdentity();
24
+ if (!identity) {
25
+ throw new Error('Not authenticated');
26
+ }
27
+ // Sync user
28
+ const user = await (0, auth_1.getOrCreateUser)(ctx, identity.subject, // clerkId
29
+ identity.tokenIdentifier, identity.email || '', identity.name || undefined, identity.pictureUrl || undefined);
30
+ if (!user) {
31
+ throw new Error('Failed to sync user');
32
+ }
33
+ // Ensure user has a default personal workspace
34
+ const defaultWorkspace = await ctx.db
35
+ .query('workspaces')
36
+ .withIndex('by_owner', (q) => q.eq('ownerId', user._id))
37
+ .filter((q) => q.and(q.eq(q.field('ownerType'), 'user'), q.eq(q.field('isDefault'), true)))
38
+ .first();
39
+ if (!defaultWorkspace) {
40
+ // Create default personal workspace
41
+ const workspaceId = await ctx.db.insert('workspaces', {
42
+ name: 'Personal',
43
+ ownerType: 'user',
44
+ ownerId: user._id,
45
+ isDefault: true,
46
+ createdAt: Date.now(),
47
+ updatedAt: Date.now(),
48
+ });
49
+ // Set as active workspace
50
+ await ctx.db.patch(user._id, { activeWorkspaceId: workspaceId });
51
+ }
52
+ // If in organization context, sync org and membership
53
+ const orgId = identity.org_id;
54
+ const orgRole = identity.org_role;
55
+ const orgSlug = identity.org_slug;
56
+ if (orgId) {
57
+ // Get org name from claims (Clerk includes it)
58
+ const orgName = identity.org_name ||
59
+ orgSlug ||
60
+ 'Organization';
61
+ // Sync organization
62
+ const org = await (0, auth_1.getOrCreateOrganization)(ctx, orgId, orgName, orgSlug, identity.org_image_url);
63
+ // Sync membership
64
+ if (org) {
65
+ await (0, auth_1.getOrCreateMembership)(ctx, user._id, org._id, identity.subject, orgId, orgRole || 'org:member');
66
+ }
67
+ }
68
+ return {
69
+ success: true,
70
+ userId: user._id,
71
+ role: user.role,
72
+ };
73
+ },
74
+ });
75
+ /**
76
+ * Get current user info with workspace context
77
+ * Returns user data + workspace info if authenticated, null otherwise
78
+ */
79
+ exports.getCurrentUser = (0, server_1.query)({
80
+ args: {},
81
+ handler: async (ctx) => {
82
+ const identity = await ctx.auth.getUserIdentity();
83
+ if (!identity) {
84
+ return null;
85
+ }
86
+ // Try to find user by Clerk ID first
87
+ let user = await ctx.db
88
+ .query('users')
89
+ .withIndex('by_clerk_id', (q) => q.eq('clerkId', identity.subject))
90
+ .first();
91
+ // Fallback to token identifier
92
+ if (!user) {
93
+ user = await ctx.db
94
+ .query('users')
95
+ .withIndex('by_token', (q) => q.eq('tokenIdentifier', identity.tokenIdentifier))
96
+ .first();
97
+ }
98
+ if (!user) {
99
+ // User not synced yet
100
+ return null;
101
+ }
102
+ // Get Clerk workspace context (org from token)
103
+ const clerkWorkspace = await (0, auth_1.getWorkspaceContext)(ctx);
104
+ // Get active Vantage workspace
105
+ let activeWorkspace = null;
106
+ if (user.activeWorkspaceId) {
107
+ activeWorkspace = await ctx.db.get(user.activeWorkspaceId);
108
+ }
109
+ // If no active workspace, get the default one
110
+ if (!activeWorkspace) {
111
+ activeWorkspace = await ctx.db
112
+ .query('workspaces')
113
+ .withIndex('by_owner', (q) => q.eq('ownerId', user._id))
114
+ .filter((q) => q.and(q.eq(q.field('ownerType'), 'user'), q.eq(q.field('isDefault'), true)))
115
+ .first();
116
+ }
117
+ // If in org context (from Clerk token), get org details
118
+ let organization = null;
119
+ const currentOrgId = clerkWorkspace.orgId;
120
+ if (currentOrgId) {
121
+ organization = await ctx.db
122
+ .query('organizations')
123
+ .withIndex('by_clerk_id', (q) => q.eq('clerkId', currentOrgId))
124
+ .first();
125
+ }
126
+ return {
127
+ _id: user._id,
128
+ clerkId: user.clerkId,
129
+ email: user.email,
130
+ name: user.name,
131
+ firstName: user.firstName,
132
+ lastName: user.lastName,
133
+ role: user.role,
134
+ avatarUrl: user.avatarUrl,
135
+ settings: user.settings,
136
+ activeWorkspaceId: user.activeWorkspaceId,
137
+ createdAt: user.createdAt,
138
+ updatedAt: user.updatedAt,
139
+ // Active Vantage workspace
140
+ activeWorkspace: activeWorkspace
141
+ ? {
142
+ _id: activeWorkspace._id,
143
+ name: activeWorkspace.name,
144
+ slug: activeWorkspace.slug,
145
+ icon: activeWorkspace.icon,
146
+ color: activeWorkspace.color,
147
+ ownerType: activeWorkspace.ownerType,
148
+ isDefault: activeWorkspace.isDefault,
149
+ }
150
+ : null,
151
+ // Clerk context (which org user is viewing)
152
+ clerkContext: {
153
+ orgId: clerkWorkspace.orgId,
154
+ entityId: clerkWorkspace.entityId,
155
+ isPersonal: clerkWorkspace.isPersonal,
156
+ organization: organization
157
+ ? {
158
+ _id: organization._id,
159
+ name: organization.name,
160
+ slug: organization.slug,
161
+ imageUrl: organization.imageUrl,
162
+ plan: organization.plan,
163
+ }
164
+ : null,
165
+ },
166
+ };
167
+ },
168
+ });
169
+ /**
170
+ * Update user settings
171
+ */
172
+ exports.updateSettings = (0, server_1.mutation)({
173
+ args: {
174
+ theme: values_1.v.optional(values_1.v.string()),
175
+ defaultModel: values_1.v.optional(values_1.v.string()),
176
+ notifications: values_1.v.optional(values_1.v.boolean()),
177
+ },
178
+ handler: async (ctx, args) => {
179
+ const identity = await ctx.auth.getUserIdentity();
180
+ if (!identity)
181
+ throw new Error('Not authenticated');
182
+ const user = await ctx.db
183
+ .query('users')
184
+ .withIndex('by_clerk_id', (q) => q.eq('clerkId', identity.subject))
185
+ .first();
186
+ if (!user)
187
+ throw new Error('User not found');
188
+ const currentSettings = user.settings || {};
189
+ const newSettings = {
190
+ ...currentSettings,
191
+ ...(args.theme !== undefined && { theme: args.theme }),
192
+ ...(args.defaultModel !== undefined && {
193
+ defaultModel: args.defaultModel,
194
+ }),
195
+ ...(args.notifications !== undefined && {
196
+ notifications: args.notifications,
197
+ }),
198
+ };
199
+ await ctx.db.patch(user._id, {
200
+ settings: newSettings,
201
+ updatedAt: Date.now(),
202
+ });
203
+ return { success: true };
204
+ },
205
+ });
206
+ /**
207
+ * Get user by Clerk ID
208
+ */
209
+ exports.getByClerkId = (0, server_1.query)({
210
+ args: { clerkId: values_1.v.string() },
211
+ handler: async (ctx, { clerkId }) => {
212
+ return await ctx.db
213
+ .query('users')
214
+ .withIndex('by_clerk_id', (q) => q.eq('clerkId', clerkId))
215
+ .first();
216
+ },
217
+ });