Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0a992e9
add ci/cd for wallet
m-parkhill Apr 10, 2026
5581403
refactor CI/CD steps
m-parkhill Apr 16, 2026
8f09b70
e2e test for credential import
m-parkhill Apr 17, 2026
987df3c
added failing e2e test for proof requests in the wallet server
m-parkhill Apr 22, 2026
538c422
Use polyfill to fix issue with referencing local storage when present…
m-parkhill May 15, 2026
f071bc0
get bbs presentation test working
mike-parkhill May 28, 2026
28cf3dc
Fix Docker Hub login by forwarding secrets to reusable publish workflow
mike-parkhill Jul 1, 2026
0b2a936
fix wallet integration tests
mike-parkhill Jul 3, 2026
de6ffb8
fix mapping for tests
mike-parkhill Jul 3, 2026
c03cbdd
feat(mcp-shared): add auth utilities module
mike-parkhill Jul 8, 2026
4dc80b5
feat(mcp-shared): thread auth context through transport and bootstrap
mike-parkhill Jul 8, 2026
8187cfe
feat(wallet-server): add JWT admin tooling
mike-parkhill Jul 8, 2026
0311826
feat(wallet-server): add WalletClientPool and JWT multi-tenant routing
mike-parkhill Jul 9, 2026
78fc56f
test: add unit tests for auth utilities and WalletClientPool
mike-parkhill Jul 9, 2026
56f5638
feat: truvera-api passthrough auth for HTTP mode
mike-parkhill Jul 9, 2026
bf22651
feat: add Terraform ECS deployment for MCP servers
mike-parkhill Jul 9, 2026
a770f31
feat(terraform): create VPC and networking from scratch
mike-parkhill Jul 10, 2026
de4dbbc
feat(terraform): add S3 backend and per-environment tfvars
mike-parkhill Jul 10, 2026
a983422
set staging terraform env values
mike-parkhill Jul 10, 2026
885e4b8
switch terraform from dynamodb to a lockfile
mike-parkhill Jul 10, 2026
1049d6e
update image tags
mike-parkhill Jul 10, 2026
b167105
tweak staging image versions
mike-parkhill Jul 10, 2026
9a3b351
ci: default manual Docker builds to sanitized branch name
mike-parkhill Jul 10, 2026
66d53fb
fix: stub @digitalbazaar/http-client to bypass esm v3.2.25 crash on N…
mike-parkhill Jul 10, 2026
36359db
add more details in preparation for demoing
mike-parkhill Jul 12, 2026
09ecfe0
Updated docs
mike-parkhill Jul 13, 2026
7ed6700
Apply suggestions from code review
mike-parkhill Jul 14, 2026
83a0201
Potential fix for pull request finding 'CodeQL / Clear-text logging o…
mike-parkhill Jul 14, 2026
4aca85b
Make wallet tokens revocable
mike-parkhill Jul 14, 2026
7311706
PR feedback fixes
mike-parkhill Jul 20, 2026
78abe41
Apply Copilot suggestions from code review
mike-parkhill Jul 22, 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
32 changes: 27 additions & 5 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This is a monorepo containing multiple Model Context Protocol (MCP) servers buil
This repository uses an `apps/` directory to organize multiple MCP servers:

- `apps/truvera-api/` - Main Truvera MCP Server for verifiable credentials and DIDs
- `apps/wallet-server/` - Wallet MCP Server for holding credentials, DIDs, and DIDComm messaging via the Truvera Wallet SDK

Each app is a self-contained MCP server with its own:
- `package.json` - Dependencies and scripts
Expand Down Expand Up @@ -44,7 +45,8 @@ Each app is a self-contained MCP server with its own:

- See `apps/truvera-api/README.md` for comprehensive setup and usage instructions
- **See `apps/truvera-api/copilot-instructions.md` for detailed development guidelines**
- See `apps/truvera-api/Dockerfile` for production container build configuration
- See `apps/wallet-server/README.md` for the wallet server's setup, auth, and tool docs
- See `apps/truvera-api/Dockerfile` / `apps/wallet-server/Dockerfile` for production container build configuration
- See `docker-compose.yml` for multi-service deployment
- See `.vscode/tasks.json` for VS Code task configuration
- See `.vscode/launch.json` for VS Code debugging setup
Expand Down Expand Up @@ -75,9 +77,29 @@ npm run typecheck # Run TypeScript type checking
Or from root:

```bash
npm run build:truvera
npm run dev:truvera
npm run test:truvera
npm run build:api
npm run dev:api
npm run test:api
```

### Wallet Server Specific

```bash
cd apps/wallet-server
npm install
npm run dev
npm run build
npm start
npm run typecheck
```

Or from root:

```bash
npm run build:wallet
npm run dev:wallet
npm run test:wallet
npm run test:wallet:integration
```

## Docker Commands
Expand Down Expand Up @@ -107,7 +129,7 @@ VS Code tasks and launch configurations are set up at the workspace root level:
5. Add Dockerfile for containerization
6. Update `docker-compose.yml` to include new service
7. Add build tasks to `.vscode/tasks.json`
8. Add CI steps to `.github/workflows/ci.yml`
8. Add a dedicated, path-filtered CI workflow (see `.github/workflows/ci-wallet.yml` for the pattern) rather than editing `.github/workflows/ci.yml`, which is scoped to `apps/truvera-api` only
9. Create README.md with server-specific documentation

## Security Notes
Expand Down
38 changes: 17 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,34 @@ name: API CI

on:
pull_request:
paths:
- 'apps/truvera-api/**'
- 'packages/mcp-shared/**'
- 'package.json'
- 'package-lock.json'
- 'scripts/**'
- '.github/workflows/ci.yml'
- '.github/workflows/ci-server-reusable.yml'
push:
branches:
- master
paths:
- 'apps/truvera-api/**'
- 'packages/mcp-shared/**'
- 'package.json'
- 'package-lock.json'
- 'scripts/**'
- '.github/workflows/ci.yml'
- '.github/workflows/ci-server-reusable.yml'

jobs:
changes:
runs-on: ubuntu-latest
outputs:
run: ${{ steps.filter.outputs.run }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
run:
- 'apps/truvera-api/**'
- 'packages/mcp-shared/**'
- 'package.json'
- 'package-lock.json'
- 'scripts/**'
- '.github/workflows/ci.yml'
- '.github/workflows/ci-server-reusable.yml'

api:
needs: changes
uses: ./.github/workflows/ci-server-reusable.yml
permissions:
actions: read
contents: read
security-events: write
with:
should_run: ${{ needs.changes.outputs.run == 'true' }}
job_name: Build and Test API Server
build_script: npm run build:api
test_script: npm run test -- --run
Expand All @@ -48,3 +43,4 @@ jobs:
trivy_sarif_file: trivy-results.sarif
trivy_scan_label: API Trivy Image Scan
sarif_category: trivy-container-scan-api

6 changes: 6 additions & 0 deletions .github/workflows/docker-publish-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ on:
required: false
default: true
type: boolean
tag:
description: Image tag (leave blank to use sanitized branch name)
required: false
default: ''
type: string

jobs:
api:
Expand All @@ -35,3 +40,4 @@ jobs:
dockerfile: ./apps/truvera-api/Dockerfile
build_number_file: apps/truvera-api/.buildnumber
should_push: ${{ github.event_name != 'workflow_dispatch' || inputs.push }}
tag: ${{ inputs.tag }}
20 changes: 15 additions & 5 deletions .github/workflows/docker-publish-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ on:
should_push:
required: true
type: boolean
tag:
description: Explicit image tag; leave blank to use branch name (workflow_dispatch) or build number (push/release)
required: false
type: string
default: ''

jobs:
publish:
Expand All @@ -29,18 +34,23 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4

- name: Read build number
- name: Determine image tag
id: build_number
run: |
if [ -f "${BUILD_NUMBER_FILE}" ]; then
if [ -n "${{ inputs.tag }}" ]; then
BUILD_NUM="${{ inputs.tag }}"
echo "Using explicit tag: $BUILD_NUM"
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
BUILD_NUM=$(echo "${{ github.ref_name }}" | sed 's/[^a-zA-Z0-9._-]/-/g')
echo "Using sanitized branch name: $BUILD_NUM"
elif [ -f "${BUILD_NUMBER_FILE}" ]; then
BUILD_NUM=$(cat "${BUILD_NUMBER_FILE}")
echo "Using ${BUILD_NUMBER_FILE}"
echo "Using ${BUILD_NUMBER_FILE}: $BUILD_NUM"
else
BUILD_NUM="${{ github.run_number }}"
echo "${BUILD_NUMBER_FILE} not found; using GitHub run number"
echo "Using GitHub run number: $BUILD_NUM"
fi
echo "build_number=$BUILD_NUM" >> "$GITHUB_OUTPUT"
echo "Build number: $BUILD_NUM"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/docker-publish-wallet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ on:
required: false
default: true
type: boolean
tag:
description: Image tag (leave blank to use sanitized branch name)
required: false
default: ''
type: string

jobs:
wallet:
Expand All @@ -35,3 +40,4 @@ jobs:
dockerfile: ./apps/wallet-server/Dockerfile
build_number_file: apps/wallet-server/.buildnumber
should_push: ${{ github.event_name != 'workflow_dispatch' || inputs.push }}
tag: ${{ inputs.tag }}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ truvera/
src/build-info.ts
.claude/settings.local.json
data/

# Terraform — state and local overrides are gitignored; environments/ is committed
terraform/.terraform/
terraform/terraform.tfstate
terraform/terraform.tfstate.backup
terraform/terraform.tfvars
79 changes: 58 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,22 @@ A monorepo containing Model Context Protocol (MCP) servers for Truvera API integ
| Server | Status | Description |
|--------|--------|-------------|
| [`apps/truvera-api`](apps/truvera-api/README.md) | ✅ Production-ready | Verifiable credentials, DIDs, proof requests, schemas, profiles, AP2 mandates |
| [`apps/wallet-server`](apps/wallet-server/README.md) | 🚧 Work in progress | Truvera Wallet SDK — hold credentials, DIDComm messaging, proof responses |
| [`apps/wallet-server`](apps/wallet-server/README.md) | ✅ Production-ready | Truvera Wallet SDK — hold credentials, DIDComm messaging, proof responses |

## Quickstart (5 minutes)
## Two ways to run these servers

There are two independent deployment models — pick whichever fits:

| | **Self-hosted** | **Hosted (Truvera-run)** |
|---|---|---|
| Who runs the container | You, locally or on your own infra | Truvera's infra team (deployed via `terraform/` to ECS) |
| Where it lives | `http://localhost:3000` (or wherever you deploy it) | `https://mcp-api.truvera.io` / `https://mcp-api-staging.truvera.io` (and the `mcp-wallet*` equivalents for the wallet server) |
| Setup required | Clone this repo, `npm install`, run via Docker or Node | None — just point your AI assistant at the URL |
| Best for | Local development, custom deployments, air-gapped environments, or a shared team key (see below) | Everyone else — no local server to run or keep up to date |

Both models expose the same HTTP API and the same authentication model — see [Connecting to AI Assistants](#connecting-to-ai-assistants). The rest of this Quickstart covers **self-hosting**. If you just want to connect to the Truvera-hosted servers, skip straight to [docs/claude-desktop-setup.md](docs/claude-desktop-setup.md).

## Quickstart (5 minutes) — self-hosting

### Prerequisites

Expand All @@ -36,6 +49,8 @@ cp apps/truvera-api/.env.example apps/truvera-api/.env
# Edit apps/truvera-api/.env and set your TRUVERA_API_KEY
```

> **Is this step required?** Only if you want a shared key: setting `TRUVERA_API_KEY` here lets every client skip sending its own Authorization header (handy for a single-user or shared-team deployment). If you'd rather each person/client use their own key, you can leave this blank — see [Connecting to AI Assistants](#connecting-to-ai-assistants) for how per-client keys work.

### 3. Build and run with Docker (recommended)

```bash
Expand Down Expand Up @@ -63,18 +78,29 @@ MCP_MODE=http npm start # HTTP transport (recommended)

## Connecting to AI Assistants

Once the server is running in HTTP mode on port 3000:
Once the server is running in HTTP mode on port 3000, connect your AI assistant to `http://localhost:3000/mcp` (self-hosted) or the Truvera-hosted URL (see [Two ways to run these servers](#two-ways-to-run-these-servers)).

### Authentication: two options

Every request to `/mcp` needs a Truvera API key, resolved one of two ways:

1. **Shared server key (self-hosted only).** If you set `TRUVERA_API_KEY` in `apps/truvera-api/.env` (Quickstart step 2), the server uses it automatically for any request that doesn't supply its own key. This is the simplest option for a single user or a small team sharing one account — no header needed, so the plain "just paste a URL" client config (Option A below) works.
2. **Per-client Authorization header.** Send `Authorization: Bearer <your-api-key>` with each connection. This always overrides the shared key when both are present, and it's the **only** option against the Truvera-hosted servers (`mcp-api.truvera.io` etc.), since those are multi-tenant and don't have a single shared key — each person authenticates with their own.

If neither is present, the server rejects the request with a 401.

### Claude Desktop

**Option A — Settings UI (recommended, no config file editing)**
**Option A — Settings UI, no header (self-hosted with a shared key only)**

1. Open Claude Desktop and go to **Settings → Integrations**
2. Click **+ Add Integration**
3. Enter a name (`Truvera`) and the URL: `http://localhost:3000/mcp`
4. Click **Add**, then restart Claude Desktop

**Option B — Manual config file** (if the Integrations UI is unavailable)
This only works if the server has `TRUVERA_API_KEY` set (option 1 above) — the Integrations UI has no field for a custom header, so it can't carry a per-client key. If you need a per-client key (including for the Truvera-hosted servers), use Option B.

**Option B — Manual config file, with a per-client key**

Edit the config file for your OS:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
Expand All @@ -86,32 +112,40 @@ Edit the config file for your OS:
"mcpServers": {
"truvera": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:3000/mcp", "--insecure"]
"args": [
"-y", "mcp-remote",
"http://localhost:3000/mcp",
"--insecure",
"--header", "Authorization: Bearer YOUR_API_KEY_HERE"
]
}
}
}
```

Restart Claude Desktop after saving.
Drop `--insecure` (it's only needed for a plain `http://` URL) and swap in the hosted URL if you're connecting to a Truvera-hosted server instead of self-hosting. Restart Claude Desktop after saving.

### VS Code (GitHub Copilot)

> **Requires VS Code 1.99 or later** (released March 2025). The workspace config uses the native `type: "http"` MCP format which is not supported in older versions — tools will silently not appear. If you're on an older version, use the [Claude Desktop manual config](#option-b-manual-config) instead.
> **Requires VS Code 1.99 or later** (released March 2025). The workspace config uses the native `type: "http"` MCP format which is not supported in older versions — tools will silently not appear. If you're on an older version, use the [Claude Desktop manual config](#option-b-manual-config-with-a-per-client-key) instead.

The workspace `.vscode/mcp.json` is already configured. Start the server, then:
The workspace `.vscode/mcp.json` is already configured with no header — this relies on the shared-key fallback, so it only works if you've set `TRUVERA_API_KEY` in `apps/truvera-api/.env`. Start the server, then:

1. Open Copilot Chat (`Ctrl+Shift+I` / `Cmd+Shift+I`)
2. Switch to **Agent** mode using the dropdown at the top of the chat pane (MCP tools are only available in Agent mode)
3. The Truvera tools will be listed in the tools panel

To add the server to your personal VS Code (all projects), open the Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`) and run **MCP: Open User Configuration**, then add:
To use a per-client key instead (required for the Truvera-hosted servers), open the Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`), run **MCP: Open User Configuration**, and add:

```json
{
"servers": {
"truvera": {
"type": "http",
"url": "http://localhost:3000/mcp"
"url": "http://localhost:3000/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY_HERE"
}
}
}
}
Expand All @@ -122,7 +156,7 @@ To add the server to your personal VS Code (all projects), open the Command Pale
1. Open **Cursor Settings** (`Ctrl+,` / `Cmd+,`)
2. Go to **Features → MCP**
3. Click **+ Add new MCP server**
4. Set type to **HTTP** and enter URL: `http://localhost:3000/mcp`
4. Set type to **HTTP**, enter URL: `http://localhost:3000/mcp`, and (unless you're relying on the shared-key fallback) add a custom header `Authorization: Bearer YOUR_API_KEY_HERE`
5. Save and restart Cursor

### What to try once connected
Expand All @@ -141,23 +175,24 @@ The AI will call the appropriate Truvera API tools and return real results. No c
## Troubleshooting

**Server won't start**
- Make sure `TRUVERA_API_KEY` is set in `apps/truvera-api/.env` (not blank, not the placeholder `your-api-key-here`).
- Run `curl http://localhost:3000/health` — if that returns an error, check the Docker container logs: `docker logs truvera-mcp-service`.
- Note that `TRUVERA_API_KEY` being unset is *not* a startup error in HTTP mode — it just means every client must send its own Authorization header (see [Authentication: two options](#authentication-two-options)). It's only required at startup in STDIO mode.

**Claude doesn't show Truvera tools**
- Restart Claude Desktop after adding the integration or editing the config file.
- Confirm the server is running first: `curl http://localhost:3000/health`.
- In VS Code, make sure you're in **Agent** mode — MCP tools are invisible in Ask or Edit mode.

**`--insecure` flag in the manual Claude Desktop config**
- Only needed when using the `mcp-remote` fallback config (Option B). It allows `mcp-remote` to connect to a plain `http://` URL. It does **not** affect your Truvera API key security. The Settings UI method (Option A) does not require this flag.
- Only needed when using the `mcp-remote` fallback config (Option B) against a plain `http://` URL (e.g. self-hosted on localhost). It allows `mcp-remote` to connect without TLS. Drop it for the Truvera-hosted `https://` URLs. It does **not** affect your Truvera API key security. The Settings UI method (Option A) doesn't use `mcp-remote` at all, so this flag never applies there.

**Tools appear but calls fail with auth errors**
- The API key in your `.env` may be for the wrong environment. Testnet keys only work with `https://api-testnet.truvera.io`; production keys with `https://api.truvera.com`. Check `TRUVERA_API_ENDPOINT` in your `.env`.
- Your API key may be for the wrong environment. Testnet keys only work with `https://api-testnet.truvera.io`; production keys with `https://api.truvera.com`. Check `TRUVERA_API_ENDPOINT` in `apps/truvera-api/.env` (self-hosted) or confirm which environment your key was issued for (hosted).
- Double check whether you're relying on the shared-key fallback or a per-client header — a stale/wrong key in either place produces the same error.

## MCP Inspector (Shared for All Servers)

Use MCP Inspector to debug or manually exercise tools from any MCP server in this repo (for example, `apps/truvera-api` now, and `apps/wallet-server` as it matures).
Use MCP Inspector to debug or manually exercise tools from any MCP server in this repo.

### 1. Start the Inspector UI

Expand All @@ -174,7 +209,7 @@ npm run docker:run:api
# or: cd apps/truvera-api && MCP_MODE=http npm run dev
```

Wallet server (HTTP mode, work in progress):
Wallet server (HTTP mode):

```bash
cd apps/wallet-server
Expand All @@ -187,7 +222,8 @@ MCP_MODE=http npm run dev
2. Choose Streamable HTTP transport
3. Connect to the target server endpoint:
- Truvera API: `http://localhost:3000/mcp`
- Wallet server: `http://localhost:3010/mcp` (if `MCP_PORT=3010`)
- Wallet server: `http://localhost:3001/mcp` by default (the *host* port shifts if you run it via `docker:run:wallet` and 3001 is busy — the container's internal `MCP_PORT` stays 3001 unless you override it)
4. If the server requires auth (a per-client header or, for the wallet server, a JWT), add it as a custom header in Inspector's connection settings.

If connection fails, check the target server health endpoint first (`/health`) and confirm ports/env values.

Expand All @@ -197,9 +233,10 @@ If connection fails, check the target server health endpoint first (`/health`) a
truvera-mcp-server/
├── apps/
│ ├── truvera-api/ # ✅ Truvera REST API MCP server (production-ready)
│ └── wallet-server/ # 🚧 Wallet SDK MCP server (work in progress)
│ └── wallet-server/ # Wallet SDK MCP server (production-ready)
├── packages/
│ └── mcp-shared/ # Shared MCP server bootstrap utilities
│ └── mcp-shared/ # Shared MCP server bootstrap, transport, and auth utilities
├── terraform/ # Hosted (Truvera-run) ECS deployment for both servers
├── .vscode/ # VS Code tasks, launch configs, MCP server config
├── docker-compose.yml # Compose file for running the truvera-api service
└── README.md # This file
Expand Down Expand Up @@ -228,7 +265,7 @@ See [apps/truvera-api/README.md](apps/truvera-api/README.md) for full documentat

## CI/CD

GitHub Actions builds all apps, runs unit and integration tests, and performs a smoke test on every push. See [.github/workflows/ci.yml](.github/workflows/ci.yml) for details.
Each server has its own GitHub Actions workflow, triggered only by changes to that server (or shared code): [.github/workflows/ci.yml](.github/workflows/ci.yml) for `truvera-api` (unit tests only) and [.github/workflows/ci-wallet.yml](.github/workflows/ci-wallet.yml) for `wallet-server` (unit + integration tests). Both build the Docker image and run a Trivy image scan.

Docker image publishing uses two tag policies:
- `latest` is pushed only when a GitHub Release is published from the `master` branch (stable release image).
Expand Down
Loading
Loading