Skip to content

Commit d01f144

Browse files
Merge pull request #15 from CASParser/release-please--branches--main--changes--next--components--cas-parser-node
release: 1.7.2
2 parents 0a075e0 + 929eacc commit d01f144

33 files changed

+311
-240
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "1.7.1"
2+
".": "1.7.2"
33
}

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## 1.7.2 (2026-02-20)
4+
5+
Full Changelog: [v1.7.1...v1.7.2](https://github.com/CASParser/cas-parser-node/compare/v1.7.1...v1.7.2)
6+
7+
### Bug Fixes
8+
9+
* **mcp:** initialize SDK lazily to avoid failing the connection on init errors ([2989ad0](https://github.com/CASParser/cas-parser-node/commit/2989ad0e371056cdf52be17d4b18b990533f1514))
10+
11+
12+
### Chores
13+
14+
* **internal/client:** fix form-urlencoded requests ([f39cb7f](https://github.com/CASParser/cas-parser-node/commit/f39cb7f6c2ad7c20da495ded91c644e432596425))
15+
* **internal:** allow setting x-stainless-api-key header on mcp server requests ([5b3baf7](https://github.com/CASParser/cas-parser-node/commit/5b3baf7ead67ae4a2e371ceda87d5d9e97019c5d))
16+
* **internal:** cache fetch instruction calls in MCP server ([335fe62](https://github.com/CASParser/cas-parser-node/commit/335fe623fd4ec8add20d330ab4e85db3165f8e81))
17+
* **internal:** remove mock server code ([c06dabf](https://github.com/CASParser/cas-parser-node/commit/c06dabff5d1c4bbc99e6691a54646299a1ba74c9))
18+
* **mcp:** correctly update version in sync with sdk ([0d611f4](https://github.com/CASParser/cas-parser-node/commit/0d611f42d2477e8cbb3e1e746a5f48bc209a3502))
19+
* update mock server docs ([89099af](https://github.com/CASParser/cas-parser-node/commit/89099af92c2f5dfff6649e325cfe1d1f08fc9d6c))
20+
321
## 1.7.1 (2026-02-14)
422

523
Full Changelog: [v1.7.0...v1.7.1](https://github.com/CASParser/cas-parser-node/compare/v1.7.0...v1.7.1)

CONTRIBUTING.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,6 @@ $ pnpm link -—global cas-parser-node
6565

6666
## Running tests
6767

68-
Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests.
69-
70-
```sh
71-
$ npx prism mock path/to/your/openapi.yml
72-
```
73-
7468
```sh
7569
$ pnpm run test
7670
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cas-parser-node",
3-
"version": "1.7.1",
3+
"version": "1.7.2",
44
"description": "The official TypeScript library for the Cas Parser API",
55
"author": "Cas Parser <sameer@casparser.in>",
66
"types": "dist/index.d.ts",

packages/mcp-server/manifest.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"dxt_version": "0.2",
33
"name": "cas-parser-node-mcp",
4-
"version": "1.5.1",
4+
"version": "1.7.2",
55
"description": "The official MCP Server for the Cas Parser API",
66
"author": {
77
"name": "Cas Parser",
@@ -18,7 +18,9 @@
1818
"entry_point": "index.js",
1919
"mcp_config": {
2020
"command": "node",
21-
"args": ["${__dirname}/index.js"],
21+
"args": [
22+
"${__dirname}/index.js"
23+
],
2224
"env": {
2325
"CAS_PARSER_API_KEY": "${user_config.CAS_PARSER_API_KEY}"
2426
}
@@ -39,5 +41,7 @@
3941
"node": ">=18.0.0"
4042
}
4143
},
42-
"keywords": ["api"]
44+
"keywords": [
45+
"api"
46+
]
4347
}

packages/mcp-server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cas-parser-node-mcp",
3-
"version": "1.7.1",
3+
"version": "1.7.2",
44
"description": "The official MCP Server for the Cas Parser API",
55
"author": "Cas Parser <sameer@casparser.in>",
66
"types": "dist/index.d.ts",

packages/mcp-server/src/auth.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,24 @@
22

33
import { IncomingMessage } from 'node:http';
44
import { ClientOptions } from 'cas-parser-node';
5+
import { McpOptions } from './options';
56

6-
export const parseAuthHeaders = (req: IncomingMessage, required?: boolean): Partial<ClientOptions> => {
7+
export const parseClientAuthHeaders = (req: IncomingMessage, required?: boolean): Partial<ClientOptions> => {
78
const apiKey =
89
Array.isArray(req.headers['x-api-key']) ? req.headers['x-api-key'][0] : req.headers['x-api-key'];
910
return { apiKey };
1011
};
12+
13+
export const getStainlessApiKey = (req: IncomingMessage, mcpOptions: McpOptions): string | undefined => {
14+
// Try to get the key from the x-stainless-api-key header
15+
const headerKey =
16+
Array.isArray(req.headers['x-stainless-api-key']) ?
17+
req.headers['x-stainless-api-key'][0]
18+
: req.headers['x-stainless-api-key'];
19+
if (headerKey && typeof headerKey === 'string') {
20+
return headerKey;
21+
}
22+
23+
// Fall back to value set in the mcpOptions (e.g. from environment variable), if provided
24+
return mcpOptions.stainlessApiKey;
25+
};

packages/mcp-server/src/code-tool.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

3-
import { McpTool, Metadata, ToolCallResult, asErrorResult, asTextContentResult } from './types';
3+
import {
4+
McpRequestContext,
5+
McpTool,
6+
Metadata,
7+
ToolCallResult,
8+
asErrorResult,
9+
asTextContentResult,
10+
} from './types';
411
import { Tool } from '@modelcontextprotocol/sdk/types.js';
512
import { readEnv, requireValue } from './util';
613
import { WorkerInput, WorkerOutput } from './code-tool-types';
714
import { SdkMethod } from './methods';
8-
import { CasParser } from 'cas-parser-node';
915

1016
const prompt = `Runs JavaScript code to interact with the Cas Parser API.
1117
@@ -36,7 +42,7 @@ Variables will not persist between calls, so make sure to return or log any data
3642
*
3743
* @param endpoints - The endpoints to include in the list.
3844
*/
39-
export function codeTool(params: { blockedMethods: SdkMethod[] | undefined }): McpTool {
45+
export function codeTool({ blockedMethods }: { blockedMethods: SdkMethod[] | undefined }): McpTool {
4046
const metadata: Metadata = { resource: 'all', operation: 'write', tags: [] };
4147
const tool: Tool = {
4248
name: 'execute',
@@ -56,19 +62,24 @@ export function codeTool(params: { blockedMethods: SdkMethod[] | undefined }): M
5662
required: ['code'],
5763
},
5864
};
59-
const handler = async (client: CasParser, args: any): Promise<ToolCallResult> => {
65+
const handler = async ({
66+
reqContext,
67+
args,
68+
}: {
69+
reqContext: McpRequestContext;
70+
args: any;
71+
}): Promise<ToolCallResult> => {
6072
const code = args.code as string;
6173
const intent = args.intent as string | undefined;
74+
const client = reqContext.client;
6275

6376
// Do very basic blocking of code that includes forbidden method names.
6477
//
6578
// WARNING: This is not secure against obfuscation and other evasion methods. If
6679
// stronger security blocks are required, then these should be enforced in the downstream
6780
// API (e.g., by having users call the MCP server with API keys with limited permissions).
68-
if (params.blockedMethods) {
69-
const blockedMatches = params.blockedMethods.filter((method) =>
70-
code.includes(method.fullyQualifiedName),
71-
);
81+
if (blockedMethods) {
82+
const blockedMatches = blockedMethods.filter((method) => code.includes(method.fullyQualifiedName));
7283
if (blockedMatches.length > 0) {
7384
return asErrorResult(
7485
`The following methods have been blocked by the MCP server and cannot be used in code execution: ${blockedMatches
@@ -78,16 +89,14 @@ export function codeTool(params: { blockedMethods: SdkMethod[] | undefined }): M
7889
}
7990
}
8091

81-
// this is not required, but passing a Stainless API key for the matching project_name
82-
// will allow you to run code-mode queries against non-published versions of your SDK.
83-
const stainlessAPIKey = readEnv('STAINLESS_API_KEY');
8492
const codeModeEndpoint =
8593
readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool';
8694

95+
// Setting a Stainless API key authenticates requests to the code tool endpoint.
8796
const res = await fetch(codeModeEndpoint, {
8897
method: 'POST',
8998
headers: {
90-
...(stainlessAPIKey && { Authorization: stainlessAPIKey }),
99+
...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }),
91100
'Content-Type': 'application/json',
92101
client_envs: JSON.stringify({
93102
CAS_PARSER_API_KEY: requireValue(

packages/mcp-server/src/docs-search-tool.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

3-
import { Metadata, asTextContentResult } from './types';
4-
import { readEnv } from './util';
5-
3+
import { Metadata, McpRequestContext, asTextContentResult } from './types';
64
import { Tool } from '@modelcontextprotocol/sdk/types.js';
75

86
export const metadata: Metadata = {
@@ -43,13 +41,18 @@ export const tool: Tool = {
4341
const docsSearchURL =
4442
process.env['DOCS_SEARCH_URL'] || 'https://api.stainless.com/api/projects/cas-parser/docs/search';
4543

46-
export const handler = async (_: unknown, args: Record<string, unknown> | undefined) => {
44+
export const handler = async ({
45+
reqContext,
46+
args,
47+
}: {
48+
reqContext: McpRequestContext;
49+
args: Record<string, unknown> | undefined;
50+
}) => {
4751
const body = args as any;
4852
const query = new URLSearchParams(body).toString();
49-
const stainlessAPIKey = readEnv('STAINLESS_API_KEY');
5053
const result = await fetch(`${docsSearchURL}?${query}`, {
5154
headers: {
52-
...(stainlessAPIKey && { Authorization: stainlessAPIKey }),
55+
...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }),
5356
},
5457
});
5558

packages/mcp-server/src/http.ts

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ClientOptions } from 'cas-parser-node';
66
import express from 'express';
77
import morgan from 'morgan';
88
import morganBody from 'morgan-body';
9-
import { parseAuthHeaders } from './auth';
9+
import { getStainlessApiKey, parseClientAuthHeaders } from './auth';
1010
import { McpOptions } from './options';
1111
import { initMcpServer, newMcpServer } from './server';
1212

@@ -21,28 +21,20 @@ const newServer = async ({
2121
req: express.Request;
2222
res: express.Response;
2323
}): Promise<McpServer | null> => {
24-
const server = await newMcpServer();
24+
const stainlessApiKey = getStainlessApiKey(req, mcpOptions);
25+
const server = await newMcpServer(stainlessApiKey);
2526

26-
try {
27-
const authOptions = parseAuthHeaders(req, false);
28-
await initMcpServer({
29-
server: server,
30-
mcpOptions: mcpOptions,
31-
clientOptions: {
32-
...clientOptions,
33-
...authOptions,
34-
},
35-
});
36-
} catch (error) {
37-
res.status(401).json({
38-
jsonrpc: '2.0',
39-
error: {
40-
code: -32000,
41-
message: `Unauthorized: ${error instanceof Error ? error.message : error}`,
42-
},
43-
});
44-
return null;
45-
}
27+
const authOptions = parseClientAuthHeaders(req, false);
28+
29+
await initMcpServer({
30+
server: server,
31+
mcpOptions: mcpOptions,
32+
clientOptions: {
33+
...clientOptions,
34+
...authOptions,
35+
},
36+
stainlessApiKey: stainlessApiKey,
37+
});
4638

4739
return server;
4840
};
@@ -112,20 +104,24 @@ export const streamableHTTPApp = ({
112104
return app;
113105
};
114106

115-
export const launchStreamableHTTPServer = async (params: {
107+
export const launchStreamableHTTPServer = async ({
108+
mcpOptions,
109+
debug,
110+
port,
111+
}: {
116112
mcpOptions: McpOptions;
117113
debug: boolean;
118114
port: number | string | undefined;
119115
}) => {
120-
const app = streamableHTTPApp({ mcpOptions: params.mcpOptions, debug: params.debug });
121-
const server = app.listen(params.port);
116+
const app = streamableHTTPApp({ mcpOptions, debug });
117+
const server = app.listen(port);
122118
const address = server.address();
123119

124120
if (typeof address === 'string') {
125121
console.error(`MCP Server running on streamable HTTP at ${address}`);
126122
} else if (address !== null) {
127123
console.error(`MCP Server running on streamable HTTP on port ${address.port}`);
128124
} else {
129-
console.error(`MCP Server running on streamable HTTP on port ${params.port}`);
125+
console.error(`MCP Server running on streamable HTTP on port ${port}`);
130126
}
131127
};

0 commit comments

Comments
 (0)