Skip to content

Commit b190601

Browse files
chore(internal): cache fetch instruction calls in MCP server
1 parent ae7615e commit b190601

2 files changed

Lines changed: 75 additions & 37 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
import { readEnv } from './util';
4+
5+
const INSTRUCTIONS_CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes
6+
7+
interface InstructionsCacheEntry {
8+
fetchedInstructions: string;
9+
fetchedAt: number;
10+
}
11+
12+
const instructionsCache = new Map<string, InstructionsCacheEntry>();
13+
14+
// Periodically evict stale entries so the cache doesn't grow unboundedly.
15+
const _cacheCleanupInterval = setInterval(() => {
16+
const now = Date.now();
17+
for (const [key, entry] of instructionsCache) {
18+
if (now - entry.fetchedAt > INSTRUCTIONS_CACHE_TTL_MS) {
19+
instructionsCache.delete(key);
20+
}
21+
}
22+
}, INSTRUCTIONS_CACHE_TTL_MS);
23+
24+
// Don't keep the process alive just for cleanup.
25+
_cacheCleanupInterval.unref();
26+
27+
export async function getInstructions(stainlessApiKey: string | undefined): Promise<string> {
28+
const cacheKey = stainlessApiKey ?? '';
29+
const cached = instructionsCache.get(cacheKey);
30+
31+
if (cached && Date.now() - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) {
32+
return cached.fetchedInstructions;
33+
}
34+
35+
const fetchedInstructions = await fetchLatestInstructions(stainlessApiKey);
36+
instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: Date.now() });
37+
return fetchedInstructions;
38+
}
39+
40+
async function fetchLatestInstructions(stainlessApiKey: string | undefined): Promise<string> {
41+
// Setting the stainless API key is optional, but may be required
42+
// to authenticate requests to the Stainless API.
43+
const response = await fetch(
44+
readEnv('CODE_MODE_INSTRUCTIONS_URL') ?? 'https://api.stainless.com/api/ai/instructions/scan-documents',
45+
{
46+
method: 'GET',
47+
headers: { ...(stainlessApiKey && { Authorization: stainlessApiKey }) },
48+
},
49+
);
50+
51+
let instructions: string | undefined;
52+
if (!response.ok) {
53+
console.warn(
54+
'Warning: failed to retrieve MCP server instructions. Proceeding with default instructions...',
55+
);
56+
57+
instructions = `
58+
This is the scan-documents MCP server. You will use Code Mode to help the user perform
59+
actions. You can use search_docs tool to learn about how to take action with this server. Then,
60+
you will write TypeScript code using the execute tool take action. It is CRITICAL that you be
61+
thoughtful and deliberate when executing code. Always try to entirely solve the problem in code
62+
block: it can be as long as you need to get the job done!
63+
`;
64+
}
65+
66+
instructions ??= ((await response.json()) as { instructions: string }).instructions;
67+
instructions = `
68+
If needed, you can get the current time by executing Date.now().
69+
70+
${instructions}
71+
`;
72+
73+
return instructions;
74+
}

packages/mcp-server/src/server.ts

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,46 +11,10 @@ import { ClientOptions } from 'scan-documents';
1111
import ScanDocuments from 'scan-documents';
1212
import { codeTool } from './code-tool';
1313
import docsSearchTool from './docs-search-tool';
14+
import { getInstructions } from './instructions';
1415
import { McpOptions } from './options';
1516
import { blockedMethodsForCodeTool } from './methods';
1617
import { HandlerFunction, McpRequestContext, ToolCallResult, McpTool } from './types';
17-
import { readEnv } from './util';
18-
19-
async function getInstructions(stainlessApiKey: string | undefined): Promise<string> {
20-
// Setting the stainless API key is optional, but may be required
21-
// to authenticate requests to the Stainless API.
22-
const response = await fetch(
23-
readEnv('CODE_MODE_INSTRUCTIONS_URL') ?? 'https://api.stainless.com/api/ai/instructions/scan-documents',
24-
{
25-
method: 'GET',
26-
headers: { ...(stainlessApiKey && { Authorization: stainlessApiKey }) },
27-
},
28-
);
29-
30-
let instructions: string | undefined;
31-
if (!response.ok) {
32-
console.warn(
33-
'Warning: failed to retrieve MCP server instructions. Proceeding with default instructions...',
34-
);
35-
36-
instructions = `
37-
This is the scan-documents MCP server. You will use Code Mode to help the user perform
38-
actions. You can use search_docs tool to learn about how to take action with this server. Then,
39-
you will write TypeScript code using the execute tool take action. It is CRITICAL that you be
40-
thoughtful and deliberate when executing code. Always try to entirely solve the problem in code
41-
block: it can be as long as you need to get the job done!
42-
`;
43-
}
44-
45-
instructions ??= ((await response.json()) as { instructions: string }).instructions;
46-
instructions = `
47-
The current time in Unix timestamps is ${Date.now()}.
48-
49-
${instructions}
50-
`;
51-
52-
return instructions;
53-
}
5418

5519
export const newMcpServer = async (stainlessApiKey: string | undefined) =>
5620
new McpServer(

0 commit comments

Comments
 (0)