diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdebfa5..d51879e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,14 +55,18 @@ jobs: run: ./scripts/build - name: Get GitHub OIDC Token - if: github.repository == 'stainless-sdks/isaacus-typescript' + if: |- + github.repository == 'stainless-sdks/isaacus-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/isaacus-typescript' + if: |- + github.repository == 'stainless-sdks/isaacus-typescript' && + !startsWith(github.ref, 'refs/heads/stl/') env: URL: https://pkg.stainless.com/s AUTH: ${{ steps.github-oidc.outputs.github_token }} @@ -70,7 +74,9 @@ jobs: run: ./scripts/utils/upload-artifact.sh - name: Upload MCP Server tarball - if: github.repository == 'stainless-sdks/isaacus-typescript' + if: |- + github.repository == 'stainless-sdks/isaacus-typescript' && + !startsWith(github.ref, 'refs/heads/stl/') env: URL: https://pkg.stainless.com/s?subpackage=mcp-server AUTH: ${{ steps.github-oidc.outputs.github_token }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d353515..788b0fa 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.21.0" + ".": "0.22.0" } diff --git a/.stats.yml b/.stats.yml index d3f723c..f028d53 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 5 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/isaacus%2Fisaacus-baf5ebdd05d1b5192759a356c2701131c47cfb5b7239a49fcb94e68d4210648c.yml -openapi_spec_hash: 6ea786d56726e18156adf57915fcbe5f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/isaacus%2Fisaacus-3fc0da1b03c07a47ee0a04ff61443710b99e6d24e8ba5986aab0b1c023c6f0d5.yml +openapi_spec_hash: 5992dd036e090cc43fa15ea5ff985f77 config_hash: 9040e7359f066240ad536041fb2c5185 diff --git a/CHANGELOG.md b/CHANGELOG.md index 96084f7..682ed59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## 0.22.0 (2026-03-11) + +Full Changelog: [v0.21.0...v0.22.0](https://github.com/isaacus-dev/isaacus-typescript/compare/v0.21.0...v0.22.0) + +### Features + +* **api:** add kanon-2-reranker ([6874725](https://github.com/isaacus-dev/isaacus-typescript/commit/68747257c2b6c34baa76d78e9c889d240189a83b)) +* **api:** drop mini models ([f08dc45](https://github.com/isaacus-dev/isaacus-typescript/commit/f08dc4595a60ced58c376960be99d73cfcf1658c)) + + +### Bug Fixes + +* **client:** preserve URL params already embedded in path ([2acb447](https://github.com/isaacus-dev/isaacus-typescript/commit/2acb447b0308ed21983f1c8d665016e8bcf802a4)) + + +### Chores + +* **ci:** skip uploading artifacts on stainless-internal branches ([ade18ff](https://github.com/isaacus-dev/isaacus-typescript/commit/ade18ffd5cf57d9c38e54665cf418dc54a56fd3c)) +* **internal:** codegen related update ([77a4e10](https://github.com/isaacus-dev/isaacus-typescript/commit/77a4e109763c99585dacdc0e5786f8bf214f173e)) +* **internal:** codegen related update ([61d1a70](https://github.com/isaacus-dev/isaacus-typescript/commit/61d1a70e6af2ff3c1c0f16517bca200f162cf9c1)) +* **internal:** update dependencies to address dependabot vulnerabilities ([625f523](https://github.com/isaacus-dev/isaacus-typescript/commit/625f523f36e1935b253dc3b7258b2f8e5cd866fd)) +* **internal:** use x-stainless-mcp-client-envs header for MCP remote code tool calls ([e372066](https://github.com/isaacus-dev/isaacus-typescript/commit/e372066ae02d0ffcf5fae210ba027c9b13950141)) +* **mcp-server:** improve instructions ([381ce99](https://github.com/isaacus-dev/isaacus-typescript/commit/381ce99950e4220f5c7ca5f20faabcdc5252ec8a)) +* **mcp-server:** return access instructions for 404 without API key ([8d188e6](https://github.com/isaacus-dev/isaacus-typescript/commit/8d188e6a165648c20580f15c5f4ce8febc7acab1)) + + +### Documentation + +* **api:** reworded descriptions ([d3924ec](https://github.com/isaacus-dev/isaacus-typescript/commit/d3924ec55f712b7df79847049c7f6e4ea07352a2)) + ## 0.21.0 (2026-03-03) Full Changelog: [v0.20.2...v0.21.0](https://github.com/isaacus-dev/isaacus-typescript/compare/v0.20.2...v0.21.0) diff --git a/package.json b/package.json index c74b97b..69d7249 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "isaacus", - "version": "0.21.0", + "version": "0.22.0", "description": "The official TypeScript library for the Isaacus API", "author": "Isaacus ", "types": "dist/index.d.ts", @@ -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", diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json index a5d6cf5..6ee684b 100644 --- a/packages/mcp-server/manifest.json +++ b/packages/mcp-server/manifest.json @@ -1,7 +1,7 @@ { "dxt_version": "0.2", "name": "isaacus-mcp", - "version": "0.21.0", + "version": "0.22.0", "description": "The official MCP Server for the Isaacus API", "author": { "name": "Isaacus", diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index bc30dc0..d8cff83 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "isaacus-mcp", - "version": "0.21.0", + "version": "0.22.0", "description": "The official MCP Server for the Isaacus API", "author": "Isaacus ", "types": "dist/index.d.ts", @@ -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": { "isaacus": "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", @@ -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", diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 200bdd6..6396cb7 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -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 'isaacus'; @@ -87,6 +88,8 @@ export function codeTool({ }, }; + const logger = getLogger(); + const handler = async ({ reqContext, args, @@ -111,11 +114,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 }; @@ -140,7 +159,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({ ISAACUS_API_KEY: requireValue( readEnv('ISAACUS_API_KEY') ?? client.apiKey, 'set ISAACUS_API_KEY environment variable or provide apiKey client option', @@ -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 diff --git a/packages/mcp-server/src/docs-search-tool.ts b/packages/mcp-server/src/docs-search-tool.ts index fa32550..7d8688f 100644 --- a/packages/mcp-server/src/docs-search-tool.ts +++ b/packages/mcp-server/src/docs-search-tool.ts @@ -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', @@ -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: { @@ -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 }; diff --git a/packages/mcp-server/src/http.ts b/packages/mcp-server/src/http.ts index 2d506d9..9606565 100644 --- a/packages/mcp-server/src/http.ts +++ b/packages/mcp-server/src/http.ts @@ -4,9 +4,10 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { ClientOptions } from 'isaacus'; 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'; @@ -70,29 +71,60 @@ const del = async (req: express.Request, res: express.Response) => { }); }; +const redactHeaders = (headers: Record) => { + 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'); @@ -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}`); } }; diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts index 654d25c..5bca4a6 100644 --- a/packages/mcp-server/src/index.ts +++ b/packages/mcp-server/src/index.ts @@ -5,15 +5,20 @@ import { McpOptions, parseCLIOptions } from './options'; import { launchStdioServer } from './stdio'; import { launchStreamableHTTPServer } from './http'; import type { McpTool } from './types'; +import { configureLogger, getLogger } from './logger'; async function main() { const options = parseOptionsOrError(); + configureLogger({ + level: options.debug ? 'debug' : 'info', + pretty: options.logFormat === 'pretty', + }); const selectedTools = await selectToolsOrError(options); - console.error( - `MCP Server starting with ${selectedTools.length} tools:`, - selectedTools.map((e) => e.tool.name), + getLogger().info( + { tools: selectedTools.map((e) => e.tool.name) }, + `MCP Server starting with ${selectedTools.length} tools`, ); switch (options.transport) { @@ -23,7 +28,6 @@ async function main() { case 'http': await launchStreamableHTTPServer({ mcpOptions: options, - debug: options.debug, port: options.socket ?? options.port, }); break; @@ -32,7 +36,8 @@ async function main() { if (require.main === module) { main().catch((error) => { - console.error('Fatal error in main():', error); + // Logger might not be initialized yet + console.error('Fatal error in main()', error); process.exit(1); }); } @@ -41,7 +46,8 @@ function parseOptionsOrError() { try { return parseCLIOptions(); } catch (error) { - console.error('Error parsing options:', error); + // Logger is initialized after options, so use console.error here + console.error('Error parsing options', error); process.exit(1); } } @@ -50,16 +56,12 @@ async function selectToolsOrError(options: McpOptions): Promise { try { const includedTools = selectTools(options); if (includedTools.length === 0) { - console.error('No tools match the provided filters.'); + getLogger().error('No tools match the provided filters'); process.exit(1); } return includedTools; } catch (error) { - if (error instanceof Error) { - console.error('Error filtering tools:', error.message); - } else { - console.error('Error filtering tools:', error); - } + getLogger().error({ error }, 'Error filtering tools'); process.exit(1); } } diff --git a/packages/mcp-server/src/instructions.ts b/packages/mcp-server/src/instructions.ts index 229beb7..06403de 100644 --- a/packages/mcp-server/src/instructions.ts +++ b/packages/mcp-server/src/instructions.ts @@ -1,6 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { readEnv } from './util'; +import { getLogger } from './logger'; const INSTRUCTIONS_CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes @@ -50,25 +51,15 @@ async function fetchLatestInstructions(stainlessApiKey: string | undefined): Pro let instructions: string | undefined; if (!response.ok) { - console.warn( + getLogger().warn( 'Warning: failed to retrieve MCP server instructions. Proceeding with default instructions...', ); - instructions = ` - This is the isaacus MCP server. You will use Code Mode to help the user perform - actions. You can use search_docs tool to learn about how to take action with this server. Then, - you will write TypeScript code using the execute tool take action. It is CRITICAL that you be - thoughtful and deliberate when executing code. Always try to entirely solve the problem in code - block: it can be as long as you need to get the job done! - `; + instructions = + '\n This is the isaacus MCP server.\n\n Available tools:\n - search_docs: Search SDK documentation to find the right methods and parameters.\n - execute: Run TypeScript code against a pre-authenticated SDK client. Define an async run(client) function.\n\n Workflow:\n - If unsure about the API, call search_docs first.\n - Write complete solutions in a single execute call when possible. For large datasets, use API filters to narrow results or paginate within a single execute block.\n - If execute returns an error, read the error and fix your code rather than retrying the same approach.\n - Variables do not persist between execute calls. Return or log all data you need.\n - Individual HTTP requests to the API have a 30-second timeout. If a request times out, try a smaller query or add filters.\n - Code execution has a total timeout of approximately 5 minutes. If your code times out, simplify it or break it into smaller steps.\n '; } instructions ??= ((await response.json()) as { instructions: string }).instructions; - instructions = ` - If needed, you can get the current time by executing Date.now(). - - ${instructions} - `; return instructions; } diff --git a/packages/mcp-server/src/logger.ts b/packages/mcp-server/src/logger.ts new file mode 100644 index 0000000..29dab11 --- /dev/null +++ b/packages/mcp-server/src/logger.ts @@ -0,0 +1,28 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { pino, type Level, type Logger } from 'pino'; +import pretty from 'pino-pretty'; + +let _logger: Logger | undefined; + +export function configureLogger({ level, pretty: usePretty }: { level: Level; pretty: boolean }): void { + _logger = pino( + { + level, + timestamp: pino.stdTimeFunctions.isoTime, + formatters: { + level(label) { + return { level: label }; + }, + }, + }, + usePretty ? pretty({ colorize: true, levelFirst: true, destination: 2 }) : process.stderr, + ); +} + +export function getLogger(): Logger { + if (!_logger) { + throw new Error('Logger has not been configured. Call configureLogger() before using the logger.'); + } + return _logger; +} diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts index 069b881..b9e8e8a 100644 --- a/packages/mcp-server/src/options.ts +++ b/packages/mcp-server/src/options.ts @@ -8,6 +8,7 @@ import { readEnv } from './util'; export type CLIOptions = McpOptions & { debug: boolean; + logFormat: 'json' | 'pretty'; transport: 'stdio' | 'http'; port: number | undefined; socket: string | undefined; @@ -52,6 +53,11 @@ export function parseCLIOptions(): CLIOptions { "Where to run code execution in code tool; 'stainless-sandbox' will execute code in Stainless-hosted sandboxes whereas 'local' will execute code locally on the MCP server machine.", }) .option('debug', { type: 'boolean', description: 'Enable debug logging' }) + .option('log-format', { + type: 'string', + choices: ['json', 'pretty'], + description: 'Format for log output; defaults to json unless tty is detected', + }) .option('no-tools', { type: 'string', array: true, @@ -97,6 +103,10 @@ export function parseCLIOptions(): CLIOptions { const includeDocsTools = shouldIncludeToolType('docs'); const transport = argv.transport as 'stdio' | 'http'; + const logFormat = + argv.logFormat ? (argv.logFormat as 'json' | 'pretty') + : process.stderr.isTTY ? 'pretty' + : 'json'; return { ...(includeCodeTool !== undefined && { includeCodeTool }), @@ -108,6 +118,7 @@ export function parseCLIOptions(): CLIOptions { codeBlockedMethods: argv.codeBlockedMethods, codeExecutionMode: argv.codeExecutionMode as McpCodeExecutionMode, transport, + logFormat, port: argv.port, socket: argv.socket, }; diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 6fd947c..7df14f6 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -20,7 +20,7 @@ export const newMcpServer = async (stainlessApiKey: string | undefined) => new McpServer( { name: 'isaacus_api', - version: '0.21.0', + version: '0.22.0', }, { instructions: await getInstructions(stainlessApiKey), diff --git a/packages/mcp-server/src/stdio.ts b/packages/mcp-server/src/stdio.ts index ceccaed..e8bcbb1 100644 --- a/packages/mcp-server/src/stdio.ts +++ b/packages/mcp-server/src/stdio.ts @@ -1,6 +1,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { McpOptions } from './options'; import { initMcpServer, newMcpServer } from './server'; +import { getLogger } from './logger'; export const launchStdioServer = async (mcpOptions: McpOptions) => { const server = await newMcpServer(mcpOptions.stainlessApiKey); @@ -9,5 +10,5 @@ export const launchStdioServer = async (mcpOptions: McpOptions) => { const transport = new StdioServerTransport(); await server.connect(transport); - console.error('MCP Server running on stdio'); + getLogger().info('MCP Server running on stdio'); }; diff --git a/src/client.ts b/src/client.ts index 2713576..0f106ab 100644 --- a/src/client.ts +++ b/src/client.ts @@ -252,8 +252,9 @@ export class Isaacus { : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path)); const defaultQuery = this.defaultQuery(); - if (!isEmptyObj(defaultQuery)) { - query = { ...defaultQuery, ...query }; + const pathQuery = Object.fromEntries(url.searchParams); + if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) { + query = { ...pathQuery, ...defaultQuery, ...query }; } if (typeof query === 'object' && query && !Array.isArray(query)) { @@ -562,9 +563,9 @@ export class Isaacus { } } - // If the API asks us to wait a certain amount of time (and it's a reasonable amount), - // just do what it says, but otherwise calculate a default - if (!(timeoutMillis && 0 <= timeoutMillis && timeoutMillis < 60 * 1000)) { + // If the API asks us to wait a certain amount of time, just do what it + // says, but otherwise calculate a default + if (timeoutMillis === undefined) { const maxRetries = options.maxRetries ?? this.maxRetries; timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries); } @@ -722,10 +723,19 @@ export class Isaacus { static toFile = Uploads.toFile; + /** + * Vectorize content with an Isaacus embedding model. + */ embeddings: API.Embeddings = new API.Embeddings(this); classifications: API.Classifications = new API.Classifications(this); + /** + * Score and rank documents by their relevance to queries with an Isaacus reranker. + */ rerankings: API.Rerankings = new API.Rerankings(this); extractions: API.Extractions = new API.Extractions(this); + /** + * Enrich documents with an Isaacus enrichment model. + */ enrichments: API.Enrichments = new API.Enrichments(this); ilgs: API.ILGS = new API.ILGS(this); } diff --git a/src/resources/classifications/universal/universal.ts b/src/resources/classifications/universal/universal.ts index b0fded3..a0f6007 100644 --- a/src/resources/classifications/universal/universal.ts +++ b/src/resources/classifications/universal/universal.ts @@ -4,10 +4,12 @@ import { APIResource } from '../../../core/resource'; import { APIPromise } from '../../../core/api-promise'; import { RequestOptions } from '../../../internal/request-options'; +/** + * Classify documents with an Isaacus classification model. + */ export class Universal extends APIResource { /** - * Classify the relevance of legal documents to a query with an Isaacus universal - * legal AI classifier. + * Classify documents with an Isaacus universal classification model. * * @example * ```ts @@ -29,7 +31,7 @@ export class Universal extends APIResource { export interface UniversalClassificationResponse { /** * The classifications of the texts, by relevance to the query, in order from - * highest to lowest relevance score. + * highest to lowest confidence score. */ classifications: Array; @@ -120,7 +122,7 @@ export interface UniversalCreateParams { * The ID of the [model](https://docs.isaacus.com/models#universal-classification) * to use for universal classification. */ - model: 'kanon-universal-classifier' | 'kanon-universal-classifier-mini'; + model: 'kanon-universal-classifier'; /** * The [Isaacus Query Language (IQL)](https://docs.isaacus.com/iql) query or, if diff --git a/src/resources/embeddings.ts b/src/resources/embeddings.ts index 01dd6ea..ad985c6 100644 --- a/src/resources/embeddings.ts +++ b/src/resources/embeddings.ts @@ -4,9 +4,12 @@ import { APIResource } from '../core/resource'; import { APIPromise } from '../core/api-promise'; import { RequestOptions } from '../internal/request-options'; +/** + * Vectorize content with an Isaacus embedding model. + */ export class Embeddings extends APIResource { /** - * Embed legal texts with an Isaacus legal AI embedder. + * Vectorize content with an Isaacus embedding model. * * @example * ```ts diff --git a/src/resources/enrichments.ts b/src/resources/enrichments.ts index e577b62..9fdaad4 100644 --- a/src/resources/enrichments.ts +++ b/src/resources/enrichments.ts @@ -5,9 +5,12 @@ import * as v1API from './ilgs/v1/v1'; import { APIPromise } from '../core/api-promise'; import { RequestOptions } from '../internal/request-options'; +/** + * Enrich documents with an Isaacus enrichment model. + */ export class Enrichments extends APIResource { /** - * Enrich documents with an Isaacus enricher model. + * Enrich documents with an Isaacus enrichment model. * * @example * ```ts diff --git a/src/resources/extractions/qa/qa.ts b/src/resources/extractions/qa/qa.ts index 873bf33..26ed207 100644 --- a/src/resources/extractions/qa/qa.ts +++ b/src/resources/extractions/qa/qa.ts @@ -4,10 +4,13 @@ import { APIResource } from '../../../core/resource'; import { APIPromise } from '../../../core/api-promise'; import { RequestOptions } from '../../../internal/request-options'; +/** + * Extract information from documents with an Isaacus extraction model. + */ export class QA extends APIResource { /** - * Extract answers to questions from legal documents with an Isaacus legal AI - * answer extractor. + * Extract information from documents with an Isaacus extractive question answering + * model. * * @example * ```ts @@ -117,7 +120,7 @@ export interface QACreateParams { * [model](https://docs.isaacus.com/models#extractive-question-answering) to use * for extractive question answering. */ - model: 'kanon-answer-extractor' | 'kanon-answer-extractor-mini'; + model: 'kanon-answer-extractor'; /** * The query to extract the answer to. diff --git a/src/resources/rerankings.ts b/src/resources/rerankings.ts index 9e7e84e..630f3b5 100644 --- a/src/resources/rerankings.ts +++ b/src/resources/rerankings.ts @@ -4,15 +4,17 @@ import { APIResource } from '../core/resource'; import { APIPromise } from '../core/api-promise'; import { RequestOptions } from '../internal/request-options'; +/** + * Score and rank documents by their relevance to queries with an Isaacus reranker. + */ export class Rerankings extends APIResource { /** - * Rank legal documents by their relevance to a query with an Isaacus legal AI - * reranker. + * Score and rank documents by their relevance to queries with an Isaacus reranker. * * @example * ```ts * const rerankingResponse = await client.rerankings.create({ - * model: 'kanon-universal-classifier', + * model: 'kanon-2-reranker', * query: * 'What are the essential elements required to establish a negligence claim?', * texts: [ @@ -32,8 +34,8 @@ export class Rerankings extends APIResource { export interface RerankingResponse { /** - * The rerankings of the texts, by relevance to the query, in order from highest to - * lowest relevance score. + * The texts reranked by relevance to the query, in order from highest to lowest + * relevance score. */ results: Array; @@ -71,10 +73,11 @@ export namespace RerankingResponse { export interface RerankingCreateParams { /** - * The ID of the [model](https://docs.isaacus.com/models#reranking) to use for - * reranking. + * The ID of the model to use for reranking, being either a + * [reranking model](https://docs.isaacus.com/models/introduction#reranking) or + * [universal classification model](https://docs.isaacus.com/models/introduction#universal-classification). */ - model: 'kanon-universal-classifier' | 'kanon-universal-classifier-mini'; + model: 'kanon-2-reranker' | 'kanon-universal-classifier'; /** * The query to evaluate the relevance of the texts to. @@ -112,7 +115,9 @@ export interface RerankingCreateParams { is_iql?: boolean; /** - * The method to use for producing an overall relevance score for a text. + * The method to use for producing an overall relevance score for a text that + * exceeds the model's local context window and has, therefore, been split into + * multiple chunks. * * `auto` is the default scoring method and is recommended for most use cases. * Currently, it is equivalent to `chunk_max`. In the future, it will automatically diff --git a/src/version.ts b/src/version.ts index bc95435..db66d33 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.21.0'; // x-release-please-version +export const VERSION = '0.22.0'; // x-release-please-version diff --git a/tests/api-resources/rerankings.test.ts b/tests/api-resources/rerankings.test.ts index 12c24b0..30bf4fc 100644 --- a/tests/api-resources/rerankings.test.ts +++ b/tests/api-resources/rerankings.test.ts @@ -11,7 +11,7 @@ describe('resource rerankings', () => { // Mock server tests are disabled test.skip('create: only required params', async () => { const responsePromise = client.rerankings.create({ - model: 'kanon-universal-classifier', + model: 'kanon-2-reranker', query: 'What are the essential elements required to establish a negligence claim?', texts: [ 'To form a contract, there must be an offer, acceptance, consideration, and mutual intent to be bound.', @@ -33,7 +33,7 @@ describe('resource rerankings', () => { // Mock server tests are disabled test.skip('create: required and optional params', async () => { const response = await client.rerankings.create({ - model: 'kanon-universal-classifier', + model: 'kanon-2-reranker', query: 'What are the essential elements required to establish a negligence claim?', texts: [ 'To form a contract, there must be an offer, acceptance, consideration, and mutual intent to be bound.', diff --git a/yarn.lock b/yarn.lock index fc9f262..078f09a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1219,15 +1219,7 @@ baseline-browser-mapping@^2.9.0: resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz#3b6af0bc032445bca04de58caa9a87cfe921cbb3" integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== -brace-expansion@^1.1.7: - version "1.1.12" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" - integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: +brace-expansion@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== @@ -1395,11 +1387,6 @@ commander@^10.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -2600,26 +2587,12 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^9.0.4: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2, minimatch@^5.0.1, minimatch@^9.0.4, minimatch@^9.0.5: + version "9.0.9" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" + integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== dependencies: - brace-expansion "^2.0.1" + brace-expansion "^2.0.2" minimist@^1.2.6: version "1.2.6"