diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 763462fa..43fd5a73 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -9,9 +9,7 @@ "postCreateCommand": "yarn install", "customizations": { "vscode": { - "extensions": [ - "esbenp.prettier-vscode" - ] + "extensions": ["esbenp.prettier-vscode"] } } } diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..12ff1e64 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,59 @@ +# Dependencies +node_modules/ +**/node_modules/ + +# Build outputs +dist/ +**/dist/ + +# Git +.git/ +.gitignore + +# CI/CD +.github/ +.gitlab-ci.yml +.travis.yml + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Testing +test/ +tests/ +__tests__/ +*.test.js +*.spec.js +coverage/ +.nyc_output/ + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Environment +.env +.env.* + +# Temporary files +*.tmp +*.temp +.cache/ + +# Examples and scripts +examples/ +bin/ + +# Other packages (we only need mcp-server) +packages/*/ +!packages/mcp-server/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5aba00bb..93bf2117 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI on: push: - branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' pull_request: branches-ignore: - 'stl-preview-head/**' @@ -17,12 +19,12 @@ jobs: timeout-minutes: 10 name: lint runs-on: ${{ github.repository == 'stainless-sdks/julep-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '18' @@ -36,15 +38,15 @@ jobs: timeout-minutes: 5 name: build runs-on: ${{ github.repository == 'stainless-sdks/julep-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') permissions: contents: read id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '20' @@ -55,29 +57,44 @@ jobs: run: ./scripts/build - name: Get GitHub OIDC Token - if: github.repository == 'stainless-sdks/julep-node' + if: |- + github.repository == 'stainless-sdks/julep-node' && + !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v6 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); - name: Upload tarball - if: github.repository == 'stainless-sdks/julep-node' + if: |- + github.repository == 'stainless-sdks/julep-node' && + !startsWith(github.ref, 'refs/heads/stl/') env: URL: https://pkg.stainless.com/s AUTH: ${{ steps.github-oidc.outputs.github_token }} SHA: ${{ github.sha }} run: ./scripts/utils/upload-artifact.sh + + - name: Upload MCP Server tarball + if: |- + github.repository == 'stainless-sdks/julep-node' && + !startsWith(github.ref, 'refs/heads/stl/') + env: + URL: https://pkg.stainless.com/s?subpackage=mcp-server + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + BASE_PATH: packages/mcp-server + run: ./scripts/utils/upload-artifact.sh test: timeout-minutes: 10 name: test runs-on: ${{ github.repository == 'stainless-sdks/julep-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '22' diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 996b6017..cb76de67 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -16,12 +16,14 @@ jobs: publish: name: publish runs-on: ubuntu-latest + permissions: + contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node - uses: actions/setup-node@v3 + uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: '20' @@ -31,11 +33,19 @@ jobs: - name: Publish to NPM run: | - if [ -n "${{ github.event.inputs.path }}" ]; then - PATHS_RELEASED='[\"${{ github.event.inputs.path }}\"]' + if [ -n "$INPUT_PATH" ]; then + PATHS_RELEASED="[\"$INPUT_PATH\"]" else PATHS_RELEASED='[\".\", \"packages/mcp-server\"]' fi yarn tsn scripts/publish-packages.ts "{ \"paths_released\": \"$PATHS_RELEASED\" }" env: + INPUT_PATH: ${{ github.event.inputs.path }} NPM_TOKEN: ${{ secrets.JULEP_NPM_TOKEN || secrets.NPM_TOKEN }} + + - name: Upload MCP Server DXT GitHub release asset + run: | + gh release upload ${{ github.event.release.tag_name }} \ + packages/mcp-server/julep_sdk_api.mcpb + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 9e11863f..f11e6b75 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'julep-ai/node-sdk' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check release environment run: | diff --git a/.gitignore b/.gitignore index d98d51a8..b7d4f6b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .prism.log +.stdy.log node_modules yarn-error.log codegen.log @@ -7,4 +8,6 @@ dist dist-deno /*.tgz .idea/ - +.eslintcache +dist-bundle +*.mcpb diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 880c4403..d4f6f299 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.7.4" + ".": "3.0.0" } diff --git a/.stats.yml b/.stats.yml index e4dce284..c033828d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 66 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/julep-ai-inc-dash%2Fjulep-21c8182519ef811d16673113f293a4b667ccd0176bdf867e8a1701b520d0bce7.yml -openapi_spec_hash: 3a08e0e8f22356dd768b19bd7e0950eb +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/julep-ai-inc-dash/julep-56c9759b7b3d7b7c409f370aa184f895d50991171153728b5ddb85ef824d82c5.yml +openapi_spec_hash: a3f61930f630788c17daa9ec5be09d0f config_hash: 5cb77b8389154096b85883a93680f511 diff --git a/CHANGELOG.md b/CHANGELOG.md index fa4339f5..8ee3af62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,226 @@ # Changelog +## 3.0.0 (2026-05-29) + +Full Changelog: [v2.7.4...v3.0.0](https://github.com/julep-ai/node-sdk/compare/v2.7.4...v3.0.0) + +### ⚠ BREAKING CHANGES + +* **mcp:** remove deprecated tool schemes +* **mcp:** **Migration:** To migrate, simply modify the command used to invoke the MCP server. Currently, the only supported tool scheme is code mode. Now, starting the server with just `node /path/to/mcp/server` or `npx package-name` will invoke code tools: changing your command to one of these is likely all you will need to do. + +### Features + +* **api:** api update ([0602cc7](https://github.com/julep-ai/node-sdk/commit/0602cc745f55c0871505ed2e62983c5375474fb3)) +* **api:** api update ([f5f76e7](https://github.com/julep-ai/node-sdk/commit/f5f76e7de501387cb282816bfaf0e77a728bb7dd)) +* **api:** api update ([f9d4d09](https://github.com/julep-ai/node-sdk/commit/f9d4d09a84f8e7e0effee08a80ae72f73962d6fe)) +* **api:** api update ([53404bd](https://github.com/julep-ai/node-sdk/commit/53404bd2c0d6902eb8f47c408aa03bfeaebf8ce5)) +* **mcp:** add an option to disable code tool ([2219842](https://github.com/julep-ai/node-sdk/commit/2219842f9b441a019c9bd6a71043b95c8210c76b)) +* **mcp:** add client infer to cloudflare oauth screen ([3ea626e](https://github.com/julep-ai/node-sdk/commit/3ea626ec8f75069484d2a0a4fd133c7993c94dbe)) +* **mcp:** add code execution tool ([1e00ca0](https://github.com/julep-ai/node-sdk/commit/1e00ca0f773fc45220972ee123ad198f06f95604)) +* **mcp:** add detail field to docs search tool ([122e298](https://github.com/julep-ai/node-sdk/commit/122e29863c49d7d88158a672a944e9a0fc591557)) +* **mcp:** add docs search tool ([269087f](https://github.com/julep-ai/node-sdk/commit/269087f17b239c24f60c7bf0c771e264bdb6756c)) +* **mcp:** add initial server instructions ([f0ae533](https://github.com/julep-ai/node-sdk/commit/f0ae533be1443f9f8b5e3a0a984e50d975ea4abe)) +* **mcp:** add logging when environment variable is set ([be36e15](https://github.com/julep-ai/node-sdk/commit/be36e1520c295adc4396ff8f85b46203e3a3b8f4)) +* **mcp:** add mcp bundles to build script ([1bb1db5](https://github.com/julep-ai/node-sdk/commit/1bb1db5552b897d8122013a3a701203796a1964c)) +* **mcp:** add option for including docs tools ([7ec226e](https://github.com/julep-ai/node-sdk/commit/7ec226e7ff1f5ad69e8e2dfed7c3916391e0db88)) +* **mcp:** add option to infer mcp client ([2d55b8d](https://github.com/julep-ai/node-sdk/commit/2d55b8db6791b4780ab33d6bc3beb3e168f4640d)) +* **mcp:** add typescript check to code execution tool ([01c6570](https://github.com/julep-ai/node-sdk/commit/01c6570a42b7627073d227b58f22290d75a69e2c)) +* **mcp:** add unix socket option for remote MCP ([e2fdc8b](https://github.com/julep-ai/node-sdk/commit/e2fdc8be5b7f55135fe889bb68ff076ff2e83c9f)) +* **mcp:** allow setting logging level ([92e01a7](https://github.com/julep-ai/node-sdk/commit/92e01a7daca8f851400d1bce1d1a04f4c9636a2b)) +* **mcp:** change remote server query option parsing logic ([1ce88f0](https://github.com/julep-ai/node-sdk/commit/1ce88f05b38e6e58c248f4a0891142026bcfe98a)) +* **mcp:** enable experimental docs search tool ([c09861a](https://github.com/julep-ai/node-sdk/commit/c09861a599eb52fb7d38a67d5eec9552f55226f4)) +* **mcp:** enable optional code execution tool on http mcp servers ([5aa88d4](https://github.com/julep-ai/node-sdk/commit/5aa88d4899c9a29e93de6facf2d5f97c837679f7)) +* **mcp:** expose client options in `streamableHTTPApp` ([c79c312](https://github.com/julep-ai/node-sdk/commit/c79c3127c770a96d946ed25d9a413d701cd807ff)) +* **mcp:** handle code mode calls in the Stainless API ([e4ad4ae](https://github.com/julep-ai/node-sdk/commit/e4ad4ae8f90c4332df65f8d5d0b357db47ab6d4a)) +* **mcp:** parse query string as mcp client options in mcp server ([ca31e54](https://github.com/julep-ai/node-sdk/commit/ca31e54b1bb7be289a73cad5ed1fd445a9b9f99c)) +* **mcp:** remote server with passthru auth ([e98a089](https://github.com/julep-ai/node-sdk/commit/e98a0891b4800926d6d8ac8b796f93fd193f27ec)) +* **mcp:** return logs on code tool errors ([67c1ed9](https://github.com/julep-ai/node-sdk/commit/67c1ed96991234dff93cc1cea655cafb0c2622c7)) +* support setting headers via env ([60a04b0](https://github.com/julep-ai/node-sdk/commit/60a04b0ad2ffdbd22b400a9777247877e9f6a278)) + + +### Bug Fixes + +* **ci:** set permissions for DXT publish action ([ce6f62c](https://github.com/julep-ai/node-sdk/commit/ce6f62cd2ee3b6c9164e3439f5ce10eaaaedd822)) +* **client:** incorrect offset pagination check ([87e78b8](https://github.com/julep-ai/node-sdk/commit/87e78b8b6e5fc759686bdc2c390838d6faf27844)) +* **client:** preserve URL params already embedded in path ([68777a4](https://github.com/julep-ai/node-sdk/commit/68777a451785d4fa783dc2a34f76518487a02d74)) +* coerce nullable values to undefined ([3d6056f](https://github.com/julep-ai/node-sdk/commit/3d6056f40235d4360435af2e8511bb5993cbb8e4)) +* **docs/contributing:** correct pnpm link command ([f8fe092](https://github.com/julep-ai/node-sdk/commit/f8fe092f2718b5e89bfe896b892dbb07e2448cf4)) +* **docs:** fix mcp installation instructions for remote servers ([05daa38](https://github.com/julep-ai/node-sdk/commit/05daa380670e0bca87be74194c9bbec908c216ea)) +* **mcp:** add client instantiation options to code tool ([adb99b7](https://github.com/julep-ai/node-sdk/commit/adb99b75fbc01aa5ce7703f521fbc5066b474c4a)) +* **mcp:** allow falling back for required env variables ([b593ded](https://github.com/julep-ai/node-sdk/commit/b593dedf8f5ce6c1a64ce890205d254dfc1dc111)) +* **mcpb:** pin @anthropic-ai/mcpb version ([e2809b3](https://github.com/julep-ai/node-sdk/commit/e2809b3a047cbdf1a54b6394198bf727a3bd304a)) +* **mcp:** bump agents version in cloudflare worker MCP servers ([4e43395](https://github.com/julep-ai/node-sdk/commit/4e43395cea27367610554cc8258146e2a2164ee1)) +* **mcp:** correct code tool API endpoint ([b19afe0](https://github.com/julep-ai/node-sdk/commit/b19afe08dd9c1337aa2e1fb3532c717fc62233b9)) +* **mcp:** correct code tool api output types ([4f83af8](https://github.com/julep-ai/node-sdk/commit/4f83af8ec39a23664f6e8ab66f0739a6069628ed)) +* **mcp:** do not fallback on baseUrl if environment env variable is set ([b60cead](https://github.com/julep-ai/node-sdk/commit/b60cead152bee65d4623d6cd7d3e780a9849ba6a)) +* **mcp:** fix bug in header handling ([c0d1022](https://github.com/julep-ai/node-sdk/commit/c0d102255f5453422dc5b562a0a0c0c7049e0c69)) +* **mcp:** fix cli argument parsing logic ([e0d84e5](https://github.com/julep-ai/node-sdk/commit/e0d84e5d5b723ab7f5b2a9131d28616086453a6c)) +* **mcp:** fix env parsing ([a985915](https://github.com/julep-ai/node-sdk/commit/a985915a6cc4cde74151a92754bdb4a09ed7bad0)) +* **mcp:** fix options parsing ([23285ec](https://github.com/julep-ai/node-sdk/commit/23285ec43de3aa4f02b3707cca36ab0a922c2392)) +* **mcp:** fix query options parsing ([1fa531c](https://github.com/julep-ai/node-sdk/commit/1fa531ceb0e673aac6179dadd83a65d4f36c7642)) +* **mcp:** fix some response schemas used for jq filtering ([b0f97cf](https://github.com/julep-ai/node-sdk/commit/b0f97cfbdc8f4e4beba885684d946b3aa3532982)) +* **mcp:** fix uploading dxt release assets ([7854bfd](https://github.com/julep-ai/node-sdk/commit/7854bfdcc35420bb1825a30ab01b39e5a0023e7a)) +* **mcp:** generate additionalProperties=true for map schemas to avoid validation issues ([12bd79f](https://github.com/julep-ai/node-sdk/commit/12bd79f67acb13742b83e0bd692f58e8759cef8b)) +* **mcp:** initialize SDK lazily to avoid failing the connection on init errors ([011fc93](https://github.com/julep-ai/node-sdk/commit/011fc935929ce55929f0c6ffd1a3d37a0a70e966)) +* **mcp:** pass base url to code tool ([9650d43](https://github.com/julep-ai/node-sdk/commit/9650d439fa5681d3581fb0cc4b8e5c451cd21b56)) +* **mcp:** resolve a linting issue in server code ([e866428](https://github.com/julep-ai/node-sdk/commit/e866428139e209784086a2219660fa6f6168a9e4)) +* **mcp:** return correct lines on typescript errors ([4d8eb4c](https://github.com/julep-ai/node-sdk/commit/4d8eb4ced3e338c82b3618b81f699a942f780b34)) +* **mcp:** return tool execution error on api error ([48209fc](https://github.com/julep-ai/node-sdk/commit/48209fc16212d444baa94141825deb17972d9c5f)) +* **mcp:** return tool execution error on jq failure ([c717c94](https://github.com/julep-ai/node-sdk/commit/c717c942ba11a3bedd2af7b8e4d660dcbb2419a2)) +* **mcp:** update cloudflare worker host page ([04faac6](https://github.com/julep-ai/node-sdk/commit/04faac63d9a19037f6a5e0567740a74627b4b999)) +* **mcp:** update code tool prompt ([e93faff](https://github.com/julep-ai/node-sdk/commit/e93faff361d5a02179e77315540433f198db828e)) +* **mcp:** update prompt ([8a606fa](https://github.com/julep-ai/node-sdk/commit/8a606fa0774ebc5117f20cea7167ad8e06cce042)) + + +### Performance Improvements + +* faster formatting ([fbb1589](https://github.com/julep-ai/node-sdk/commit/fbb1589e1287a6b7d4880a243ab1559d3bf29180)) + + +### Chores + +* break long lines in snippets into multiline ([a60ce49](https://github.com/julep-ai/node-sdk/commit/a60ce4955a4945cf84e94969a8e5669a03796535)) +* ci build action ([82be9a4](https://github.com/julep-ai/node-sdk/commit/82be9a42306af22a0e70f909553ae05643389b15)) +* **ci:** escape input path in publish-npm workflow ([fcfb53d](https://github.com/julep-ai/node-sdk/commit/fcfb53df9bb6276b164507dd3cf8a200d5229787)) +* **ci:** skip lint on metadata-only changes ([e202d8f](https://github.com/julep-ai/node-sdk/commit/e202d8f761194bafaddd755810fa1280c2a42007)) +* **ci:** skip uploading artifacts on stainless-internal branches ([a8f6594](https://github.com/julep-ai/node-sdk/commit/a8f65947d376262e7455c017077b202a0fc1ef14)) +* **ci:** upgrade `actions/github-script` ([53d6cd1](https://github.com/julep-ai/node-sdk/commit/53d6cd17f9d58f7b3d365b4e26b7368d1062ad4a)) +* **client:** do not parse responses with empty content-length ([093077c](https://github.com/julep-ai/node-sdk/commit/093077cb4ae2fdfdfd6fc556f6da33f0ae9fedfd)) +* **codegen:** internal codegen update ([a7f4ec9](https://github.com/julep-ai/node-sdk/commit/a7f4ec91e05ebe0764064280a4e1942c56bc254f)) +* **deps:** update dependency node-fetch to v2.6.13 ([c62fad2](https://github.com/julep-ai/node-sdk/commit/c62fad2a45e62eb30e842a7a0795316c43fadb6f)) +* do not install brew dependencies in ./scripts/bootstrap by default ([ad1dec4](https://github.com/julep-ai/node-sdk/commit/ad1dec48b3925c5a522192cc519e2e8782d24b37)) +* extract some types in mcp docs ([438f807](https://github.com/julep-ai/node-sdk/commit/438f8073a887f9375e482d3ea4a744b3ba3d18c7)) +* fix typo in descriptions ([6cee00e](https://github.com/julep-ai/node-sdk/commit/6cee00e377897ac68715f98ce54913efc90d6036)) +* **internal:** add health check to MCP server when running in HTTP mode ([bbc38ee](https://github.com/julep-ai/node-sdk/commit/bbc38ee0125f68bfcfca864c55350a8b61fca00e)) +* **internal:** allow basic filtering of methods allowed for MCP code mode ([bb39c62](https://github.com/julep-ai/node-sdk/commit/bb39c62e71ecb610721b73c9eedc799318854e33)) +* **internal:** allow setting x-stainless-api-key header on mcp server requests ([be6c595](https://github.com/julep-ai/node-sdk/commit/be6c595d1058dda5acdc0007ebea87c3037094de)) +* **internal:** always generate MCP server dockerfiles and upgrade associated dependencies ([f7edfed](https://github.com/julep-ai/node-sdk/commit/f7edfed04953a3a76a03e759a787fbe51d7a2e8b)) +* **internal:** bump @modelcontextprotocol/sdk, @hono/node-server, and minimatch ([3ffe8a3](https://github.com/julep-ai/node-sdk/commit/3ffe8a33f4a7700f1305a5a9e1c8b95c88b646ca)) +* **internal:** bump MCP dependencies ([650313f](https://github.com/julep-ai/node-sdk/commit/650313f7cd00d42e82c3f79a5bab480351f1169e)) +* **internal:** cache fetch instruction calls in MCP server ([7019a7f](https://github.com/julep-ai/node-sdk/commit/7019a7fb0d1cbf0fd7be22b7725160ff113688dc)) +* **internal:** codegen related update ([9961fe5](https://github.com/julep-ai/node-sdk/commit/9961fe58ee75ee87ca60e85206c92e1acf1a317d)) +* **internal:** codegen related update ([6346bbb](https://github.com/julep-ai/node-sdk/commit/6346bbbd655b2aca788fa1f45dba6f3f24442269)) +* **internal:** codegen related update ([5edc9bb](https://github.com/julep-ai/node-sdk/commit/5edc9bb58a59e16845882adee0a44f330c69d143)) +* **internal:** codegen related update ([f9228eb](https://github.com/julep-ai/node-sdk/commit/f9228ebaca9b62c3f6f957b9eb270f7b8215f6d7)) +* **internal:** codegen related update ([ce94b68](https://github.com/julep-ai/node-sdk/commit/ce94b68a9e9f62411be030a0b950c20e112e9d58)) +* **internal:** codegen related update ([af60a89](https://github.com/julep-ai/node-sdk/commit/af60a89e18c701b754155c534a89b4aa6109a20f)) +* **internal:** codegen related update ([0612bd5](https://github.com/julep-ai/node-sdk/commit/0612bd5c2d3c7fcdecc5fb9d5804125a05c94758)) +* **internal:** codegen related update ([0b334db](https://github.com/julep-ai/node-sdk/commit/0b334db1c71eb79a6f76be368946cb20956a0750)) +* **internal:** codegen related update ([8b8ceaf](https://github.com/julep-ai/node-sdk/commit/8b8ceafd6a23eb616d25458811a8d9ec651f1feb)) +* **internal:** codegen related update ([3df78ae](https://github.com/julep-ai/node-sdk/commit/3df78ae0929412a0421d7f169c5bb9f768f32e43)) +* **internal:** codegen related update ([4d9100f](https://github.com/julep-ai/node-sdk/commit/4d9100f3075bf341f628a83867934b5972c09bf0)) +* **internal:** codegen related update ([1e6655c](https://github.com/julep-ai/node-sdk/commit/1e6655c9531a6e198b7d8d2f29b8e4375ba82345)) +* **internal:** codegen related update ([5783182](https://github.com/julep-ai/node-sdk/commit/5783182f2d0940f311c47a2779023e4b63f1983a)) +* **internal:** codegen related update ([47bcda4](https://github.com/julep-ai/node-sdk/commit/47bcda4efbce2c72a4fc36dbd7b10b97f313bc3d)) +* **internal:** codegen related update ([7fb00f0](https://github.com/julep-ai/node-sdk/commit/7fb00f02ff4e2e992e01771465f95efa3c93abdf)) +* **internal:** codegen related update ([6fd386f](https://github.com/julep-ai/node-sdk/commit/6fd386f5bf3d5fb6cc5160117dcd75225d033f58)) +* **internal:** codegen related update ([14f1d5a](https://github.com/julep-ai/node-sdk/commit/14f1d5a803b499ca8e6b488beae1f69f04627a3f)) +* **internal:** codegen related update ([afd9e2f](https://github.com/julep-ai/node-sdk/commit/afd9e2f21798037026c4aa52f3fb23e4b0d4c119)) +* **internal:** codegen related update ([cf90b27](https://github.com/julep-ai/node-sdk/commit/cf90b27161d72ab3ebef74ce779c2f8341457e35)) +* **internal:** codegen related update ([f9801ac](https://github.com/julep-ai/node-sdk/commit/f9801ac0ec4e91a5165f34a1a12bcfea73a5add2)) +* **internal:** codegen related update ([b09d85c](https://github.com/julep-ai/node-sdk/commit/b09d85c6a6e0bc2eac482ba9cfb9b7b09ec5679d)) +* **internal:** fix incremental formatting in some cases ([9db3270](https://github.com/julep-ai/node-sdk/commit/9db3270f61c87cd831c4e4640d9dca3d52e03b9f)) +* **internal:** fix MCP cloudflare worker builds ([30ab041](https://github.com/julep-ai/node-sdk/commit/30ab0415ab06ab35588546bd0a43311d7e344e07)) +* **internal:** fix MCP cloudflare worker initialization ([1df92ef](https://github.com/julep-ai/node-sdk/commit/1df92ef83b5b9ea78cc75ada2820da8df27e6016)) +* **internal:** fix MCP docker image builds in yarn projects ([0a95651](https://github.com/julep-ai/node-sdk/commit/0a95651441d481c1891cadaddd46a353a4f120c3)) +* **internal:** fix MCP Dockerfiles so they can be built without buildkit ([08a9df6](https://github.com/julep-ai/node-sdk/commit/08a9df6b64fbab7025855988c54cea1d5009f143)) +* **internal:** fix MCP Dockerfiles so they can be built without buildkit ([6f70f99](https://github.com/julep-ai/node-sdk/commit/6f70f99e3959209dc77b14f0f08c2e200b3127e1)) +* **internal:** fix MCP server import ordering ([9702a7d](https://github.com/julep-ai/node-sdk/commit/9702a7dede53e5aa321a791932e3375738caef80)) +* **internal:** fix MCP server TS errors that occur with required client options ([b837153](https://github.com/julep-ai/node-sdk/commit/b8371530792c4fb926bac64c65b1a5ffe8b9f071)) +* **internal:** formatting change ([12fb0e6](https://github.com/julep-ai/node-sdk/commit/12fb0e6a7dd00c23e1be0187f9980c9e3494974e)) +* **internal:** gitignore .mcpb files ([1379dad](https://github.com/julep-ai/node-sdk/commit/1379dad5516bf5c4d7377ba5b0fcac8acd2c6564)) +* **internal:** grammar fix (it's -> its) ([0683aeb](https://github.com/julep-ai/node-sdk/commit/0683aeb95c43df79890d5604597c947a3c5d7396)) +* **internal:** ignore .eslintcache ([19aa6c1](https://github.com/julep-ai/node-sdk/commit/19aa6c137cd938c55a4a1ad639ac6998a4ad0063)) +* **internal:** improve layout of generated MCP server files ([5dbe7e6](https://github.com/julep-ai/node-sdk/commit/5dbe7e632e5f4672f857282dc8ef1e8aca929d91)) +* **internal:** improve local docs search for MCP servers ([6aa2f3b](https://github.com/julep-ai/node-sdk/commit/6aa2f3b60abbd010009ba7cd997459077983e3d3)) +* **internal:** make generated MCP servers compatible with Cloudflare worker environments ([b96aed5](https://github.com/julep-ai/node-sdk/commit/b96aed5d2feb49f7dca7fb2dc66d15ebb3288bda)) +* **internal:** make MCP code execution location configurable via a flag ([04819f0](https://github.com/julep-ai/node-sdk/commit/04819f04aafc01010f1f679cc4e4b1d56a714690)) +* **internal:** make mcp-server publishing public by defaut ([a80dd72](https://github.com/julep-ai/node-sdk/commit/a80dd722b30a9f2448d873dd63084daa3377ebdc)) +* **internal:** more robust bootstrap script ([c909c52](https://github.com/julep-ai/node-sdk/commit/c909c52da759b9e89a8bcace1480981a4cba5bb1)) +* **internal:** move publish config ([e9e8777](https://github.com/julep-ai/node-sdk/commit/e9e87771a244b79cf4acfb3eac691a6ece19dd45)) +* **internal:** move stringifyQuery implementation to internal function ([cad2b7b](https://github.com/julep-ai/node-sdk/commit/cad2b7b8c34545252f9b884c6ac8285e459e6f57)) +* **internal:** refactor array check ([fd2fa21](https://github.com/julep-ai/node-sdk/commit/fd2fa21ceeef064b14d4db7282d0927170ec6103)) +* **internal:** refactor flag parsing for MCP servers and add debug flag ([108db42](https://github.com/julep-ai/node-sdk/commit/108db426ed1866c8e2406a485c3d4a71b1e6fba3)) +* **internal:** remove .eslintcache ([0a2fdd6](https://github.com/julep-ai/node-sdk/commit/0a2fdd60302b10b9b5ccfc25902cecacda32444a)) +* **internal:** remove deprecated `compilerOptions.baseUrl` from tsconfig.json ([1174a7f](https://github.com/julep-ai/node-sdk/commit/1174a7f5b63bb5cbb0233ecdb79bcb5562339b96)) +* **internal:** show error causes in MCP servers when running in local mode ([9c2dff4](https://github.com/julep-ai/node-sdk/commit/9c2dff4ec5e6e2241e47f54a94c6fbffb1f8711c)) +* **internal:** support custom-instructions-path flag in MCP servers ([df1f35d](https://github.com/julep-ai/node-sdk/commit/df1f35d538ca803c4032b364bbffe47182f58b5c)) +* **internal:** support local docs search in MCP servers ([ad564a7](https://github.com/julep-ai/node-sdk/commit/ad564a773a7c779f3dd97660ce8f74a95b1fe226)) +* **internal:** support oauth authorization code flow for MCP servers ([9b420b6](https://github.com/julep-ai/node-sdk/commit/9b420b6b55b1ade18395459adacade846e783e19)) +* **internal:** support type annotations when running MCP in local execution mode ([d714c04](https://github.com/julep-ai/node-sdk/commit/d714c042ae1097f32f42c556fb2b097dc5fb7402)) +* **internal:** support x-stainless-mcp-client-envs header in MCP servers ([308a359](https://github.com/julep-ai/node-sdk/commit/308a359e4a580c734483e2d119c139621fdb6e8f)) +* **internal:** support x-stainless-mcp-client-permissions headers in MCP servers ([6500bef](https://github.com/julep-ai/node-sdk/commit/6500bef012a4f29b5bdef0d93eb89c6a892b2779)) +* **internal:** tweak CI branches ([b4dec8a](https://github.com/julep-ai/node-sdk/commit/b4dec8a91c3d4ff4f0bd84f6fe150c0bd57029d5)) +* **internal:** update `actions/checkout` version ([54f6539](https://github.com/julep-ai/node-sdk/commit/54f65398dbd58cbf1160dd1c92ab9861a4da2789)) +* **internal:** update agents version ([16a7ae1](https://github.com/julep-ai/node-sdk/commit/16a7ae1613640e0ccce5a1d9d189e64d9802c92e)) +* **internal:** update comment in script ([dae9b0f](https://github.com/julep-ai/node-sdk/commit/dae9b0fbfa4616d0211eaff2747723e4eca697c8)) +* **internal:** update dependencies to address dependabot vulnerabilities ([f1c4c46](https://github.com/julep-ai/node-sdk/commit/f1c4c46854fae516e7480b37cc224f12ad7cbcd5)) +* **internal:** update gitignore ([a2b641d](https://github.com/julep-ai/node-sdk/commit/a2b641d32841de73be2c00d90a30447805749234)) +* **internal:** update multipart form array serialization ([c000412](https://github.com/julep-ai/node-sdk/commit/c0004120122f34872c492260713d55e1b7923475)) +* **internal:** upgrade @modelcontextprotocol/sdk and hono ([e671738](https://github.com/julep-ai/node-sdk/commit/e671738657f0a6a87d30991300304d6baa7a0ac9)) +* **internal:** upgrade babel, qs, js-yaml ([5e48026](https://github.com/julep-ai/node-sdk/commit/5e48026bdc5cdeac12dceddcf9f96ad03d296872)) +* **internal:** upgrade hono ([c2c19a7](https://github.com/julep-ai/node-sdk/commit/c2c19a72322d02cfc653436c23e31596e1893dc3)) +* **internal:** upgrade wrangler version ([b1dac50](https://github.com/julep-ai/node-sdk/commit/b1dac506dc6e3e1c8add18a2d34703a13d981049)) +* **internal:** use link instead of file in MCP server package.json files ([8aee377](https://github.com/julep-ai/node-sdk/commit/8aee3779d01868820a02e854e497418324de5096)) +* **internal:** use npm pack for build uploads ([5c45d4c](https://github.com/julep-ai/node-sdk/commit/5c45d4ca134673d609811ed299cf05454a2e0cfb)) +* **internal:** use x-stainless-mcp-client-envs header for MCP remote code tool calls ([ba5d919](https://github.com/julep-ai/node-sdk/commit/ba5d9193ed364a6d0db0166b31d22c2234c4d33e)) +* mcp code tool explicit error message when missing a run function ([bc1b68e](https://github.com/julep-ai/node-sdk/commit/bc1b68ef5d32565ed56f4906df6d92bd3e0e4593)) +* **mcp-server:** add support for session id, forward client info ([62a6382](https://github.com/julep-ai/node-sdk/commit/62a63823d02164db0334d1cdd5a2ca5488f94773)) +* **mcp-server:** improve instructions ([07cd946](https://github.com/julep-ai/node-sdk/commit/07cd9461f66fcac361591147d72644744ca731f5)) +* **mcp-server:** increase local docs search result count from 5 to 10 ([7b291ea](https://github.com/julep-ai/node-sdk/commit/7b291eadbf0a7d31c79d90cebc519bc97498237e)) +* **mcp-server:** log client info ([5c84254](https://github.com/julep-ai/node-sdk/commit/5c84254a1a14f91ba969a4fa26966b0b7696bfa3)) +* **mcp-server:** return access instructions for 404 without API key ([f8a6c88](https://github.com/julep-ai/node-sdk/commit/f8a6c88cd963f6bd9f8ccfe4ef1690f8351ea7b2)) +* **mcp:** add cors to oauth metadata route ([cc312bc](https://github.com/julep-ai/node-sdk/commit/cc312bcae0e89e1d92eb515ec80fc761d5e91729)) +* **mcp:** add friendlier MCP code tool errors on incorrect method invocations ([3341c0e](https://github.com/julep-ai/node-sdk/commit/3341c0e1190583108b1e17a867a30c9b6d42e316)) +* **mcp:** add intent param to execute tool ([d460803](https://github.com/julep-ai/node-sdk/commit/d460803b844c4d3d700a4e7826e4a5520fe05871)) +* **mcp:** add line numbers to code tool errors ([70bf6d0](https://github.com/julep-ai/node-sdk/commit/70bf6d073837b67d3f2b7dee3bc5a623822c3388)) +* **mcp:** allow pointing `docs_search` tool at other URLs ([29f1271](https://github.com/julep-ai/node-sdk/commit/29f127175999ad32c2b29a6148c00b101a5f2910)) +* **mcp:** clarify http auth error ([b8623e9](https://github.com/julep-ai/node-sdk/commit/b8623e9a8d4ab974c59be1c29ffdacdd5e1e6d8e)) +* **mcp:** correctly update version in sync with sdk ([a4bb9b3](https://github.com/julep-ai/node-sdk/commit/a4bb9b3004e2868c7b0c06d6a69f0ba63f6cbd58)) +* **mcp:** document remote server in README.md ([0b818f2](https://github.com/julep-ai/node-sdk/commit/0b818f293d83c27fd26f625a69f3e17ead61bbea)) +* **mcp:** forward STAINLESS_API_KEY to docs search endpoint ([61599b5](https://github.com/julep-ai/node-sdk/commit/61599b500ca055a28b2b4cc5841e536bc80c7a74)) +* **mcp:** minor cleanup of types and package.json ([dc011fe](https://github.com/julep-ai/node-sdk/commit/dc011fea568e36e556942012656a4cfcc4ccd101)) +* **mcp:** pass intent param to execute handler ([0b69eda](https://github.com/julep-ai/node-sdk/commit/0b69edaec0332ac1da055ffea2a9c21d2369d816)) +* **mcp:** refactor streamable http transport ([00cb972](https://github.com/julep-ai/node-sdk/commit/00cb972cf1a90c69d879c10d0417e0429f76b88d)) +* **mcp:** remove deprecated tool schemes ([b4e317d](https://github.com/julep-ai/node-sdk/commit/b4e317d26cb44741474c41bd0249382579ed0ecd)) +* **mcp:** rename dxt to mcpb ([551b096](https://github.com/julep-ai/node-sdk/commit/551b0962e23197a7f840e72481bfd1957603ddff)) +* **mcp:** up tsconfig lib version to es2022 ([9efac91](https://github.com/julep-ai/node-sdk/commit/9efac91e636f769f646ef02324a7770f50d51708)) +* **mcp:** update lockfile ([5b0ef90](https://github.com/julep-ai/node-sdk/commit/5b0ef90b166d510f91594c4ab1782466b3d2eebd)) +* **mcp:** update package.json ([0d2ee4b](https://github.com/julep-ai/node-sdk/commit/0d2ee4b3982034605a315adfdc2aecc6a6fbc5bd)) +* **mcp:** update README ([0d69b8d](https://github.com/julep-ai/node-sdk/commit/0d69b8d3a63228328cadcaeb97f8ec06942f691e)) +* **mcp:** update types ([6b6f77c](https://github.com/julep-ai/node-sdk/commit/6b6f77c4742e866839b0edb290422f61c96e44c2)) +* **mcp:** upgrade dependencies ([4977c78](https://github.com/julep-ai/node-sdk/commit/4977c7802647aee4e24eb2c9c3200e0329d4b1f4)) +* **mcp:** upgrade jq-web ([62ddb5b](https://github.com/julep-ai/node-sdk/commit/62ddb5b12b87bcf76501eb3819524f1b06e48113)) +* **mcp:** upload dxt as release asset ([8565331](https://github.com/julep-ai/node-sdk/commit/856533108438a2ff00f3cbf84debf022ad4b2658)) +* **test:** do not count install time for mock server timeout ([92d3dfe](https://github.com/julep-ai/node-sdk/commit/92d3dfe223f91ddd34a7346cec7c00975a95ccc0)) +* **tests:** bump steady to v0.19.4 ([6b489ac](https://github.com/julep-ai/node-sdk/commit/6b489acd994c26ad41e9fecb51868ea99d9201fa)) +* **tests:** bump steady to v0.19.5 ([c586f1c](https://github.com/julep-ai/node-sdk/commit/c586f1c06174419e92c09eaf12d67a4f2d11f377)) +* **tests:** bump steady to v0.19.6 ([cea3a42](https://github.com/julep-ai/node-sdk/commit/cea3a427b3892ae8b11bb88c5fbe24efd64ecae4)) +* **tests:** bump steady to v0.19.7 ([d1b6fb6](https://github.com/julep-ai/node-sdk/commit/d1b6fb68d35f590a5537456fa610e890b1e7557f)) +* **tests:** bump steady to v0.20.1 ([1b517ca](https://github.com/julep-ai/node-sdk/commit/1b517ca83d26e891003d505b594330c31c035300)) +* **tests:** bump steady to v0.20.2 ([4bfc8ce](https://github.com/julep-ai/node-sdk/commit/4bfc8ced1f908f2013bcaa25e97b5951349a8a84)) +* **tests:** bump steady to v0.22.1 ([8f72b8f](https://github.com/julep-ai/node-sdk/commit/8f72b8f568c96e1ee269c45650f4e24b7a6ba277)) +* update @stainless-api/prism-cli to v5.15.0 ([3237c3b](https://github.com/julep-ai/node-sdk/commit/3237c3b7e985e59c6459fbda21f3e79353187df1)) +* update CI script ([eb0d865](https://github.com/julep-ai/node-sdk/commit/eb0d8650804d09b5a0ffe4561250b1e82eaf979d)) +* update lockfile ([1fb9f41](https://github.com/julep-ai/node-sdk/commit/1fb9f4126f4c5a17a4317dcdf514100a10a94f69)) +* update mock server docs ([ef69c2e](https://github.com/julep-ai/node-sdk/commit/ef69c2ecd67b8b90652df07d612d81c67a69414c)) +* use latest @modelcontextprotocol/sdk ([4832ece](https://github.com/julep-ai/node-sdk/commit/4832ececf6d2f9a78b9ca32a17b03a5b232fec37)) +* use proper capitalization for WebSockets ([dd98637](https://github.com/julep-ai/node-sdk/commit/dd986372ba78b063eb06147d109c74a6129e29c7)) +* use structured error when code execution tool errors ([db94eb7](https://github.com/julep-ai/node-sdk/commit/db94eb75a0897677db72191a268c49129c7c5183)) + + +### Documentation + +* **mcp:** add a README button for one-click add to Cursor ([e925a0f](https://github.com/julep-ai/node-sdk/commit/e925a0f9524b70237decae3ee8351378e5fc700a)) +* **mcp:** add a README link to add server to VS Code or Claude Code ([72a04d7](https://github.com/julep-ai/node-sdk/commit/72a04d70df45bd08ef155dec5e09f540abcd9abd)) +* prominently feature MCP server setup in root SDK readmes ([a473911](https://github.com/julep-ai/node-sdk/commit/a47391196153cd1e9dcce4ab0113f9eb4b80be9c)) + + +### Refactors + +* **tests:** switch from prism to steady ([ecea3ce](https://github.com/julep-ai/node-sdk/commit/ecea3ceed546c240b31e359acb55fcc4df4d03ef)) + ## 2.7.4 (2025-08-01) Full Changelog: [v2.7.3...v2.7.4](https://github.com/julep-ai/node-sdk/compare/v2.7.3...v2.7.4) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8adea49e..8b3147f8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,15 +60,15 @@ $ yarn link @julep/sdk # With pnpm $ pnpm link --global $ cd ../my-package -$ pnpm link -—global @julep/sdk +$ pnpm link --global @julep/sdk ``` ## Running tests -Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. +Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests. ```sh -$ npx prism mock path/to/your/openapi.yml +$ ./scripts/mock ``` ```sh diff --git a/LICENSE b/LICENSE index 8f072ef6..6a30d4f9 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2025 Julep + Copyright 2026 Julep Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 3f3fb61d..7a6b8b06 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,15 @@ The REST API documentation can be found on [docs.julep.ai](https://docs.julep.ai It is generated with [Stainless](https://www.stainless.com/). +## MCP Server + +Use the Julep MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application. + +[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=%40julep%2Fsdk-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBqdWxlcC9zZGstbWNwIl0sImVudiI6eyJKVUxFUF9BUElfS0VZIjoiTXkgQVBJIEtleSJ9fQ) +[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40julep%2Fsdk-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40julep%2Fsdk-mcp%22%5D%2C%22env%22%3A%7B%22JULEP_API_KEY%22%3A%22My%20API%20Key%22%7D%7D) + +> Note: You may need to set environment variables in your MCP client. + ## Installation ```sh @@ -53,7 +62,10 @@ const params: Julep.AgentCreateOrUpdateParams = { instructions: ['Protect Leia', 'Kick butt'], model: 'o1-preview', }; -const agent: Julep.Agent = await client.agents.createOrUpdate('dad00000-0000-4000-a000-000000000000', params); +const agent: Julep.Agent = await client.agents.createOrUpdate( + 'dad00000-0000-4000-a000-000000000000', + params, +); ``` Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors. @@ -112,7 +124,11 @@ const client = new Julep({ }); // Or, configure per-request: -await client.agents.createOrUpdate('dad00000-0000-4000-a000-000000000000', { name: 'R2D2', instructions: ['Protect Leia', 'Kick butt'], model: 'o1-preview' }, { +await client.agents.createOrUpdate('dad00000-0000-4000-a000-000000000000', { + name: 'R2D2', + instructions: ['Protect Leia', 'Kick butt'], + model: 'o1-preview', +}, { maxRetries: 5, }); ``` @@ -129,7 +145,11 @@ const client = new Julep({ }); // Override per-request: -await client.agents.createOrUpdate('dad00000-0000-4000-a000-000000000000', { name: 'R2D2', instructions: ['Protect Leia', 'Kick butt'], model: 'o1-preview' }, { +await client.agents.createOrUpdate('dad00000-0000-4000-a000-000000000000', { + name: 'R2D2', + instructions: ['Protect Leia', 'Kick butt'], + model: 'o1-preview', +}, { timeout: 5 * 1000, }); ``` @@ -305,7 +325,11 @@ const client = new Julep({ // Override per-request: await client.agents.createOrUpdate( 'dad00000-0000-4000-a000-000000000000', - { name: 'R2D2', instructions: ['Protect Leia', 'Kick butt'], model: 'o1-preview' }, + { + name: 'R2D2', + instructions: ['Protect Leia', 'Kick butt'], + model: 'o1-preview', + }, { httpAgent: new http.Agent({ keepAlive: false }), }, diff --git a/bin/publish-npm b/bin/publish-npm index fa2243d2..45e8aa80 100644 --- a/bin/publish-npm +++ b/bin/publish-npm @@ -58,4 +58,4 @@ else fi # Publish with the appropriate tag -yarn publish --access public --tag "$TAG" +yarn publish --tag "$TAG" diff --git a/package.json b/package.json index d5035477..ef3b3d3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@julep/sdk", - "version": "2.7.4", + "version": "3.0.0", "description": "The official TypeScript library for the Julep API", "author": "Julep ", "types": "dist/index.d.ts", @@ -13,6 +13,9 @@ "**/*" ], "private": false, + "publishConfig": { + "access": "public" + }, "scripts": { "test": "./scripts/test", "build": "./scripts/build", diff --git a/packages/mcp-server/Dockerfile b/packages/mcp-server/Dockerfile new file mode 100644 index 00000000..a42f1e2a --- /dev/null +++ b/packages/mcp-server/Dockerfile @@ -0,0 +1,78 @@ +# Dockerfile for Julep MCP Server +# +# This Dockerfile builds a Docker image for the MCP Server. +# +# To build the image locally: +# docker build -f packages/mcp-server/Dockerfile -t @julep/sdk-mcp:local . +# +# To run the image: +# docker run -i @julep/sdk-mcp:local [OPTIONS] +# +# Common options: +# --tool= Include specific tools +# --resource= Include tools for specific resources +# --operation=read|write Filter by operation type +# --client= Set client compatibility (e.g., claude, cursor) +# --transport= Set transport type (stdio or http) +# +# For a full list of options: +# docker run -i @julep/sdk-mcp:local --help +# +# Note: The MCP server uses stdio transport by default. Docker's -i flag +# enables interactive mode, allowing the container to communicate over stdin/stdout. + +# Build stage +FROM node:24-alpine AS builder + +# Install bash for build script +RUN apk add --no-cache bash openssl + +# Set working directory +WORKDIR /build + +# Copy entire repository +COPY . . + +# Install all dependencies and build everything +RUN yarn install --frozen-lockfile && \ + yarn build && \ + # Remove the symlink to the SDK so it doesn't interfere with the explicit COPY below + rm -Rf packages/mcp-server/node_modules/@julep/sdk + +FROM denoland/deno:alpine-2.7.1 + +# Install node and npm +RUN apk add --no-cache nodejs npm + +ENV LD_LIBRARY_PATH=/usr/lib:/usr/local/lib + +# Add non-root user +RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001 + +# Set working directory +WORKDIR /app + +# Copy the built mcp-server dist directory +COPY --from=builder /build/packages/mcp-server/dist ./ + +# Copy node_modules from mcp-server (includes all production deps) +COPY --from=builder /build/packages/mcp-server/node_modules ./node_modules + +# Copy the built @julep/sdk into node_modules +COPY --from=builder /build/dist ./node_modules/@julep/sdk + +# Change ownership to nodejs user +RUN chown -R nodejs:nodejs /app +RUN chown -R nodejs:nodejs /deno-dir + +# Switch to non-root user +USER nodejs + +# The MCP server uses stdio transport by default +# No exposed ports needed for stdio communication + +# Set the entrypoint to the MCP server +ENTRYPOINT ["node", "index.js"] + +# Allow passing arguments to the MCP server +CMD [] diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index bc08edf8..2b8a9b16 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -26,7 +26,7 @@ For clients with a configuration JSON, it might look something like this: "mcpServers": { "julep_sdk_api": { "command": "npx", - "args": ["-y", "@julep/sdk-mcp", "--client=claude", "--tools=dynamic"], + "args": ["-y", "@julep/sdk-mcp"], "env": { "JULEP_API_KEY": "My API Key", "JULEP_ENVIRONMENT": "production" @@ -36,350 +36,68 @@ For clients with a configuration JSON, it might look something like this: } ``` -## Exposing endpoints to your MCP Client +### Cursor -There are two ways to expose endpoints as tools in the MCP server: +If you use Cursor, you can install the MCP server by using the button below. You will need to set your environment variables +in Cursor's `mcp.json`, which can be found in Cursor Settings > Tools & MCP > New MCP Server. -1. Exposing one tool per endpoint, and filtering as necessary -2. Exposing a set of tools to dynamically discover and invoke endpoints from the API +[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=%40julep%2Fsdk-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBqdWxlcC9zZGstbWNwIl0sImVudiI6eyJKVUxFUF9BUElfS0VZIjoiTXkgQVBJIEtleSJ9fQ) -### Filtering endpoints and tools +### VS Code -You can run the package on the command line to discover and filter the set of tools that are exposed by the -MCP Server. This can be helpful for large APIs where including all endpoints at once is too much for your AI's -context window. +If you use MCP, you can install the MCP server by clicking the link below. You will need to set your environment variables +in VS Code's `mcp.json`, which can be found via Command Palette > MCP: Open User Configuration. -You can filter by multiple aspects: +[Open VS Code](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40julep%2Fsdk-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40julep%2Fsdk-mcp%22%5D%2C%22env%22%3A%7B%22JULEP_API_KEY%22%3A%22My%20API%20Key%22%7D%7D) -- `--tool` includes a specific tool by name -- `--resource` includes all tools under a specific resource, and can have wildcards, e.g. `my.resource*` -- `--operation` includes just read (get/list) or just write operations +### Claude Code -### Dynamic tools +If you use Claude Code, you can install the MCP server by running the command below in your terminal. You will need to set your +environment variables in Claude Code's `.claude.json`, which can be found in your home directory. -If you specify `--tools=dynamic` to the MCP server, instead of exposing one tool per endpoint in the API, it will -expose the following tools: - -1. `list_api_endpoints` - Discovers available endpoints, with optional filtering by search query -2. `get_api_endpoint_schema` - Gets detailed schema information for a specific endpoint -3. `invoke_api_endpoint` - Executes any endpoint with the appropriate parameters - -This allows you to have the full set of API endpoints available to your MCP Client, while not requiring that all -of their schemas be loaded into context at once. Instead, the LLM will automatically use these tools together to -search for, look up, and invoke endpoints dynamically. However, due to the indirect nature of the schemas, it -can struggle to provide the correct properties a bit more than when tools are imported explicitly. Therefore, -you can opt-in to explicit tools, the dynamic tools, or both. - -See more information with `--help`. - -All of these command-line options can be repeated, combined together, and have corresponding exclusion versions (e.g. `--no-tool`). - -Use `--list` to see the list of available tools, or see below. - -### Specifying the MCP Client - -Different clients have varying abilities to handle arbitrary tools and schemas. - -You can specify the client you are using with the `--client` argument, and the MCP server will automatically -serve tools and schemas that are more compatible with that client. - -- `--client=`: Set all capabilities based on a known MCP client - - - Valid values: `openai-agents`, `claude`, `claude-code`, `cursor` - - Example: `--client=cursor` - -Additionally, if you have a client not on the above list, or the client has gotten better -over time, you can manually enable or disable certain capabilities: - -- `--capability=`: Specify individual client capabilities - - Available capabilities: - - `top-level-unions`: Enable support for top-level unions in tool schemas - - `valid-json`: Enable JSON string parsing for arguments - - `refs`: Enable support for $ref pointers in schemas - - `unions`: Enable support for union types (anyOf) in schemas - - `formats`: Enable support for format validations in schemas (e.g. date-time, email) - - `tool-name-length=N`: Set maximum tool name length to N characters - - Example: `--capability=top-level-unions --capability=tool-name-length=40` - - Example: `--capability=top-level-unions,tool-name-length=40` - -### Examples - -1. Filter for read operations on cards: - -```bash ---resource=cards --operation=read ``` - -2. Exclude specific tools while including others: - -```bash ---resource=cards --no-tool=create_cards +claude mcp add julep_sdk_mcp_api --env JULEP_API_KEY="My API Key" -- npx -y @julep/sdk-mcp ``` -3. Configure for Cursor client with custom max tool name length: - -```bash ---client=cursor --capability=tool-name-length=40 -``` +## Code Mode -4. Complex filtering with multiple criteria: +This MCP server is built on the "Code Mode" tool scheme. In this MCP Server, +your agent will write code against the TypeScript SDK, which will then be executed in an +isolated sandbox. To accomplish this, the server will expose two tools to your agent: -```bash ---resource=cards,accounts --operation=read --tag=kyc --no-tool=create_cards -``` +- The first tool is a docs search tool, which can be used to generically query for + documentation about your API/SDK. -## Importing the tools and server individually +- The second tool is a code tool, where the agent can write code against the TypeScript SDK. + The code will be executed in a sandbox environment without web or filesystem access. Then, + anything the code returns or prints will be returned to the agent as the result of the + tool call. -```js -// Import the server, generated endpoints, or the init function -import { server, endpoints, init } from "@julep/sdk-mcp/server"; +Using this scheme, agents are capable of performing very complex tasks deterministically +and repeatably. -// import a specific tool -import createAgents from "@julep/sdk-mcp/tools/agents/create-agents"; +## Running remotely -// initialize the server and all endpoints -init({ server, endpoints }); +Launching the client with `--transport=http` launches the server as a remote server using Streamable HTTP transport. The `--port` setting can choose the port it will run on, and the `--socket` setting allows it to run on a Unix socket. -// manually start server -const transport = new StdioServerTransport(); -await server.connect(transport); +Authorization can be provided via the `Authorization` header using the Bearer scheme. -// or initialize your own server with specific tools -const myServer = new McpServer(...); +Additionally, authorization can be provided via the following headers: +| Header | Equivalent client option | Security scheme | +| ----------------- | ------------------------ | --------------- | +| `x-julep-api-key` | `apiKey` | APIKeyHeader | -// define your own endpoint -const myCustomEndpoint = { - tool: { - name: 'my_custom_tool', - description: 'My custom tool', - inputSchema: zodToJsonSchema(z.object({ a_property: z.string() })), - }, - handler: async (client: client, args: any) => { - return { myResponse: 'Hello world!' }; - }) -}; +A configuration JSON for this server might look like this, assuming the server is hosted at `http://localhost:3000`: -// initialize the server with your custom endpoints -init({ server: myServer, endpoints: [createAgents, myCustomEndpoint] }); +```json +{ + "mcpServers": { + "julep_sdk_api": { + "url": "http://localhost:3000", + "headers": { + "Authorization": "Bearer " + } + } + } +} ``` - -## Available Tools - -The following tools are available in this MCP server. - -### Resource `agents`: - -- `create_agents` (`write`): Create Agent -- `update_agents` (`write`): Patch Agent -- `list_agents` (`read`): List Agents -- `delete_agents` (`write`): Delete Agent -- `create_or_update_agents` (`write`): Create Or Update Agent -- `get_agents` (`read`): Get Agent Details -- `list_models_agents` (`read`): List all available models that can be used with agents. - - Returns: - ListModelsResponse: A list of available models - -- `reset_agents` (`write`): Update Agent - -### Resource `agents.tools`: - -- `create_agents_tools` (`write`): Create Agent Tool -- `update_agents_tools` (`write`): Patch Agent Tool -- `list_agents_tools` (`read`): List Agent Tools -- `delete_agents_tools` (`write`): Delete Agent Tool -- `reset_agents_tools` (`write`): Update Agent Tool - -### Resource `agents.docs`: - -- `create_agents_docs` (`write`): Create Agent Doc -- `list_agents_docs` (`read`): List Agent Docs -- `delete_agents_docs` (`write`): Delete Agent Doc -- `bulk_delete_agents_docs` (`write`): Bulk delete documents owned by an agent based on metadata filter -- `search_agents_docs` (`write`): Searches for documents associated with a specific agent. - - Parameters: - x_developer_id (UUID): The unique identifier of the developer associated with the agent. - search_params (TextOnlyDocSearchRequest | VectorDocSearchRequest | HybridDocSearchRequest): The parameters for the search. - agent_id (UUID): The umnique identifier of the agent associated with the documents. - Returns: - DocSearchResponse: The search results. - -### Resource `files`: - -- `create_files` (`write`): Create File -- `list_files` (`read`): List Files -- `delete_files` (`write`): Delete File -- `get_files` (`read`): Get File - -### Resource `sessions`: - -- `create_sessions` (`write`): Create Session -- `update_sessions` (`write`): Patch Session -- `list_sessions` (`read`): List Sessions -- `delete_sessions` (`write`): Delete Session -- `chat_sessions` (`write`): Initiates a chat session. - - Routes to different implementations based on feature flags: - - - If auto_run_tools_chat feature flag is enabled, uses the new auto-tools implementation - - Otherwise, uses the legacy implementation - - Parameters: - developer (Developer): The developer associated with the chat session. - session_id (UUID): The unique identifier of the chat session. - chat_input (ChatInput): The chat input data. - background_tasks (BackgroundTasks): The background tasks to run. - x_custom_api_key (Optional[str]): The custom API key. - mock_response (Optional[str]): Mock response for testing. - connection_pool: Connection pool for testing purposes. - - Returns: - ChatResponse or StreamingResponse: The chat response or streaming response. - -- `create_or_update_sessions` (`write`): Create Or Update Session -- `get_sessions` (`read`): Get Session -- `history_sessions` (`read`): Get Session History -- `render_sessions` (`write`): Renders a chat input. - - Routes to different implementations based on feature flags: - - - If auto_run_tools_chat feature flag is enabled, uses the new auto-tools implementation - - Otherwise, uses the legacy implementation - - Parameters: - developer (Developer): The developer associated with the chat session. - session_id (UUID): The unique identifier of the chat session. - chat_input (ChatInput): The chat input data. - - Returns: - RenderResponse: The rendered chat input. - -- `reset_sessions` (`write`): Update Session - -### Resource `users`: - -- `create_users` (`write`): Create User -- `update_users` (`write`): Patch User -- `list_users` (`read`): List Users -- `delete_users` (`write`): Delete User -- `create_or_update_users` (`write`): Create Or Update User -- `get_users` (`read`): Get User Details -- `reset_users` (`write`): Update User - -### Resource `users.docs`: - -- `create_users_docs` (`write`): Creates a new document for a user. - - Parameters: - user_id (UUID): The unique identifier of the user associated with the document. - data (CreateDocRequest): The data to create the document with. - x_developer_id (UUID): The unique identifier of the developer associated with the document. - - Returns: - Doc: The created document. - -- `list_users_docs` (`read`): List User Docs -- `delete_users_docs` (`write`): Delete User Doc -- `bulk_delete_users_docs` (`write`): Bulk delete documents owned by a user based on metadata filter -- `search_users_docs` (`write`): Searches for documents associated with a specific user. - - Parameters: - x_developer_id (UUID): The unique identifier of the developer associated with the user. - search_params (TextOnlyDocSearchRequest | VectorDocSearchRequest | HybridDocSearchRequest): The parameters for the search. - user_id (UUID): The unique identifier of the user associated with the documents. - Returns: - DocSearchResponse: The search results. - -### Resource `jobs`: - -- `get_jobs` (`read`): Get Job Status - -### Resource `docs`: - -- `embed_docs` (`write`): Embed -- `get_docs` (`read`): Get Doc - -### Resource `tasks`: - -- `create_tasks` (`write`): Create Task -- `list_tasks` (`read`): List Tasks -- `create_or_update_tasks` (`write`): Create Or Update Task -- `get_tasks` (`read`): Get Task Details - -### Resource `executions`: - -- `create_executions` (`write`): Create Task Execution -- `list_executions` (`read`): List Task Executions -- `change_status_executions` (`write`): Update Execution -- `get_executions` (`read`): Get Execution Details - -### Resource `executions.transitions`: - -- `retrieve_executions_transitions` (`read`): Get Execution Transition -- `list_executions_transitions` (`read`): List Execution Transitions -- `stream_executions_transitions` (`read`): Stream Transitions Events - -### Resource `executions.status`: - -- `get_executions_status` (`read`): Get Execution Details -- `stream_executions_status` (`read`): SSE endpoint that streams the status of a given execution_id by polling the - latest_executions view. - -### Resource `secrets`: - -- `create_secrets` (`write`): Create a new secret for a developer. - - Args: - developer_id: ID of the developer creating the secret - secret: Secret to create - - Returns: - The created secret - - Raises: - HTTPException: If a secret with this name already exists (409 Conflict) - -- `update_secrets` (`write`): Update a developer secret. - - Args: - developer_id: ID of the developer who owns the secret - secret_id: ID of the secret to update - data: New secret data - - Returns: - The updated secret - - Raises: - HTTPException: If the secret doesn't exist or doesn't belong to the developer - -- `list_secrets` (`read`): List all secrets for a developer. - - Args: - x_developer_id: ID of the developer whose secrets to list - limit: Maximum number of secrets to return - offset: Number of secrets to skip - - Returns: - List of secrets - -- `delete_secrets` (`write`): Delete a secret. - - Args: - secret_id: ID of the secret to delete - x_developer_id: ID of the developer who owns the secret - - Returns: - The deleted secret - - Raises: - HTTPException: If the secret doesn't exist - -### Resource `projects`: - -- `create_projects` (`write`): Create Project -- `list_projects` (`read`): List Projects - -### Resource `healthz`: - -- `check_healthz` (`read`): Check Health diff --git a/packages/mcp-server/build b/packages/mcp-server/build index b6db4f70..8af7357b 100644 --- a/packages/mcp-server/build +++ b/packages/mcp-server/build @@ -30,3 +30,27 @@ cp tsconfig.dist-src.json dist/src/tsconfig.json chmod +x dist/index.js DIST_PATH=./dist PKG_IMPORT_PATH=@julep/sdk-mcp/ node ../../scripts/utils/postprocess-files.cjs + +# mcp bundle +rm -rf dist-bundle julep_sdk_api.mcpb; mkdir dist-bundle + +# copy package.json +PKG_JSON_PATH=../../packages/mcp-server/package.json node ../../scripts/utils/make-dist-package-json.cjs > dist-bundle/package.json + +# copy files +node scripts/copy-bundle-files.cjs + +# install runtime deps +cd dist-bundle +npm install +cd .. + +# pack bundle +cp manifest.json dist-bundle + +npx mcpb pack dist-bundle julep_sdk_api.mcpb + +npx mcpb sign julep_sdk_api.mcpb --self-signed + +# clean up +rm -rf dist-bundle diff --git a/packages/mcp-server/cloudflare-worker/biome.json b/packages/mcp-server/cloudflare-worker/biome.json index ecd40b64..13b23ad3 100644 --- a/packages/mcp-server/cloudflare-worker/biome.json +++ b/packages/mcp-server/cloudflare-worker/biome.json @@ -1,34 +1,34 @@ { - "$schema": "https://biomejs.dev/schemas/1.6.2/schema.json", - "organizeImports": { - "enabled": true - }, - "files": { - "ignore": ["worker-configuration.d.ts"] - }, - "vcs": { - "enabled": true, - "clientKind": "git", - "useIgnoreFile": true - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "suspicious": { - "noExplicitAny": "off", - "noDebugger": "off", - "noConsoleLog": "off", - "noConfusingVoidType": "off" - }, - "style": { - "noNonNullAssertion": "off" - } - } - }, - "formatter": { - "enabled": true, - "indentWidth": 4, - "lineWidth": 100 - } + "$schema": "https://biomejs.dev/schemas/1.6.2/schema.json", + "organizeImports": { + "enabled": true + }, + "files": { + "ignore": ["worker-configuration.d.ts"] + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off", + "noDebugger": "off", + "noConsoleLog": "off", + "noConfusingVoidType": "off" + }, + "style": { + "noNonNullAssertion": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentWidth": 4, + "lineWidth": 100 + } } diff --git a/packages/mcp-server/cloudflare-worker/package.json b/packages/mcp-server/cloudflare-worker/package.json index 52f35f0a..56e7d9d9 100644 --- a/packages/mcp-server/cloudflare-worker/package.json +++ b/packages/mcp-server/cloudflare-worker/package.json @@ -14,14 +14,26 @@ "marked": "^15.0.11", "typescript": "^5.8.3", "workers-mcp": "0.1.0-3", - "wrangler": "^4.15.2" + "wrangler": "^4.59.1" }, "dependencies": { "@cloudflare/workers-oauth-provider": "^0.0.5", - "@modelcontextprotocol/sdk": "^1.11.4", - "agents": "^0.0.88", - "hono": "^4.7.9", + "@modelcontextprotocol/sdk": "1.28.0", + "agents": "~0.8.7", + "hono": "^4.12.4", "@julep/sdk-mcp": "latest", - "zod": "^3.24.4" + "@julep/sdk": "latest" + }, + "// overrides": "agents pins @modelcontextprotocol/sdk exactly; the three override blocks below force npm/yarn/pnpm to dedupe to that same version. Keep all four @modelcontextprotocol/sdk values in sync. See src/index.ts for the full rationale.", + "overrides": { + "@modelcontextprotocol/sdk": "1.28.0" + }, + "resolutions": { + "@modelcontextprotocol/sdk": "1.28.0" + }, + "pnpm": { + "overrides": { + "@modelcontextprotocol/sdk": "1.28.0" + } } } diff --git a/packages/mcp-server/cloudflare-worker/src/app.ts b/packages/mcp-server/cloudflare-worker/src/app.ts index 4802e57b..5e3a8ec4 100644 --- a/packages/mcp-server/cloudflare-worker/src/app.ts +++ b/packages/mcp-server/cloudflare-worker/src/app.ts @@ -8,14 +8,13 @@ import { renderAuthorizationRejectedContent, } from './utils'; import type { OAuthHelpers } from '@cloudflare/workers-oauth-provider'; -import { McpOptions } from '@julep/sdk-mcp/server'; import { ServerConfig } from '.'; export type Bindings = Env & { OAUTH_PROVIDER: OAuthHelpers; }; -export function makeOAuthConsent(config: ServerConfig, defaultOptions?: Partial) { +export function makeOAuthConsent(config: ServerConfig) { const app = new Hono<{ Bindings: Bindings; }>(); @@ -30,17 +29,14 @@ export function makeOAuthConsent(config: ServerConfig, defaultOptions?: Partial< app.get('/authorize', async (c) => { const oauthReqInfo = await c.env.OAUTH_PROVIDER.parseAuthRequest(c.req.raw); - const content = await renderLoggedOutAuthorizeScreen(config, oauthReqInfo, defaultOptions); + const content = await renderLoggedOutAuthorizeScreen(config, oauthReqInfo); return c.html(layout(content, 'Authorization', config)); }); // This endpoint is responsible for validating any login information and // then completing the authorization request with the OAUTH_PROVIDER app.post('/approve', async (c) => { - const { action, oauthReqInfo, clientProps, clientConfig } = await parseApproveFormBody( - await c.req.parseBody(), - config, - ); + const { action, oauthReqInfo, clientProps } = await parseApproveFormBody(await c.req.parseBody(), config); if (action !== 'login_approve') { return c.html( @@ -52,7 +48,7 @@ export function makeOAuthConsent(config: ServerConfig, defaultOptions?: Partial< ); } - if (!oauthReqInfo || !clientProps || !clientConfig) { + if (!oauthReqInfo || !clientProps) { return c.html('INVALID LOGIN', 401); } @@ -67,7 +63,6 @@ export function makeOAuthConsent(config: ServerConfig, defaultOptions?: Partial< scope: oauthReqInfo.scope, props: { clientProps, - clientConfig, }, }); @@ -78,7 +73,7 @@ export function makeOAuthConsent(config: ServerConfig, defaultOptions?: Partial< // Render the authorize screen for demoing the OAuth flow (it won't actually log in) app.get('/demo', async (c) => { - const content = await renderLoggedOutAuthorizeScreen(config, {} as any, defaultOptions); + const content = await renderLoggedOutAuthorizeScreen(config, {} as any); return c.html(layout(content, 'Authorization', config)); }); diff --git a/packages/mcp-server/cloudflare-worker/src/index.ts b/packages/mcp-server/cloudflare-worker/src/index.ts index 508ae5a8..3fcb64bc 100644 --- a/packages/mcp-server/cloudflare-worker/src/index.ts +++ b/packages/mcp-server/cloudflare-worker/src/index.ts @@ -1,7 +1,18 @@ import { makeOAuthConsent } from './app'; +// `agents` and `@modelcontextprotocol/sdk` versions must stay in sync with the +// pins/overrides in package.json. `agents` declares an exact pin on +// `@modelcontextprotocol/sdk`; if our resolved version drifts, npm installs a +// second copy under `agents/node_modules/`, and `initMcpServer`'s runtime +// `instanceof McpServer` check fails because the two `McpServer` classes are +// distinct constructors. import { McpAgent } from 'agents/mcp'; +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import OAuthProvider from '@cloudflare/workers-oauth-provider'; -import { McpOptions, initMcpServer, server, ClientOptions } from '@julep/sdk-mcp/server'; +import { ClientOptions } from '@julep/sdk'; +import { McpOptions } from '@julep/sdk-mcp/options'; +import { initMcpServer, newMcpServer } from '@julep/sdk-mcp/server'; +import { configureLogger } from '@julep/sdk-mcp/logger'; +import type { ExportedHandler } from '@cloudflare/workers-types'; type MCPProps = { clientProps: ClientOptions; @@ -43,15 +54,74 @@ const serverConfig: ServerConfig = { ], }; +// `newMcpServer` fetches MCP server instructions from the Stainless API. In a +// Durable Object, that fetch happens inside `blockConcurrencyWhile`; if it +// hangs the DO is reset, and if it rejects the same thing happens. Race +// against a short timeout and catch any rejection so any failure mode lands +// on a fallback server constructed without instructions (the `initialize` +// response simply omits the `instructions` field, which is spec-allowed). +const INSTRUCTIONS_FETCH_TIMEOUT_MS = 5000; + +function fallbackMcpServer(): McpServer { + return new McpServer( + { name: 'julep_sdk_api', version: '2.7.4' }, + { capabilities: { tools: {}, logging: {} } }, + ); +} + +async function buildMcpServer(stainlessApiKey?: string): Promise { + let timeoutId: ReturnType | undefined; + try { + const fetched = newMcpServer({ stainlessApiKey }); + const timeout = new Promise((resolve) => { + timeoutId = setTimeout(() => resolve(null), INSTRUCTIONS_FETCH_TIMEOUT_MS); + }); + + const result = await Promise.race([fetched, timeout]); + + if (result != null) { + return result; + } + } catch (error) { + console.error('Failed to build MCP server from upstream instructions; using fallback', error); + } finally { + if (timeoutId != null) { + clearTimeout(timeoutId); + } + } + + return fallbackMcpServer(); +} + export class MyMCP extends McpAgent { - server = server; + #resolveServer!: (server: McpServer) => void; + #rejectServer!: (error: unknown) => void; + server: Promise = new Promise((resolve, reject) => { + this.#resolveServer = resolve; + this.#rejectServer = reject; + }); async init() { - initMcpServer({ - server: this.server, - clientOptions: this.props.clientProps, - mcpOptions: this.props.clientConfig, - }); + try { + if (this.props == null) { + throw new Error('MCP props are not initialized'); + } + + configureLogger({ level: 'info', pretty: false }); + + const server = await buildMcpServer(this.props.clientConfig?.stainlessApiKey); + + await initMcpServer({ + server, + clientOptions: this.props.clientProps, + mcpOptions: this.props.clientConfig, + }); + + this.#resolveServer(server); + } catch (error) { + this.#rejectServer(error); + throw error; + } } } @@ -96,7 +166,9 @@ export default new OAuthProvider({ // @ts-expect-error '/mcp': MyMCP.serve('/mcp'), // Streaming HTTP }, - defaultHandler: makeOAuthConsent(serverConfig), + // Type assertion needed due to Headers type mismatch between Hono and @cloudflare/workers-types + // At runtime, Hono's fetch handler is fully compatible with ExportedHandler + defaultHandler: makeOAuthConsent(serverConfig) as unknown as ExportedHandler, authorizeEndpoint: '/authorize', tokenEndpoint: '/token', clientRegistrationEndpoint: '/register', diff --git a/packages/mcp-server/cloudflare-worker/src/utils.ts b/packages/mcp-server/cloudflare-worker/src/utils.ts index 5803d74a..4c00c592 100644 --- a/packages/mcp-server/cloudflare-worker/src/utils.ts +++ b/packages/mcp-server/cloudflare-worker/src/utils.ts @@ -4,7 +4,7 @@ import type { HtmlEscapedString } from 'hono/utils/html'; import { marked } from 'marked'; import type { AuthRequest } from '@cloudflare/workers-oauth-provider'; import { env } from 'cloudflare:workers'; -import { ServerConfig, McpOptions, ClientType, Filter, ClientProperty } from '@julep/sdk-mcp/server'; +import { ServerConfig, ClientProperty } from '.'; export const layout = (content: HtmlEscapedString | string, title: string, config: ServerConfig) => html` @@ -161,46 +161,10 @@ export const homeContent = async (req: Request): Promise => { return html`
${raw(content)}
`; }; -export const renderLoggedOutAuthorizeScreen = async ( - config: ServerConfig, - oauthReqInfo: AuthRequest, - defaultOptions?: Partial, -) => { +export const renderLoggedOutAuthorizeScreen = async (config: ServerConfig, oauthReqInfo: AuthRequest) => { const checked = (condition: boolean) => (condition ? 'checked' : ''); const selected = (condition: boolean) => (condition ? 'selected' : ''); - // Helper to check if a capability is enabled by default - const hasCapability = (capability: string) => { - if (!defaultOptions?.capabilities) { - // Default capabilities when none specified - return ['refs', 'unions', 'formats'].includes(capability); - } - switch (capability) { - case 'top-level-unions': - return defaultOptions.capabilities.topLevelUnions || false; - case 'valid-json': - return defaultOptions.capabilities.validJson || false; - case 'refs': - return defaultOptions.capabilities.refs || false; - case 'unions': - return defaultOptions.capabilities.unions || false; - case 'formats': - return defaultOptions.capabilities.formats || false; - default: - return false; - } - }; - - // Helper to check if an operation is enabled by default - const hasOperation = (operation: string) => { - if (!defaultOptions?.filters) { - // Default operations when none specified - return ['read', 'write'].includes(operation); - } - return defaultOptions.filters.some( - (f) => f.type === 'operation' && f.op === 'include' && f.value === operation, - ); - }; const renderField = (field: ClientProperty) => { if (field.type === 'select' && field.options) { return html` @@ -268,111 +232,6 @@ export const renderLoggedOutAuthorizeScreen = async (
${config.clientProperties.map(renderField)}
-
-
- - Configuration Options - -
-
- - -
- -
- - -
- - ? - -
- Have the LLM dynamically discover the endpoints, instead of directly exposing one tool per - endpoint. -
-
-
- -
-
- - -
- - ? - -
- Restrict the available tools to only be able to read data. -
-
-
-
-
-
-
-