npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ganju/sdk

v0.0.3

Published

The typed `ctx` a Ganju custom tool is written against

Downloads

55

Readme

@ganju/sdk

The typed ctx a Ganju custom tool is written against.

npm install --save-dev @ganju/sdk

A custom tool is a Cloudflare Worker you write, deployed to Ganju with @ganju/cli and registered as MCP tools on your project. This package is what makes ctx autocomplete while you write one.

import { defineTool } from '@ganju/sdk';

export default defineTool(async (input, ctx) => {
  const { accessToken } = await ctx.connection('google-gmail');
  const res = await fetch(`https://api.acme.com/orders/${input.orderId}`);
  ctx.log('looked up ' + input.orderId);
  return { status: (await res.json()).status };
});

Mostly a dev dependency

ganju build marks this package external and rewrites the import to ./ganju-sdk.js — the sibling module Ganju attaches beside your script on every deploy. So the runtime code your tool executes is the one the platform ships, and what you install here is the types and the shape.

That is deliberate rather than incidental: the SDK is typed sugar over host bindings, and a copy frozen into a customer's bundle is a copy that stops matching the broker it talks to. The security lives in the bindings, not in this package — a library that performed the token exchange itself would expose the client secret to user code.

What ctx gives you

| Member | Contract | | --- | --- | | ctx.connection(provider) | { accessToken, provider, expiresAt }. Short-lived. Never a refresh token or client secret. Throws unless the provider is in the tool's declared connections. | | ctx.secret(name) | A value set with ganju secret set, resolved per call — so it is live from the next call with no deploy after it. | | ctx.resources.search / read / list | The same retrieval the native RAG tools use. Binary resources are refused rather than base64'd — that is what sendFile is for. | | ctx.resources.create(opts) | Write a resource: inline text or file bytes. Not indexed unless index: true, so tool output stays out of the search corpus by default. | | ctx.resources.delete(uri, opts) | Remove one, and with children: true everything beneath it. Idempotent — a uri with nothing behind it answers deleted: false. | | ctx.sendFile(opts) | Mail or post a file to gmail, outlook or slack without the bytes passing through your isolate. The destination must be in your declared connections. | | ctx.log(...) | Buffered in the isolate and returned with the result, so a log call costs no round trip. Read them with ganju logs. Capped at 50 lines. | | fetch | Global, screened against your declared allowedHosts and against private/loopback ranges. |

Not available: require, process, fs, raw database access, or anything belonging to another project. User scripts get the plain Workers runtime with no nodejs_compat.

Two ways to export

One script serves every tool your project declares, so the deployed module's default export routes on the tool name. Give each tool an entry in ganju.json and the CLI generates that router for you — the export above is all you write. Or write the map yourself:

import { createHandler, defineTool } from '@ganju/sdk';

export default createHandler({
  'lookup-order': defineTool(async (input, ctx) => ({ status: 'shipped' }))
});

The keys must match the tool names in ganju.json. Publishing verifies that against what the bundle actually exports, so lookup-order versus lookupOrder fails at deploy rather than on the first customer call.

License

Apache-2.0.