@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.
- package/dist/clients/UcmLocalApiClient.d.ts +47 -0
- package/dist/clients/UcmLocalApiClient.js +55 -0
- package/dist/server/ToolRegistry.js +26 -12
- package/dist/tools/authorization/ListAuthorizationsTool.d.ts +21 -0
- package/dist/tools/authorization/ListAuthorizationsTool.js +146 -0
- package/dist/tools/authorization/SignOutTool.d.ts +23 -0
- package/dist/tools/authorization/SignOutTool.js +128 -0
- package/dist/tools/connections/AccessConnectionTool.d.ts +20 -0
- package/dist/tools/connections/AccessConnectionTool.js +83 -0
- package/dist/tools/connections/CallRemoteToolTool.d.ts +22 -0
- package/dist/tools/connections/CallRemoteToolTool.js +103 -0
- package/dist/utils/SharePointErrorHandler.js +2 -2
- package/package.json +1 -1
- package/package.json.backup +1 -1
|
@@ -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
|
-
|
|
27
|
-
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
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
|
|
76
|
+
// Register connection tools (ListConnectionsTool is unified - lists all connection types)
|
|
70
77
|
this.registerTool(new ListConnectionsTool(this.ucmClient, this.logger, this.authorId));
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this.registerTool(new
|
|
74
|
-
this.registerTool(new
|
|
75
|
-
this.registerTool(new
|
|
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
|
|
26
|
-
if (error?.response?.status === 401 && serverError?.error === '
|
|
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