Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
af6595f
Refuse ledger transfer/approval methods in canister_update_call
claude Aug 26, 2026
6ddd4a1
Remove financial framing; state the server is not for financial opera…
claude Aug 26, 2026
d9a0a33
Block cycles-ledger creation spends; scope the no-financial claim (re…
claude Aug 26, 2026
46cc3d6
Use opencloud.org example; scope financial claims to enforcement (rev…
claude Aug 26, 2026
f1a52ca
State the no-financial rule as policy, not as a capability claim (rev…
claude Aug 26, 2026
33a33c3
Present the posture as policy plus standardized-method guardrail (rev…
claude Aug 26, 2026
4897e25
Merge remote-tracking branch 'origin/main' into claude/icp-marketplac…
claude Aug 26, 2026
0f31725
Merge branch 'claude/icp-marketplace-compliance-vpawel-2-ledger-guard…
claude Aug 26, 2026
ef65fef
Stack on the ledger-guard branch; mark the top-up milestone merged
claude Aug 26, 2026
cdb31ab
Frame the financial restriction as intent plus reasonable measures
claude Aug 26, 2026
01de76c
Align the listing draft and reviewer test with the intent framing
claude Aug 26, 2026
6bb73a0
Explain why real funds live on the standardized ledger surface (review)
claude Aug 27, 2026
40ff3a5
Merge remote-tracking branch 'origin/main' into claude/icp-marketplac…
claude Aug 27, 2026
52a7b98
Merge branch 'claude/icp-marketplace-compliance-vpawel-2-ledger-guard…
claude Aug 27, 2026
d28a5ed
Split the guard: literal ICRC matching plus ledger-scoped abstract names
claude Aug 27, 2026
20ea3e3
Merge branch 'claude/icp-marketplace-compliance-vpawel-2-ledger-guard…
claude Aug 27, 2026
17d33bc
Describe the per-ledger scoping of the guard in the OpenAI doc
claude Aug 27, 2026
c068c23
Point the creation refusal at the user-run icp CLI, not a connector tool
claude Aug 27, 2026
0979f0b
Merge branch 'claude/icp-marketplace-compliance-vpawel-2-ledger-guard…
claude Aug 27, 2026
fe696eb
Make the financial policy a server instruction in plain protective terms
claude Aug 27, 2026
59002f3
Merge branch 'claude/icp-marketplace-compliance-vpawel-2-ledger-guard…
claude Aug 27, 2026
05b3bda
Align the directory-submission docs with the server-instruction policy
claude Aug 27, 2026
dddc4a4
Make icp_create_canister instructions-only: never execute funding
claude Aug 27, 2026
a06d0ed
Merge branch 'claude/icp-marketplace-compliance-vpawel-2-ledger-guard…
claude Aug 27, 2026
15eb6b3
Merge the instructions-only creation change; sweep NNS and de-risk th…
claude Aug 27, 2026
1538d39
Keep the instructions-only tool descriptions functional, not disclaiming
claude Aug 27, 2026
776af88
Merge branch 'claude/icp-marketplace-compliance-vpawel-2-ledger-guard…
claude Aug 27, 2026
c46add8
Fix the read-only tool count and pin the top-up example prompts
claude Aug 27, 2026
82c7335
Merge remote-tracking branch 'origin/main' into claude/icp-marketplac…
claude Aug 27, 2026
1ef2037
Neutralize the remaining financial data examples; fix a heading and a…
claude Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ categories = [
"network-programming",
"web-programming::http-server",
"api-bindings",
"cryptography::cryptocurrencies",
]
# Everything the library and the bundled binary compile from ships; the
# deployment scaffolding (container, systemd/native deploy, dashboards, CI,
Expand Down
32 changes: 21 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ encoding/decoding and signing against the IC via
[`ic-agent`](https://github.com/dfinity/agent-rs). The MCP layer is the
[official Rust SDK](https://github.com/modelcontextprotocol/rust-sdk) (`rmcp`).

**Not for financial operations.** IMCP2 is infrastructure tooling for reading,
building, and operating canisters — it is not a wallet or trading tool, and
financial operations (token transfers, spending approvals, payments, trades)
are unsupported by policy. The standardized ledger surface is refused
mechanically: `canister_update_call` rejects the ICRC-1/ICRC-2 (and related
ledger-standard) transfer/approval methods on every canister, and
`icp_top_up_canister` and `icp_create_canister` return `icp` CLI instructions
for the user to run themselves instead of executing anything — those tools
create nothing and move no funds. For financial operations, users act
themselves in a wallet or frontend they control (e.g.
[oisy.com](https://oisy.com)), in their own browser.

## Use as a library

Published on [crates.io](https://crates.io/crates/imcp2), API docs on
Expand Down Expand Up @@ -133,10 +145,10 @@ results).

| Tool | Args | Returns |
|------|------|---------|
| `open_app` | `app` (name **or** URL) | **One-call entry point** when a user names/links an app: resolves the Internet Identity `derivation_origin` *and* discovers the canisters behind it, together. A name or bare host is matched to the known-app registry first (so a wrong-TLD guess like `multidex.com` repairs to the canonical URL); an explicit `https://` URL is resolved as given. An unknown bare name, or a URL with no IC evidence, is *refused* (never guessed). Also probes the app's own canisters and reports per-canister `oql`/`api_doc_available` capability flags plus a caller-gated data-access note (which canister holds the app data, and the origin to read it as the user). Wraps `resolve_app` + `discover_app_canisters`; no auth |
| `open_app` | `app` (name **or** URL) | **One-call entry point** when a user names/links an app: resolves the Internet Identity `derivation_origin` *and* discovers the canisters behind it, together. A name or bare host is matched to the known-app registry first (so a wrong-TLD guess repairs to the canonical URL); an explicit `https://` URL is resolved as given. An unknown bare name, or a URL with no IC evidence, is *refused* (never guessed). Also probes the app's own canisters and reports per-canister `oql`/`api_doc_available` capability flags plus a caller-gated data-access note (which canister holds the app data, and the origin to read it as the user). Wraps `resolve_app` + `discover_app_canisters`; no auth |
| `discover_app_canisters` | `domain` | Canister ids behind a web domain — app-declared App Connect metadata first (`/ai-connect.html`'s `ic:canister-id` meta, `/.well-known/ic-app.json` manifest), then the frontend via `x-ic-canister-id` and backend candidates via `/env.json` + JS-bundle mining — each with provenance, its IC dashboard label/type where known, and (for the app's own canisters) `oql`/`api_doc_available` capability flags from a one-shot Candid probe |
| `icp_find_canister_by_name` | `query` | Canister ids matching a name/symbol, searched in the IC dashboard's service registries — ICRC token ledgers (e.g. `ckUSDC`) and the SNS project catalog |
| `icp_find_app_by_name` | `name` | A well-known app's front-end URL + `derivation_origin`, for a small built-in set (NNS, Oisy, MULTI/DEX, ICPSwap). The **first stop** whenever only an app *name* is known — never guess a domain from a name. Any other name returns no match and a `note` directing a web search for the app's URL (there's no on-chain name→URL directory) |
| `icp_find_app_by_name` | `name` | A well-known app's front-end URL + `derivation_origin`, for a small built-in set of well-known IC apps. The **first stop** whenever only an app *name* is known — never guess a domain from a name. Any other name returns no match and a `note` directing a web search for the app's URL (there's no on-chain name→URL directory) |
| `icp_lookup_canister_info_by_id` | `canister_id` | What a canister IS, per the IC dashboard: label/name, type, controllers, subnet, module hash, latest upgrade proposal |
| `get_canister_candid` | `canister_id` | The canister's `candid:service` interface (`.did` text), plus two capability flags: `oql` (`true` when it exposes an OQL query surface — a `schema` + `execute` pair — with a pointer to `icp_oql_guide`) and `api_doc_available` (`true` when it declares a `getApiDoc`/`get_api_doc` method, gating `get_canister_api_doc`) |
| `get_canister_api_doc` | `canister_id` | The canister's own prose API guide ("how this app behaves" — units, auth, lifecycle, mutation safety, polling, gotchas), from its `getApiDoc`/`get_api_doc` method. Call **only** when `get_canister_candid`/`open_app` report `api_doc_available`. Returns a **structured** result in every case — `available` + the doc on success, else `available:false` with `expected`/`retry`/`next` so an expected absence is distinct from an unreachable canister |
Expand Down Expand Up @@ -174,16 +186,16 @@ candidates, pick by label, prefer production/`IC_` ids, and confirm with
Acting **for the user** at an app:

0–2. **`open_app(name-or-URL)`** — the one-call entry point. Pass the *name* the user
said (well-known apps NNS/Oisy/MULTI/DEX/ICPSwap resolve offline) or a URL you
have; it returns the `derivation_origin` **and** the app's canisters in one shot
said (well-known apps resolve offline) or a URL you have (e.g.
`https://opencloud.org`); it returns the `derivation_origin` **and** the app's canisters in one shot
(it runs `resolve_app` + `discover_app_canisters` concurrently under the hood).
**Never guess a domain from a name** — a lookalike like `<name>.com` is an
unrelated or squatted site. The tool enforces this: a bare *unknown* name is
refused (find the real URL — web-search or ask the user), and a URL that resolves
to `app_url_default` while showing **no IC evidence** (no valid `x-ic-canister-id`
gateway header, no `ic-app.json` derivation origin) is refused too; when the host
resembles a known app the error names it and gives the real URL ("did you mean
MULTI/DEX → `https://multidex.ai`"). For a single step, the narrower tools remain:
resembles a known app the error names it and gives the real URL (a
"did you mean" repair). For a single step, the narrower tools remain:
**`icp_find_app_by_name`** (name→URL only), **`resolve_app(url)`** (origin only),
**`discover_app_canisters(url)`** (canisters only).
3. **`list_app_accounts`** — if there's more than one account, ask which to use (and
Expand Down Expand Up @@ -297,10 +309,8 @@ token (see Auth).
> and reuse it: `open_app` (or `resolve_app`) turns an app name/URL into it and
> reports how — `derivation_origin_source`: **declared**
> (`/.well-known/ic-app.json` → `derivation_origin`), else a built-in **known-app**
> value for a few apps that pin a custom origin without declaring it (NNS →
> `https://nns.ic0.app`, Oisy → `https://oisy.com`, MULTI/DEX →
> `https://hcv4s-…icp0.io`, ICPSwap → `https://app.icpswap.com`; an app's own
> declaration always overrides this), else the app origin *assumed*
> value for a few apps that pin a custom origin without declaring it (an app's
> own declaration always overrides this), else the app origin *assumed*
> (**app_url_default**). Feeding that resolved origin to an identity tool records
> `derivation_origin_source` = **explicit**. Every identity result echoes
> `derived_for_origin` (the origin actually used) alongside `requested` (what you
Expand Down Expand Up @@ -328,7 +338,7 @@ token (see Auth).
`icp_list_skills` / `icp_get_skill` expose the official Internet Computer
[skills](https://skills.internetcomputer.org) — authoritative, current how-to
guides for authoring and shipping IC apps (the Motoko language, the `mops` and
`icp` CLIs, cycles management, stable memory & upgrades, canister security, DeFi,
`icp` CLIs, cycles management, stable memory & upgrades, canister security,
auth, …). The catalogue is fetched live from the registry's manifest
(`/api/skills.json`, cached ~15 min) and each skill's `SKILL.md` on demand;
nothing is bundled, so the agent always sees the current skills. They are also
Expand Down
9 changes: 5 additions & 4 deletions crates/imcp2-core/src/discover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1778,7 +1778,8 @@ impl From<(String, Vec<Match>)> for FindCanisterOutput {
/// Arguments for `icp_find_app_by_name`.
#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct FindAppArgs {
/// The app name to look up, e.g. "Oisy", "NNS", "MULTI/DEX", "ICPSwap".
/// The app name to look up, as the user said it. For a site you already know
/// (e.g. "opencloud.org"), use open_app / resolve_app directly.
pub name: String,
}

Expand Down Expand Up @@ -1810,9 +1811,9 @@ pub struct FindAppOutput {
/// Arguments for `open_app`.
#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct OpenAppArgs {
/// An app NAME (e.g. "MULTI/DEX", "Oisy", "NNS") OR its URL (e.g.
/// "https://oisy.com"). A name — or a bare host — is matched against the
/// built-in known-app registry first, so a wrong-TLD guess like "multidex.com"
/// An app NAME as the user said it OR its URL (e.g.
/// "https://opencloud.org"). A name — or a bare host — is matched against the
/// built-in known-app registry first, so a wrong-TLD guess
/// repairs to the canonical URL; an explicit `https://…` URL is resolved as
/// given. NEVER pass a domain you fabricated from a name: an unknown bare name
/// is refused with instructions to find the real URL, and a URL with no
Expand Down
Loading
Loading