@metronome/mcp 3.4.1 → 3.6.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/README.md +3 -3
- package/code-tool-paths.cjs +4 -2
- package/code-tool-paths.cjs.map +1 -1
- package/code-tool-paths.d.cts +1 -1
- package/code-tool-paths.d.cts.map +1 -1
- package/code-tool-worker.d.mts.map +1 -1
- package/code-tool-worker.d.ts.map +1 -1
- package/code-tool-worker.js +46 -3
- package/code-tool-worker.js.map +1 -1
- package/code-tool-worker.mjs +13 -3
- package/code-tool-worker.mjs.map +1 -1
- package/code-tool.d.mts.map +1 -1
- package/code-tool.d.ts.map +1 -1
- package/code-tool.js +31 -25
- package/code-tool.js.map +1 -1
- package/code-tool.mjs +22 -13
- package/code-tool.mjs.map +1 -1
- package/docs-search-tool.d.mts +2 -0
- package/docs-search-tool.d.mts.map +1 -1
- package/docs-search-tool.d.ts +2 -0
- package/docs-search-tool.d.ts.map +1 -1
- package/docs-search-tool.js +32 -2
- package/docs-search-tool.js.map +1 -1
- package/docs-search-tool.mjs +31 -2
- package/docs-search-tool.mjs.map +1 -1
- package/http.d.mts.map +1 -1
- package/http.d.ts.map +1 -1
- package/http.js +65 -3
- package/http.js.map +1 -1
- package/http.mjs +65 -3
- package/http.mjs.map +1 -1
- package/instructions.d.mts +4 -1
- package/instructions.d.mts.map +1 -1
- package/instructions.d.ts +4 -1
- package/instructions.d.ts.map +1 -1
- package/instructions.js +29 -14
- package/instructions.js.map +1 -1
- package/instructions.mjs +26 -14
- package/instructions.mjs.map +1 -1
- package/local-docs-search.d.mts +28 -0
- package/local-docs-search.d.mts.map +1 -0
- package/local-docs-search.d.ts +28 -0
- package/local-docs-search.d.ts.map +1 -0
- package/local-docs-search.js +5304 -0
- package/local-docs-search.js.map +1 -0
- package/local-docs-search.mjs +5264 -0
- package/local-docs-search.mjs.map +1 -0
- package/methods.d.mts.map +1 -1
- package/methods.d.ts.map +1 -1
- package/methods.js +6 -0
- package/methods.js.map +1 -1
- package/methods.mjs +6 -0
- package/methods.mjs.map +1 -1
- package/options.d.mts +3 -0
- package/options.d.mts.map +1 -1
- package/options.d.ts +3 -0
- package/options.d.ts.map +1 -1
- package/options.js +19 -0
- package/options.js.map +1 -1
- package/options.mjs +19 -0
- package/options.mjs.map +1 -1
- package/package.json +13 -2
- package/server.d.mts +10 -1
- package/server.d.mts.map +1 -1
- package/server.d.ts +10 -1
- package/server.d.ts.map +1 -1
- package/server.js +13 -3
- package/server.js.map +1 -1
- package/server.mjs +13 -3
- package/server.mjs.map +1 -1
- package/src/code-tool-paths.cts +3 -1
- package/src/code-tool-worker.ts +13 -3
- package/src/code-tool.ts +29 -18
- package/src/docs-search-tool.ts +46 -8
- package/src/http.ts +71 -3
- package/src/instructions.ts +33 -15
- package/src/local-docs-search.ts +6325 -0
- package/src/methods.ts +6 -0
- package/src/options.ts +24 -0
- package/src/server.ts +23 -3
- package/src/stdio.ts +4 -1
- package/src/types.ts +3 -0
- package/src/util.ts +2 -2
- package/stdio.d.mts.map +1 -1
- package/stdio.d.ts.map +1 -1
- package/stdio.js +4 -1
- package/stdio.js.map +1 -1
- package/stdio.mjs +4 -1
- package/stdio.mjs.map +1 -1
- package/types.d.mts +6 -0
- package/types.d.mts.map +1 -1
- package/types.d.ts +6 -0
- package/types.d.ts.map +1 -1
- package/types.js.map +1 -1
- package/types.mjs.map +1 -1
- package/util.js +2 -2
- package/util.js.map +1 -1
- package/util.mjs +2 -2
- package/util.mjs.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metronome/mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "The official MCP Server for the Metronome API",
|
|
5
5
|
"author": "Metronome <dev-feedback@metronome.com>",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"fix": "eslint --fix ."
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@metronome/sdk": "^3.
|
|
30
|
+
"@metronome/sdk": "^3.6.0",
|
|
31
31
|
"ajv": "^8.18.0",
|
|
32
32
|
"@cloudflare/cabidela": "^0.2.4",
|
|
33
33
|
"@hono/node-server": "^1.19.10",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"cors": "^2.8.5",
|
|
39
39
|
"express": "^5.1.0",
|
|
40
40
|
"fuse.js": "^7.1.0",
|
|
41
|
+
"minisearch": "^7.2.0",
|
|
41
42
|
"jq-web": "https://github.com/stainless-api/jq-web/releases/download/v0.8.8/jq-web.tar.gz",
|
|
42
43
|
"pino": "^10.3.1",
|
|
43
44
|
"pino-http": "^11.0.0",
|
|
@@ -151,6 +152,16 @@
|
|
|
151
152
|
"./instructions.mjs": {
|
|
152
153
|
"default": "./instructions.mjs"
|
|
153
154
|
},
|
|
155
|
+
"./local-docs-search": {
|
|
156
|
+
"import": "./local-docs-search.mjs",
|
|
157
|
+
"require": "./local-docs-search.js"
|
|
158
|
+
},
|
|
159
|
+
"./local-docs-search.js": {
|
|
160
|
+
"default": "./local-docs-search.js"
|
|
161
|
+
},
|
|
162
|
+
"./local-docs-search.mjs": {
|
|
163
|
+
"default": "./local-docs-search.mjs"
|
|
164
|
+
},
|
|
154
165
|
"./logger": {
|
|
155
166
|
"import": "./logger.mjs",
|
|
156
167
|
"require": "./logger.js"
|
package/server.d.mts
CHANGED
|
@@ -3,7 +3,10 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
3
3
|
import { ClientOptions } from '@metronome/sdk';
|
|
4
4
|
import { McpOptions } from "./options.mjs";
|
|
5
5
|
import { HandlerFunction, McpRequestContext, ToolCallResult, McpTool } from "./types.mjs";
|
|
6
|
-
export declare const newMcpServer: (stainlessApiKey
|
|
6
|
+
export declare const newMcpServer: ({ stainlessApiKey, customInstructionsPath, }: {
|
|
7
|
+
stainlessApiKey?: string | undefined;
|
|
8
|
+
customInstructionsPath?: string | undefined;
|
|
9
|
+
}) => Promise<McpServer>;
|
|
7
10
|
/**
|
|
8
11
|
* Initializes the provided MCP Server with the given tools and handlers.
|
|
9
12
|
* If not provided, the default client, tools and handlers will be used.
|
|
@@ -13,6 +16,12 @@ export declare function initMcpServer(params: {
|
|
|
13
16
|
clientOptions?: ClientOptions;
|
|
14
17
|
mcpOptions?: McpOptions;
|
|
15
18
|
stainlessApiKey?: string | undefined;
|
|
19
|
+
upstreamClientEnvs?: Record<string, string> | undefined;
|
|
20
|
+
mcpSessionId?: string | undefined;
|
|
21
|
+
mcpClientInfo?: {
|
|
22
|
+
name: string;
|
|
23
|
+
version: string;
|
|
24
|
+
} | undefined;
|
|
16
25
|
}): Promise<void>;
|
|
17
26
|
/**
|
|
18
27
|
* Selects the tools to include in the MCP Server based on the provided options.
|
package/server.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.mts","sourceRoot":"","sources":["src/server.ts"],"names":[],"mappings":"OAEO,EAAE,MAAM,EAAE,MAAM,2CAA2C;OAC3D,EAAE,SAAS,EAAE,MAAM,yCAAyC;OAM5D,EAAE,aAAa,EAAE,MAAM,gBAAgB;
|
|
1
|
+
{"version":3,"file":"server.d.mts","sourceRoot":"","sources":["src/server.ts"],"names":[],"mappings":"OAEO,EAAE,MAAM,EAAE,MAAM,2CAA2C;OAC3D,EAAE,SAAS,EAAE,MAAM,yCAAyC;OAM5D,EAAE,aAAa,EAAE,MAAM,gBAAgB;OAOvC,EAAE,UAAU,EAAE;OAEd,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,OAAO,EAAE;AAEtE,eAAO,MAAM,YAAY,GAAU,8CAGhC;IACD,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C,uBAUE,CAAC;AAEJ;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACxD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,aAAa,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAC/D,iBA0HA;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,EAAE,CAe3D;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,EACnC,OAAO,EACP,UAAU,EACV,IAAI,GACL,EAAE;IACD,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CAC3C,GAAG,OAAO,CAAC,cAAc,CAAC,CAE1B"}
|
package/server.d.ts
CHANGED
|
@@ -3,7 +3,10 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
3
3
|
import { ClientOptions } from '@metronome/sdk';
|
|
4
4
|
import { McpOptions } from "./options.js";
|
|
5
5
|
import { HandlerFunction, McpRequestContext, ToolCallResult, McpTool } from "./types.js";
|
|
6
|
-
export declare const newMcpServer: (stainlessApiKey
|
|
6
|
+
export declare const newMcpServer: ({ stainlessApiKey, customInstructionsPath, }: {
|
|
7
|
+
stainlessApiKey?: string | undefined;
|
|
8
|
+
customInstructionsPath?: string | undefined;
|
|
9
|
+
}) => Promise<McpServer>;
|
|
7
10
|
/**
|
|
8
11
|
* Initializes the provided MCP Server with the given tools and handlers.
|
|
9
12
|
* If not provided, the default client, tools and handlers will be used.
|
|
@@ -13,6 +16,12 @@ export declare function initMcpServer(params: {
|
|
|
13
16
|
clientOptions?: ClientOptions;
|
|
14
17
|
mcpOptions?: McpOptions;
|
|
15
18
|
stainlessApiKey?: string | undefined;
|
|
19
|
+
upstreamClientEnvs?: Record<string, string> | undefined;
|
|
20
|
+
mcpSessionId?: string | undefined;
|
|
21
|
+
mcpClientInfo?: {
|
|
22
|
+
name: string;
|
|
23
|
+
version: string;
|
|
24
|
+
} | undefined;
|
|
16
25
|
}): Promise<void>;
|
|
17
26
|
/**
|
|
18
27
|
* Selects the tools to include in the MCP Server based on the provided options.
|
package/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["src/server.ts"],"names":[],"mappings":"OAEO,EAAE,MAAM,EAAE,MAAM,2CAA2C;OAC3D,EAAE,SAAS,EAAE,MAAM,yCAAyC;OAM5D,EAAE,aAAa,EAAE,MAAM,gBAAgB;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["src/server.ts"],"names":[],"mappings":"OAEO,EAAE,MAAM,EAAE,MAAM,2CAA2C;OAC3D,EAAE,SAAS,EAAE,MAAM,yCAAyC;OAM5D,EAAE,aAAa,EAAE,MAAM,gBAAgB;OAOvC,EAAE,UAAU,EAAE;OAEd,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,OAAO,EAAE;AAEtE,eAAO,MAAM,YAAY,GAAU,8CAGhC;IACD,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C,uBAUE,CAAC;AAEJ;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACxD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,aAAa,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAC/D,iBA0HA;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,EAAE,CAe3D;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,EACnC,OAAO,EACP,UAAU,EACV,IAAI,GACL,EAAE;IACD,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CAC3C,GAAG,OAAO,CAAC,cAAc,CAAC,CAE1B"}
|
package/server.js
CHANGED
|
@@ -13,13 +13,15 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
|
13
13
|
const sdk_1 = __importDefault(require("@metronome/sdk"));
|
|
14
14
|
const code_tool_1 = require("./code-tool.js");
|
|
15
15
|
const docs_search_tool_1 = __importDefault(require("./docs-search-tool.js"));
|
|
16
|
+
const docs_search_tool_2 = require("./docs-search-tool.js");
|
|
17
|
+
const local_docs_search_1 = require("./local-docs-search.js");
|
|
16
18
|
const instructions_1 = require("./instructions.js");
|
|
17
19
|
const methods_1 = require("./methods.js");
|
|
18
|
-
const newMcpServer = async (stainlessApiKey) => new mcp_js_1.McpServer({
|
|
20
|
+
const newMcpServer = async ({ stainlessApiKey, customInstructionsPath, }) => new mcp_js_1.McpServer({
|
|
19
21
|
name: 'metronome_sdk_api',
|
|
20
|
-
version: '3.
|
|
22
|
+
version: '3.6.0',
|
|
21
23
|
}, {
|
|
22
|
-
instructions: await (0, instructions_1.getInstructions)(stainlessApiKey),
|
|
24
|
+
instructions: await (0, instructions_1.getInstructions)({ stainlessApiKey, customInstructionsPath }),
|
|
23
25
|
capabilities: { tools: {}, logging: {} },
|
|
24
26
|
});
|
|
25
27
|
exports.newMcpServer = newMcpServer;
|
|
@@ -41,6 +43,11 @@ async function initMcpServer(params) {
|
|
|
41
43
|
warn: logAtLevel('warning'),
|
|
42
44
|
error: logAtLevel('error'),
|
|
43
45
|
};
|
|
46
|
+
if (params.mcpOptions?.docsSearchMode === 'local') {
|
|
47
|
+
const docsDir = params.mcpOptions?.docsDir;
|
|
48
|
+
const localSearch = await local_docs_search_1.LocalDocsSearch.create(docsDir ? { docsDir } : undefined);
|
|
49
|
+
(0, docs_search_tool_2.setLocalSearch)(localSearch);
|
|
50
|
+
}
|
|
44
51
|
let _client;
|
|
45
52
|
let _clientError;
|
|
46
53
|
let _logLevel;
|
|
@@ -101,6 +108,9 @@ async function initMcpServer(params) {
|
|
|
101
108
|
reqContext: {
|
|
102
109
|
client,
|
|
103
110
|
stainlessApiKey: params.stainlessApiKey ?? params.mcpOptions?.stainlessApiKey,
|
|
111
|
+
upstreamClientEnvs: params.upstreamClientEnvs,
|
|
112
|
+
mcpSessionId: params.mcpSessionId,
|
|
113
|
+
mcpClientInfo: params.mcpClientInfo,
|
|
104
114
|
},
|
|
105
115
|
args,
|
|
106
116
|
});
|
package/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["src/server.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;;;;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["src/server.ts"],"names":[],"mappings":"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"}
|
package/server.mjs
CHANGED
|
@@ -4,13 +4,15 @@ import { CallToolRequestSchema, ListToolsRequestSchema, SetLevelRequestSchema, }
|
|
|
4
4
|
import Metronome from '@metronome/sdk';
|
|
5
5
|
import { codeTool } from "./code-tool.mjs";
|
|
6
6
|
import docsSearchTool from "./docs-search-tool.mjs";
|
|
7
|
+
import { setLocalSearch } from "./docs-search-tool.mjs";
|
|
8
|
+
import { LocalDocsSearch } from "./local-docs-search.mjs";
|
|
7
9
|
import { getInstructions } from "./instructions.mjs";
|
|
8
10
|
import { blockedMethodsForCodeTool } from "./methods.mjs";
|
|
9
|
-
export const newMcpServer = async (stainlessApiKey) => new McpServer({
|
|
11
|
+
export const newMcpServer = async ({ stainlessApiKey, customInstructionsPath, }) => new McpServer({
|
|
10
12
|
name: 'metronome_sdk_api',
|
|
11
|
-
version: '3.
|
|
13
|
+
version: '3.6.0',
|
|
12
14
|
}, {
|
|
13
|
-
instructions: await getInstructions(stainlessApiKey),
|
|
15
|
+
instructions: await getInstructions({ stainlessApiKey, customInstructionsPath }),
|
|
14
16
|
capabilities: { tools: {}, logging: {} },
|
|
15
17
|
});
|
|
16
18
|
/**
|
|
@@ -31,6 +33,11 @@ export async function initMcpServer(params) {
|
|
|
31
33
|
warn: logAtLevel('warning'),
|
|
32
34
|
error: logAtLevel('error'),
|
|
33
35
|
};
|
|
36
|
+
if (params.mcpOptions?.docsSearchMode === 'local') {
|
|
37
|
+
const docsDir = params.mcpOptions?.docsDir;
|
|
38
|
+
const localSearch = await LocalDocsSearch.create(docsDir ? { docsDir } : undefined);
|
|
39
|
+
setLocalSearch(localSearch);
|
|
40
|
+
}
|
|
34
41
|
let _client;
|
|
35
42
|
let _clientError;
|
|
36
43
|
let _logLevel;
|
|
@@ -91,6 +98,9 @@ export async function initMcpServer(params) {
|
|
|
91
98
|
reqContext: {
|
|
92
99
|
client,
|
|
93
100
|
stainlessApiKey: params.stainlessApiKey ?? params.mcpOptions?.stainlessApiKey,
|
|
101
|
+
upstreamClientEnvs: params.upstreamClientEnvs,
|
|
102
|
+
mcpSessionId: params.mcpSessionId,
|
|
103
|
+
mcpClientInfo: params.mcpClientInfo,
|
|
94
104
|
},
|
|
95
105
|
args,
|
|
96
106
|
});
|
package/server.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","sourceRoot":"","sources":["src/server.ts"],"names":[],"mappings":"AAAA,sFAAsF;OAG/E,EAAE,SAAS,EAAE,MAAM,yCAAyC;OAC5D,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,oCAAoC;OAEpC,SAAS,MAAM,gBAAgB;OAC/B,EAAE,QAAQ,EAAE;OACZ,cAAc;OACd,EAAE,eAAe,EAAE;OAEnB,EAAE,yBAAyB,EAAE;AAGpC,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"server.mjs","sourceRoot":"","sources":["src/server.ts"],"names":[],"mappings":"AAAA,sFAAsF;OAG/E,EAAE,SAAS,EAAE,MAAM,yCAAyC;OAC5D,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,oCAAoC;OAEpC,SAAS,MAAM,gBAAgB;OAC/B,EAAE,QAAQ,EAAE;OACZ,cAAc;OACd,EAAE,cAAc,EAAE;OAClB,EAAE,eAAe,EAAE;OACnB,EAAE,eAAe,EAAE;OAEnB,EAAE,yBAAyB,EAAE;AAGpC,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,EACjC,eAAe,EACf,sBAAsB,GAIvB,EAAE,EAAE,CACH,IAAI,SAAS,CACX;IACE,IAAI,EAAE,mBAAmB;IACzB,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE,MAAM,eAAe,CAAC,EAAE,eAAe,EAAE,sBAAsB,EAAE,CAAC;IAChF,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;CACzC,CACF,CAAC;AAEJ;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAQnC;IACC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,YAAY,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;IAEzF,MAAM,UAAU,GACd,CAAC,KAA6C,EAAE,EAAE,CAClD,CAAC,OAAe,EAAE,GAAG,IAAe,EAAE,EAAE;QACtC,KAAK,MAAM,CAAC,kBAAkB,CAAC;YAC7B,KAAK;YACL,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;SACxB,CAAC,CAAC;IACL,CAAC,CAAC;IACJ,MAAM,MAAM,GAAG;QACb,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC;QAC1B,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC;QACxB,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC;QAC3B,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC;KAC3B,CAAC;IAEF,IAAI,MAAM,CAAC,UAAU,EAAE,cAAc,KAAK,OAAO,EAAE,CAAC;QAClD,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC;QAC3C,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACpF,cAAc,CAAC,WAAW,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,OAA8B,CAAC;IACnC,IAAI,YAA+B,CAAC;IACpC,IAAI,SAAkE,CAAC;IAEvE,MAAM,SAAS,GAAG,GAAc,EAAE;QAChC,IAAI,YAAY;YAAE,MAAM,YAAY,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,SAAS,CAAC;oBACtB,MAAM;oBACN,GAAG,MAAM,CAAC,aAAa;oBACvB,cAAc,EAAE;wBACd,GAAG,MAAM,CAAC,aAAa,EAAE,cAAc;wBACvC,iBAAiB,EAAE,MAAM;qBAC1B;iBACF,CAAC,CAAC;gBACH,IAAI,SAAS,EAAE,CAAC;oBACd,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;gBACzD,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,YAAY,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7D,MAAM,YAAY,CAAC;YACrB,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAEjG,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QAC1D,OAAO;YACL,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;SACpD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACjD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,MAAiB,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,GAAG,SAAS,EAAE,CAAC;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,gCAAgC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;qBAC/F;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,OAAO,cAAc,CAAC;YACpB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,UAAU,EAAE;gBACV,MAAM;gBACN,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,UAAU,EAAE,eAAe;gBAC7E,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;gBAC7C,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,aAAa,EAAE,MAAM,CAAC,aAAa;aACpC;YACD,IAAI;SACL,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACjC,IAAI,QAAqD,CAAC;QAC1D,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,OAAO;gBACV,QAAQ,GAAG,OAAO,CAAC;gBACnB,MAAM;YACR,KAAK,MAAM;gBACT,QAAQ,GAAG,MAAM,CAAC;gBAClB,MAAM;YACR,KAAK,QAAQ,CAAC;YACd,KAAK,SAAS;gBACZ,QAAQ,GAAG,MAAM,CAAC;gBAClB,MAAM;YACR,KAAK,OAAO;gBACV,QAAQ,GAAG,OAAO,CAAC;gBACnB,MAAM;YACR;gBACE,QAAQ,GAAG,KAAK,CAAC;gBACjB,MAAM;QACV,CAAC;QACD,SAAS,GAAG,QAAQ,CAAC;QACrB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,OAAoB;IAC9C,MAAM,aAAa,GAAG,EAAE,CAAC;IAEzB,IAAI,OAAO,EAAE,eAAe,IAAI,IAAI,EAAE,CAAC;QACrC,aAAa,CAAC,IAAI,CAChB,QAAQ,CAAC;YACP,cAAc,EAAE,yBAAyB,CAAC,OAAO,CAAC;YAClD,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,IAAI,mBAAmB;SACrE,CAAC,CACH,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,EAAE,gBAAgB,IAAI,IAAI,EAAE,CAAC;QACtC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EACnC,OAAO,EACP,UAAU,EACV,IAAI,GAKL;IACC,OAAO,MAAM,OAAO,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC"}
|
package/src/code-tool-paths.cts
CHANGED
package/src/code-tool-worker.ts
CHANGED
|
@@ -7,6 +7,10 @@ import ts from 'typescript';
|
|
|
7
7
|
import { WorkerOutput } from './code-tool-types';
|
|
8
8
|
import { Metronome, ClientOptions } from '@metronome/sdk';
|
|
9
9
|
|
|
10
|
+
async function tseval(code: string) {
|
|
11
|
+
return import('data:application/typescript;charset=utf-8;base64,' + Buffer.from(code).toString('base64'));
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
function getRunFunctionSource(code: string): {
|
|
11
15
|
type: 'declaration' | 'expression';
|
|
12
16
|
client: string | undefined;
|
|
@@ -184,6 +188,7 @@ const fuse = new Fuse(
|
|
|
184
188
|
'client.v1.contracts.getNetBalance',
|
|
185
189
|
'client.v1.contracts.list',
|
|
186
190
|
'client.v1.contracts.listBalances',
|
|
191
|
+
'client.v1.contracts.listSeatBalances',
|
|
187
192
|
'client.v1.contracts.retrieve',
|
|
188
193
|
'client.v1.contracts.retrieveRateSchedule',
|
|
189
194
|
'client.v1.contracts.retrieveSubscriptionQuantityHistory',
|
|
@@ -297,7 +302,8 @@ function makeSdkProxy<T extends object>(obj: T, { path, isBelievedBad = false }:
|
|
|
297
302
|
|
|
298
303
|
function parseError(code: string, error: unknown): string | undefined {
|
|
299
304
|
if (!(error instanceof Error)) return;
|
|
300
|
-
const
|
|
305
|
+
const cause = error.cause instanceof Error ? `: ${error.cause.message}` : '';
|
|
306
|
+
const message = error.name ? `${error.name}: ${error.message}${cause}` : `${error.message}${cause}`;
|
|
301
307
|
try {
|
|
302
308
|
// Deno uses V8; the first "<anonymous>:LINE:COLUMN" is the top of stack.
|
|
303
309
|
const lineNumber = error.stack?.match(/<anonymous>:([0-9]+):[0-9]+/)?.[1];
|
|
@@ -353,7 +359,9 @@ const fetch = async (req: Request): Promise<Response> => {
|
|
|
353
359
|
|
|
354
360
|
const log_lines: string[] = [];
|
|
355
361
|
const err_lines: string[] = [];
|
|
356
|
-
const
|
|
362
|
+
const originalConsole = globalThis.console;
|
|
363
|
+
globalThis.console = {
|
|
364
|
+
...originalConsole,
|
|
357
365
|
log: (...args: unknown[]) => {
|
|
358
366
|
log_lines.push(util.format(...args));
|
|
359
367
|
},
|
|
@@ -363,7 +371,7 @@ const fetch = async (req: Request): Promise<Response> => {
|
|
|
363
371
|
};
|
|
364
372
|
try {
|
|
365
373
|
let run_ = async (client: any) => {};
|
|
366
|
-
|
|
374
|
+
run_ = (await tseval(`${code}\nexport default run;`)).default;
|
|
367
375
|
const result = await run_(makeSdkProxy(client, { path: ['client'] }));
|
|
368
376
|
return Response.json({
|
|
369
377
|
is_error: false,
|
|
@@ -381,6 +389,8 @@ const fetch = async (req: Request): Promise<Response> => {
|
|
|
381
389
|
} satisfies WorkerOutput,
|
|
382
390
|
{ status: 400, statusText: 'Code execution error' },
|
|
383
391
|
);
|
|
392
|
+
} finally {
|
|
393
|
+
globalThis.console = originalConsole;
|
|
384
394
|
}
|
|
385
395
|
};
|
|
386
396
|
|
package/src/code-tool.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2
2
|
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import url from 'node:url';
|
|
6
|
-
import { newDenoHTTPWorker } from '@valtown/deno-http-worker';
|
|
7
|
-
import { workerPath } from './code-tool-paths.cjs';
|
|
8
3
|
import {
|
|
9
4
|
ContentBlock,
|
|
10
5
|
McpRequestContext,
|
|
@@ -157,20 +152,24 @@ const remoteStainlessHandler = async ({
|
|
|
157
152
|
|
|
158
153
|
const codeModeEndpoint = readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool';
|
|
159
154
|
|
|
155
|
+
const localClientEnvs = {
|
|
156
|
+
METRONOME_BEARER_TOKEN: requireValue(
|
|
157
|
+
readEnv('METRONOME_BEARER_TOKEN') ?? client.bearerToken,
|
|
158
|
+
'set METRONOME_BEARER_TOKEN environment variable or provide bearerToken client option',
|
|
159
|
+
),
|
|
160
|
+
METRONOME_WEBHOOK_SECRET: readEnv('METRONOME_WEBHOOK_SECRET') ?? client.webhookSecret ?? undefined,
|
|
161
|
+
METRONOME_BASE_URL: readEnv('METRONOME_BASE_URL') ?? client.baseURL ?? undefined,
|
|
162
|
+
};
|
|
163
|
+
// Merge any upstream client envs from the request header, with upstream values taking precedence.
|
|
164
|
+
const mergedClientEnvs = { ...localClientEnvs, ...reqContext.upstreamClientEnvs };
|
|
165
|
+
|
|
160
166
|
// Setting a Stainless API key authenticates requests to the code tool endpoint.
|
|
161
167
|
const res = await fetch(codeModeEndpoint, {
|
|
162
168
|
method: 'POST',
|
|
163
169
|
headers: {
|
|
164
170
|
...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }),
|
|
165
171
|
'Content-Type': 'application/json',
|
|
166
|
-
'x-stainless-mcp-client-envs': JSON.stringify(
|
|
167
|
-
METRONOME_BEARER_TOKEN: requireValue(
|
|
168
|
-
readEnv('METRONOME_BEARER_TOKEN') ?? client.bearerToken,
|
|
169
|
-
'set METRONOME_BEARER_TOKEN environment variable or provide bearerToken client option',
|
|
170
|
-
),
|
|
171
|
-
METRONOME_WEBHOOK_SECRET: readEnv('METRONOME_WEBHOOK_SECRET') ?? client.webhookSecret ?? undefined,
|
|
172
|
-
METRONOME_BASE_URL: readEnv('METRONOME_BASE_URL') ?? client.baseURL ?? undefined,
|
|
173
|
-
}),
|
|
172
|
+
'x-stainless-mcp-client-envs': JSON.stringify(mergedClientEnvs),
|
|
174
173
|
},
|
|
175
174
|
body: JSON.stringify({
|
|
176
175
|
project_name: 'metronome',
|
|
@@ -213,6 +212,13 @@ const localDenoHandler = async ({
|
|
|
213
212
|
reqContext: McpRequestContext;
|
|
214
213
|
args: unknown;
|
|
215
214
|
}): Promise<ToolCallResult> => {
|
|
215
|
+
const fs = await import('node:fs');
|
|
216
|
+
const path = await import('node:path');
|
|
217
|
+
const url = await import('node:url');
|
|
218
|
+
const { newDenoHTTPWorker } = await import('@valtown/deno-http-worker');
|
|
219
|
+
const { getWorkerPath } = await import('./code-tool-paths.cjs');
|
|
220
|
+
const workerPath = getWorkerPath();
|
|
221
|
+
|
|
216
222
|
const client = reqContext.client;
|
|
217
223
|
const baseURLHostname = new URL(client.baseURL).hostname;
|
|
218
224
|
const { code } = args as { code: string };
|
|
@@ -274,6 +280,9 @@ const localDenoHandler = async ({
|
|
|
274
280
|
printOutput: true,
|
|
275
281
|
spawnOptions: {
|
|
276
282
|
cwd: path.dirname(workerPath),
|
|
283
|
+
// Merge any upstream client envs into the Deno subprocess environment,
|
|
284
|
+
// with the upstream env vars taking precedence.
|
|
285
|
+
env: { ...process.env, ...reqContext.upstreamClientEnvs },
|
|
277
286
|
},
|
|
278
287
|
});
|
|
279
288
|
|
|
@@ -283,14 +292,16 @@ const localDenoHandler = async ({
|
|
|
283
292
|
reject(new Error(`Worker exited with code ${exitCode}`));
|
|
284
293
|
});
|
|
285
294
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
295
|
+
// Strip null/undefined values so that the worker SDK client can fall back to
|
|
296
|
+
// reading from environment variables (including any upstreamClientEnvs).
|
|
297
|
+
const opts = {
|
|
298
|
+
...(client.baseURL != null ? { baseURL: client.baseURL } : undefined),
|
|
299
|
+
...(client.bearerToken != null ? { bearerToken: client.bearerToken } : undefined),
|
|
300
|
+
...(client.webhookSecret != null ? { webhookSecret: client.webhookSecret } : undefined),
|
|
290
301
|
defaultHeaders: {
|
|
291
302
|
'X-Stainless-MCP': 'true',
|
|
292
303
|
},
|
|
293
|
-
};
|
|
304
|
+
} satisfies Partial<ClientOptions> as ClientOptions;
|
|
294
305
|
|
|
295
306
|
const req = worker.request(
|
|
296
307
|
'http://localhost',
|
package/src/docs-search-tool.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
4
4
|
import { Metadata, McpRequestContext, asTextContentResult } from './types';
|
|
5
5
|
import { getLogger } from './logger';
|
|
6
|
+
import type { LocalDocsSearch } from './local-docs-search';
|
|
6
7
|
|
|
7
8
|
export const metadata: Metadata = {
|
|
8
9
|
resource: 'all',
|
|
@@ -43,13 +44,30 @@ export const tool: Tool = {
|
|
|
43
44
|
const docsSearchURL =
|
|
44
45
|
process.env['DOCS_SEARCH_URL'] || 'https://api.stainless.com/api/projects/metronome/docs/search';
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
let _localSearch: LocalDocsSearch | undefined;
|
|
48
|
+
|
|
49
|
+
export function setLocalSearch(search: LocalDocsSearch): void {
|
|
50
|
+
_localSearch = search;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function searchLocal(args: Record<string, unknown>): Promise<unknown> {
|
|
54
|
+
if (!_localSearch) {
|
|
55
|
+
throw new Error('Local search not initialized');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const query = (args['query'] as string) ?? '';
|
|
59
|
+
const language = (args['language'] as string) ?? 'typescript';
|
|
60
|
+
const detail = (args['detail'] as string) ?? 'default';
|
|
61
|
+
|
|
62
|
+
return _localSearch.search({
|
|
63
|
+
query,
|
|
64
|
+
language,
|
|
65
|
+
detail,
|
|
66
|
+
maxResults: 10,
|
|
67
|
+
}).results;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function searchRemote(args: Record<string, unknown>, reqContext: McpRequestContext): Promise<unknown> {
|
|
53
71
|
const body = args as any;
|
|
54
72
|
const query = new URLSearchParams(body).toString();
|
|
55
73
|
|
|
@@ -57,6 +75,10 @@ export const handler = async ({
|
|
|
57
75
|
const result = await fetch(`${docsSearchURL}?${query}`, {
|
|
58
76
|
headers: {
|
|
59
77
|
...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }),
|
|
78
|
+
...(reqContext.mcpSessionId && { 'x-stainless-mcp-session-id': reqContext.mcpSessionId }),
|
|
79
|
+
...(reqContext.mcpClientInfo && {
|
|
80
|
+
'x-stainless-mcp-client-info': JSON.stringify(reqContext.mcpClientInfo),
|
|
81
|
+
}),
|
|
60
82
|
},
|
|
61
83
|
});
|
|
62
84
|
|
|
@@ -94,7 +116,23 @@ export const handler = async ({
|
|
|
94
116
|
},
|
|
95
117
|
'Got docs search result',
|
|
96
118
|
);
|
|
97
|
-
return
|
|
119
|
+
return resultBody;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const handler = async ({
|
|
123
|
+
reqContext,
|
|
124
|
+
args,
|
|
125
|
+
}: {
|
|
126
|
+
reqContext: McpRequestContext;
|
|
127
|
+
args: Record<string, unknown> | undefined;
|
|
128
|
+
}) => {
|
|
129
|
+
const body = args ?? {};
|
|
130
|
+
|
|
131
|
+
if (_localSearch) {
|
|
132
|
+
return asTextContentResult(await searchLocal(body));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return asTextContentResult(await searchRemote(body, reqContext));
|
|
98
136
|
};
|
|
99
137
|
|
|
100
138
|
export default { metadata, tool, handler };
|
package/src/http.ts
CHANGED
|
@@ -23,20 +23,74 @@ const newServer = async ({
|
|
|
23
23
|
res: express.Response;
|
|
24
24
|
}): Promise<McpServer | null> => {
|
|
25
25
|
const stainlessApiKey = getStainlessApiKey(req, mcpOptions);
|
|
26
|
-
const
|
|
26
|
+
const customInstructionsPath = mcpOptions.customInstructionsPath;
|
|
27
|
+
const server = await newMcpServer({ stainlessApiKey, customInstructionsPath });
|
|
27
28
|
|
|
28
29
|
const authOptions = parseClientAuthHeaders(req, false);
|
|
29
30
|
|
|
31
|
+
let upstreamClientEnvs: Record<string, string> | undefined;
|
|
32
|
+
const clientEnvsHeader = req.headers['x-stainless-mcp-client-envs'];
|
|
33
|
+
if (typeof clientEnvsHeader === 'string') {
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(clientEnvsHeader);
|
|
36
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
37
|
+
upstreamClientEnvs = parsed;
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
// Ignore malformed header
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Parse x-stainless-mcp-client-permissions header to override permission options
|
|
45
|
+
//
|
|
46
|
+
// Note: Permissions are best-effort and intended to prevent clients from doing unexpected things;
|
|
47
|
+
// they're not a hard security boundary, so we allow arbitrary, client-driven overrides.
|
|
48
|
+
//
|
|
49
|
+
// See the Stainless MCP documentation for more details.
|
|
50
|
+
let effectiveMcpOptions = mcpOptions;
|
|
51
|
+
const clientPermissionsHeader = req.headers['x-stainless-mcp-client-permissions'];
|
|
52
|
+
if (typeof clientPermissionsHeader === 'string') {
|
|
53
|
+
try {
|
|
54
|
+
const parsed = JSON.parse(clientPermissionsHeader);
|
|
55
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
56
|
+
effectiveMcpOptions = {
|
|
57
|
+
...mcpOptions,
|
|
58
|
+
...(typeof parsed.allow_http_gets === 'boolean' && { codeAllowHttpGets: parsed.allow_http_gets }),
|
|
59
|
+
...(Array.isArray(parsed.allowed_methods) && { codeAllowedMethods: parsed.allowed_methods }),
|
|
60
|
+
...(Array.isArray(parsed.blocked_methods) && { codeBlockedMethods: parsed.blocked_methods }),
|
|
61
|
+
};
|
|
62
|
+
getLogger().info(
|
|
63
|
+
{ clientPermissions: parsed },
|
|
64
|
+
'Overriding code execution permissions from x-stainless-mcp-client-permissions header',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
getLogger().warn({ error }, 'Failed to parse x-stainless-mcp-client-permissions header');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const mcpClientInfo =
|
|
73
|
+
typeof req.body?.params?.clientInfo?.name === 'string' ?
|
|
74
|
+
{ name: req.body.params.clientInfo.name, version: String(req.body.params.clientInfo.version ?? '') }
|
|
75
|
+
: undefined;
|
|
76
|
+
|
|
30
77
|
await initMcpServer({
|
|
31
78
|
server: server,
|
|
32
|
-
mcpOptions:
|
|
79
|
+
mcpOptions: effectiveMcpOptions,
|
|
33
80
|
clientOptions: {
|
|
34
81
|
...clientOptions,
|
|
35
82
|
...authOptions,
|
|
36
83
|
},
|
|
37
84
|
stainlessApiKey: stainlessApiKey,
|
|
85
|
+
upstreamClientEnvs,
|
|
86
|
+
mcpSessionId: (req as any).mcpSessionId,
|
|
87
|
+
mcpClientInfo,
|
|
38
88
|
});
|
|
39
89
|
|
|
90
|
+
if (mcpClientInfo) {
|
|
91
|
+
getLogger().info({ mcpSessionId: (req as any).mcpSessionId, mcpClientInfo }, 'MCP client connected');
|
|
92
|
+
}
|
|
93
|
+
|
|
40
94
|
return server;
|
|
41
95
|
};
|
|
42
96
|
|
|
@@ -72,7 +126,7 @@ const del = async (req: express.Request, res: express.Response) => {
|
|
|
72
126
|
};
|
|
73
127
|
|
|
74
128
|
const redactHeaders = (headers: Record<string, any>) => {
|
|
75
|
-
const hiddenHeaders = /auth|cookie|key|token/i;
|
|
129
|
+
const hiddenHeaders = /auth|cookie|key|token|x-stainless-mcp-client-envs/i;
|
|
76
130
|
const filtered = { ...headers };
|
|
77
131
|
Object.keys(filtered).forEach((key) => {
|
|
78
132
|
if (hiddenHeaders.test(key)) {
|
|
@@ -92,9 +146,23 @@ export const streamableHTTPApp = ({
|
|
|
92
146
|
const app = express();
|
|
93
147
|
app.set('query parser', 'extended');
|
|
94
148
|
app.use(express.json());
|
|
149
|
+
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
150
|
+
const existing = req.headers['mcp-session-id'];
|
|
151
|
+
const sessionId = (Array.isArray(existing) ? existing[0] : existing) || crypto.randomUUID();
|
|
152
|
+
(req as any).mcpSessionId = sessionId;
|
|
153
|
+
const origWriteHead = res.writeHead.bind(res);
|
|
154
|
+
res.writeHead = function (statusCode: number, ...rest: any[]) {
|
|
155
|
+
res.setHeader('mcp-session-id', sessionId);
|
|
156
|
+
return origWriteHead(statusCode, ...rest);
|
|
157
|
+
} as typeof res.writeHead;
|
|
158
|
+
next();
|
|
159
|
+
});
|
|
95
160
|
app.use(
|
|
96
161
|
pinoHttp({
|
|
97
162
|
logger: getLogger(),
|
|
163
|
+
customProps: (req) => ({
|
|
164
|
+
mcpSessionId: (req as any).mcpSessionId,
|
|
165
|
+
}),
|
|
98
166
|
customLogLevel: (req, res) => {
|
|
99
167
|
if (res.statusCode >= 500) {
|
|
100
168
|
return 'error';
|
package/src/instructions.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
4
|
import { getLogger } from './logger';
|
|
5
|
+
import { readEnv } from './util';
|
|
5
6
|
|
|
6
7
|
const INSTRUCTIONS_CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes
|
|
7
8
|
|
|
@@ -12,33 +13,50 @@ interface InstructionsCacheEntry {
|
|
|
12
13
|
|
|
13
14
|
const instructionsCache = new Map<string, InstructionsCacheEntry>();
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
export async function getInstructions({
|
|
17
|
+
stainlessApiKey,
|
|
18
|
+
customInstructionsPath,
|
|
19
|
+
}: {
|
|
20
|
+
stainlessApiKey?: string | undefined;
|
|
21
|
+
customInstructionsPath?: string | undefined;
|
|
22
|
+
}): Promise<string> {
|
|
17
23
|
const now = Date.now();
|
|
24
|
+
const cacheKey = customInstructionsPath ?? stainlessApiKey ?? '';
|
|
25
|
+
const cached = instructionsCache.get(cacheKey);
|
|
26
|
+
|
|
27
|
+
if (cached && now - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) {
|
|
28
|
+
return cached.fetchedInstructions;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Evict stale entries so the cache doesn't grow unboundedly.
|
|
18
32
|
for (const [key, entry] of instructionsCache) {
|
|
19
33
|
if (now - entry.fetchedAt > INSTRUCTIONS_CACHE_TTL_MS) {
|
|
20
34
|
instructionsCache.delete(key);
|
|
21
35
|
}
|
|
22
36
|
}
|
|
23
|
-
}, INSTRUCTIONS_CACHE_TTL_MS);
|
|
24
37
|
|
|
25
|
-
|
|
26
|
-
_cacheCleanupInterval.unref();
|
|
38
|
+
let fetchedInstructions: string;
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (cached && Date.now() - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) {
|
|
33
|
-
return cached.fetchedInstructions;
|
|
40
|
+
if (customInstructionsPath) {
|
|
41
|
+
fetchedInstructions = await fetchLatestInstructionsFromFile(customInstructionsPath);
|
|
42
|
+
} else {
|
|
43
|
+
fetchedInstructions = await fetchLatestInstructionsFromApi(stainlessApiKey);
|
|
34
44
|
}
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: Date.now() });
|
|
46
|
+
instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: now });
|
|
38
47
|
return fetchedInstructions;
|
|
39
48
|
}
|
|
40
49
|
|
|
41
|
-
async function
|
|
50
|
+
async function fetchLatestInstructionsFromFile(path: string): Promise<string> {
|
|
51
|
+
try {
|
|
52
|
+
return await fs.readFile(path, 'utf-8');
|
|
53
|
+
} catch (error) {
|
|
54
|
+
getLogger().error({ error, path }, 'Error fetching instructions from file');
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function fetchLatestInstructionsFromApi(stainlessApiKey: string | undefined): Promise<string> {
|
|
42
60
|
// Setting the stainless API key is optional, but may be required
|
|
43
61
|
// to authenticate requests to the Stainless API.
|
|
44
62
|
const response = await fetch(
|