{"_id":"@cancore/mcp","name":"@cancore/mcp","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@cancore/mcp","version":"0.1.0","type":"module","description":"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.","bin":{"cancore-mcp":"dist/bin.js"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"main":"./dist/index.js","types":"./dist/index.d.ts","sideEffects":false,"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/Cancore-io/sdk.git","directory":"packages/mcp"},"homepage":"https://docs.cancore.io/sdk/mcp","bugs":{"url":"https://github.com/Cancore-io/sdk/issues"},"keywords":["mcp","model-context-protocol","canton","cancore","agent","trading","wallet"],"publishConfig":{"access":"public"},"scripts":{"build":"tsup","prepack":"tsup --silent","prepublishOnly":"npm run build"},"dependencies":{"@modelcontextprotocol/sdk":"^1.20.0","zod":"^3.25.0"},"engines":{"node":">=20"},"_id":"@cancore/mcp@0.1.0","gitHead":"8981a41eeb48d4b948f1a724725d7ef26c2b5122","_nodeVersion":"24.9.0","_npmVersion":"11.6.0","dist":{"integrity":"sha512-4RNbxL27B1aOx0MFVXCmbzqbhvad0HfTRKwJXJ3pj/Y8WX+r58iFvxl+givFBX8U+0Z5fCI9WbSpByyHaqZWcw==","shasum":"1864950cc85605a0b014c5af189bbc675d2e17d2","tarball":"https://registry.npmjs.org/@cancore/mcp/-/mcp-0.1.0.tgz","fileCount":11,"unpackedSize":91596,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAJkWS5cSVCChngGHOQ4zXDrCVWYB2gffP5DEZwr8izNAiAYOtIgPWzlWYv3i+szz9z6nB7uKLH/CyaBUr4/Z+goSA=="}]},"_npmUser":{"name":"merqry","email":"merqry.dev@gmail.com"},"directories":{},"maintainers":[{"name":"merqry","email":"merqry.dev@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp_0.1.0_1788789218387_0.4896438549397051"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-07T13:53:38.012Z","0.1.0":"2026-09-07T13:53:38.516Z","modified":"2026-09-07T13:53:39.151Z"},"maintainers":[{"name":"merqry","email":"merqry.dev@gmail.com"}],"description":"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.","homepage":"https://docs.cancore.io/sdk/mcp","keywords":["mcp","model-context-protocol","canton","cancore","agent","trading","wallet"],"repository":{"type":"git","url":"git+https://github.com/Cancore-io/sdk.git","directory":"packages/mcp"},"bugs":{"url":"https://github.com/Cancore-io/sdk/issues"},"license":"Apache-2.0","readme":"# `@cancore/mcp`\n\nAn MCP server that lets an AI agent ask a Cancore wallet owner for a trade.\n\nThe agent queues a request. The owner reviews it in their own wallet and signs it with their\nown key. **No key, token or prepared transaction ever reaches the agent** — the API this\nserver speaks does not offer one. The worst a prompt-injected agent achieves here is a request\nthe owner declines.\n\n```bash\nnpx @cancore/mcp\n```\n\n## Install\n\nClaude Code:\n\n```bash\nclaude mcp add cancore -- npx -y @cancore/mcp \\\n  -e CANCORE_API_URL=https://api.cancore.io \\\n  -e CANCORE_APP_URL=https://cancore.io\n```\n\nClaude Desktop (`claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"cancore\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@cancore/mcp\"],\n      \"env\": {\n        \"CANCORE_API_URL\": \"https://api.cancore.io\",\n        \"CANCORE_APP_URL\": \"https://cancore.io\"\n      }\n    }\n  }\n}\n```\n\nAny MCP client works — the transport is stdio and the server writes nothing but protocol to\nstdout.\n\n| Variable | Meaning |\n| --- | --- |\n| `CANCORE_API_URL` | the Cancore gateway the agent talks to |\n| `CANCORE_APP_URL` | where the wallet is served — the consent page lives there |\n| `CANCORE_APP_NAME` | how the server introduces itself on the consent page (default `Cancore MCP`) |\n| `CANCORE_GRANT_FILE` | where the approved grant is stored (default `~/.config/cancore-mcp/grants.json`) |\n\nBoth URLs have to be set explicitly. They are not derived from one another: on one stand that\nderivation is right and on another it is wrong, and a consent page on the wrong stand is the\nkind of mistake you only notice by what it does there.\n\n## First run\n\n1. The agent calls `cancore_connect_wallet`. Your browser opens the consent page and the tool\n   answers with a short code.\n2. **The page must show the same code.** If it does not, the request is not the one your agent\n   started — decline it.\n3. You approve. The server keeps a scoped grant (`agent:propose`, `agent:read`); the token is\n   written to the grant file and never returned to the agent.\n\nThe call waits a bounded while — 45 seconds by default, 5 to 300 by argument — and then\nanswers `pending` rather than failing. The request stays live and the next call resumes it, so\nthe code already on your screen keeps working. Minting a second code would teach you that a\nmismatched code is normal, which is the one thing the consent page relies on you noticing.\n\n## Tools\n\n### `cancore_connect_wallet`\n\n| Argument | Type | Meaning |\n| --- | --- | --- |\n| `appName` | string, optional | how to introduce itself on the consent page |\n| `waitSeconds` | number, optional | how long to wait in this call, 5–300 (default 45) |\n| `force` | boolean, optional | ask again even if this stand is already authorized |\n\nAnswers `{ status: 'granted' | 'denied' | 'pending', … }`. A `pending` answer carries\n`userCode`, `page`, whether the browser `opened`, and whether the request was `resumed`.\n\n### `cancore_propose_autotrade`\n\nBuying one token for another straight from the Cancore pool, with no counterparty to wait for.\n\n| Argument | Type | Meaning |\n| --- | --- | --- |\n| `sourceTokenName` | string | token symbol being spent, e.g. `CC` |\n| `targetTokenName` | string | token symbol being bought, e.g. `CBTC` |\n| `sourceAmount` | string | decimal amount to spend |\n| `agentLabel` | string, optional | how to identify yourself to the owner (display only) |\n\nThe pair is named by **token symbol** and carries **no rate**, and neither is an abbreviation.\nAn agent on a scoped grant cannot read the pair list at all, so a pair id would be a value it\ncould not have obtained honestly — and a rate proposed now is a rate that no longer exists\nwhen the owner answers. The wallet resolves the pair and quotes it live at the press, and\nshows the owner what the trade would actually buy.\n\n### `cancore_propose_order`\n\nA cross-chain swap offer, reviewed in the owner's own create-order form.\n\n| Argument | Type |\n| --- | --- |\n| `sourceNetwork`, `sourceTokenAddress`, `sourceAmount` | string |\n| `targetNetwork`, `targetTokenAddress`, `targetAmount` | string |\n| `sourceTokenName`, `targetTokenName` | string, optional display names |\n| `agentLabel` | string, optional |\n\n### `cancore_propose_transfer`\n\n| Argument | Type | Meaning |\n| --- | --- | --- |\n| `receiverPartyId` | string | Canton party id (`hint::namespace`) |\n| `amount` | string | decimal amount |\n| `tokenId` | string, optional | instrument to send; defaults to the wallet's CC |\n| `description` | string, optional | memo shown to the owner and carried with the transfer |\n| `agentLabel` | string, optional |\n\n### `cancore_intent_status`\n\n`{ intentId }` → the outcome: `pending` (waiting for the owner), `executed` (signed and\ncommitted) or `rejected`. The outcome, never the transaction.\n\n### `cancore_list_intents`\n\nNo arguments. What is still awaiting the owner's decision.\n\n## The grant\n\nStored at `~/.config/cancore-mcp/grants.json`, mode `0600`, **keyed by API base URL** — a dev\ngrant cannot be carried to mainnet by accident, and a token that goes to the wrong stand is\nonly noticed by what it does there.\n\nThis is the same file, in the same format, that the Go server in `Cancore-io/mcp-server`\nwrites. An owner who switches between the two runtimes does not approve twice.\n\nTwo refusals are told apart on purpose:\n\n- **401** — the grant is no longer accepted. The server drops it and the next call asks you to\n  connect again.\n- **403** — the grant is live, it just was not approved for this. The grant is kept; dropping\n  it would make you re-approve something you already approved.\n\nEverything else is passed through with the server's own message, because a cap or a queue\nlimit reads as guidance, not as a transport failure.\n\n## What this package cannot do\n\nIt does not sign, hold keys, or read balances. Amount caps and queue limits are enforced by\nthe server and deliberately **not** restated in the tool descriptions an agent reads: a\ndescription is reachable by whoever writes the prompt, the server is not.\n\nThe grant file is `0600` and nothing more — no keyring, no encryption at rest. Anything\nrunning as your user can read it, the same as the `~/.aws` and `~/.kube` files next to it. The\ngrant is revocable from the wallet and expires on its own.\n\n## Using the pieces directly\n\n```ts\nimport { CancoreSession, AgentQueueClient, createServer, registerAgentTools } from '@cancore/mcp';\n\nconst session = new CancoreSession(\n  { apiBaseUrl, appBaseUrl, appName: 'My agent' },\n  { fetchImpl, openBrowser },\n);\nregisterAgentTools(myExistingMcpServer, session);\n```\n\n`CancoreSession` is the whole surface without the transport — six methods returning plain\nJSON — and `AgentQueueClient` is the REST client under it, if you are talking to the queue\nfrom something that is not an MCP server at all. `startAuthorization` / `awaitGrant` and\n`loadGrant` / `saveGrant` / `forgetGrant` are exported for the same reason.\n\n## Troubleshooting\n\n**\"this server has no access to the wallet yet\"** — no grant for this `CANCORE_API_URL`. Run\n`cancore_connect_wallet`.\n\n**The consent page shows a different code** — decline it. Some other request is in flight.\n\n**Nothing opens** — the browser launch is best effort by design (this server also runs\nheadless and over SSH). The tool always returns the URL; open it by hand.\n\n**The client drops the connection at startup** — something wrote to stdout. Only the protocol\nbelongs there; this server puts its own warnings on stderr.\n\n## Full documentation\n\n**<https://docs.cancore.io/sdk/mcp>**\n\n## License\n\nApache-2.0.\n","readmeFilename":"README.md","_rev":"1-225216bc55acda4ad47397d68a06e1d7"}