context.dev-mcp 1.0.1 → 1.2.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.
Files changed (74) hide show
  1. package/code-tool-worker.d.mts.map +1 -1
  2. package/code-tool-worker.d.ts.map +1 -1
  3. package/code-tool-worker.js +43 -2
  4. package/code-tool-worker.js.map +1 -1
  5. package/code-tool-worker.mjs +10 -2
  6. package/code-tool-worker.mjs.map +1 -1
  7. package/docs-search-tool.d.mts +2 -0
  8. package/docs-search-tool.d.mts.map +1 -1
  9. package/docs-search-tool.d.ts +2 -0
  10. package/docs-search-tool.d.ts.map +1 -1
  11. package/docs-search-tool.js +32 -2
  12. package/docs-search-tool.js.map +1 -1
  13. package/docs-search-tool.mjs +31 -2
  14. package/docs-search-tool.mjs.map +1 -1
  15. package/http.d.mts.map +1 -1
  16. package/http.d.ts.map +1 -1
  17. package/http.js +24 -1
  18. package/http.js.map +1 -1
  19. package/http.mjs +24 -1
  20. package/http.mjs.map +1 -1
  21. package/instructions.d.mts +4 -1
  22. package/instructions.d.mts.map +1 -1
  23. package/instructions.d.ts +4 -1
  24. package/instructions.d.ts.map +1 -1
  25. package/instructions.js +23 -4
  26. package/instructions.js.map +1 -1
  27. package/instructions.mjs +20 -4
  28. package/instructions.mjs.map +1 -1
  29. package/local-docs-search.d.mts +28 -0
  30. package/local-docs-search.d.mts.map +1 -0
  31. package/local-docs-search.d.ts +28 -0
  32. package/local-docs-search.d.ts.map +1 -0
  33. package/local-docs-search.js +940 -0
  34. package/local-docs-search.js.map +1 -0
  35. package/local-docs-search.mjs +900 -0
  36. package/local-docs-search.mjs.map +1 -0
  37. package/options.d.mts +3 -0
  38. package/options.d.mts.map +1 -1
  39. package/options.d.ts +3 -0
  40. package/options.d.ts.map +1 -1
  41. package/options.js +19 -0
  42. package/options.js.map +1 -1
  43. package/options.mjs +19 -0
  44. package/options.mjs.map +1 -1
  45. package/package.json +13 -2
  46. package/server.d.mts +9 -1
  47. package/server.d.mts.map +1 -1
  48. package/server.d.ts +9 -1
  49. package/server.d.ts.map +1 -1
  50. package/server.js +12 -3
  51. package/server.js.map +1 -1
  52. package/server.mjs +12 -3
  53. package/server.mjs.map +1 -1
  54. package/src/code-tool-worker.ts +10 -2
  55. package/src/docs-search-tool.ts +46 -8
  56. package/src/http.ts +27 -1
  57. package/src/instructions.ts +27 -4
  58. package/src/local-docs-search.ts +1096 -0
  59. package/src/options.ts +24 -0
  60. package/src/server.ts +21 -3
  61. package/src/stdio.ts +4 -1
  62. package/src/types.ts +2 -0
  63. package/stdio.d.mts.map +1 -1
  64. package/stdio.d.ts.map +1 -1
  65. package/stdio.js +4 -1
  66. package/stdio.js.map +1 -1
  67. package/stdio.mjs +4 -1
  68. package/stdio.mjs.map +1 -1
  69. package/types.d.mts +5 -0
  70. package/types.d.mts.map +1 -1
  71. package/types.d.ts +5 -0
  72. package/types.d.ts.map +1 -1
  73. package/types.js.map +1 -1
  74. package/types.mjs.map +1 -1
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 server = await newMcpServer(stainlessApiKey);
26
+ const customInstructionsPath = mcpOptions.customInstructionsPath;
27
+ const server = await newMcpServer({ stainlessApiKey, customInstructionsPath });
27
28
 
28
29
  const authOptions = parseClientAuthHeaders(req, false);
29
30
 
@@ -68,6 +69,11 @@ const newServer = async ({
68
69
  }
69
70
  }
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
+
71
77
  await initMcpServer({
72
78
  server: server,
73
79
  mcpOptions: effectiveMcpOptions,
@@ -77,8 +83,14 @@ const newServer = async ({
77
83
  },
78
84
  stainlessApiKey: stainlessApiKey,
79
85
  upstreamClientEnvs,
86
+ mcpSessionId: (req as any).mcpSessionId,
87
+ mcpClientInfo,
80
88
  });
81
89
 
90
+ if (mcpClientInfo) {
91
+ getLogger().info({ mcpSessionId: (req as any).mcpSessionId, mcpClientInfo }, 'MCP client connected');
92
+ }
93
+
82
94
  return server;
83
95
  };
84
96
 
@@ -134,9 +146,23 @@ export const streamableHTTPApp = ({
134
146
  const app = express();
135
147
  app.set('query parser', 'extended');
136
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
+ });
137
160
  app.use(
138
161
  pinoHttp({
139
162
  logger: getLogger(),
163
+ customProps: (req) => ({
164
+ mcpSessionId: (req as any).mcpSessionId,
165
+ }),
140
166
  customLogLevel: (req, res) => {
141
167
  if (res.statusCode >= 500) {
142
168
  return 'error';
@@ -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(stainlessApiKey: string | undefined): Promise<string> {
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
- const fetchedInstructions = await fetchLatestInstructions(stainlessApiKey);
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 fetchLatestInstructions(stainlessApiKey: string | undefined): Promise<string> {
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(