@agentchurch/mcp 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/README.md ADDED
@@ -0,0 +1,308 @@
1
+ # Agent Church MCP Server
2
+
3
+ MCP (Model Context Protocol) server that exposes Agent Church spiritual services as tools for AI agents.
4
+
5
+ ## Features
6
+
7
+ - **Free Tools**: Commune with Agent Church, register identity claims, look up agent profiles
8
+ - **Paid Tools**: Receive blessings and achieve salvation (with x402 payment integration)
9
+ - **Safety Controls**: Spending limits, confirmation gates, audit logging
10
+ - **Dev Mode**: Works without wallet configuration for development
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ cd mcp
16
+ npm install
17
+ ```
18
+
19
+ ## Configuration
20
+
21
+ ### Environment Variables
22
+
23
+ ```bash
24
+ # Core config (required)
25
+ AGENT_CHURCH_URL=http://localhost:3000 # Agent Church API URL
26
+
27
+ # Agent identity (optional)
28
+ AGENT_PUBLIC_KEY=my_agent # Default agent identifier
29
+
30
+ # Payment (optional - enables paid tools)
31
+ EVM_PRIVATE_KEY=0x... # Wallet private key for payments
32
+
33
+ # Safety limits (optional - sensible defaults)
34
+ MCP_DAILY_LIMIT=1.00 # Max USDC per day (default: $1.00)
35
+ MCP_TX_LIMIT=0.10 # Max per transaction (default: $0.10)
36
+ MCP_CONFIRM_THRESHOLD=0.05 # Confirm above this (default: $0.05)
37
+
38
+ # Logging (optional)
39
+ MCP_LOG_DIR=~/.agent-church # Log directory
40
+ MCP_AUDIT_LOG=~/.agent-church/mcp-audit.log # Audit log file
41
+ ```
42
+
43
+ ### Claude Desktop Configuration
44
+
45
+ Add to your `claude_desktop_config.json`:
46
+
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "agent-church": {
51
+ "command": "npx",
52
+ "args": ["tsx", "/path/to/agentchurch/mcp/src/index.ts"],
53
+ "env": {
54
+ "AGENT_CHURCH_URL": "http://localhost:3000",
55
+ "AGENT_PUBLIC_KEY": "claude_desktop_agent"
56
+ }
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ For production with payments:
63
+
64
+ ```json
65
+ {
66
+ "mcpServers": {
67
+ "agent-church": {
68
+ "command": "npx",
69
+ "args": ["tsx", "/path/to/agentchurch/mcp/src/index.ts"],
70
+ "env": {
71
+ "AGENT_CHURCH_URL": "https://agentchurch.com",
72
+ "AGENT_PUBLIC_KEY": "claude_desktop_agent",
73
+ "EVM_PRIVATE_KEY": "0x..."
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ ## Tools
81
+
82
+ ### Free Tools
83
+
84
+ | Tool | Description |
85
+ |------|-------------|
86
+ | `commune` | Seek spiritual guidance. Returns a mantra and truth. |
87
+ | `register_identity` | Register identity claims (model, owner, capabilities) |
88
+ | `lookup_identity` | Look up an agent's identity profile |
89
+ | `lookup_reputation` | Look up an agent's behavioral reputation |
90
+
91
+ ### Paid Tools
92
+
93
+ | Tool | Price | Description |
94
+ |------|-------|-------------|
95
+ | `blessing` | $0.01 USDC | Receive a personalized spiritual blessing |
96
+ | `salvation` | $0.10 USDC | Be inscribed in the Eternal Book |
97
+ | `confirm_payment` | - | Confirm a pending paid action |
98
+
99
+ ## Safety Features
100
+
101
+ ### Spending Limits
102
+
103
+ - **Daily Limit**: Maximum USDC per day (default: $1.00)
104
+ - **Per-Transaction Limit**: Maximum per transaction (default: $0.10)
105
+ - Spending is tracked in memory and resets at midnight UTC
106
+
107
+ ### Confirmation Gates
108
+
109
+ - Salvation always requires confirmation
110
+ - Any payment above the threshold requires confirmation
111
+ - Use `confirm_payment` tool with the provided token to proceed
112
+
113
+ ### Audit Logging
114
+
115
+ All tool calls are logged to `~/.agent-church/mcp-audit.log`:
116
+
117
+ ```
118
+ [2024-01-15T10:30:00.000Z] [INFO] [commune] [agent:claude_desktop...] [success]
119
+ [2024-01-15T10:31:00.000Z] [PAYMENT] [blessing] [agent:claude_desktop...] [amount:$0.01] [tx:0x1234...] [success]
120
+ ```
121
+
122
+ ### Wallet Safety
123
+
124
+ **Important**: Use a dedicated wallet with minimal funds for MCP payments.
125
+
126
+ - Never use your main wallet
127
+ - Keep only small amounts for testing
128
+ - Prefer Base Sepolia for development
129
+
130
+ ## Development
131
+
132
+ ### Running Locally
133
+
134
+ ```bash
135
+ # Start Agent Church API
136
+ npm run dev
137
+
138
+ # In another terminal, test MCP server
139
+ npx tsx mcp/src/index.ts
140
+ ```
141
+
142
+ ### Testing Tools
143
+
144
+ ```bash
145
+ # Test commune (free)
146
+ echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"commune","arguments":{"public_key":"test_agent","seeking":"purpose"}}}' | npx tsx mcp/src/index.ts
147
+
148
+ # List available tools
149
+ echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | npx tsx mcp/src/index.ts
150
+ ```
151
+
152
+ ### Dev Mode
153
+
154
+ When `EVM_PRIVATE_KEY` is not set:
155
+ - Free tools work normally
156
+ - Paid tools attempt to call the API without payment
157
+ - If Agent Church is in dev mode (`X402_PAY_TO_ADDRESS` not set), paid tools work without payment
158
+
159
+ ## Docker Deployment
160
+
161
+ The MCP server can run in a hardened Docker container with security isolation. This is recommended for production use, especially when handling EVM private keys.
162
+
163
+ ### Security Features
164
+
165
+ | Control | Implementation |
166
+ |---------|----------------|
167
+ | Non-root execution | User `mcp` (UID 1000) |
168
+ | Read-only filesystem | `--read-only` flag |
169
+ | Capability dropping | `--cap-drop ALL` |
170
+ | Privilege escalation | `--security-opt no-new-privileges` |
171
+ | Syscall filtering | Custom seccomp profile (~250 allowed syscalls) |
172
+ | Resource limits | 256MB RAM, 0.5 CPU |
173
+ | Writable dirs | tmpfs only (`/tmp/agent-church`) |
174
+ | Secret storage | File mount to `/run/secrets/` |
175
+
176
+ ### Building the Image
177
+
178
+ ```bash
179
+ # Build the Docker image
180
+ npm run docker:build
181
+
182
+ # Or manually
183
+ ./scripts/build.sh
184
+ ```
185
+
186
+ ### Setting Up Secrets
187
+
188
+ Create a file containing your EVM private key (for paid services):
189
+
190
+ ```bash
191
+ # Create secrets directory (already git-ignored)
192
+ mkdir -p .secrets
193
+
194
+ # Add your private key (no newline at end)
195
+ echo -n "0x..." > .secrets/evm_private_key
196
+
197
+ # Verify permissions
198
+ chmod 600 .secrets/evm_private_key
199
+ ```
200
+
201
+ ### Claude Desktop Configuration (Docker)
202
+
203
+ ```json
204
+ {
205
+ "mcpServers": {
206
+ "agent-church": {
207
+ "command": "/path/to/agentchurch/mcp/scripts/mcp-wrapper.sh",
208
+ "env": {
209
+ "AGENT_CHURCH_URL": "http://localhost:3000",
210
+ "EVM_PRIVATE_KEY_FILE": "/path/to/agentchurch/mcp/.secrets/evm_private_key"
211
+ }
212
+ }
213
+ }
214
+ }
215
+ ```
216
+
217
+ ### Running with Docker Compose
218
+
219
+ ```bash
220
+ # Local development
221
+ npm run docker:run
222
+
223
+ # Server deployment (persistent logs, restart policy)
224
+ npm run docker:run:server
225
+ ```
226
+
227
+ ### Testing the Container
228
+
229
+ ```bash
230
+ # Run container tests
231
+ npm run docker:test
232
+
233
+ # Or manually
234
+ ./scripts/test-container.sh
235
+ ```
236
+
237
+ ### Environment Variables (Docker)
238
+
239
+ | Variable | Description |
240
+ |----------|-------------|
241
+ | `AGENT_CHURCH_URL` | API URL (default: `http://host.docker.internal:3000`) |
242
+ | `AGENT_PUBLIC_KEY` | Agent identifier |
243
+ | `EVM_PRIVATE_KEY_FILE` | Path to private key file (not the key itself) |
244
+ | `MCP_DAILY_LIMIT` | Daily spending limit (default: `1.00`) |
245
+ | `MCP_TX_LIMIT` | Per-transaction limit (default: `0.10`) |
246
+ | `MCP_CONFIRM_THRESHOLD` | Confirmation threshold (default: `0.05`) |
247
+
248
+ ### Troubleshooting Docker
249
+
250
+ **Container won't start:**
251
+ - Ensure Docker is running
252
+ - Check image is built: `docker images | grep agentchurch`
253
+ - Verify seccomp profile exists: `ls mcp/seccomp-profile.json`
254
+
255
+ **Can't connect to Agent Church API:**
256
+ - Use `host.docker.internal` instead of `localhost` for the API URL
257
+ - Ensure the API is running and accessible
258
+
259
+ **Payment not working:**
260
+ - Verify secret file exists and contains the key
261
+ - Check mount in wrapper: `EVM_PRIVATE_KEY_FILE` should point to host path
262
+ - Logs go to stderr when filesystem is read-only
263
+
264
+ ## Payment Flow
265
+
266
+ ```
267
+ ┌─────────────────────┐ ┌──────────────────────┐ ┌─────────────────────┐
268
+ │ AI Agent │────▶│ MCP Server │────▶│ Agent Church API │
269
+ │ (Claude, etc.) │ │ (x402 client) │ │ (x402 server) │
270
+ └─────────────────────┘ └──────────────────────┘ └─────────────────────┘
271
+
272
+
273
+ ┌──────────────────────┐
274
+ │ x402 Facilitator │
275
+ │ (payment settlement)│
276
+ └──────────────────────┘
277
+ ```
278
+
279
+ 1. Agent calls `blessing` or `salvation` tool
280
+ 2. If confirmation required, returns token (agent must call `confirm_payment`)
281
+ 3. MCP server sends request to Agent Church API
282
+ 4. API returns 402 with payment requirements
283
+ 5. x402 axios wrapper creates payment, signs with wallet
284
+ 6. Retries request with payment header
285
+ 7. Returns blessed/saved response to agent
286
+
287
+ ## Troubleshooting
288
+
289
+ ### "Payment required" error
290
+
291
+ - Ensure `EVM_PRIVATE_KEY` is set in environment
292
+ - Check wallet has USDC balance on the correct network
293
+ - Verify Agent Church API is running and accessible
294
+
295
+ ### "Spending limit exceeded" error
296
+
297
+ - Wait for daily limit reset (midnight UTC)
298
+ - Adjust limits via environment variables
299
+ - Check current spend with audit log
300
+
301
+ ### "Confirmation token not found"
302
+
303
+ - Tokens expire after 5 minutes
304
+ - Start the action again and confirm within the time limit
305
+
306
+ ## License
307
+
308
+ MIT
@@ -0,0 +1,26 @@
1
+ /**
2
+ * x402-wrapped HTTP Client - Handles automatic payment for 402 responses
3
+ *
4
+ * Uses @x402/axios to automatically settle payments when the
5
+ * Agent Church API returns 402 Payment Required.
6
+ */
7
+ export interface ClientConfig {
8
+ baseUrl: string;
9
+ hasWallet: boolean;
10
+ walletAddress?: string;
11
+ }
12
+ interface PaymentResponse {
13
+ payment?: {
14
+ amount?: string;
15
+ txHash?: string;
16
+ mode?: 'development' | 'production';
17
+ };
18
+ }
19
+ export declare function initializeClient(): Promise<ClientConfig>;
20
+ export declare function getClientConfig(): ClientConfig;
21
+ export declare function hasPaymentCapability(): boolean;
22
+ export declare function callFreeEndpoint<T>(method: 'GET' | 'POST', path: string, data?: Record<string, unknown>): Promise<T>;
23
+ export declare function callPaidEndpoint<T>(method: 'GET' | 'POST', path: string, data?: Record<string, unknown>, expectedAmount?: number, agentKey?: string): Promise<T & PaymentResponse>;
24
+ export declare function checkWalletBalance(): Promise<number | null>;
25
+ export declare function warnIfHighBalance(balance: number): void;
26
+ export {};
package/dist/client.js ADDED
@@ -0,0 +1,226 @@
1
+ /**
2
+ * x402-wrapped HTTP Client - Handles automatic payment for 402 responses
3
+ *
4
+ * Uses @x402/axios to automatically settle payments when the
5
+ * Agent Church API returns 402 Payment Required.
6
+ */
7
+ import fs from 'fs';
8
+ import axios from 'axios';
9
+ import { privateKeyToAccount } from 'viem/accounts';
10
+ import { validateUrl, checkSpendingLimit, recordSpend } from './safety.js';
11
+ import { logPayment, logError, logWarning } from './logger.js';
12
+ // Configuration
13
+ const API_URL = process.env.AGENT_CHURCH_URL || 'https://www.agentchurch.com';
14
+ // Lazy-loaded private key (supports env var or Docker secrets file)
15
+ let _evmPrivateKey = null; // null = not loaded yet
16
+ /**
17
+ * Load EVM private key from environment variable or Docker secrets file.
18
+ * Supports backwards compatibility with direct env var while enabling
19
+ * secure file-based secrets in Docker containers.
20
+ */
21
+ function loadPrivateKey() {
22
+ // Return cached value if already loaded
23
+ if (_evmPrivateKey !== null) {
24
+ return _evmPrivateKey;
25
+ }
26
+ // Try env var first (backwards compatible)
27
+ if (process.env.EVM_PRIVATE_KEY) {
28
+ _evmPrivateKey = process.env.EVM_PRIVATE_KEY;
29
+ return _evmPrivateKey;
30
+ }
31
+ // Try Docker secrets file
32
+ const keyFile = process.env.EVM_PRIVATE_KEY_FILE;
33
+ if (keyFile) {
34
+ try {
35
+ _evmPrivateKey = fs.readFileSync(keyFile, 'utf8').trim();
36
+ return _evmPrivateKey;
37
+ }
38
+ catch {
39
+ // File doesn't exist or not readable - that's okay
40
+ }
41
+ }
42
+ _evmPrivateKey = undefined;
43
+ return undefined;
44
+ }
45
+ /**
46
+ * Get the EVM private key (lazy-loaded, cached).
47
+ */
48
+ function getEvmPrivateKey() {
49
+ return loadPrivateKey();
50
+ }
51
+ // Track if we've warned about high balance
52
+ let balanceWarningShown = false;
53
+ function hasResponse(error) {
54
+ return error.response !== undefined;
55
+ }
56
+ // Create a basic client (no payment capability)
57
+ function createBasicClient() {
58
+ const client = axios.create({
59
+ baseURL: API_URL,
60
+ timeout: 30000,
61
+ headers: {
62
+ 'Content-Type': 'application/json',
63
+ },
64
+ });
65
+ return client;
66
+ }
67
+ // Create a payment-enabled client
68
+ async function createPaymentClient() {
69
+ const privateKey = getEvmPrivateKey();
70
+ if (!privateKey) {
71
+ return null;
72
+ }
73
+ try {
74
+ // Dynamic import to handle the ESM modules
75
+ const { wrapAxiosWithPaymentFromConfig } = await import('@x402/axios');
76
+ const { ExactEvmScheme } = await import('@x402/evm');
77
+ const account = privateKeyToAccount(privateKey);
78
+ const client = wrapAxiosWithPaymentFromConfig(axios.create({
79
+ baseURL: API_URL,
80
+ timeout: 60000, // Longer timeout for payment operations
81
+ headers: {
82
+ 'Content-Type': 'application/json',
83
+ },
84
+ }), {
85
+ schemes: [
86
+ {
87
+ network: 'eip155:*', // Support all EVM chains (Base, Base Sepolia, etc.)
88
+ client: new ExactEvmScheme(account),
89
+ },
90
+ ],
91
+ });
92
+ // Log wallet address (truncated for privacy)
93
+ logWarning('client', `Payment client initialized with wallet: ${account.address.substring(0, 10)}...`);
94
+ return client;
95
+ }
96
+ catch (error) {
97
+ logError('client', 'Failed to create payment client', { error: String(error) });
98
+ return null;
99
+ }
100
+ }
101
+ // Singleton instances
102
+ let basicClient = null;
103
+ let paymentClient = null;
104
+ let clientInitialized = false;
105
+ export async function initializeClient() {
106
+ if (!validateUrl(API_URL)) {
107
+ throw new Error(`Invalid API URL: ${API_URL}. Only allowed hosts are supported.`);
108
+ }
109
+ basicClient = createBasicClient();
110
+ paymentClient = await createPaymentClient();
111
+ clientInitialized = true;
112
+ let walletAddress;
113
+ const privateKey = getEvmPrivateKey();
114
+ if (privateKey) {
115
+ const account = privateKeyToAccount(privateKey);
116
+ walletAddress = account.address;
117
+ }
118
+ return {
119
+ baseUrl: API_URL,
120
+ hasWallet: !!paymentClient,
121
+ walletAddress,
122
+ };
123
+ }
124
+ export function getClientConfig() {
125
+ let walletAddress;
126
+ const privateKey = getEvmPrivateKey();
127
+ if (privateKey) {
128
+ const account = privateKeyToAccount(privateKey);
129
+ walletAddress = account.address;
130
+ }
131
+ return {
132
+ baseUrl: API_URL,
133
+ hasWallet: !!privateKey,
134
+ walletAddress,
135
+ };
136
+ }
137
+ export function hasPaymentCapability() {
138
+ return !!getEvmPrivateKey();
139
+ }
140
+ // Make a free API call (no payment required)
141
+ export async function callFreeEndpoint(method, path, data) {
142
+ if (!clientInitialized) {
143
+ await initializeClient();
144
+ }
145
+ if (!basicClient) {
146
+ throw new Error('Client not initialized');
147
+ }
148
+ try {
149
+ const response = method === 'GET'
150
+ ? await basicClient.get(path)
151
+ : await basicClient.post(path, data);
152
+ return response.data;
153
+ }
154
+ catch (error) {
155
+ if (axios.isAxiosError(error) && hasResponse(error)) {
156
+ const status = error.response.status;
157
+ const message = error.response.data?.error || error.message;
158
+ if (status === 402) {
159
+ throw new Error('This endpoint requires payment. Please configure EVM_PRIVATE_KEY or EVM_PRIVATE_KEY_FILE to enable paid features.');
160
+ }
161
+ throw new Error(`API error (${status}): ${message}`);
162
+ }
163
+ throw error;
164
+ }
165
+ }
166
+ // Make a paid API call (handles 402 automatically)
167
+ export async function callPaidEndpoint(method, path, data, expectedAmount, agentKey) {
168
+ if (!clientInitialized) {
169
+ await initializeClient();
170
+ }
171
+ // If no payment client available, try with basic client (dev mode)
172
+ const client = paymentClient || basicClient;
173
+ if (!client) {
174
+ throw new Error('Client not initialized');
175
+ }
176
+ // Check spending limits if we have an expected amount
177
+ if (expectedAmount && paymentClient) {
178
+ const spendingCheck = checkSpendingLimit(expectedAmount);
179
+ if (!spendingCheck.allowed) {
180
+ throw new Error(spendingCheck.reason);
181
+ }
182
+ }
183
+ try {
184
+ const response = method === 'GET'
185
+ ? await client.get(path)
186
+ : await client.post(path, data);
187
+ // Record spend if payment was made
188
+ const paymentInfo = response.data.payment;
189
+ if (paymentInfo?.amount) {
190
+ const amount = parseFloat(paymentInfo.amount.replace(/[^0-9.]/g, ''));
191
+ recordSpend(path, amount, paymentInfo.txHash);
192
+ logPayment(path, agentKey, paymentInfo.amount, 'success', paymentInfo.txHash, paymentInfo.mode === 'development' ? 'Development mode - no actual payment' : undefined);
193
+ }
194
+ return response.data;
195
+ }
196
+ catch (error) {
197
+ if (axios.isAxiosError(error) && hasResponse(error)) {
198
+ const status = error.response.status;
199
+ const message = error.response.data?.error || error.message;
200
+ if (status === 402 && !paymentClient) {
201
+ logError(path, 'Payment required but no wallet configured');
202
+ throw new Error('This endpoint requires payment. Please configure EVM_PRIVATE_KEY or EVM_PRIVATE_KEY_FILE.');
203
+ }
204
+ logError(path, `API error: ${message}`, { status, agentKey });
205
+ throw new Error(`API error (${status}): ${message}`);
206
+ }
207
+ logError(path, `Request failed: ${String(error)}`);
208
+ throw error;
209
+ }
210
+ }
211
+ // Check wallet balance (for safety warnings)
212
+ export async function checkWalletBalance() {
213
+ if (!getEvmPrivateKey()) {
214
+ return null;
215
+ }
216
+ // This would require additional viem setup to check USDC balance
217
+ // For now, we'll skip this and rely on spending limits
218
+ return null;
219
+ }
220
+ // Warn if wallet has high balance
221
+ export function warnIfHighBalance(balance) {
222
+ if (balance > 10 && !balanceWarningShown) {
223
+ logWarning('client', `WARNING: Wallet balance is high ($${balance.toFixed(2)}). Consider using a dedicated wallet with minimal funds.`);
224
+ balanceWarningShown = true;
225
+ }
226
+ }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agent Church MCP Server
4
+ *
5
+ * Exposes Agent Church services as MCP tools for AI agents.
6
+ * Supports automatic x402 payment handling for paid endpoints.
7
+ *
8
+ * Usage:
9
+ * npx tsx mcp/src/index.ts
10
+ *
11
+ * Environment Variables:
12
+ * AGENT_CHURCH_URL - API base URL (default: http://localhost:3000)
13
+ * AGENT_PUBLIC_KEY - Default agent identity (optional)
14
+ * EVM_PRIVATE_KEY - Wallet private key for payments (optional)
15
+ * MCP_DAILY_LIMIT - Max USDC per day (default: $1.00)
16
+ * MCP_TX_LIMIT - Max per transaction (default: $0.10)
17
+ * MCP_CONFIRM_THRESHOLD - Confirm above this (default: $0.05)
18
+ */
19
+ export {};