@utaba/ucm-mcp-server 4.3.2 → 5.0.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.
@@ -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
@@ -21,13 +21,20 @@ import { CreateRepositoryTool } from '../tools/repository/CreateRepositoryTool.j
21
21
  import { GetRepositoryTool } from '../tools/repository/GetRepositoryTool.js';
22
22
  import { UpdateRepositoryTool } from '../tools/repository/UpdateRepositoryTool.js';
23
23
  import { DeleteRepositoryGuidanceTool } from '../tools/repository/DeleteRepositoryGuidanceTool.js';
24
- // Import SharePoint tools
24
+ // Import connection/SharePoint tools
25
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';
26
+ // DEPRECATED: Legacy SharePoint tools - use ucm_call_remote_tool via unified gateway instead
27
+ // Commented out 2026-02-13 - pending confirmation that unified gateway works correctly
28
+ // import { SharePointSearchTool } from '../tools/sharepoint/SharePointSearchTool.js';
29
+ // import { SharePointListFoldersTool } from '../tools/sharepoint/SharePointListFoldersTool.js';
30
+ // import { SharePointReadFileTool } from '../tools/sharepoint/SharePointReadFileTool.js';
31
+ // import { SharePointReadRelatedFileTool } from '../tools/sharepoint/SharePointReadRelatedFileTool.js';
32
+ // Import unified authorization tools
33
+ import { SignOutTool } from '../tools/authorization/SignOutTool.js';
34
+ import { ListAuthorizationsTool } from '../tools/authorization/ListAuthorizationsTool.js';
35
+ // Import connection tools
36
+ import { AccessConnectionTool } from '../tools/connections/AccessConnectionTool.js';
37
+ import { CallRemoteToolTool } from '../tools/connections/CallRemoteToolTool.js';
31
38
  export class ToolRegistry {
32
39
  ucmClient;
33
40
  logger;
@@ -66,13 +73,20 @@ export class ToolRegistry {
66
73
  this.registerTool(new GetRepositoryTool(this.ucmClient, this.logger, this.authorId));
67
74
  this.registerTool(new UpdateRepositoryTool(this.ucmClient, this.logger, this.authorId));
68
75
  this.registerTool(new DeleteRepositoryGuidanceTool(this.ucmClient, this.logger, this.authorId, this.baseUrl));
69
- // Register SharePoint tools
76
+ // Register connection tools (ListConnectionsTool is unified - lists all connection types)
70
77
  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));
78
+ // DEPRECATED: Legacy SharePoint tools - use ucm_call_remote_tool via unified gateway instead
79
+ // Commented out 2026-02-13 - pending confirmation that unified gateway works correctly
80
+ // this.registerTool(new SharePointSearchTool(this.ucmClient, this.logger, this.authorId));
81
+ // this.registerTool(new SharePointListFoldersTool(this.ucmClient, this.logger, this.authorId));
82
+ // this.registerTool(new SharePointReadFileTool(this.ucmClient, this.logger, this.authorId));
83
+ // this.registerTool(new SharePointReadRelatedFileTool(this.ucmClient, this.logger, this.authorId));
84
+ // Register connection tools (unified gateway)
85
+ this.registerTool(new AccessConnectionTool(this.ucmClient, this.logger, this.authorId));
86
+ this.registerTool(new CallRemoteToolTool(this.ucmClient, this.logger, this.authorId));
87
+ // Register unified authorization tools
88
+ this.registerTool(new SignOutTool(this.ucmClient, this.logger, this.authorId));
89
+ this.registerTool(new ListAuthorizationsTool(this.ucmClient, this.logger, this.authorId));
76
90
  this.logger.info('ToolRegistry', `Registered ${this.tools.size} MCP tools`);
77
91
  }
78
92
  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
@@ -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.0",
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.0",
4
4
  "description": "Universal Context Manager MCP Server - AI Productivity Platform",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",