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

@subtask/mcp

v0.2.0

Published

Subtask MCP server: lets AI clients reach Subtask workspaces and issues with an API token

Readme

Subtask MCP server

A stdio server that lets AI clients (Claude Code, Claude Desktop, anything that speaks MCP) reach Subtask's workspace and issue endpoints with an API token.

This is direction 1: an outside AI connects to Subtask. Subtask is passive here — it calls no LLM and pays for nothing. Subtask triggering AI from the inside (direction 2) is separate work.

Setup

  1. Create a token in Subtask (Company → Integrations → API tokens). Scopes: workspaces:read, issues:read, issues:write.
  2. The token is shown once. Copy it.
  3. Add it to your MCP client's configuration:
{
  "mcpServers": {
    "subtask": {
      "command": "npx",
      "args": ["-y", "@subtask/mcp"],
      "env": {
        "SUBTASK_URL": "https://subtask.app",
        "SUBTASK_TOKEN": "sbt_..."
      }
    }
  }
}

Claude Code needs no file editing:

claude mcp add subtask --env SUBTASK_URL=https://subtask.app --env SUBTASK_TOKEN=sbt_... -- npx -y @subtask/mcp

On Windows, if the client reports that it cannot find npx, use cmd as the command and ["/c", "npx", "-y", "@subtask/mcp"] as the arguments — npx is a .cmd shim there and some clients cannot spawn it directly.

Both snippets are also generated inside the Subtask interface (client/src/integrations/ApiTokenHelp.js) with the address and token already filled in. If the package name changes, update both places: the MCP_PACKAGE constant there and the examples here.

To run a working copy instead of the published version during local development: "command": "node", "args": ["<repo>/mcp-server/index.js"].

The token is read only from the environment; it is not a parameter of any tool. The model can neither see it nor copy it into a tool call.

| Variable | Required | Default | | --- | --- | --- | | SUBTASK_URL | yes | — | | SUBTASK_TOKEN | yes | — | | SUBTASK_TIMEOUT_MS | no | 30000 |

Tools

| Tool | Writes | Scope | | --- | --- | --- | | subtask_workspaces_list | no | workspaces:read | | subtask_issue_statuses | no | workspaces:read | | subtask_issues_list | no | issues:read | | subtask_issue_get | no | issues:read | | subtask_issue_create | yes | issues:write | | subtask_issue_update | yes | issues:read + issues:write | | subtask_issue_comment | yes | issues:write | | subtask_issue_set_status | yes | issues:write (+ issues:read recommended) | | subtask_issue_ai_solve | yes | ai:run | | subtask_issue_link_labels | no | issues:read | | subtask_issue_links | no | issues:read | | subtask_issue_link | yes | issues:read + issues:write |

ai:run is a separate scope: it hands the issue to an AI workflow in GitHub Actions, which spends the customer's Actions quota and their LLM key. That is not an ordinary write, so it was not lumped in with issues:write.

Writing tools are marked with MCP annotations.readOnlyHint: false, so the client can ask for confirmation.

Two traps this server absorbs

Both are real behaviours of the Subtask API; without the MCP layer an agent falls into each of them.

1. /issues/upsert does not patch, it replaces. A field missing from the body becomes null. This never causes trouble in the browser, because the UI always submits a fully populated edit form. But for an agent, "update the description of this issue" is the most natural request, and it would silently wipe the issue's workspace, assignee and dates.

That is why subtask_issue_update does read-merge-write: it fetches the issue first, changes only the given fields, and sends a complete body to the endpoint. This is also why issues:read is mandatory for that tool.

2. Failure is reported with HTTP 200. Subtask returns business rule errors as {"status": null, "values": {"message": "..."}} — with HTTP status still 200. The assumption "it returned 200, so it worked" breaks here. Every response goes through unwrap and failures are turned into MCP isError responses, so the model does not quietly assume success; it can read the message and correct itself.

Tests

npm test

test/stdio.test.js starts a real process, speaks real JSON-RPC and connects to a fake Subtask server — no manual verification needed.

Layers of protection

  • Argument validation is derived from the tool schema and runs again on the server before the call (inputSchema is metadata; there is no guarantee the client enforces it). An unknown field or a wrong type never reaches the API.
  • A partial read blocks an update. If /issues/getSingle ever starts returning partial responses, merging would drop the missing fields; so when the issue that was read is incomplete, the tool stops loudly instead of writing data.
  • An empty success response is not treated as success. subtask_issue_create errors on a response that carries no id, so the model does not say "issue created" and carry on without one.

Known limits

  • The server can only reach endpoints whose scope is declared in app/docs/api-tokens.md. Opening a new endpoint takes two steps on the Subtask side (the scope, plus scope on the route).
  • There are no tools for deleting issues, worklogs or labels; those endpoints are closed to tokens.
  • Issue key generation (key) is open to a MAX(key) + 1 race: two issues created in the same workspace at the same time can get the same key.
  • The read-merge-write in subtask_issue_update is not atomic. A change made by someone else between the read and the write can be overwritten with the older snapshot. The API has no version/etag field, so there is no fix today.
  • If subtask_issue_set_status cannot read the issue, it still changes the status, but the response carries a cache_context: "skipped: ..." field — list screens may show stale data for a while.

For maintainers

The setup text in the Subtask interface says npx -y @subtask/mcp, so the package must be published on npm; until it is, the instructions on that screen do not work.

cd mcp-server
npm test
npm publish     # scoped package; publishConfig.access = public
  • The @subtask scope must belong to you on npm (npm org ls subtask, or the scope is created automatically on the first publish).
  • The files field ships only index.js + src; test/ and node_modules/ stay out of the package.
  • Bump version after every change — npx caches by version number, and republishing the same version is not possible.
  • license is currently UNLICENSED. Since the package sits publicly on npm, that means "you may read the source but you have no right to use it". A permissive licence (MIT, for example) would be more consistent if customers are meant to install it — a deliberate decision, which is why it was left alone.