Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/mcp/common/mcpGalleryManifestService.ts
5284 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { CancellationToken } from '../../../base/common/cancellation.js';
7
import { Event } from '../../../base/common/event.js';
8
import { Disposable } from '../../../base/common/lifecycle.js';
9
import { ILogService } from '../../log/common/log.js';
10
import { IProductService } from '../../product/common/productService.js';
11
import { IRequestService, isSuccess } from '../../request/common/request.js';
12
import { McpGalleryResourceType, IMcpGalleryManifest, IMcpGalleryManifestService, McpGalleryManifestStatus } from './mcpGalleryManifest.js';
13
14
const SUPPORTED_VERSIONS = [
15
'v0.1',
16
'v0',
17
];
18
19
export class McpGalleryManifestService extends Disposable implements IMcpGalleryManifestService {
20
21
readonly _serviceBrand: undefined;
22
readonly onDidChangeMcpGalleryManifest = Event.None;
23
readonly onDidChangeMcpGalleryManifestStatus = Event.None;
24
25
private readonly versionByUrl = new Map<string, Promise<string>>();
26
27
get mcpGalleryManifestStatus(): McpGalleryManifestStatus {
28
return !!this.productService.mcpGallery?.serviceUrl ? McpGalleryManifestStatus.Available : McpGalleryManifestStatus.Unavailable;
29
}
30
31
constructor(
32
@IProductService private readonly productService: IProductService,
33
@IRequestService private readonly requestService: IRequestService,
34
@ILogService protected readonly logService: ILogService,
35
) {
36
super();
37
}
38
39
async getMcpGalleryManifest(): Promise<IMcpGalleryManifest | null> {
40
if (!this.productService.mcpGallery) {
41
return null;
42
}
43
return this.createMcpGalleryManifest(this.productService.mcpGallery.serviceUrl, SUPPORTED_VERSIONS[0]);
44
}
45
46
protected async createMcpGalleryManifest(url: string, version?: string): Promise<IMcpGalleryManifest> {
47
url = url.endsWith('/') ? url.slice(0, -1) : url;
48
49
if (!version) {
50
let versionPromise = this.versionByUrl.get(url);
51
if (!versionPromise) {
52
this.versionByUrl.set(url, versionPromise = this.getVersion(url));
53
}
54
version = await versionPromise;
55
}
56
57
const isProductGalleryUrl = this.productService.mcpGallery?.serviceUrl === url;
58
const serversUrl = `${url}/${version}/servers`;
59
const resources = [
60
{
61
id: serversUrl,
62
type: McpGalleryResourceType.McpServersQueryService
63
},
64
{
65
id: `${serversUrl}/{name}/versions/{version}`,
66
type: McpGalleryResourceType.McpServerVersionUri
67
},
68
{
69
id: `${serversUrl}/{name}/versions/latest`,
70
type: McpGalleryResourceType.McpServerLatestVersionUri
71
}
72
];
73
74
if (isProductGalleryUrl) {
75
resources.push({
76
id: `${serversUrl}/by-name/{name}`,
77
type: McpGalleryResourceType.McpServerNamedResourceUri
78
});
79
resources.push({
80
id: this.productService.mcpGallery.itemWebUrl,
81
type: McpGalleryResourceType.McpServerWebUri
82
});
83
resources.push({
84
id: this.productService.mcpGallery.publisherUrl,
85
type: McpGalleryResourceType.PublisherUriTemplate
86
});
87
resources.push({
88
id: this.productService.mcpGallery.supportUrl,
89
type: McpGalleryResourceType.ContactSupportUri
90
});
91
resources.push({
92
id: this.productService.mcpGallery.supportUrl,
93
type: McpGalleryResourceType.ContactSupportUri
94
});
95
resources.push({
96
id: this.productService.mcpGallery.privacyPolicyUrl,
97
type: McpGalleryResourceType.PrivacyPolicyUri
98
});
99
resources.push({
100
id: this.productService.mcpGallery.termsOfServiceUrl,
101
type: McpGalleryResourceType.TermsOfServiceUri
102
});
103
resources.push({
104
id: this.productService.mcpGallery.reportUrl,
105
type: McpGalleryResourceType.ReportUri
106
});
107
}
108
109
if (version === 'v0') {
110
resources.push({
111
id: `${serversUrl}/{id}`,
112
type: McpGalleryResourceType.McpServerIdUri
113
});
114
}
115
116
return {
117
version,
118
url,
119
resources
120
};
121
}
122
123
private async getVersion(url: string): Promise<string> {
124
for (const version of SUPPORTED_VERSIONS) {
125
if (await this.checkVersion(url, version)) {
126
return version;
127
}
128
}
129
return SUPPORTED_VERSIONS[0];
130
}
131
132
private async checkVersion(url: string, version: string): Promise<boolean> {
133
try {
134
const context = await this.requestService.request({
135
type: 'GET',
136
url: `${url}/${version}/servers?limit=1`,
137
}, CancellationToken.None);
138
if (isSuccess(context)) {
139
return true;
140
}
141
this.logService.info(`The service at ${url} does not support version ${version}. Service returned status ${context.res.statusCode}.`);
142
} catch (error) {
143
this.logService.error(error);
144
}
145
return false;
146
}
147
}
148
149