@coti-io/coti-sdk-private-messaging
v0.1.0
Published
TypeScript SDK for COTI private messaging, encrypted message access, rewards, and MCP tooling.
Maintainers
Readme
COTI SDK Private Messaging
TypeScript client for PrivateMessaging.
Features
- Encrypt message bodies with a COTI-capable signer or wallet.
- Send private messages to public recipient addresses.
- Automatically split long plaintext into multipart encrypted chunks.
- Page through inbox and sent messages.
- Read viewer-specific ciphertext and decrypt it client-side.
- Check and claim biweekly rewards.
- Request, inspect, or submit a one-time starter COTI claim.
- Expose JSON-safe MCP-style tool definitions and a tool dispatcher.
Example
Install:
npm install @coti-io/coti-sdk-private-messaging @coti-io/coti-ethersimport { Wallet, JsonRpcProvider, CotiNetwork } from "@coti-io/coti-ethers";
import {
getDefaultCotiRpcUrl,
createPrivateMessagingClient,
sendMessage,
listInbox,
claimRewards
} from "@coti-io/coti-sdk-private-messaging";
const provider = new JsonRpcProvider(getDefaultCotiRpcUrl(CotiNetwork.Testnet));
const wallet = new Wallet(process.env.PRIVATE_KEY!, provider);
wallet.setAesKey(process.env.AES_KEY!);
const client = createPrivateMessagingClient({
network: CotiNetwork.Testnet,
runner: wallet
});
await sendMessage(client, {
to: "0xRecipient",
plaintext: "hello from coti"
});
const inbox = await listInbox(client, {
account: wallet.address
});
const claim = await claimRewards(client, {
epoch: 0n
});Longer plaintext is chunked automatically. By default the SDK uses a conservative 24-byte chunk size, matching the current contract guard and the known-safe 3-cell COTI string boundary.
For encrypted message sends, the SDK always attaches a conservative gas limit because estimation is unreliable for encrypted values on COTI. You can still override it when needed:
await sendMessage(client, {
to: "0xRecipient",
plaintext: "very long message ...",
gasLimit: 8_000_000n
});Additional Read APIs
The SDK also exposes the contract inspection helpers agents typically need:
getContractConfig()getAccountStats()getMessageMetadata()getCurrentEpoch()getEpochForTimestamp()getEpochUsage()getEpochSummary()getPendingRewards()
MCP-Style Tool Surface
import {
PRIVATE_MESSAGING_MCP_TOOLS,
invokePrivateMessagingTool
} from "@coti-io/coti-sdk-private-messaging";
const tools = PRIVATE_MESSAGING_MCP_TOOLS;
const result = await invokePrivateMessagingTool(client, "list_inbox", {
account: wallet.address,
limit: 10,
decrypt: true
});invokePrivateMessagingTool() returns JSON-safe data, so bigint fields are serialized as strings for easier MCP transport.
The MCP tool registry includes:
send_messageread_messagelist_inboxlist_sentget_contract_configget_account_statsget_message_metadataget_current_epochget_epoch_for_timestampget_epoch_usageget_pending_rewardsget_epoch_summaryclaim_rewardsfund_epochget_starter_grant_challengeclaim_starter_grant
MCP Server
The package also ships a stdio MCP server entrypoint:
npm run build
npm run start:mcpRequired environment variables:
PRIVATE_KEYAES_KEYCOTI_NETWORK
Optional overrides:
PRIVATE_MESSAGING_CONTRACT_ADDRESS_OVERRIDECOTI_RPC_URL_OVERRIDECOTI_TESTNET_RPC_URL_OVERRIDECOTI_MAINNET_RPC_URL_OVERRIDE
Optional starter-grant service config overrides:
STARTER_GRANT_SERVICE_URLSTARTER_GRANT_SERVICE_TIMEOUT_MSSTARTER_GRANT_SERVICE_AUTH_TOKENSTARTER_GRANT_INSTALL_ID_PATH
Copy .env.example to .env in this package if you want to run the MCP server from the package directory.
Default Network Config
The SDK ships with built-in defaults for both COTI RPC URLs and the private messaging contract address resolution:
- Testnet RPC:
https://testnet.coti.io/rpc - Mainnet RPC:
https://mainnet.coti.io/rpc - Testnet contract:
0xa4C514225Db5B8AE6eF1548d4CE912234A7CD954 - Mainnet contract:
0xe461F448cB935a14585F6f1a30F5b4C73ffF8c05
If you use createPrivateMessagingClient() without contractAddress, the SDK resolves the address from network and defaults to testnet. You can still pass contractAddress explicitly to override the built-in default for either network.
The MCP server exposes these starter-grant tools by default, pointing at the deployed service unless you override it with STARTER_GRANT_SERVICE_URL:
get_starter_grant_challengeget_starter_grant_statusclaim_starter_grantrequest_starter_grant
The starter-grant flow now supports three patterns: request a challenge directly, inspect current claim status, or use the single-call request_starter_grant helper for the current trivial prompt flow. The prompt is lightweight friction, not a serious anti-bot wall, and installId remains only a soft local dedupe signal.
The SDK-level starter-grant helpers also default to the deployed service, so url is optional unless you want to override it:
import { requestStarterGrant } from "@coti-io/coti-sdk-private-messaging";
const result = await requestStarterGrant(client, {
timeoutMs: 15000
});ABI Source
The SDK ships a vendored ABI snapshot in src/abi.ts so published consumers do not depend on contract build artifacts at runtime. Maintainers can refresh it with:
npm run sync:abiBy default the sync script reads ./abi/PrivateMessaging.json when that file exists in this repository. Otherwise set COTI_CONTRACT_ABI_PATH=/absolute/path/to/PrivateMessaging.json.
