Skip to content

feat(jupyter): add Jupyter integration (contents, kernels, sessions)#5527

Merged
waleedlatif1 merged 9 commits into
stagingfrom
worktree-add-jupyter-integration
Jul 9, 2026
Merged

feat(jupyter): add Jupyter integration (contents, kernels, sessions)#5527
waleedlatif1 merged 9 commits into
stagingfrom
worktree-add-jupyter-integration

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a Jupyter integration: 16 tools covering the Contents, Kernels, Kernelspecs, and Sessions REST APIs on a self-hosted Jupyter server
  • File upload/download handled via UserFile, following the same internal-route pattern used by Box (auth → download from storage → base64 → PUT to server)
  • New block with an operation dropdown, server URL + token auth fields, and 8 catalog templates
  • Registered tools + block in the registries, generated docs, bumped the API validation route-count baseline

Type of Change

  • New feature

Testing

Tested manually (typecheck, biome, check:api-validation:strict, check:bare-icons all pass). Verified tool endpoints/fields against the Jupyter Server REST API spec and cross-checked the file-handling code against Box's established pattern with independent review passes.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 9, 2026 2:29am

Request Review

@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Server-side proxying to user-supplied Jupyter URLs (including HTTP/private IPs) and remote file/kernel mutations warrant careful review of SSRF and auth boundaries, though DNS pinning, path checks, and internal auth mitigate much of the exposure.

Overview
Adds a Jupyter workflow integration for self-hosted Jupyter Server instances, authenticated with server URL + token.

16 tools cover Contents (list/read/create/upload/rename/copy/delete), Kernels (list/start/stop/restart/interrupt), Kernelspecs, and Sessions. Most calls go through new internal routes /api/tools/jupyter/proxy and /api/tools/jupyter/upload, which DNS-pin upstream requests, allow HTTP for local/private hosts, disable redirects, and validate paths against traversal (including encoded segments). Uploads follow the existing pattern: resolve UserFile from storage with access checks, then PUT base64 content to Jupyter.

A new Jupyter block exposes an operation dropdown and shared auth fields; tools, block metadata, integrations.json, docs (jupyter.mdx), and JupyterIcon mappings are wired in. API validation baseline increases by 2 routes (proxy + upload).

Reviewed by Cursor Bugbot for commit 280ad02. Configure here.

Comment thread apps/sim/tools/jupyter/list_contents.ts Outdated
Comment thread apps/sim/tools/jupyter/create_file.ts
@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a Jupyter integration for self-hosted servers. The main changes are:

  • New tools for Contents, Kernels, Kernelspecs, and Sessions APIs.
  • Internal proxy and upload routes with DNS-pinned outbound requests.
  • A new Jupyter block, icon, registry entries, and docs.
  • Upload handling through existing file storage and UserFile patterns.
  • API validation baseline updates for the new routes.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
apps/sim/app/api/tools/jupyter/upload/route.ts Adds the Jupyter upload route with file access checks, destination path encoding, outbound URL validation, pinned fetches, and redirects disabled.
apps/sim/app/api/tools/jupyter/proxy/route.ts Adds the shared Jupyter proxy route for authenticated calls to Jupyter REST endpoints.
apps/sim/tools/jupyter/utils.ts Adds shared helpers for Jupyter URL normalization, auth headers, path validation, path encoding, and response mapping.
apps/sim/tools/jupyter/create_file.ts Adds create-file support for files, directories, and notebooks with invalid notebook JSON surfaced as an error.
apps/sim/blocks/blocks/jupyter.ts Adds the Jupyter block configuration, operation selector, auth fields, upload fields, and tool parameter mapping.

Reviews (8): Last reviewed commit: "fix(jupyter): decode the whole path befo..." | Re-trigger Greptile

Comment thread apps/sim/app/api/tools/jupyter/upload/route.ts Outdated
Comment thread apps/sim/app/api/tools/jupyter/upload/route.ts Outdated
Comment thread apps/sim/tools/jupyter/create_file.ts Outdated
Comment thread apps/sim/tools/jupyter/get_content.ts
- 16 tools covering Contents, Kernels, Kernelspecs, and Sessions REST APIs
- File upload/download via UserFile, following the Box upload pattern
- Block with operation dropdown, token auth, and 8 catalog templates
- Registered tools + block, generated docs, bumped API validation baseline
…uity, silent notebook fallback

- Route uploads through validateUrlWithDNS + secureFetchWithPinnedIP (matches Grafana/1Password pattern) instead of a raw fetch to the user-supplied server URL
- Replace the upload path/trailing-slash heuristic with an unambiguous directory + filename split
- create_file no longer silently writes an empty notebook when notebook content is malformed JSON — it now errors clearly
Without it, Jupyter Server returns directory metadata with content:
null, so jupyter_list_contents always reported an empty items array.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/tools/jupyter/upload/route.ts Outdated
Comment thread apps/sim/app/api/tools/jupyter/upload/route.ts
Comment thread apps/sim/blocks/blocks/jupyter.ts
encodeJupyterPath now rejects '.'/'..' segments across the whole path
(shared by all 16 tools, not just upload); the upload route returns a
clean 400 when it's hit.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/tools/jupyter/rename_content.ts Outdated
Comment thread apps/sim/app/api/tools/jupyter/upload/route.ts
…gaps

- extract the traversal check out of encodeJupyterPath into a shared
  assertion, and apply it to body-only path fields (rename newPath,
  copy copyFromPath, session path) that never flowed through URL
  encoding and so skipped the check
- pass stripAuthOnRedirect to the upload route's secureFetchWithPinnedIP
  call so a malicious Jupyter server can't redirect the PUT to another
  origin and receive the caller's token
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/tools/jupyter/utils.ts
A segment like %2e%2e wouldn't match the literal '..' check. Now
decodes each segment before comparing, in addition to the literal
check, so an already-encoded traversal attempt is caught too.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/tools/jupyter/upload/route.ts
Comment thread apps/sim/tools/jupyter/list_contents.ts Outdated
Comment thread apps/sim/tools/jupyter/get_content.ts Outdated
…for HTTP/private-host support and no redirects

The generic external tool executor blocks plain-HTTP and non-localhost
private-IP hosts by default, so every non-upload Jupyter operation
could fail against typical self-hosted setups (LAN IP, docker
hostname, or even literal localhost on a hosted deployment) even
though the upload route worked via its own internal route.

Added /api/tools/jupyter/proxy (DNS-pinned, allowHttp, maxRedirects: 0)
that mirrors the upstream Jupyter response verbatim, matching the
established pattern for self-hosted-arbitrary-host integrations
(Grafana, 1Password) instead of the generic executor path. Each tool's
request block now posts to the proxy instead of building a direct
external URL; transformResponse and outputs are unchanged since the
proxy response mirrors upstream status/body exactly.

Also switches the upload route from stripAuthOnRedirect to
maxRedirects: 0 — stronger, since it stops the uploaded file body
(not just the token) from ever reaching a redirect target.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/tools/jupyter/proxy/route.ts
Comment thread apps/sim/app/api/tools/jupyter/upload/route.ts
…separators in upload filename

- The proxy route now independently validates the incoming path field
  for traversal segments instead of only relying on tool-side
  validation before the request reaches it — the route is a shared
  internal boundary, not something only our own tool code can call
- The upload route's fileName can come from an advanced override or
  the legacy fileContent path and could itself contain '/' or '\',
  silently nesting the upload deeper than the directory param
  specified. Now rejected outright before joining.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/tools/jupyter/utils.ts
…-split segment

A segment like foo%2f..%2fsecret has no literal slash, so splitting on
literal '/' first and decoding each piece in isolation treats it as
one opaque segment and never notices the '..' hiding behind the
encoded slash. Decode the full path once, then split and check every
segment the target server's own single URL-decode pass would see.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 280ad02. Configure here.

@waleedlatif1 waleedlatif1 merged commit 6e87d74 into staging Jul 9, 2026
18 checks passed
@waleedlatif1 waleedlatif1 deleted the worktree-add-jupyter-integration branch July 9, 2026 02:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant