@utaba/ucm-mcp-server 4.3.2 → 5.0.1

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.
@@ -114,6 +114,40 @@ export declare class UcmLocalApiClient {
114
114
  limit?: number;
115
115
  offset?: number;
116
116
  }): Promise<string>;
117
+ /**
118
+ * Access a specific external connection
119
+ * Returns connection details, auth status, and available tool schemas
120
+ * Connection type is auto-detected by the server from the connectionId
121
+ */
122
+ accessConnection(connectionId: string): Promise<{
123
+ success: boolean;
124
+ connectionId: string;
125
+ connectionType: 'sharepoint' | 'remote-mcp';
126
+ connectionName: string;
127
+ isAuthenticated: boolean;
128
+ authRequired?: boolean;
129
+ loginUrl?: string;
130
+ tools?: any[];
131
+ markdown: string;
132
+ }>;
133
+ /**
134
+ * Call a tool on an external connection
135
+ * Executes a tool on a SharePoint connection or Remote MCP Server
136
+ * Connection type is auto-detected by the server from the connectionId
137
+ */
138
+ callRemoteTool(connectionId: string, toolName: string, args?: Record<string, any>): Promise<{
139
+ success: boolean;
140
+ connectionId: string;
141
+ connectionType: 'sharepoint' | 'remote-mcp';
142
+ toolName: string;
143
+ result?: any;
144
+ error?: string;
145
+ errorCode?: string;
146
+ authRequired?: boolean;
147
+ loginUrl?: string;
148
+ loginMessage?: string;
149
+ markdown: string;
150
+ }>;
117
151
  /**
118
152
  * Search SharePoint documents using Microsoft Search API
119
153
  * Uses V1 API - organizationId is auto-detected from auth token
@@ -159,7 +193,20 @@ export declare class UcmLocalApiClient {
159
193
  * Revoke SharePoint OnBehalfOf authorization for a connection
160
194
  * Deletes user's access tokens from Key Vault and database
161
195
  * Uses V1 API - organizationId is auto-detected from auth token
196
+ * @deprecated Use signOut with connectionType='sharepoint' instead
162
197
  */
163
198
  sharePointSignOut(connectionId: string): Promise<any>;
199
+ /**
200
+ * Unified OAuth sign out for any connection type
201
+ * Revokes user's OAuth authorization by deleting tokens from Key Vault and database
202
+ * @param connectionType - Type of connection: 'sharepoint' | 'remote-mcp'
203
+ * @param connectionId - Connection ID to revoke authorization for
204
+ */
205
+ signOut(connectionType: string, connectionId: string): Promise<any>;
206
+ /**
207
+ * List all user's active OAuth authorizations
208
+ * @param connectionType - Optional filter: 'sharepoint' | 'remote-mcp' | 'all' (default: 'all')
209
+ */
210
+ listAuthorizations(connectionType?: string): Promise<any>;
164
211
  }
165
212
  //# sourceMappingURL=UcmLocalApiClient.d.ts.map
@@ -373,6 +373,30 @@ export class UcmLocalApiClient {
373
373
  const response = await client.get(url, { params });
374
374
  return response.data;
375
375
  }
376
+ /**
377
+ * Access a specific external connection
378
+ * Returns connection details, auth status, and available tool schemas
379
+ * Connection type is auto-detected by the server from the connectionId
380
+ */
381
+ async accessConnection(connectionId) {
382
+ const client = this.ensureClient();
383
+ const response = await client.get(`/api/v1/connections/${connectionId}`);
384
+ return response.data;
385
+ }
386
+ /**
387
+ * Call a tool on an external connection
388
+ * Executes a tool on a SharePoint connection or Remote MCP Server
389
+ * Connection type is auto-detected by the server from the connectionId
390
+ */
391
+ async callRemoteTool(connectionId, toolName, args) {
392
+ const client = this.ensureClient();
393
+ const body = {
394
+ toolName,
395
+ args: args || {}
396
+ };
397
+ const response = await client.post(`/api/v1/connections/${connectionId}/tools`, body);
398
+ return response.data;
399
+ }
376
400
  /**
377
401
  * Search SharePoint documents using Microsoft Search API
378
402
  * Uses V1 API - organizationId is auto-detected from auth token
@@ -450,11 +474,42 @@ export class UcmLocalApiClient {
450
474
  * Revoke SharePoint OnBehalfOf authorization for a connection
451
475
  * Deletes user's access tokens from Key Vault and database
452
476
  * Uses V1 API - organizationId is auto-detected from auth token
477
+ * @deprecated Use signOut with connectionType='sharepoint' instead
453
478
  */
454
479
  async sharePointSignOut(connectionId) {
455
480
  const client = this.ensureClient();
456
481
  const response = await client.post(`/api/v1/connections/sharepoint/signout`, { connectionId });
457
482
  return response.data;
458
483
  }
484
+ /**
485
+ * Unified OAuth sign out for any connection type
486
+ * Revokes user's OAuth authorization by deleting tokens from Key Vault and database
487
+ * @param connectionType - Type of connection: 'sharepoint' | 'remote-mcp'
488
+ * @param connectionId - Connection ID to revoke authorization for
489
+ */
490
+ async signOut(connectionType, connectionId) {
491
+ const client = this.ensureClient();
492
+ // Route to appropriate endpoint based on connection type
493
+ if (connectionType === 'sharepoint') {
494
+ const response = await client.post(`/api/v1/connections/sharepoint/signout`, { connectionId });
495
+ return response.data;
496
+ }
497
+ else if (connectionType === 'remote-mcp') {
498
+ // Remote MCP uses the oauth signout endpoint
499
+ const response = await client.post(`/api/v1/oauth/signout`, { connectionType, connectionId });
500
+ return response.data;
501
+ }
502
+ throw new Error(`Unsupported connection type: ${connectionType}`);
503
+ }
504
+ /**
505
+ * List all user's active OAuth authorizations
506
+ * @param connectionType - Optional filter: 'sharepoint' | 'remote-mcp' | 'all' (default: 'all')
507
+ */
508
+ async listAuthorizations(connectionType) {
509
+ const client = this.ensureClient();
510
+ const params = connectionType && connectionType !== 'all' ? { connectionType } : {};
511
+ const response = await client.get('/api/v1/oauth/authorizations', { params });
512
+ return response.data;
513
+ }
459
514
  }
460
515
  //# sourceMappingURL=UcmLocalApiClient.js.map
@@ -16,18 +16,26 @@ import { DeleteArtifactTool } from '../tools/core/DeleteArtifactTool.js';
16
16
  import { GetArtifactVersionsTool } from '../tools/core/GetArtifactVersionsTool.js';
17
17
  import { SearchArtifactsTool } from '../tools/core/SearchArtifactsTool.js';
18
18
  import { EditArtifactMetadataTool } from '../tools/core/EditArtifactMetadataTool.js';
19
+ import { MoveArtifactTool } from '../tools/core/MoveArtifactTool.js';
19
20
  // Import repository tools
20
21
  import { CreateRepositoryTool } from '../tools/repository/CreateRepositoryTool.js';
21
22
  import { GetRepositoryTool } from '../tools/repository/GetRepositoryTool.js';
22
23
  import { UpdateRepositoryTool } from '../tools/repository/UpdateRepositoryTool.js';
23
24
  import { DeleteRepositoryGuidanceTool } from '../tools/repository/DeleteRepositoryGuidanceTool.js';
24
- // Import SharePoint tools
25
- import { SharePointListConnectionsTool as ListConnectionsTool } from '../tools/sharepoint/SharePointListConnectionsTool.js';
26
- import { SharePointSearchTool } from '../tools/sharepoint/SharePointSearchTool.js';
27
- import { SharePointListFoldersTool } from '../tools/sharepoint/SharePointListFoldersTool.js';
28
- import { SharePointReadFileTool } from '../tools/sharepoint/SharePointReadFileTool.js';
29
- import { SharePointReadRelatedFileTool } from '../tools/sharepoint/SharePointReadRelatedFileTool.js';
30
- import { SharePointSignOutTool } from '../tools/sharepoint/SharePointSignOutTool.js';
25
+ // Import connection/SharePoint tools
26
+ import { ListConnectionsTool } from '../tools/sharepoint/SharePointListConnectionsTool.js';
27
+ // DEPRECATED: Legacy SharePoint tools - use ucm_call_remote_tool via unified gateway instead
28
+ // Commented out 2026-02-13 - pending confirmation that unified gateway works correctly
29
+ // import { SharePointSearchTool } from '../tools/sharepoint/SharePointSearchTool.js';
30
+ // import { SharePointListFoldersTool } from '../tools/sharepoint/SharePointListFoldersTool.js';
31
+ // import { SharePointReadFileTool } from '../tools/sharepoint/SharePointReadFileTool.js';
32
+ // import { SharePointReadRelatedFileTool } from '../tools/sharepoint/SharePointReadRelatedFileTool.js';
33
+ // Import unified authorization tools
34
+ import { SignOutTool } from '../tools/authorization/SignOutTool.js';
35
+ import { ListAuthorizationsTool } from '../tools/authorization/ListAuthorizationsTool.js';
36
+ // Import connection tools
37
+ import { AccessConnectionTool } from '../tools/connections/AccessConnectionTool.js';
38
+ import { CallRemoteToolTool } from '../tools/connections/CallRemoteToolTool.js';
31
39
  export class ToolRegistry {
32
40
  ucmClient;
33
41
  logger;
@@ -61,18 +69,26 @@ export class ToolRegistry {
61
69
  this.registerTool(new GetArtifactVersionsTool(this.ucmClient, this.logger, this.authorId));
62
70
  this.registerTool(new SearchArtifactsTool(this.ucmClient, this.logger, this.authorId));
63
71
  this.registerTool(new EditArtifactMetadataTool(this.ucmClient, this.logger, this.authorId));
72
+ this.registerTool(new MoveArtifactTool(this.ucmClient, this.logger, this.authorId));
64
73
  // Register repository tools
65
74
  this.registerTool(new CreateRepositoryTool(this.ucmClient, this.logger, this.authorId));
66
75
  this.registerTool(new GetRepositoryTool(this.ucmClient, this.logger, this.authorId));
67
76
  this.registerTool(new UpdateRepositoryTool(this.ucmClient, this.logger, this.authorId));
68
77
  this.registerTool(new DeleteRepositoryGuidanceTool(this.ucmClient, this.logger, this.authorId, this.baseUrl));
69
- // Register SharePoint tools
78
+ // Register connection tools (ListConnectionsTool is unified - lists all connection types)
70
79
  this.registerTool(new ListConnectionsTool(this.ucmClient, this.logger, this.authorId));
71
- this.registerTool(new SharePointSearchTool(this.ucmClient, this.logger, this.authorId));
72
- this.registerTool(new SharePointListFoldersTool(this.ucmClient, this.logger, this.authorId));
73
- this.registerTool(new SharePointReadFileTool(this.ucmClient, this.logger, this.authorId));
74
- this.registerTool(new SharePointReadRelatedFileTool(this.ucmClient, this.logger, this.authorId));
75
- this.registerTool(new SharePointSignOutTool(this.ucmClient, this.logger, this.authorId));
80
+ // DEPRECATED: Legacy SharePoint tools - use ucm_call_remote_tool via unified gateway instead
81
+ // Commented out 2026-02-13 - pending confirmation that unified gateway works correctly
82
+ // this.registerTool(new SharePointSearchTool(this.ucmClient, this.logger, this.authorId));
83
+ // this.registerTool(new SharePointListFoldersTool(this.ucmClient, this.logger, this.authorId));
84
+ // this.registerTool(new SharePointReadFileTool(this.ucmClient, this.logger, this.authorId));
85
+ // this.registerTool(new SharePointReadRelatedFileTool(this.ucmClient, this.logger, this.authorId));
86
+ // Register connection tools (unified gateway)
87
+ this.registerTool(new AccessConnectionTool(this.ucmClient, this.logger, this.authorId));
88
+ this.registerTool(new CallRemoteToolTool(this.ucmClient, this.logger, this.authorId));
89
+ // Register unified authorization tools
90
+ this.registerTool(new SignOutTool(this.ucmClient, this.logger, this.authorId));
91
+ this.registerTool(new ListAuthorizationsTool(this.ucmClient, this.logger, this.authorId));
76
92
  this.logger.info('ToolRegistry', `Registered ${this.tools.size} MCP tools`);
77
93
  }
78
94
  registerTool(tool) {
@@ -0,0 +1,21 @@
1
+ /**
2
+ * ListAuthorizationsTool - List all user's active OAuth authorizations
3
+ */
4
+ import { BaseToolController } from '../base/BaseToolController.js';
5
+ import { UcmLocalApiClient } from '../../clients/UcmLocalApiClient.js';
6
+ import { ILogger } from '../../interfaces/ILogger.js';
7
+ export declare class ListAuthorizationsTool extends BaseToolController {
8
+ constructor(ucmClient: UcmLocalApiClient, logger: ILogger, publishingAuthorId?: string);
9
+ get name(): string;
10
+ get description(): string;
11
+ get inputSchema(): any;
12
+ protected validateParams(params: any): void;
13
+ protected handleExecute(params: {
14
+ connectionType?: string;
15
+ }): Promise<any>;
16
+ /**
17
+ * Format authorizations response as markdown
18
+ */
19
+ private formatAuthorizationsResponse;
20
+ }
21
+ //# sourceMappingURL=ListAuthorizationsTool.d.ts.map
@@ -0,0 +1,146 @@
1
+ /**
2
+ * ListAuthorizationsTool - List all user's active OAuth authorizations
3
+ */
4
+ import { BaseToolController } from '../base/BaseToolController.js';
5
+ import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
6
+ export class ListAuthorizationsTool extends BaseToolController {
7
+ constructor(ucmClient, logger, publishingAuthorId) {
8
+ super(ucmClient, logger, publishingAuthorId);
9
+ }
10
+ get name() {
11
+ return 'ucm_list_authorizations';
12
+ }
13
+ get description() {
14
+ return 'List all your active OAuth authorizations across connection types. Shows which services you are currently authorized to access, with expiry info and connection details.';
15
+ }
16
+ get inputSchema() {
17
+ return {
18
+ type: 'object',
19
+ properties: {
20
+ connectionType: {
21
+ type: 'string',
22
+ enum: ['sharepoint', 'remote-mcp', 'all'],
23
+ description: 'Filter by connection type. Default: "all"'
24
+ }
25
+ },
26
+ required: []
27
+ };
28
+ }
29
+ validateParams(params) {
30
+ super.validateParams(params);
31
+ // Validate connectionType if provided
32
+ if (params.connectionType) {
33
+ const validTypes = ['sharepoint', 'remote-mcp', 'all'];
34
+ if (!validTypes.includes(params.connectionType)) {
35
+ throw new McpError(McpErrorCode.InvalidParams, `connectionType must be one of: ${validTypes.join(', ')}`);
36
+ }
37
+ }
38
+ }
39
+ async handleExecute(params) {
40
+ const filter = params.connectionType || 'all';
41
+ this.logger.info('ListAuthorizationsTool', `Listing authorizations (filter: ${filter})`);
42
+ try {
43
+ // Call API to list authorizations
44
+ const result = await this.ucmClient.listAuthorizations(filter);
45
+ this.logger.info('ListAuthorizationsTool', 'Authorizations retrieved successfully', '', {
46
+ filter,
47
+ count: result.authorizations?.length || 0
48
+ });
49
+ // Return structured markdown response
50
+ const markdown = this.formatAuthorizationsResponse(result, filter);
51
+ return markdown;
52
+ }
53
+ catch (error) {
54
+ const sanitizedError = {
55
+ message: error?.message,
56
+ status: error?.response?.status,
57
+ data: error?.response?.data
58
+ };
59
+ this.logger.error('ListAuthorizationsTool', 'Failed to list authorizations', '', sanitizedError);
60
+ if (error instanceof McpError) {
61
+ throw error;
62
+ }
63
+ if (error.response) {
64
+ const status = error.response.status;
65
+ const errorData = error.response.data;
66
+ if (status === 401 || status === 403) {
67
+ throw new McpError(McpErrorCode.InvalidRequest, `Access denied: ${errorData?.message || 'Not authorized'}`);
68
+ }
69
+ throw new McpError(McpErrorCode.InternalError, `API error: ${errorData?.message || error.message}`);
70
+ }
71
+ throw new McpError(McpErrorCode.InternalError, `Failed to list authorizations: ${error.message}`);
72
+ }
73
+ }
74
+ /**
75
+ * Format authorizations response as markdown
76
+ */
77
+ formatAuthorizationsResponse(result, filter) {
78
+ const lines = [];
79
+ const authorizations = result.authorizations || [];
80
+ lines.push('# Your OAuth Authorizations\n');
81
+ if (authorizations.length === 0) {
82
+ if (filter === 'all') {
83
+ lines.push('You have no active OAuth authorizations.\n');
84
+ lines.push('When you access services that require OAuth (like SharePoint or Remote MCP Servers), you will be prompted to authorize.');
85
+ }
86
+ else {
87
+ lines.push(`You have no active ${filter} authorizations.\n`);
88
+ }
89
+ }
90
+ else {
91
+ lines.push(`Found ${authorizations.length} active authorization(s)${filter !== 'all' ? ` for ${filter}` : ''}.\n`);
92
+ // Group by connection type
93
+ const grouped = new Map();
94
+ for (const auth of authorizations) {
95
+ const type = auth.connectionType || 'other';
96
+ if (!grouped.has(type)) {
97
+ grouped.set(type, []);
98
+ }
99
+ grouped.get(type).push(auth);
100
+ }
101
+ const typeNames = {
102
+ 'sharepoint': 'SharePoint Connections',
103
+ 'remote-mcp': 'Remote MCP Servers'
104
+ };
105
+ for (const [type, auths] of grouped) {
106
+ const typeName = typeNames[type] || type;
107
+ lines.push(`## ${typeName}\n`);
108
+ for (const auth of auths) {
109
+ const now = new Date();
110
+ const refreshTokenExpiresAt = auth.refreshTokenExpiresAt ? new Date(auth.refreshTokenExpiresAt) : null;
111
+ const accessTokenExpiresAt = auth.expiresAt ? new Date(auth.expiresAt) : null;
112
+ // Authorization is expired only if refresh token has expired
113
+ // Access token expiry doesn't matter - it will auto-refresh
114
+ const isExpired = refreshTokenExpiresAt ? refreshTokenExpiresAt < now : false;
115
+ const accessTokenExpired = accessTokenExpiresAt ? accessTokenExpiresAt < now : false;
116
+ let status;
117
+ if (isExpired) {
118
+ status = '**Expired** (re-authorization required)';
119
+ }
120
+ else if (refreshTokenExpiresAt) {
121
+ status = `Active (valid until ${refreshTokenExpiresAt.toISOString()} UTC)`;
122
+ }
123
+ else {
124
+ // No refresh token expiry means it doesn't expire (e.g., Google)
125
+ status = 'Active (does not expire)';
126
+ }
127
+ lines.push(`### ${auth.connectionName || auth.connectionId}`);
128
+ lines.push(`- **Connection ID**: ${auth.connectionId}`);
129
+ lines.push(`- **Provider**: ${auth.providerType || type}`);
130
+ lines.push(`- **Status**: ${status}`);
131
+ if (accessTokenExpired && !isExpired) {
132
+ lines.push(`- **Note**: Access token expired but will auto-refresh on next use`);
133
+ }
134
+ if (auth.scopes) {
135
+ lines.push(`- **Scopes**: ${auth.scopes}`);
136
+ }
137
+ lines.push('');
138
+ }
139
+ }
140
+ lines.push('\n## Actions\n');
141
+ lines.push('To revoke an authorization, use: `ucm_signout` with connectionType and connectionId');
142
+ }
143
+ return lines.join('\n');
144
+ }
145
+ }
146
+ //# sourceMappingURL=ListAuthorizationsTool.js.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * SignOutTool - Unified OAuth sign out for all connection types
3
+ * Replaces SharePointSignOutTool with a unified tool supporting multiple connection types
4
+ */
5
+ import { BaseToolController } from '../base/BaseToolController.js';
6
+ import { UcmLocalApiClient } from '../../clients/UcmLocalApiClient.js';
7
+ import { ILogger } from '../../interfaces/ILogger.js';
8
+ export declare class SignOutTool extends BaseToolController {
9
+ constructor(ucmClient: UcmLocalApiClient, logger: ILogger, publishingAuthorId?: string);
10
+ get name(): string;
11
+ get description(): string;
12
+ get inputSchema(): any;
13
+ protected validateParams(params: any): void;
14
+ protected handleExecute(params: {
15
+ connectionType: string;
16
+ connectionId: string;
17
+ }): Promise<any>;
18
+ /**
19
+ * Format sign out response as markdown
20
+ */
21
+ private formatSignOutResponse;
22
+ }
23
+ //# sourceMappingURL=SignOutTool.d.ts.map
@@ -0,0 +1,128 @@
1
+ /**
2
+ * SignOutTool - Unified OAuth sign out for all connection types
3
+ * Replaces SharePointSignOutTool with a unified tool supporting multiple connection types
4
+ */
5
+ import { BaseToolController } from '../base/BaseToolController.js';
6
+ import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
7
+ export class SignOutTool extends BaseToolController {
8
+ constructor(ucmClient, logger, publishingAuthorId) {
9
+ super(ucmClient, logger, publishingAuthorId);
10
+ }
11
+ get name() {
12
+ return 'ucm_signout';
13
+ }
14
+ get description() {
15
+ return 'Sign out from an OAuth-authorized connection. Revokes user-delegated permissions by deleting access tokens. Works for SharePoint OnBehalfOf connections and Remote MCP Servers with user-delegated OAuth.';
16
+ }
17
+ get inputSchema() {
18
+ return {
19
+ type: 'object',
20
+ properties: {
21
+ connectionType: {
22
+ type: 'string',
23
+ enum: ['sharepoint', 'remote-mcp'],
24
+ description: 'Type of connection: "sharepoint" or "remote-mcp"'
25
+ },
26
+ connectionId: {
27
+ type: 'string',
28
+ description: 'Connection ID to revoke authorization for (get from ucm_list_authorizations or ucm_list_connections)',
29
+ minLength: 36,
30
+ maxLength: 36
31
+ }
32
+ },
33
+ required: ['connectionType', 'connectionId']
34
+ };
35
+ }
36
+ validateParams(params) {
37
+ super.validateParams(params);
38
+ // Validate connectionType
39
+ if (!params.connectionType || typeof params.connectionType !== 'string') {
40
+ throw new McpError(McpErrorCode.InvalidParams, 'connectionType is required and must be a string');
41
+ }
42
+ const validTypes = ['sharepoint', 'remote-mcp'];
43
+ if (!validTypes.includes(params.connectionType)) {
44
+ throw new McpError(McpErrorCode.InvalidParams, `connectionType must be one of: ${validTypes.join(', ')}`);
45
+ }
46
+ // Validate connectionId
47
+ if (!params.connectionId || typeof params.connectionId !== 'string') {
48
+ throw new McpError(McpErrorCode.InvalidParams, 'connectionId is required and must be a string');
49
+ }
50
+ if (params.connectionId.length !== 36) {
51
+ throw new McpError(McpErrorCode.InvalidParams, 'connectionId must be a valid UUID (36 characters)');
52
+ }
53
+ }
54
+ async handleExecute(params) {
55
+ this.logger.info('SignOutTool', `Revoking ${params.connectionType} authorization for connection ${params.connectionId}`);
56
+ try {
57
+ // Call unified API method
58
+ const result = await this.ucmClient.signOut(params.connectionType, params.connectionId);
59
+ this.logger.info('SignOutTool', 'Authorization revoked successfully', '', {
60
+ connectionType: params.connectionType,
61
+ connectionId: params.connectionId,
62
+ success: result.success
63
+ });
64
+ // Return structured markdown response
65
+ const markdown = this.formatSignOutResponse(params.connectionType, params.connectionId, result);
66
+ return markdown;
67
+ }
68
+ catch (error) {
69
+ // Sanitize error for logging to avoid circular reference issues
70
+ const sanitizedError = {
71
+ message: error?.message,
72
+ status: error?.response?.status,
73
+ data: error?.response?.data
74
+ };
75
+ this.logger.error('SignOutTool', 'Failed to revoke authorization', '', sanitizedError);
76
+ if (error instanceof McpError) {
77
+ throw error;
78
+ }
79
+ // Handle API errors
80
+ if (error.response) {
81
+ const status = error.response.status;
82
+ const errorData = error.response.data;
83
+ if (status === 401 || status === 403) {
84
+ throw new McpError(McpErrorCode.InvalidRequest, `Access denied: ${errorData?.message || 'Not authorized to revoke authorization'}`);
85
+ }
86
+ if (status === 404) {
87
+ throw new McpError(McpErrorCode.InvalidRequest, `Connection not found: ${errorData?.message || 'Connection does not exist'}`);
88
+ }
89
+ if (status === 400 && errorData?.error === 'INVALID_AUTH_TYPE') {
90
+ throw new McpError(McpErrorCode.InvalidRequest, `Invalid operation: This connection does not use user-delegated authentication.`);
91
+ }
92
+ throw new McpError(McpErrorCode.InternalError, `API error: ${errorData?.message || error.message}`);
93
+ }
94
+ throw new McpError(McpErrorCode.InternalError, `Failed to revoke authorization: ${error.message}`);
95
+ }
96
+ }
97
+ /**
98
+ * Format sign out response as markdown
99
+ */
100
+ formatSignOutResponse(connectionType, connectionId, result) {
101
+ const lines = [];
102
+ const providerName = connectionType === 'sharepoint' ? 'SharePoint' : 'Remote MCP Server';
103
+ lines.push(`# ${providerName} Authorization Revoked\n`);
104
+ if (result.success) {
105
+ lines.push('✅ **Status**: Authorization successfully revoked\n');
106
+ lines.push(`**Connection ID**: ${result.connectionId || connectionId}\n`);
107
+ if (result.connectionName) {
108
+ lines.push(`**Connection Name**: ${result.connectionName}\n`);
109
+ }
110
+ if (result.revokedAt) {
111
+ lines.push(`**Revoked At**: ${new Date(result.revokedAt).toLocaleString()}\n`);
112
+ }
113
+ lines.push('\n## What This Means\n');
114
+ lines.push('- Your access tokens have been deleted from the system');
115
+ lines.push(`- You are now signed out of this ${providerName} connection`);
116
+ lines.push('- To access this service again, you will be prompted to re-authorize on next use\n');
117
+ }
118
+ else {
119
+ lines.push('⚠️ **Status**: Revocation failed or not needed\n');
120
+ lines.push(`**Message**: ${result.message}\n`);
121
+ if (result.details?.reason) {
122
+ lines.push(`**Reason**: ${result.details.reason}\n`);
123
+ }
124
+ }
125
+ return lines.join('\n');
126
+ }
127
+ }
128
+ //# sourceMappingURL=SignOutTool.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * AccessConnectionTool
3
+ * Access a specific external connection, check auth status, and return available tool schemas.
4
+ *
5
+ * Phase 2 of Unified Connection Gateway Implementation
6
+ */
7
+ import { BaseToolController } from '../base/BaseToolController.js';
8
+ import { UcmLocalApiClient } from '../../clients/UcmLocalApiClient.js';
9
+ import { ILogger } from '../../interfaces/ILogger.js';
10
+ export declare class AccessConnectionTool extends BaseToolController {
11
+ constructor(ucmClient: UcmLocalApiClient, logger: ILogger, publishingAuthorId?: string);
12
+ get name(): string;
13
+ get description(): string;
14
+ get inputSchema(): any;
15
+ protected validateParams(params: any): void;
16
+ protected handleExecute(params: {
17
+ connectionId: string;
18
+ }): Promise<any>;
19
+ }
20
+ //# sourceMappingURL=AccessConnectionTool.d.ts.map
@@ -0,0 +1,83 @@
1
+ /**
2
+ * AccessConnectionTool
3
+ * Access a specific external connection, check auth status, and return available tool schemas.
4
+ *
5
+ * Phase 2 of Unified Connection Gateway Implementation
6
+ */
7
+ import { BaseToolController } from '../base/BaseToolController.js';
8
+ import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
9
+ export class AccessConnectionTool extends BaseToolController {
10
+ constructor(ucmClient, logger, publishingAuthorId) {
11
+ super(ucmClient, logger, publishingAuthorId);
12
+ }
13
+ get name() {
14
+ return 'ucm_access_connection';
15
+ }
16
+ get description() {
17
+ return 'Access a specific external connection, check auth status, and return available tool schemas. Use this to see what tools are available for a connection before calling them.';
18
+ }
19
+ get inputSchema() {
20
+ return {
21
+ type: 'object',
22
+ properties: {
23
+ connectionId: {
24
+ type: 'string',
25
+ description: 'Connection ID (SharePoint connection or Remote MCP Server) from ucm_list_connections',
26
+ minLength: 36,
27
+ maxLength: 36
28
+ }
29
+ },
30
+ required: ['connectionId']
31
+ };
32
+ }
33
+ validateParams(params) {
34
+ super.validateParams(params);
35
+ // Validate connectionId
36
+ if (!params.connectionId || typeof params.connectionId !== 'string') {
37
+ throw new McpError(McpErrorCode.InvalidParams, 'connectionId is required and must be a string');
38
+ }
39
+ if (params.connectionId.length !== 36) {
40
+ throw new McpError(McpErrorCode.InvalidParams, 'connectionId must be a valid UUID (36 characters)');
41
+ }
42
+ }
43
+ async handleExecute(params) {
44
+ this.logger.info('AccessConnectionTool', `Accessing connection: ${params.connectionId}`);
45
+ try {
46
+ const result = await this.ucmClient.accessConnection(params.connectionId);
47
+ this.logger.info('AccessConnectionTool', 'Connection accessed successfully', '', {
48
+ connectionId: params.connectionId,
49
+ connectionType: result.connectionType,
50
+ isAuthenticated: result.isAuthenticated,
51
+ toolCount: result.tools?.length || 0
52
+ });
53
+ // Return the markdown content for AI consumption
54
+ return result.markdown;
55
+ }
56
+ catch (error) {
57
+ // Sanitize error for logging
58
+ const sanitizedError = {
59
+ message: error?.message,
60
+ status: error?.response?.status,
61
+ data: error?.response?.data
62
+ };
63
+ this.logger.error('AccessConnectionTool', 'Failed to access connection', '', sanitizedError);
64
+ if (error instanceof McpError) {
65
+ throw error;
66
+ }
67
+ // Handle API errors
68
+ if (error.response) {
69
+ const status = error.response.status;
70
+ const errorData = error.response.data;
71
+ if (status === 401 || status === 403) {
72
+ throw new McpError(McpErrorCode.InvalidRequest, `Access denied: ${errorData?.message || 'Not authorized to access this connection'}`);
73
+ }
74
+ if (status === 404) {
75
+ throw new McpError(McpErrorCode.InvalidParams, `Connection not found: ${params.connectionId}`);
76
+ }
77
+ throw new McpError(McpErrorCode.InternalError, `API error: ${errorData?.message || error.message}`);
78
+ }
79
+ throw new McpError(McpErrorCode.InternalError, `Failed to access connection: ${error.message}`);
80
+ }
81
+ }
82
+ }
83
+ //# sourceMappingURL=AccessConnectionTool.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * CallRemoteToolTool
3
+ * Execute a tool on an external connection (SharePoint or Remote MCP Server).
4
+ *
5
+ * Phase 3 of Unified Connection Gateway Implementation
6
+ */
7
+ import { BaseToolController } from '../base/BaseToolController.js';
8
+ import { UcmLocalApiClient } from '../../clients/UcmLocalApiClient.js';
9
+ import { ILogger } from '../../interfaces/ILogger.js';
10
+ export declare class CallRemoteToolTool extends BaseToolController {
11
+ constructor(ucmClient: UcmLocalApiClient, logger: ILogger, publishingAuthorId?: string);
12
+ get name(): string;
13
+ get description(): string;
14
+ get inputSchema(): any;
15
+ protected validateParams(params: any): void;
16
+ protected handleExecute(params: {
17
+ connectionId: string;
18
+ toolName: string;
19
+ args?: Record<string, any>;
20
+ }): Promise<any>;
21
+ }
22
+ //# sourceMappingURL=CallRemoteToolTool.d.ts.map
@@ -0,0 +1,103 @@
1
+ /**
2
+ * CallRemoteToolTool
3
+ * Execute a tool on an external connection (SharePoint or Remote MCP Server).
4
+ *
5
+ * Phase 3 of Unified Connection Gateway Implementation
6
+ */
7
+ import { BaseToolController } from '../base/BaseToolController.js';
8
+ import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
9
+ export class CallRemoteToolTool extends BaseToolController {
10
+ constructor(ucmClient, logger, publishingAuthorId) {
11
+ super(ucmClient, logger, publishingAuthorId);
12
+ }
13
+ get name() {
14
+ return 'ucm_call_remote_tool';
15
+ }
16
+ get description() {
17
+ return 'Call a tool on an external connection. Use ucm_access_connection first to see available tools and their schemas.';
18
+ }
19
+ get inputSchema() {
20
+ return {
21
+ type: 'object',
22
+ properties: {
23
+ connectionId: {
24
+ type: 'string',
25
+ description: 'Connection ID (SharePoint connection or Remote MCP Server) from ucm_list_connections',
26
+ minLength: 36,
27
+ maxLength: 36
28
+ },
29
+ toolName: {
30
+ type: 'string',
31
+ description: 'Name of the tool to call (see ucm_access_connection for available tools)',
32
+ minLength: 1
33
+ },
34
+ args: {
35
+ type: 'object',
36
+ description: 'Tool arguments as key-value pairs (see ucm_access_connection for parameter schemas)'
37
+ }
38
+ },
39
+ required: ['connectionId', 'toolName']
40
+ };
41
+ }
42
+ validateParams(params) {
43
+ super.validateParams(params);
44
+ // Validate connectionId
45
+ if (!params.connectionId || typeof params.connectionId !== 'string') {
46
+ throw new McpError(McpErrorCode.InvalidParams, 'connectionId is required and must be a string');
47
+ }
48
+ if (params.connectionId.length !== 36) {
49
+ throw new McpError(McpErrorCode.InvalidParams, 'connectionId must be a valid UUID (36 characters)');
50
+ }
51
+ // Validate toolName
52
+ if (!params.toolName || typeof params.toolName !== 'string') {
53
+ throw new McpError(McpErrorCode.InvalidParams, 'toolName is required and must be a string');
54
+ }
55
+ // Validate args if provided
56
+ if (params.args !== undefined && typeof params.args !== 'object') {
57
+ throw new McpError(McpErrorCode.InvalidParams, 'args must be an object');
58
+ }
59
+ }
60
+ async handleExecute(params) {
61
+ this.logger.info('CallRemoteToolTool', `Calling tool ${params.toolName} on connection: ${params.connectionId}`);
62
+ try {
63
+ const result = await this.ucmClient.callRemoteTool(params.connectionId, params.toolName, params.args);
64
+ this.logger.info('CallRemoteToolTool', 'Tool executed successfully', '', {
65
+ connectionId: params.connectionId,
66
+ toolName: params.toolName,
67
+ success: result.success,
68
+ authRequired: result.authRequired || false
69
+ });
70
+ // Return the markdown content for AI consumption
71
+ return result.markdown;
72
+ }
73
+ catch (error) {
74
+ // Sanitize error for logging
75
+ const sanitizedError = {
76
+ message: error?.message,
77
+ status: error?.response?.status,
78
+ data: error?.response?.data
79
+ };
80
+ this.logger.error('CallRemoteToolTool', 'Failed to execute tool', '', sanitizedError);
81
+ if (error instanceof McpError) {
82
+ throw error;
83
+ }
84
+ // Handle API errors
85
+ if (error.response) {
86
+ const status = error.response.status;
87
+ const errorData = error.response.data;
88
+ if (status === 401 || status === 403) {
89
+ throw new McpError(McpErrorCode.InvalidRequest, `Access denied: ${errorData?.message || 'Not authorized to access this connection'}`);
90
+ }
91
+ if (status === 404) {
92
+ throw new McpError(McpErrorCode.InvalidParams, `Connection not found: ${params.connectionId}`);
93
+ }
94
+ if (status === 400) {
95
+ throw new McpError(McpErrorCode.InvalidParams, `Invalid request: ${errorData?.message || errorData?.error || 'Invalid parameters'}`);
96
+ }
97
+ throw new McpError(McpErrorCode.InternalError, `API error: ${errorData?.message || error.message}`);
98
+ }
99
+ throw new McpError(McpErrorCode.InternalError, `Failed to execute tool: ${error.message}`);
100
+ }
101
+ }
102
+ }
103
+ //# sourceMappingURL=CallRemoteToolTool.js.map
@@ -0,0 +1,12 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ import { UcmLocalApiClient } from '../../clients/UcmLocalApiClient.js';
3
+ import { ILogger } from '../../interfaces/ILogger.js';
4
+ export declare class MoveArtifactTool extends BaseToolController {
5
+ constructor(ucmClient: UcmLocalApiClient, logger: ILogger, publishingAuthorId?: string);
6
+ get name(): string;
7
+ get description(): string;
8
+ get inputSchema(): Record<string, unknown>;
9
+ protected handleExecute(params: Record<string, unknown>): Promise<Record<string, unknown>>;
10
+ protected validateParams(params: Record<string, unknown>): void;
11
+ }
12
+ //# sourceMappingURL=MoveArtifactTool.d.ts.map
@@ -0,0 +1,104 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ import { parsePath } from '../../utils/PathUtils.js';
3
+ import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
4
+ export class MoveArtifactTool extends BaseToolController {
5
+ constructor(ucmClient, logger, publishingAuthorId) {
6
+ super(ucmClient, logger, publishingAuthorId);
7
+ }
8
+ get name() {
9
+ return 'ucm_move_artifact';
10
+ }
11
+ get description() {
12
+ return 'Move an artifact from one namespace location to another. All versions are moved. Requires read access on source and write access on destination.';
13
+ }
14
+ get inputSchema() {
15
+ return {
16
+ type: 'object',
17
+ properties: {
18
+ sourcePath: {
19
+ type: 'string',
20
+ description: `Full source artifact path (e.g., "${this.publishingAuthorId || '1234567890'}/main/commands/user/CreateUserCommand.ts")`,
21
+ minLength: 1,
22
+ maxLength: 250
23
+ },
24
+ destinationPath: {
25
+ type: 'string',
26
+ description: `Full destination artifact path (e.g., "${this.publishingAuthorId || '1234567890'}/main/services/user/UserService.ts")`,
27
+ minLength: 1,
28
+ maxLength: 250
29
+ }
30
+ },
31
+ required: ['sourcePath', 'destinationPath'],
32
+ additionalProperties: false
33
+ };
34
+ }
35
+ async handleExecute(params) {
36
+ const { sourcePath, destinationPath } = params;
37
+ this.logger.debug('MoveArtifactTool', `Moving artifact: ${sourcePath} → ${destinationPath}`);
38
+ try {
39
+ const source = parsePath(sourcePath);
40
+ const dest = parsePath(destinationPath);
41
+ // Build destination namespace (author/repository/category/subcategory)
42
+ const destNamespace = `${dest.author}/${dest.repository}/${dest.category}/${dest.subcategory}`;
43
+ const result = await this.ucmClient.editArtifactMetadata(source.author, source.repository, source.category, source.subcategory, source.filename, {
44
+ updateNamespace: destNamespace,
45
+ updateFilename: dest.filename
46
+ });
47
+ const responseData = result.data || result;
48
+ const response = {
49
+ success: true,
50
+ operation: 'move',
51
+ artifact: {
52
+ originalPath: sourcePath,
53
+ newPath: destinationPath,
54
+ affectedVersions: responseData.affectedVersions || 'all'
55
+ },
56
+ details: responseData.details || `Artifact moved from ${sourcePath} to ${destinationPath}`,
57
+ _links: responseData._links || {}
58
+ };
59
+ this.logger.info('MoveArtifactTool', `Artifact moved successfully: ${sourcePath} → ${destinationPath}`);
60
+ return response;
61
+ }
62
+ catch (error) {
63
+ this.logger.error('MoveArtifactTool', `Failed to move artifact: ${sourcePath} → ${destinationPath}`, '', error);
64
+ const errorMessage = error instanceof Error ? error.message : String(error);
65
+ if (errorMessage?.includes('not found')) {
66
+ throw new McpError(McpErrorCode.InvalidParams, `Artifact not found at path: ${sourcePath}. Please verify the path is correct and the artifact exists.`);
67
+ }
68
+ if (errorMessage?.includes('permission') || errorMessage?.includes('unauthorized')) {
69
+ throw new McpError(McpErrorCode.InvalidParams, `Insufficient permissions. You need read access on the source and write access on the destination namespace.`);
70
+ }
71
+ if (errorMessage?.includes('destination already exists')) {
72
+ throw new McpError(McpErrorCode.InvalidParams, `An artifact already exists at: ${destinationPath}. Choose a different destination.`);
73
+ }
74
+ if (errorMessage?.includes('Author not found')) {
75
+ throw new McpError(McpErrorCode.InvalidParams, `Author not found. Ensure both source and destination paths use valid author identifiers.`);
76
+ }
77
+ throw error;
78
+ }
79
+ }
80
+ validateParams(params) {
81
+ super.validateParams(params);
82
+ const { sourcePath, destinationPath } = params;
83
+ // Validate source path
84
+ const sourceParts = sourcePath.split('/');
85
+ if (sourceParts.length < 5) {
86
+ throw new McpError(McpErrorCode.InvalidParams, 'sourcePath must contain author/repository/category/subcategory/filename');
87
+ }
88
+ // Validate destination path
89
+ const destParts = destinationPath.split('/');
90
+ if (destParts.length < 5) {
91
+ throw new McpError(McpErrorCode.InvalidParams, 'destinationPath must contain author/repository/category/subcategory/filename');
92
+ }
93
+ // Validate destination filename
94
+ const destFilename = destParts[destParts.length - 1];
95
+ if (!/^[^\/\\:*?"<>|]+\.[a-zA-Z0-9]+$/.test(destFilename)) {
96
+ throw new McpError(McpErrorCode.InvalidParams, 'destinationPath filename must be valid with an extension (e.g., "UserService.ts")');
97
+ }
98
+ // Source and destination must differ
99
+ if (sourcePath === destinationPath) {
100
+ throw new McpError(McpErrorCode.InvalidParams, 'sourcePath and destinationPath must be different');
101
+ }
102
+ }
103
+ }
104
+ //# sourceMappingURL=MoveArtifactTool.js.map
@@ -75,6 +75,14 @@ export class PublishArtifactFromFileTool extends BaseToolController {
75
75
  this.logger.debug('PublishArtifactFromFileTool', `File read successfully: ${filePath}`, '', {
76
76
  size: content.length
77
77
  });
78
+ // Validate path depth - must be exactly 4 segments: author/repository/category/subcategory
79
+ const cleanPathParts = path.replace(/^\/+|\/+$/g, '').split('/').filter((p) => p.length > 0);
80
+ if (cleanPathParts.length > 4) {
81
+ throw new McpError(McpErrorCode.InvalidParams, `Path has too many segments (got ${cleanPathParts.length}, maximum is 4). ` +
82
+ `The path must follow the structure: author/repository/category/subcategory. ` +
83
+ `The filename is a separate parameter. Do not add additional sub-levels. ` +
84
+ `Example: "${this.publishingAuthorId || '1234567890'}/main/commands/user"`);
85
+ }
78
86
  // Parse the path to extract components
79
87
  const pathComponents = parsePath(path);
80
88
  // Validate path components exist including repository
@@ -66,6 +66,14 @@ export class PublishArtifactTool extends BaseToolController {
66
66
  size: content.length
67
67
  });
68
68
  try {
69
+ // Validate path depth - must be exactly 4 segments: author/repository/category/subcategory
70
+ const cleanPath = path.replace(/^\/+|\/+$/g, '').split('/').filter((p) => p.length > 0);
71
+ if (cleanPath.length > 4) {
72
+ throw new McpError(McpErrorCode.InvalidParams, `Path has too many segments (got ${cleanPath.length}, maximum is 4). ` +
73
+ `The path must follow the structure: author/repository/category/subcategory. ` +
74
+ `The filename is a separate parameter. Do not add additional sub-levels. ` +
75
+ `Example: "${this.publishingAuthorId || '1234567890'}/main/commands/user"`);
76
+ }
69
77
  // Parse the path to extract components
70
78
  const pathComponents = parsePath(path);
71
79
  // Validate path components exist including repository
@@ -1,11 +1,11 @@
1
1
  /**
2
- * SharePointListConnectionsTool
3
- * List all SharePoint connections available to the user
2
+ * ListConnectionsTool
3
+ * List all external connections available to the user
4
4
  */
5
5
  import { BaseToolController } from '../base/BaseToolController.js';
6
6
  import { UcmLocalApiClient } from '../../clients/UcmLocalApiClient.js';
7
7
  import { ILogger } from '../../interfaces/ILogger.js';
8
- export declare class SharePointListConnectionsTool extends BaseToolController {
8
+ export declare class ListConnectionsTool extends BaseToolController {
9
9
  constructor(ucmClient: UcmLocalApiClient, logger: ILogger, publishingAuthorId?: string);
10
10
  get name(): string;
11
11
  get description(): string;
@@ -1,10 +1,10 @@
1
1
  /**
2
- * SharePointListConnectionsTool
3
- * List all SharePoint connections available to the user
2
+ * ListConnectionsTool
3
+ * List all external connections available to the user
4
4
  */
5
5
  import { BaseToolController } from '../base/BaseToolController.js';
6
6
  import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
7
- export class SharePointListConnectionsTool extends BaseToolController {
7
+ export class ListConnectionsTool extends BaseToolController {
8
8
  constructor(ucmClient, logger, publishingAuthorId) {
9
9
  super(ucmClient, logger, publishingAuthorId);
10
10
  }
@@ -49,7 +49,7 @@ export class SharePointListConnectionsTool extends BaseToolController {
49
49
  }
50
50
  }
51
51
  async handleExecute(params) {
52
- this.logger.info('SharePointListConnectionsTool', `Listing SharePoint connections (limit: ${params.limit || 20}, offset: ${params.offset || 0})`);
52
+ this.logger.info('ListConnectionsTool', `Listing external connections (limit: ${params.limit || 20}, offset: ${params.offset || 0})`);
53
53
  try {
54
54
  // Get organizationId from auth ticket (via UcmApiClient)
55
55
  // Call API to list connections with pagination params
@@ -70,7 +70,7 @@ export class SharePointListConnectionsTool extends BaseToolController {
70
70
  status: error?.response?.status,
71
71
  data: error?.response?.data
72
72
  };
73
- this.logger.error('SharePointListConnectionsTool', 'Failed to list connections', '', sanitizedError);
73
+ this.logger.error('ListConnectionsTool', 'Failed to list connections', '', sanitizedError);
74
74
  if (error instanceof McpError) {
75
75
  throw error;
76
76
  }
@@ -1,6 +1,6 @@
1
1
  import { BaseToolController } from '../base/BaseToolController.js';
2
2
  //import packageJson from '../../../../publish/package.json' assert { type: 'json' };
3
- const version = '3.0.0'; //TODO: tried to sync this with packageJson but it didn't work.
3
+ const version = '5.0.1'; //TODO: tried to sync this with packageJson but it didn't work.
4
4
  export class HealthCheckController extends BaseToolController {
5
5
  constructor(ucmClient, logger, publishingAuthorId) {
6
6
  super(ucmClient, logger, publishingAuthorId);
@@ -22,8 +22,8 @@ export class SharePointErrorHandler {
22
22
  };
23
23
  logger.error(toolName, 'SharePoint operation failed', '', sanitizedError);
24
24
  const serverError = error?.response?.data;
25
- // Handle SharePoint authentication required (OnBehalfOf flow)
26
- if (error?.response?.status === 401 && serverError?.error === 'SHAREPOINT_LOGIN_REQUIRED') {
25
+ // Handle OAuth authentication required (user-delegated flow)
26
+ if (error?.response?.status === 401 && serverError?.error === 'AUTH_REQUIRED') {
27
27
  const userMessage = serverError.message || 'To access SharePoint, please login then continue your conversation.';
28
28
  // loginUrl can be in serverError directly or in details (check both)
29
29
  const loginUrl = serverError.loginUrl || serverError.details?.loginUrl;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utaba/ucm-mcp-server",
3
- "version": "4.3.2",
3
+ "version": "5.0.1",
4
4
  "description": "Universal Context Manager MCP Server - AI Productivity Platform",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucm-mcp-server",
3
- "version": "4.3.2",
3
+ "version": "5.0.1",
4
4
  "description": "Universal Context Manager MCP Server - AI Productivity Platform",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",