Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.10.1"
".": "1.10.2"
}
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
# Changelog

## 1.10.2 (2026-03-14)

Full Changelog: [v1.10.1...v1.10.2](https://github.com/CASParser/cas-parser-node/compare/v1.10.1...v1.10.2)

### Chores

* **internal:** codegen related update ([42785e5](https://github.com/CASParser/cas-parser-node/commit/42785e5ad046d551e6c851d7521203a176354433))
* **internal:** codegen related update ([4515206](https://github.com/CASParser/cas-parser-node/commit/4515206424c06f436dc61bea1994c56cb687c394))
* **internal:** codegen related update ([f91eb0e](https://github.com/CASParser/cas-parser-node/commit/f91eb0e9800b5d0f970f31e97d06355aa6d3df77))
* **internal:** codegen related update ([c38d136](https://github.com/CASParser/cas-parser-node/commit/c38d136006bcbf2f05e1642b1098c6d785c32af2))
* **internal:** codegen related update ([44b93cc](https://github.com/CASParser/cas-parser-node/commit/44b93cccf487ebf941d1c947e4d3320338e22f77))
* **internal:** codegen related update ([4005e2f](https://github.com/CASParser/cas-parser-node/commit/4005e2f3a16bba4dccfdfad92e3ff03696d67d10))
* **internal:** codegen related update ([a9f558a](https://github.com/CASParser/cas-parser-node/commit/a9f558abca126a4b575f9015dd6550706c60783a))
* **internal:** codegen related update ([da227d9](https://github.com/CASParser/cas-parser-node/commit/da227d988d02d7a168bc84b5d1e7ae5094390747))
* **internal:** codegen related update ([57415da](https://github.com/CASParser/cas-parser-node/commit/57415dabd939521c07a8fcf745624f790dcbdf3e))
* **internal:** codegen related update ([5040a3f](https://github.com/CASParser/cas-parser-node/commit/5040a3f99ad8354df704b5fd383f503f2d704e8a))
* **internal:** make generated MCP servers compatible with Cloudflare worker environments ([d44814b](https://github.com/CASParser/cas-parser-node/commit/d44814bb29c75f80ea39a7e51b8c0ec495672e0f))
* **internal:** support x-stainless-mcp-client-envs header in MCP servers ([a97ef55](https://github.com/CASParser/cas-parser-node/commit/a97ef55a49629a248550f498201d884891b41aec))
* **internal:** update dependencies to address dependabot vulnerabilities ([67cd264](https://github.com/CASParser/cas-parser-node/commit/67cd26484b9ab683bf8101210ac9ae285c86eab2))
* **internal:** update lock file ([e95138b](https://github.com/CASParser/cas-parser-node/commit/e95138b5d2cdf73a4474d11d52beb2ad8a966c89))
* **internal:** update lockfile ([9f834de](https://github.com/CASParser/cas-parser-node/commit/9f834de4889c6439090b17132ab987376603740c))


### Refactors

* update sdk ([b972fe0](https://github.com/CASParser/cas-parser-node/commit/b972fe0ffc1dc4b3ab81afb47c86a0e306f06120))

## 1.10.1 (2026-03-07)

Full Changelog: [v1.10.0...v1.10.1](https://github.com/CASParser/cas-parser-node/compare/v1.10.0...v1.10.1)
Expand Down
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cas-parser-node",
"version": "1.10.1",
"version": "1.10.2",
"description": "The official TypeScript library for the Cas Parser API",
"author": "Cas Parser <sameer@casparser.in>",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -50,6 +50,17 @@
"typescript": "5.8.3",
"typescript-eslint": "8.31.1"
},
"overrides": {
"minimatch": "^9.0.5"
},
"pnpm": {
"overrides": {
"minimatch": "^9.0.5"
}
},
"resolutions": {
"minimatch": "^9.0.5"
},
"exports": {
".": {
"import": "./dist/index.mjs",
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-server/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"dxt_version": "0.2",
"name": "cas-parser-node-mcp",
"version": "1.10.1",
"version": "1.10.2",
"description": "The official MCP Server for the Cas Parser API",
"author": {
"name": "Cas Parser",
Expand Down
17 changes: 10 additions & 7 deletions packages/mcp-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cas-parser-node-mcp",
"version": "1.10.1",
"version": "1.10.2",
"description": "The official MCP Server for the Cas Parser API",
"author": "Cas Parser <sameer@casparser.in>",
"types": "dist/index.d.ts",
Expand All @@ -25,13 +25,16 @@
"prepublishOnly": "echo 'to publish, run pnpm build && (cd dist; pnpm publish)' && exit 1",
"format": "prettier --write --cache --cache-strategy metadata . !dist",
"tsn": "ts-node -r tsconfig-paths/register",
"lint": "eslint --ext ts,js .",
"fix": "eslint --fix --ext ts,js ."
"lint": "eslint .",
"fix": "eslint --fix ."
},
"dependencies": {
"cas-parser-node": "workspace:*",
"ajv": "^8.18.0",
"@cloudflare/cabidela": "^0.2.4",
"@modelcontextprotocol/sdk": "^1.26.0",
"@hono/node-server": "^1.19.10",
"@modelcontextprotocol/sdk": "^1.27.1",
"hono": "^4.12.4",
"@valtown/deno-http-worker": "^0.0.21",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
Expand Down Expand Up @@ -61,9 +64,9 @@
"@types/yargs": "^17.0.8",
"@typescript-eslint/eslint-plugin": "8.31.1",
"@typescript-eslint/parser": "8.31.1",
"eslint": "^8.49.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-unused-imports": "^3.0.0",
"eslint": "^9.39.1",
"eslint-plugin-prettier": "^5.4.1",
"eslint-plugin-unused-imports": "^4.1.4",
"jest": "^29.4.0",
"prettier": "^3.0.0",
"ts-jest": "^29.1.0",
Expand Down
4 changes: 3 additions & 1 deletion packages/mcp-server/src/code-tool-paths.cts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

export const workerPath = require.resolve('./code-tool-worker.mjs');
export function getWorkerPath(): string {
return require.resolve('./code-tool-worker.mjs');
}
51 changes: 32 additions & 19 deletions packages/mcp-server/src/code-tool.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import fs from 'node:fs';
import path from 'node:path';
import url from 'node:url';
import { newDenoHTTPWorker } from '@valtown/deno-http-worker';
import { workerPath } from './code-tool-paths.cjs';
import {
ContentBlock,
McpRequestContext,
Expand Down Expand Up @@ -149,19 +144,23 @@ const remoteStainlessHandler = async ({

const codeModeEndpoint = readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool';

const localClientEnvs = {
CAS_PARSER_API_KEY: requireValue(
readEnv('CAS_PARSER_API_KEY') ?? client.apiKey,
'set CAS_PARSER_API_KEY environment variable or provide apiKey client option',
),
CAS_PARSER_BASE_URL: readEnv('CAS_PARSER_BASE_URL') ?? client.baseURL ?? undefined,
};
// Merge any upstream client envs from the request header, with upstream values taking precedence.
const mergedClientEnvs = { ...localClientEnvs, ...reqContext.upstreamClientEnvs };

// Setting a Stainless API key authenticates requests to the code tool endpoint.
const res = await fetch(codeModeEndpoint, {
method: 'POST',
headers: {
...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }),
'Content-Type': 'application/json',
'x-stainless-mcp-client-envs': JSON.stringify({
CAS_PARSER_API_KEY: requireValue(
readEnv('CAS_PARSER_API_KEY') ?? client.apiKey,
'set CAS_PARSER_API_KEY environment variable or provide apiKey client option',
),
CAS_PARSER_BASE_URL: readEnv('CAS_PARSER_BASE_URL') ?? client.baseURL ?? undefined,
}),
'x-stainless-mcp-client-envs': JSON.stringify(mergedClientEnvs),
},
body: JSON.stringify({
project_name: 'cas-parser',
Expand Down Expand Up @@ -204,6 +203,13 @@ const localDenoHandler = async ({
reqContext: McpRequestContext;
args: unknown;
}): Promise<ToolCallResult> => {
const fs = await import('node:fs');
const path = await import('node:path');
const url = await import('node:url');
const { newDenoHTTPWorker } = await import('@valtown/deno-http-worker');
const { getWorkerPath } = await import('./code-tool-paths.cjs');
const workerPath = getWorkerPath();

const client = reqContext.client;
const baseURLHostname = new URL(client.baseURL).hostname;
const { code } = args as { code: string };
Expand Down Expand Up @@ -265,6 +271,9 @@ const localDenoHandler = async ({
printOutput: true,
spawnOptions: {
cwd: path.dirname(workerPath),
// Merge any upstream client envs into the Deno subprocess environment,
// with the upstream env vars taking precedence.
env: { ...process.env, ...reqContext.upstreamClientEnvs },
},
});

Expand All @@ -274,13 +283,17 @@ const localDenoHandler = async ({
reject(new Error(`Worker exited with code ${exitCode}`));
});

const opts: ClientOptions = {
baseURL: client.baseURL,
apiKey: client.apiKey,
defaultHeaders: {
'X-Stainless-MCP': 'true',
},
};
// Strip null/undefined values so that the worker SDK client can fall back to
// reading from environment variables (including any upstreamClientEnvs).
const opts: ClientOptions = Object.fromEntries(
Object.entries({
baseURL: client.baseURL,
apiKey: client.apiKey,
defaultHeaders: {
'X-Stainless-MCP': 'true',
},
}).filter(([_, v]) => v != null),
) as ClientOptions;

const req = worker.request(
'http://localhost',
Expand Down
16 changes: 15 additions & 1 deletion packages/mcp-server/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ const newServer = async ({

const authOptions = parseClientAuthHeaders(req, false);

let upstreamClientEnvs: Record<string, string> | undefined;
const clientEnvsHeader = req.headers['x-stainless-mcp-client-envs'];
if (typeof clientEnvsHeader === 'string') {
try {
const parsed = JSON.parse(clientEnvsHeader);
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
upstreamClientEnvs = parsed;
}
} catch {
// Ignore malformed header
}
}

await initMcpServer({
server: server,
mcpOptions: mcpOptions,
Expand All @@ -35,6 +48,7 @@ const newServer = async ({
...authOptions,
},
stainlessApiKey: stainlessApiKey,
upstreamClientEnvs,
});

return server;
Expand Down Expand Up @@ -72,7 +86,7 @@ const del = async (req: express.Request, res: express.Response) => {
};

const redactHeaders = (headers: Record<string, any>) => {
const hiddenHeaders = /auth|cookie|key|token/i;
const hiddenHeaders = /auth|cookie|key|token|x-stainless-mcp-client-envs/i;
const filtered = { ...headers };
Object.keys(filtered).forEach((key) => {
if (hiddenHeaders.test(key)) {
Expand Down
25 changes: 10 additions & 15 deletions packages/mcp-server/src/instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,24 @@ interface InstructionsCacheEntry {

const instructionsCache = new Map<string, InstructionsCacheEntry>();

// Periodically evict stale entries so the cache doesn't grow unboundedly.
const _cacheCleanupInterval = setInterval(() => {
const now = Date.now();
for (const [key, entry] of instructionsCache) {
if (now - entry.fetchedAt > INSTRUCTIONS_CACHE_TTL_MS) {
instructionsCache.delete(key);
}
}
}, INSTRUCTIONS_CACHE_TTL_MS);

// Don't keep the process alive just for cleanup.
_cacheCleanupInterval.unref();

export async function getInstructions(stainlessApiKey: string | undefined): Promise<string> {
const now = Date.now();
const cacheKey = stainlessApiKey ?? '';
const cached = instructionsCache.get(cacheKey);

if (cached && Date.now() - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) {
if (cached && now - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) {
return cached.fetchedInstructions;
}

// Evict stale entries so the cache doesn't grow unboundedly.
for (const [key, entry] of instructionsCache) {
if (now - entry.fetchedAt > INSTRUCTIONS_CACHE_TTL_MS) {
instructionsCache.delete(key);
}
}

const fetchedInstructions = await fetchLatestInstructions(stainlessApiKey);
instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: Date.now() });
instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: now });
return fetchedInstructions;
}

Expand Down
4 changes: 3 additions & 1 deletion packages/mcp-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const newMcpServer = async (stainlessApiKey: string | undefined) =>
new McpServer(
{
name: 'cas_parser_node_api',
version: '1.10.1',
version: '1.10.2',
},
{
instructions: await getInstructions(stainlessApiKey),
Expand All @@ -37,6 +37,7 @@ export async function initMcpServer(params: {
clientOptions?: ClientOptions;
mcpOptions?: McpOptions;
stainlessApiKey?: string | undefined;
upstreamClientEnvs?: Record<string, string> | undefined;
}) {
const server = params.server instanceof McpServer ? params.server.server : params.server;

Expand Down Expand Up @@ -118,6 +119,7 @@ export async function initMcpServer(params: {
reqContext: {
client,
stainlessApiKey: params.stainlessApiKey ?? params.mcpOptions?.stainlessApiKey,
upstreamClientEnvs: params.upstreamClientEnvs,
},
args,
});
Expand Down
1 change: 1 addition & 0 deletions packages/mcp-server/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type ToolCallResult = {
export type McpRequestContext = {
client: CasParser;
stainlessApiKey?: string | undefined;
upstreamClientEnvs?: Record<string, string> | undefined;
};

export type HandlerFunction = ({
Expand Down
Loading
Loading