@cjavdev/believe-mcp 0.20.0 → 0.20.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/code-tool.js +4 -4
- package/code-tool.js.map +1 -1
- package/code-tool.mjs +4 -4
- 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 +17 -1
- package/http.js.map +1 -1
- package/http.mjs +17 -1
- 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 +23 -4
- package/instructions.js.map +1 -1
- package/instructions.mjs +20 -4
- 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 +3932 -0
- package/local-docs-search.js.map +1 -0
- package/local-docs-search.mjs +3892 -0
- package/local-docs-search.mjs.map +1 -0
- 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 +9 -1
- package/server.d.mts.map +1 -1
- package/server.d.ts +9 -1
- package/server.d.ts.map +1 -1
- package/server.js +12 -3
- package/server.js.map +1 -1
- package/server.mjs +12 -3
- package/server.mjs.map +1 -1
- package/src/code-tool.ts +7 -9
- package/src/docs-search-tool.ts +46 -8
- package/src/http.ts +18 -1
- package/src/instructions.ts +27 -4
- package/src/local-docs-search.ts +4636 -0
- package/src/options.ts +24 -0
- package/src/server.ts +21 -3
- package/src/stdio.ts +4 -1
- package/src/types.ts +2 -0
- 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 +5 -0
- package/types.d.mts.map +1 -1
- package/types.d.ts +5 -0
- package/types.d.ts.map +1 -1
- package/types.js.map +1 -1
- package/types.mjs.map +1 -1
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/believe/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: 5,
|
|
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,7 +23,8 @@ 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
|
|
|
@@ -77,6 +78,11 @@ const newServer = async ({
|
|
|
77
78
|
},
|
|
78
79
|
stainlessApiKey: stainlessApiKey,
|
|
79
80
|
upstreamClientEnvs,
|
|
81
|
+
mcpSessionId: (req as any).mcpSessionId,
|
|
82
|
+
mcpClientInfo:
|
|
83
|
+
typeof req.body?.params?.clientInfo?.name === 'string' ?
|
|
84
|
+
{ name: req.body.params.clientInfo.name, version: String(req.body.params.clientInfo.version ?? '') }
|
|
85
|
+
: undefined,
|
|
80
86
|
});
|
|
81
87
|
|
|
82
88
|
return server;
|
|
@@ -134,6 +140,17 @@ export const streamableHTTPApp = ({
|
|
|
134
140
|
const app = express();
|
|
135
141
|
app.set('query parser', 'extended');
|
|
136
142
|
app.use(express.json());
|
|
143
|
+
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
144
|
+
const existing = req.headers['mcp-session-id'];
|
|
145
|
+
const sessionId = (Array.isArray(existing) ? existing[0] : existing) || crypto.randomUUID();
|
|
146
|
+
(req as any).mcpSessionId = sessionId;
|
|
147
|
+
const origWriteHead = res.writeHead.bind(res);
|
|
148
|
+
res.writeHead = function (statusCode: number, ...rest: any[]) {
|
|
149
|
+
res.setHeader('mcp-session-id', sessionId);
|
|
150
|
+
return origWriteHead(statusCode, ...rest);
|
|
151
|
+
} as typeof res.writeHead;
|
|
152
|
+
next();
|
|
153
|
+
});
|
|
137
154
|
app.use(
|
|
138
155
|
pinoHttp({
|
|
139
156
|
logger: getLogger(),
|
package/src/instructions.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2
2
|
|
|
3
|
+
import fs from 'fs/promises';
|
|
3
4
|
import { readEnv } from './util';
|
|
4
5
|
import { getLogger } from './logger';
|
|
5
6
|
|
|
@@ -12,9 +13,15 @@ interface InstructionsCacheEntry {
|
|
|
12
13
|
|
|
13
14
|
const instructionsCache = new Map<string, InstructionsCacheEntry>();
|
|
14
15
|
|
|
15
|
-
export async function getInstructions(
|
|
16
|
+
export async function getInstructions({
|
|
17
|
+
stainlessApiKey,
|
|
18
|
+
customInstructionsPath,
|
|
19
|
+
}: {
|
|
20
|
+
stainlessApiKey?: string | undefined;
|
|
21
|
+
customInstructionsPath?: string | undefined;
|
|
22
|
+
}): Promise<string> {
|
|
16
23
|
const now = Date.now();
|
|
17
|
-
const cacheKey = stainlessApiKey ?? '';
|
|
24
|
+
const cacheKey = customInstructionsPath ?? stainlessApiKey ?? '';
|
|
18
25
|
const cached = instructionsCache.get(cacheKey);
|
|
19
26
|
|
|
20
27
|
if (cached && now - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) {
|
|
@@ -28,12 +35,28 @@ export async function getInstructions(stainlessApiKey: string | undefined): Prom
|
|
|
28
35
|
}
|
|
29
36
|
}
|
|
30
37
|
|
|
31
|
-
|
|
38
|
+
let fetchedInstructions: string;
|
|
39
|
+
|
|
40
|
+
if (customInstructionsPath) {
|
|
41
|
+
fetchedInstructions = await fetchLatestInstructionsFromFile(customInstructionsPath);
|
|
42
|
+
} else {
|
|
43
|
+
fetchedInstructions = await fetchLatestInstructionsFromApi(stainlessApiKey);
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: now });
|
|
33
47
|
return fetchedInstructions;
|
|
34
48
|
}
|
|
35
49
|
|
|
36
|
-
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> {
|
|
37
60
|
// Setting the stainless API key is optional, but may be required
|
|
38
61
|
// to authenticate requests to the Stainless API.
|
|
39
62
|
const response = await fetch(
|