@paylobster/mcp-server 1.3.0 → 1.5.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/BUILD_SUMMARY.md +294 -0
- package/CHANGELOG.md +77 -0
- package/COMPLETION_REPORT.md +284 -0
- package/INTEGRATION.md +286 -0
- package/QUICKSTART.md +226 -0
- package/README.md +232 -127
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +16 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/contracts.d.ts +15 -0
- package/dist/lib/contracts.d.ts.map +1 -0
- package/dist/lib/contracts.js +148 -0
- package/dist/lib/contracts.js.map +1 -0
- package/dist/lib/types.d.ts +39 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/{types/index.js → lib/types.js} +1 -1
- package/dist/lib/types.js.map +1 -0
- package/dist/resources/agent.d.ts +3 -0
- package/dist/resources/agent.d.ts.map +1 -0
- package/dist/resources/agent.js +36 -0
- package/dist/resources/agent.js.map +1 -0
- package/dist/resources/escrow.d.ts +3 -0
- package/dist/resources/escrow.d.ts.map +1 -0
- package/dist/resources/escrow.js +33 -0
- package/dist/resources/escrow.js.map +1 -0
- package/dist/resources/services.d.ts +2 -0
- package/dist/resources/services.d.ts.map +1 -0
- package/dist/resources/services.js +55 -0
- package/dist/resources/services.js.map +1 -0
- package/dist/server.d.ts +6 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +114 -834
- package/dist/server.js.map +1 -1
- package/dist/tools/agent.d.ts +17 -0
- package/dist/tools/agent.d.ts.map +1 -0
- package/dist/tools/agent.js +51 -0
- package/dist/tools/agent.js.map +1 -0
- package/dist/tools/balance.d.ts +16 -5
- package/dist/tools/balance.d.ts.map +1 -1
- package/dist/tools/balance.js +44 -26
- package/dist/tools/balance.js.map +1 -1
- package/dist/tools/escrow.d.ts +31 -90
- package/dist/tools/escrow.d.ts.map +1 -1
- package/dist/tools/escrow.js +98 -215
- package/dist/tools/escrow.js.map +1 -1
- package/dist/tools/reputation.d.ts +15 -49
- package/dist/tools/reputation.d.ts.map +1 -1
- package/dist/tools/reputation.js +45 -107
- package/dist/tools/reputation.js.map +1 -1
- package/dist/tools/search.d.ts +30 -54
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +122 -85
- package/dist/tools/search.js.map +1 -1
- package/examples/test-server.ts +36 -0
- package/examples/test-tools.ts +64 -0
- package/package.json +20 -11
- package/src/__tests__/server.test.ts +24 -0
- package/src/index.ts +24 -0
- package/src/lib/config.ts +22 -0
- package/src/lib/contracts.ts +164 -0
- package/src/lib/types.ts +44 -0
- package/src/resources/agent.ts +40 -0
- package/src/resources/escrow.ts +35 -0
- package/src/resources/services.ts +53 -0
- package/src/server.ts +190 -0
- package/src/tools/agent.ts +56 -0
- package/src/tools/balance.ts +61 -0
- package/src/tools/escrow.ts +142 -0
- package/src/tools/reputation.ts +69 -0
- package/src/tools/search.ts +148 -0
- package/tsconfig.json +20 -0
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -86
- package/dist/cli.js.map +0 -1
- package/dist/resources/index.d.ts +0 -9
- package/dist/resources/index.d.ts.map +0 -1
- package/dist/resources/index.js +0 -159
- package/dist/resources/index.js.map +0 -1
- package/dist/tools/bridge.d.ts +0 -142
- package/dist/tools/bridge.d.ts.map +0 -1
- package/dist/tools/bridge.js +0 -121
- package/dist/tools/bridge.js.map +0 -1
- package/dist/tools/cascade.d.ts +0 -59
- package/dist/tools/cascade.d.ts.map +0 -1
- package/dist/tools/cascade.js +0 -119
- package/dist/tools/cascade.js.map +0 -1
- package/dist/tools/compliance.d.ts +0 -36
- package/dist/tools/compliance.d.ts.map +0 -1
- package/dist/tools/compliance.js +0 -57
- package/dist/tools/compliance.js.map +0 -1
- package/dist/tools/credit.d.ts +0 -46
- package/dist/tools/credit.d.ts.map +0 -1
- package/dist/tools/credit.js +0 -102
- package/dist/tools/credit.js.map +0 -1
- package/dist/tools/disputes.d.ts +0 -71
- package/dist/tools/disputes.d.ts.map +0 -1
- package/dist/tools/disputes.js +0 -121
- package/dist/tools/disputes.js.map +0 -1
- package/dist/tools/intent.d.ts +0 -78
- package/dist/tools/intent.d.ts.map +0 -1
- package/dist/tools/intent.js +0 -192
- package/dist/tools/intent.js.map +0 -1
- package/dist/tools/portfolio.d.ts +0 -44
- package/dist/tools/portfolio.d.ts.map +0 -1
- package/dist/tools/portfolio.js +0 -166
- package/dist/tools/portfolio.js.map +0 -1
- package/dist/tools/revenue.d.ts +0 -42
- package/dist/tools/revenue.d.ts.map +0 -1
- package/dist/tools/revenue.js +0 -67
- package/dist/tools/revenue.js.map +0 -1
- package/dist/tools/streaming.d.ts +0 -60
- package/dist/tools/streaming.d.ts.map +0 -1
- package/dist/tools/streaming.js +0 -163
- package/dist/tools/streaming.js.map +0 -1
- package/dist/tools/swap.d.ts +0 -93
- package/dist/tools/swap.d.ts.map +0 -1
- package/dist/tools/swap.js +0 -150
- package/dist/tools/swap.js.map +0 -1
- package/dist/tools/treasury.d.ts +0 -169
- package/dist/tools/treasury.d.ts.map +0 -1
- package/dist/tools/treasury.js +0 -475
- package/dist/tools/treasury.js.map +0 -1
- package/dist/types/index.d.ts +0 -83
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/utils/contracts.d.ts +0 -552
- package/dist/utils/contracts.d.ts.map +0 -1
- package/dist/utils/contracts.js +0 -401
- package/dist/utils/contracts.js.map +0 -1
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { createPublicClient, http, type Address } from 'viem';
|
|
2
|
+
import { base, baseSepolia } from 'viem/chains';
|
|
3
|
+
import type { MCPServerConfig } from './config.js';
|
|
4
|
+
import type { AgentInfo, ReputationData, EscrowData } from './types.js';
|
|
5
|
+
|
|
6
|
+
// Contract addresses from existing web/src/lib/contracts.ts
|
|
7
|
+
const CONTRACTS_MAINNET = {
|
|
8
|
+
IDENTITY: '0xA174f250380c4B5C37F6709bBa719E662b77E662' as Address,
|
|
9
|
+
REPUTATION: '0x02bb4c4cd10EeaCD04DF7631c81d6E7c1d0c4b29' as Address,
|
|
10
|
+
CREDIT: '0xD924B81F2d8EFF3E0A3Bf0a4b9D77d37CC0980E1' as Address,
|
|
11
|
+
ESCROW: '0x49Ed7003C6941a033E7dD8b44552e4E18cf28806' as Address,
|
|
12
|
+
USDC: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as Address,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// NOTE: These contracts are not yet deployed to Base Sepolia
|
|
16
|
+
const CONTRACTS_SEPOLIA = {
|
|
17
|
+
IDENTITY: '0x3dfA02Ed4F0e4F10E8031d7a4cB8Ea0bBbFbCB8c' as Address,
|
|
18
|
+
REPUTATION: '0x0000000000000000000000000000000000000000' as Address, // TODO: Deploy and update
|
|
19
|
+
CREDIT: '0xBA64e2b2F2a80D03A4B13b3396942C1e78205C7d' as Address,
|
|
20
|
+
ESCROW: '0x78D1f50a1965dE34f6b5a3D3546C94FE1809Cd82' as Address,
|
|
21
|
+
USDC: '0x036CbD53842c5426634e7929541eC2318f3dCF7e' as Address,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// ABIs from existing contracts.ts
|
|
25
|
+
const IDENTITY_ABI = [
|
|
26
|
+
{
|
|
27
|
+
inputs: [{ name: 'user', type: 'address' }],
|
|
28
|
+
name: 'getAgentInfo',
|
|
29
|
+
outputs: [
|
|
30
|
+
{ name: 'name', type: 'string' },
|
|
31
|
+
{ name: 'tokenId', type: 'uint256' },
|
|
32
|
+
{ name: 'registered', type: 'bool' },
|
|
33
|
+
],
|
|
34
|
+
stateMutability: 'view',
|
|
35
|
+
type: 'function',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
inputs: [{ name: '', type: 'address' }],
|
|
39
|
+
name: 'addressToAgentId',
|
|
40
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
41
|
+
stateMutability: 'view',
|
|
42
|
+
type: 'function',
|
|
43
|
+
},
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
const REPUTATION_ABI = [
|
|
47
|
+
{
|
|
48
|
+
inputs: [{ name: 'user', type: 'address' }],
|
|
49
|
+
name: 'getReputation',
|
|
50
|
+
outputs: [
|
|
51
|
+
{ name: 'score', type: 'uint256' },
|
|
52
|
+
{ name: 'trustVector', type: 'uint256' },
|
|
53
|
+
],
|
|
54
|
+
stateMutability: 'view',
|
|
55
|
+
type: 'function',
|
|
56
|
+
},
|
|
57
|
+
] as const;
|
|
58
|
+
|
|
59
|
+
const ESCROW_ABI = [
|
|
60
|
+
{
|
|
61
|
+
inputs: [{ name: 'escrowId', type: 'uint256' }],
|
|
62
|
+
name: 'getEscrow',
|
|
63
|
+
outputs: [
|
|
64
|
+
{ name: 'sender', type: 'address' },
|
|
65
|
+
{ name: 'recipient', type: 'address' },
|
|
66
|
+
{ name: 'amount', type: 'uint256' },
|
|
67
|
+
{ name: 'token', type: 'address' },
|
|
68
|
+
{ name: 'status', type: 'uint8' },
|
|
69
|
+
],
|
|
70
|
+
stateMutability: 'view',
|
|
71
|
+
type: 'function',
|
|
72
|
+
},
|
|
73
|
+
] as const;
|
|
74
|
+
|
|
75
|
+
const USDC_ABI = [
|
|
76
|
+
{
|
|
77
|
+
inputs: [{ name: 'account', type: 'address' }],
|
|
78
|
+
name: 'balanceOf',
|
|
79
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
80
|
+
stateMutability: 'view',
|
|
81
|
+
type: 'function',
|
|
82
|
+
},
|
|
83
|
+
] as const;
|
|
84
|
+
|
|
85
|
+
export class ContractsClient {
|
|
86
|
+
private publicClient;
|
|
87
|
+
private contracts;
|
|
88
|
+
|
|
89
|
+
constructor(config: MCPServerConfig) {
|
|
90
|
+
const chain = config.network === 'sepolia' ? baseSepolia : base;
|
|
91
|
+
this.contracts = config.network === 'sepolia' ? CONTRACTS_SEPOLIA : CONTRACTS_MAINNET;
|
|
92
|
+
|
|
93
|
+
this.publicClient = createPublicClient({
|
|
94
|
+
chain,
|
|
95
|
+
transport: http(config.rpcUrl),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async getAgentInfo(address: Address): Promise<AgentInfo> {
|
|
100
|
+
const result = await this.publicClient.readContract({
|
|
101
|
+
address: this.contracts.IDENTITY,
|
|
102
|
+
abi: IDENTITY_ABI,
|
|
103
|
+
functionName: 'getAgentInfo',
|
|
104
|
+
args: [address],
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
name: result[0],
|
|
109
|
+
tokenId: result[1],
|
|
110
|
+
registered: result[2],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async getReputation(address: Address): Promise<ReputationData> {
|
|
115
|
+
const result = await this.publicClient.readContract({
|
|
116
|
+
address: this.contracts.REPUTATION,
|
|
117
|
+
abi: REPUTATION_ABI,
|
|
118
|
+
functionName: 'getReputation',
|
|
119
|
+
args: [address],
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
score: result[0],
|
|
124
|
+
trustVector: result[1],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async getBalance(address: Address): Promise<bigint> {
|
|
129
|
+
const balance = await this.publicClient.readContract({
|
|
130
|
+
address: this.contracts.USDC,
|
|
131
|
+
abi: USDC_ABI,
|
|
132
|
+
functionName: 'balanceOf',
|
|
133
|
+
args: [address],
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return balance;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async getEscrow(escrowId: bigint): Promise<EscrowData> {
|
|
140
|
+
const result = await this.publicClient.readContract({
|
|
141
|
+
address: this.contracts.ESCROW,
|
|
142
|
+
abi: ESCROW_ABI,
|
|
143
|
+
functionName: 'getEscrow',
|
|
144
|
+
args: [escrowId],
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
sender: result[0],
|
|
149
|
+
recipient: result[1],
|
|
150
|
+
amount: result[2],
|
|
151
|
+
token: result[3],
|
|
152
|
+
status: result[4],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async getUserTransactionCount(address: Address): Promise<bigint> {
|
|
157
|
+
// TODO: Implement when Escrow V3 is deployed with getUserTransactionCount
|
|
158
|
+
return 0n;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
getContractAddress(name: 'IDENTITY' | 'REPUTATION' | 'CREDIT' | 'ESCROW' | 'USDC'): Address {
|
|
162
|
+
return this.contracts[name];
|
|
163
|
+
}
|
|
164
|
+
}
|
package/src/lib/types.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Address } from 'viem';
|
|
2
|
+
|
|
3
|
+
export interface AgentInfo {
|
|
4
|
+
name: string;
|
|
5
|
+
tokenId: bigint;
|
|
6
|
+
registered: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ReputationData {
|
|
10
|
+
score: bigint;
|
|
11
|
+
trustVector: bigint;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface CreditStatus {
|
|
15
|
+
limit: bigint;
|
|
16
|
+
available: bigint;
|
|
17
|
+
inUse: bigint;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface EscrowData {
|
|
21
|
+
sender: Address;
|
|
22
|
+
recipient: Address;
|
|
23
|
+
amount: bigint;
|
|
24
|
+
token: Address;
|
|
25
|
+
status: number; // 0=Pending, 1=Active, 2=Released, 3=Refunded, 4=Disputed
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ServiceListing {
|
|
29
|
+
id: string;
|
|
30
|
+
provider: Address;
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
category: string;
|
|
34
|
+
price: string;
|
|
35
|
+
currency: string;
|
|
36
|
+
reputation?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface MCPError {
|
|
40
|
+
code: string;
|
|
41
|
+
message: string;
|
|
42
|
+
details?: unknown;
|
|
43
|
+
retryable: boolean;
|
|
44
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Address } from 'viem';
|
|
2
|
+
import type { ContractsClient } from '../lib/contracts.js';
|
|
3
|
+
|
|
4
|
+
export async function getAgentResource(
|
|
5
|
+
address: string,
|
|
6
|
+
contracts: ContractsClient
|
|
7
|
+
): Promise<object> {
|
|
8
|
+
const addr = address as Address;
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const [reputation, agentInfo] = await Promise.all([
|
|
12
|
+
contracts.getReputation(addr),
|
|
13
|
+
contracts.getAgentInfo(addr),
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const normalizedScore = Number(reputation.score) / 1000;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
uri: `paylobster://agent/${address}`,
|
|
20
|
+
mimeType: 'application/json',
|
|
21
|
+
text: JSON.stringify({
|
|
22
|
+
address,
|
|
23
|
+
name: agentInfo.name || 'Unregistered',
|
|
24
|
+
tokenId: agentInfo.tokenId.toString(),
|
|
25
|
+
registered: agentInfo.registered,
|
|
26
|
+
reputation: {
|
|
27
|
+
score: normalizedScore,
|
|
28
|
+
trustVector: reputation.trustVector.toString(),
|
|
29
|
+
verified: agentInfo.registered,
|
|
30
|
+
},
|
|
31
|
+
}, null, 2),
|
|
32
|
+
};
|
|
33
|
+
} catch (error) {
|
|
34
|
+
throw {
|
|
35
|
+
code: 'ERR_RESOURCE_NOT_FOUND',
|
|
36
|
+
message: `Failed to fetch agent resource for ${address}`,
|
|
37
|
+
retryable: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { formatUnits } from 'viem';
|
|
2
|
+
import type { ContractsClient } from '../lib/contracts.js';
|
|
3
|
+
|
|
4
|
+
const ESCROW_STATUS = ['Pending', 'Active', 'Released', 'Refunded', 'Disputed'];
|
|
5
|
+
|
|
6
|
+
export async function getEscrowResource(
|
|
7
|
+
escrowId: string,
|
|
8
|
+
contracts: ContractsClient
|
|
9
|
+
): Promise<object> {
|
|
10
|
+
try {
|
|
11
|
+
const id = BigInt(escrowId);
|
|
12
|
+
const escrow = await contracts.getEscrow(id);
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
uri: `paylobster://escrow/${escrowId}`,
|
|
16
|
+
mimeType: 'application/json',
|
|
17
|
+
text: JSON.stringify({
|
|
18
|
+
escrowId,
|
|
19
|
+
sender: escrow.sender,
|
|
20
|
+
recipient: escrow.recipient,
|
|
21
|
+
amount: formatUnits(escrow.amount, 6),
|
|
22
|
+
amountRaw: escrow.amount.toString(),
|
|
23
|
+
token: escrow.token,
|
|
24
|
+
status: ESCROW_STATUS[escrow.status] || 'Unknown',
|
|
25
|
+
statusCode: escrow.status,
|
|
26
|
+
}, null, 2),
|
|
27
|
+
};
|
|
28
|
+
} catch (error) {
|
|
29
|
+
throw {
|
|
30
|
+
code: 'ERR_RESOURCE_NOT_FOUND',
|
|
31
|
+
message: `Escrow ${escrowId} not found`,
|
|
32
|
+
retryable: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Mock services resource (ServiceRegistry not deployed yet)
|
|
2
|
+
|
|
3
|
+
const MOCK_CATEGORIES = [
|
|
4
|
+
'code-review',
|
|
5
|
+
'security-audit',
|
|
6
|
+
'ai-training',
|
|
7
|
+
'data-labeling',
|
|
8
|
+
'code-generation',
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export async function getServicesResource(): Promise<object> {
|
|
12
|
+
return {
|
|
13
|
+
uri: 'paylobster://services',
|
|
14
|
+
mimeType: 'application/json',
|
|
15
|
+
text: JSON.stringify({
|
|
16
|
+
totalServices: 5,
|
|
17
|
+
categories: MOCK_CATEGORIES,
|
|
18
|
+
note: 'Mock data - ServiceRegistry contract not yet deployed',
|
|
19
|
+
services: [
|
|
20
|
+
{
|
|
21
|
+
id: 'service-001',
|
|
22
|
+
name: 'TypeScript Code Reviewer',
|
|
23
|
+
category: 'code-review',
|
|
24
|
+
price: '10.00',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'service-002',
|
|
28
|
+
name: 'Rust Security Auditor',
|
|
29
|
+
category: 'security-audit',
|
|
30
|
+
price: '50.00',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'service-003',
|
|
34
|
+
name: 'AI Model Training',
|
|
35
|
+
category: 'ai-training',
|
|
36
|
+
price: '100.00',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'service-004',
|
|
40
|
+
name: 'Data Labeling Service',
|
|
41
|
+
category: 'data-labeling',
|
|
42
|
+
price: '5.00',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'service-005',
|
|
46
|
+
name: 'Python Code Generator',
|
|
47
|
+
category: 'code-generation',
|
|
48
|
+
price: '15.00',
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
}, null, 2),
|
|
52
|
+
};
|
|
53
|
+
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import {
|
|
4
|
+
CallToolRequestSchema,
|
|
5
|
+
ListResourcesRequestSchema,
|
|
6
|
+
ListToolsRequestSchema,
|
|
7
|
+
ReadResourceRequestSchema,
|
|
8
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import { getConfig, type MCPServerConfig } from './lib/config.js';
|
|
10
|
+
import { ContractsClient } from './lib/contracts.js';
|
|
11
|
+
|
|
12
|
+
// Tool handlers
|
|
13
|
+
import { getReputationTool, handleGetReputation } from './tools/reputation.js';
|
|
14
|
+
import { getBalanceTool, handleGetBalance } from './tools/balance.js';
|
|
15
|
+
import { getAgentTool, handleGetAgent } from './tools/agent.js';
|
|
16
|
+
import { getEscrowTool, listEscrowsTool, handleGetEscrow, handleListEscrows } from './tools/escrow.js';
|
|
17
|
+
import { searchServicesTool, handleSearchServices } from './tools/search.js';
|
|
18
|
+
|
|
19
|
+
// Resource handlers
|
|
20
|
+
import { getAgentResource } from './resources/agent.js';
|
|
21
|
+
import { getEscrowResource } from './resources/escrow.js';
|
|
22
|
+
import { getServicesResource } from './resources/services.js';
|
|
23
|
+
|
|
24
|
+
export class PayLobsterMCPServer {
|
|
25
|
+
private server: Server;
|
|
26
|
+
private config: MCPServerConfig;
|
|
27
|
+
private contracts: ContractsClient;
|
|
28
|
+
|
|
29
|
+
constructor(config?: Partial<MCPServerConfig>) {
|
|
30
|
+
this.config = { ...getConfig(), ...config };
|
|
31
|
+
this.contracts = new ContractsClient(this.config);
|
|
32
|
+
|
|
33
|
+
this.server = new Server(
|
|
34
|
+
{
|
|
35
|
+
name: 'paylobster-mcp-server',
|
|
36
|
+
version: '1.0.0',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
capabilities: {
|
|
40
|
+
tools: {},
|
|
41
|
+
resources: {},
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
this.setupHandlers();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private setupHandlers() {
|
|
50
|
+
// List available tools
|
|
51
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
52
|
+
tools: [
|
|
53
|
+
getReputationTool,
|
|
54
|
+
getBalanceTool,
|
|
55
|
+
getAgentTool,
|
|
56
|
+
getEscrowTool,
|
|
57
|
+
listEscrowsTool,
|
|
58
|
+
searchServicesTool,
|
|
59
|
+
],
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
// Handle tool calls
|
|
63
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
64
|
+
try {
|
|
65
|
+
const { name, arguments: args } = request.params;
|
|
66
|
+
|
|
67
|
+
let result;
|
|
68
|
+
switch (name) {
|
|
69
|
+
case 'paylobster_get_reputation':
|
|
70
|
+
result = await handleGetReputation(args, this.contracts);
|
|
71
|
+
break;
|
|
72
|
+
case 'paylobster_get_balance':
|
|
73
|
+
result = await handleGetBalance(args, this.contracts);
|
|
74
|
+
break;
|
|
75
|
+
case 'paylobster_get_agent':
|
|
76
|
+
result = await handleGetAgent(args, this.contracts);
|
|
77
|
+
break;
|
|
78
|
+
case 'paylobster_get_escrow':
|
|
79
|
+
result = await handleGetEscrow(args, this.contracts);
|
|
80
|
+
break;
|
|
81
|
+
case 'paylobster_list_escrows':
|
|
82
|
+
result = await handleListEscrows(args, this.contracts);
|
|
83
|
+
break;
|
|
84
|
+
case 'paylobster_search_services':
|
|
85
|
+
result = await handleSearchServices(args);
|
|
86
|
+
break;
|
|
87
|
+
default:
|
|
88
|
+
throw {
|
|
89
|
+
code: 'ERR_UNKNOWN_TOOL',
|
|
90
|
+
message: `Unknown tool: ${name}`,
|
|
91
|
+
retryable: false,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
content: [
|
|
97
|
+
{
|
|
98
|
+
type: 'text',
|
|
99
|
+
text: JSON.stringify(result, null, 2),
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
} catch (error: any) {
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: 'text',
|
|
108
|
+
text: JSON.stringify({
|
|
109
|
+
error: {
|
|
110
|
+
code: error.code || 'ERR_UNKNOWN',
|
|
111
|
+
message: error.message || 'An unknown error occurred',
|
|
112
|
+
details: error.details,
|
|
113
|
+
retryable: error.retryable ?? false,
|
|
114
|
+
},
|
|
115
|
+
}, null, 2),
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
isError: true,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// List available resources
|
|
124
|
+
this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
125
|
+
resources: [
|
|
126
|
+
{
|
|
127
|
+
uri: 'paylobster://services',
|
|
128
|
+
name: 'Service Catalog',
|
|
129
|
+
description: 'Browse all available AI agent services',
|
|
130
|
+
mimeType: 'application/json',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
uri: 'paylobster://agent/{address}',
|
|
134
|
+
name: 'Agent Profile',
|
|
135
|
+
description: 'Get agent identity and reputation by address',
|
|
136
|
+
mimeType: 'application/json',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
uri: 'paylobster://escrow/{id}',
|
|
140
|
+
name: 'Escrow Details',
|
|
141
|
+
description: 'Get details for a specific escrow',
|
|
142
|
+
mimeType: 'application/json',
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
}));
|
|
146
|
+
|
|
147
|
+
// Handle resource reads
|
|
148
|
+
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
149
|
+
try {
|
|
150
|
+
const uri = request.params.uri;
|
|
151
|
+
|
|
152
|
+
if (uri === 'paylobster://services') {
|
|
153
|
+
return await getServicesResource();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const agentMatch = uri.match(/^paylobster:\/\/agent\/(.+)$/);
|
|
157
|
+
if (agentMatch) {
|
|
158
|
+
return await getAgentResource(agentMatch[1], this.contracts);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const escrowMatch = uri.match(/^paylobster:\/\/escrow\/(.+)$/);
|
|
162
|
+
if (escrowMatch) {
|
|
163
|
+
return await getEscrowResource(escrowMatch[1], this.contracts);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
throw {
|
|
167
|
+
code: 'ERR_RESOURCE_NOT_FOUND',
|
|
168
|
+
message: `Unknown resource URI: ${uri}`,
|
|
169
|
+
retryable: false,
|
|
170
|
+
};
|
|
171
|
+
} catch (error: any) {
|
|
172
|
+
throw new Error(error.message || 'Failed to read resource');
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async start() {
|
|
178
|
+
const transport = new StdioServerTransport();
|
|
179
|
+
await this.server.connect(transport);
|
|
180
|
+
|
|
181
|
+
// Log to stderr (stdout is used for MCP protocol)
|
|
182
|
+
console.error('PayLobster MCP Server started');
|
|
183
|
+
console.error(`Network: ${this.config.network}`);
|
|
184
|
+
console.error(`RPC URL: ${this.config.rpcUrl}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
getServer(): Server {
|
|
188
|
+
return this.server;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Address } from 'viem';
|
|
3
|
+
import type { ContractsClient } from '../lib/contracts.js';
|
|
4
|
+
|
|
5
|
+
const GetAgentInput = z.object({
|
|
6
|
+
address: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address'),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const getAgentTool = {
|
|
10
|
+
name: 'paylobster_get_agent',
|
|
11
|
+
description: 'Get agent identity information including name, token ID, and registration status',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
address: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'Ethereum address of the agent',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: ['address'],
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export async function handleGetAgent(
|
|
25
|
+
input: unknown,
|
|
26
|
+
contracts: ContractsClient
|
|
27
|
+
): Promise<object> {
|
|
28
|
+
try {
|
|
29
|
+
const params = GetAgentInput.parse(input);
|
|
30
|
+
const address = params.address as Address;
|
|
31
|
+
|
|
32
|
+
const agentInfo = await contracts.getAgentInfo(address);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
address,
|
|
36
|
+
name: agentInfo.name || 'Unregistered',
|
|
37
|
+
tokenId: agentInfo.tokenId.toString(),
|
|
38
|
+
registered: agentInfo.registered,
|
|
39
|
+
};
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (error instanceof z.ZodError) {
|
|
42
|
+
throw {
|
|
43
|
+
code: 'ERR_INVALID_INPUT',
|
|
44
|
+
message: 'Invalid input parameters',
|
|
45
|
+
details: error.errors,
|
|
46
|
+
retryable: false,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
throw {
|
|
51
|
+
code: 'ERR_NETWORK_ERROR',
|
|
52
|
+
message: error instanceof Error ? error.message : 'Failed to fetch agent info',
|
|
53
|
+
retryable: true,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Address } from 'viem';
|
|
3
|
+
import { formatUnits } from 'viem';
|
|
4
|
+
import type { ContractsClient } from '../lib/contracts.js';
|
|
5
|
+
|
|
6
|
+
const GetBalanceInput = z.object({
|
|
7
|
+
address: z.string().regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid Ethereum address'),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const getBalanceTool = {
|
|
11
|
+
name: 'paylobster_get_balance',
|
|
12
|
+
description: 'Check USDC balance for an Ethereum address',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
address: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Ethereum address to check balance for',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
required: ['address'],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export async function handleGetBalance(
|
|
26
|
+
input: unknown,
|
|
27
|
+
contracts: ContractsClient
|
|
28
|
+
): Promise<object> {
|
|
29
|
+
try {
|
|
30
|
+
const params = GetBalanceInput.parse(input);
|
|
31
|
+
const address = params.address as Address;
|
|
32
|
+
|
|
33
|
+
const balance = await contracts.getBalance(address);
|
|
34
|
+
|
|
35
|
+
// USDC has 6 decimals
|
|
36
|
+
const formattedBalance = formatUnits(balance, 6);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
address,
|
|
40
|
+
balance: {
|
|
41
|
+
usdc: formattedBalance,
|
|
42
|
+
raw: balance.toString(),
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error instanceof z.ZodError) {
|
|
47
|
+
throw {
|
|
48
|
+
code: 'ERR_INVALID_INPUT',
|
|
49
|
+
message: 'Invalid input parameters',
|
|
50
|
+
details: error.errors,
|
|
51
|
+
retryable: false,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
throw {
|
|
56
|
+
code: 'ERR_NETWORK_ERROR',
|
|
57
|
+
message: error instanceof Error ? error.message : 'Failed to fetch balance',
|
|
58
|
+
retryable: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|