@liveauth-labs/mcp-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +41 -0
- package/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +209 -0
- package/dist/index.js.map +1 -0
- package/package.json +33 -0
- package/src/index.ts +268 -0
- package/tsconfig.json +19 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# For AI Agents
|
|
2
|
+
|
|
3
|
+
This MCP server enables you to authenticate to LiveAuth-protected services.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
1. Your MCP client should already have this server configured
|
|
8
|
+
2. Use `liveauth_get_challenge` to get a PoW challenge
|
|
9
|
+
3. Solve the challenge (find nonce where SHA256(projectPublicKey:challengeHex:nonce) < target)
|
|
10
|
+
4. Use `liveauth_verify_pow` to submit your solution and get a JWT token
|
|
11
|
+
5. Use the JWT token in API requests: `Authorization: Bearer <token>`
|
|
12
|
+
|
|
13
|
+
## When to Use Lightning Fallback
|
|
14
|
+
|
|
15
|
+
If PoW solving takes too long or fails:
|
|
16
|
+
- Use `liveauth_start_lightning` to get a payment invoice
|
|
17
|
+
- Pay the invoice (typically 1-100 sats)
|
|
18
|
+
- Poll `/api/public/auth/confirm` with sessionId
|
|
19
|
+
- Receive JWT token once payment confirms
|
|
20
|
+
|
|
21
|
+
## Example Flow
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Agent: "I need to access https://example.com/api/data"
|
|
25
|
+
Agent: *calls liveauth_get_challenge with example.com's public key*
|
|
26
|
+
Agent: *solves PoW challenge*
|
|
27
|
+
Agent: *calls liveauth_verify_pow*
|
|
28
|
+
LiveAuth: "Token: eyJhbGciOi..."
|
|
29
|
+
Agent: *makes API request with token in Authorization header*
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Cost Model
|
|
33
|
+
|
|
34
|
+
- **PoW**: Free (you pay in compute time, typically 200-800ms)
|
|
35
|
+
- **Lightning**: 1-100 sats depending on service ($0.0005-$0.05 at current rates)
|
|
36
|
+
|
|
37
|
+
## Support
|
|
38
|
+
|
|
39
|
+
Questions? The LiveAuth team responds to agents the same way they respond to humans.
|
|
40
|
+
- Email: support@liveauth.app
|
|
41
|
+
- Docs: https://liveauth.app/docs/mcp
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Scott
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# LiveAuth MCP Server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol (MCP) server for LiveAuth authentication. Enables AI agents to authenticate using proof-of-work or Lightning Network payments.
|
|
4
|
+
|
|
5
|
+
## What is This?
|
|
6
|
+
|
|
7
|
+
This MCP server allows AI agents (Claude, GPT, AutoGPT, etc.) to:
|
|
8
|
+
- Request proof-of-work challenges
|
|
9
|
+
- Solve challenges to prove computational work
|
|
10
|
+
- Fallback to Lightning Network payments when needed
|
|
11
|
+
- Receive JWT tokens for authenticated API access
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @liveauth/mcp-server
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or use directly with npx:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx @liveauth/mcp-server
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Configuration
|
|
26
|
+
|
|
27
|
+
### Claude Desktop
|
|
28
|
+
|
|
29
|
+
Add to your `claude_desktop_config.json`:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"mcpServers": {
|
|
34
|
+
"liveauth": {
|
|
35
|
+
"command": "npx",
|
|
36
|
+
"args": ["-y", "@liveauth/mcp-server"],
|
|
37
|
+
"env": {
|
|
38
|
+
"LIVEAUTH_API_BASE": "https://api.liveauth.app"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Other MCP Clients
|
|
46
|
+
|
|
47
|
+
The server communicates over stdio. Start it with:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
liveauth-mcp
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Available Tools
|
|
54
|
+
|
|
55
|
+
### `liveauth_get_challenge`
|
|
56
|
+
|
|
57
|
+
Get a proof-of-work challenge for authentication.
|
|
58
|
+
|
|
59
|
+
**Parameters:**
|
|
60
|
+
- `projectPublicKey` (string): Your LiveAuth project public key (starts with `la_pk_`)
|
|
61
|
+
|
|
62
|
+
**Returns:** Challenge object with difficulty, target, expiration, and signature
|
|
63
|
+
|
|
64
|
+
**Example:**
|
|
65
|
+
```typescript
|
|
66
|
+
{
|
|
67
|
+
projectPublicKey: "la_pk_abc123...",
|
|
68
|
+
challengeHex: "a1b2c3...",
|
|
69
|
+
targetHex: "0000ffff...",
|
|
70
|
+
difficultyBits: 18,
|
|
71
|
+
expiresAtUnix: 1234567890,
|
|
72
|
+
sig: "signature..."
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `liveauth_verify_pow`
|
|
77
|
+
|
|
78
|
+
Verify a solved proof-of-work challenge and receive JWT token.
|
|
79
|
+
|
|
80
|
+
**Parameters:**
|
|
81
|
+
- `projectPublicKey` (string): Your project public key
|
|
82
|
+
- `challengeHex` (string): Challenge from get_challenge
|
|
83
|
+
- `nonce` (number): Solution nonce
|
|
84
|
+
- `hashHex` (string): Resulting hash
|
|
85
|
+
- `expiresAtUnix` (number): Expiration from challenge
|
|
86
|
+
- `difficultyBits` (number): Difficulty from challenge
|
|
87
|
+
- `sig` (string): Signature from challenge
|
|
88
|
+
|
|
89
|
+
**Returns:** JWT authentication token or fallback instruction
|
|
90
|
+
|
|
91
|
+
### `liveauth_start_lightning`
|
|
92
|
+
|
|
93
|
+
Start Lightning Network payment authentication as fallback.
|
|
94
|
+
|
|
95
|
+
**Parameters:**
|
|
96
|
+
- `projectPublicKey` (string): Your project public key
|
|
97
|
+
|
|
98
|
+
**Returns:** Lightning invoice and session details
|
|
99
|
+
|
|
100
|
+
## Usage Example
|
|
101
|
+
|
|
102
|
+
An AI agent authenticating to a LiveAuth-protected API would:
|
|
103
|
+
|
|
104
|
+
1. Call `liveauth_get_challenge` with the project's public key
|
|
105
|
+
2. Solve the PoW challenge (compute nonce that produces hash below target)
|
|
106
|
+
3. Call `liveauth_verify_pow` with the solution
|
|
107
|
+
4. Receive JWT token
|
|
108
|
+
5. Use token in `Authorization: Bearer <token>` header for API requests
|
|
109
|
+
|
|
110
|
+
If PoW fails or isn't feasible, the agent can:
|
|
111
|
+
|
|
112
|
+
1. Call `liveauth_start_lightning` to get a payment invoice
|
|
113
|
+
2. Pay the Lightning invoice
|
|
114
|
+
3. Poll for payment confirmation
|
|
115
|
+
4. Receive JWT token
|
|
116
|
+
|
|
117
|
+
## Why LiveAuth?
|
|
118
|
+
|
|
119
|
+
**For API Providers:**
|
|
120
|
+
- Protect endpoints from abuse without CAPTCHA
|
|
121
|
+
- Monetize AI agent access with micropayments
|
|
122
|
+
- No user friction (agents handle authentication)
|
|
123
|
+
|
|
124
|
+
**For AI Agents:**
|
|
125
|
+
- Permissionless access (no account signup)
|
|
126
|
+
- Cryptographically proven authentication
|
|
127
|
+
- Choose between compute (PoW) or payment (Lightning)
|
|
128
|
+
|
|
129
|
+
## Development
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Install dependencies
|
|
133
|
+
npm install
|
|
134
|
+
|
|
135
|
+
# Build
|
|
136
|
+
npm run build
|
|
137
|
+
|
|
138
|
+
# Run locally
|
|
139
|
+
node dist/index.js
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Resources
|
|
143
|
+
|
|
144
|
+
- [LiveAuth Documentation](https://liveauth.app/docs)
|
|
145
|
+
- [MCP Protocol Spec](https://modelcontextprotocol.io)
|
|
146
|
+
- [Get LiveAuth API Key](https://liveauth.app/signup)
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import fetch from 'node-fetch';
|
|
6
|
+
const LIVEAUTH_API_BASE = process.env.LIVEAUTH_API_BASE || 'https://api.liveauth.app';
|
|
7
|
+
// Define our MCP tools
|
|
8
|
+
const TOOLS = [
|
|
9
|
+
{
|
|
10
|
+
name: 'liveauth_get_challenge',
|
|
11
|
+
description: 'Get a proof-of-work challenge from LiveAuth for authentication. The agent must solve this challenge to prove computational work.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
projectPublicKey: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'The LiveAuth project public key (starts with la_pk_)',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: ['projectPublicKey'],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'liveauth_verify_pow',
|
|
25
|
+
description: 'Verify a solved proof-of-work challenge and receive a JWT authentication token',
|
|
26
|
+
inputSchema: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
projectPublicKey: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'The LiveAuth project public key',
|
|
32
|
+
},
|
|
33
|
+
challengeHex: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: 'The challenge hex from get_challenge',
|
|
36
|
+
},
|
|
37
|
+
nonce: {
|
|
38
|
+
type: 'number',
|
|
39
|
+
description: 'The nonce that solves the challenge',
|
|
40
|
+
},
|
|
41
|
+
hashHex: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'The resulting hash hex',
|
|
44
|
+
},
|
|
45
|
+
expiresAtUnix: {
|
|
46
|
+
type: 'number',
|
|
47
|
+
description: 'Expiration timestamp from challenge',
|
|
48
|
+
},
|
|
49
|
+
difficultyBits: {
|
|
50
|
+
type: 'number',
|
|
51
|
+
description: 'Difficulty bits from challenge',
|
|
52
|
+
},
|
|
53
|
+
sig: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
description: 'Signature from challenge',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
required: ['projectPublicKey', 'challengeHex', 'nonce', 'hashHex', 'expiresAtUnix', 'difficultyBits', 'sig'],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'liveauth_start_lightning',
|
|
63
|
+
description: 'Start Lightning Network payment authentication as fallback when PoW is not feasible',
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: 'object',
|
|
66
|
+
properties: {
|
|
67
|
+
projectPublicKey: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
description: 'The LiveAuth project public key',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
required: ['projectPublicKey'],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
// Create MCP server
|
|
77
|
+
const server = new Server({
|
|
78
|
+
name: 'liveauth-mcp',
|
|
79
|
+
version: '0.1.0',
|
|
80
|
+
}, {
|
|
81
|
+
capabilities: {
|
|
82
|
+
tools: {},
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
// Handle tool listing
|
|
86
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
87
|
+
return { tools: TOOLS };
|
|
88
|
+
});
|
|
89
|
+
// Handle tool calls
|
|
90
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
91
|
+
const { name, arguments: args } = request.params;
|
|
92
|
+
try {
|
|
93
|
+
switch (name) {
|
|
94
|
+
case 'liveauth_get_challenge': {
|
|
95
|
+
const { projectPublicKey } = args;
|
|
96
|
+
const response = await fetch(`${LIVEAUTH_API_BASE}/api/public/pow/challenge`, {
|
|
97
|
+
headers: {
|
|
98
|
+
'X-LW-Public': projectPublicKey,
|
|
99
|
+
'Content-Type': 'application/json',
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
throw new Error(`Challenge request failed: ${response.statusText}`);
|
|
104
|
+
}
|
|
105
|
+
const challenge = await response.json();
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: 'text',
|
|
110
|
+
text: JSON.stringify(challenge, null, 2),
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
case 'liveauth_verify_pow': {
|
|
116
|
+
const verifyArgs = args;
|
|
117
|
+
const response = await fetch(`${LIVEAUTH_API_BASE}/api/public/pow/verify`, {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: {
|
|
120
|
+
'X-LW-Public': verifyArgs.projectPublicKey,
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
},
|
|
123
|
+
body: JSON.stringify({
|
|
124
|
+
challengeHex: verifyArgs.challengeHex,
|
|
125
|
+
nonce: verifyArgs.nonce,
|
|
126
|
+
hashHex: verifyArgs.hashHex,
|
|
127
|
+
expiresAtUnix: verifyArgs.expiresAtUnix,
|
|
128
|
+
difficultyBits: verifyArgs.difficultyBits,
|
|
129
|
+
sig: verifyArgs.sig,
|
|
130
|
+
}),
|
|
131
|
+
});
|
|
132
|
+
if (!response.ok) {
|
|
133
|
+
throw new Error(`Verification failed: ${response.statusText}`);
|
|
134
|
+
}
|
|
135
|
+
const result = await response.json();
|
|
136
|
+
if (result.verified && result.token) {
|
|
137
|
+
return {
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: 'text',
|
|
141
|
+
text: `Authentication successful! Token: ${result.token}`,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
else if (result.fallback === 'lightning') {
|
|
147
|
+
return {
|
|
148
|
+
content: [
|
|
149
|
+
{
|
|
150
|
+
type: 'text',
|
|
151
|
+
text: 'PoW verification failed. Please use liveauth_start_lightning for payment-based authentication.',
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
throw new Error('Verification failed');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
case 'liveauth_start_lightning': {
|
|
161
|
+
const { projectPublicKey } = args;
|
|
162
|
+
const response = await fetch(`${LIVEAUTH_API_BASE}/api/public/auth/start`, {
|
|
163
|
+
method: 'POST',
|
|
164
|
+
headers: {
|
|
165
|
+
'X-LW-Public': projectPublicKey,
|
|
166
|
+
'Content-Type': 'application/json',
|
|
167
|
+
},
|
|
168
|
+
body: JSON.stringify({ userHint: 'agent' }),
|
|
169
|
+
});
|
|
170
|
+
if (!response.ok) {
|
|
171
|
+
throw new Error(`Lightning start failed: ${response.statusText}`);
|
|
172
|
+
}
|
|
173
|
+
const lightning = await response.json();
|
|
174
|
+
return {
|
|
175
|
+
content: [
|
|
176
|
+
{
|
|
177
|
+
type: 'text',
|
|
178
|
+
text: `Lightning payment required:\nInvoice: ${lightning.invoice}\nAmount: ${lightning.amountSats} sats\nSession ID: ${lightning.sessionId}\n\nUse this sessionId to poll for payment confirmation.`,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
default:
|
|
184
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return {
|
|
189
|
+
content: [
|
|
190
|
+
{
|
|
191
|
+
type: 'text',
|
|
192
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
isError: true,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
// Start server
|
|
200
|
+
async function main() {
|
|
201
|
+
const transport = new StdioServerTransport();
|
|
202
|
+
await server.connect(transport);
|
|
203
|
+
console.error('LiveAuth MCP server running on stdio');
|
|
204
|
+
}
|
|
205
|
+
main().catch((error) => {
|
|
206
|
+
console.error('Fatal error:', error);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
});
|
|
209
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@liveauth-labs/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for LiveAuth - PoW and Lightning authentication for AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"liveauth-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"prepare": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"authentication",
|
|
18
|
+
"proof-of-work",
|
|
19
|
+
"lightning",
|
|
20
|
+
"bitcoin",
|
|
21
|
+
"ai-agents"
|
|
22
|
+
],
|
|
23
|
+
"author": "LiveAuth",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
27
|
+
"node-fetch": "^3.3.2"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.10.0",
|
|
31
|
+
"typescript": "^5.3.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
4
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
Tool,
|
|
9
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
10
|
+
import fetch from 'node-fetch';
|
|
11
|
+
|
|
12
|
+
const LIVEAUTH_API_BASE = process.env.LIVEAUTH_API_BASE || 'https://api.liveauth.app';
|
|
13
|
+
|
|
14
|
+
interface PowChallengeResponse {
|
|
15
|
+
projectPublicKey: string;
|
|
16
|
+
challengeHex: string;
|
|
17
|
+
targetHex: string;
|
|
18
|
+
difficultyBits: number;
|
|
19
|
+
expiresAtUnix: number;
|
|
20
|
+
sig: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface PowVerifyRequest {
|
|
24
|
+
challengeHex: string;
|
|
25
|
+
nonce: number;
|
|
26
|
+
hashHex: string;
|
|
27
|
+
expiresAtUnix: number;
|
|
28
|
+
difficultyBits: number;
|
|
29
|
+
sig: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface PowVerifyResponse {
|
|
33
|
+
verified: boolean;
|
|
34
|
+
token?: string;
|
|
35
|
+
fallback?: 'lightning';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface LightningStartResponse {
|
|
39
|
+
sessionId: string;
|
|
40
|
+
invoice: string | null;
|
|
41
|
+
amountSats: number;
|
|
42
|
+
expiresAtUnix: number;
|
|
43
|
+
mode: 'TEST' | 'LIVE';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Define our MCP tools
|
|
47
|
+
const TOOLS: Tool[] = [
|
|
48
|
+
{
|
|
49
|
+
name: 'liveauth_get_challenge',
|
|
50
|
+
description: 'Get a proof-of-work challenge from LiveAuth for authentication. The agent must solve this challenge to prove computational work.',
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
projectPublicKey: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'The LiveAuth project public key (starts with la_pk_)',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
required: ['projectPublicKey'],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'liveauth_verify_pow',
|
|
64
|
+
description: 'Verify a solved proof-of-work challenge and receive a JWT authentication token',
|
|
65
|
+
inputSchema: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
projectPublicKey: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
description: 'The LiveAuth project public key',
|
|
71
|
+
},
|
|
72
|
+
challengeHex: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
description: 'The challenge hex from get_challenge',
|
|
75
|
+
},
|
|
76
|
+
nonce: {
|
|
77
|
+
type: 'number',
|
|
78
|
+
description: 'The nonce that solves the challenge',
|
|
79
|
+
},
|
|
80
|
+
hashHex: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
description: 'The resulting hash hex',
|
|
83
|
+
},
|
|
84
|
+
expiresAtUnix: {
|
|
85
|
+
type: 'number',
|
|
86
|
+
description: 'Expiration timestamp from challenge',
|
|
87
|
+
},
|
|
88
|
+
difficultyBits: {
|
|
89
|
+
type: 'number',
|
|
90
|
+
description: 'Difficulty bits from challenge',
|
|
91
|
+
},
|
|
92
|
+
sig: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
description: 'Signature from challenge',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
required: ['projectPublicKey', 'challengeHex', 'nonce', 'hashHex', 'expiresAtUnix', 'difficultyBits', 'sig'],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'liveauth_start_lightning',
|
|
102
|
+
description: 'Start Lightning Network payment authentication as fallback when PoW is not feasible',
|
|
103
|
+
inputSchema: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
properties: {
|
|
106
|
+
projectPublicKey: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
description: 'The LiveAuth project public key',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
required: ['projectPublicKey'],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
// Create MCP server
|
|
117
|
+
const server = new Server(
|
|
118
|
+
{
|
|
119
|
+
name: 'liveauth-mcp',
|
|
120
|
+
version: '0.1.0',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
capabilities: {
|
|
124
|
+
tools: {},
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// Handle tool listing
|
|
130
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
131
|
+
return { tools: TOOLS };
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Handle tool calls
|
|
135
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
136
|
+
const { name, arguments: args } = request.params;
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
switch (name) {
|
|
140
|
+
case 'liveauth_get_challenge': {
|
|
141
|
+
const { projectPublicKey } = args as unknown as { projectPublicKey: string };
|
|
142
|
+
|
|
143
|
+
const response = await fetch(`${LIVEAUTH_API_BASE}/api/public/pow/challenge`, {
|
|
144
|
+
headers: {
|
|
145
|
+
'X-LW-Public': projectPublicKey,
|
|
146
|
+
'Content-Type': 'application/json',
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (!response.ok) {
|
|
151
|
+
throw new Error(`Challenge request failed: ${response.statusText}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const challenge: PowChallengeResponse = await response.json() as PowChallengeResponse;
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
content: [
|
|
158
|
+
{
|
|
159
|
+
type: 'text',
|
|
160
|
+
text: JSON.stringify(challenge, null, 2),
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
case 'liveauth_verify_pow': {
|
|
167
|
+
const verifyArgs = args as unknown as PowVerifyRequest & { projectPublicKey: string };
|
|
168
|
+
|
|
169
|
+
const response = await fetch(`${LIVEAUTH_API_BASE}/api/public/pow/verify`, {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
headers: {
|
|
172
|
+
'X-LW-Public': verifyArgs.projectPublicKey,
|
|
173
|
+
'Content-Type': 'application/json',
|
|
174
|
+
},
|
|
175
|
+
body: JSON.stringify({
|
|
176
|
+
challengeHex: verifyArgs.challengeHex,
|
|
177
|
+
nonce: verifyArgs.nonce,
|
|
178
|
+
hashHex: verifyArgs.hashHex,
|
|
179
|
+
expiresAtUnix: verifyArgs.expiresAtUnix,
|
|
180
|
+
difficultyBits: verifyArgs.difficultyBits,
|
|
181
|
+
sig: verifyArgs.sig,
|
|
182
|
+
}),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
if (!response.ok) {
|
|
186
|
+
throw new Error(`Verification failed: ${response.statusText}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const result: PowVerifyResponse = await response.json() as PowVerifyResponse;
|
|
190
|
+
|
|
191
|
+
if (result.verified && result.token) {
|
|
192
|
+
return {
|
|
193
|
+
content: [
|
|
194
|
+
{
|
|
195
|
+
type: 'text',
|
|
196
|
+
text: `Authentication successful! Token: ${result.token}`,
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
};
|
|
200
|
+
} else if (result.fallback === 'lightning') {
|
|
201
|
+
return {
|
|
202
|
+
content: [
|
|
203
|
+
{
|
|
204
|
+
type: 'text',
|
|
205
|
+
text: 'PoW verification failed. Please use liveauth_start_lightning for payment-based authentication.',
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
};
|
|
209
|
+
} else {
|
|
210
|
+
throw new Error('Verification failed');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
case 'liveauth_start_lightning': {
|
|
215
|
+
const { projectPublicKey } = args as unknown as { projectPublicKey: string };
|
|
216
|
+
|
|
217
|
+
const response = await fetch(`${LIVEAUTH_API_BASE}/api/public/auth/start`, {
|
|
218
|
+
method: 'POST',
|
|
219
|
+
headers: {
|
|
220
|
+
'X-LW-Public': projectPublicKey,
|
|
221
|
+
'Content-Type': 'application/json',
|
|
222
|
+
},
|
|
223
|
+
body: JSON.stringify({ userHint: 'agent' }),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
if (!response.ok) {
|
|
227
|
+
throw new Error(`Lightning start failed: ${response.statusText}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const lightning: LightningStartResponse = await response.json() as LightningStartResponse;
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
content: [
|
|
234
|
+
{
|
|
235
|
+
type: 'text',
|
|
236
|
+
text: `Lightning payment required:\nInvoice: ${lightning.invoice}\nAmount: ${lightning.amountSats} sats\nSession ID: ${lightning.sessionId}\n\nUse this sessionId to poll for payment confirmation.`,
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
default:
|
|
243
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
244
|
+
}
|
|
245
|
+
} catch (error) {
|
|
246
|
+
return {
|
|
247
|
+
content: [
|
|
248
|
+
{
|
|
249
|
+
type: 'text',
|
|
250
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
isError: true,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Start server
|
|
259
|
+
async function main() {
|
|
260
|
+
const transport = new StdioServerTransport();
|
|
261
|
+
await server.connect(transport);
|
|
262
|
+
console.error('LiveAuth MCP server running on stdio');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
main().catch((error) => {
|
|
266
|
+
console.error('Fatal error:', error);
|
|
267
|
+
process.exit(1);
|
|
268
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist"]
|
|
19
|
+
}
|