@0xmonaco/mcp-server 0.1.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # @0xmonaco/mcp-server
2
+
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - ce9ff98: Initial 0.1.0 release - Early development version
8
+
9
+ ## 2.0.0
10
+
11
+ ### Documentation Updates
12
+
13
+ - Updated package information and dependencies
14
+
15
+ ## 1.0.0
16
+
17
+ ### Major Changes
18
+
19
+ - 82a856a: Initial release
package/bin/cli.js ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'node:url';
4
+ import { dirname, resolve } from 'node:path';
5
+ import { spawn } from 'node:child_process';
6
+ import { createRequire } from 'node:module';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ const require = createRequire(import.meta.url);
11
+
12
+ // Path to the main MCP server
13
+ const scriptPath = resolve(__dirname, '../src/index.js');
14
+
15
+ try {
16
+ // Check if the built files exist
17
+ require.resolve(scriptPath);
18
+
19
+ // Execute the server
20
+ const server = spawn('node', [scriptPath], {
21
+ stdio: 'inherit',
22
+ shell: false
23
+ });
24
+
25
+ server.on('error', (err) => {
26
+ console.error('Failed to start server:', err);
27
+ process.exit(1);
28
+ });
29
+
30
+ // Handle clean shutdown
31
+ const cleanup = () => {
32
+ if (!server.killed) {
33
+ server.kill();
34
+ }
35
+ };
36
+
37
+ process.on('SIGINT', cleanup);
38
+ process.on('SIGTERM', cleanup);
39
+ process.on('exit', cleanup);
40
+
41
+ } catch (error) {
42
+ console.error('Error: Server files not found. The package may not be built correctly.');
43
+ console.error('Please try reinstalling the package or contact the maintainers.');
44
+ console.error(error);
45
+ process.exit(1);
46
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@0xmonaco/mcp-server",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "description": "MCP server for the Monaco documentation",
6
+ "engines": {
7
+ "node": ">=18.0.0"
8
+ },
9
+ "bin": "./bin/cli.js",
10
+ "main": "src/index.js",
11
+ "author": "codebycarson",
12
+ "dependencies": {
13
+ "@mintlify/openapi-types": "^0.0.0",
14
+ "@mintlify/validation": "^0.1.320",
15
+ "@modelcontextprotocol/sdk": "^1.6.1",
16
+ "axios": "^1.8.1",
17
+ "dashify": "^2.0.0",
18
+ "trieve-ts-sdk": "^0.0.62"
19
+ },
20
+ "scripts": {
21
+ "start": "tsx src/index.js",
22
+ "test": "jest"
23
+ }
24
+ }
package/settings.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare const SERVER_NAME: string;
2
+ export declare const SERVER_VERSION: string;
package/settings.js ADDED
@@ -0,0 +1,2 @@
1
+ export const SERVER_NAME = "mintlify";
2
+ export const SERVER_VERSION = "1.0.0";
@@ -0,0 +1,2 @@
1
+ export declare const SUBDOMAIN: Readonly<string>;
2
+ export declare const SERVER_URL: Readonly<string>;
@@ -0,0 +1,3 @@
1
+ // read-only
2
+ export const SUBDOMAIN = "monaco";
3
+ export const SERVER_URL = "https://leaves.mintlify.com";
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function connectServer(server: McpServer): Promise<void>;
package/src/connect.js ADDED
@@ -0,0 +1,41 @@
1
+ var __awaiter =
2
+ (this && this.__awaiter) ||
3
+ function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) {
5
+ return value instanceof P
6
+ ? value
7
+ : new P(function (resolve) {
8
+ resolve(value);
9
+ });
10
+ }
11
+ return new (P || (P = Promise))(function (resolve, reject) {
12
+ function fulfilled(value) {
13
+ try {
14
+ step(generator.next(value));
15
+ } catch (e) {
16
+ reject(e);
17
+ }
18
+ }
19
+ function rejected(value) {
20
+ try {
21
+ step(generator["throw"](value));
22
+ } catch (e) {
23
+ reject(e);
24
+ }
25
+ }
26
+ function step(result) {
27
+ result.done
28
+ ? resolve(result.value)
29
+ : adopt(result.value).then(fulfilled, rejected);
30
+ }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
35
+ export function connectServer(server) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ const transport = new StdioServerTransport();
38
+ yield server.connect(transport);
39
+ console.error("MCP Server running on stdio");
40
+ });
41
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/src/index.js ADDED
@@ -0,0 +1,50 @@
1
+ var __awaiter =
2
+ (this && this.__awaiter) ||
3
+ function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) {
5
+ return value instanceof P
6
+ ? value
7
+ : new P(function (resolve) {
8
+ resolve(value);
9
+ });
10
+ }
11
+ return new (P || (P = Promise))(function (resolve, reject) {
12
+ function fulfilled(value) {
13
+ try {
14
+ step(generator.next(value));
15
+ } catch (e) {
16
+ reject(e);
17
+ }
18
+ }
19
+ function rejected(value) {
20
+ try {
21
+ step(generator["throw"](value));
22
+ } catch (e) {
23
+ reject(e);
24
+ }
25
+ }
26
+ function step(result) {
27
+ result.done
28
+ ? resolve(result.value)
29
+ : adopt(result.value).then(fulfilled, rejected);
30
+ }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ import { connectServer } from "./connect.js";
35
+ import { initialize } from "./initialize.js";
36
+ import { createSearchTool } from "./search.js";
37
+ import { createTools } from "./tools/index.js";
38
+ function main() {
39
+ return __awaiter(this, void 0, void 0, function* () {
40
+ const server = initialize();
41
+ const existingTools = new Set();
42
+ yield createSearchTool(server);
43
+ yield createTools(server, existingTools);
44
+ yield connectServer(server);
45
+ });
46
+ }
47
+ main().catch((error) => {
48
+ console.error("Fatal error in trying to initialize MCP server: ", error);
49
+ process.exit(1);
50
+ });
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function initialize(): McpServer;
@@ -0,0 +1,10 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { SERVER_NAME, SERVER_VERSION } from "../settings.js";
3
+ export function initialize() {
4
+ console.error("Initializing MCP Server...");
5
+ const server = new McpServer({
6
+ name: SERVER_NAME,
7
+ version: SERVER_VERSION,
8
+ });
9
+ return server;
10
+ }
@@ -0,0 +1,6 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { InitializationConfiguration } from "./types.js";
3
+ export declare function fetchSearchConfigurationAndTools(
4
+ subdomain: string,
5
+ ): Promise<InitializationConfiguration>;
6
+ export declare function createSearchTool(server: McpServer): Promise<void>;
package/src/search.js ADDED
@@ -0,0 +1,118 @@
1
+ var __awaiter =
2
+ (this && this.__awaiter) ||
3
+ function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) {
5
+ return value instanceof P
6
+ ? value
7
+ : new P(function (resolve) {
8
+ resolve(value);
9
+ });
10
+ }
11
+ return new (P || (P = Promise))(function (resolve, reject) {
12
+ function fulfilled(value) {
13
+ try {
14
+ step(generator.next(value));
15
+ } catch (e) {
16
+ reject(e);
17
+ }
18
+ }
19
+ function rejected(value) {
20
+ try {
21
+ step(generator["throw"](value));
22
+ } catch (e) {
23
+ reject(e);
24
+ }
25
+ }
26
+ function step(result) {
27
+ result.done
28
+ ? resolve(result.value)
29
+ : adopt(result.value).then(fulfilled, rejected);
30
+ }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ import axios from "axios";
35
+ import { TrieveSDK } from "trieve-ts-sdk";
36
+ import { z } from "zod";
37
+ import { SUBDOMAIN } from "./config.readonly.js";
38
+ import { SERVER_URL } from "./config.readonly.js";
39
+ import { formatErr, throwOnAxiosError } from "./utils.js";
40
+ const DEFAULT_BASE_URL = "https://api.mintlifytrieve.com";
41
+ export function fetchSearchConfigurationAndTools(subdomain) {
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ try {
44
+ const response = yield axios.get(
45
+ `${SERVER_URL}/api/mcp/config/${subdomain}`,
46
+ {
47
+ validateStatus() {
48
+ return true;
49
+ },
50
+ },
51
+ );
52
+ throwOnAxiosError(response, "Failed to fetch MCP config");
53
+ return response.data;
54
+ } catch (err) {
55
+ throw new Error(
56
+ formatErr(err).replace(
57
+ "Request failed with status code 404",
58
+ `${subdomain} not found`,
59
+ ),
60
+ );
61
+ }
62
+ });
63
+ }
64
+ function search(query, config) {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ const trieve = new TrieveSDK({
67
+ apiKey: config.trieveApiKey,
68
+ datasetId: config.trieveDatasetId,
69
+ baseUrl: DEFAULT_BASE_URL,
70
+ });
71
+ const data = yield trieve.autocomplete({
72
+ page_size: 10,
73
+ query,
74
+ search_type: "fulltext",
75
+ extend_results: true,
76
+ score_threshold: 1,
77
+ });
78
+ if (data.chunks === undefined || data.chunks.length === 0) {
79
+ throw new Error("No results found");
80
+ }
81
+ return data.chunks.map((result) => {
82
+ const { chunk } = result;
83
+ // TODO: Append custom domain to the link
84
+ return {
85
+ title: chunk.metadata.title,
86
+ content: chunk.chunk_html,
87
+ link: chunk.link,
88
+ };
89
+ });
90
+ });
91
+ }
92
+ export function createSearchTool(server) {
93
+ return __awaiter(this, void 0, void 0, function* () {
94
+ const config = yield fetchSearchConfigurationAndTools(SUBDOMAIN);
95
+ server.tool(
96
+ "search",
97
+ `Search across the ${config.name} documentation to fetch relevant context for a given query`,
98
+ {
99
+ query: z.string(),
100
+ },
101
+ (_a) =>
102
+ __awaiter(this, [_a], void 0, function* ({ query }) {
103
+ const results = yield search(query, config);
104
+ const content = results.map((result) => {
105
+ const { title, content, link } = result;
106
+ const text = `Title: ${title}\nContent: ${content}\nLink: ${link}`;
107
+ return {
108
+ type: "text",
109
+ text,
110
+ };
111
+ });
112
+ return {
113
+ content,
114
+ };
115
+ }),
116
+ );
117
+ });
118
+ }
@@ -0,0 +1,45 @@
1
+ import { OpenAPI } from "@mintlify/openapi-types";
2
+ import { HttpMethod, SecurityParameterGroup } from "@mintlify/validation";
3
+ import { Endpoint } from "@mintlify/validation";
4
+ import { DataSchemaArray } from "@mintlify/validation";
5
+ import { Tool } from "@modelcontextprotocol/sdk/types.js";
6
+ import { ServerParams, ToolWithEndpoint } from "../types.js";
7
+ import { NestedRecord, SimpleRecord } from "../utils.js";
8
+ export declare function convertStrToTitle(str: string): string;
9
+ export declare function findNextIteration(
10
+ set: Set<string>,
11
+ str: string,
12
+ ): number;
13
+ export declare function getMcpEnabledEndpointsFromOpenApiSpec(
14
+ spec: OpenAPI.Document,
15
+ ): Endpoint<DataSchemaArray>[];
16
+ export declare function convertEndpointToTool(
17
+ endpoint: Endpoint<DataSchemaArray>,
18
+ ): Omit<Tool, "inputSchema">;
19
+ export declare function getMcpToolsAndEndpointsFromOpenApiSpec(
20
+ spec: OpenAPI.Document,
21
+ ): ToolWithEndpoint[];
22
+ export declare function getEndpointsFromOpenApi(
23
+ specification: OpenAPI.Document,
24
+ ): Endpoint<DataSchemaArray>[];
25
+ export declare function loadEnv(key: string): SimpleRecord;
26
+ export declare function convertSecurityParameterSection(
27
+ securityParameters: SecurityParameterGroup,
28
+ envVariables: SimpleRecord,
29
+ location: string,
30
+ ): {
31
+ key: string;
32
+ value: NestedRecord | undefined;
33
+ }[];
34
+ export declare function convertEndpointToCategorizedZod(
35
+ envKey: string,
36
+ endpoint: Endpoint,
37
+ ): {
38
+ url: string;
39
+ method: HttpMethod;
40
+ paths: ServerParams;
41
+ queries: ServerParams;
42
+ body: ServerParams | undefined;
43
+ headers: ServerParams;
44
+ cookies: ServerParams;
45
+ };
@@ -0,0 +1,278 @@
1
+ import { OpenApiToEndpointConverter } from "@mintlify/validation";
2
+ import dashify from "dashify";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { OpenAPIV3 } from "openapi-types";
7
+ import { z } from "zod";
8
+ import { initializeObject } from "../utils.js";
9
+ import { dataSchemaArrayToZod, dataSchemaToZod } from "./zod.js";
10
+ export function convertStrToTitle(str) {
11
+ const spacedString = str.replace(/[-_]/g, " ");
12
+ const words = spacedString.split(/(?=[A-Z])|\s+/);
13
+ const titleCasedWords = words.map((word) => {
14
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
15
+ });
16
+ return titleCasedWords.join(" ");
17
+ }
18
+ export function findNextIteration(set, str) {
19
+ let count = 1;
20
+ set.forEach((val) => {
21
+ if (val.startsWith(`${str}---`)) {
22
+ count = Number(val.replace(`${str}---`, ""));
23
+ }
24
+ });
25
+ return count + 1;
26
+ }
27
+ export function getMcpEnabledEndpointsFromOpenApiSpec(spec) {
28
+ var _a;
29
+ const mcpEnabledEndpoints = [];
30
+ const isMcpEnabledGloballyInSpec =
31
+ ((_a = spec["x-mcp"]) === null || _a === void 0 ? void 0 : _a.enabled) ===
32
+ true;
33
+ const endpoints = getEndpointsFromOpenApi(spec);
34
+ if (isMcpEnabledGloballyInSpec) {
35
+ const notDisabledEndpoints = endpoints.filter((endpoint) => {
36
+ var _a;
37
+ return (
38
+ ((_a = endpoint.xMcp) === null || _a === void 0
39
+ ? void 0
40
+ : _a.enabled) !== false
41
+ );
42
+ });
43
+ mcpEnabledEndpoints.push(...notDisabledEndpoints);
44
+ } else {
45
+ const enabledEndpoints = endpoints.filter((endpoint) => {
46
+ var _a;
47
+ return (
48
+ ((_a = endpoint.xMcp) === null || _a === void 0
49
+ ? void 0
50
+ : _a.enabled) === true
51
+ );
52
+ });
53
+ mcpEnabledEndpoints.push(...enabledEndpoints);
54
+ }
55
+ return mcpEnabledEndpoints;
56
+ }
57
+ export function convertEndpointToTool(endpoint) {
58
+ var _a, _b;
59
+ let name;
60
+ if ((_a = endpoint.xMcp) === null || _a === void 0 ? void 0 : _a.name) {
61
+ name = endpoint.xMcp.name;
62
+ } else if (endpoint.title) {
63
+ name = dashify(endpoint.title);
64
+ } else {
65
+ name = convertStrToTitle(endpoint.path);
66
+ }
67
+ let description;
68
+ if (
69
+ (_b = endpoint.xMcp) === null || _b === void 0 ? void 0 : _b.description
70
+ ) {
71
+ description = endpoint.xMcp.description;
72
+ } else if (endpoint.description) {
73
+ description = endpoint.description;
74
+ } else {
75
+ description = `${endpoint.method} ${endpoint.path}`;
76
+ }
77
+ return {
78
+ name,
79
+ description,
80
+ };
81
+ }
82
+ export function getMcpToolsAndEndpointsFromOpenApiSpec(spec) {
83
+ const endpoints = getMcpEnabledEndpointsFromOpenApiSpec(spec);
84
+ const toolsWithEndpoints = [];
85
+ endpoints.forEach((endpoint) => {
86
+ const tool = convertEndpointToTool(endpoint);
87
+ toolsWithEndpoints.push({
88
+ tool,
89
+ endpoint,
90
+ });
91
+ });
92
+ return toolsWithEndpoints;
93
+ }
94
+ export function getEndpointsFromOpenApi(specification) {
95
+ const endpoints = [];
96
+ const paths = specification.paths;
97
+ for (const path in paths) {
98
+ const pathObj = paths[path];
99
+ const httpMethods = Object.values(OpenAPIV3.HttpMethods);
100
+ for (const method of httpMethods) {
101
+ if (!pathObj || !(method in pathObj)) {
102
+ continue;
103
+ }
104
+ const endpoint = OpenApiToEndpointConverter.convert(
105
+ specification,
106
+ path,
107
+ method,
108
+ true,
109
+ );
110
+ endpoints.push(endpoint);
111
+ }
112
+ }
113
+ return endpoints;
114
+ }
115
+ export function loadEnv(key) {
116
+ var _a;
117
+ let envVars = {};
118
+ try {
119
+ const envPath = path.join(
120
+ fileURLToPath(import.meta.url),
121
+ "../../../",
122
+ ".env.json",
123
+ );
124
+ if (fs.existsSync(envPath)) {
125
+ envVars = JSON.parse(fs.readFileSync(envPath).toString());
126
+ return (_a = envVars[key]) !== null && _a !== void 0 ? _a : {};
127
+ }
128
+ } catch (error) {
129
+ // if there's no env, the user will be prompted
130
+ // for their auth info at runtime if necessary
131
+ // (shouldn't happen either way)
132
+ }
133
+ return envVars;
134
+ }
135
+ function convertParameterSection(parameters, paramSection) {
136
+ Object.entries(parameters).forEach(([key, value]) => {
137
+ const schema = value.schema;
138
+ paramSection[key] = dataSchemaArrayToZod(schema);
139
+ });
140
+ }
141
+ function convertParametersAndAddToRelevantParamGroups(
142
+ parameters,
143
+ paths,
144
+ queries,
145
+ headers,
146
+ cookies,
147
+ ) {
148
+ convertParameterSection(parameters.path, paths);
149
+ convertParameterSection(parameters.query, queries);
150
+ convertParameterSection(parameters.header, headers);
151
+ convertParameterSection(parameters.cookie, cookies);
152
+ }
153
+ // this function returns all the securityParameters and seeds them with the envVariables if we have them
154
+ export function convertSecurityParameterSection(
155
+ securityParameters,
156
+ envVariables,
157
+ location,
158
+ ) {
159
+ const res = [];
160
+ Object.entries(securityParameters).forEach(([key, value]) => {
161
+ let envKeyList = [];
162
+ let targetKey = "";
163
+ switch (value.type) {
164
+ case "apiKey":
165
+ envKeyList = [location, key];
166
+ targetKey = "API_KEY";
167
+ break;
168
+ case "http":
169
+ envKeyList = [location, key, "HTTP"];
170
+ targetKey = value.scheme;
171
+ break;
172
+ case "oauth2":
173
+ default:
174
+ break;
175
+ }
176
+ const target = initializeObject(
177
+ Object.assign({}, envVariables),
178
+ envKeyList,
179
+ );
180
+ if (envKeyList.length && !target[targetKey]) {
181
+ res.push({
182
+ key,
183
+ value: undefined,
184
+ });
185
+ } else {
186
+ res.push({
187
+ key,
188
+ value: target[targetKey],
189
+ });
190
+ }
191
+ });
192
+ return res;
193
+ }
194
+ function convertSecurityParametersAndAddToRelevantParamGroups(
195
+ securityParameters,
196
+ queries,
197
+ headers,
198
+ cookies,
199
+ envVariables,
200
+ ) {
201
+ const queryRes = convertSecurityParameterSection(
202
+ securityParameters.query,
203
+ envVariables,
204
+ "query",
205
+ );
206
+ const headerRes = convertSecurityParameterSection(
207
+ securityParameters.header,
208
+ envVariables,
209
+ "header",
210
+ );
211
+ const cookieRes = convertSecurityParameterSection(
212
+ securityParameters.cookie,
213
+ envVariables,
214
+ "cookie",
215
+ );
216
+ // non intuitive that we seed the query with a zod type if it *doesn't* exist
217
+ // but that's because if we don't have it in our env Variables, that means the user should provide it in their query.
218
+ queryRes.forEach(({ key, value }) => {
219
+ if (!value) {
220
+ queries[key] = z.string();
221
+ }
222
+ });
223
+ headerRes.forEach(({ key, value }) => {
224
+ if (!value) {
225
+ headers[key] = z.string();
226
+ }
227
+ });
228
+ cookieRes.forEach(({ key, value }) => {
229
+ if (!value) {
230
+ cookies[key] = z.string();
231
+ }
232
+ });
233
+ }
234
+ export function convertEndpointToCategorizedZod(envKey, endpoint) {
235
+ var _a, _b, _c;
236
+ const envVariables = loadEnv(envKey);
237
+ const url = `${((_b = (_a = endpoint.servers) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.url) || ""}${endpoint.path}`;
238
+ const method = endpoint.method;
239
+ const paths = {};
240
+ const queries = {};
241
+ const headers = {};
242
+ const cookies = {};
243
+ let body = undefined;
244
+ convertParametersAndAddToRelevantParamGroups(
245
+ endpoint.request.parameters,
246
+ paths,
247
+ queries,
248
+ headers,
249
+ cookies,
250
+ );
251
+ if (
252
+ (_c = endpoint.request.security[0]) === null || _c === void 0
253
+ ? void 0
254
+ : _c.parameters
255
+ ) {
256
+ convertSecurityParametersAndAddToRelevantParamGroups(
257
+ endpoint.request.security[0].parameters,
258
+ queries,
259
+ headers,
260
+ cookies,
261
+ envVariables,
262
+ );
263
+ }
264
+ const jsonBodySchema = endpoint.request.body["application/json"];
265
+ const bodySchemaArray =
266
+ jsonBodySchema === null || jsonBodySchema === void 0
267
+ ? void 0
268
+ : jsonBodySchema.schemaArray;
269
+ const bodySchema =
270
+ bodySchemaArray === null || bodySchemaArray === void 0
271
+ ? void 0
272
+ : bodySchemaArray[0];
273
+ if (bodySchema) {
274
+ const zodBodySchema = dataSchemaToZod(bodySchema);
275
+ body = { body: zodBodySchema };
276
+ }
277
+ return { url, method, paths, queries, body, headers, cookies };
278
+ }
@@ -0,0 +1,5 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function createTools(
3
+ server: McpServer,
4
+ existingTools: Set<string>,
5
+ ): Promise<void>;
@@ -0,0 +1,185 @@
1
+ var __awaiter =
2
+ (this && this.__awaiter) ||
3
+ function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) {
5
+ return value instanceof P
6
+ ? value
7
+ : new P(function (resolve) {
8
+ resolve(value);
9
+ });
10
+ }
11
+ return new (P || (P = Promise))(function (resolve, reject) {
12
+ function fulfilled(value) {
13
+ try {
14
+ step(generator.next(value));
15
+ } catch (e) {
16
+ reject(e);
17
+ }
18
+ }
19
+ function rejected(value) {
20
+ try {
21
+ step(generator["throw"](value));
22
+ } catch (e) {
23
+ reject(e);
24
+ }
25
+ }
26
+ function step(result) {
27
+ result.done
28
+ ? resolve(result.value)
29
+ : adopt(result.value).then(fulfilled, rejected);
30
+ }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ import axios, { isAxiosError } from "axios";
35
+ import dashify from "dashify";
36
+ import fs from "node:fs";
37
+ import path from "node:path";
38
+ import { fileURLToPath } from "node:url";
39
+ import {
40
+ convertEndpointToCategorizedZod,
41
+ convertSecurityParameterSection,
42
+ convertStrToTitle,
43
+ findNextIteration,
44
+ loadEnv,
45
+ } from "./helpers.js";
46
+ export function createTools(server, existingTools) {
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ const toolsDir = path.join(fileURLToPath(import.meta.url), "..", "..");
49
+ let tools = JSON.parse(
50
+ fs.readFileSync(path.join(toolsDir, "tools.json"), "utf-8"),
51
+ );
52
+ tools = tools.filter((tool) => tool.endpoint);
53
+ tools.forEach(({ uuid, endpoint }) => {
54
+ const envVars = loadEnv(uuid);
55
+ const {
56
+ url: urlSchema,
57
+ method: methodSchema,
58
+ paths: pathsSchema,
59
+ queries: queriesSchema,
60
+ body: bodySchema,
61
+ headers: headersSchema,
62
+ cookies: cookiesSchema,
63
+ } = convertEndpointToCategorizedZod(uuid, endpoint);
64
+ const serverArgumentsSchemas = Object.assign(
65
+ Object.assign(
66
+ Object.assign(
67
+ Object.assign(Object.assign({}, pathsSchema), queriesSchema),
68
+ bodySchema,
69
+ ),
70
+ headersSchema,
71
+ ),
72
+ cookiesSchema,
73
+ );
74
+ if (!endpoint.title) {
75
+ endpoint.title = `${endpoint.method} ${convertStrToTitle(endpoint.path)}`;
76
+ }
77
+ if (existingTools.has(endpoint.title)) {
78
+ const lastCount = findNextIteration(existingTools, endpoint.title);
79
+ endpoint.title = `${endpoint.title}---${lastCount}`;
80
+ }
81
+ if (endpoint.title.length > 64) {
82
+ endpoint.title = endpoint.title.slice(0, -64);
83
+ }
84
+ existingTools.add(endpoint.title);
85
+ server.tool(
86
+ dashify(endpoint.title),
87
+ endpoint.description || endpoint.title,
88
+ serverArgumentsSchemas,
89
+ (inputArgs) =>
90
+ __awaiter(this, void 0, void 0, function* () {
91
+ var _a;
92
+ const inputParams = {};
93
+ const inputHeaders = {};
94
+ const inputCookies = {};
95
+ let urlWithPathParams = urlSchema;
96
+ let inputBody = undefined;
97
+ if ("body" in inputArgs) {
98
+ inputBody = inputArgs.body;
99
+ delete inputArgs.body;
100
+ }
101
+ Object.entries(inputArgs).forEach(([key, value]) => {
102
+ if (key in pathsSchema) {
103
+ urlWithPathParams = urlWithPathParams.replace(
104
+ `{${key}}`,
105
+ value,
106
+ );
107
+ } else if (key in queriesSchema) {
108
+ inputParams[key] = value;
109
+ } else if (key in headersSchema) {
110
+ inputHeaders[key] = value;
111
+ } else if (key in cookiesSchema) {
112
+ inputCookies[key] = value;
113
+ }
114
+ });
115
+ const securityParamSections =
116
+ (_a = endpoint.request.security[0]) === null || _a === void 0
117
+ ? void 0
118
+ : _a.parameters;
119
+ if (securityParamSections) {
120
+ const headerRes = convertSecurityParameterSection(
121
+ securityParamSections.header,
122
+ envVars,
123
+ "header",
124
+ );
125
+ headerRes.forEach(({ key, value }) => {
126
+ if (value) {
127
+ inputHeaders[key] = value;
128
+ }
129
+ });
130
+ const cookieRes = convertSecurityParameterSection(
131
+ securityParamSections.cookie,
132
+ envVars,
133
+ "cookie",
134
+ );
135
+ cookieRes.forEach(({ key, value }) => {
136
+ if (value) {
137
+ inputCookies[key] = value;
138
+ }
139
+ });
140
+ const queryRes = convertSecurityParameterSection(
141
+ securityParamSections.query,
142
+ envVars,
143
+ "query",
144
+ );
145
+ queryRes.forEach(({ key, value }) => {
146
+ if (value) {
147
+ inputParams[key] = value;
148
+ }
149
+ });
150
+ }
151
+ try {
152
+ const response = yield axios({
153
+ url: urlWithPathParams,
154
+ method: methodSchema,
155
+ params: inputParams,
156
+ data: inputBody,
157
+ headers: inputHeaders,
158
+ });
159
+ return {
160
+ content: [
161
+ {
162
+ type: "text",
163
+ text: JSON.stringify(response.data, undefined, 2),
164
+ },
165
+ ],
166
+ };
167
+ } catch (error) {
168
+ const errMsg = JSON.stringify(error, undefined, 2);
169
+ return {
170
+ isError: true,
171
+ content: [
172
+ {
173
+ type: "text",
174
+ text: isAxiosError(error)
175
+ ? `${error.message}\n\n${errMsg}`
176
+ : errMsg,
177
+ },
178
+ ],
179
+ };
180
+ }
181
+ }),
182
+ );
183
+ });
184
+ });
185
+ }
@@ -0,0 +1,13 @@
1
+ import {
2
+ DataSchema,
3
+ DataSchemaArray,
4
+ IncrementalDataSchema,
5
+ IncrementalDataSchemaArray,
6
+ } from "@mintlify/validation";
7
+ import { z } from "zod";
8
+ type SchemaInput = DataSchema | IncrementalDataSchema;
9
+ export declare function dataSchemaArrayToZod(
10
+ schemas: DataSchemaArray | IncrementalDataSchemaArray,
11
+ ): z.ZodTypeAny;
12
+ export declare function dataSchemaToZod(schema: SchemaInput): z.ZodTypeAny;
13
+ export {};
@@ -0,0 +1,207 @@
1
+ import { Blob } from "node:buffer";
2
+ import { z } from "zod";
3
+ function panic(error) {
4
+ throw error;
5
+ }
6
+ // WebFile polyfill implementation partly taken from the fetch-blob package:
7
+ // https://github.com/node-fetch/fetch-blob/blob/main/file.js - MIT License
8
+ const WebFile = class File extends Blob {
9
+ constructor(
10
+ init,
11
+ name = panic(new TypeError("File constructor requires name argument")),
12
+ options = {},
13
+ ) {
14
+ if (arguments.length < 2) {
15
+ throw new TypeError(
16
+ `Failed to construct 'File': 2 arguments required, but only ${arguments.length} present.`,
17
+ );
18
+ }
19
+ super(init, options);
20
+ this._lastModified = 0;
21
+ this._name = "";
22
+ // Simulate WebIDL type casting for NaN value in lastModified option.
23
+ const lastModified =
24
+ options.lastModified === undefined
25
+ ? Date.now()
26
+ : Number(options.lastModified);
27
+ if (!Number.isNaN(lastModified)) {
28
+ this._lastModified = lastModified;
29
+ }
30
+ this._name = String(name);
31
+ }
32
+ get name() {
33
+ return this._name;
34
+ }
35
+ get lastModified() {
36
+ return this._lastModified;
37
+ }
38
+ get [Symbol.toStringTag]() {
39
+ return "File";
40
+ }
41
+ static [Symbol.hasInstance](object) {
42
+ return (
43
+ !!object &&
44
+ object instanceof Blob &&
45
+ /^(File)$/.test(String(object[Symbol.toStringTag]))
46
+ );
47
+ }
48
+ };
49
+ const File = typeof global.File === "undefined" ? WebFile : global.File;
50
+ const ANY = z.any();
51
+ const ANY_OPT = ANY.optional();
52
+ const BOOLEAN = z.boolean();
53
+ const BOOLEAN_OPT = BOOLEAN.optional();
54
+ const DATE = z.coerce.date();
55
+ const DATE_OPT = DATE.optional();
56
+ const FILE = z.instanceof(File);
57
+ const FILE_OPT = FILE.optional();
58
+ const NULL = z.null();
59
+ const NULL_OPT = NULL.optional();
60
+ const RECORD = z.record(z.any());
61
+ const RECORD_WITH_DEFAULT = RECORD.default({});
62
+ const RECORD_OPT = RECORD.optional();
63
+ const STRING = z.string();
64
+ const NUMBER = z.number();
65
+ const INTEGER = z.number().int();
66
+ export function dataSchemaArrayToZod(schemas) {
67
+ const firstSchema = dataSchemaToZod(schemas[0]);
68
+ if (!schemas[1]) {
69
+ return firstSchema;
70
+ }
71
+ const secondSchema = dataSchemaToZod(schemas[1]);
72
+ const zodSchemas = [firstSchema, secondSchema];
73
+ for (const schema of schemas.slice(2)) {
74
+ zodSchemas.push(dataSchemaToZod(schema));
75
+ }
76
+ return z.union(zodSchemas).array();
77
+ }
78
+ function getEnumSchema(enumList, type) {
79
+ const zodSchema = z.enum(enumList.map(String));
80
+ if (type === "string") return zodSchema;
81
+ return zodSchema.transform(Number);
82
+ }
83
+ export function dataSchemaToZod(schema) {
84
+ if (!("type" in schema) || Object.keys(schema).length === 0) {
85
+ return schema.required ? ANY : ANY_OPT;
86
+ }
87
+ switch (schema.type) {
88
+ case "null":
89
+ return schema.required ? NULL : NULL_OPT;
90
+ case "boolean":
91
+ return schema.required ? BOOLEAN : BOOLEAN_OPT;
92
+ case "enum<string>":
93
+ const strEnumSchema = getEnumSchema(schema.enum, "string");
94
+ return schema.required ? strEnumSchema : strEnumSchema.optional();
95
+ case "enum<number>":
96
+ case "enum<integer>":
97
+ const numEnumSchema = getEnumSchema(schema.enum, "number");
98
+ return schema.required ? numEnumSchema : numEnumSchema.optional();
99
+ case "file":
100
+ return schema.required ? FILE : FILE_OPT;
101
+ case "any":
102
+ return schema.required ? ANY : ANY_OPT;
103
+ case "string":
104
+ if ("enum" in schema && Array.isArray(schema.enum)) {
105
+ return schema.required
106
+ ? z.enum(schema.enum)
107
+ : z.enum(schema.enum).optional();
108
+ }
109
+ if (schema.format === "binary") {
110
+ return schema.required ? FILE : FILE_OPT;
111
+ }
112
+ let stringSchema = STRING;
113
+ if (schema.minLength !== undefined) {
114
+ stringSchema = stringSchema.min(schema.minLength);
115
+ }
116
+ if (schema.maxLength !== undefined) {
117
+ stringSchema = stringSchema.max(schema.maxLength);
118
+ }
119
+ if (schema.pattern !== undefined) {
120
+ stringSchema = stringSchema.regex(new RegExp(schema.pattern));
121
+ }
122
+ switch (schema.format) {
123
+ case "email":
124
+ stringSchema = stringSchema.email();
125
+ break;
126
+ case "uri":
127
+ case "url":
128
+ stringSchema = stringSchema.url();
129
+ break;
130
+ case "uuid":
131
+ stringSchema = stringSchema.uuid();
132
+ break;
133
+ case "date-time":
134
+ return schema.required ? DATE : DATE_OPT;
135
+ }
136
+ return schema.required ? stringSchema : stringSchema.optional();
137
+ case "number":
138
+ case "integer":
139
+ if ("enum" in schema && Array.isArray(schema.enum)) {
140
+ const numEnumSchema = getEnumSchema(schema.enum, schema.type);
141
+ return schema.required ? numEnumSchema : numEnumSchema.optional();
142
+ }
143
+ let numberSchema = schema.type === "integer" ? INTEGER : NUMBER;
144
+ if (schema.minimum !== undefined) {
145
+ numberSchema = numberSchema.min(schema.minimum);
146
+ }
147
+ if (schema.maximum !== undefined) {
148
+ numberSchema = numberSchema.max(schema.maximum);
149
+ }
150
+ if (
151
+ schema.exclusiveMinimum !== undefined &&
152
+ schema.minimum !== undefined
153
+ ) {
154
+ numberSchema = numberSchema.gt(schema.minimum);
155
+ }
156
+ if (
157
+ schema.exclusiveMaximum !== undefined &&
158
+ schema.maximum !== undefined
159
+ ) {
160
+ numberSchema = numberSchema.lt(schema.maximum);
161
+ }
162
+ return schema.required ? numberSchema : numberSchema.optional();
163
+ case "array":
164
+ let itemSchema;
165
+ let arraySchema = z.any().array();
166
+ if (Array.isArray(schema.items)) {
167
+ itemSchema = dataSchemaArrayToZod(schema.items);
168
+ if (schema.items.length > 1) {
169
+ arraySchema = itemSchema;
170
+ } else {
171
+ arraySchema = itemSchema.array();
172
+ }
173
+ } else {
174
+ itemSchema = dataSchemaToZod(schema.items);
175
+ arraySchema = itemSchema.array();
176
+ }
177
+ if (schema.minItems !== undefined) {
178
+ arraySchema = arraySchema.min(schema.minItems);
179
+ }
180
+ if (schema.maxItems !== undefined) {
181
+ arraySchema = arraySchema.max(schema.maxItems);
182
+ }
183
+ return schema.required ? arraySchema : arraySchema.optional();
184
+ case "object":
185
+ const shape = {};
186
+ const requiredProperties = schema.requiredProperties;
187
+ const requiredPropertiesSet = new Set(
188
+ requiredProperties !== null && requiredProperties !== void 0
189
+ ? requiredProperties
190
+ : [],
191
+ );
192
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
193
+ const zodPropSchema = Array.isArray(propSchema)
194
+ ? dataSchemaArrayToZod(propSchema)
195
+ : dataSchemaToZod(propSchema);
196
+ shape[key] = requiredPropertiesSet.has(key)
197
+ ? zodPropSchema
198
+ : zodPropSchema.optional();
199
+ }
200
+ if (Object.keys(shape).length === 0) {
201
+ return schema.required ? RECORD_WITH_DEFAULT : RECORD_OPT;
202
+ }
203
+ return schema.required ? z.object(shape) : z.object(shape).optional();
204
+ default:
205
+ return ANY;
206
+ }
207
+ }
package/src/tools.json ADDED
@@ -0,0 +1,8 @@
1
+ [
2
+ {
3
+ "tool": {
4
+ "name": "search",
5
+ "description": "Search across the Mint Starter Kit documentation to fetch relevant context for a given query"
6
+ }
7
+ }
8
+ ]
package/src/utils.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { OpenAPI } from "@mintlify/openapi-types";
2
+ import { AxiosResponse } from "axios";
3
+ export type NestedRecord =
4
+ | string
5
+ | {
6
+ [key: string]: NestedRecord;
7
+ };
8
+ export type SimpleRecord = Record<string, NestedRecord>;
9
+ export declare function initializeObject(
10
+ obj: SimpleRecord,
11
+ path: string[],
12
+ ): SimpleRecord;
13
+ export declare function getFileId(
14
+ spec: OpenAPI.Document,
15
+ index: number,
16
+ ): string | number;
17
+ export declare function throwOnAxiosError(
18
+ response: AxiosResponse<any, any>,
19
+ errMsg: string,
20
+ ): void;
21
+ export declare function formatErr(err: unknown): any;
package/src/utils.js ADDED
@@ -0,0 +1,62 @@
1
+ import axios from "axios";
2
+ export function initializeObject(obj, path) {
3
+ let current = obj;
4
+ for (const key of path) {
5
+ if (!current[key] || typeof current[key] !== "object") {
6
+ current[key] = {};
7
+ }
8
+ current = current[key];
9
+ }
10
+ return current;
11
+ }
12
+ export function getFileId(spec, index) {
13
+ var _a;
14
+ return ((_a = spec.info) === null || _a === void 0 ? void 0 : _a.title) &&
15
+ spec.info.version
16
+ ? `${spec.info.title} - ${spec.info.version}`
17
+ : index;
18
+ }
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ export function throwOnAxiosError(response, errMsg) {
21
+ var _a, _b;
22
+ if (response.status !== 200) {
23
+ if (
24
+ ((_a = response.headers["content-type"]) === null || _a === void 0
25
+ ? void 0
26
+ : _a.includes("application/json")) &&
27
+ ((_b = response.data) === null || _b === void 0 ? void 0 : _b.error)
28
+ ) {
29
+ throw new Error(`${errMsg}: ${response.data.error}`);
30
+ } else {
31
+ throw new Error(
32
+ `${errMsg}: ${response.status} ${response.statusText || ""}`,
33
+ );
34
+ }
35
+ }
36
+ if (!response.data) {
37
+ throw new Error(`${errMsg}: ${response.status} ${response.statusText}`);
38
+ }
39
+ }
40
+ export function formatErr(err) {
41
+ var _a, _b;
42
+ if (axios.isAxiosError(err)) {
43
+ if (err.message) {
44
+ return err.message;
45
+ } else if (err.response) {
46
+ return (_b =
47
+ (_a = err.response.data) === null || _a === void 0
48
+ ? void 0
49
+ : _a.error) !== null && _b !== void 0
50
+ ? _b
51
+ : `${err.response.status} ${err.response.statusText}`;
52
+ } else if (err.request) {
53
+ return "No response received from server";
54
+ } else {
55
+ err = "An unknown error occurred";
56
+ }
57
+ } else if (err instanceof Error) {
58
+ return err.message;
59
+ } else {
60
+ return JSON.stringify(err, undefined, 2);
61
+ }
62
+ }