You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: forc mcp server - with forc-call tool integration (#7284)
## Description
Introducing Forc-MCP module (CLI).
The MCP server can be run in 3 modes:
- `stdio`
- `sse`: long-running server - using http server-side-events
- `http`: long-running server - using http streams
### Forc-call integration
The first tool to be integrated is `forc-call` - which exposes the
following tool calls:
- `list_contract_functions`
- `transfer_assets`
- `get_execution_trace`
- `call_contract`
And the following resources:
- `MCP Type Encoding Reference`
- `MCP Tool Usage Examples`
- `Contract Examples with MCP Tools`
### Screenshots from `@modelcontextprotocol/inspector`
<img width="2543" height="549" alt="Screenshot 2025-07-15 at 5 09 16 PM"
src="https://github.com/user-attachments/assets/aaab5174-d5e6-4685-be71-fd17cd4f5d0f"
/>
<img width="2540" height="531" alt="Screenshot 2025-07-15 at 5 09 30 PM"
src="https://github.com/user-attachments/assets/9ab39d5a-950c-4404-a3a3-6e34b89f58e7"
/>
### Example usage (claude code)
Pre-requisites:
- Run local fuel-core node
```
fuel-core run --ip 127.0.0.1 --port 4000 --db-type rocks-db --debug
--historical-execution
```
- Deploy a contract to local node: `forc deploy --default-signer`
1. Run the MCP server (using http-steam in this example):
```
cargo run -p forc-mcp -- http
```
2. Add mcp server to claude
```
claude mcp add --transport http forc-mcp-http http://localhost:3001/mcp
```
3. Use claude/LLM to interact with MCP server
<details>
<summary>Claude output</summary>
```
╭───────────────────────────────────────────────────╮
│ ✻ Welcome to Claude Code! │
│ │
│ /help for help, /status for your current setup │
│ │
│ cwd: /Users/z/Desktop/fuellabs/sway │
╰───────────────────────────────────────────────────╯
> Use the forc-mcp-http.
Given the following sway contract:
demo/demo/src/main.sw
deployed at the following address:
ae40e8dbe366c5ac61fe558e4e9139300534cd5d9463522621a903464c441eb2
Using the forc-mcp-http:
> list all its callable functions.
> can you get the current count?
> tell me how much gas it would cost to increment the count.
> show me the execution trace for incrementing the count.
⎿ ⧉ Selected 14 lines from scratch/ai/templates.md in Cursor
⏺ I'll help you interact with the deployed Sway contract using forc-mcp-http. Let me start by reading
the contract to understand its structure, then perform the requested operations.
⏺ Read(demo/demo/src/main.sw)
⎿ Read 34 lines (ctrl+r to expand)
⏺ Now I need to find the ABI file for this contract to use with the forc-mcp-http tools:
⏺ Search(pattern: "demo/demo/out/**/*.json")
⎿ Found 7 files (ctrl+r to expand)
⏺ Perfect! I found the ABI file and a deployment file that matches the contract address. Let me read
the ABI:
⏺ Read(demo/demo/out/release/demo-abi.json)
⎿ Read 89 lines (ctrl+r to expand)
⏺ Now let me use the forc-mcp-http tools to interact with the contract. First, I'll list all callable
functions:
⏺ forc-mcp-http:list_contract_functions (MCP)(abi: "/demo/demo/out/rele
ase/demo-abi.json", contract_id: "ae40e8dbe366c5ac61fe55
8e4e9139300534cd5d9463522621a903464c441eb2")
⎿ Callable functions for contract: ae40e8dbe366c5ac61fe558e4e9139300534cd5d9463522621a903464c441eb2
get_count() -> u64
… +186 lines (ctrl+r to expand)
⏺ Now let me get the current count:
⏺ forc-mcp-http:call_contract (MCP)(abi: "/demo/demo/out/release/demo-a
bi.json", contract_id: "ae40e8dbe366c5ac61fe558e4e9139300534cd5d94
63522621a903464c441eb2", function: "get_count")
⎿ {
"tx_hash": "0f1e868e75530f33d37748cac9ae25c33bf166c9e02d28e36f3ffdd67ecf3584",
"total_gas": 65007,
… +354 lines (ctrl+r to expand)
⏺ Now let me check the gas cost for incrementing the count using dry-run mode:
⏺ forc-mcp-http:call_contract (MCP)(abi: "/demo/demo/out/release/demo-a
bi.json", contract_id: "ae40e8dbe366c5ac61fe558e4e9139300534cd5d94
63522621a903464c441eb2", function: "increment_count",
function_args: ["(1, 2)"], mode: "dry-run")
⎿ Error: Contract call failed: input must start with '{' and end with '}': (1, 2)
⏺ Let me fix the format for the struct argument:
⏺ forc-mcp-http:call_contract (MCP)(abi: "/demo/demo/out/release/demo-a
bi.json", contract_id: "ae40e8dbe366c5ac61fe558e4e9139300534cd5d94
63522621a903464c441eb2", function: "increment_count",
function_args: ["{\"vals\": [1, 2]}"], mode: "dry-run")
⎿ Error: Contract call failed: input must start with '(' and end with ')': "vals": [1, 2]
⏺ Let me try the tuple format as shown in the example:
⏺ forc-mcp-http:call_contract (MCP)(abi: "/demo/demo/out/release/demo-a
bi.json", contract_id: "ae40e8dbe366c5ac61fe558e4e9139300534cd5d94
63522621a903464c441eb2", function: "increment_count",
function_args: ["((1, 2))"], mode: "dry-run")
⎿ Error: Contract call failed: input must start with '{' and end with '}': ((1, 2))
⏺ Let me try with the correct struct format:
⏺ forc-mcp-http:call_contract (MCP)(abi: "/demo/demo/out/release/demo-a
bi.json", contract_id: "ae40e8dbe366c5ac61fe558e4e9139300534cd5d94
63522621a903464c441eb2", function: "increment_count",
function_args: ["{(1, 2)}"], mode: "dry-run")
⎿ {
"tx_hash": "f694c73230e893b00968d8106f5bafbcd585c2cf0f4aea1c6a562ce7e1868690",
"total_gas": 78940,
… +399 lines (ctrl+r to expand)
⏺ Perfect! Now let me get the execution trace for that increment operation:
⏺ forc-mcp-http:get_execution_trace (MCP)(total_gas: 78940, trace_events: [{"Call":{"index":0,"method
":"increment_count","arguments":["Adder { vals: (1, 2)
}"],"to":"ae40e8dbe366c5ac61fe558e4e9139300534cd5d9463522621
a903464c441eb2","amount":0,"gas":26222}},{"LogData":{"index"
:2,"id":"ae40e8dbe366c5ac61fe558e4e9139300534cd5d9463522621a
903464c441eb2","value":"3","len":8}},{"ReturnData":{"index":
4,"id":"ae40e8dbe366c5ac61fe558e4e9139300534cd5d9463522621a9
03464c441eb2","data":"()"}},{"Return":{"index":6,"id":"00000
00000000000000000000000000000000000000000000000000000000000"
,"val":1}},{"ScriptResult":{"index":7,"result":"Success","ga
s_used":22210}}])
⎿ Traces:
[Script]
├─ [26222] 0xae40e8dbe366c5ac61fe558e4e9139300534cd5d9463522621a903464c441eb2::
… +8 lines (ctrl+r to expand)
⏺ Summary
Here are the results for the deployed Sway contract at
ae40e8dbe366c5ac61fe558e4e9139300534cd5d9463522621a903464c441eb2:
Callable Functions:
1. get_count() → returns u64 (read-only)
3. increment_count(adder: Adder) → returns () (read/write storage)
Current Count:
The current count is 1
Gas Cost for Increment:
Incrementing the count with Adder { vals: (1, 2) } costs 78,940 gas total (22,210 gas used by
script)
Execution Trace:
The increment operation:
- Calls increment_count with Adder { vals: (1, 2) }
- Uses 26,222 gas for the contract call
- Emits a log with value 3 (0 + 1 + 2 = 3, the new count)
- Returns () (unit/void)
- Total script gas used: 22,210
- Result: Success
The contract adds the two values from the Adder struct (1 + 2 = 3) to the current storage count (0)
and logs the result.
```
</details>
## Checklist
- [ ] I have linked to any relevant issues.
- [ ] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] If my change requires substantial documentation changes, I have
[requested support from the DevRel
team](https://github.com/FuelLabs/devrel-requests/issues/new/choose)
- [ ] I have added tests that prove my fix is effective or that my
feature works.
- [ ] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [ ] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [ ] I have requested a review from the relevant team or maintainers.
---------
Co-authored-by: z <zees-dev@users.noreply.github.com>
0 commit comments