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

@cancore/mcp

v0.1.0

Published

An MCP server that lets an AI agent ask a Cancore wallet owner for a trade. The agent queues a request; the person approves and signs it in their own wallet. No key ever reaches the agent.

Readme

@cancore/mcp

An MCP server that lets an AI agent ask a Cancore wallet owner for a trade.

The agent queues a request. The owner reviews it in their own wallet and signs it with their own key. No key, token or prepared transaction ever reaches the agent — the API this server speaks does not offer one. The worst a prompt-injected agent achieves here is a request the owner declines.

npx @cancore/mcp

Install

Claude Code:

claude mcp add cancore -- npx -y @cancore/mcp \
  -e CANCORE_API_URL=https://api.cancore.io \
  -e CANCORE_APP_URL=https://cancore.io

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "cancore": {
      "command": "npx",
      "args": ["-y", "@cancore/mcp"],
      "env": {
        "CANCORE_API_URL": "https://api.cancore.io",
        "CANCORE_APP_URL": "https://cancore.io"
      }
    }
  }
}

Any MCP client works — the transport is stdio and the server writes nothing but protocol to stdout.

| Variable | Meaning | | --- | --- | | CANCORE_API_URL | the Cancore gateway the agent talks to | | CANCORE_APP_URL | where the wallet is served — the consent page lives there | | CANCORE_APP_NAME | how the server introduces itself on the consent page (default Cancore MCP) | | CANCORE_GRANT_FILE | where the approved grant is stored (default ~/.config/cancore-mcp/grants.json) |

Both URLs have to be set explicitly. They are not derived from one another: on one stand that derivation is right and on another it is wrong, and a consent page on the wrong stand is the kind of mistake you only notice by what it does there.

First run

  1. The agent calls cancore_connect_wallet. Your browser opens the consent page and the tool answers with a short code.
  2. The page must show the same code. If it does not, the request is not the one your agent started — decline it.
  3. You approve. The server keeps a scoped grant (agent:propose, agent:read); the token is written to the grant file and never returned to the agent.

The call waits a bounded while — 45 seconds by default, 5 to 300 by argument — and then answers pending rather than failing. The request stays live and the next call resumes it, so the code already on your screen keeps working. Minting a second code would teach you that a mismatched code is normal, which is the one thing the consent page relies on you noticing.

Tools

cancore_connect_wallet

| Argument | Type | Meaning | | --- | --- | --- | | appName | string, optional | how to introduce itself on the consent page | | waitSeconds | number, optional | how long to wait in this call, 5–300 (default 45) | | force | boolean, optional | ask again even if this stand is already authorized |

Answers { status: 'granted' | 'denied' | 'pending', … }. A pending answer carries userCode, page, whether the browser opened, and whether the request was resumed.

cancore_propose_autotrade

Buying one token for another straight from the Cancore pool, with no counterparty to wait for.

| Argument | Type | Meaning | | --- | --- | --- | | sourceTokenName | string | token symbol being spent, e.g. CC | | targetTokenName | string | token symbol being bought, e.g. CBTC | | sourceAmount | string | decimal amount to spend | | agentLabel | string, optional | how to identify yourself to the owner (display only) |

The pair is named by token symbol and carries no rate, and neither is an abbreviation. An agent on a scoped grant cannot read the pair list at all, so a pair id would be a value it could not have obtained honestly — and a rate proposed now is a rate that no longer exists when the owner answers. The wallet resolves the pair and quotes it live at the press, and shows the owner what the trade would actually buy.

cancore_propose_order

A cross-chain swap offer, reviewed in the owner's own create-order form.

| Argument | Type | | --- | --- | | sourceNetwork, sourceTokenAddress, sourceAmount | string | | targetNetwork, targetTokenAddress, targetAmount | string | | sourceTokenName, targetTokenName | string, optional display names | | agentLabel | string, optional |

cancore_propose_transfer

| Argument | Type | Meaning | | --- | --- | --- | | receiverPartyId | string | Canton party id (hint::namespace) | | amount | string | decimal amount | | tokenId | string, optional | instrument to send; defaults to the wallet's CC | | description | string, optional | memo shown to the owner and carried with the transfer | | agentLabel | string, optional |

cancore_intent_status

{ intentId } → the outcome: pending (waiting for the owner), executed (signed and committed) or rejected. The outcome, never the transaction.

cancore_list_intents

No arguments. What is still awaiting the owner's decision.

The grant

Stored at ~/.config/cancore-mcp/grants.json, mode 0600, keyed by API base URL — a dev grant cannot be carried to mainnet by accident, and a token that goes to the wrong stand is only noticed by what it does there.

This is the same file, in the same format, that the Go server in Cancore-io/mcp-server writes. An owner who switches between the two runtimes does not approve twice.

Two refusals are told apart on purpose:

  • 401 — the grant is no longer accepted. The server drops it and the next call asks you to connect again.
  • 403 — the grant is live, it just was not approved for this. The grant is kept; dropping it would make you re-approve something you already approved.

Everything else is passed through with the server's own message, because a cap or a queue limit reads as guidance, not as a transport failure.

What this package cannot do

It does not sign, hold keys, or read balances. Amount caps and queue limits are enforced by the server and deliberately not restated in the tool descriptions an agent reads: a description is reachable by whoever writes the prompt, the server is not.

The grant file is 0600 and nothing more — no keyring, no encryption at rest. Anything running as your user can read it, the same as the ~/.aws and ~/.kube files next to it. The grant is revocable from the wallet and expires on its own.

Using the pieces directly

import { CancoreSession, AgentQueueClient, createServer, registerAgentTools } from '@cancore/mcp';

const session = new CancoreSession(
  { apiBaseUrl, appBaseUrl, appName: 'My agent' },
  { fetchImpl, openBrowser },
);
registerAgentTools(myExistingMcpServer, session);

CancoreSession is the whole surface without the transport — six methods returning plain JSON — and AgentQueueClient is the REST client under it, if you are talking to the queue from something that is not an MCP server at all. startAuthorization / awaitGrant and loadGrant / saveGrant / forgetGrant are exported for the same reason.

Troubleshooting

"this server has no access to the wallet yet" — no grant for this CANCORE_API_URL. Run cancore_connect_wallet.

The consent page shows a different code — decline it. Some other request is in flight.

Nothing opens — the browser launch is best effort by design (this server also runs headless and over SSH). The tool always returns the URL; open it by hand.

The client drops the connection at startup — something wrote to stdout. Only the protocol belongs there; this server puts its own warnings on stderr.

Full documentation

https://docs.cancore.io/sdk/mcp

License

Apache-2.0.