diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b1b538..81859d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,10 @@ on: - 'integrated/**' - 'stl-preview-head/**' - 'stl-preview-base/**' + pull_request: + branches-ignore: + - 'stl-preview-head/**' + - 'stl-preview-base/**' jobs: lint: diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b3b5e58..12342be 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0-alpha.4" + ".": "0.1.0-alpha.5" } diff --git a/.stats.yml b/.stats.yml index b05f69d..5b2cf0d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 17 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/scan-documents%2Fscan-documents-41af66c2f2ba90b745f6c36d89d31281f16d916521b98a8f510ec3a8f90a1efb.yml -openapi_spec_hash: 0c629cd46c640a055b0cfa5d88aad337 -config_hash: ee078a284af580362c78b746a0c811c2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/scan-documents%2Fscan-documents-42f9aa9c3b2769584f60fa3742f3a004bdf2e3b5ba30a44535b0191654b1a51e.yml +openapi_spec_hash: a0bab3e1411b24d8a662df98d227049a +config_hash: 4f1180f734cbc7323ff2ed85a9cd510d diff --git a/CHANGELOG.md b/CHANGELOG.md index 3976c3f..14f66d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,55 @@ # Changelog +## 0.1.0-alpha.5 (2025-06-24) + +Full Changelog: [v0.1.0-alpha.4...v0.1.0-alpha.5](https://github.com/Scan-Documents/node-sdk/compare/v0.1.0-alpha.4...v0.1.0-alpha.5) + +### Features + +* **api:** update via SDK Studio ([70168fa](https://github.com/Scan-Documents/node-sdk/commit/70168fa2bf97cb14beb6e050d90871240168e44c)) +* **api:** update via SDK Studio ([a43a003](https://github.com/Scan-Documents/node-sdk/commit/a43a00355e7fc01e579754abb248329b4917d778)) +* **api:** update via SDK Studio ([4bedf3d](https://github.com/Scan-Documents/node-sdk/commit/4bedf3df8238c97071510a3683a742add5939d5d)) +* **api:** update via SDK Studio ([5c5a063](https://github.com/Scan-Documents/node-sdk/commit/5c5a063a2f53dec45c0a193adb446250efc430a8)) +* **api:** update via SDK Studio ([d04e245](https://github.com/Scan-Documents/node-sdk/commit/d04e2459e3af994e8124b85240423f171dfd6f75)) +* **client:** add support for endpoint-specific base URLs ([d154b67](https://github.com/Scan-Documents/node-sdk/commit/d154b6772a1a12c052eb1f97a4ae45f685100fc8)) +* **mcp:** implement support for binary responses ([a89234f](https://github.com/Scan-Documents/node-sdk/commit/a89234fd6eb4de419eb80f4c9d5a324e3da34183)) +* **mcp:** include http information in tools ([3d8e524](https://github.com/Scan-Documents/node-sdk/commit/3d8e52400096f3f9868ab83586976a3a92a2d467)) +* **mcp:** set X-Stainless-MCP header ([6d04f3b](https://github.com/Scan-Documents/node-sdk/commit/6d04f3b56632c8b8f4fbb47862b3b5c0d5371f0e)) + + +### Bug Fixes + +* **client:** explicitly copy fetch in withOptions ([0c5fe5a](https://github.com/Scan-Documents/node-sdk/commit/0c5fe5acff20bd4ca263d05f08d8d890257b72a8)) +* compat with more runtimes ([96df746](https://github.com/Scan-Documents/node-sdk/commit/96df7469f152a286f008ef22757d79512bd28cef)) +* **mcp:** fix cursor schema transformation issue with recursive references ([112fb67](https://github.com/Scan-Documents/node-sdk/commit/112fb67a3f37d876d0b00d801c4092c508ec33da)) +* **mcp:** include description in dynamic tool search ([588cd03](https://github.com/Scan-Documents/node-sdk/commit/588cd035d9908c073e26021046cbace0f809a5c0)) +* publish script — handle NPM errors correctly ([fc5ea58](https://github.com/Scan-Documents/node-sdk/commit/fc5ea5897660f918c600d9b6f90c405d52b609b5)) + + +### Chores + +* adjust eslint.config.mjs ignore pattern ([a3eeed2](https://github.com/Scan-Documents/node-sdk/commit/a3eeed23683fea726fca2a9755320caec5f5b08a)) +* avoid type error in certain environments ([13a317a](https://github.com/Scan-Documents/node-sdk/commit/13a317a3d40e8e53686c06860e9a8eb13ae3d6a5)) +* **ci:** enable for pull requests ([23ffa3c](https://github.com/Scan-Documents/node-sdk/commit/23ffa3cafec1e413ad5468f804e1ccf8d26872b7)) +* **client:** refactor imports ([ebb2afd](https://github.com/Scan-Documents/node-sdk/commit/ebb2afd0c55b2c02f374c761b4e668cfdc9cd0a0)) +* **deps:** bump eslint-plugin-prettier ([d4ded48](https://github.com/Scan-Documents/node-sdk/commit/d4ded48bc45b220e74f4130d3ba95f2203318933)) +* **docs:** grammar improvements ([93f8200](https://github.com/Scan-Documents/node-sdk/commit/93f8200ffd314b4ec9e36dd3ea972ab2a9781ef7)) +* **docs:** use top-level-await in example snippets ([d9519ed](https://github.com/Scan-Documents/node-sdk/commit/d9519eda1ac2b8e10ae306c0690815a915709fe0)) +* improve publish-npm script --latest tag logic ([2053613](https://github.com/Scan-Documents/node-sdk/commit/20536130588f3ea098459c542c680a1f0b8c7537)) +* **internal:** add pure annotations, make base APIResource abstract ([9ce83f6](https://github.com/Scan-Documents/node-sdk/commit/9ce83f6d0994b3e9712dc25bd64c51762d18cb95)) +* **internal:** codegen related update ([f65a114](https://github.com/Scan-Documents/node-sdk/commit/f65a114a884f9595b263d7216ff48fa05d194430)) +* **internal:** fix readablestream types in node 20 ([79d997e](https://github.com/Scan-Documents/node-sdk/commit/79d997eaa3df27558d7664c02381ac989b141848)) +* **internal:** update jest config ([2d34bb5](https://github.com/Scan-Documents/node-sdk/commit/2d34bb54aa3f193ca3bbb64d5bfc8c058b6c9b50)) +* **mcp:** provides high-level initMcpServer function and exports known clients ([67ae503](https://github.com/Scan-Documents/node-sdk/commit/67ae503e605c16a97f3286017348bc7e8fa366c8)) +* **mcp:** remove duplicate assignment ([df3e9b5](https://github.com/Scan-Documents/node-sdk/commit/df3e9b575185818ec86317331deaaa339795914a)) +* **readme:** update badges ([96e2b2e](https://github.com/Scan-Documents/node-sdk/commit/96e2b2e4bcdc2aac0ae54a166da1bb74d9b0a4ed)) +* **readme:** use better example snippet for undocumented params ([4f15e11](https://github.com/Scan-Documents/node-sdk/commit/4f15e1146712fe17de280aafcf7e0db38a3978e9)) + + +### Refactors + +* **types:** replace Record with mapped types ([1b2ab0b](https://github.com/Scan-Documents/node-sdk/commit/1b2ab0b6728a66f72c8726c814c1480af27ac799)) + ## 0.1.0-alpha.4 (2025-05-21) Full Changelog: [v0.1.0-alpha.3...v0.1.0-alpha.4](https://github.com/Scan-Documents/node-sdk/compare/v0.1.0-alpha.3...v0.1.0-alpha.4) diff --git a/README.md b/README.md index 9aec1ac..7c31c42 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Scan Documents TypeScript API Library -[![NPM version](https://img.shields.io/npm/v/scan-documents.svg)](https://npmjs.org/package/scan-documents) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/scan-documents) +[![NPM version]()](https://npmjs.org/package/scan-documents) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/scan-documents) This library provides convenient access to the Scan Documents REST API from server-side TypeScript or JavaScript. @@ -26,11 +26,7 @@ const client = new ScanDocuments({ apiKey: process.env['SCAN_DOCUMENTS_API_KEY'], // This is the default and can be omitted }); -async function main() { - const file = await client.files.upload({ file: fs.createReadStream('path/to/file'), name: 'REPLACE_ME' }); -} - -main(); +const file = await client.files.upload({ file: fs.createReadStream('path/to/file'), name: 'REPLACE_ME' }); ``` ### Request & Response types @@ -45,15 +41,11 @@ const client = new ScanDocuments({ apiKey: process.env['SCAN_DOCUMENTS_API_KEY'], // This is the default and can be omitted }); -async function main() { - const params: ScanDocuments.FileUploadParams = { - file: fs.createReadStream('path/to/file'), - name: 'REPLACE_ME', - }; - const file: ScanDocuments.File = await client.files.upload(params); -} - -main(); +const params: ScanDocuments.FileUploadParams = { + file: fs.createReadStream('path/to/file'), + name: 'REPLACE_ME', +}; +const file: ScanDocuments.File = await client.files.upload(params); ``` Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors. @@ -115,21 +107,17 @@ a subclass of `APIError` will be thrown: ```ts -async function main() { - const file = await client.files - .upload({ file: fs.createReadStream('path/to/file'), name: 'REPLACE_ME' }) - .catch(async (err) => { - if (err instanceof ScanDocuments.APIError) { - console.log(err.status); // 400 - console.log(err.name); // BadRequestError - console.log(err.headers); // {server: 'nginx', ...} - } else { - throw err; - } - }); -} - -main(); +const file = await client.files + .upload({ file: fs.createReadStream('path/to/file'), name: 'REPLACE_ME' }) + .catch(async (err) => { + if (err instanceof ScanDocuments.APIError) { + console.log(err.status); // 400 + console.log(err.name); // BadRequestError + console.log(err.headers); // {server: 'nginx', ...} + } else { + throw err; + } + }); ``` Error codes are as follows: @@ -291,9 +279,8 @@ parameter. This library doesn't validate at runtime that the request matches the send will be sent as-is. ```ts -client.foo.create({ - foo: 'my_param', - bar: 12, +client.files.upload({ + // ... // @ts-expect-error baz is not yet public baz: 'undocumented option', }); diff --git a/SECURITY.md b/SECURITY.md index cf67981..fb762fc 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -16,11 +16,11 @@ before making any information public. ## Reporting Non-SDK Related Security Issues If you encounter security issues that are not directly related to SDKs but pertain to the services -or products provided by Scan Documents please follow the respective company's security reporting guidelines. +or products provided by Scan Documents, please follow the respective company's security reporting guidelines. ### Scan Documents Terms and Policies -Please contact support@scan-documents.com for any questions or concerns regarding security of our services. +Please contact support@scan-documents.com for any questions or concerns regarding the security of our services. --- diff --git a/api.md b/api.md index 884d588..1984514 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,9 @@ +# Shared + +Types: + +- JsonSchemaSpec + # Files Types: @@ -48,7 +54,6 @@ Types: - ExtractTextRequest - ExtractTextResponse - ImageFromTaskResponse -- JsonSchemaSpec - WarpRequest - WarpResponse diff --git a/bin/publish-npm b/bin/publish-npm index 4c21181..fa2243d 100644 --- a/bin/publish-npm +++ b/bin/publish-npm @@ -4,19 +4,55 @@ set -eux npm config set '//registry.npmjs.org/:_authToken' "$NPM_TOKEN" -# Build the project yarn build - -# Navigate to the dist directory cd dist -# Get the version from package.json -VERSION="$(node -p "require('./package.json').version")" +# Get package name and version from package.json +PACKAGE_NAME="$(jq -r -e '.name' ./package.json)" +VERSION="$(jq -r -e '.version' ./package.json)" + +# Get latest version from npm +# +# If the package doesn't exist, npm will return: +# { +# "error": { +# "code": "E404", +# "summary": "Unpublished on 2025-06-05T09:54:53.528Z", +# "detail": "'the_package' is not in this registry..." +# } +# } +NPM_INFO="$(npm view "$PACKAGE_NAME" version --json 2>/dev/null || true)" + +# Check if we got an E404 error +if echo "$NPM_INFO" | jq -e '.error.code == "E404"' > /dev/null 2>&1; then + # Package doesn't exist yet, no last version + LAST_VERSION="" +elif echo "$NPM_INFO" | jq -e '.error' > /dev/null 2>&1; then + # Report other errors + echo "ERROR: npm returned unexpected data:" + echo "$NPM_INFO" + exit 1 +else + # Success - get the version + LAST_VERSION=$(echo "$NPM_INFO" | jq -r '.') # strip quotes +fi -# Extract the pre-release tag if it exists +# Check if current version is pre-release (e.g. alpha / beta / rc) +CURRENT_IS_PRERELEASE=false if [[ "$VERSION" =~ -([a-zA-Z]+) ]]; then - # Extract the part before any dot in the pre-release identifier - TAG="${BASH_REMATCH[1]}" + CURRENT_IS_PRERELEASE=true + CURRENT_TAG="${BASH_REMATCH[1]}" +fi + +# Check if last version is a stable release +LAST_IS_STABLE_RELEASE=true +if [[ -z "$LAST_VERSION" || "$LAST_VERSION" =~ -([a-zA-Z]+) ]]; then + LAST_IS_STABLE_RELEASE=false +fi + +# Use a corresponding alpha/beta tag if there already is a stable release and we're publishing a prerelease. +if $CURRENT_IS_PRERELEASE && $LAST_IS_STABLE_RELEASE; then + TAG="$CURRENT_TAG" else TAG="latest" fi diff --git a/eslint.config.mjs b/eslint.config.mjs index a4591a8..b386e66 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -10,7 +10,7 @@ export default tseslint.config( parserOptions: { sourceType: 'module' }, }, files: ['**/*.ts', '**/*.mts', '**/*.cts', '**/*.js', '**/*.mjs', '**/*.cjs'], - ignores: ['dist/**'], + ignores: ['dist/'], plugins: { '@typescript-eslint': tseslint.plugin, 'unused-imports': unusedImports, diff --git a/jest.config.ts b/jest.config.ts index 81d36c7..1d86472 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -15,6 +15,7 @@ const config: JestConfigWithTsJest = { '/dist/', '/deno/', '/deno_tests/', + '/packages/', ], testPathIgnorePatterns: ['scripts'], }; diff --git a/package.json b/package.json index ba12075..f3fe912 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scan-documents", - "version": "0.1.0-alpha.4", + "version": "0.1.0-alpha.5", "description": "The official TypeScript library for the Scan Documents API", "author": "Scan Documents ", "types": "dist/index.d.ts", @@ -34,7 +34,7 @@ "@typescript-eslint/eslint-plugin": "8.31.1", "@typescript-eslint/parser": "8.31.1", "eslint": "^9.20.1", - "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-prettier": "^5.4.1", "eslint-plugin-unused-imports": "^4.1.4", "iconv-lite": "^0.6.3", "jest": "^29.4.0", @@ -42,13 +42,10 @@ "publint": "^0.2.12", "ts-jest": "^29.1.0", "ts-node": "^10.5.0", - "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz", + "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.8/tsc-multi.tgz", "tsconfig-paths": "^4.0.0", "typescript": "5.8.3" }, - "resolutions": { - "synckit": "0.8.8" - }, "imports": { "scan-documents": ".", "scan-documents/*": "./src/*" diff --git a/packages/mcp-server/build b/packages/mcp-server/build index 146ea13..c9d265f 100644 --- a/packages/mcp-server/build +++ b/packages/mcp-server/build @@ -23,14 +23,10 @@ PKG_JSON_PATH=../../packages/mcp-server/package.json node ../../scripts/utils/ma node scripts/postprocess-dist-package-json.cjs # build to .js/.mjs/.d.ts files -npm exec tsc-multi +./node_modules/.bin/tsc-multi cp tsconfig.dist-src.json dist/src/tsconfig.json -# Add proper Node.js shebang to the top of the file -sed -i.bak '1s;^;#!/usr/bin/env node\n;' dist/index.js -rm dist/index.js.bak - chmod +x dist/index.js DIST_PATH=./dist PKG_IMPORT_PATH=scan-documents-mcp/ node ../../scripts/utils/postprocess-files.cjs diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index bd57562..4939d5b 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "scan-documents-mcp", - "version": "0.1.0-alpha.4", + "version": "0.1.0-alpha.5", "description": "The official MCP Server for the Scan Documents API", "author": "Scan Documents ", "types": "dist/index.d.ts", @@ -28,10 +28,10 @@ }, "dependencies": { "scan-documents": "file:../../dist/", - "@modelcontextprotocol/sdk": "^1.6.1", + "@modelcontextprotocol/sdk": "^1.11.5", "yargs": "^17.7.2", "@cloudflare/cabidela": "^0.2.4", - "zod": "^3.24.4", + "zod": "^3.25.20", "zod-to-json-schema": "^3.24.5" }, "bin": { @@ -49,7 +49,7 @@ "ts-jest": "^29.1.0", "ts-morph": "^19.0.0", "ts-node": "^10.5.0", - "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz", + "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.8/tsc-multi.tgz", "tsconfig-paths": "^4.0.0", "typescript": "5.8.3" }, diff --git a/packages/mcp-server/src/compat.ts b/packages/mcp-server/src/compat.ts index e4d48a4..ff0d6d4 100644 --- a/packages/mcp-server/src/compat.ts +++ b/packages/mcp-server/src/compat.ts @@ -19,6 +19,46 @@ export const defaultClientCapabilities: ClientCapabilities = { toolNameLength: undefined, }; +export type ClientType = 'openai-agents' | 'claude' | 'claude-code' | 'cursor'; + +// Client presets for compatibility +// Note that these could change over time as models get better, so this is +// a best effort. +export const knownClients: Record = { + 'openai-agents': { + topLevelUnions: false, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }, + claude: { + topLevelUnions: true, + validJson: false, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }, + 'claude-code': { + topLevelUnions: false, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }, + cursor: { + topLevelUnions: false, + validJson: true, + refs: false, + unions: false, + formats: false, + toolNameLength: 50, + }, +}; + /** * Attempts to parse strings into JSON objects */ @@ -148,7 +188,11 @@ export function removeTopLevelUnions(tool: Tool): Tool[] { }); } -function findUsedDefs(schema: JSONSchema, defs: Record): Record { +function findUsedDefs( + schema: JSONSchema, + defs: Record, + visited: Set = new Set(), +): Record { const usedDefs: Record = {}; if (typeof schema !== 'object' || schema === null) { @@ -160,9 +204,11 @@ function findUsedDefs(schema: JSONSchema, defs: Record): Rec if (refParts[0] === '#' && refParts[1] === '$defs' && refParts[2]) { const defName = refParts[2]; const def = defs[defName]; - if (def) { + if (def && !visited.has(schema.$ref)) { usedDefs[defName] = def; - Object.assign(usedDefs, findUsedDefs(def, defs)); + visited.add(schema.$ref); + Object.assign(usedDefs, findUsedDefs(def, defs, visited)); + visited.delete(schema.$ref); } } return usedDefs; @@ -170,13 +216,16 @@ function findUsedDefs(schema: JSONSchema, defs: Record): Rec for (const key in schema) { if (key !== '$defs' && typeof schema[key] === 'object' && schema[key] !== null) { - Object.assign(usedDefs, findUsedDefs(schema[key] as JSONSchema, defs)); + Object.assign(usedDefs, findUsedDefs(schema[key] as JSONSchema, defs, visited)); } } return usedDefs; } +// Export for testing +export { findUsedDefs }; + /** * Inlines all $refs in a schema, eliminating $defs. * If a circular reference is detected, the circular property is removed. diff --git a/packages/mcp-server/src/dynamic-tools.ts b/packages/mcp-server/src/dynamic-tools.ts index b15efd3..f6870b4 100644 --- a/packages/mcp-server/src/dynamic-tools.ts +++ b/packages/mcp-server/src/dynamic-tools.ts @@ -1,5 +1,5 @@ import ScanDocuments from 'scan-documents'; -import { Endpoint } from './tools'; +import { Endpoint, asTextContentResult, ToolCallResult } from './tools/types'; import { zodToJsonSchema } from 'zod-to-json-schema'; import { z } from 'zod'; import { Cabidela } from '@cloudflare/cabidela'; @@ -41,7 +41,10 @@ export function dynamicTools(endpoints: Endpoint[]): Endpoint[] { description: 'List or search for all endpoints in the Scan Documents TypeScript API', inputSchema: zodToInputSchema(listEndpointsSchema), }, - handler: async (client: ScanDocuments, args: Record | undefined) => { + handler: async ( + client: ScanDocuments, + args: Record | undefined, + ): Promise => { const query = args && listEndpointsSchema.parse(args).search_query?.trim(); const filteredEndpoints = @@ -49,15 +52,16 @@ export function dynamicTools(endpoints: Endpoint[]): Endpoint[] { endpoints.filter((endpoint) => { const fieldsToMatch = [ endpoint.tool.name, + endpoint.tool.description, endpoint.metadata.resource, endpoint.metadata.operation, ...endpoint.metadata.tags, ]; - return fieldsToMatch.some((field) => field.toLowerCase().includes(query.toLowerCase())); + return fieldsToMatch.some((field) => field && field.toLowerCase().includes(query.toLowerCase())); }) : endpoints; - return { + return asTextContentResult({ tools: filteredEndpoints.map(({ tool, metadata }) => ({ name: tool.name, description: tool.description, @@ -65,7 +69,7 @@ export function dynamicTools(endpoints: Endpoint[]): Endpoint[] { operation: metadata.operation, tags: metadata.tags, })), - }; + }); }, }; @@ -94,7 +98,7 @@ export function dynamicTools(endpoints: Endpoint[]): Endpoint[] { if (!endpoint) { throw new Error(`Endpoint ${endpointName} not found`); } - return endpoint.tool; + return asTextContentResult(endpoint.tool); }, }; @@ -119,7 +123,10 @@ export function dynamicTools(endpoints: Endpoint[]): Endpoint[] { 'Invoke an endpoint in the Scan Documents TypeScript API. Note: use the `list_api_endpoints` tool to get the list of endpoints and `get_api_endpoint_schema` tool to get the schema for an endpoint.', inputSchema: zodToInputSchema(invokeEndpointSchema), }, - handler: async (client: ScanDocuments, args: Record | undefined) => { + handler: async ( + client: ScanDocuments, + args: Record | undefined, + ): Promise => { if (!args) { throw new Error('No endpoint provided'); } @@ -144,7 +151,7 @@ export function dynamicTools(endpoints: Endpoint[]): Endpoint[] { throw new Error(`Invalid arguments for endpoint ${endpoint_name}:\n${error}`); } - return endpoint.handler(client, endpointArgs); + return await endpoint.handler(client, endpointArgs); }, }; diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts index 17e31ce..0621357 100644 --- a/packages/mcp-server/src/index.ts +++ b/packages/mcp-server/src/index.ts @@ -1,7 +1,9 @@ +#!/usr/bin/env node + import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { init, selectTools, server } from './server'; import { Endpoint, endpoints } from './tools'; -import { ParsedOptions, parseOptions } from './options'; +import { McpOptions, parseOptions } from './options'; async function main() { const options = parseOptionsOrError(); @@ -41,7 +43,7 @@ function parseOptionsOrError() { } } -function selectToolsOrError(endpoints: Endpoint[], options: ParsedOptions) { +function selectToolsOrError(endpoints: Endpoint[], options: McpOptions) { try { const includedTools = selectTools(endpoints, options); if (includedTools.length === 0) { diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts index 4a2d166..c075101 100644 --- a/packages/mcp-server/src/options.ts +++ b/packages/mcp-server/src/options.ts @@ -1,55 +1,19 @@ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { endpoints, Filter } from './tools'; -import { ClientCapabilities } from './compat'; +import { ClientCapabilities, knownClients, ClientType } from './compat'; -type ClientType = 'openai-agents' | 'claude' | 'claude-code' | 'cursor'; - -// Client presets for compatibility -// Note that these could change over time as models get better, so this is -// a best effort. -const CLIENT_PRESETS: Record = { - 'openai-agents': { - topLevelUnions: false, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }, - claude: { - topLevelUnions: true, - validJson: false, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }, - 'claude-code': { - topLevelUnions: false, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }, - cursor: { - topLevelUnions: false, - validJson: true, - refs: false, - unions: false, - formats: false, - toolNameLength: 50, - }, +export type CLIOptions = McpOptions & { + list: boolean; }; -export interface ParsedOptions { +export type McpOptions = { + client: ClientType | undefined; includeDynamicTools: boolean | undefined; includeAllTools: boolean | undefined; filters: Filter[]; - capabilities: ClientCapabilities; - list: boolean; -} + capabilities?: Partial; +}; const CAPABILITY_CHOICES = [ 'top-level-unions', @@ -80,7 +44,7 @@ function parseCapabilityValue(cap: string): { name: Capability; value?: number } return { name: cap as Capability }; } -export function parseOptions(): ParsedOptions { +export function parseOptions(): CLIOptions { const opts = yargs(hideBin(process.argv)) .option('tools', { type: 'string', @@ -141,7 +105,7 @@ export function parseOptions(): ParsedOptions { }) .option('client', { type: 'string', - choices: Object.keys(CLIENT_PRESETS), + choices: Object.keys(knownClients), description: 'Specify the MCP client being used', }) .option('capability', { @@ -229,14 +193,6 @@ export function parseOptions(): ParsedOptions { toolNameLength: undefined, }; - // Apply client preset if specified - if (typeof argv.client === 'string') { - const clientKey = argv.client as ClientType; - if (CLIENT_PRESETS[clientKey]) { - Object.assign(clientCapabilities, CLIENT_PRESETS[clientKey]); - } - } - // Apply individual capability overrides if (Array.isArray(argv.capability)) { for (const cap of argv.capability) { @@ -282,7 +238,9 @@ export function parseOptions(): ParsedOptions { const includeAllTools = explicitTools ? argv.tools?.includes('all') && !argv.noTools?.includes('all') : undefined; + const client = argv.client as ClientType; return { + client: client && knownClients[client] ? client : undefined, includeDynamicTools, includeAllTools, filters, @@ -323,7 +281,7 @@ Client Presets (--client): Presets like '--client=openai-agents' or '--client=cursor' automatically configure these capabilities based on current known limitations of those clients, simplifying setup. Current presets: -${JSON.stringify(CLIENT_PRESETS, null, 2)} +${JSON.stringify(knownClients, null, 2)} `; } diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index a943e9a..fa19ef1 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -4,22 +4,29 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { Endpoint, endpoints, HandlerFunction, query } from './tools'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool } from '@modelcontextprotocol/sdk/types.js'; +import { ClientOptions } from 'scan-documents'; import ScanDocuments from 'scan-documents'; import { applyCompatibilityTransformations, ClientCapabilities, defaultClientCapabilities, + knownClients, parseEmbeddedJSON, } from './compat'; import { dynamicTools } from './dynamic-tools'; -import { ParsedOptions } from './options'; +import { McpOptions } from './options'; + +export { McpOptions } from './options'; +export { ClientType } from './compat'; +export { Filter } from './tools'; +export { ClientOptions } from 'scan-documents'; export { endpoints } from './tools'; // Create server instance export const server = new McpServer( { name: 'scan_documents_api', - version: '0.1.0-alpha.4', + version: '0.1.0-alpha.5', }, { capabilities: { @@ -32,6 +39,21 @@ export const server = new McpServer( * Initializes the provided MCP Server with the given tools and handlers. * If not provided, the default client, tools and handlers will be used. */ +export function initMcpServer(params: { + server: Server | McpServer; + clientOptions: ClientOptions; + mcpOptions: McpOptions; + endpoints?: { tool: Tool; handler: HandlerFunction }[]; +}) { + const transformedEndpoints = selectTools(endpoints, params.mcpOptions); + const client = new ScanDocuments(params.clientOptions); + const capabilities = { + ...defaultClientCapabilities, + ...(params.mcpOptions.client ? knownClients[params.mcpOptions.client] : params.mcpOptions.capabilities), + }; + init({ server: params.server, client, endpoints: transformedEndpoints, capabilities }); +} + export function init(params: { server: Server | McpServer; client?: ScanDocuments; @@ -43,7 +65,7 @@ export function init(params: { const endpointMap = Object.fromEntries(providedEndpoints.map((endpoint) => [endpoint.tool.name, endpoint])); - const client = params.client || new ScanDocuments({}); + const client = params.client || new ScanDocuments({ defaultHeaders: { 'X-Stainless-MCP': 'true' } }); server.setRequestHandler(ListToolsRequestSchema, async () => { return { @@ -65,24 +87,27 @@ export function init(params: { /** * Selects the tools to include in the MCP Server based on the provided options. */ -export function selectTools(endpoints: Endpoint[], options: ParsedOptions) { +export function selectTools(endpoints: Endpoint[], options: McpOptions) { const filteredEndpoints = query(options.filters, endpoints); - const includedTools = filteredEndpoints; + let includedTools = filteredEndpoints; - if (options.includeAllTools && includedTools.length === 0) { - includedTools.push(...endpoints); - } - - if (options.includeDynamicTools) { - includedTools.push(...dynamicTools(endpoints)); - } - - if (includedTools.length === 0) { - includedTools.push(...endpoints); + if (includedTools.length > 0) { + if (options.includeDynamicTools) { + includedTools = dynamicTools(includedTools); + } + } else { + if (options.includeAllTools) { + includedTools = endpoints; + } else if (options.includeDynamicTools) { + includedTools = dynamicTools(endpoints); + } else { + includedTools = endpoints; + } } - return applyCompatibilityTransformations(includedTools, options.capabilities); + const capabilities = { ...defaultClientCapabilities, ...options.capabilities }; + return applyCompatibilityTransformations(includedTools, capabilities); } /** @@ -97,17 +122,9 @@ export async function executeHandler( ) { const options = { ...defaultClientCapabilities, ...compatibilityOptions }; if (options.validJson && args) { - args = args = parseEmbeddedJSON(args, tool.inputSchema); + args = parseEmbeddedJSON(args, tool.inputSchema); } - const result = await handler(client, args || {}); - return { - content: [ - { - type: 'text', - text: JSON.stringify(result, null, 2), - }, - ], - }; + return await handler(client, args || {}); } export const readEnv = (env: string): string | undefined => { diff --git a/packages/mcp-server/src/tools/events/list-events.ts b/packages/mcp-server/src/tools/events/list-events.ts index 09c1b70..6dc693f 100644 --- a/packages/mcp-server/src/tools/events/list-events.ts +++ b/packages/mcp-server/src/tools/events/list-events.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'events', operation: 'read', tags: [], + httpMethod: 'get', + httpPath: '/v1/events', + operationId: 'listEvents', }; export const tool: Tool = { @@ -26,9 +31,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.events.list(body); + return asTextContentResult(await client.events.list(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/delete-files.ts b/packages/mcp-server/src/tools/files/delete-files.ts index bfa290b..55141de 100644 --- a/packages/mcp-server/src/tools/files/delete-files.ts +++ b/packages/mcp-server/src/tools/files/delete-files.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'files', operation: 'write', tags: [], + httpMethod: 'delete', + httpPath: '/v1/files/{id}', + operationId: 'deleteFile', }; export const tool: Tool = { @@ -23,9 +28,10 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const { id, ...body } = args as any; - return client.files.delete(id); + await client.files.delete(id); + return asTextContentResult('Successful tool call'); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/download-files.ts b/packages/mcp-server/src/tools/files/download-files.ts index cd8c759..564189c 100644 --- a/packages/mcp-server/src/tools/files/download-files.ts +++ b/packages/mcp-server/src/tools/files/download-files.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asBinaryContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'files', operation: 'read', tags: [], + httpMethod: 'get', + httpPath: '/v1/files/{id}/download', + operationId: 'downloadFile', }; export const tool: Tool = { @@ -23,9 +28,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const { id, ...body } = args as any; - return client.files.download(id); + return asBinaryContentResult(await client.files.download(id)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/list-files.ts b/packages/mcp-server/src/tools/files/list-files.ts index 6482460..4419839 100644 --- a/packages/mcp-server/src/tools/files/list-files.ts +++ b/packages/mcp-server/src/tools/files/list-files.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'files', operation: 'read', tags: [], + httpMethod: 'get', + httpPath: '/v1/files', + operationId: 'listFiles', }; export const tool: Tool = { @@ -28,9 +33,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.files.list(body); + return asTextContentResult(await client.files.list(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/retrieve-files.ts b/packages/mcp-server/src/tools/files/retrieve-files.ts index de4ab3e..9b35a64 100644 --- a/packages/mcp-server/src/tools/files/retrieve-files.ts +++ b/packages/mcp-server/src/tools/files/retrieve-files.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'files', operation: 'read', tags: [], + httpMethod: 'get', + httpPath: '/v1/files/{id}', + operationId: 'getFile', }; export const tool: Tool = { @@ -23,9 +28,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const { id, ...body } = args as any; - return client.files.retrieve(id); + return asTextContentResult(await client.files.retrieve(id)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/upload-files.ts b/packages/mcp-server/src/tools/files/upload-files.ts index 6bc76b4..af089e9 100644 --- a/packages/mcp-server/src/tools/files/upload-files.ts +++ b/packages/mcp-server/src/tools/files/upload-files.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'files', operation: 'write', tags: [], + httpMethod: 'post', + httpPath: '/v1/files', + operationId: 'uploadFile', }; export const tool: Tool = { @@ -28,9 +33,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.files.upload(body); + return asTextContentResult(await client.files.upload(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/image-operations/apply-effect-image-operations.ts b/packages/mcp-server/src/tools/image-operations/apply-effect-image-operations.ts index 6d2ac86..02c917c 100644 --- a/packages/mcp-server/src/tools/image-operations/apply-effect-image-operations.ts +++ b/packages/mcp-server/src/tools/image-operations/apply-effect-image-operations.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'image_operations', operation: 'write', tags: [], + httpMethod: 'post', + httpPath: '/v1/image-operations/apply-effect', + operationId: 'applyEffect', }; export const tool: Tool = { @@ -23,7 +28,12 @@ export const tool: Tool = { }, input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', + }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', }, name: { type: 'string', @@ -33,9 +43,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.imageOperations.applyEffect(body); + return asTextContentResult(await client.imageOperations.applyEffect(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/image-operations/convert-image-operations.ts b/packages/mcp-server/src/tools/image-operations/convert-image-operations.ts index 1cf1099..f642b09 100644 --- a/packages/mcp-server/src/tools/image-operations/convert-image-operations.ts +++ b/packages/mcp-server/src/tools/image-operations/convert-image-operations.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'image_operations', operation: 'write', tags: [], + httpMethod: 'post', + httpPath: '/v1/image-operations/convert', + operationId: 'convertImage', }; export const tool: Tool = { @@ -21,13 +26,18 @@ export const tool: Tool = { properties: { input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', }, target_format: { type: 'string', description: 'The format to convert the image to.', enum: ['image/png'], }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', + }, name: { type: 'string', description: 'The name of the file', @@ -39,7 +49,7 @@ export const tool: Tool = { properties: { input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', }, quality: { type: 'number', @@ -50,6 +60,11 @@ export const tool: Tool = { description: 'The format to convert the image to.', enum: ['image/jpeg'], }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', + }, name: { type: 'string', description: 'The name of the file', @@ -61,7 +76,7 @@ export const tool: Tool = { properties: { input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', }, quality: { type: 'number', @@ -72,6 +87,11 @@ export const tool: Tool = { description: 'The format to convert the image to.', enum: ['image/webp'], }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', + }, name: { type: 'string', description: 'The name of the file', @@ -82,9 +102,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.imageOperations.convert(body); + return asTextContentResult(await client.imageOperations.convert(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/image-operations/detect-documents-image-operations.ts b/packages/mcp-server/src/tools/image-operations/detect-documents-image-operations.ts index b4c59a9..905f20f 100644 --- a/packages/mcp-server/src/tools/image-operations/detect-documents-image-operations.ts +++ b/packages/mcp-server/src/tools/image-operations/detect-documents-image-operations.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'image_operations', operation: 'write', tags: [], + httpMethod: 'post', + httpPath: '/v1/image-operations/detect-documents', + operationId: 'detectDocuments', }; export const tool: Tool = { @@ -18,15 +23,20 @@ export const tool: Tool = { properties: { input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', + }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', }, }, }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.imageOperations.detectDocuments(body); + return asTextContentResult(await client.imageOperations.detectDocuments(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/image-operations/extract-text-image-operations.ts b/packages/mcp-server/src/tools/image-operations/extract-text-image-operations.ts index 3c13c7f..2755a04 100644 --- a/packages/mcp-server/src/tools/image-operations/extract-text-image-operations.ts +++ b/packages/mcp-server/src/tools/image-operations/extract-text-image-operations.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'image_operations', operation: 'write', tags: [], + httpMethod: 'post', + httpPath: '/v1/image-operations/extract-text', + operationId: 'extractText', }; export const tool: Tool = { @@ -26,7 +31,12 @@ export const tool: Tool = { }, input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', + }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', }, }, }, @@ -40,7 +50,12 @@ export const tool: Tool = { }, input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', + }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', }, }, }, @@ -54,7 +69,12 @@ export const tool: Tool = { }, input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', + }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', }, }, }, @@ -68,40 +88,15 @@ export const tool: Tool = { }, input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', }, schema: { - type: 'object', + $ref: '#/$defs/json_schema_spec', + }, + callback_url: { + type: 'string', description: - "An OpenAPI schema object describing the expected JSON structure. Required if format is 'json'.", - properties: { - description: { - type: 'string', - }, - example: { - type: 'object', - }, - format: { - type: 'string', - }, - items: { - $ref: '#/$defs/json_schema_spec', - }, - properties: { - type: 'object', - }, - required: { - type: 'array', - items: { - type: 'string', - }, - }, - type: { - type: 'string', - enum: ['string', 'number', 'integer', 'boolean', 'array', 'object'], - }, - }, - required: [], + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', }, }, }, @@ -109,6 +104,8 @@ export const tool: Tool = { $defs: { json_schema_spec: { type: 'object', + description: + "An OpenAPI schema object describing the expected JSON structure. Required if format is 'json'.", properties: { description: { type: 'string', @@ -142,9 +139,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.imageOperations.extractText(body); + return asTextContentResult(await client.imageOperations.extractText(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/image-operations/warp-image-operations.ts b/packages/mcp-server/src/tools/image-operations/warp-image-operations.ts index 6e546f9..90d6c92 100644 --- a/packages/mcp-server/src/tools/image-operations/warp-image-operations.ts +++ b/packages/mcp-server/src/tools/image-operations/warp-image-operations.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'image_operations', operation: 'write', tags: [], + httpMethod: 'post', + httpPath: '/v1/image-operations/warp', + operationId: 'warpImage', }; export const tool: Tool = { @@ -19,7 +24,7 @@ export const tool: Tool = { properties: { input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', }, vertices: { type: 'array', @@ -28,6 +33,11 @@ export const tool: Tool = { type: 'object', }, }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', + }, name: { type: 'string', description: 'The name of the file', @@ -36,9 +46,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.imageOperations.warp(body); + return asTextContentResult(await client.imageOperations.warp(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/index.ts b/packages/mcp-server/src/tools/index.ts index 4ab0cd3..972f773 100644 --- a/packages/mcp-server/src/tools/index.ts +++ b/packages/mcp-server/src/tools/index.ts @@ -1,7 +1,8 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import ScanDocuments from 'scan-documents'; -import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import { Metadata, Endpoint, HandlerFunction } from './types'; + +export { Metadata, Endpoint, HandlerFunction }; import retrieve_files from './files/retrieve-files'; import list_files from './files/list-files'; @@ -21,23 +22,6 @@ import merge_pdf_operations from './pdf-operations/merge-pdf-operations'; import render_pdf_operations from './pdf-operations/render-pdf-operations'; import split_pdf_operations from './pdf-operations/split-pdf-operations'; -export type HandlerFunction = ( - client: ScanDocuments, - args: Record | undefined, -) => Promise; - -export type Metadata = { - resource: string; - operation: 'read' | 'write'; - tags: string[]; -}; - -export type Endpoint = { - metadata: Metadata; - tool: Tool; - handler: HandlerFunction; -}; - export const endpoints: Endpoint[] = []; function addEndpoint(endpoint: Endpoint) { @@ -86,9 +70,10 @@ export function query(filters: Filter[], endpoints: Endpoint[]): Endpoint[] { }); // Check if any filters didn't match - if (unmatchedFilters.size > 0) { + const unmatched = Array.from(unmatchedFilters).filter((f) => f.type === 'tool' || f.type === 'resource'); + if (unmatched.length > 0) { throw new Error( - `The following filters did not match any endpoints: ${[...unmatchedFilters] + `The following filters did not match any endpoints: ${unmatched .map((f) => `${f.type}=${f.value}`) .join(', ')}`, ); diff --git a/packages/mcp-server/src/tools/pdf-operations/extract-pages-pdf-operations.ts b/packages/mcp-server/src/tools/pdf-operations/extract-pages-pdf-operations.ts index 7147b8c..bee5b28 100644 --- a/packages/mcp-server/src/tools/pdf-operations/extract-pages-pdf-operations.ts +++ b/packages/mcp-server/src/tools/pdf-operations/extract-pages-pdf-operations.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'pdf_operations', operation: 'write', tags: [], + httpMethod: 'post', + httpPath: '/v1/pdf-operations/extract-pages', + operationId: 'extractPdfPages', }; export const tool: Tool = { @@ -18,12 +23,17 @@ export const tool: Tool = { properties: { input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', }, pages: { type: 'string', description: 'Page range (e.g., 2-7), a comma-separated list (e.g., 2,3,7) of pages.', }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', + }, name: { type: 'string', description: 'The name of the file', @@ -32,9 +42,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.pdfOperations.extractPages(body); + return asTextContentResult(await client.pdfOperations.extractPages(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/pdf-operations/merge-pdf-operations.ts b/packages/mcp-server/src/tools/pdf-operations/merge-pdf-operations.ts index d543d4f..2aeeab2 100644 --- a/packages/mcp-server/src/tools/pdf-operations/merge-pdf-operations.ts +++ b/packages/mcp-server/src/tools/pdf-operations/merge-pdf-operations.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'pdf_operations', operation: 'write', tags: [], + httpMethod: 'post', + httpPath: '/v1/pdf-operations/merge', + operationId: 'mergePdf', }; export const tool: Tool = { @@ -21,9 +26,14 @@ export const tool: Tool = { description: 'The list of ids of the files to be merged', items: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', }, }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', + }, name: { type: 'string', description: 'The name of the file', @@ -32,9 +42,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.pdfOperations.merge(body); + return asTextContentResult(await client.pdfOperations.merge(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/pdf-operations/render-pdf-operations.ts b/packages/mcp-server/src/tools/pdf-operations/render-pdf-operations.ts index 34e43c2..6629703 100644 --- a/packages/mcp-server/src/tools/pdf-operations/render-pdf-operations.ts +++ b/packages/mcp-server/src/tools/pdf-operations/render-pdf-operations.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'pdf_operations', operation: 'write', tags: [], + httpMethod: 'post', + httpPath: '/v1/pdf-operations/render', + operationId: 'renderPdf', }; export const tool: Tool = { @@ -18,7 +23,12 @@ export const tool: Tool = { properties: { input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', + }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', }, dpi: { type: 'integer', @@ -36,9 +46,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.pdfOperations.render(body); + return asTextContentResult(await client.pdfOperations.render(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/pdf-operations/split-pdf-operations.ts b/packages/mcp-server/src/tools/pdf-operations/split-pdf-operations.ts index 2aca4c6..89956cd 100644 --- a/packages/mcp-server/src/tools/pdf-operations/split-pdf-operations.ts +++ b/packages/mcp-server/src/tools/pdf-operations/split-pdf-operations.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'pdf_operations', operation: 'write', tags: [], + httpMethod: 'post', + httpPath: '/v1/pdf-operations/split', + operationId: 'splitPdf', }; export const tool: Tool = { @@ -18,7 +23,12 @@ export const tool: Tool = { properties: { input: { type: 'string', - description: 'The id of the file to operate on.', + description: 'The id of the file or task to operate on.', + }, + callback_url: { + type: 'string', + description: + 'The URL to call when the task is completed or failed. If you want to receive events, you probably prefer to use `webhooks` instead.', }, name: { type: 'string', @@ -28,9 +38,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.pdfOperations.split(body); + return asTextContentResult(await client.pdfOperations.split(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/tasks/list-tasks.ts b/packages/mcp-server/src/tools/tasks/list-tasks.ts index 1d0182f..d57788e 100644 --- a/packages/mcp-server/src/tools/tasks/list-tasks.ts +++ b/packages/mcp-server/src/tools/tasks/list-tasks.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'tasks', operation: 'read', tags: [], + httpMethod: 'get', + httpPath: '/v1/tasks', + operationId: 'listTasks', }; export const tool: Tool = { @@ -26,9 +31,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const body = args as any; - return client.tasks.list(body); + return asTextContentResult(await client.tasks.list(body)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/tasks/retrieve-tasks.ts b/packages/mcp-server/src/tools/tasks/retrieve-tasks.ts index 3b422de..071d89f 100644 --- a/packages/mcp-server/src/tools/tasks/retrieve-tasks.ts +++ b/packages/mcp-server/src/tools/tasks/retrieve-tasks.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { asTextContentResult } from 'scan-documents-mcp/tools/types'; + import { Tool } from '@modelcontextprotocol/sdk/types.js'; import type { Metadata } from '../'; import ScanDocuments from 'scan-documents'; @@ -8,6 +10,9 @@ export const metadata: Metadata = { resource: 'tasks', operation: 'read', tags: [], + httpMethod: 'get', + httpPath: '/v1/tasks/{id}', + operationId: 'getTask', }; export const tool: Tool = { @@ -24,9 +29,9 @@ export const tool: Tool = { }, }; -export const handler = (client: ScanDocuments, args: Record | undefined) => { +export const handler = async (client: ScanDocuments, args: Record | undefined) => { const { id, ...body } = args as any; - return client.tasks.retrieve(id); + return asTextContentResult(await client.tasks.retrieve(id)); }; export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/types.ts b/packages/mcp-server/src/tools/types.ts new file mode 100644 index 0000000..d3d1169 --- /dev/null +++ b/packages/mcp-server/src/tools/types.ts @@ -0,0 +1,103 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ScanDocuments from 'scan-documents'; +import { Tool } from '@modelcontextprotocol/sdk/types.js'; + +type TextContentBlock = { + type: 'text'; + text: string; +}; + +type ImageContentBlock = { + type: 'image'; + data: string; + mimeType: string; +}; + +type AudioContentBlock = { + type: 'audio'; + data: string; + mimeType: string; +}; + +type ResourceContentBlock = { + type: 'resource'; + resource: + | { + uri: string; + mimeType: string; + text: string; + } + | { + uri: string; + mimeType: string; + blob: string; + }; +}; + +export type ContentBlock = TextContentBlock | ImageContentBlock | AudioContentBlock | ResourceContentBlock; + +export type ToolCallResult = { + content: ContentBlock[]; + isError?: boolean; +}; + +export type HandlerFunction = ( + client: ScanDocuments, + args: Record | undefined, +) => Promise; + +export function asTextContentResult(result: Object): ToolCallResult { + return { + content: [ + { + type: 'text', + text: JSON.stringify(result, null, 2), + }, + ], + }; +} + +export async function asBinaryContentResult(response: Response): Promise { + const blob = await response.blob(); + const mimeType = blob.type; + const data = Buffer.from(await blob.arrayBuffer()).toString('base64'); + if (mimeType.startsWith('image/')) { + return { + content: [{ type: 'image', mimeType, data }], + }; + } else if (mimeType.startsWith('audio/')) { + return { + content: [{ type: 'audio', mimeType, data }], + }; + } else { + return { + content: [ + { + type: 'resource', + resource: { + // We must give a URI, even though this isn't actually an MCP resource. + uri: 'resource://tool-response', + mimeType, + blob: data, + }, + }, + ], + }; + } +} + +export type Metadata = { + resource: string; + operation: 'read' | 'write'; + tags: string[]; + httpMethod?: string; + httpPath?: string; + operationId?: string; +}; + +export type Endpoint = { + metadata: Metadata; + tool: Tool; + handler: HandlerFunction; +}; diff --git a/packages/mcp-server/tests/compat.test.ts b/packages/mcp-server/tests/compat.test.ts index 034b35c..d6272f6 100644 --- a/packages/mcp-server/tests/compat.test.ts +++ b/packages/mcp-server/tests/compat.test.ts @@ -5,6 +5,7 @@ import { inlineRefs, applyCompatibilityTransformations, removeFormats, + findUsedDefs, } from '../src/compat'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import { JSONSchema } from '../src/compat'; @@ -316,6 +317,103 @@ describe('removeAnyOf', () => { }); }); +describe('findUsedDefs', () => { + it('should handle circular references without stack overflow', () => { + const defs = { + person: { + type: 'object', + properties: { + name: { type: 'string' }, + friend: { $ref: '#/$defs/person' }, // Circular reference + }, + }, + }; + + const schema = { + type: 'object', + properties: { + user: { $ref: '#/$defs/person' }, + }, + }; + + // This should not throw a stack overflow error + expect(() => { + const result = findUsedDefs(schema, defs); + expect(result).toHaveProperty('person'); + }).not.toThrow(); + }); + + it('should handle indirect circular references without stack overflow', () => { + const defs = { + node: { + type: 'object', + properties: { + value: { type: 'string' }, + child: { $ref: '#/$defs/childNode' }, + }, + }, + childNode: { + type: 'object', + properties: { + value: { type: 'string' }, + parent: { $ref: '#/$defs/node' }, // Indirect circular reference + }, + }, + }; + + const schema = { + type: 'object', + properties: { + root: { $ref: '#/$defs/node' }, + }, + }; + + // This should not throw a stack overflow error + expect(() => { + const result = findUsedDefs(schema, defs); + expect(result).toHaveProperty('node'); + expect(result).toHaveProperty('childNode'); + }).not.toThrow(); + }); + + it('should find all used definitions in non-circular schemas', () => { + const defs = { + user: { + type: 'object', + properties: { + name: { type: 'string' }, + address: { $ref: '#/$defs/address' }, + }, + }, + address: { + type: 'object', + properties: { + street: { type: 'string' }, + city: { type: 'string' }, + }, + }, + unused: { + type: 'object', + properties: { + data: { type: 'string' }, + }, + }, + }; + + const schema = { + type: 'object', + properties: { + person: { $ref: '#/$defs/user' }, + }, + }; + + const result = findUsedDefs(schema, defs); + expect(result).toHaveProperty('user'); + expect(result).toHaveProperty('address'); + expect(result).not.toHaveProperty('unused'); + }); +}); + describe('inlineRefs', () => { it('should return the original schema if it does not contain $refs', () => { const schema: JSONSchema = { diff --git a/packages/mcp-server/tests/dynamic-tools.test.ts b/packages/mcp-server/tests/dynamic-tools.test.ts index c74ef4a..08963af 100644 --- a/packages/mcp-server/tests/dynamic-tools.test.ts +++ b/packages/mcp-server/tests/dynamic-tools.test.ts @@ -13,13 +13,16 @@ describe('dynamicTools', () => { const tools = dynamicTools(endpoints); - describe('list_api_endpoints', () => { - const listEndpointsTool = tools.find((tool) => tool.tool.name === 'list_api_endpoints'); + const toolsMap = { + list_api_endpoints: toolOrError('list_api_endpoints'), + get_api_endpoint_schema: toolOrError('get_api_endpoint_schema'), + invoke_api_endpoint: toolOrError('invoke_api_endpoint'), + }; + describe('list_api_endpoints', () => { it('should return all endpoints when no search query is provided', async () => { - if (!listEndpointsTool) fail('list_api_endpoints tool not found'); - - const result = await listEndpointsTool.handler(fakeClient, {}); + const content = await toolsMap.list_api_endpoints.handler(fakeClient, {}); + const result = JSON.parse(content.content[0].text); expect(result.tools).toHaveLength(endpoints.length); expect(result.tools.map((t: { name: string }) => t.name)).toContain('test_read_endpoint'); @@ -29,36 +32,32 @@ describe('dynamicTools', () => { }); it('should filter endpoints by name', async () => { - if (!listEndpointsTool) fail('list_api_endpoints tool not found'); - - const result = await listEndpointsTool.handler(fakeClient, { search_query: 'user' }); + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'user' }); + const result = JSON.parse(content.content[0].text); expect(result.tools).toHaveLength(1); expect(result.tools[0].name).toBe('user_endpoint'); }); it('should filter endpoints by resource', async () => { - if (!listEndpointsTool) fail('list_api_endpoints tool not found'); - - const result = await listEndpointsTool.handler(fakeClient, { search_query: 'admin' }); + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'admin' }); + const result = JSON.parse(content.content[0].text); expect(result.tools.some((t: { resource: string }) => t.resource === 'admin')).toBeTruthy(); }); it('should filter endpoints by tag', async () => { - if (!listEndpointsTool) fail('list_api_endpoints tool not found'); - - const result = await listEndpointsTool.handler(fakeClient, { search_query: 'admin' }); + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'admin' }); + const result = JSON.parse(content.content[0].text); expect(result.tools.some((t: { tags: string[] }) => t.tags.includes('admin'))).toBeTruthy(); }); it('should be case insensitive in search', async () => { - if (!listEndpointsTool) fail('list_api_endpoints tool not found'); + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'ADMIN' }); + const result = JSON.parse(content.content[0].text); - const result = await listEndpointsTool.handler(fakeClient, { search_query: 'ADMIN' }); - - expect(result.tools.length).toBeGreaterThan(0); + expect(result.tools.length).toBe(2); result.tools.forEach((tool: { name: string; resource: string; tags: string[] }) => { expect( tool.name.toLowerCase().includes('admin') || @@ -67,46 +66,58 @@ describe('dynamicTools', () => { ).toBeTruthy(); }); }); + + it('should filter endpoints by description', async () => { + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { + search_query: 'Test endpoint for user_endpoint', + }); + const result = JSON.parse(content.content[0].text); + + expect(result.tools).toHaveLength(1); + expect(result.tools[0].name).toBe('user_endpoint'); + expect(result.tools[0].description).toBe('Test endpoint for user_endpoint'); + }); + + it('should filter endpoints by partial description match', async () => { + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { + search_query: 'endpoint for user', + }); + const result = JSON.parse(content.content[0].text); + + expect(result.tools).toHaveLength(1); + expect(result.tools[0].name).toBe('user_endpoint'); + }); }); describe('get_api_endpoint_schema', () => { - const getEndpointSchemaTool = tools.find((tool) => tool.tool.name === 'get_api_endpoint_schema'); - it('should return schema for existing endpoint', async () => { - if (!getEndpointSchemaTool) fail('get_api_endpoint_schema tool not found'); - - const result = await getEndpointSchemaTool.handler(fakeClient, { endpoint: 'test_read_endpoint' }); + const content = await toolsMap.get_api_endpoint_schema.handler(fakeClient, { + endpoint: 'test_read_endpoint', + }); + const result = JSON.parse(content.content[0].text); expect(result).toEqual(endpoints[0]?.tool); }); it('should throw error for non-existent endpoint', async () => { - if (!getEndpointSchemaTool) fail('get_api_endpoint_schema tool not found'); - await expect( - getEndpointSchemaTool.handler(fakeClient, { endpoint: 'non_existent_endpoint' }), + toolsMap.get_api_endpoint_schema.handler(fakeClient, { endpoint: 'non_existent_endpoint' }), ).rejects.toThrow('Endpoint non_existent_endpoint not found'); }); it('should throw error when no endpoint provided', async () => { - if (!getEndpointSchemaTool) fail('get_api_endpoint_schema tool not found'); - - await expect(getEndpointSchemaTool.handler(fakeClient, undefined)).rejects.toThrow( + await expect(toolsMap.get_api_endpoint_schema.handler(fakeClient, undefined)).rejects.toThrow( 'No endpoint provided', ); }); }); describe('invoke_api_endpoint', () => { - const invokeEndpointTool = tools.find((tool) => tool.tool.name === 'invoke_api_endpoint'); - it('should successfully invoke endpoint with valid arguments', async () => { - if (!invokeEndpointTool) fail('invoke_api_endpoint tool not found'); - const mockHandler = endpoints[0]?.handler as jest.Mock; mockHandler.mockClear(); - await invokeEndpointTool.handler(fakeClient, { + await toolsMap.invoke_api_endpoint.handler(fakeClient, { endpoint_name: 'test_read_endpoint', args: { testParam: 'test value' }, }); @@ -115,10 +126,8 @@ describe('dynamicTools', () => { }); it('should throw error for non-existent endpoint', async () => { - if (!invokeEndpointTool) fail('invoke_api_endpoint tool not found'); - await expect( - invokeEndpointTool.handler(fakeClient, { + toolsMap.invoke_api_endpoint.handler(fakeClient, { endpoint_name: 'non_existent_endpoint', args: { testParam: 'test value' }, }), @@ -126,22 +135,26 @@ describe('dynamicTools', () => { }); it('should throw error when no arguments provided', async () => { - if (!invokeEndpointTool) fail('invoke_api_endpoint tool not found'); - - await expect(invokeEndpointTool.handler(fakeClient, undefined)).rejects.toThrow('No endpoint provided'); + await expect(toolsMap.invoke_api_endpoint.handler(fakeClient, undefined)).rejects.toThrow( + 'No endpoint provided', + ); }); it('should throw error for invalid argument schema', async () => { - if (!invokeEndpointTool) fail('invoke_api_endpoint tool not found'); - await expect( - invokeEndpointTool.handler(fakeClient, { + toolsMap.invoke_api_endpoint.handler(fakeClient, { endpoint_name: 'test_read_endpoint', args: { wrongParam: 'test value' }, // Missing required testParam }), ).rejects.toThrow(/Invalid arguments for endpoint/); }); }); + + function toolOrError(name: string) { + const tool = tools.find((tool) => tool.tool.name === name); + if (!tool) throw new Error(`Tool ${name} not found`); + return tool; + } }); function makeEndpoint( diff --git a/packages/mcp-server/tests/options.test.ts b/packages/mcp-server/tests/options.test.ts index 9e2b2f8..f7661d6 100644 --- a/packages/mcp-server/tests/options.test.ts +++ b/packages/mcp-server/tests/options.test.ts @@ -77,14 +77,7 @@ describe('parseOptions', () => { const result = parseOptions(); - expect(result.capabilities).toEqual({ - topLevelUnions: false, - validJson: true, - refs: true, - unions: true, - formats: true, - toolNameLength: undefined, - }); + expect(result.client).toEqual('openai-agents'); cleanup(); }); diff --git a/packages/mcp-server/yarn.lock b/packages/mcp-server/yarn.lock index 9056173..9970ec3 100644 --- a/packages/mcp-server/yarn.lock +++ b/packages/mcp-server/yarn.lock @@ -585,9 +585,9 @@ "@jridgewell/sourcemap-codec" "^1.4.14" "@modelcontextprotocol/sdk@^1.6.1": - version "1.11.2" - resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.11.2.tgz#d81784c140d1a9cc937f61af9f071d8b78befe30" - integrity sha512-H9vwztj5OAqHg9GockCQC06k1natgcxWQSRpQcPJf6i5+MWBzfKkRtxGbjQf0X2ihii0ffLZCRGbYV2f2bjNCQ== + version "1.11.1" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.11.1.tgz#c7f4a1432872ef10130f5d9b0072060c17a3946b" + integrity sha512-9LfmxKTb1v+vUS1/emSk1f5ePmTLkb9Le9AxOB5T0XM59EUumwcS45z05h7aiZx3GI0Bl7mjb3FMEglYj+acuQ== dependencies: content-type "^1.0.5" cors "^2.8.5" @@ -3305,9 +3305,9 @@ ts-node@^10.5.0: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz": - version "1.1.4" - resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz#cbed459a9e902f5295ec3daaf1c7aa3b10427e55" +"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.7/tsc-multi.tgz": + version "1.1.7" + resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.7/tsc-multi.tgz#52f40adf8b808bd0b633346d11cc4a8aeea465cd" dependencies: debug "^4.3.7" fast-glob "^3.3.2" diff --git a/scripts/build b/scripts/build index 9d3d404..1edc9c7 100755 --- a/scripts/build +++ b/scripts/build @@ -31,14 +31,12 @@ fi node scripts/utils/make-dist-package-json.cjs > dist/package.json # build to .js/.mjs/.d.ts files -npm exec tsc-multi +./node_modules/.bin/tsc-multi # we need to patch index.js so that `new module.exports()` works for cjs backwards # compat. No way to get that from index.ts because it would cause compile errors # when building .mjs node scripts/utils/fix-index-exports.cjs cp tsconfig.dist-src.json dist/src/tsconfig.json -cp src/internal/shim-types.d.ts dist/internal/shim-types.d.ts -cp src/internal/shim-types.d.ts dist/internal/shim-types.d.mts node scripts/utils/postprocess-files.cjs diff --git a/src/client.ts b/src/client.ts index b47256b..ce47ee9 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5,7 +5,6 @@ import type { HTTPMethod, PromiseOrValue, MergedRequestInit, FinalizedRequestIni import { uuid4 } from './internal/utils/uuid'; import { validatePositiveInteger, isAbsoluteURL, safeJSON } from './internal/utils/values'; import { sleep } from './internal/utils/sleep'; -import { type Logger, type LogLevel, parseLogLevel } from './internal/utils/log'; export type { Logger, LogLevel } from './internal/utils/log'; import { castToError, isAbortError } from './internal/errors'; import type { APIResponseProps } from './internal/parse'; @@ -17,9 +16,6 @@ import * as Errors from './core/error'; import * as Uploads from './core/uploads'; import * as API from './resources/index'; import { APIPromise } from './core/api-promise'; -import { type Fetch } from './internal/builtin-types'; -import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers'; -import { FinalRequestOptions, RequestOptions } from './internal/request-options'; import { EventListParams, EventListResponse, Events } from './resources/events'; import { File, FileListParams, FileListResponse, FileUploadParams, Files } from './resources/files'; import { @@ -38,7 +34,6 @@ import { ImageOperationExtractTextParams, ImageOperationWarpParams, ImageOperations, - JsonSchemaSpec, WarpRequest, WarpResponse, } from './resources/image-operations'; @@ -55,8 +50,17 @@ import { Split, } from './resources/pdf-operations'; import { TaskListParams, TaskListResponse, TaskResponse, Tasks } from './resources/tasks'; +import { type Fetch } from './internal/builtin-types'; +import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers'; +import { FinalRequestOptions, RequestOptions } from './internal/request-options'; import { readEnv } from './internal/utils/env'; -import { formatRequestDetails, loggerFor } from './internal/utils/log'; +import { + type LogLevel, + type Logger, + formatRequestDetails, + loggerFor, + parseLogLevel, +} from './internal/utils/log'; import { isEmptyObj } from './internal/utils/values'; export interface ClientOptions { @@ -210,12 +214,20 @@ export class ScanDocuments { timeout: this.timeout, logger: this.logger, logLevel: this.logLevel, + fetch: this.fetch, fetchOptions: this.fetchOptions, apiKey: this.apiKey, ...options, }); } + /** + * Check whether the base URL is set to its default. + */ + #baseURLOverridden(): boolean { + return this.baseURL !== 'https://api.scan-documents.com'; + } + protected defaultQuery(): Record | undefined { return this._options.defaultQuery; } @@ -265,11 +277,16 @@ export class ScanDocuments { return Errors.APIError.generate(status, error, message, headers); } - buildURL(path: string, query: Record | null | undefined): string { + buildURL( + path: string, + query: Record | null | undefined, + defaultBaseURL?: string | undefined, + ): string { + const baseURL = (!this.#baseURLOverridden() && defaultBaseURL) || this.baseURL; const url = isAbsoluteURL(path) ? new URL(path) - : new URL(this.baseURL + (this.baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path)); + : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path)); const defaultQuery = this.defaultQuery(); if (!isEmptyObj(defaultQuery)) { @@ -610,9 +627,9 @@ export class ScanDocuments { { retryCount = 0 }: { retryCount?: number } = {}, ): { req: FinalizedRequestInit; url: string; timeout: number } { const options = { ...inputOptions }; - const { method, path, query } = options; + const { method, path, query, defaultBaseURL } = options; - const url = this.buildURL(path!, query as Record); + const url = this.buildURL(path!, query as Record, defaultBaseURL); if ('timeout' in options) validatePositiveInteger('timeout', options.timeout); options.timeout = options.timeout ?? this.timeout; const { bodyHeaders, body } = this.buildBody({ options }); @@ -771,7 +788,6 @@ export declare namespace ScanDocuments { type ExtractTextRequest as ExtractTextRequest, type ExtractTextResponse as ExtractTextResponse, type ImageFromTaskResponse as ImageFromTaskResponse, - type JsonSchemaSpec as JsonSchemaSpec, type WarpRequest as WarpRequest, type WarpResponse as WarpResponse, type ImageOperationApplyEffectParams as ImageOperationApplyEffectParams, @@ -793,4 +809,6 @@ export declare namespace ScanDocuments { type PdfOperationRenderParams as PdfOperationRenderParams, type PdfOperationSplitParams as PdfOperationSplitParams, }; + + export type JsonSchemaSpec = API.JsonSchemaSpec; } diff --git a/src/core/resource.ts b/src/core/resource.ts index 4762be3..3c20868 100644 --- a/src/core/resource.ts +++ b/src/core/resource.ts @@ -2,7 +2,7 @@ import type { ScanDocuments } from '../client'; -export class APIResource { +export abstract class APIResource { protected _client: ScanDocuments; constructor(client: ScanDocuments) { diff --git a/src/internal/detect-platform.ts b/src/internal/detect-platform.ts index c5e273b..e82d95c 100644 --- a/src/internal/detect-platform.ts +++ b/src/internal/detect-platform.ts @@ -85,10 +85,10 @@ const getPlatformProperties = (): PlatformProperties => { return { 'X-Stainless-Lang': 'js', 'X-Stainless-Package-Version': VERSION, - 'X-Stainless-OS': normalizePlatform((globalThis as any).process.platform), - 'X-Stainless-Arch': normalizeArch((globalThis as any).process.arch), + 'X-Stainless-OS': normalizePlatform((globalThis as any).process.platform ?? 'unknown'), + 'X-Stainless-Arch': normalizeArch((globalThis as any).process.arch ?? 'unknown'), 'X-Stainless-Runtime': 'node', - 'X-Stainless-Runtime-Version': (globalThis as any).process.version, + 'X-Stainless-Runtime-Version': (globalThis as any).process.version ?? 'unknown', }; } diff --git a/src/internal/headers.ts b/src/internal/headers.ts index 5cc03ce..c724a9d 100644 --- a/src/internal/headers.ts +++ b/src/internal/headers.ts @@ -1,5 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import { isReadonlyArray } from './utils/values'; + type HeaderValue = string | undefined | null; export type HeadersLike = | Headers @@ -9,7 +11,7 @@ export type HeadersLike = | null | NullableHeaders; -const brand_privateNullableHeaders = Symbol('brand.privateNullableHeaders'); +const brand_privateNullableHeaders = /* @__PURE__ */ Symbol('brand.privateNullableHeaders'); /** * @internal @@ -25,8 +27,6 @@ export type NullableHeaders = { nulls: Set; }; -const isArray = Array.isArray as (val: unknown) => val is readonly unknown[]; - function* iterateHeaders(headers: HeadersLike): IterableIterator { if (!headers) return; @@ -43,7 +43,7 @@ function* iterateHeaders(headers: HeadersLike): IterableIterator; if (headers instanceof Headers) { iter = headers.entries(); - } else if (isArray(headers)) { + } else if (isReadonlyArray(headers)) { iter = headers; } else { shouldClear = true; @@ -52,7 +52,7 @@ function* iterateHeaders(headers: HeadersLike): IterableIterator = keyof T extends never ? false : true; - -// @ts-ignore -type _ReadableStream = - // @ts-ignore - HasProperties extends true ? NodeJS.ReadableStream : ReadableStream; - -// @ts-ignore -declare const _ReadableStream: unknown extends typeof ReadableStream ? never : typeof ReadableStream; -export { _ReadableStream as ReadableStream }; diff --git a/src/internal/shim-types.ts b/src/internal/shim-types.ts new file mode 100644 index 0000000..8ddf7b0 --- /dev/null +++ b/src/internal/shim-types.ts @@ -0,0 +1,26 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +/** + * Shims for types that we can't always rely on being available globally. + * + * Note: these only exist at the type-level, there is no corresponding runtime + * version for any of these symbols. + */ + +type NeverToAny = T extends never ? any : T; + +/** @ts-ignore */ +type _DOMReadableStream = globalThis.ReadableStream; + +/** @ts-ignore */ +type _NodeReadableStream = import('stream/web').ReadableStream; + +type _ConditionalNodeReadableStream = + typeof globalThis extends { ReadableStream: any } ? never : _NodeReadableStream; + +type _ReadableStream = NeverToAny< + | ([0] extends [1 & _DOMReadableStream] ? never : _DOMReadableStream) + | ([0] extends [1 & _ConditionalNodeReadableStream] ? never : _ConditionalNodeReadableStream) +>; + +export type { _ReadableStream as ReadableStream }; diff --git a/src/internal/shims.ts b/src/internal/shims.ts index 05ee204..32dd47f 100644 --- a/src/internal/shims.ts +++ b/src/internal/shims.ts @@ -7,8 +7,8 @@ * messages in cases where an environment isn't fully supported. */ -import { type Fetch } from './builtin-types'; -import { type ReadableStream } from './shim-types'; +import type { Fetch } from './builtin-types'; +import type { ReadableStream } from './shim-types'; export function getDefaultFetch(): Fetch { if (typeof fetch !== 'undefined') { diff --git a/src/internal/uploads.ts b/src/internal/uploads.ts index bbf7eab..ce181b0 100644 --- a/src/internal/uploads.ts +++ b/src/internal/uploads.ts @@ -90,7 +90,7 @@ export const multipartFormRequestOptions = async ( return { ...opts, body: await createForm(opts.body, fetch) }; }; -const supportsFormDataMap = new WeakMap>(); +const supportsFormDataMap = /** @__PURE__ */ new WeakMap>(); /** * node-fetch doesn't support the global FormData object in recent node versions. Instead of sending @@ -138,7 +138,7 @@ export const createForm = async >( // We check for Blob not File because Bun.File doesn't inherit from File, // but they both inherit from Blob and have a `name` property at runtime. -const isNamedBlob = (value: object) => value instanceof Blob && 'name' in value; +const isNamedBlob = (value: unknown) => value instanceof Blob && 'name' in value; const isUploadable = (value: unknown) => typeof value === 'object' && diff --git a/src/internal/utils/log.ts b/src/internal/utils/log.ts index 2bae499..c3b661f 100644 --- a/src/internal/utils/log.ts +++ b/src/internal/utils/log.ts @@ -58,7 +58,7 @@ const noopLogger = { debug: noop, }; -let cachedLoggers = new WeakMap(); +let cachedLoggers = /** @__PURE__ */ new WeakMap(); export function loggerFor(client: ScanDocuments): Logger { const logger = client.logger; diff --git a/src/internal/utils/path.ts b/src/internal/utils/path.ts index b56185a..fd17c83 100644 --- a/src/internal/utils/path.ts +++ b/src/internal/utils/path.ts @@ -62,4 +62,4 @@ export const createPathTagFunction = (pathEncoder = encodeURIPath) => /** * URI-encodes path params and ensures no unsafe /./ or /../ path segments are introduced. */ -export const path = createPathTagFunction(encodeURIPath); +export const path = /* @__PURE__ */ createPathTagFunction(encodeURIPath); diff --git a/src/internal/utils/values.ts b/src/internal/utils/values.ts index 7c833c6..084f7a1 100644 --- a/src/internal/utils/values.ts +++ b/src/internal/utils/values.ts @@ -9,6 +9,9 @@ export const isAbsoluteURL = (url: string): boolean => { return startsWithSchemeRegexp.test(url); }; +export let isArray = (val: unknown): val is unknown[] => ((isArray = Array.isArray), isArray(val)); +export let isReadonlyArray = isArray as (val: unknown) => val is readonly unknown[]; + /** Returns an object if the given value isn't an object, otherwise returns as-is */ export function maybeObj(x: unknown): object { if (typeof x !== 'object') { diff --git a/src/resources/events.ts b/src/resources/events.ts index d48490f..ef452cd 100644 --- a/src/resources/events.ts +++ b/src/resources/events.ts @@ -237,7 +237,8 @@ export namespace EventListResponse { | 'merge' | 'extract-pages' | 'detect-documents' - | 'apply-effect'; + | 'apply-effect' + | 'scan'; } } @@ -325,6 +326,11 @@ export namespace EventListResponse { */ bounding_box: ImageOperationsAPI.BoundingBox; + /** + * The ID of the file containing the document + */ + file_id: string; + /** * The coordinates of the vertex inside the image */ @@ -523,7 +529,8 @@ export namespace EventListResponse { | 'merge' | 'extract-pages' | 'detect-documents' - | 'apply-effect'; + | 'apply-effect' + | 'scan'; } } @@ -574,7 +581,8 @@ export namespace EventListResponse { | 'merge' | 'extract-pages' | 'detect-documents' - | 'apply-effect'; + | 'apply-effect' + | 'scan'; result: Data.Result; } @@ -584,7 +592,7 @@ export namespace EventListResponse { /** * Additional details about the error */ - details: Record; + details: { [key: string]: unknown }; /** * The error message diff --git a/src/resources/image-operations.ts b/src/resources/image-operations.ts index fab8729..f1cfc32 100644 --- a/src/resources/image-operations.ts +++ b/src/resources/image-operations.ts @@ -2,6 +2,7 @@ import { APIResource } from '../core/resource'; import * as ImageOperationsAPI from './image-operations'; +import * as Shared from './shared'; import { APIPromise } from '../core/api-promise'; import { RequestOptions } from '../internal/request-options'; @@ -116,6 +117,11 @@ export namespace ApplyEffectResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -149,10 +155,16 @@ export namespace ApplyEffectResponse { effect: 'grayscale' | 'scanner' | 'black-background'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -170,6 +182,11 @@ export namespace ApplyEffectResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -203,10 +220,16 @@ export namespace ApplyEffectResponse { effect: 'grayscale' | 'scanner' | 'black-background'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -220,6 +243,11 @@ export namespace ApplyEffectResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -253,10 +281,16 @@ export namespace ApplyEffectResponse { effect: 'grayscale' | 'scanner' | 'black-background'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -270,6 +304,11 @@ export namespace ApplyEffectResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -303,10 +342,16 @@ export namespace ApplyEffectResponse { effect: 'grayscale' | 'scanner' | 'black-background'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -314,7 +359,7 @@ export namespace ApplyEffectResponse { } export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -356,7 +401,7 @@ export type ConvertRequest = export namespace ConvertRequest { export interface PngOptions { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -365,6 +410,12 @@ export namespace ConvertRequest { */ target_format: 'image/png'; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -373,7 +424,7 @@ export namespace ConvertRequest { export interface JpegOptions { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -387,6 +438,12 @@ export namespace ConvertRequest { */ target_format: 'image/jpeg'; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -395,7 +452,7 @@ export namespace ConvertRequest { export interface WebpOptions { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -409,6 +466,12 @@ export namespace ConvertRequest { */ target_format: 'image/webp'; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -432,6 +495,11 @@ export namespace ConvertResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -469,6 +537,11 @@ export namespace ConvertResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -500,6 +573,11 @@ export namespace ConvertResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -531,6 +609,11 @@ export namespace ConvertResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -558,7 +641,7 @@ export namespace ConvertResponse { export namespace FailedConvertTaskResponse { export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -593,6 +676,11 @@ export namespace DetectDocumentsResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -633,6 +721,11 @@ export namespace DetectDocumentsResponse { */ bounding_box: ImageOperationsAPI.BoundingBox; + /** + * The ID of the file containing the document + */ + file_id: string; + /** * The coordinates of the vertex inside the image */ @@ -647,6 +740,11 @@ export namespace DetectDocumentsResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -678,6 +776,11 @@ export namespace DetectDocumentsResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -709,6 +812,11 @@ export namespace DetectDocumentsResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -736,7 +844,7 @@ export namespace DetectDocumentsResponse { export namespace FailedConvertTaskResponse { export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -760,9 +868,15 @@ export namespace ExtractTextRequest { format: 'plain'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; } export interface Markdown { @@ -772,9 +886,15 @@ export namespace ExtractTextRequest { format: 'markdown'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; } export interface HTML { @@ -784,9 +904,15 @@ export namespace ExtractTextRequest { format: 'html'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; } export interface Json { @@ -796,7 +922,7 @@ export namespace ExtractTextRequest { format: 'json'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -804,31 +930,13 @@ export namespace ExtractTextRequest { * An OpenAPI schema object describing the expected JSON structure. Required if * format is 'json'. */ - schema: Json.Schema; - } + schema: Shared.JsonSchemaSpec; - export namespace Json { /** - * An OpenAPI schema object describing the expected JSON structure. Required if - * format is 'json'. + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. */ - export interface Schema { - description?: string; - - example?: unknown; - - format?: string; - - items?: ImageOperationsAPI.JsonSchemaSpec; - - properties?: Record; - - required?: Array; - - type?: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object'; - - [k: string]: unknown; - } + callback_url?: string; } } @@ -848,6 +956,11 @@ export namespace ExtractTextResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -890,6 +1003,11 @@ export namespace ExtractTextResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -924,6 +1042,11 @@ export namespace ExtractTextResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -958,6 +1081,11 @@ export namespace ExtractTextResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -988,7 +1116,7 @@ export namespace ExtractTextResponse { export namespace FailedExtractTextTaskResponse { export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -1046,30 +1174,12 @@ export namespace ImageFromTaskResponse { } } -export interface JsonSchemaSpec { - description?: string; - - example?: unknown; - - format?: string; - - items?: JsonSchemaSpec; - - properties?: Record; - - required?: Array; - - type?: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object'; - - [k: string]: unknown; -} - /** * Transform an image by warping it to a quadrilateral. */ export interface WarpRequest { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -1078,6 +1188,12 @@ export interface WarpRequest { */ vertices: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1100,6 +1216,11 @@ export namespace WarpResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1140,6 +1261,11 @@ export namespace WarpResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1174,6 +1300,11 @@ export namespace WarpResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1208,6 +1339,11 @@ export namespace WarpResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1238,7 +1374,7 @@ export namespace WarpResponse { export namespace FailedWarpTaskResponse { export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -1252,10 +1388,16 @@ export interface ImageOperationApplyEffectParams { effect: 'grayscale' | 'scanner' | 'black-background'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1270,7 +1412,7 @@ export type ImageOperationConvertParams = export declare namespace ImageOperationConvertParams { export interface PngOptions { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -1279,6 +1421,12 @@ export declare namespace ImageOperationConvertParams { */ target_format: 'image/png'; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1287,7 +1435,7 @@ export declare namespace ImageOperationConvertParams { export interface JpegOptions { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -1301,6 +1449,12 @@ export declare namespace ImageOperationConvertParams { */ target_format: 'image/jpeg'; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1309,7 +1463,7 @@ export declare namespace ImageOperationConvertParams { export interface WebpOptions { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -1323,6 +1477,12 @@ export declare namespace ImageOperationConvertParams { */ target_format: 'image/webp'; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1332,9 +1492,15 @@ export declare namespace ImageOperationConvertParams { export interface ImageOperationDetectDocumentsParams { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; } export type ImageOperationExtractTextParams = @@ -1351,9 +1517,15 @@ export declare namespace ImageOperationExtractTextParams { format: 'plain'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; } export interface Markdown { @@ -1363,9 +1535,15 @@ export declare namespace ImageOperationExtractTextParams { format: 'markdown'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; } export interface HTML { @@ -1375,9 +1553,15 @@ export declare namespace ImageOperationExtractTextParams { format: 'html'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; } export interface Json { @@ -1387,7 +1571,7 @@ export declare namespace ImageOperationExtractTextParams { format: 'json'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -1395,37 +1579,19 @@ export declare namespace ImageOperationExtractTextParams { * An OpenAPI schema object describing the expected JSON structure. Required if * format is 'json'. */ - schema: Json.Schema; - } + schema: Shared.JsonSchemaSpec; - export namespace Json { /** - * An OpenAPI schema object describing the expected JSON structure. Required if - * format is 'json'. + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. */ - export interface Schema { - description?: string; - - example?: unknown; - - format?: string; - - items?: ImageOperationsAPI.JsonSchemaSpec; - - properties?: Record; - - required?: Array; - - type?: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object'; - - [k: string]: unknown; - } + callback_url?: string; } } export interface ImageOperationWarpParams { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -1434,6 +1600,12 @@ export interface ImageOperationWarpParams { */ vertices: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1451,7 +1623,6 @@ export declare namespace ImageOperations { type ExtractTextRequest as ExtractTextRequest, type ExtractTextResponse as ExtractTextResponse, type ImageFromTaskResponse as ImageFromTaskResponse, - type JsonSchemaSpec as JsonSchemaSpec, type WarpRequest as WarpRequest, type WarpResponse as WarpResponse, type ImageOperationApplyEffectParams as ImageOperationApplyEffectParams, diff --git a/src/resources/index.ts b/src/resources/index.ts index 2d6e2ff..d5e18da 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -1,5 +1,6 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +export * from './shared'; export { Events, type EventListResponse, type EventListParams } from './events'; export { Files, type File, type FileListResponse, type FileListParams, type FileUploadParams } from './files'; export { @@ -13,7 +14,6 @@ export { type ExtractTextRequest, type ExtractTextResponse, type ImageFromTaskResponse, - type JsonSchemaSpec, type WarpRequest, type WarpResponse, type ImageOperationApplyEffectParams, diff --git a/src/resources/pdf-operations.ts b/src/resources/pdf-operations.ts index 035c49d..37cd311 100644 --- a/src/resources/pdf-operations.ts +++ b/src/resources/pdf-operations.ts @@ -128,6 +128,11 @@ export namespace ExtractPages { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -156,7 +161,7 @@ export namespace ExtractPages { export namespace CompletedExtractPagesTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -165,6 +170,12 @@ export namespace ExtractPages { */ pages: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -182,6 +193,11 @@ export namespace ExtractPages { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -210,7 +226,7 @@ export namespace ExtractPages { export namespace PendingExtractPagesTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -219,6 +235,12 @@ export namespace ExtractPages { */ pages: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -232,6 +254,11 @@ export namespace ExtractPages { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -260,7 +287,7 @@ export namespace ExtractPages { export namespace ProcessingExtractPagesTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -269,6 +296,12 @@ export namespace ExtractPages { */ pages: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -282,6 +315,11 @@ export namespace ExtractPages { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -310,7 +348,7 @@ export namespace ExtractPages { export namespace FailedExtractPagesTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -319,6 +357,12 @@ export namespace ExtractPages { */ pages: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -326,7 +370,7 @@ export namespace ExtractPages { } export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -349,6 +393,11 @@ export namespace Merge { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -381,6 +430,12 @@ export namespace Merge { */ input: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -398,6 +453,11 @@ export namespace Merge { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -430,6 +490,12 @@ export namespace Merge { */ input: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -443,6 +509,11 @@ export namespace Merge { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -475,6 +546,12 @@ export namespace Merge { */ input: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -488,6 +565,11 @@ export namespace Merge { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -520,6 +602,12 @@ export namespace Merge { */ input: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -527,7 +615,7 @@ export namespace Merge { } export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -550,6 +638,11 @@ export namespace Render { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -583,10 +676,16 @@ export namespace Render { dpi: number; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -609,6 +708,11 @@ export namespace Render { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -642,10 +746,16 @@ export namespace Render { dpi: number; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -664,6 +774,11 @@ export namespace Render { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -697,10 +812,16 @@ export namespace Render { dpi: number; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -719,6 +840,11 @@ export namespace Render { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -752,10 +878,16 @@ export namespace Render { dpi: number; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -768,7 +900,7 @@ export namespace Render { } export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -791,6 +923,11 @@ export namespace Split { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -819,10 +956,16 @@ export namespace Split { export namespace CompletedSplitTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -840,6 +983,11 @@ export namespace Split { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -868,10 +1016,16 @@ export namespace Split { export namespace PendingSplitTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -885,6 +1039,11 @@ export namespace Split { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -913,10 +1072,16 @@ export namespace Split { export namespace ProcessingSplitTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -930,6 +1095,11 @@ export namespace Split { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -958,10 +1128,16 @@ export namespace Split { export namespace FailedSplitTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -969,7 +1145,7 @@ export namespace Split { } export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -978,7 +1154,7 @@ export namespace Split { export interface PdfOperationExtractPagesParams { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -987,6 +1163,12 @@ export interface PdfOperationExtractPagesParams { */ pages: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -999,6 +1181,12 @@ export interface PdfOperationMergeParams { */ input: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1007,10 +1195,16 @@ export interface PdfOperationMergeParams { export interface PdfOperationRenderParams { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * Dots per inch (DPI) for the rendered image. Default is 300. */ @@ -1029,10 +1223,16 @@ export interface PdfOperationRenderParams { export interface PdfOperationSplitParams { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ diff --git a/src/resources/shared.ts b/src/resources/shared.ts new file mode 100644 index 0000000..39e6ce6 --- /dev/null +++ b/src/resources/shared.ts @@ -0,0 +1,27 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +/** + * An OpenAPI schema object describing the expected JSON structure. Required if + * format is 'json'. + */ +export interface JsonSchemaSpec { + description?: string; + + example?: unknown; + + format?: string; + + /** + * An OpenAPI schema object describing the expected JSON structure. Required if + * format is 'json'. + */ + items?: JsonSchemaSpec; + + properties?: { [key: string]: JsonSchemaSpec }; + + required?: Array; + + type?: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object'; + + [k: string]: unknown; +} diff --git a/src/resources/tasks.ts b/src/resources/tasks.ts index e68f5b0..a6e1e0c 100644 --- a/src/resources/tasks.ts +++ b/src/resources/tasks.ts @@ -65,7 +65,11 @@ export type TaskResponse = | TaskResponse.CompletedSplitTaskResponse | TaskResponse.PendingSplitTaskResponse | TaskResponse.ProcessingSplitTaskResponse - | TaskResponse.FailedSplitTaskResponse; + | TaskResponse.FailedSplitTaskResponse + | TaskResponse.CompletedScanTaskResponse + | TaskResponse.PendingScanTaskResponse + | TaskResponse.ProcessingScanTaskResponse + | TaskResponse.FailedScanTaskResponse; export namespace TaskResponse { export interface CompletedApplyEffectTaskResponse { @@ -74,6 +78,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -107,10 +116,16 @@ export namespace TaskResponse { effect: 'grayscale' | 'scanner' | 'black-background'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -128,6 +143,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -161,10 +181,16 @@ export namespace TaskResponse { effect: 'grayscale' | 'scanner' | 'black-background'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -178,6 +204,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -211,10 +242,16 @@ export namespace TaskResponse { effect: 'grayscale' | 'scanner' | 'black-background'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -228,6 +265,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -261,10 +303,16 @@ export namespace TaskResponse { effect: 'grayscale' | 'scanner' | 'black-background'; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -272,7 +320,7 @@ export namespace TaskResponse { } export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -284,6 +332,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -321,6 +374,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -352,6 +410,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -383,6 +446,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -410,7 +478,7 @@ export namespace TaskResponse { export namespace FailedConvertTaskResponse { export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -422,6 +490,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -462,6 +535,11 @@ export namespace TaskResponse { */ bounding_box: ImageOperationsAPI.BoundingBox; + /** + * The ID of the file containing the document + */ + file_id: string; + /** * The coordinates of the vertex inside the image */ @@ -476,6 +554,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -507,6 +590,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -538,6 +626,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -565,7 +658,7 @@ export namespace TaskResponse { export namespace FailedConvertTaskResponse { export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -577,6 +670,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -619,6 +717,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -653,6 +756,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -687,6 +795,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -717,7 +830,7 @@ export namespace TaskResponse { export namespace FailedExtractTextTaskResponse { export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -729,6 +842,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -769,6 +887,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -803,6 +926,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -837,6 +965,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -867,7 +1000,7 @@ export namespace TaskResponse { export namespace FailedWarpTaskResponse { export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -879,6 +1012,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -907,7 +1045,7 @@ export namespace TaskResponse { export namespace CompletedExtractPagesTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -916,6 +1054,12 @@ export namespace TaskResponse { */ pages: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -933,6 +1077,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -961,7 +1110,7 @@ export namespace TaskResponse { export namespace PendingExtractPagesTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -970,6 +1119,12 @@ export namespace TaskResponse { */ pages: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -983,6 +1138,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1011,7 +1171,7 @@ export namespace TaskResponse { export namespace ProcessingExtractPagesTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -1020,6 +1180,12 @@ export namespace TaskResponse { */ pages: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1033,6 +1199,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1061,7 +1232,7 @@ export namespace TaskResponse { export namespace FailedExtractPagesTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; @@ -1070,6 +1241,12 @@ export namespace TaskResponse { */ pages: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1077,7 +1254,7 @@ export namespace TaskResponse { } export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -1089,6 +1266,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1121,6 +1303,12 @@ export namespace TaskResponse { */ input: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1138,6 +1326,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1170,6 +1363,12 @@ export namespace TaskResponse { */ input: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1183,6 +1382,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1215,6 +1419,12 @@ export namespace TaskResponse { */ input: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1228,6 +1438,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1260,6 +1475,12 @@ export namespace TaskResponse { */ input: Array; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1267,7 +1488,7 @@ export namespace TaskResponse { } export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -1279,6 +1500,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1312,10 +1538,16 @@ export namespace TaskResponse { dpi: number; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1338,6 +1570,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1371,10 +1608,16 @@ export namespace TaskResponse { dpi: number; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1393,6 +1636,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1426,10 +1674,16 @@ export namespace TaskResponse { dpi: number; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1448,6 +1702,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1481,10 +1740,16 @@ export namespace TaskResponse { dpi: number; /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1497,7 +1762,7 @@ export namespace TaskResponse { } export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } @@ -1509,6 +1774,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1537,10 +1807,16 @@ export namespace TaskResponse { export namespace CompletedSplitTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1558,6 +1834,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1586,10 +1867,16 @@ export namespace TaskResponse { export namespace PendingSplitTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1603,6 +1890,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1631,10 +1923,16 @@ export namespace TaskResponse { export namespace ProcessingSplitTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. */ input: string; + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1648,6 +1946,11 @@ export namespace TaskResponse { */ id: string; + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + /** * The creation date of the task in ISO format. */ @@ -1676,10 +1979,310 @@ export namespace TaskResponse { export namespace FailedSplitTaskResponse { export interface Parameters { /** - * The id of the file to operate on. + * The id of the file or task to operate on. + */ + input: string; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + + /** + * The name of the file + */ + name?: string; + } + + export interface Result { + details: { [key: string]: unknown }; + + error: string; + } + } + + export interface CompletedScanTaskResponse { + /** + * The unique identifier for the task. + */ + id: string; + + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + + /** + * The creation date of the task in ISO format. + */ + created_at: string; + + /** + * The type of operation being performed by the task. + */ + operation: 'scan'; + + parameters: CompletedScanTaskResponse.Parameters; + + result: CompletedScanTaskResponse.Result; + + /** + * The current status of the task. + */ + status: 'completed'; + + /** + * The last update date of the task in ISO format. + */ + updated_at: string; + } + + export namespace CompletedScanTaskResponse { + export interface Parameters { + /** + * The effect to apply to the image + */ + effect: 'none' | 'grayscale' | 'scanner' | 'black-background'; + + /** + * The id of the file or task to operate on. + */ + input: string; + + /** + * Mode for detecting documents in the image. Available modes are: + * + * - **none**: No document detection is performed. + * - **standard**: Using a quick algorithm. Document is detected in the image, and + * the image is cropped to the detected document area fixing the perspective to + * match the document's shape. + */ + scan_mode: 'none' | 'standard'; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + + /** + * The name of the file + */ + name?: string; + } + + export interface Result { + generated_files: Array; + } + } + + export interface PendingScanTaskResponse { + /** + * The unique identifier for the task. + */ + id: string; + + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + + /** + * The creation date of the task in ISO format. + */ + created_at: string; + + /** + * The type of operation being performed by the task. + */ + operation: 'scan'; + + parameters: PendingScanTaskResponse.Parameters; + + result: unknown; + + /** + * The current status of the task. + */ + status: 'pending'; + + /** + * The last update date of the task in ISO format. + */ + updated_at: string; + } + + export namespace PendingScanTaskResponse { + export interface Parameters { + /** + * The effect to apply to the image + */ + effect: 'none' | 'grayscale' | 'scanner' | 'black-background'; + + /** + * The id of the file or task to operate on. */ input: string; + /** + * Mode for detecting documents in the image. Available modes are: + * + * - **none**: No document detection is performed. + * - **standard**: Using a quick algorithm. Document is detected in the image, and + * the image is cropped to the detected document area fixing the perspective to + * match the document's shape. + */ + scan_mode: 'none' | 'standard'; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + + /** + * The name of the file + */ + name?: string; + } + } + + export interface ProcessingScanTaskResponse { + /** + * The unique identifier for the task. + */ + id: string; + + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + + /** + * The creation date of the task in ISO format. + */ + created_at: string; + + /** + * The type of operation being performed by the task. + */ + operation: 'scan'; + + parameters: ProcessingScanTaskResponse.Parameters; + + result: unknown; + + /** + * The current status of the task. + */ + status: 'processing'; + + /** + * The last update date of the task in ISO format. + */ + updated_at: string; + } + + export namespace ProcessingScanTaskResponse { + export interface Parameters { + /** + * The effect to apply to the image + */ + effect: 'none' | 'grayscale' | 'scanner' | 'black-background'; + + /** + * The id of the file or task to operate on. + */ + input: string; + + /** + * Mode for detecting documents in the image. Available modes are: + * + * - **none**: No document detection is performed. + * - **standard**: Using a quick algorithm. Document is detected in the image, and + * the image is cropped to the detected document area fixing the perspective to + * match the document's shape. + */ + scan_mode: 'none' | 'standard'; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + + /** + * The name of the file + */ + name?: string; + } + } + + export interface FailedScanTaskResponse { + /** + * The unique identifier for the task. + */ + id: string; + + /** + * The URL to which the task result will be sent upon completion or failure. + */ + callback_url: string | null; + + /** + * The creation date of the task in ISO format. + */ + created_at: string; + + /** + * The type of operation being performed by the task. + */ + operation: 'scan'; + + parameters: FailedScanTaskResponse.Parameters; + + result: FailedScanTaskResponse.Result; + + /** + * The current status of the task. + */ + status: 'failed'; + + /** + * The last update date of the task in ISO format. + */ + updated_at: string; + } + + export namespace FailedScanTaskResponse { + export interface Parameters { + /** + * The effect to apply to the image + */ + effect: 'none' | 'grayscale' | 'scanner' | 'black-background'; + + /** + * The id of the file or task to operate on. + */ + input: string; + + /** + * Mode for detecting documents in the image. Available modes are: + * + * - **none**: No document detection is performed. + * - **standard**: Using a quick algorithm. Document is detected in the image, and + * the image is cropped to the detected document area fixing the perspective to + * match the document's shape. + */ + scan_mode: 'none' | 'standard'; + + /** + * The URL to call when the task is completed or failed. If you want to receive + * events, you probably prefer to use `webhooks` instead. + */ + callback_url?: string; + /** * The name of the file */ @@ -1687,7 +2290,7 @@ export namespace TaskResponse { } export interface Result { - details: Record; + details: { [key: string]: unknown }; error: string; } diff --git a/src/version.ts b/src/version.ts index fe6d1df..66c10e6 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.1.0-alpha.4'; // x-release-please-version +export const VERSION = '0.1.0-alpha.5'; // x-release-please-version diff --git a/tests/api-resources/image-operations.test.ts b/tests/api-resources/image-operations.test.ts index e8f7113..07957d7 100644 --- a/tests/api-resources/image-operations.test.ts +++ b/tests/api-resources/image-operations.test.ts @@ -28,6 +28,7 @@ describe('resource imageOperations', () => { const response = await client.imageOperations.applyEffect({ effect: 'grayscale', input: 'file_avyrvozb9302uwhq', + callback_url: 'https://example.com/callback', name: 'Example Image', }); }); @@ -52,6 +53,7 @@ describe('resource imageOperations', () => { const response = await client.imageOperations.convert({ input: 'file_avyrvozb9302uwhq', target_format: 'image/png', + callback_url: 'https://example.com/callback', name: 'Example Image', }); }); @@ -70,7 +72,10 @@ describe('resource imageOperations', () => { // skipped: tests are disabled for the time being test.skip('detectDocuments: required and optional params', async () => { - const response = await client.imageOperations.detectDocuments({ input: 'file_avyrvozb9302uwhq' }); + const response = await client.imageOperations.detectDocuments({ + input: 'file_avyrvozb9302uwhq', + callback_url: 'https://example.com/callback', + }); }); // skipped: tests are disabled for the time being @@ -93,6 +98,7 @@ describe('resource imageOperations', () => { const response = await client.imageOperations.extractText({ format: 'plain', input: 'file_avyrvozb9302uwhq', + callback_url: 'https://example.com/callback', }); }); @@ -126,6 +132,7 @@ describe('resource imageOperations', () => { { x: 100, y: 100 }, { x: 0, y: 90 }, ], + callback_url: 'https://example.com/callback', name: 'Example Image', }); }); diff --git a/tests/api-resources/pdf-operations.test.ts b/tests/api-resources/pdf-operations.test.ts index f6d050c..dc39d81 100644 --- a/tests/api-resources/pdf-operations.test.ts +++ b/tests/api-resources/pdf-operations.test.ts @@ -28,6 +28,7 @@ describe('resource pdfOperations', () => { const response = await client.pdfOperations.extractPages({ input: 'file_avyrvozb9302uwhq', pages: '2-7', + callback_url: 'https://example.com/callback', name: 'File Name', }); }); @@ -48,6 +49,7 @@ describe('resource pdfOperations', () => { test.skip('merge: required and optional params', async () => { const response = await client.pdfOperations.merge({ input: ['file_avyrvozb9302uwhq'], + callback_url: 'https://example.com/callback', name: 'File Name', }); }); @@ -68,6 +70,7 @@ describe('resource pdfOperations', () => { test.skip('render: required and optional params', async () => { const response = await client.pdfOperations.render({ input: 'file_avyrvozb9302uwhq', + callback_url: 'https://example.com/callback', dpi: 300, name: 'File Name', pages: '2-7', @@ -88,6 +91,10 @@ describe('resource pdfOperations', () => { // skipped: tests are disabled for the time being test.skip('split: required and optional params', async () => { - const response = await client.pdfOperations.split({ input: 'file_avyrvozb9302uwhq', name: 'File Name' }); + const response = await client.pdfOperations.split({ + input: 'file_avyrvozb9302uwhq', + callback_url: 'https://example.com/callback', + name: 'File Name', + }); }); }); diff --git a/tests/index.test.ts b/tests/index.test.ts index 54da281..92a38ff 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -320,6 +320,28 @@ describe('instantiate client', () => { const client = new ScanDocuments({ apiKey: 'My API Key' }); expect(client.baseURL).toEqual('https://api.scan-documents.com'); }); + + test('in request options', () => { + const client = new ScanDocuments({ apiKey: 'My API Key' }); + expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual( + 'http://localhost:5000/option/foo', + ); + }); + + test('in request options overridden by client options', () => { + const client = new ScanDocuments({ apiKey: 'My API Key', baseURL: 'http://localhost:5000/client' }); + expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual( + 'http://localhost:5000/client/foo', + ); + }); + + test('in request options overridden by env variable', () => { + process.env['SCAN_DOCUMENTS_BASE_URL'] = 'http://localhost:5000/env'; + const client = new ScanDocuments({ apiKey: 'My API Key' }); + expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual( + 'http://localhost:5000/env/foo', + ); + }); }); test('maxRetries option is correctly set', () => { diff --git a/tsc-multi.json b/tsc-multi.json index 170bac7..384ddac 100644 --- a/tsc-multi.json +++ b/tsc-multi.json @@ -1,7 +1,15 @@ { "targets": [ - { "extname": ".js", "module": "commonjs", "shareHelpers": "internal/tslib.js" }, - { "extname": ".mjs", "module": "esnext", "shareHelpers": "internal/tslib.mjs" } + { + "extname": ".js", + "module": "commonjs", + "shareHelpers": "internal/tslib.js" + }, + { + "extname": ".mjs", + "module": "esnext", + "shareHelpers": "internal/tslib.mjs" + } ], "projects": ["tsconfig.build.json"] } diff --git a/yarn.lock b/yarn.lock index 43da555..58c08d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -714,10 +714,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@pkgr/core@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" - integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== +"@pkgr/core@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.4.tgz#d897170a2b0ba51f78a099edccd968f7b103387c" + integrity sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw== "@sinclair/typebox@^0.27.8": version "0.27.8" @@ -1547,13 +1547,13 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-plugin-prettier@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz#c4af01691a6fa9905207f0fbba0d7bea0902cce5" - integrity sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw== +eslint-plugin-prettier@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.1.tgz#99b55d7dd70047886b2222fdd853665f180b36af" + integrity sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg== dependencies: prettier-linter-helpers "^1.0.0" - synckit "^0.9.1" + synckit "^0.11.7" eslint-plugin-unused-imports@^4.1.4: version "4.1.4" @@ -3198,13 +3198,12 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -synckit@0.8.8, synckit@^0.9.1: - version "0.8.8" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7" - integrity sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ== +synckit@^0.11.7: + version "0.11.8" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.8.tgz#b2aaae998a4ef47ded60773ad06e7cb821f55457" + integrity sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A== dependencies: - "@pkgr/core" "^0.1.0" - tslib "^2.6.2" + "@pkgr/core" "^0.2.4" test-exclude@^6.0.0: version "6.0.0" @@ -3284,9 +3283,9 @@ ts-node@^10.5.0: v8-compile-cache-lib "^3.0.0" yn "3.1.1" -"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz": - version "1.1.4" - resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz#cbed459a9e902f5295ec3daaf1c7aa3b10427e55" +"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.8/tsc-multi.tgz": + version "1.1.8" + resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.8/tsc-multi.tgz#f544b359b8f05e607771ffacc280e58201476b04" dependencies: debug "^4.3.7" fast-glob "^3.3.2" @@ -3308,11 +3307,6 @@ tsconfig-paths@^4.0.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"