Skip to content
Merged
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
12 changes: 9 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,28 @@ jobs:
run: ./scripts/build

- name: Get GitHub OIDC Token
if: github.repository == 'stainless-sdks/finch-typescript'
if: |-
github.repository == 'stainless-sdks/finch-typescript' &&
!startsWith(github.ref, 'refs/heads/stl/')
id: github-oidc
uses: actions/github-script@v8
with:
script: core.setOutput('github_token', await core.getIDToken());

- name: Upload tarball
if: github.repository == 'stainless-sdks/finch-typescript'
if: |-
github.repository == 'stainless-sdks/finch-typescript' &&
!startsWith(github.ref, 'refs/heads/stl/')
env:
URL: https://pkg.stainless.com/s
AUTH: ${{ steps.github-oidc.outputs.github_token }}
SHA: ${{ github.sha }}
run: ./scripts/utils/upload-artifact.sh

- name: Upload MCP Server tarball
if: github.repository == 'stainless-sdks/finch-typescript'
if: |-
github.repository == 'stainless-sdks/finch-typescript' &&
!startsWith(github.ref, 'refs/heads/stl/')
env:
URL: https://pkg.stainless.com/s?subpackage=mcp-server
AUTH: ${{ steps.github-oidc.outputs.github_token }}
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "9.2.0"
".": "9.3.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: 46
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-46f433f34d440aa1dfcc48cc8d822c598571b68be2f723ec99e1b4fba6c13b1e.yml
openapi_spec_hash: 5b5cd728776723ac773900f7e8a32c05
config_hash: ccdf6a5b4aaa2a0897c89ac8685d8eb0
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-093ade6f1d3115b654a73b97855fbe334c9f9c5d906081dad2ec973ab0c0b24d.yml
openapi_spec_hash: 7cc27b8e483d9db9c411875289c42eb9
config_hash: d21a244fc073152c8dbecb8ece970209
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## 9.3.0 (2026-03-10)

Full Changelog: [v9.2.0...v9.3.0](https://github.com/Finch-API/finch-api-node/compare/v9.2.0...v9.3.0)

### Features

* **api:** api update ([fa2b60a](https://github.com/Finch-API/finch-api-node/commit/fa2b60a267e9da0df1f8d884ffb47f4d5757f502))


### Bug Fixes

* **api:** remove invalid transform config ([325973d](https://github.com/Finch-API/finch-api-node/commit/325973dd45c7ec667d8e8b061025b07ef0a43571))
* **client:** preserve URL params already embedded in path ([0e6e60b](https://github.com/Finch-API/finch-api-node/commit/0e6e60b12695f3f939e68ed532df8635514e8a28))


### Chores

* **ci:** skip uploading artifacts on stainless-internal branches ([73749d6](https://github.com/Finch-API/finch-api-node/commit/73749d6fa0efd3bc663806c129b97d550d2f5173))
* **internal:** codegen related update ([c3b5362](https://github.com/Finch-API/finch-api-node/commit/c3b536242683da322c746ec84036f5e5f5b4dff9))
* **internal:** update dependencies to address dependabot vulnerabilities ([a4b3eac](https://github.com/Finch-API/finch-api-node/commit/a4b3eac445d4796c00f782a10e2fc74a1eb56624))
* **mcp-server:** improve instructions ([3240242](https://github.com/Finch-API/finch-api-node/commit/32402420f11116f77ceddff72ff66a93573da557))

## 9.2.0 (2026-02-26)

Full Changelog: [v9.1.1...v9.2.0](https://github.com/Finch-API/finch-api-node/compare/v9.1.1...v9.2.0)
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": "@tryfinch/finch-api",
"version": "9.2.0",
"version": "9.3.0",
"description": "The official TypeScript library for the Finch API",
"author": "Finch <founders@tryfinch.com>",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -55,6 +55,17 @@
"bin": {
"tryfinch-finch-api": "bin/cli"
},
"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": "@tryfinch/finch-api-mcp",
"version": "9.2.0",
"version": "9.3.0",
"description": "The official MCP Server for the Finch API",
"author": {
"name": "Finch",
Expand Down
23 changes: 13 additions & 10 deletions packages/mcp-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tryfinch/finch-api-mcp",
"version": "9.2.0",
"version": "9.3.0",
"description": "The official MCP Server for the Finch API",
"author": "Finch <founders@tryfinch.com>",
"types": "dist/index.d.ts",
Expand All @@ -26,21 +26,25 @@
"format": "prettier --write --cache --cache-strategy metadata . !dist",
"prepare": "npm run build",
"tsn": "ts-node -r tsconfig-paths/register",
"lint": "eslint --ext ts,js .",
"fix": "eslint --fix --ext ts,js ."
"lint": "eslint .",
"fix": "eslint --fix ."
},
"dependencies": {
"@tryfinch/finch-api": "file:../../dist/",
"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",
"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,14 +61,13 @@
"@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",
"@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
36 changes: 31 additions & 5 deletions packages/mcp-server/src/code-tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ import {
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { readEnv } from './util';
import { WorkerInput, WorkerOutput } from './code-tool-types';
import { getLogger } from './logger';
import { SdkMethod } from './methods';
import { McpCodeExecutionMode } from './options';
import { ClientOptions } from '@tryfinch/finch-api';

const prompt = `Runs JavaScript code to interact with the Finch API.

You are a skilled programmer writing code to interface with the service.
You are a skilled TypeScript programmer writing code to interface with the service.
Define an async function named "run" that takes a single parameter of an initialized SDK client and it will be run.
For example:

Expand All @@ -40,7 +41,9 @@ You will be returned anything that your function returns, plus the results of an
Do not add try-catch blocks for single API calls. The tool will handle errors for you.
Do not add comments unless necessary for generating better code.
Code will run in a container, and cannot interact with the network outside of the given SDK client.
Variables will not persist between calls, so make sure to return or log any data you might need later.`;
Variables will not persist between calls, so make sure to return or log any data you might need later.
Remember that you are writing TypeScript code, so you need to be careful with your types.
Always type dynamic key-value stores explicitly as Record<string, YourValueType> instead of {}.`;

/**
* A tool that runs code against a copy of the SDK.
Expand Down Expand Up @@ -82,6 +85,8 @@ export function codeTool({
},
};

const logger = getLogger();

const handler = async ({
reqContext,
args,
Expand All @@ -106,11 +111,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 All @@ -135,7 +156,7 @@ const remoteStainlessHandler = async ({
headers: {
...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }),
'Content-Type': 'application/json',
client_envs: JSON.stringify({
'x-stainless-mcp-client-envs': JSON.stringify({
FINCH_CLIENT_ID: readEnv('FINCH_CLIENT_ID') ?? client.clientID ?? undefined,
FINCH_CLIENT_SECRET: readEnv('FINCH_CLIENT_SECRET') ?? client.clientSecret ?? undefined,
FINCH_WEBHOOK_SECRET: readEnv('FINCH_WEBHOOK_SECRET') ?? client.webhookSecret ?? undefined,
Expand All @@ -151,6 +172,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
40 changes: 36 additions & 4 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 All @@ -12,7 +13,8 @@ export const metadata: Metadata = {

export const tool: Tool = {
name: 'search_docs',
description: 'Search for documentation for how to use the client to interact with the API.',
description:
'Search SDK documentation to find methods, parameters, and usage examples for interacting with the API. Use this before writing code when you need to discover the right approach.',
inputSchema: {
type: 'object',
properties: {
Expand Down Expand Up @@ -50,19 +52,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 };
Loading