stellar-copilot-mcp

stellar-copilot-mcp

MCP server for Stellar that enables AI assistants to explain account holdings, diagnose transaction failures, and describe Soroban contracts in plain language, with optional payment proposal flow.

Category
访问服务器

README

stellar-copilot-mcp

MCP server for Stellar. Ask an AI assistant what an account holds, why a transaction failed, or what a Soroban contract does — and get an answer in plain language. Optionally, propose payments the user approves in their own wallet.

Holds no keys. Signs nothing. Submits nothing. Reads use public chain data. Transactions are built unsigned and handed to a page the user controls; signing happens in Freighter and nowhere else.


Tools

Tool Answers
explain_account "What do I hold?" · "Why can't I spend my whole balance?" · "Is my trustline set up?"
diagnose_transaction "Why did this fail?" — decodes transaction and operation result codes into causes and fixes, and recovers Soroban contract error codes
explain_contract "What can this contract do?" — reads a deployed contract's published interface

Running the HTTP transport adds three more:

Tool Purpose
start_pairing Returns a link the user opens in the browser where Freighter lives
get_pairing_status Whether the wallet connected, and how an approval turned out
propose_payment Builds an unsigned payment and sends it to the user's approval page

The stdio binary exposes only the three read tools. Pairing needs a server to host the approval page and hold session state, which stdio has neither of.

Install

Requires Node 20+.

npm install && npm run build

Claude Code

claude mcp add stellar-copilot -- node /absolute/path/to/stellar-copilot-mcp/dist/index.js

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "stellar-copilot": {
      "command": "node",
      "args": ["/absolute/path/to/stellar-copilot-mcp/dist/index.js"],
      "env": { "STELLAR_NETWORK": "testnet" }
    }
  }
}

Cursor and other local clients take the same command; see their connector docs.

Remote clients (ChatGPT, Gemini)

Remote clients connect over a URL rather than spawning a process, so run the HTTP transport:

npm run start:http

Serves MCP at http://127.0.0.1:3000/mcp and a health check at /health. Point your client at the /mcp URL.

To use it from Claude, ChatGPT, or Gemini you need a public HTTPS URL — those clients cannot reach localhost. A Dockerfile and fly.toml are included. See DEPLOY.md.

Configuration

Variable Default Notes
STELLAR_NETWORK testnet testnet or public
STELLAR_HORIZON_URL network default Override for a private Horizon
STELLAR_RPC_URL testnet default; empty on mainnet Required on public — see below

HTTP transport only:

Variable Default Notes
PORT 3000 Listen port
HOST 127.0.0.1 Bind address. Keep it on loopback unless it is behind a reverse proxy.
MCP_PATH /mcp Endpoint path
MCP_ALLOWED_HOSTS localhost variants Comma-separated. Required when deployed under a real hostname, or requests are rejected with 403.
MCP_ALLOWED_ORIGINS unset Comma-separated browser origins, if any

On mainnet you must set STELLAR_RPC_URL. SDF does not operate a public mainnet Soroban RPC endpoint, so there is no sensible default. Horizon-backed tools (explain_account, diagnose_transaction) work on mainnet without it; explain_contract needs it and will tell you so rather than failing at startup.

Try it

npm run inspect   # MCP Inspector

Or drive it directly:

printf '%s\n%s\n%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"cli","version":"0"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
  | node dist/index.js

Development

npm run typecheck   # tsc --noEmit
npm run dev         # tsc --watch
npm run build       # emit to dist/

Two constraints to respect when adding code

stdout is the JSON-RPC channel. Never write to it. All logging goes to stderr — a stray console.log corrupts the protocol stream, and the failure looks like a client bug.

The HTTP transport is stateless. No session IDs, no shared state — a fresh server per request. Every tool is a pure read, so there is nothing to remember between calls, and stateless means no session table to leak, expire, or scale. It also means POST is the only meaningful method: there is no stream to open and no session to delete.

DNS rebinding protection is on by default. Without it, any web page the user visits could POST to a server bound on localhost and drive its tools from the browser. Deploying under a real hostname means adding it to MCP_ALLOWED_HOSTS.

Keep this server read-only. No key material, no signing, no submission. Transaction building and signing belong behind an independent simulation-and-approval step in a separate deployable (see ../TECHNICAL-SPEC.md). The boundary is structural, not a policy note.

Decoding contract errors

Contract-defined codes (Error(Contract, #1205)) mean whatever that contract's #[contracterror] enum says, so they need a protocol table. Pass a protocol hint:

diagnose_transaction(hash: "...", protocol: "blend-v2-pool")

Known tables: blend-v1-pool, blend-v2-pool, soroswap-pair — see src/lib/contractErrorTables.ts.

Tables are keyed by protocol, not contract address, because Blend pools are permissionless: every pool is a separate deployment sharing one error enum, so an address-keyed registry would mean enumerating every pool that will ever exist.

KNOWN_CONTRACTS maps verified addresses to protocols and is intentionally empty. An address goes in only once confirmed against a published deployment list — a wrong entry would attach a confident wrong explanation to someone's real failed transaction. Every table must cite the source and date it was read from; a test enforces this.

Adding a failure explanation

src/lib/resultCodes.ts maps XDR result-code names to a { meaning, fix } pair. Add the code, write the explanation for someone who has never read the Stellar docs, and say what to do about it.

Contract-defined codes are not in the result XDR, and they are not recoverable from the historical record either: Horizon 27 returns no transaction meta at all, and public Soroban RPC nodes leave diagnosticEvents empty. The server recovers them by re-simulating the call, and says so in its output — simulation runs against current ledger state, so it is evidence rather than proof of the original error.

Status

Verified against live Stellar testnet on 30 July 2026: clean typecheck under strict + noUncheckedIndexedAccess, working MCP handshake, correct reserve math on a Friendbot-funded account, and correct diagnosis of a real failed Soroban contract call.

62 unit tests (including the HTTP transport and the approval page end to end) and 9 live integration tests pass. The pairing flow, independent decoding, and injection blocking are verified in a real browser.

Not yet verified: Freighter signing itself. It is a browser extension, so signTransaction and the submit path need a machine with Freighter installed. Known gap: KNOWN_CONTRACTS is empty, so contract errors need an explicit protocol hint until verified deployment addresses are added.

Proposing transactions

Only over the HTTP transport:

PUBLIC_BASE_URL=https://your-host npm run start:http

The flow:

  1. start_pairing returns a link. The user opens it where Freighter is installed.
  2. The page connects the wallet and reports the address back.
  3. propose_payment builds an unsigned transaction and queues it for the page.
  4. The page decodes the XDR itself and shows what it actually does.
  5. The user signs in Freighter. The page submits.
  6. get_pairing_status reports the outcome.

Why the page decodes it again

The assistant's description of a transaction is treated as an untrusted claim, never as truth. If a prompt injection made the model build a malicious transaction, the model's description of that transaction would be malicious too — so the only thing that catches it is comparing the description against independently decoded reality.

When they disagree, the page shows the mismatch and disables the approve button. A warning a user can click straight past is not a control.

For the same reason, no tool returns a decoded preview to the model. If it could read the decode, it could misreport it, and the user would be approving the model's account of the transaction rather than the transaction. A test asserts no such tool exists.

PUBLIC_BASE_URL must be an origin the user's browser can reach; it is what pairing links point at. Sessions are in-memory, expire after 30 minutes idle, and hold no key material.

Privacy

The server runs locally, stores nothing, and handles only public blockchain identifiers — never keys or credentials. It queries public Stellar infrastructure (Horizon, Soroban RPC), both of which you can repoint via environment variables. Full policy: PRIVACY.md.

Licence

Apache-2.0

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选