Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 0 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# Ethereum JSON-RPC endpoint (default: https://ethereum-rpc.publicnode.com)
ETH_RPC_URL=

# CoinGecko API key (optional, raises rate limits for token discovery)
COINGECKO_API_KEY=

# Blockchair proxy base URL (default: https://api.vultisig.com/blockchair)
BLOCKCHAIR_API_URL=
75 changes: 75 additions & 0 deletions .github/workflows/build-push-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Build and Push Docker Image

on:
workflow_call:
inputs:
service:
required: true
type: string
description: Service name (e.g., mcp-server)
registry:
required: true
type: string
description: Container registry URL
image_name:
required: true
type: string
description: Image name (repository)
tag:
required: true
type: string
description: Image tag
additional_tag:
required: false
type: string
description: Additional tag (e.g., latest, dev-latest)
default: ""
platforms:
required: false
type: string
description: Target platforms (e.g., linux/amd64,linux/arm64)
default: "linux/amd64"

jobs:
build-and-push:
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ inputs.registry }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Prepare tags
id: prep
run: |
TAGS="${{ inputs.registry }}/${{ inputs.image_name }}/${{ inputs.service }}:${{ inputs.tag }}"
if [ -n "${{ inputs.additional_tag }}" ]; then
TAGS="${TAGS},${{ inputs.registry }}/${{ inputs.image_name }}/${{ inputs.service }}:${{ inputs.additional_tag }}"
fi
echo "tags=${TAGS}" >> $GITHUB_OUTPUT

- name: Build and push image
uses: docker/build-push-action@v5
with:
context: .
push: true
platforms: ${{ inputs.platforms }}
tags: ${{ steps.prep.outputs.tags }}
build-args: |
SERVICE=${{ inputs.service }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: false
55 changes: 55 additions & 0 deletions .github/workflows/deploy-dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Build and Deploy to Dev

on:
push:
branches:
- dev
paths-ignore:
- '**.md'
- '.gitignore'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
NS: agent

jobs:
build-server:
uses: ./.github/workflows/build-push-image.yaml
permissions:
contents: read
packages: write
with:
service: mcp-server
registry: ghcr.io
image_name: ${{ github.repository }}
tag: dev-${{ github.sha }}
additional_tag: dev-latest

deploy:
needs: [build-server]
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up kubeconfig
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG_DEV }}" > ~/.kube/config
chmod 600 ~/.kube/config

- name: Update deployment files
run: |
TAG="dev-${{ github.sha }}"
sed -i "s|image: ghcr.io/${{ env.IMAGE_NAME }}/mcp-server:.*|image: ghcr.io/${{ env.IMAGE_NAME }}/mcp-server:${TAG}|" deploy/01_server.yaml

- name: Deploy to Kubernetes
run: |
kubectl -n ${{ env.NS }} apply -f deploy/dev
kubectl -n ${{ env.NS }} apply -f deploy/01_server.yaml
kubectl -n ${{ env.NS }} rollout status deployment/server --timeout=300s
90 changes: 90 additions & 0 deletions .github/workflows/deploy-prod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Build and Push Images

on:
release:
types: [published]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
NS: agent

jobs:
build-server:
uses: ./.github/workflows/build-push-image.yaml
permissions:
contents: read
packages: write
with:
service: mcp-server
registry: ghcr.io
image_name: ${{ github.repository }}
tag: ${{ github.event.release.tag_name }}
additional_tag: latest

set-public:
needs: [build-server]
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- name: Set GHCR packages public
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
owner="${{ github.repository_owner }}"
repo="${{ github.event.repository.name }}"
gh api -X PATCH "/orgs/${owner}/packages/container/${repo}%2Fmcp-server/visibility" -f visibility=public || true

update-manifests:
needs: [build-server, set-public]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main

- name: Update deployment files with new image tags
run: |
TAG="${{ github.event.release.tag_name }}"
sed -i "s|image: ghcr.io/${{ env.IMAGE_NAME }}/mcp-server:.*|image: ghcr.io/${{ env.IMAGE_NAME }}/mcp-server:${TAG}|" deploy/01_server.yaml

- name: Commit and push updated manifests
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add deploy/
git commit -m "chore: update image tags to ${{ github.event.release.tag_name }}" || echo "No changes to commit"
git push origin main

deploy:
needs: [update-manifests]
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main

- name: Set up kubeconfig
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG_PROD }}" > ~/.kube/config
chmod 600 ~/.kube/config

- name: Update deployment files
run: |
TAG="${{ github.event.release.tag_name }}"
sed -i "s|image: ghcr.io/${{ env.IMAGE_NAME }}/mcp-server:.*|image: ghcr.io/${{ env.IMAGE_NAME }}/mcp-server:${TAG}|" deploy/01_server.yaml

- name: Deploy to Kubernetes
run: |
kubectl -n ${{ env.NS }} apply -f deploy/prod
kubectl -n ${{ env.NS }} apply -f deploy/01_server.yaml
kubectl -n ${{ env.NS }} rollout status deployment/server --timeout=300s
41 changes: 0 additions & 41 deletions .github/workflows/deploy.yml

This file was deleted.

26 changes: 26 additions & 0 deletions .github/workflows/sync-main-to-dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Sync main to dev

on:
push:
branches:
- main

jobs:
sync-main-to-dev:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Sync main to dev
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git fetch origin dev:dev
git checkout dev
git merge origin/main --no-edit
git push origin dev
5 changes: 2 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ Config uses `github.com/kelseyhightower/envconfig`. All EVM RPC URLs default to
| `EVM_BLAST_URL` | `https://blast-rpc.publicnode.com` | Blast endpoint |
| `EVM_MANTLE_URL` | `https://mantle-rpc.publicnode.com` | Mantle endpoint |
| `EVM_ZKSYNC_URL` | `https://mainnet.era.zksync.io` | zkSync Era endpoint |
| `COINGECKO_API_KEY` | (empty) | CoinGecko API key (optional, raises rate limits) |
| `BLOCKCHAIR_API_URL` | `https://api.vultisig.com/blockchair` | Blockchair proxy base URL for UTXO chain queries |
| `THORCHAIN_URL` | `https://thornode.ninerealms.com` | THORChain node base URL for fee rates |
| `SOLANA_RPC_URL` | `https://api.mainnet-beta.solana.com` | Solana JSON-RPC endpoint |
Expand Down Expand Up @@ -64,8 +63,8 @@ internal/tools/
search_token.go # Token discovery via CoinGecko API
abi_encode.go # ABI encode function calls / raw args
abi_decode.go # ABI decode output data
convert_amount.go
registry.go
convert_amount.go
registry.go
internal/coingecko/client.go # CoinGecko REST API client
internal/blockchair/client.go # Blockchair UTXO chain API client (via Vultisig proxy)
internal/thorchain/client.go # THORChain node client (fee rates via inbound_addresses)
Expand Down
37 changes: 37 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
FROM golang:1.25.5 AS builder

ARG TARGETARCH=amd64

RUN apt-get update && apt-get install -y clang lld wget

RUN wget https://github.com/vultisig/go-wrappers/archive/refs/heads/master.tar.gz && \
tar -xzf master.tar.gz && \
cd go-wrappers-master && \
mkdir -p /usr/local/lib/dkls/includes && \
cp includes/go-dkls.h includes/go-schnorr.h /usr/local/lib/dkls/includes/ && \
if [ ! -d "includes/linux-${TARGETARCH}" ]; then echo "Error: includes/linux-${TARGETARCH} not found" && exit 1; fi && \
cp -r includes/linux-${TARGETARCH} /usr/local/lib/dkls/includes/linux

ARG SERVICE
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .

ENV CGO_ENABLED=1
ENV CC=clang
ENV CGO_LDFLAGS=-fuse-ld=lld
ENV LD_LIBRARY_PATH=/usr/local/lib/dkls/includes/linux:$LD_LIBRARY_PATH
RUN go build -o main cmd/${SERVICE}/main.go

FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY --from=builder /app/main .
COPY --from=builder /usr/local/lib/dkls /usr/local/lib/dkls
ENV LD_LIBRARY_PATH=/usr/local/lib/dkls/includes/linux:$LD_LIBRARY_PATH
5 changes: 4 additions & 1 deletion cmd/mcp-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func main() {
defer evmPool.Close()

store := vault.NewStore()
cgClient := coingecko.NewClient(cfg.CoinGeckoAPIKey)
cgClient := coingecko.NewClient()
bcClient := blockchair.NewClient(cfg.BlockchairURL)

hooks := mcplog.NewHooks(logger)
Expand Down Expand Up @@ -66,6 +66,9 @@ func main() {
mcpHandler := server.NewStreamableHTTPServer(s)

mux := http.NewServeMux()
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
mux.Handle("/mcp", mcpHandler)
skillHandler := skills.NewHandler(logger)
mux.Handle("/skills", skillHandler)
Expand Down
Loading