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 @@
{
".": "0.130.0"
".": "0.131.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 185
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-a45946df228eec554b3cd2491f658bd5a45cb91509da0a9f92d50468ea88072f.yml
openapi_spec_hash: 24c7c13e1e7385cab5442ca66091ffc6
config_hash: 50031f78031362c2e4900222b9ce7ada
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-ee8607f0a2cdcaee420935050334a439db8dd097be83023fccdaf1d6f9a7de14.yml
openapi_spec_hash: 0f21c68cdddb7c5bd99f42356d507393
config_hash: fb5070d41fcabdedbc084b83964b592a
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## 0.131.0 (2026-03-04)

Full Changelog: [v0.130.0...v0.131.0](https://github.com/lithic-com/lithic-node/compare/v0.130.0...v0.131.0)

### Features

* **api:** add action_counts to rule performance reports and code to authorization actions ([b721196](https://github.com/lithic-com/lithic-node/commit/b7211961ba92cc5cd68ad633592b5428060e8478))
* **api:** Add Hold API for financial account fund reservations ([582aeb7](https://github.com/lithic-com/lithic-node/commit/582aeb7164a9e9523becb601a08e7393904cd4e5))
* **api:** Remove deprecated beneficial owner entities field ([e0b8292](https://github.com/lithic-com/lithic-node/commit/e0b8292748ddb37a3758a909c5e7314c511985e4))


### Chores

* **internal:** codegen related update ([bccb03c](https://github.com/lithic-com/lithic-node/commit/bccb03cc6e6d046aa76dac2b77237233d1a614a7))

## 0.130.0 (2026-02-27)

Full Changelog: [v0.129.0...v0.130.0](https://github.com/lithic-com/lithic-node/compare/v0.129.0...v0.130.0)
Expand Down
3 changes: 2 additions & 1 deletion api.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Types:

- <code><a href="./src/resources/auth-rules/v2/v2.ts">AuthRule</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">AuthRuleCondition</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">BacktestStats</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">Conditional3DSActionParameters</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">ConditionalACHActionParameters</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">ConditionalAttribute</a></code>
Expand All @@ -94,7 +95,7 @@ Types:
- <code><a href="./src/resources/auth-rules/v2/v2.ts">ConditionalValue</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">EventStream</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">MerchantLockParameters</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">RuleStats</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">ReportStats</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">VelocityLimitParams</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">VelocityLimitPeriod</a></code>
- <code><a href="./src/resources/auth-rules/v2/v2.ts">V2ListResultsResponse</a></code>
Expand Down
2 changes: 1 addition & 1 deletion examples/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async function simulateAuthDeclined(card: Lithic.Cards.Card) {

const authResponse = await client.transactions.simulateAuthorization({
pan: card.pan!,
amount: 999999999999,
amount: 2000000000,
descriptor: 'coffee shop',
});

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lithic",
"version": "0.130.0",
"version": "0.131.0",
"description": "The official TypeScript library for the Lithic API",
"author": "Lithic <sdk-feedback@lithic.com>",
"types": "dist/index.d.ts",
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": "lithic-mcp",
"version": "0.130.0",
"version": "0.131.0",
"description": "The official MCP Server for the Lithic API",
"author": {
"name": "Lithic",
Expand Down
8 changes: 4 additions & 4 deletions packages/mcp-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lithic-mcp",
"version": "0.130.0",
"version": "0.131.0",
"description": "The official MCP Server for the Lithic API",
"author": "Lithic <sdk-feedback@lithic.com>",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -39,8 +39,9 @@
"express": "^5.1.0",
"fuse.js": "^7.1.0",
"jq-web": "https://github.com/stainless-api/jq-web/releases/download/v0.8.8/jq-web.tar.gz",
"morgan": "^1.10.0",
"morgan-body": "^2.6.9",
"pino": "^10.3.1",
"pino-http": "^11.0.0",
"pino-pretty": "^13.1.3",
"qs": "^6.14.1",
"typescript": "5.8.3",
"yargs": "^17.7.2",
Expand All @@ -57,7 +58,6 @@
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@types/jest": "^29.4.0",
"@types/morgan": "^1.9.10",
"@types/qs": "^6.14.0",
"@types/yargs": "^17.0.8",
"@typescript-eslint/eslint-plugin": "8.31.1",
Expand Down
28 changes: 26 additions & 2 deletions packages/mcp-server/src/code-tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { readEnv, requireValue } from './util';
import { WorkerInput, WorkerOutput } from './code-tool-types';
import { getLogger } from './logger';
import { SdkMethod } from './methods';
import { McpCodeExecutionMode } from './options';
import { ClientOptions } from 'lithic';
Expand Down Expand Up @@ -83,6 +84,8 @@ export function codeTool({
},
};

const logger = getLogger();

const handler = async ({
reqContext,
args,
Expand All @@ -107,11 +110,27 @@ export function codeTool({
}
}

let result: ToolCallResult;
const startTime = Date.now();

if (codeExecutionMode === 'local') {
return await localDenoHandler({ reqContext, args });
logger.debug('Executing code in local Deno environment');
result = await localDenoHandler({ reqContext, args });
} else {
return await remoteStainlessHandler({ reqContext, args });
logger.debug('Executing code in remote Stainless environment');
result = await remoteStainlessHandler({ reqContext, args });
}

logger.info(
{
codeExecutionMode,
durationMs: Date.now() - startTime,
isError: result.isError,
contentRows: result.content?.length ?? 0,
},
'Got code tool execution result',
);
return result;
};

return { metadata, tool, handler };
Expand Down Expand Up @@ -157,6 +176,11 @@ const remoteStainlessHandler = async ({
});

if (!res.ok) {
if (res.status === 404 && !reqContext.stainlessApiKey) {
throw new Error(
'Could not access code tool for this project. You may need to provide a Stainless API key via the STAINLESS_API_KEY environment variable, the --stainless-api-key flag, or the x-stainless-api-key HTTP header.',
);
}
throw new Error(
`${res.status}: ${
res.statusText
Expand Down
37 changes: 34 additions & 3 deletions packages/mcp-server/src/docs-search-tool.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { Metadata, McpRequestContext, asTextContentResult } from './types';
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { Metadata, McpRequestContext, asTextContentResult } from './types';
import { getLogger } from './logger';

export const metadata: Metadata = {
resource: 'all',
Expand Down Expand Up @@ -50,19 +51,49 @@ export const handler = async ({
}) => {
const body = args as any;
const query = new URLSearchParams(body).toString();

const startTime = Date.now();
const result = await fetch(`${docsSearchURL}?${query}`, {
headers: {
...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }),
},
});

const logger = getLogger();

if (!result.ok) {
const errorText = await result.text();
logger.warn(
{
durationMs: Date.now() - startTime,
query: body.query,
status: result.status,
statusText: result.statusText,
errorText,
},
'Got error response from docs search tool',
);

if (result.status === 404 && !reqContext.stainlessApiKey) {
throw new Error(
'Could not find docs for this project. You may need to provide a Stainless API key via the STAINLESS_API_KEY environment variable, the --stainless-api-key flag, or the x-stainless-api-key HTTP header.',
);
}

throw new Error(
`${result.status}: ${result.statusText} when using doc search tool. Details: ${await result.text()}`,
`${result.status}: ${result.statusText} when using doc search tool. Details: ${errorText}`,
);
}

return asTextContentResult(await result.json());
const resultBody = await result.json();
logger.info(
{
durationMs: Date.now() - startTime,
query: body.query,
},
'Got docs search result',
);
return asTextContentResult(resultBody);
};

export default { metadata, tool, handler };
74 changes: 53 additions & 21 deletions packages/mcp-server/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { ClientOptions } from 'lithic';
import express from 'express';
import morgan from 'morgan';
import morganBody from 'morgan-body';
import pino from 'pino';
import pinoHttp from 'pino-http';
import { getStainlessApiKey, parseClientAuthHeaders } from './auth';
import { getLogger } from './logger';
import { McpOptions } from './options';
import { initMcpServer, newMcpServer } from './server';

Expand Down Expand Up @@ -70,29 +71,60 @@ const del = async (req: express.Request, res: express.Response) => {
});
};

const redactHeaders = (headers: Record<string, any>) => {
const hiddenHeaders = /auth|cookie|key|token/i;
const filtered = { ...headers };
Object.keys(filtered).forEach((key) => {
if (hiddenHeaders.test(key)) {
filtered[key] = '[REDACTED]';
}
});
return filtered;
};

export const streamableHTTPApp = ({
clientOptions = {},
mcpOptions,
debug,
}: {
clientOptions?: ClientOptions;
mcpOptions: McpOptions;
debug: boolean;
}): express.Express => {
const app = express();
app.set('query parser', 'extended');
app.use(express.json());

if (debug) {
morganBody(app, {
logAllReqHeader: true,
logAllResHeader: true,
logRequestBody: true,
logResponseBody: true,
});
} else {
app.use(morgan('combined'));
}
app.use(
pinoHttp({
logger: getLogger(),
customLogLevel: (req, res) => {
if (res.statusCode >= 500) {
return 'error';
} else if (res.statusCode >= 400) {
return 'warn';
}
return 'info';
},
customSuccessMessage: function (req, res) {
return `Request ${req.method} to ${req.url} completed with status ${res.statusCode}`;
},
customErrorMessage: function (req, res, err) {
return `Request ${req.method} to ${req.url} errored with status ${res.statusCode}`;
},
serializers: {
req: pino.stdSerializers.wrapRequestSerializer((req) => {
return {
...req,
headers: redactHeaders(req.raw.headers),
};
}),
res: pino.stdSerializers.wrapResponseSerializer((res) => {
return {
...res,
headers: redactHeaders(res.headers),
};
}),
},
}),
);

app.get('/health', async (req: express.Request, res: express.Response) => {
res.status(200).send('OK');
Expand All @@ -106,22 +138,22 @@ export const streamableHTTPApp = ({

export const launchStreamableHTTPServer = async ({
mcpOptions,
debug,
port,
}: {
mcpOptions: McpOptions;
debug: boolean;
port: number | string | undefined;
}) => {
const app = streamableHTTPApp({ mcpOptions, debug });
const app = streamableHTTPApp({ mcpOptions });
const server = app.listen(port);
const address = server.address();

const logger = getLogger();

if (typeof address === 'string') {
console.error(`MCP Server running on streamable HTTP at ${address}`);
logger.info(`MCP Server running on streamable HTTP at ${address}`);
} else if (address !== null) {
console.error(`MCP Server running on streamable HTTP on port ${address.port}`);
logger.info(`MCP Server running on streamable HTTP on port ${address.port}`);
} else {
console.error(`MCP Server running on streamable HTTP on port ${port}`);
logger.info(`MCP Server running on streamable HTTP on port ${port}`);
}
};
Loading