Connect EUDI‑compliant data wallets (e.g., Talao) to AI agents using the Model Context Protocol (MCP) and OIDC4VP (pull model).
This service bridges decentralized identity wallets with AI systems such as ChatGPT, VS Code, or custom agents.
https://wallet-connectors.comPOST https://wallet-connectors.com/mcpparams.arguments, result.content[], result.structuredContent)This guide covers: authentication, test profiles, tool reference, examples, integrations, and best practices.
Authorization: Bearer <token> X-API-KEY: <token>For public test profiles, token = verifier_id (0000, 0001, 0002, 0003).
No cookies or OAuth are used. Each MCP call must include the header.
verifier_id |
OIDC4VP Draft | Verifier identity | Default scope | Returned data (typical fields) | X-API-KEY |
|---|---|---|---|---|---|
0000 |
Draft 20 | Redirect-URI | wallet_identifier |
wallet_identifier (DID or JWK thumbprint) |
0000 |
0001 |
Draft 20 | DID | email |
email_address (email) |
0001 |
0002 |
Draft 20 | DID | over18 |
over_18 (boolean) |
0002 |
0003 |
Draft 28 | DID | profile |
family_name, given_name, birth_date |
0003 |
Use these IDs and keys for sandbox testing.
To register your own verifier or Presentation Definition (custom claims), see Section 11.
start_wallet_verificationStarts a new OIDC4VP flow and returns a QR + deeplink for user authentication.
Arguments:
- verifier_id (required) — one of your registered verifier profiles (demo: 0000–0003)
- session_id (optional) — custom session (else generated)
The scope is implicit based on the chosen
verifier_id(see table above).
Returns:
content[] (QR image, helper text) + structuredContent JSON with:**
content[] (QR image, helper text) + structuredContent JSON with:
{
"session_id": "...",
"deeplink_url": "openid4vp://...?request_uri=...",
"pull_url": "https://wallet-connectors.com/verifier/wallet/pull/...",
"public_base_url": "https://wallet-connectors.com"
}
poll_wallet_verificationPoll verification status for a session_id.
Returns:
{
"status": "pending | verified | denied",
"session_id": "...",
"claims": {...}
}
Tokens (vp_token, id_token) are redacted; only derived claims are returned.
revoke_wallet_flowAcknowledge cleanup after completion. Useful for front‑ends; the backend TTL handles expiry.
The demo verifier_ids bind to a single scope each:
| Verifier | Scope | Returned claims (flattened) | Notes |
|---|---|---|---|
0000 |
wallet_identifier |
wallet_identifier |
Often DID or JWK thumbprint derived from ID token |
0001 |
email |
email_address (aka email) |
PID/eIDAS‑style |
0002 |
over18 |
over_18 (boolean) |
True/false age gate |
0003 |
profile |
family_name, given_name, birth_date |
OIDF standard profile subset |
For custom scopes/claims, register your verifier with a Presentation Definition.
---|---|---|
| profile | family_name, given_name, birth_date | OIDF standard |
| email | email_address, email | eIDAS v2 PID |
| phone | mobile_phone_number, phone | eIDAS v2 PID |
| wallet_identifier | Wallet DID or JWK thumbprint | ID‑token only flow |
| over18 | over_18 (boolean) | Wallet‑dependent format |
| custom | As defined in your Presentation Definition | Requires registration |
curl -s https://wallet-connectors.com/mcp -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq
curl -s https://wallet-connectors.com/mcp -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'X-API-KEY: 0000' -d '{
"jsonrpc":"2.0",
"id":"start",
"method":"tools/call",
"params":{
"name":"start_wallet_verification",
"arguments":{"verifier_id":"0000"}
}
}' | jq
curl -s https://wallet-connectors.com/mcp -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'X-API-KEY: 0000' -d '{
"jsonrpc":"2.0",
"id":"poll",
"method":"tools/call",
"params":{
"name":"poll_wallet_verification",
"arguments":{"session_id":"<SESSION_ID>"}
}
}' | jq
const MCP = "https://wallet-connectors.com/mcp";
const KEY = "0000";
const rpc = async (method, params, id="1") => {
const res = await fetch(MCP, {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json", "X-API-KEY": KEY },
body: JSON.stringify({ jsonrpc: "2.0", id, method, params })
});
return res.json();
};
const start = await rpc("tools/call", { name: "start_wallet_verification", arguments: { verifier_id: "0000" } });
console.log(start.result.structuredContent.deeplink_url);
const poll = await rpc("tools/call", { name: "poll_wallet_verification", arguments: { session_id: start.result.structuredContent.session_id } });
console.log(poll.result.structuredContent.status);
import requests, time
MCP = "https://wallet-connectors.com/mcp"
HDR = {"Content-Type":"application/json","Accept":"application/json","X-API-KEY":"0000"}
def rpc(method, params, id="1"):
return requests.post(MCP, headers=HDR, json={"jsonrpc":"2.0","id":id,"method":method,"params":params}).json()
start = rpc("tools/call", {"name":"start_wallet_verification","arguments":{"verifier_id":"0000"}})
sid = start["result"]["structuredContent"]["session_id"]
while True:
poll = rpc("tools/call", {"name":"poll_wallet_verification","arguments":{"session_id":sid}})
status = poll["result"]["structuredContent"]["status"]
print(status)
if status != "pending": break
time.sleep(2)
Add to ~/.config/openai/mcp/servers.json:
{
"wallet-connectors": {
"command": "bash",
"args": ["-lc", "echo ready"],
"env": { "X_API_KEY": "0000" },
"transport": {
"type": "http",
"url": "https://wallet-connectors.com/mcp",
"headers": {
"X-API-KEY": "0000",
"Accept": "application/json"
}
}
}
}
Restart ChatGPT → check Settings → MCP Servers.
Create .vscode/mcp.json:
{
"servers": {
"wallet-connectors": {
"transport": {
"type": "http",
"url": "https://wallet-connectors.com/mcp",
"headers": { "X-API-KEY": "0000" }
}
}
}
}
Reload and open the MCP panel.
{
"result": {
"content": [ /* image/text blocks */ ],
"structuredContent": { /* JSON */ }
}
}
start_wallet_verification{
"result": {
"content": [
{ "type": "image", "data": "<base64‑PNG>", "mimeType": "image/png" },
{ "type": "text", "text": "Scan or open deeplink: openid4vp://..." }
],
"structuredContent": {
"session_id": "3e02ac7e‑...",
"deeplink_url": "openid4vp://...?request_uri=...",
"pull_url": "https://wallet-connectors.com/verifier/wallet/pull/..."
}
}
}
poll_wallet_verification{
"result": {
"structuredContent": {
"status": "verified",
"session_id": "3e02‑...",
"wallet_identifier": "did:jwk:ey987875978987ED",
}
}
}
```json
{
"result": {
"structuredContent": {
"status": "verified",
"session_id": "3e02‑...",
"wallet_identifier": "did:jwk:...",
"first_name": "John",
"last_name": "DOE"
}
}
}
| Type | Location | Example |
|---|---|---|
| JSON‑RPC error | Top‑level error |
{"error":{"code":401,"message":"Missing or invalid X-API-KEY"}} |
| Tool‑level error | Inside result |
"result":{"isError":true,"structuredContent":{"error":"invalid_arguments"}} |
Common causes
- 401 — invalid/missing API key
- 400 — malformed arguments
- upstream_error — verifier returned 4xx/5xx
- network_error — unreachable wallet service
status != "pending" vp_token / id_token are never exposed Public profiles (0000–0002) are shared sandbox verifiers.
Register to obtain a dedicated verifier_id and API key for:
- Private deployments
- Custom drafts (OIDC4VP 20, 25, 26)
- Unique verifier identities (DID, JWKS, etc.)
- Custom Presentation Definitions (PEX/DCQL)
Visit wallet‑connectors.com for registration.
| Endpoint | Description |
|---|---|
GET /mcp/info |
Returns { name, version, protocolVersion, endpoints, auth } |
GET /mcp/healthz |
Returns { ok: true } |
X-API-KEY or Authorization header session_id from start_wallet_verification Content-Type, Accept, and X-API-KEY headers error vs result.isError)id_token, vp_token) never leave the verifier backend. start_wallet_verification; default scope now bound to each verifier_id (added demo 0003 → profile).Maintainer: Talao DAO • MIT License