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

@kadoa/mcp

v0.5.25

Published

Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients

Readme

Kadoa MCP Server

Use Kadoa from ChatGPT, Claude.ai, Claude Code, Cursor, and other MCP clients.

Remote Server (no install needed)

A hosted MCP server is available at https://mcp.kadoa.com/mcp. Connect from any MCP client — no local install needed. You sign in with your Kadoa account via OAuth.

Claude Code

claude mcp add kadoa --transport http https://mcp.kadoa.com/mcp

ChatGPT

  1. Go to Settings → Connectors → Add MCP server
  2. Enter the URL: https://mcp.kadoa.com/mcp
  3. Choose OAuth authentication and sign in with your Kadoa account (Google)

Claude.ai

  1. Go to Settings → Connectors → Add custom MCP
  2. Enter the URL: https://mcp.kadoa.com/mcp
  3. Sign in with your Kadoa account via OAuth

Claude service connector

A Kadoa workspace admin can generate service connector credentials under Workspace settings → MCP connector. The generated OAuth client is fixed to that workspace's service account.

  1. In Kadoa, select Generate credentials and copy the MCP URL, OAuth Client ID, and OAuth Client Secret. The secret is shown once.
  2. In Claude organization Admin settings → Connectors, select Add custom connector.
  3. Enter https://service.mcp.kadoa.com/mcp and paste the generated OAuth Client ID/Secret under Advanced settings.
  4. In the connector's tool settings, allow only the tools that organization members should use.
  5. Organization members find the custom connector under Settings → Connectors and select Connect.

The service connector uses a separate hostname from the personal connector at https://mcp.kadoa.com/mcp, so Claude users can configure both. Members do not need Kadoa accounts. All actions are attributed to the shared Kadoa service account, and team switching is unavailable. To rotate access, rotate the secret in Kadoa, replace it in Claude, and reconnect. Revoking the connector stops access immediately.

Do not paste a Kadoa workspace API key into Claude.

Cursor

Add to .cursor/mcp.json:

{
  "mcpServers": {
    "kadoa": {
      "type": "http",
      "url": "https://mcp.kadoa.com/mcp"
    }
  }
}

Any MCP Client

Point your client to https://mcp.kadoa.com/mcp with OAuth authentication.

Tools

| Tool | Description | |------|-------------| | scrape | Immediately fetch one URL as markdown or raw HTML (shown only for enabled workspaces) | | list_inbox | List personal unread Inbox items across workflows, optionally including read history | | mark_inbox_item_read | Acknowledge one Inbox item without performing its underlying workflow action | | create_workflow | Create an agentic navigation workflow from a prompt | | create_realtime_monitor | Create an asynchronous realtime monitoring workflow after persisting notification channels; returns workflow/session/thread/job IDs | | list_workflows | List all workflows with status | | get_workflow | Get canonical intent, Assistant/session, template ownership, run health, schedule, location, monitoring, and validation details | | request_workflow_update | Ask the workflow Assistant to change extraction intent, navigation, sourcing, pagination, or generated behavior without changing the workflow ID | | get_workflow_assistant | Get the workflow Assistant's current activity and any pending clarification question | | get_workflow_assistant_timeline | Read paginated customer-visible Assistant messages and questions for one-time, scheduled, or realtime workflows | | answer_workflow_assistant_question | Answer the workflow Assistant's current clarification question and resume it | | interrupt_workflow_assistant | Safely interrupt active Assistant work without pausing the workflow schedule | | resume_workflow_assistant | Resume an idle/interrupted workflow Assistant with its persisted role and conversation | | stop_workflow_assistant | Stop active Assistant work without deleting or replacing the workflow | | get_workflow_strategy | Get the current customer-safe extraction/build strategy for an Assistant or custom-script workflow | | get_workflow_history | Get the workflow's configuration revision history (audit log) — who changed it, when, from which channel, and a changedFields summary per revision | | list_workflow_runs | List a workflow's execution run history (status, start/finish, record count, errors); filter by outcome and paginate | | run_workflow | Execute a workflow | | fetch_data | Get extracted data from a workflow | | delete_workflow | Delete a workflow | | approve_workflow | Approve and activate a workflow | | pause_workflow | Pause an active workflow | | update_workflow | Update deterministic configuration, including location, schedule timezone, monitoring conditions, and unlimited rows | | whoami | Show current user details, auth method, and team memberships | | team_list | List all teams you belong to and see which is active | | team_switch | Switch the active team by name or ID |

Usage Examples

Once the MCP server is configured, you can manage the full workflow lifecycle through natural conversation. Here are a few common operations shown as Claude Code sessions.

Fetch a page for an LLM

> You: Fetch https://example.com as markdown.

Claude calls scrape with smart routing and returns compact markdown.
Routing is fully automatic — it escalates through the stealth tiers on
its own when a site blocks bots. Ask for HTML when you need the raw source.

The scrape tool is available only to workspaces enabled for the Scrape API. Use a workflow instead for structured extraction, recurring runs, monitoring, or multi-page navigation.

Review work that needs attention

> You: What needs my attention in Kadoa?

Claude calls list_inbox and shows unread questions and review items across
workflows. For an Assistant question, it calls get_workflow_assistant to load
the authoritative question before asking whether you want to answer it.
Marking the Inbox item read only acknowledges it.

Create and run a workflow

> You: Create a workflow to extract product names, prices, and ratings
        from https://example-shop.com/products

Claude calls create_workflow and returns the workflow ID, proposed
navigation steps, and data schema for your review.

> You: The schema looks good. Approve it and kick off a run.

Claude calls approve_workflow to activate the workflow, then
run_workflow to start extraction.

> You: Is the run done? Show me the results.

Claude checks the run status with get_workflow, then calls fetch_data
to retrieve the extracted records and display them as a table.

Create a workflow from a template

> You: Use my "Product Scraper" template to scrape https://example-shop.com.
>      For this source, only include products that are in stock.

Claude calls list_templates to find the matching template, then
create_workflow with `templateId`, `urls`, and `prompt` containing only the
source-specific instruction. The template's shared prompt, entity, and schema
are inherited and must not be copied into the call. The optional workflow
instruction remains workflow-owned when the template is updated. Returns the
workflow ID for follow-up with get_workflow or fetch_data.

Update a workflow and re-run

> You: List my workflows.

Claude calls list_workflows and shows all workflows with their
current status (complete, running, failed, paused, scheduled, etc.).

> You: Update wf_abc123 — add an "availability" field to the schema
        and rename "cost" to "price".

Claude calls update_workflow with the new schema, confirms the
changes, and shows the updated field list.

> You: Run it again with the new schema.

Claude calls run_workflow and returns the workflow ID while the run proceeds
asynchronously. Check back later with get_workflow or fetch_data. If a
one-time or scheduled workflow fails, call run_workflow again with the same
workflow ID and configuration - never delete and recreate it just to retry.

Update deterministic workflow settings

> You: Run wf_abc123 from the US at 8 AM New York time on weekdays,
       alert when price changes above 100, and remove its row limit.

Claude calls get_workflow first because monitoring is a replacement setting,
then calls update_workflow with a manual US location, a weekday cron schedule
in `America/New_York`, monitoring fields/conditions, and `limit: null` for all
rows.

Create a realtime monitoring workflow

> You: Watch https://example-shop.com/products for price changes and alert me by webhook.

Claude calls create_realtime_monitor with the URL, schema/change intent, and notification settings. Kadoa persists reusable notification channels first, then asynchronously accepts creation and returns workflow, session, thread, job, and dashboard identifiers.

> You: Does the workflow Assistant need anything from me?

Claude uses get_workflow_assistant and the other workflow Assistant tools for follow-up status, questions, interrupts, resumes, or stops. It does not poll or sleep-wait.

Realtime monitors cannot be converted in place to or from scheduled workflows; create them with create_realtime_monitor from the start.

Update an Assistant-built workflow without replacing it

> You: Update workflow wf_abc123 to follow pagination and extract the detail-page URL.

Claude calls request_workflow_update. Kadoa keeps wf_abc123 and returns the
Assistant session, thread, and job identifiers.

> You: Does Kadoa need anything from me?

Claude calls get_workflow_assistant. To review the conversation, Claude calls
get_workflow_assistant_timeline and follows its opaque nextCursor only when
older messages are needed. If a clarification is pending, Claude shows the
question and calls answer_workflow_assistant_question with your answer. It does
not poll or sleep-wait for the Assistant.

> You: How does this workflow extract the data now?

Claude calls get_workflow_strategy and shows the customer-safe sourcing,
navigation, and extraction approach.

> You: Interrupt the Assistant for now, then resume it later.

Claude calls interrupt_workflow_assistant. When asked later, it calls
resume_workflow_assistant, which continues the same Assistant role and
conversation for either workflow mode. stop_workflow_assistant cancels active
Assistant work without pausing or deleting the workflow itself.

Use update_workflow for deterministic metadata such as name, schedule, tags, and limits. Use template version/apply tools for template-controlled parts. Never delete and recreate a workflow to work around an Assistant update—the workflow ID is part of downstream integrations and history.

Monitor and clean up

> You: Show me all active workflows and their last run results.

Claude calls list_workflows, filters to active ones, then calls
fetch_data for each to summarize the latest extraction results.

> You: Delete the ones that haven't produced data in the last week.

Claude identifies stale workflows from the results and calls
delete_workflow for each, confirming before proceeding.

Troubleshooting

Claude says "I don't have access to Kadoa"

  • Verify the MCP server is configured correctly
  • Restart your MCP client
  • Re-authenticate via OAuth if prompted

Releases

This repo ships two surfaces, and a "release" usually touches both:

  • npm package @kadoa/mcp — stdio CLI users install locally (npx @kadoa/mcp). Cut by Release Please from Conventional Commits on main.
  • Hosted server at mcp.kadoa.com — Docker container on GKE, built and deployed from the kadoa-backend monorepo, which pins a specific @kadoa/mcp version.

Relevant files in kadoa-backend:

  • infra/docker/mcp/Dockerfile.mcp-server — image definition
  • infra/docker/mcp/package.json — pinned @kadoa/mcp version
  • infra/cdk8s/mcp/charts/Server.ts — k8s manifest source (prod chart only; the test deployment is provisioned ad hoc — see below)

Production release

  1. Merge PRs to main using Conventional Commits (feat:, fix:, etc.). Release Please opens/maintains a chore(main): release mcp x.y.z PR.
  2. Merge the release PR. The release-please.yml workflow tags, drafts a GitHub Release, and publishes to npm (latest dist-tag).
  3. After npm publication, release-please.yml opens or updates a deterministic PR in kadoa-backend that bumps infra/docker/mcp/package.json and refreshes bun.lock. The PR validates a frozen production install and Docker image build, and remains manually mergeable.
  4. Merge the backend PR to main. CI (main-build-deploy.yml) builds and pushes europe-west3-docker.pkg.dev/oceanic-base-310208/kadoa-artifacts/mcp-server:<IMAGE_TAG> (tag shown in the build summary).
  5. Trigger the Deploy to Production workflow (deploy-prod.yml) with:
    • Target cluster: gcp
    • Deployment scope: mcp
    • Image tag: the tag from step 4
    • Method: kubectl

The cross-repository bump requires the BACKEND_REPO_TOKEN secret in this repository. The selected authentication approach is a fine-grained PAT scoped only to kadoa-org/kadoa-backend with Contents read/write and Pull requests read/write permissions (Metadata read is automatic). If the secret is not provisioned, the release workflow skips the backend bump with a warning rather than failing npm releases; an administrator must provision it before relying on automatic hosted-server updates.

RC / test release

Use this when you want to validate a change end-to-end against real clients (Claude Desktop, Cursor, ChatGPT) before promoting to latest / prod. The flow mirrors the prod one, but every step targets rc channels.

There are two ways to consume an RC:

  • Local stdio: npx @kadoa/mcp@rc — installs from the rc dist-tag on npm. Good for quick sanity checks where the bug doesn't depend on the hosted OAuth flow.
  • Hosted mcp-server-test: a separate k8s deployment alongside prod in the same GKE cluster. Required when you need to validate the OAuth callback, Redis token store, multi-tenant session behavior, etc.

Publish an RC to npm

Manual — Release Please only cuts stable versions today.

# from a branch on kadoa-mcp
bun install && bun run build
# bump version to a prerelease, e.g. 0.5.7-rc.1
npm version 0.5.7-rc.1 --no-git-tag-version
npm publish --tag rc --access public

Verify: npm view @kadoa/mcp dist-tags. The rc tag should now point to your version. Existing tags today: latest, rc, next.

Promote later with: npm dist-tag add @kadoa/[email protected] latest (run after the matching stable version has been published the normal way).

Deploy the RC to mcp-server-test

  1. In kadoa-backend, set infra/docker/mcp/package.json @kadoa/mcp to the RC version, bun install, push to a branch and merge to main. CI builds the mcp-server:<IMAGE_TAG> image as usual.

  2. Update the test deployment to the new image:

    gcloud container clusters get-credentials kadoa-prod \
      --region=europe-west3 --project=oceanic-base-310208
    
    kubectl set image deployment/mcp-server-test \
      mcp-server=europe-west3-docker.pkg.dev/oceanic-base-310208/kadoa-artifacts/mcp-server:<IMAGE_TAG>
  3. Port-forward to reach it (the test deployment has no public ingress by design):

    kubectl port-forward svc/mcp-server-test 3000:3000
  4. Point your MCP client at http://localhost:3000/mcp and exercise the change.

Auth gotcha: auth.kadoa.com enforces an ALLOWED_CALLBACK_ORIGINS allowlist (app.kadoa.com, mcp.kadoa.com, kadoa.com) plus a loopback bypass for 127.0.0.1 when NODE_ENV !== production. localhost is not treated the same as 127.0.0.1 by that check. The test pod is configured with MCP_SERVER_URL=http://localhost:3000 so the port-forwarded session resolves correctly. If you change the local port, update the deployment env var too:

kubectl set env deployment/mcp-server-test MCP_SERVER_URL=http://localhost:<PORT>

Promotion checklist

Before flipping latest / deploying to prod:

  • [ ] RC consumed locally (npx @kadoa/mcp@rc) — stdio tools work
  • [ ] RC consumed via mcp-server-test — OAuth login succeeds end-to-end
  • [ ] No new errors in the test pod's logs (kubectl logs deployment/mcp-server-test -f)
  • [ ] Release Please PR open with the stable version

Development

bun install
bun run dev        # Run locally
bun run test       # Run tests
bun run build      # Build for distribution

Connecting to local services

To develop and test against a local Kadoa backend (instead of the production API), point the MCP at your local public-api service using the KADOA_PUBLIC_API_URI environment variable.

Prerequisites: the public-api service must be running locally (default port 12380).

Run the MCP server locally:

KADOA_PUBLIC_API_URI=http://localhost:12380 bun run dev

The server starts in HTTP mode. You authenticate via OAuth the same way as with the remote server.

Contributing

A typical feature touches three repos — kadoa-backendkadoa-sdkskadoa-mcp. The same surgery on three layers; same operation, different audience above it.

Architecture

| Layer | Repo / path | Role | |---|---|---| | Backend endpoint | kadoa-backend/ | The HTTP API. Source of truth; the OpenAPI spec is generated from here. | | SDK low-level | kadoa-sdks/sdks/node/src/generated/api/* | Auto-generated axios client. Raw HTTP, typed from the OpenAPI spec. Do not hand-edit. | | SDK domain (high-level) | kadoa-sdks/sdks/node/src/domains/<thing>/<thing>.service.ts | Thin domain wrapper over the generated client. client.<thing>.<action>(...). This is what callers use. | | CLI | kadoa-cli/src/commands/<thing>.ts | Commander action: flag parsing, spinner, table/json output. Calls SDK domain. | | MCP (this repo) | kadoa-mcp/src/tools.ts | Zod input schema + tool description tuned for LLMs. Calls SDK domain. |

Mental model:

  • SDK = pure function over HTTP.
  • CLI = SDK + human UX (flags, table output).
  • MCP = SDK + LLM UX (zod schema, prose description).

CLI and MCP both call identical SDK domain methods. A new feature added to the SDK is picked up by both clients for free — as long as we keep the layering honest.

Adding a new feature

  1. Backend (kadoa-backend)

    • Add or modify the HTTP endpoint.
    • Make sure the OpenAPI spec reflects the change (paths, request/response schemas).
  2. SDK (kadoa-sdks) — always do this before MCP/CLI work (the SDK-first rule). No bandaid axios calls in MCP.

    • Regenerate the low-level client:
      bun kadoa-codegen fetch-spec -e https://api.kadoa.com/openapi -f
      bun kadoa-codegen generate -e https://api.kadoa.com/openapi --fetch-latest -f
      (Swap the endpoint for http://localhost:12380/openapi when developing against a local backend.)
    • Add or extend the domain service in sdks/node/src/domains/<thing>/<thing>.service.ts. Keep it typed, no any.
    • Commit specs/ separately from generated/domain code so the API diff is reviewable.
    • Open a PR. On merge, a new @kadoa/node-sdk version is released.
  3. MCP (this repo)

    • Bump @kadoa/node-sdk in package.json to the new version, bun install.
    • Add the tool in src/tools.ts:
      • Zod input schema (use coercion helpers in src/coercion.ts where needed — LLM clients sometimes send strings for numbers/bools).
      • Description string written for the LLM: when to call it, what inputs mean, what the response shape is.
      • Implementation calls ctx.client.<thing>.<action>(...) — never raw HTTP.
    • Add a unit test under tests/unit/.
    • Use a Conventional Commit message (feat(mcp): ..., fix(mcp): ...). Release Please will cut the next version on merge to main.
  4. CLI (kadoa-cli), if the feature is user-facing on the command line: mirror the MCP step in src/commands/<thing>.ts. Same SDK call, different UX.

Anti-patterns

  • ❌ Raw axios calls or hand-rolled HTTP in src/tools.ts. Always go through ctx.client.<thing>.
  • ❌ Putting tool-shaped logic (input validation, response shaping) in the MCP layer when the SDK should expose it. If two clients would need the same logic, it belongs in the SDK.
  • ❌ Editing files under sdks/node/src/generated/ by hand. They get overwritten by kadoa-codegen generate.
  • ❌ Bundling a spec update and an SDK feature in the same commit — keep specs/ PRs separate from domain-method PRs for clean review.

Where to find things in this repo

  • src/tools.ts — every MCP tool. Single file by design; search for the tool name.
  • src/auth.ts — OAuth flow, JWT verification, team selection.
  • src/client.ts — wires @kadoa/node-sdk per request, injects bearer token.
  • src/http.ts — Express server, transport adapter, session handling.
  • src/redis-store.ts — token store for the hosted server.
  • tests/unit/ — bun tests; run with bun run test.

License

MIT