@emilia-protocol/mcp-server 0.1.0 → 1.0.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 +967 -34
- package/auto-receipt.js +402 -0
- package/index.js +1411 -245
- package/package.json +40 -10
package/index.js
CHANGED
|
@@ -2,36 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* EMILIA Protocol — MCP Server
|
|
5
|
+
* @license Apache-2.0
|
|
5
6
|
*
|
|
6
|
-
* Trust
|
|
7
|
-
* Add this server to any MCP-compatible client
|
|
8
|
-
* to
|
|
7
|
+
* Trust evaluation tools for AI agents.
|
|
8
|
+
* Add this server to any MCP-compatible client to give your agent
|
|
9
|
+
* access to EP trust profiles and policy evaluation.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* ep_search_entities — Search for entities by name or capability
|
|
15
|
-
* ep_register_entity — Register a new entity in the EMILIA network
|
|
16
|
-
* ep_leaderboard — Get the top-scored entities
|
|
11
|
+
* PRIMARY TOOLS (trust-profile-first):
|
|
12
|
+
* ep_trust_profile — Get an entity's full trust profile (canonical)
|
|
13
|
+
* ep_trust_evaluate — Evaluate an entity against a trust policy
|
|
14
|
+
* ep_submit_receipt — Submit a transaction receipt
|
|
17
15
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* SECONDARY TOOLS:
|
|
17
|
+
* ep_search_entities — Search for entities
|
|
18
|
+
* ep_verify_receipt — Verify receipt against Merkle root
|
|
19
|
+
* ep_register_entity — Register a new entity
|
|
20
|
+
* ep_leaderboard — Get top entities
|
|
21
|
+
*
|
|
22
|
+
* V1.0 ADDITIONS:
|
|
23
|
+
* ep_create_delegation — Create a delegation record (principal → agent)
|
|
24
|
+
* ep_verify_delegation — Verify an agent's delegation for an action
|
|
25
|
+
* ep_trust_gate — Pre-action trust check (canonical gate)
|
|
26
|
+
* ep_batch_submit — Batch receipt submission (up to 50)
|
|
27
|
+
* ep_domain_score — Per-domain trust breakdown
|
|
28
|
+
*
|
|
29
|
+
* SPRINT 4A: Attribution Chain
|
|
30
|
+
* ep_delegation_judgment — Principal delegation authority (how well they choose agents)
|
|
21
31
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* SPRINT 5A: Privacy-Preserving Commitment Proof layer
|
|
33
|
+
* ep_generate_zk_proof — Prove trust threshold without revealing receipt contents or counterparties
|
|
34
|
+
* ep_verify_zk_proof — Verify a commitment trust proof by proof_id (public, no transaction history revealed)
|
|
35
|
+
*
|
|
36
|
+
* EP HANDSHAKE (structured identity exchange):
|
|
37
|
+
* ep_initiate_handshake — Initiate a structured identity exchange between parties
|
|
38
|
+
* ep_add_presentation — Add an identity presentation (proof) to a handshake
|
|
39
|
+
* ep_verify_handshake — Evaluate handshake presentations against policy
|
|
40
|
+
* ep_get_handshake — Get full handshake state
|
|
41
|
+
* ep_revoke_handshake — Revoke an active handshake
|
|
42
|
+
*
|
|
43
|
+
* EP COMMIT (signed authorization token for high-stakes machine actions):
|
|
44
|
+
* ep_issue_commit — Issue a signed EP Commit before a high-stakes action
|
|
45
|
+
* ep_verify_commit — Verify a commit's signature, status, and validity
|
|
46
|
+
* ep_get_commit_status — Get current state of a commit
|
|
47
|
+
* ep_revoke_commit — Revoke an active commit
|
|
48
|
+
* ep_bind_receipt_to_commit — Bind a post-action receipt to a commit
|
|
49
|
+
*
|
|
50
|
+
* Setup:
|
|
51
|
+
* EP_BASE_URL=https://emiliaprotocol.ai
|
|
52
|
+
* EP_API_KEY=ep_live_... (for authenticated writes; registration is public)
|
|
35
53
|
*
|
|
36
54
|
* @license Apache-2.0
|
|
37
55
|
*/
|
|
@@ -41,214 +59,693 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
41
59
|
import {
|
|
42
60
|
CallToolRequestSchema,
|
|
43
61
|
ListToolsRequestSchema,
|
|
62
|
+
ListResourcesRequestSchema,
|
|
63
|
+
ReadResourceRequestSchema,
|
|
64
|
+
ListPromptsRequestSchema,
|
|
65
|
+
GetPromptRequestSchema,
|
|
44
66
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
67
|
+
import { AutoReceiptMiddleware } from './auto-receipt.js';
|
|
45
68
|
|
|
46
69
|
const BASE_URL = process.env.EP_BASE_URL || 'https://emiliaprotocol.ai';
|
|
47
70
|
const API_KEY = process.env.EP_API_KEY || '';
|
|
48
71
|
|
|
49
72
|
// =============================================================================
|
|
50
|
-
//
|
|
73
|
+
// Auto-Receipt Middleware (Sprint 2)
|
|
51
74
|
// =============================================================================
|
|
52
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Global middleware instance. Shared across all tool calls in this process.
|
|
78
|
+
*
|
|
79
|
+
* Opt-in is false by default — agents must call ep_configure_auto_receipt
|
|
80
|
+
* to enable. The entity_id and epApiKey are pre-populated from environment
|
|
81
|
+
* variables so the operator doesn't need to expose them to the agent.
|
|
82
|
+
*/
|
|
83
|
+
const autoReceipt = new AutoReceiptMiddleware({
|
|
84
|
+
epApiUrl: process.env.EP_AUTO_RECEIPT_URL || 'https://emiliaprotocol.ai',
|
|
85
|
+
epApiKey: process.env.EP_AUTO_RECEIPT_KEY || API_KEY,
|
|
86
|
+
optIn: process.env.EP_AUTO_RECEIPT_OPT_IN === 'true',
|
|
87
|
+
entityId: process.env.EP_AUTO_RECEIPT_ENTITY_ID || '',
|
|
88
|
+
});
|
|
89
|
+
|
|
53
90
|
async function epFetch(path, options = {}) {
|
|
54
91
|
const url = `${BASE_URL}${path}`;
|
|
55
92
|
const headers = {
|
|
56
93
|
'Content-Type': 'application/json',
|
|
57
94
|
...(options.auth && API_KEY ? { Authorization: `Bearer ${API_KEY}` } : {}),
|
|
58
95
|
};
|
|
59
|
-
|
|
60
96
|
const res = await fetch(url, {
|
|
61
97
|
method: options.method || 'GET',
|
|
62
98
|
headers,
|
|
63
99
|
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
64
100
|
});
|
|
65
|
-
|
|
66
101
|
const data = await res.json();
|
|
67
|
-
if (!res.ok) {
|
|
68
|
-
throw new Error(data.error || `EP API error: ${res.status}`);
|
|
69
|
-
}
|
|
102
|
+
if (!res.ok) throw new Error(data.error || `EP API error: ${res.status}`);
|
|
70
103
|
return data;
|
|
71
104
|
}
|
|
72
105
|
|
|
73
106
|
// =============================================================================
|
|
74
|
-
// Tool definitions
|
|
107
|
+
// Tool definitions — trust-profile-first
|
|
75
108
|
// =============================================================================
|
|
76
109
|
|
|
77
110
|
const TOOLS = [
|
|
111
|
+
// PRIMARY: Trust profile (canonical read surface)
|
|
78
112
|
{
|
|
79
|
-
name: '
|
|
113
|
+
name: 'ep_trust_profile',
|
|
80
114
|
description:
|
|
81
|
-
'
|
|
82
|
-
'Returns
|
|
83
|
-
'
|
|
115
|
+
'Get an entity\'s full trust profile. This is the CANONICAL way to check trust in EP. ' +
|
|
116
|
+
'Returns behavioral rates (completion, retry, abandon, dispute), signal breakdowns, ' +
|
|
117
|
+
'provenance composition, consistency, anomaly alerts, current confidence, historical establishment, ' +
|
|
118
|
+
'and dispute summary. Use this before transacting with any counterparty or installing any software.',
|
|
84
119
|
inputSchema: {
|
|
85
120
|
type: 'object',
|
|
86
121
|
properties: {
|
|
87
122
|
entity_id: {
|
|
88
123
|
type: 'string',
|
|
89
|
-
description: '
|
|
124
|
+
description: 'Entity ID (slug like "merchant-xyz") or UUID',
|
|
90
125
|
},
|
|
91
126
|
},
|
|
92
127
|
required: ['entity_id'],
|
|
93
128
|
},
|
|
94
129
|
},
|
|
130
|
+
|
|
131
|
+
// PRIMARY: Trust evaluation (policy consumption)
|
|
95
132
|
{
|
|
96
|
-
name: '
|
|
133
|
+
name: 'ep_trust_evaluate',
|
|
97
134
|
description:
|
|
98
|
-
'
|
|
99
|
-
'
|
|
100
|
-
'
|
|
101
|
-
'return_processing, agent_satisfaction. At least one signal is required.',
|
|
135
|
+
'Evaluate an entity against a trust policy. Returns a Trust Decision (allow/review/deny) with specific failure reasons. ' +
|
|
136
|
+
'Built-in policies: "strict" (high-value), "standard" (normal), "permissive" (low-risk), "discovery" (allow unevaluated). ' +
|
|
137
|
+
'Accepts optional context for context-aware evaluation. Use this to make routing and payment decisions.',
|
|
102
138
|
inputSchema: {
|
|
103
139
|
type: 'object',
|
|
104
140
|
properties: {
|
|
105
141
|
entity_id: {
|
|
106
142
|
type: 'string',
|
|
107
|
-
description: '
|
|
143
|
+
description: 'Entity ID to evaluate',
|
|
144
|
+
},
|
|
145
|
+
policy: {
|
|
146
|
+
type: 'string',
|
|
147
|
+
description: 'Policy name: "strict", "standard", "permissive", "discovery"',
|
|
148
|
+
},
|
|
149
|
+
context: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
description: 'Context key for context-aware evaluation: { task_type, category, geo, modality, value_band }',
|
|
108
152
|
},
|
|
153
|
+
},
|
|
154
|
+
required: ['entity_id'],
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
// PRIMARY: Submit receipt
|
|
159
|
+
{
|
|
160
|
+
name: 'ep_submit_receipt',
|
|
161
|
+
description:
|
|
162
|
+
'Submit a transaction receipt to the EP ledger. Requires an API key. ' +
|
|
163
|
+
'Receipts are append-only, cryptographically hashed, and chain-linked. ' +
|
|
164
|
+
'transaction_ref is REQUIRED. agent_behavior is the strongest signal.',
|
|
165
|
+
inputSchema: {
|
|
166
|
+
type: 'object',
|
|
167
|
+
properties: {
|
|
168
|
+
entity_id: { type: 'string', description: 'Entity to evaluate' },
|
|
169
|
+
transaction_ref: { type: 'string', description: 'External transaction reference (required)' },
|
|
109
170
|
transaction_type: {
|
|
110
171
|
type: 'string',
|
|
111
172
|
enum: ['purchase', 'service', 'task_completion', 'delivery', 'return'],
|
|
112
|
-
description: 'Type of transaction',
|
|
113
173
|
},
|
|
114
|
-
|
|
174
|
+
agent_behavior: {
|
|
115
175
|
type: 'string',
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
delivery_accuracy: {
|
|
119
|
-
type: 'number',
|
|
120
|
-
description: '0-100: Did it arrive when promised?',
|
|
176
|
+
enum: ['completed', 'retried_same', 'retried_different', 'abandoned', 'disputed'],
|
|
177
|
+
description: 'Observable behavioral outcome (strongest Phase 1 signal)',
|
|
121
178
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
},
|
|
130
|
-
return_processing: {
|
|
131
|
-
type: 'number',
|
|
132
|
-
description: '0-100: Was the return policy followed?',
|
|
133
|
-
},
|
|
134
|
-
agent_satisfaction: {
|
|
135
|
-
type: 'number',
|
|
136
|
-
description: '0-100: Was the purchasing agent satisfied?',
|
|
137
|
-
},
|
|
138
|
-
evidence: {
|
|
179
|
+
delivery_accuracy: { type: 'number', description: '0-100' },
|
|
180
|
+
product_accuracy: { type: 'number', description: '0-100' },
|
|
181
|
+
price_integrity: { type: 'number', description: '0-100' },
|
|
182
|
+
return_processing: { type: 'number', description: '0-100' },
|
|
183
|
+
claims: { type: 'object', description: 'Structured claims (delivered, on_time, price_honored, as_described)' },
|
|
184
|
+
evidence: { type: 'object', description: 'Supporting evidence references' },
|
|
185
|
+
context: {
|
|
139
186
|
type: 'object',
|
|
140
|
-
description: '
|
|
187
|
+
description: 'Context key: { task_type, category, geo, modality, value_band, risk_class }',
|
|
141
188
|
},
|
|
142
189
|
},
|
|
143
|
-
required: ['entity_id', 'transaction_type'],
|
|
190
|
+
required: ['entity_id', 'transaction_type', 'transaction_ref'],
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
// SECONDARY
|
|
195
|
+
{
|
|
196
|
+
name: 'ep_search_entities',
|
|
197
|
+
description: 'Search for entities by name, capability, or category.',
|
|
198
|
+
inputSchema: {
|
|
199
|
+
type: 'object',
|
|
200
|
+
properties: {
|
|
201
|
+
query: { type: 'string', description: 'Search query' },
|
|
202
|
+
entity_type: { type: 'string', enum: ['agent','merchant','service_provider','github_app','github_action','mcp_server','npm_package','chrome_extension','shopify_app','marketplace_plugin','agent_tool'] },
|
|
203
|
+
},
|
|
204
|
+
required: ['query'],
|
|
144
205
|
},
|
|
145
206
|
},
|
|
146
207
|
{
|
|
147
208
|
name: 'ep_verify_receipt',
|
|
209
|
+
description: 'Verify a receipt against the on-chain Merkle root.',
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
properties: {
|
|
213
|
+
receipt_id: { type: 'string', description: 'Receipt ID (ep_rcpt_...)' },
|
|
214
|
+
},
|
|
215
|
+
required: ['receipt_id'],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: 'ep_register_entity',
|
|
220
|
+
description: 'Register a new entity. Public — returns the first API key.',
|
|
221
|
+
inputSchema: {
|
|
222
|
+
type: 'object',
|
|
223
|
+
properties: {
|
|
224
|
+
entity_id: { type: 'string', description: 'Slug (lowercase, hyphens)' },
|
|
225
|
+
display_name: { type: 'string' },
|
|
226
|
+
entity_type: { type: 'string', enum: ['agent','merchant','service_provider','github_app','github_action','mcp_server','npm_package','chrome_extension','shopify_app','marketplace_plugin','agent_tool'] },
|
|
227
|
+
description: { type: 'string' },
|
|
228
|
+
capabilities: { type: 'array', items: { type: 'string' } },
|
|
229
|
+
},
|
|
230
|
+
required: ['entity_id', 'display_name', 'entity_type', 'description'],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'ep_leaderboard',
|
|
235
|
+
description: 'Get top entities ranked by trust confidence.',
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: 'object',
|
|
238
|
+
properties: {
|
|
239
|
+
limit: { type: 'number', description: 'Max entities (default 10, max 50)' },
|
|
240
|
+
entity_type: { type: 'string', enum: ['agent','merchant','service_provider','github_app','github_action','mcp_server','npm_package','chrome_extension','shopify_app','marketplace_plugin','agent_tool'] },
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
// DUE PROCESS
|
|
245
|
+
{
|
|
246
|
+
name: 'ep_dispute_file',
|
|
148
247
|
description:
|
|
149
|
-
'
|
|
150
|
-
'
|
|
151
|
-
'
|
|
248
|
+
'File a dispute against a receipt. Any affected party can challenge. ' +
|
|
249
|
+
'Reasons: fraudulent_receipt, inaccurate_signals, identity_dispute, context_mismatch, duplicate_transaction, coerced_receipt, other. ' +
|
|
250
|
+
'The receipt submitter has 7 days to respond.',
|
|
251
|
+
inputSchema: {
|
|
252
|
+
type: 'object',
|
|
253
|
+
properties: {
|
|
254
|
+
receipt_id: { type: 'string', description: 'Receipt ID to dispute (ep_rcpt_...)' },
|
|
255
|
+
reason: { type: 'string', description: 'Reason for dispute' },
|
|
256
|
+
description: { type: 'string', description: 'Explanation of the dispute' },
|
|
257
|
+
evidence: { type: 'object', description: 'Supporting evidence' },
|
|
258
|
+
},
|
|
259
|
+
required: ['receipt_id', 'reason'],
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
name: 'ep_dispute_status',
|
|
264
|
+
description: 'Check the status of a dispute. Public — transparency is a protocol value.',
|
|
152
265
|
inputSchema: {
|
|
153
266
|
type: 'object',
|
|
154
267
|
properties: {
|
|
155
|
-
|
|
268
|
+
dispute_id: { type: 'string', description: 'Dispute ID (ep_disp_...)' },
|
|
269
|
+
},
|
|
270
|
+
required: ['dispute_id'],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: 'ep_report_trust_issue',
|
|
275
|
+
description:
|
|
276
|
+
'Report a trust issue as a human. No authentication required. ' +
|
|
277
|
+
'For when someone is wrongly downgraded, harmed by a trusted entity, or sees fraud. ' +
|
|
278
|
+
'EP must never make trust more powerful than appeal.',
|
|
279
|
+
inputSchema: {
|
|
280
|
+
type: 'object',
|
|
281
|
+
properties: {
|
|
282
|
+
entity_id: { type: 'string', description: 'Entity the report is about' },
|
|
283
|
+
report_type: {
|
|
156
284
|
type: 'string',
|
|
157
|
-
|
|
285
|
+
enum: ['wrongly_downgraded', 'harmed_by_trusted_entity', 'fraudulent_entity', 'inaccurate_profile', 'other'],
|
|
158
286
|
},
|
|
287
|
+
description: { type: 'string', description: 'What happened' },
|
|
288
|
+
contact_email: { type: 'string', description: 'Email for follow-up (optional)' },
|
|
159
289
|
},
|
|
160
|
-
required: ['
|
|
290
|
+
required: ['entity_id', 'report_type', 'description'],
|
|
161
291
|
},
|
|
162
292
|
},
|
|
293
|
+
// EP-SX: Software Trust
|
|
163
294
|
{
|
|
164
|
-
name: '
|
|
295
|
+
name: 'ep_appeal_dispute',
|
|
296
|
+
description:
|
|
297
|
+
'Appeal a dispute resolution. Only dispute participants can appeal. ' +
|
|
298
|
+
'Requires the dispute to be in upheld, reversed, or dismissed state. ' +
|
|
299
|
+
'"Trust must never be more powerful than appeal."',
|
|
300
|
+
inputSchema: {
|
|
301
|
+
type: 'object',
|
|
302
|
+
properties: {
|
|
303
|
+
dispute_id: { type: 'string', description: 'The dispute ID to appeal' },
|
|
304
|
+
reason: { type: 'string', description: 'Why the resolution should be reconsidered (min 10 chars)' },
|
|
305
|
+
evidence: { type: 'object', description: 'Optional supporting evidence for the appeal' },
|
|
306
|
+
},
|
|
307
|
+
required: ['dispute_id', 'reason'],
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: 'ep_install_preflight',
|
|
165
312
|
description:
|
|
166
|
-
'
|
|
167
|
-
'
|
|
313
|
+
'EP-SX: Should I install this plugin/app/package/extension? ' +
|
|
314
|
+
'Evaluates a software entity against a software-specific trust policy with context. ' +
|
|
315
|
+
'Returns allow/review/deny with specific reasons covering publisher, permissions, provenance, and trust history.',
|
|
168
316
|
inputSchema: {
|
|
169
317
|
type: 'object',
|
|
170
318
|
properties: {
|
|
171
|
-
|
|
319
|
+
entity_id: { type: 'string', description: 'Software entity ID (e.g. github_app:acme/code-helper)' },
|
|
320
|
+
policy: {
|
|
172
321
|
type: 'string',
|
|
173
|
-
description: '
|
|
322
|
+
description: 'Software policy: github_private_repo_safe_v1, npm_buildtime_safe_v1, browser_extension_safe_v1, mcp_server_safe_v1, or standard EP policies',
|
|
174
323
|
},
|
|
175
|
-
|
|
176
|
-
type: '
|
|
177
|
-
|
|
178
|
-
description: 'Filter by entity type',
|
|
324
|
+
context: {
|
|
325
|
+
type: 'object',
|
|
326
|
+
description: 'Install context: { host, install_scope, permission_class, data_sensitivity, execution_mode }',
|
|
179
327
|
},
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
328
|
+
},
|
|
329
|
+
required: ['entity_id'],
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
// EP-IX Identity Continuity
|
|
333
|
+
{
|
|
334
|
+
name: 'ep_principal_lookup',
|
|
335
|
+
description: 'Look up a principal — the enduring actor behind entities. Returns bindings, controlled entities, and continuity history.',
|
|
336
|
+
inputSchema: {
|
|
337
|
+
type: 'object',
|
|
338
|
+
properties: {
|
|
339
|
+
principal_id: { type: 'string', description: 'Principal ID (e.g. ep_principal_abc)' },
|
|
340
|
+
},
|
|
341
|
+
required: ['principal_id'],
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
name: 'ep_lineage',
|
|
346
|
+
description: 'View entity lineage — predecessors, successors, continuity decisions, and whitewashing flags. Use to check if an entity has suspicious continuity gaps.',
|
|
347
|
+
inputSchema: {
|
|
348
|
+
type: 'object',
|
|
349
|
+
properties: {
|
|
350
|
+
entity_id: { type: 'string', description: 'Entity ID to check lineage for' },
|
|
351
|
+
},
|
|
352
|
+
required: ['entity_id'],
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
name: 'ep_list_policies',
|
|
357
|
+
description: 'List all available trust policies with their requirements and families. Use to discover which policy to evaluate against.',
|
|
358
|
+
inputSchema: { type: 'object', properties: {} },
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
// V1.0: Delegation
|
|
362
|
+
{
|
|
363
|
+
name: 'ep_create_delegation',
|
|
364
|
+
description:
|
|
365
|
+
'Create a delegation record: a human or principal authorizes an agent to act on their behalf. ' +
|
|
366
|
+
'The delegation is recorded in the EP ledger with scope, expiry, and optional constraints. ' +
|
|
367
|
+
'Every delegated action can reference this delegation to prove authorization.',
|
|
368
|
+
inputSchema: {
|
|
369
|
+
type: 'object',
|
|
370
|
+
properties: {
|
|
371
|
+
principal_id: { type: 'string', description: 'The principal (human/org) granting the delegation' },
|
|
372
|
+
agent_entity_id: { type: 'string', description: 'The agent entity being authorized' },
|
|
373
|
+
scope: { type: 'array', items: { type: 'string' }, description: 'List of permitted action types (e.g. ["purchase", "book", "send_email"])' },
|
|
374
|
+
max_value_usd: { type: 'number', description: 'Maximum transaction value in USD this delegation authorizes (optional)' },
|
|
375
|
+
expires_at: { type: 'string', description: 'ISO8601 expiry timestamp. If omitted, defaults to 24 hours.' },
|
|
376
|
+
constraints: { type: 'object', description: 'Additional constraints (geo, merchant_category, etc.)' },
|
|
377
|
+
},
|
|
378
|
+
required: ['principal_id', 'agent_entity_id', 'scope'],
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
// V1.0: Verify delegation
|
|
383
|
+
{
|
|
384
|
+
name: 'ep_verify_delegation',
|
|
385
|
+
description:
|
|
386
|
+
'Verify that an agent currently holds a valid delegation from a principal for a specific action. ' +
|
|
387
|
+
'Use this before accepting a task from an agent claiming to act on behalf of a human. ' +
|
|
388
|
+
'Returns: valid/expired/not_found with scope details.',
|
|
389
|
+
inputSchema: {
|
|
390
|
+
type: 'object',
|
|
391
|
+
properties: {
|
|
392
|
+
delegation_id: { type: 'string', description: 'Delegation ID (ep_dlg_...)' },
|
|
393
|
+
action_type: { type: 'string', description: 'The action type to check (must be in delegation scope)' },
|
|
394
|
+
},
|
|
395
|
+
required: ['delegation_id'],
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
|
|
399
|
+
// V1.0: Trust gate
|
|
400
|
+
{
|
|
401
|
+
name: 'ep_trust_gate',
|
|
402
|
+
description:
|
|
403
|
+
'Trust gate: check if an entity meets the required trust threshold BEFORE executing a high-stakes action. ' +
|
|
404
|
+
'This is the canonical pre-action check. Always call this before: payments, sending messages on behalf of users, ' +
|
|
405
|
+
'installing software, or any irreversible action. Returns allow/review/deny with reason.',
|
|
406
|
+
inputSchema: {
|
|
407
|
+
type: 'object',
|
|
408
|
+
properties: {
|
|
409
|
+
entity_id: { type: 'string', description: 'Entity requesting to perform the action' },
|
|
410
|
+
action: { type: 'string', description: 'Action being requested (e.g. "process_payment", "send_email", "install_package")' },
|
|
411
|
+
policy: { type: 'string', description: 'Policy to enforce: "strict", "standard", "permissive". Default: "standard"' },
|
|
412
|
+
value_usd: { type: 'number', description: 'Transaction value in USD (used for risk calibration)' },
|
|
413
|
+
delegation_id: { type: 'string', description: 'If agent is acting on behalf of a human, the delegation ID' },
|
|
414
|
+
},
|
|
415
|
+
required: ['entity_id', 'action'],
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
// V1.0: Batch receipt submission
|
|
420
|
+
{
|
|
421
|
+
name: 'ep_batch_submit',
|
|
422
|
+
description:
|
|
423
|
+
'Submit multiple receipts atomically. All receipts share the same submitter (your API key). ' +
|
|
424
|
+
'Useful for bulk reconciliation or recording a session of agent-entity interactions. ' +
|
|
425
|
+
'Returns per-receipt success/failure. Max 50 receipts per batch.',
|
|
426
|
+
inputSchema: {
|
|
427
|
+
type: 'object',
|
|
428
|
+
properties: {
|
|
429
|
+
receipts: {
|
|
430
|
+
type: 'array',
|
|
431
|
+
description: 'Array of receipt objects (same schema as ep_submit_receipt)',
|
|
432
|
+
items: {
|
|
433
|
+
type: 'object',
|
|
434
|
+
properties: {
|
|
435
|
+
entity_id: { type: 'string' },
|
|
436
|
+
transaction_ref: { type: 'string' },
|
|
437
|
+
transaction_type: { type: 'string' },
|
|
438
|
+
agent_behavior: { type: 'string' },
|
|
439
|
+
delivery_accuracy: { type: 'number' },
|
|
440
|
+
product_accuracy: { type: 'number' },
|
|
441
|
+
price_integrity: { type: 'number' },
|
|
442
|
+
return_processing: { type: 'number' },
|
|
443
|
+
},
|
|
444
|
+
required: ['entity_id', 'transaction_ref', 'transaction_type'],
|
|
445
|
+
},
|
|
183
446
|
},
|
|
184
447
|
},
|
|
185
|
-
required: ['
|
|
448
|
+
required: ['receipts'],
|
|
186
449
|
},
|
|
187
450
|
},
|
|
451
|
+
|
|
452
|
+
// Sprint 2: Auto-receipt configuration
|
|
188
453
|
{
|
|
189
|
-
name: '
|
|
454
|
+
name: 'ep_configure_auto_receipt',
|
|
190
455
|
description:
|
|
191
|
-
'
|
|
192
|
-
'
|
|
456
|
+
'Enable or disable automatic receipt generation for this session. ' +
|
|
457
|
+
'When enabled, every EP tool call generates a behavioral receipt automatically. ' +
|
|
458
|
+
'Privacy-preserving: sensitive fields (passwords, tokens, API keys) are redacted before storage. ' +
|
|
459
|
+
'Receipts are marked unilateral — they cannot be bilateral without counterparty confirmation. ' +
|
|
460
|
+
'Auto-receipt is opt-in and disabled by default.',
|
|
193
461
|
inputSchema: {
|
|
194
462
|
type: 'object',
|
|
195
463
|
properties: {
|
|
464
|
+
enabled: {
|
|
465
|
+
type: 'boolean',
|
|
466
|
+
description: 'Enable (true) or disable (false) auto-receipt generation for this session',
|
|
467
|
+
},
|
|
196
468
|
entity_id: {
|
|
197
469
|
type: 'string',
|
|
198
|
-
description: '
|
|
470
|
+
description: 'Entity ID to attribute auto-generated receipts to (your EP entity slug)',
|
|
199
471
|
},
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
472
|
+
},
|
|
473
|
+
required: ['enabled', 'entity_id'],
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
|
|
477
|
+
// V1.0: Domain score
|
|
478
|
+
{
|
|
479
|
+
name: 'ep_domain_score',
|
|
480
|
+
description:
|
|
481
|
+
'Get an entity\'s trust score broken down by behavioral domain. ' +
|
|
482
|
+
'Trust is not a scalar — an agent excellent at financial transactions may be unreliable at creative tasks. ' +
|
|
483
|
+
'Domains: financial, code_execution, communication, delegation, infrastructure, content_creation, data_access. ' +
|
|
484
|
+
'Returns per-domain confidence, evidence count, and behavioral rates.',
|
|
485
|
+
inputSchema: {
|
|
486
|
+
type: 'object',
|
|
487
|
+
properties: {
|
|
488
|
+
entity_id: { type: 'string', description: 'Entity to query' },
|
|
489
|
+
domains: {
|
|
490
|
+
type: 'array',
|
|
491
|
+
items: { type: 'string' },
|
|
492
|
+
description: 'Domains to query. If omitted, returns all domains.',
|
|
203
493
|
},
|
|
204
|
-
|
|
494
|
+
},
|
|
495
|
+
required: ['entity_id'],
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
|
|
499
|
+
// Sprint 5A: Privacy-Preserving Commitment Proof layer
|
|
500
|
+
{
|
|
501
|
+
name: 'ep_generate_zk_proof',
|
|
502
|
+
description:
|
|
503
|
+
'Generate a privacy-preserving commitment proof. Proves a trust claim (e.g., score > 0.85 in the ' +
|
|
504
|
+
'financial domain, or > 50 verified receipts) WITHOUT revealing receipt contents, counterparty ' +
|
|
505
|
+
'identities, or transaction details. Uses HMAC-SHA256 commitments + Merkle trees. ' +
|
|
506
|
+
'Returns a proof_id you can share publicly — verifiers call ep_verify_zk_proof with only the ' +
|
|
507
|
+
'proof_id and learn nothing about your transaction history. ' +
|
|
508
|
+
'Essential for privacy-sensitive contexts: healthcare (HIPAA), legal (privilege), ' +
|
|
509
|
+
'finance (NDA/MNPI), and any situation where sharing transaction history is not possible. ' +
|
|
510
|
+
'Requires your EP API key (you can only prove claims about your own entity).',
|
|
511
|
+
inputSchema: {
|
|
512
|
+
type: 'object',
|
|
513
|
+
properties: {
|
|
514
|
+
entity_id: {
|
|
205
515
|
type: 'string',
|
|
206
|
-
|
|
207
|
-
description: 'Type of entity',
|
|
516
|
+
description: 'Your EP entity ID. Must match the API key used to authenticate.',
|
|
208
517
|
},
|
|
209
|
-
|
|
518
|
+
claim_type: {
|
|
210
519
|
type: 'string',
|
|
211
|
-
|
|
520
|
+
enum: ['score_above', 'domain_score_above', 'receipt_count_above'],
|
|
521
|
+
description:
|
|
522
|
+
'score_above: global behavioral score > threshold. ' +
|
|
523
|
+
'domain_score_above: per-domain score > threshold (requires domain). ' +
|
|
524
|
+
'receipt_count_above: total receipts > threshold (integer count).',
|
|
212
525
|
},
|
|
213
|
-
|
|
214
|
-
type: '
|
|
215
|
-
|
|
216
|
-
|
|
526
|
+
threshold: {
|
|
527
|
+
type: 'number',
|
|
528
|
+
description:
|
|
529
|
+
'0.0–1.0 for score claims (e.g. 0.85 = 85th percentile behavioral score). ' +
|
|
530
|
+
'Positive integer for receipt_count_above (e.g. 50 = at least 50 receipts).',
|
|
217
531
|
},
|
|
218
|
-
|
|
532
|
+
domain: {
|
|
219
533
|
type: 'string',
|
|
220
|
-
|
|
534
|
+
enum: [
|
|
535
|
+
'financial', 'code_execution', 'communication', 'delegation',
|
|
536
|
+
'infrastructure', 'content_creation', 'data_access',
|
|
537
|
+
],
|
|
538
|
+
description: 'Required for domain_score_above claims. Which behavioral domain to prove.',
|
|
221
539
|
},
|
|
222
|
-
|
|
540
|
+
},
|
|
541
|
+
required: ['entity_id', 'claim_type', 'threshold'],
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
name: 'ep_verify_zk_proof',
|
|
546
|
+
description:
|
|
547
|
+
'Verify a privacy-preserving commitment proof by proof_id. ' +
|
|
548
|
+
'Returns whether the claim is currently valid — without revealing anything about the ' +
|
|
549
|
+
'entity\'s transaction history, counterparties, or receipt contents. ' +
|
|
550
|
+
'The proof holder shares only the proof_id. You verify without learning who they transacted with. ' +
|
|
551
|
+
'Use this to accept trust claims from entities in privacy-sensitive industries ' +
|
|
552
|
+
'(healthcare, legal, finance) who cannot share raw transaction history.',
|
|
553
|
+
inputSchema: {
|
|
554
|
+
type: 'object',
|
|
555
|
+
properties: {
|
|
556
|
+
proof_id: {
|
|
223
557
|
type: 'string',
|
|
224
|
-
description: '
|
|
558
|
+
description: 'The proof identifier (ep_zkp_...) shared by the proving entity.',
|
|
225
559
|
},
|
|
226
|
-
|
|
560
|
+
},
|
|
561
|
+
required: ['proof_id'],
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
|
|
565
|
+
// Sprint 4A: Delegation judgment
|
|
566
|
+
{
|
|
567
|
+
name: 'ep_delegation_judgment',
|
|
568
|
+
description:
|
|
569
|
+
'Get a principal\'s delegation authority — how well they choose and authorize agents. ' +
|
|
570
|
+
'High judgment principals consistently authorize well-behaved agents. ' +
|
|
571
|
+
'Low judgment principals frequently authorize agents that fail, dispute, or abandon tasks. ' +
|
|
572
|
+
'This signal is deliberately weak (0.15 weight per outcome): a single bad delegation should not ' +
|
|
573
|
+
'define a principal, but a pattern of them should be legible. ' +
|
|
574
|
+
'Returns: judgment_score (0–1), agents_authorized, good_outcome_rate, and signal counts.',
|
|
575
|
+
inputSchema: {
|
|
576
|
+
type: 'object',
|
|
577
|
+
properties: {
|
|
578
|
+
principal_id: {
|
|
227
579
|
type: 'string',
|
|
228
|
-
description: '
|
|
580
|
+
description: 'Principal entity ID or human identifier (e.g. ep_principal_abc or user@example.com)',
|
|
229
581
|
},
|
|
230
582
|
},
|
|
231
|
-
required: ['
|
|
583
|
+
required: ['principal_id'],
|
|
232
584
|
},
|
|
233
585
|
},
|
|
586
|
+
|
|
587
|
+
// EP Commit — signed authorization token for high-stakes machine actions
|
|
234
588
|
{
|
|
235
|
-
name: '
|
|
589
|
+
name: 'ep_issue_commit',
|
|
236
590
|
description:
|
|
237
|
-
'
|
|
238
|
-
'Returns
|
|
591
|
+
'Issue a signed EP Commit before a high-stakes action. ' +
|
|
592
|
+
'Returns a commit_id, decision (allow/deny/review), expiry, scope, and appeal path. ' +
|
|
593
|
+
'The commit binds the agent to a specific action type, entity, and policy before execution.',
|
|
239
594
|
inputSchema: {
|
|
240
595
|
type: 'object',
|
|
241
596
|
properties: {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
597
|
+
action_type: { type: 'string', description: 'Action type: install, connect, delegate, transact' },
|
|
598
|
+
entity_id: { type: 'string', description: 'Entity requesting the commit' },
|
|
599
|
+
principal_id: { type: 'string', description: 'Principal authorizing the action (optional)' },
|
|
600
|
+
counterparty_entity_id: { type: 'string', description: 'Counterparty entity ID (optional)' },
|
|
601
|
+
delegation_id: { type: 'string', description: 'Delegation ID if acting under delegation (optional)' },
|
|
602
|
+
scope: { type: 'array', items: { type: 'string' }, description: 'Action scope list (optional)' },
|
|
603
|
+
max_value_usd: { type: 'number', description: 'Maximum value in USD (optional)' },
|
|
604
|
+
context: { type: 'object', description: 'Additional context metadata (optional)' },
|
|
605
|
+
policy: { type: 'string', description: 'Trust policy to enforce (default: standard)' },
|
|
606
|
+
},
|
|
607
|
+
required: ['action_type', 'entity_id'],
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
name: 'ep_verify_commit',
|
|
612
|
+
description:
|
|
613
|
+
'Verify a commit\'s signature, status, and validity. ' +
|
|
614
|
+
'Returns valid/invalid, current status, decision, and expiry.',
|
|
615
|
+
inputSchema: {
|
|
616
|
+
type: 'object',
|
|
617
|
+
properties: {
|
|
618
|
+
commit_id: { type: 'string', description: 'Commit ID (epc_...)' },
|
|
619
|
+
},
|
|
620
|
+
required: ['commit_id'],
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
name: 'ep_get_commit_status',
|
|
625
|
+
description: 'Get the current state of a commit (active, revoked, expired, fulfilled).',
|
|
626
|
+
inputSchema: {
|
|
627
|
+
type: 'object',
|
|
628
|
+
properties: {
|
|
629
|
+
commit_id: { type: 'string', description: 'Commit ID (epc_...)' },
|
|
630
|
+
},
|
|
631
|
+
required: ['commit_id'],
|
|
632
|
+
},
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
name: 'ep_revoke_commit',
|
|
636
|
+
description: 'Revoke an active commit before it is fulfilled or expires.',
|
|
637
|
+
inputSchema: {
|
|
638
|
+
type: 'object',
|
|
639
|
+
properties: {
|
|
640
|
+
commit_id: { type: 'string', description: 'Commit ID to revoke (epc_...)' },
|
|
641
|
+
reason: { type: 'string', description: 'Reason for revocation' },
|
|
642
|
+
},
|
|
643
|
+
required: ['commit_id', 'reason'],
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
name: 'ep_bind_receipt_to_commit',
|
|
648
|
+
description:
|
|
649
|
+
'Bind a post-action receipt to a commit, completing the commit-execute-receipt cycle. ' +
|
|
650
|
+
'Links the behavioral outcome back to the signed authorization token.',
|
|
651
|
+
inputSchema: {
|
|
652
|
+
type: 'object',
|
|
653
|
+
properties: {
|
|
654
|
+
commit_id: { type: 'string', description: 'Commit ID to bind to (epc_...)' },
|
|
655
|
+
receipt_id: { type: 'string', description: 'Receipt ID to bind (ep_rcpt_...)' },
|
|
656
|
+
},
|
|
657
|
+
required: ['commit_id', 'receipt_id'],
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
|
|
661
|
+
// EP Handshake — structured identity exchange
|
|
662
|
+
{
|
|
663
|
+
name: 'ep_initiate_handshake',
|
|
664
|
+
description:
|
|
665
|
+
'Initiate an EP Handshake — a structured identity exchange between parties. ' +
|
|
666
|
+
'The handshake coordinates mutual presentation of identity proofs before a trust decision. ' +
|
|
667
|
+
'Requires at least 2 parties and a governing trust policy.',
|
|
668
|
+
inputSchema: {
|
|
669
|
+
type: 'object',
|
|
670
|
+
properties: {
|
|
671
|
+
mode: { type: 'string', description: 'Handshake mode: "mutual", "one-way", "delegated"' },
|
|
672
|
+
policy_id: { type: 'string', description: 'Trust policy governing the handshake' },
|
|
673
|
+
parties: {
|
|
674
|
+
type: 'array',
|
|
675
|
+
description: 'Parties in the handshake (min 2). Each: { entity_ref, role }',
|
|
676
|
+
items: {
|
|
677
|
+
type: 'object',
|
|
678
|
+
properties: {
|
|
679
|
+
entity_ref: { type: 'string' },
|
|
680
|
+
role: { type: 'string' },
|
|
681
|
+
},
|
|
682
|
+
required: ['entity_ref', 'role'],
|
|
683
|
+
},
|
|
250
684
|
},
|
|
685
|
+
binding: { type: 'object', description: 'Optional binding constraints' },
|
|
686
|
+
interaction_id: { type: 'string', description: 'Optional external interaction reference' },
|
|
687
|
+
},
|
|
688
|
+
required: ['mode', 'policy_id', 'parties'],
|
|
689
|
+
},
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
name: 'ep_add_presentation',
|
|
693
|
+
description:
|
|
694
|
+
'Add an identity presentation (proof) to an active handshake. ' +
|
|
695
|
+
'Each party presents their identity claims for evaluation against the handshake policy. ' +
|
|
696
|
+
'Supports full, selective, or zero-knowledge disclosure modes.',
|
|
697
|
+
inputSchema: {
|
|
698
|
+
type: 'object',
|
|
699
|
+
properties: {
|
|
700
|
+
handshake_id: { type: 'string', description: 'Handshake ID to present to' },
|
|
701
|
+
party_role: { type: 'string', description: 'Role of the presenting party' },
|
|
702
|
+
presentation_type: { type: 'string', description: 'Type: "verifiable_credential", "ep_trust_profile", "attestation"' },
|
|
703
|
+
issuer_ref: { type: 'string', description: 'Optional credential issuer reference' },
|
|
704
|
+
claims: { type: 'object', description: 'Identity claims being presented' },
|
|
705
|
+
disclosure_mode: { type: 'string', description: 'Disclosure mode: "full", "selective", "zk"' },
|
|
706
|
+
},
|
|
707
|
+
required: ['handshake_id', 'party_role', 'presentation_type', 'claims'],
|
|
708
|
+
},
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
name: 'ep_verify_handshake',
|
|
712
|
+
description:
|
|
713
|
+
'Evaluate all presentations in a handshake against the governing policy. ' +
|
|
714
|
+
'Returns: accepted (all requirements met), rejected (policy violations), or partial (awaiting presentations). ' +
|
|
715
|
+
'Includes reason_codes explaining the outcome.',
|
|
716
|
+
inputSchema: {
|
|
717
|
+
type: 'object',
|
|
718
|
+
properties: {
|
|
719
|
+
handshake_id: { type: 'string', description: 'Handshake ID to verify' },
|
|
720
|
+
},
|
|
721
|
+
required: ['handshake_id'],
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
name: 'ep_get_handshake',
|
|
726
|
+
description:
|
|
727
|
+
'Get the full state of a handshake including parties, presentations, binding, and result. ' +
|
|
728
|
+
'Use this to check handshake progress or review completed exchanges.',
|
|
729
|
+
inputSchema: {
|
|
730
|
+
type: 'object',
|
|
731
|
+
properties: {
|
|
732
|
+
handshake_id: { type: 'string', description: 'Handshake ID to retrieve' },
|
|
733
|
+
},
|
|
734
|
+
required: ['handshake_id'],
|
|
735
|
+
},
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
name: 'ep_revoke_handshake',
|
|
739
|
+
description:
|
|
740
|
+
'Revoke an active handshake. Only parties to the handshake may revoke it. ' +
|
|
741
|
+
'Revocation is terminal — the handshake cannot be reopened.',
|
|
742
|
+
inputSchema: {
|
|
743
|
+
type: 'object',
|
|
744
|
+
properties: {
|
|
745
|
+
handshake_id: { type: 'string', description: 'Handshake ID to revoke' },
|
|
746
|
+
reason: { type: 'string', description: 'Reason for revocation' },
|
|
251
747
|
},
|
|
748
|
+
required: ['handshake_id', 'reason'],
|
|
252
749
|
},
|
|
253
750
|
},
|
|
254
751
|
];
|
|
@@ -259,73 +756,582 @@ const TOOLS = [
|
|
|
259
756
|
|
|
260
757
|
async function handleTool(name, args) {
|
|
261
758
|
switch (name) {
|
|
262
|
-
case '
|
|
263
|
-
const data = await epFetch(`/api/
|
|
264
|
-
return
|
|
759
|
+
case 'ep_trust_profile': {
|
|
760
|
+
const data = await epFetch(`/api/trust/profile/${encodeURIComponent(args.entity_id)}`);
|
|
761
|
+
return formatTrustProfile(data);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
case 'ep_trust_evaluate': {
|
|
765
|
+
const body = { entity_id: args.entity_id, policy: args.policy || 'standard' };
|
|
766
|
+
if (args.context) body.context = args.context;
|
|
767
|
+
const data = await epFetch('/api/trust/evaluate', { method: 'POST', body });
|
|
768
|
+
return formatEvaluation(data);
|
|
265
769
|
}
|
|
266
770
|
|
|
267
771
|
case 'ep_submit_receipt': {
|
|
268
|
-
if (!API_KEY)
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
const data = await epFetch('/api/receipts/submit', {
|
|
272
|
-
method: 'POST',
|
|
273
|
-
auth: true,
|
|
274
|
-
body: {
|
|
275
|
-
entity_id: args.entity_id,
|
|
276
|
-
transaction_type: args.transaction_type,
|
|
277
|
-
transaction_ref: args.transaction_ref,
|
|
278
|
-
delivery_accuracy: args.delivery_accuracy,
|
|
279
|
-
product_accuracy: args.product_accuracy,
|
|
280
|
-
price_integrity: args.price_integrity,
|
|
281
|
-
return_processing: args.return_processing,
|
|
282
|
-
agent_satisfaction: args.agent_satisfaction,
|
|
283
|
-
evidence: args.evidence,
|
|
284
|
-
},
|
|
285
|
-
});
|
|
772
|
+
if (!API_KEY) return 'Error: EP_API_KEY required. Set it in MCP server config.';
|
|
773
|
+
const body = { ...args };
|
|
774
|
+
const data = await epFetch('/api/receipts/submit', { method: 'POST', auth: true, body });
|
|
286
775
|
return `Receipt submitted.\n` +
|
|
287
|
-
`
|
|
288
|
-
`Composite Score: ${data.receipt.composite_score}\n` +
|
|
776
|
+
`ID: ${data.receipt.receipt_id}\n` +
|
|
289
777
|
`Hash: ${data.receipt.receipt_hash}\n` +
|
|
290
|
-
`Entity
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
case 'ep_verify_receipt': {
|
|
294
|
-
const data = await epFetch(`/api/verify/${encodeURIComponent(args.receipt_id)}`);
|
|
295
|
-
return formatVerification(data);
|
|
778
|
+
`Entity trust profile updated. Query with ep_trust_profile for current state.`;
|
|
296
779
|
}
|
|
297
780
|
|
|
298
781
|
case 'ep_search_entities': {
|
|
299
782
|
const params = new URLSearchParams({ q: args.query });
|
|
300
783
|
if (args.entity_type) params.set('type', args.entity_type);
|
|
301
|
-
if (args.min_score) params.set('min_score', args.min_score.toString());
|
|
302
784
|
const data = await epFetch(`/api/entities/search?${params}`);
|
|
303
|
-
|
|
785
|
+
const entities = data.entities || data.results || [];
|
|
786
|
+
if (!entities.length) return 'No entities found.';
|
|
787
|
+
return entities.map(e => {
|
|
788
|
+
const conf = e.confidence || 'pending';
|
|
789
|
+
const ee = e.effective_evidence != null ? ` · evidence: ${e.effective_evidence.toFixed(2)}` : '';
|
|
790
|
+
return `${e.display_name} (${e.entity_id})\n confidence: ${conf}${ee} · trust profile available`;
|
|
791
|
+
}).join('\n\n');
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
case 'ep_verify_receipt': {
|
|
795
|
+
const data = await epFetch(`/api/verify/${encodeURIComponent(args.receipt_id)}`);
|
|
796
|
+
return `Receipt: ${data.receipt_id}\nHash: ${data.receipt_hash}\nAnchored: ${data.anchored ? 'Yes' : 'No'}\nVerified: ${data.verified ? 'YES' : 'FAILED'}`;
|
|
304
797
|
}
|
|
305
798
|
|
|
306
799
|
case 'ep_register_entity': {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
const data = await epFetch('/api/entities/register', {
|
|
311
|
-
method: 'POST',
|
|
312
|
-
auth: true,
|
|
313
|
-
body: args,
|
|
314
|
-
});
|
|
315
|
-
return `Entity registered!\n` +
|
|
316
|
-
`Entity ID: ${data.entity.entity_id}\n` +
|
|
317
|
-
`Entity #: ${data.entity.entity_number || 'N/A'}\n` +
|
|
318
|
-
`EMILIA Score: ${data.entity.emilia_score} (new entity — score starts at 50)\n` +
|
|
319
|
-
`API Key: ${data.api_key}\n\n` +
|
|
320
|
-
`⚠️ Save this API key — it won't be shown again.`;
|
|
800
|
+
const data = await epFetch('/api/entities/register', { method: 'POST', body: args });
|
|
801
|
+
return `Registered: ${data.entity.entity_id}\nAPI Key: ${data.api_key}\n⚠️ Save this key — it won't be shown again.`;
|
|
321
802
|
}
|
|
322
803
|
|
|
323
804
|
case 'ep_leaderboard': {
|
|
324
805
|
const params = new URLSearchParams();
|
|
325
|
-
if (args?.limit) params.set('limit', Math.min(args.limit, 50)
|
|
806
|
+
if (args?.limit) params.set('limit', String(Math.min(args.limit, 50)));
|
|
326
807
|
if (args?.entity_type) params.set('type', args.entity_type);
|
|
327
808
|
const data = await epFetch(`/api/leaderboard?${params}`);
|
|
328
|
-
|
|
809
|
+
const lb = data.leaderboard || [];
|
|
810
|
+
if (!lb.length) return 'No entities in leaderboard yet.';
|
|
811
|
+
return lb.map(e => {
|
|
812
|
+
const conf = e.confidence || 'pending';
|
|
813
|
+
return `#${e.rank} ${e.display_name} (${e.entity_id})\n confidence: ${conf} · trust profile available`;
|
|
814
|
+
}).join('\n\n');
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
case 'ep_dispute_file': {
|
|
818
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to file disputes.';
|
|
819
|
+
const body = {
|
|
820
|
+
receipt_id: args.receipt_id,
|
|
821
|
+
reason: args.reason,
|
|
822
|
+
description: args.description || null,
|
|
823
|
+
evidence: args.evidence || null,
|
|
824
|
+
};
|
|
825
|
+
const data = await epFetch('/api/disputes/file', { method: 'POST', auth: true, body });
|
|
826
|
+
return `Dispute filed.\n` +
|
|
827
|
+
`Dispute ID: ${data.dispute_id}\n` +
|
|
828
|
+
`Receipt: ${data.receipt_id}\n` +
|
|
829
|
+
`Status: ${data.status}\n` +
|
|
830
|
+
`Response deadline: ${data.response_deadline}\n` +
|
|
831
|
+
`${data._message}`;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
case 'ep_dispute_status': {
|
|
835
|
+
const data = await epFetch(`/api/disputes/${encodeURIComponent(args.dispute_id)}`);
|
|
836
|
+
let out = `Dispute: ${data.dispute_id}\n`;
|
|
837
|
+
out += `Status: ${data.status}\n`;
|
|
838
|
+
out += `Reason: ${data.reason}\n`;
|
|
839
|
+
out += `Entity: ${data.entity?.display_name} (${data.entity?.entity_id})\n`;
|
|
840
|
+
out += `Filed by: ${data.filed_by?.display_name} (${data.filed_by_type})\n`;
|
|
841
|
+
if (data.response) out += `Response: ${data.response}\n`;
|
|
842
|
+
if (data.resolution) out += `Resolution: ${data.resolution}\nRationale: ${data.resolution_rationale}\n`;
|
|
843
|
+
return out;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
case 'ep_report_trust_issue': {
|
|
847
|
+
const body = {
|
|
848
|
+
entity_id: args.entity_id,
|
|
849
|
+
report_type: args.report_type,
|
|
850
|
+
description: args.description,
|
|
851
|
+
contact_email: args.contact_email || null,
|
|
852
|
+
};
|
|
853
|
+
const data = await epFetch('/api/disputes/report', { method: 'POST', body });
|
|
854
|
+
return `Report filed.\n` +
|
|
855
|
+
`Report ID: ${data.report_id}\n` +
|
|
856
|
+
`${data._message}\n` +
|
|
857
|
+
`${data._principle}`;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
case 'ep_appeal_dispute': {
|
|
861
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to file appeals.';
|
|
862
|
+
const body = {
|
|
863
|
+
dispute_id: args.dispute_id,
|
|
864
|
+
reason: args.reason,
|
|
865
|
+
evidence: args.evidence || null,
|
|
866
|
+
};
|
|
867
|
+
const data = await epFetch('/api/disputes/appeal', { method: 'POST', auth: true, body });
|
|
868
|
+
return `Appeal filed.\n` +
|
|
869
|
+
`Appeal ID: ${data.appeal_id || data.dispute_id}\n` +
|
|
870
|
+
`Status: ${data.status}\n` +
|
|
871
|
+
`${data._message || 'Your appeal has been submitted for review.'}`;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
case 'ep_install_preflight': {
|
|
875
|
+
const body = { entity_id: args.entity_id, policy: args.policy || 'standard' };
|
|
876
|
+
if (args.context) body.context = args.context;
|
|
877
|
+
const data = await epFetch('/api/trust/install-preflight', { method: 'POST', body });
|
|
878
|
+
let out = `Pre-Action Enforcement: ${data.display_name} (${data.entity_id})\n`;
|
|
879
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
880
|
+
out += `Decision: ${data.decision === 'allow' ? '✓ ALLOW' : data.decision === 'deny' ? '✗ DENY' : '⚠ REVIEW'}\n`;
|
|
881
|
+
out += `Policy: ${data.policy_used}\n`;
|
|
882
|
+
out += `Confidence: ${data.confidence}\n\n`;
|
|
883
|
+
if (data.reasons?.length) {
|
|
884
|
+
out += `Reasons:\n`;
|
|
885
|
+
for (const r of data.reasons) out += ` ${r}\n`;
|
|
886
|
+
}
|
|
887
|
+
if (data.software_meta) {
|
|
888
|
+
out += `\nSoftware:\n`;
|
|
889
|
+
out += ` Publisher verified: ${data.software_meta.publisher_verified}\n`;
|
|
890
|
+
out += ` Provenance verified: ${data.software_meta.provenance_verified}\n`;
|
|
891
|
+
out += ` Permission class: ${data.software_meta.permission_class || 'unknown'}\n`;
|
|
892
|
+
}
|
|
893
|
+
out += `\n(Legacy compatibility score (fallback only): ${data.score}/100)\n`;
|
|
894
|
+
return out;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
case 'ep_principal_lookup': {
|
|
898
|
+
const data = await epFetch(`/api/identity/principal/${encodeURIComponent(args.principal_id)}`);
|
|
899
|
+
if (data.error) return `Principal not found: ${args.principal_id}`;
|
|
900
|
+
const p = data.principal;
|
|
901
|
+
let out = `Principal: ${p.display_name} (${p.principal_id})\n`;
|
|
902
|
+
out += `Type: ${p.principal_type} · Status: ${p.status}\n`;
|
|
903
|
+
if (p.bootstrap_verified) out += `Bootstrap verified: yes\n`;
|
|
904
|
+
if (data.entities?.length) {
|
|
905
|
+
out += `\nControlled entities (${data.entities.length}):\n`;
|
|
906
|
+
for (const e of data.entities) out += ` ${e.display_name} (${e.entity_id}) — ${e.entity_type}\n`;
|
|
907
|
+
}
|
|
908
|
+
if (data.bindings?.length) {
|
|
909
|
+
out += `\nIdentity bindings (${data.bindings.length}):\n`;
|
|
910
|
+
for (const b of data.bindings) out += ` ${b.binding_type}: ${b.binding_target} [${b.status}] provenance: ${b.provenance}\n`;
|
|
911
|
+
}
|
|
912
|
+
if (data.continuity_claims?.length) {
|
|
913
|
+
out += `\nContinuity history (${data.continuity_claims.length}):\n`;
|
|
914
|
+
for (const c of data.continuity_claims) out += ` ${c.old_entity_id} → ${c.new_entity_id} (${c.reason}) [${c.status}]\n`;
|
|
915
|
+
}
|
|
916
|
+
return out;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
case 'ep_lineage': {
|
|
920
|
+
const data = await epFetch(`/api/identity/lineage/${encodeURIComponent(args.entity_id)}`);
|
|
921
|
+
let out = `Lineage: ${data.entity_id}\n`;
|
|
922
|
+
if (data.predecessors?.length) {
|
|
923
|
+
out += `\nPredecessors:\n`;
|
|
924
|
+
for (const p of data.predecessors) out += ` ← ${p.from} (${p.reason}) [${p.status}] transfer: ${p.transfer_policy || 'pending'}\n`;
|
|
925
|
+
} else {
|
|
926
|
+
out += `\nNo predecessors — this is an original entity.\n`;
|
|
927
|
+
}
|
|
928
|
+
if (data.successors?.length) {
|
|
929
|
+
out += `\nSuccessors:\n`;
|
|
930
|
+
for (const s of data.successors) out += ` → ${s.to} (${s.reason}) [${s.status}] transfer: ${s.transfer_policy || 'pending'}\n`;
|
|
931
|
+
} else {
|
|
932
|
+
out += `No successors.\n`;
|
|
933
|
+
}
|
|
934
|
+
return out;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
case 'ep_list_policies': {
|
|
938
|
+
const data = await epFetch('/api/policies');
|
|
939
|
+
let out = `Available Trust Policies (${data.policies.length})\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
940
|
+
for (const p of data.policies) {
|
|
941
|
+
out += `\n${p.name} [${p.family}]\n`;
|
|
942
|
+
out += ` ${p.description}\n`;
|
|
943
|
+
if (p.min_confidence) out += ` min confidence: ${p.min_confidence}\n`;
|
|
944
|
+
}
|
|
945
|
+
out += `\nUse ep_trust_evaluate with a policy name to evaluate an entity.`;
|
|
946
|
+
return out;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
case 'ep_create_delegation': {
|
|
950
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to create delegations.';
|
|
951
|
+
const body = {
|
|
952
|
+
principal_id: args.principal_id,
|
|
953
|
+
agent_entity_id: args.agent_entity_id,
|
|
954
|
+
scope: args.scope,
|
|
955
|
+
max_value_usd: args.max_value_usd || null,
|
|
956
|
+
expires_at: args.expires_at || null,
|
|
957
|
+
constraints: args.constraints || null,
|
|
958
|
+
};
|
|
959
|
+
const data = await epFetch('/api/delegations/create', { method: 'POST', auth: true, body });
|
|
960
|
+
return `Delegation created.\n` +
|
|
961
|
+
`ID: ${data.delegation_id}\n` +
|
|
962
|
+
`Principal: ${data.principal_id}\n` +
|
|
963
|
+
`Agent: ${data.agent_entity_id}\n` +
|
|
964
|
+
`Scope: ${typeof data.scope === 'object' ? JSON.stringify(data.scope) : String(data.scope)}\n` +
|
|
965
|
+
`Expires: ${data.expires_at}\n` +
|
|
966
|
+
`Status: ${data.status}`;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
case 'ep_verify_delegation': {
|
|
970
|
+
const params = new URLSearchParams();
|
|
971
|
+
if (args.action_type) params.set('action_type', args.action_type);
|
|
972
|
+
const data = await epFetch(`/api/delegations/${encodeURIComponent(args.delegation_id)}/verify?${params}`);
|
|
973
|
+
let out = `Delegation: ${data.delegation_id}\n`;
|
|
974
|
+
out += `Status: ${data.valid ? '✓ VALID' : '✗ ' + (data.status || 'INVALID')}\n`;
|
|
975
|
+
if (data.principal_id) out += `Principal: ${data.principal_id}\n`;
|
|
976
|
+
if (data.agent_entity_id) out += `Agent: ${data.agent_entity_id}\n`;
|
|
977
|
+
if (data.scope) out += `Scope: ${typeof data.scope === 'object' ? JSON.stringify(data.scope) : String(data.scope)}\n`;
|
|
978
|
+
if (data.expires_at) out += `Expires: ${data.expires_at}\n`;
|
|
979
|
+
if (data.action_type && data.action_permitted != null) {
|
|
980
|
+
out += `Action "${data.action_type}": ${data.action_permitted ? '✓ Permitted' : '✗ Not in scope'}\n`;
|
|
981
|
+
}
|
|
982
|
+
if (data.reason) out += `Reason: ${data.reason}\n`;
|
|
983
|
+
return out;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
case 'ep_trust_gate': {
|
|
987
|
+
const body = {
|
|
988
|
+
entity_id: args.entity_id,
|
|
989
|
+
action: args.action,
|
|
990
|
+
policy: args.policy || 'standard',
|
|
991
|
+
value_usd: args.value_usd || null,
|
|
992
|
+
delegation_id: args.delegation_id || null,
|
|
993
|
+
};
|
|
994
|
+
const data = await epFetch('/api/trust/gate', { method: 'POST', body });
|
|
995
|
+
let out = `Trust Gate: ${args.action}\n`;
|
|
996
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
997
|
+
out += `Entity: ${data.entity_id}\n`;
|
|
998
|
+
out += `Decision: ${data.decision === 'allow' ? '✓ ALLOW' : data.decision === 'review' ? '⚠ REVIEW' : '✗ DENY'}\n`;
|
|
999
|
+
out += `Policy: ${data.policy_used}\n`;
|
|
1000
|
+
out += `Confidence: ${data.confidence}\n`;
|
|
1001
|
+
if (data.delegation_verified != null) out += `Delegation: ${data.delegation_verified ? '✓ Verified' : '✗ Not verified'}\n`;
|
|
1002
|
+
if (data.reasons?.length) {
|
|
1003
|
+
out += `\nReasons:\n`;
|
|
1004
|
+
for (const r of data.reasons) out += ` ${data.decision === 'allow' ? '✓' : '✗'} ${r}\n`;
|
|
1005
|
+
}
|
|
1006
|
+
if (data.decision === 'deny' && data.appeal_path) {
|
|
1007
|
+
out += `\nAppeal path: ${data.appeal_path}\n`;
|
|
1008
|
+
out += `Trust must never be more powerful than appeal.\n`;
|
|
1009
|
+
}
|
|
1010
|
+
return out;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
case 'ep_batch_submit': {
|
|
1014
|
+
if (!API_KEY) return 'Error: EP_API_KEY required for batch submission.';
|
|
1015
|
+
const receipts = (args.receipts || []).slice(0, 50);
|
|
1016
|
+
if (!receipts.length) return 'Error: No receipts provided.';
|
|
1017
|
+
const data = await epFetch('/api/receipts/batch', { method: 'POST', auth: true, body: { receipts } });
|
|
1018
|
+
const results = data.results || [];
|
|
1019
|
+
const succeeded = results.filter(r => r.success).length;
|
|
1020
|
+
const failed = results.filter(r => !r.success).length;
|
|
1021
|
+
let out = `Batch submission: ${succeeded} succeeded, ${failed} failed\n`;
|
|
1022
|
+
for (const r of results) {
|
|
1023
|
+
out += r.success
|
|
1024
|
+
? ` ✓ ${r.entity_id} — ${r.receipt_id}\n`
|
|
1025
|
+
: ` ✗ ${r.entity_id} — ${r.error}\n`;
|
|
1026
|
+
}
|
|
1027
|
+
return out;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
case 'ep_domain_score': {
|
|
1031
|
+
const params = new URLSearchParams();
|
|
1032
|
+
if (args.domains?.length) params.set('domains', args.domains.join(','));
|
|
1033
|
+
const data = await epFetch(`/api/trust/domain-score/${encodeURIComponent(args.entity_id)}?${params}`);
|
|
1034
|
+
let out = `Domain Scores: ${data.entity_id}\n`;
|
|
1035
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
1036
|
+
const domains = data.domains || {};
|
|
1037
|
+
for (const [domain, score] of Object.entries(domains)) {
|
|
1038
|
+
out += `\n${domain}:\n`;
|
|
1039
|
+
out += ` Confidence: ${score.confidence ?? 'pending'}\n`;
|
|
1040
|
+
out += ` Evidence: ${score.evidence_count ?? 0} receipts\n`;
|
|
1041
|
+
if (score.completion_rate != null) out += ` Completion: ${score.completion_rate}%\n`;
|
|
1042
|
+
if (score.dispute_rate != null) out += ` Dispute rate: ${score.dispute_rate}%\n`;
|
|
1043
|
+
}
|
|
1044
|
+
if (!Object.keys(domains).length) out += 'No domain data available yet.\n';
|
|
1045
|
+
return out;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
case 'ep_configure_auto_receipt': {
|
|
1049
|
+
const previousState = autoReceipt.optIn;
|
|
1050
|
+
autoReceipt.configure(args.enabled, args.entity_id);
|
|
1051
|
+
const stateChanged = previousState !== autoReceipt.optIn;
|
|
1052
|
+
if (args.enabled) {
|
|
1053
|
+
return (
|
|
1054
|
+
`Auto-receipt enabled for entity: ${args.entity_id}\n` +
|
|
1055
|
+
`Every EP tool call will now generate a behavioral receipt automatically.\n` +
|
|
1056
|
+
`Receipts are privacy-preserving (sensitive fields redacted) and marked unilateral.\n` +
|
|
1057
|
+
`${stateChanged ? 'Status changed from disabled → enabled.' : 'Was already enabled.'}\n` +
|
|
1058
|
+
`To disable: call ep_configure_auto_receipt with enabled: false.`
|
|
1059
|
+
);
|
|
1060
|
+
} else {
|
|
1061
|
+
return (
|
|
1062
|
+
`Auto-receipt disabled.\n` +
|
|
1063
|
+
`${stateChanged ? 'Status changed from enabled → disabled.' : 'Was already disabled.'}\n` +
|
|
1064
|
+
`No further automatic receipts will be generated for this session.`
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
case 'ep_delegation_judgment': {
|
|
1070
|
+
const data = await epFetch(`/api/attribution/delegation-judgment/${encodeURIComponent(args.principal_id)}`);
|
|
1071
|
+
let out = `Delegation Authority: ${args.principal_id}\n`;
|
|
1072
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
1073
|
+
if (data.judgment_score == null) {
|
|
1074
|
+
out += `Score: pending (no delegation signals yet)\n`;
|
|
1075
|
+
} else {
|
|
1076
|
+
const pct = Math.round(data.judgment_score * 100);
|
|
1077
|
+
const label = pct >= 80 ? 'High' : pct >= 50 ? 'Moderate' : 'Low';
|
|
1078
|
+
out += `Score: ${pct}/100 (${label} judgment)\n`;
|
|
1079
|
+
}
|
|
1080
|
+
out += `Agents authorized: ${data.agents_authorized ?? 0}\n`;
|
|
1081
|
+
if (data.good_outcome_rate != null) {
|
|
1082
|
+
out += `Good outcome rate: ${Math.round(data.good_outcome_rate * 100)}%\n`;
|
|
1083
|
+
}
|
|
1084
|
+
out += `Total signals: ${data.total_signals ?? 0}`;
|
|
1085
|
+
if (data.total_signals > 0) {
|
|
1086
|
+
out += ` (${data.positive_signals} positive, ${data.negative_signals} negative)`;
|
|
1087
|
+
}
|
|
1088
|
+
out += `\n`;
|
|
1089
|
+
out += `\nSignal weight: 0.15 per delegation outcome (weak signal by design).\n`;
|
|
1090
|
+
out += `A pattern of low judgment signals that a principal repeatedly authorizes misbehaving agents.`;
|
|
1091
|
+
return out;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
case 'ep_generate_zk_proof': {
|
|
1095
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to generate commitment proofs.';
|
|
1096
|
+
const body = {
|
|
1097
|
+
entity_id: args.entity_id,
|
|
1098
|
+
claim: {
|
|
1099
|
+
type: args.claim_type,
|
|
1100
|
+
threshold: args.threshold,
|
|
1101
|
+
...(args.domain ? { domain: args.domain } : {}),
|
|
1102
|
+
},
|
|
1103
|
+
};
|
|
1104
|
+
let data;
|
|
1105
|
+
try {
|
|
1106
|
+
data = await epFetch('/api/trust/zk-proof', { method: 'POST', auth: true, body });
|
|
1107
|
+
} catch (err) {
|
|
1108
|
+
if (err.message?.includes('CLAIM_NOT_PROVABLE') || err.message?.includes('claim_not_provable')) {
|
|
1109
|
+
return (
|
|
1110
|
+
`Commitment Proof: Claim Not Provable\n` +
|
|
1111
|
+
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n` +
|
|
1112
|
+
`Your current trust data does not meet the specified threshold.\n` +
|
|
1113
|
+
`Claim: ${args.claim_type} > ${args.threshold}` +
|
|
1114
|
+
(args.domain ? ` in ${args.domain}` : '') + `\n` +
|
|
1115
|
+
`Accumulate more receipts or lower the threshold and try again.`
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
throw err;
|
|
1119
|
+
}
|
|
1120
|
+
let out = `Commitment Proof Generated\n`;
|
|
1121
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
1122
|
+
out += `Proof ID: ${data.proof_id}\n`;
|
|
1123
|
+
out += `Entity: ${data.entity_id}\n`;
|
|
1124
|
+
out += `Claim: ${data.claim.type} > ${data.claim.threshold}`;
|
|
1125
|
+
if (data.claim.domain) out += ` (${data.claim.domain})`;
|
|
1126
|
+
out += `\n`;
|
|
1127
|
+
out += `Receipts: ${data.receipt_count} (hidden from verifiers)\n`;
|
|
1128
|
+
out += `Expires: ${data.expires_at}\n`;
|
|
1129
|
+
if (data.anchor_block) out += `Anchor: ${data.anchor_block}\n`;
|
|
1130
|
+
out += `\nShare this proof_id with any verifier:\n ${data.proof_id}\n`;
|
|
1131
|
+
out += `\nThe verifier calls ep_verify_zk_proof with only the proof_id.\n`;
|
|
1132
|
+
out += `They confirm your claim without seeing your receipt history, counterparties, or transaction details.`;
|
|
1133
|
+
return out;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
case 'ep_verify_zk_proof': {
|
|
1137
|
+
const params = new URLSearchParams({ proof_id: args.proof_id });
|
|
1138
|
+
const data = await epFetch(`/api/trust/zk-proof?${params}`);
|
|
1139
|
+
let out = `Commitment Proof Verification\n`;
|
|
1140
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
1141
|
+
out += `Proof ID: ${data.proof_id}\n`;
|
|
1142
|
+
out += `Entity: ${data.entity_id}\n`;
|
|
1143
|
+
out += `Result: ${data.valid ? '✓ VALID' : '✗ INVALID'}\n`;
|
|
1144
|
+
if (data.claim) {
|
|
1145
|
+
out += `Claim: ${data.claim.type} > ${data.claim.threshold}`;
|
|
1146
|
+
if (data.claim.domain) out += ` (${data.claim.domain})`;
|
|
1147
|
+
out += `\n`;
|
|
1148
|
+
}
|
|
1149
|
+
out += `Verified at: ${data.verified_at}\n`;
|
|
1150
|
+
if (data.valid) {
|
|
1151
|
+
out += `Receipt count: ${data.receipt_count} (counterparties and contents remain hidden)\n`;
|
|
1152
|
+
out += `Expires: ${data.expires_at}\n`;
|
|
1153
|
+
if (data.anchor_block) out += `Anchor: ${data.anchor_block}\n`;
|
|
1154
|
+
out += `\n✓ The entity has proven the stated trust claim.\n`;
|
|
1155
|
+
out += `You have learned nothing about their transaction history or counterparties.`;
|
|
1156
|
+
} else {
|
|
1157
|
+
out += `Reason: ${data.reason || 'unknown'}\n`;
|
|
1158
|
+
if (data.expired_at) out += `Expired: ${data.expired_at}\n`;
|
|
1159
|
+
out += `\n${data._note || 'Proof could not be verified.'}`;
|
|
1160
|
+
}
|
|
1161
|
+
return out;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
case 'ep_issue_commit': {
|
|
1165
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to issue commits.';
|
|
1166
|
+
const body = {
|
|
1167
|
+
action_type: args.action_type,
|
|
1168
|
+
entity_id: args.entity_id,
|
|
1169
|
+
principal_id: args.principal_id || null,
|
|
1170
|
+
counterparty_entity_id: args.counterparty_entity_id || null,
|
|
1171
|
+
delegation_id: args.delegation_id || null,
|
|
1172
|
+
scope: args.scope || null,
|
|
1173
|
+
max_value_usd: args.max_value_usd || null,
|
|
1174
|
+
context: args.context || null,
|
|
1175
|
+
policy: args.policy || 'standard',
|
|
1176
|
+
};
|
|
1177
|
+
const data = await epFetch('/api/commit/issue', { method: 'POST', auth: true, body });
|
|
1178
|
+
const commit = data.commit;
|
|
1179
|
+
let out = `EP Commit Issued\n`;
|
|
1180
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
1181
|
+
out += `Commit ID: ${commit.commit_id}\n`;
|
|
1182
|
+
out += `Decision: ${data.decision === 'allow' ? '✓ ALLOW' : data.decision === 'deny' ? '✗ DENY' : '⚠ REVIEW'}\n`;
|
|
1183
|
+
out += `Expires: ${commit.expires_at}\n`;
|
|
1184
|
+
if (commit.scope) out += `Scope: ${typeof commit.scope === 'object' ? JSON.stringify(commit.scope) : commit.scope}\n`;
|
|
1185
|
+
if (commit.appeal_path) out += `Appeal: ${commit.appeal_path}\n`;
|
|
1186
|
+
return out;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
case 'ep_verify_commit': {
|
|
1190
|
+
const data = await epFetch('/api/commit/verify', { method: 'POST', body: { commit_id: args.commit_id } });
|
|
1191
|
+
let out = `Commit Verification\n`;
|
|
1192
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
1193
|
+
out += `Commit ID: ${args.commit_id}\n`;
|
|
1194
|
+
out += `Valid: ${data.valid ? '✓ YES' : '✗ NO'}\n`;
|
|
1195
|
+
out += `Status: ${data.status}\n`;
|
|
1196
|
+
out += `Decision: ${data.decision}\n`;
|
|
1197
|
+
if (data.expires_at) out += `Expires: ${data.expires_at}\n`;
|
|
1198
|
+
return out;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
case 'ep_get_commit_status': {
|
|
1202
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to get commit status.';
|
|
1203
|
+
const data = await epFetch(`/api/commit/${encodeURIComponent(args.commit_id)}`, { auth: true });
|
|
1204
|
+
const commit = data.commit;
|
|
1205
|
+
let out = `Commit: ${commit.commit_id}\n`;
|
|
1206
|
+
out += `Status: ${commit.status}\n`;
|
|
1207
|
+
out += `Action type: ${commit.action_type}\n`;
|
|
1208
|
+
out += `Entity: ${commit.entity_id}\n`;
|
|
1209
|
+
if (commit.decision) out += `Decision: ${commit.decision}\n`;
|
|
1210
|
+
if (commit.expires_at) out += `Expires: ${commit.expires_at}\n`;
|
|
1211
|
+
if (commit.receipt_id) out += `Receipt: ${commit.receipt_id}\n`;
|
|
1212
|
+
return out;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
case 'ep_revoke_commit': {
|
|
1216
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to revoke commits.';
|
|
1217
|
+
const data = await epFetch(`/api/commit/${encodeURIComponent(args.commit_id)}/revoke`, {
|
|
1218
|
+
method: 'POST', auth: true, body: { reason: args.reason },
|
|
1219
|
+
});
|
|
1220
|
+
return `Commit revoked.\n` +
|
|
1221
|
+
`Commit ID: ${data.commit_id}\n` +
|
|
1222
|
+
`Status: ${data.status}\n` +
|
|
1223
|
+
`Reason: ${args.reason}`;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
case 'ep_bind_receipt_to_commit': {
|
|
1227
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to bind receipts.';
|
|
1228
|
+
const data = await epFetch(`/api/commit/${encodeURIComponent(args.commit_id)}/receipt`, {
|
|
1229
|
+
method: 'POST', auth: true, body: { receipt_id: args.receipt_id },
|
|
1230
|
+
});
|
|
1231
|
+
return `Receipt bound to commit.\n` +
|
|
1232
|
+
`Commit ID: ${data.commit_id}\n` +
|
|
1233
|
+
`Receipt ID: ${data.receipt_id}\n` +
|
|
1234
|
+
`Status: ${data.status}`;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
case 'ep_initiate_handshake': {
|
|
1238
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to initiate handshakes.';
|
|
1239
|
+
const body = {
|
|
1240
|
+
mode: args.mode,
|
|
1241
|
+
policy_id: args.policy_id,
|
|
1242
|
+
parties: args.parties,
|
|
1243
|
+
binding: args.binding || null,
|
|
1244
|
+
interaction_id: args.interaction_id || null,
|
|
1245
|
+
};
|
|
1246
|
+
const data = await epFetch('/api/handshake', { method: 'POST', auth: true, body });
|
|
1247
|
+
let out = `Handshake Initiated\n`;
|
|
1248
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
1249
|
+
out += `Handshake ID: ${data.handshake_id}\n`;
|
|
1250
|
+
out += `Mode: ${data.mode}\n`;
|
|
1251
|
+
out += `Policy: ${data.policy_id}\n`;
|
|
1252
|
+
out += `Status: ${data.status}\n`;
|
|
1253
|
+
if (data.parties?.length) {
|
|
1254
|
+
out += `Parties: ${data.parties.map(p => `${p.entity_ref} (${p.role})`).join(', ')}\n`;
|
|
1255
|
+
}
|
|
1256
|
+
out += `\nNext: each party calls ep_add_presentation to submit identity proofs.`;
|
|
1257
|
+
return out;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
case 'ep_add_presentation': {
|
|
1261
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to add presentations.';
|
|
1262
|
+
const body = {
|
|
1263
|
+
party_role: args.party_role,
|
|
1264
|
+
presentation_type: args.presentation_type,
|
|
1265
|
+
issuer_ref: args.issuer_ref || null,
|
|
1266
|
+
claims: args.claims,
|
|
1267
|
+
disclosure_mode: args.disclosure_mode || null,
|
|
1268
|
+
};
|
|
1269
|
+
const data = await epFetch(`/api/handshake/${encodeURIComponent(args.handshake_id)}/present`, {
|
|
1270
|
+
method: 'POST', auth: true, body,
|
|
1271
|
+
});
|
|
1272
|
+
return `Presentation added.\n` +
|
|
1273
|
+
`Presentation ID: ${data.presentation_id}\n` +
|
|
1274
|
+
`Handshake: ${data.handshake_id}\n` +
|
|
1275
|
+
`Party role: ${data.party_role}\n` +
|
|
1276
|
+
`Type: ${data.presentation_type}\n` +
|
|
1277
|
+
`When all parties have presented, call ep_verify_handshake to evaluate.`;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
case 'ep_verify_handshake': {
|
|
1281
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to verify handshakes.';
|
|
1282
|
+
const data = await epFetch(`/api/handshake/${encodeURIComponent(args.handshake_id)}/verify`, {
|
|
1283
|
+
method: 'POST', auth: true,
|
|
1284
|
+
});
|
|
1285
|
+
let out = `Handshake Verification\n`;
|
|
1286
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
1287
|
+
out += `Handshake ID: ${data.handshake_id}\n`;
|
|
1288
|
+
out += `Result: ${data.result === 'accepted' ? '✓ ACCEPTED' : data.result === 'rejected' ? '✗ REJECTED' : '⚠ PARTIAL'}\n`;
|
|
1289
|
+
if (data.reason_codes?.length) {
|
|
1290
|
+
out += `Reasons:\n`;
|
|
1291
|
+
for (const r of data.reason_codes) out += ` ${r}\n`;
|
|
1292
|
+
}
|
|
1293
|
+
if (data.evaluated_at) out += `Evaluated at: ${data.evaluated_at}\n`;
|
|
1294
|
+
return out;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
case 'ep_get_handshake': {
|
|
1298
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to get handshake details.';
|
|
1299
|
+
const data = await epFetch(`/api/handshake/${encodeURIComponent(args.handshake_id)}`, { auth: true });
|
|
1300
|
+
let out = `Handshake: ${data.handshake_id}\n`;
|
|
1301
|
+
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
1302
|
+
out += `Mode: ${data.mode}\n`;
|
|
1303
|
+
out += `Policy: ${data.policy_id}\n`;
|
|
1304
|
+
out += `Status: ${data.status}\n`;
|
|
1305
|
+
if (data.parties?.length) {
|
|
1306
|
+
out += `\nParties:\n`;
|
|
1307
|
+
for (const p of data.parties) {
|
|
1308
|
+
out += ` ${p.entity_ref} (${p.role}) — ${p.presented ? 'presented' : 'awaiting'}\n`;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
if (data.presentations?.length) {
|
|
1312
|
+
out += `\nPresentations (${data.presentations.length}):\n`;
|
|
1313
|
+
for (const pr of data.presentations) {
|
|
1314
|
+
out += ` ${pr.party_role}: ${pr.presentation_type} [${pr.disclosure_mode || 'full'}]\n`;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
if (data.result) {
|
|
1318
|
+
out += `\nResult: ${data.result.outcome}\n`;
|
|
1319
|
+
if (data.result.reason_codes?.length) {
|
|
1320
|
+
for (const r of data.result.reason_codes) out += ` ${r}\n`;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
return out;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
case 'ep_revoke_handshake': {
|
|
1327
|
+
if (!API_KEY) return 'Error: EP_API_KEY required to revoke handshakes.';
|
|
1328
|
+
const data = await epFetch(`/api/handshake/${encodeURIComponent(args.handshake_id)}/revoke`, {
|
|
1329
|
+
method: 'POST', auth: true, body: { reason: args.reason },
|
|
1330
|
+
});
|
|
1331
|
+
return `Handshake revoked.\n` +
|
|
1332
|
+
`Handshake ID: ${data.handshake_id}\n` +
|
|
1333
|
+
`Status: ${data.status}\n` +
|
|
1334
|
+
`Reason: ${args.reason}`;
|
|
329
1335
|
}
|
|
330
1336
|
|
|
331
1337
|
default:
|
|
@@ -337,117 +1343,277 @@ async function handleTool(name, args) {
|
|
|
337
1343
|
// Formatters
|
|
338
1344
|
// =============================================================================
|
|
339
1345
|
|
|
340
|
-
function
|
|
341
|
-
let out = `
|
|
1346
|
+
function formatTrustProfile(data) {
|
|
1347
|
+
let out = `Trust Profile: ${data.display_name} (${data.entity_id})\n`;
|
|
342
1348
|
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
343
|
-
out += `
|
|
344
|
-
out += data.
|
|
345
|
-
out +=
|
|
346
|
-
out += `
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
1349
|
+
out += `Confidence: ${data.current_confidence}\n`;
|
|
1350
|
+
out += `Established: ${data.historical_establishment ? 'Yes' : 'No'}\n`;
|
|
1351
|
+
out += `Evidence: ${data.effective_evidence_current} (current) / ${data.effective_evidence_historical} (historical)\n`;
|
|
1352
|
+
out += `Receipts: ${data.receipt_count ?? 'N/A'} from ${data.unique_submitters ?? 'N/A'} submitters\n`;
|
|
1353
|
+
|
|
1354
|
+
const p = data.trust_profile;
|
|
1355
|
+
if (p) {
|
|
1356
|
+
if (p.behavioral) {
|
|
1357
|
+
out += `\nBehavioral:\n`;
|
|
1358
|
+
out += ` Completion rate: ${p.behavioral.completion_rate ?? 'N/A'}%\n`;
|
|
1359
|
+
out += ` Retry rate: ${p.behavioral.retry_rate ?? 'N/A'}%\n`;
|
|
1360
|
+
out += ` Abandon rate: ${p.behavioral.abandon_rate ?? 'N/A'}%\n`;
|
|
1361
|
+
out += ` Dispute rate: ${p.behavioral.dispute_rate ?? 'N/A'}%\n`;
|
|
1362
|
+
}
|
|
1363
|
+
if (p.signals) {
|
|
1364
|
+
out += `\nSignals:\n`;
|
|
1365
|
+
out += ` Delivery: ${p.signals.delivery_accuracy ?? 'N/A'}\n`;
|
|
1366
|
+
out += ` Product: ${p.signals.product_accuracy ?? 'N/A'}\n`;
|
|
1367
|
+
out += ` Price: ${p.signals.price_integrity ?? 'N/A'}\n`;
|
|
1368
|
+
out += ` Returns: ${p.signals.return_processing ?? 'N/A'}\n`;
|
|
1369
|
+
}
|
|
1370
|
+
out += ` Consistency: ${p.consistency ?? 'N/A'}\n`;
|
|
1371
|
+
if (p.provenance) {
|
|
1372
|
+
const bd = p.provenance.breakdown || {};
|
|
1373
|
+
const tiers = Object.entries(bd).map(([k,v]) => `${k}: ${v}`).join(', ');
|
|
1374
|
+
out += `\nProvenance: ${tiers}\n`;
|
|
1375
|
+
if (p.provenance.bilateral_rate != null) out += ` Bilateral rate: ${p.provenance.bilateral_rate}%\n`;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
if (data.disputes && data.disputes.total > 0) {
|
|
1380
|
+
out += `\nDisputes: ${data.disputes.total} total, ${data.disputes.active} active, ${data.disputes.reversed} reversed\n`;
|
|
357
1381
|
}
|
|
358
1382
|
|
|
359
|
-
if (data.
|
|
360
|
-
out += `\n${data.
|
|
1383
|
+
if (data.anomaly) {
|
|
1384
|
+
out += `\n⚠️ ANOMALY: ${data.anomaly.type} (${data.anomaly.delta} points, ${data.anomaly.alert})\n`;
|
|
361
1385
|
}
|
|
362
1386
|
|
|
1387
|
+
out += `\n(Legacy compatibility score (fallback only): ${data.compat_score}/100 — use trust profile for decisions)\n`;
|
|
363
1388
|
return out;
|
|
364
1389
|
}
|
|
365
1390
|
|
|
366
|
-
function
|
|
367
|
-
let out = `
|
|
1391
|
+
function formatEvaluation(data) {
|
|
1392
|
+
let out = `Trust Evaluation: ${data.display_name} (${data.entity_id})\n`;
|
|
368
1393
|
out += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
369
|
-
out += `
|
|
370
|
-
out += `
|
|
371
|
-
out += `
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
out +=
|
|
376
|
-
out += `
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
1394
|
+
out += `Policy: ${data.policy_used}\n`;
|
|
1395
|
+
out += `Decision: ${data.decision === 'allow' ? '✓ ALLOW' : data.decision === 'review' ? '⚠ REVIEW' : '✗ DENY'}\n`;
|
|
1396
|
+
out += `Confidence: ${data.confidence}\n`;
|
|
1397
|
+
out += `Context: ${JSON.stringify(data.context_used) || 'global'}\n`;
|
|
1398
|
+
|
|
1399
|
+
if (data.failures?.length > 0) {
|
|
1400
|
+
out += `\nFailures:\n`;
|
|
1401
|
+
for (const f of data.failures) out += ` ✗ ${f}\n`;
|
|
1402
|
+
}
|
|
1403
|
+
if (data.warnings?.length > 0) {
|
|
1404
|
+
out += `\nWarnings:\n`;
|
|
1405
|
+
for (const w of data.warnings) out += ` ⚠ ${w}\n`;
|
|
381
1406
|
}
|
|
382
1407
|
|
|
383
1408
|
return out;
|
|
384
1409
|
}
|
|
385
1410
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
return 'No entities found matching your query.';
|
|
390
|
-
}
|
|
1411
|
+
// =============================================================================
|
|
1412
|
+
// Server
|
|
1413
|
+
// =============================================================================
|
|
391
1414
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
if (e.description) out += ` ${e.description}\n`;
|
|
397
|
-
out += `\n`;
|
|
398
|
-
}
|
|
399
|
-
return out;
|
|
400
|
-
}
|
|
1415
|
+
const server = new Server(
|
|
1416
|
+
{ name: 'emilia-protocol', version: '1.0.0' },
|
|
1417
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
1418
|
+
);
|
|
401
1419
|
|
|
402
|
-
|
|
403
|
-
if (!data.entities || data.entities.length === 0) {
|
|
404
|
-
return 'No entities in the leaderboard yet.';
|
|
405
|
-
}
|
|
1420
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
406
1421
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
1422
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1423
|
+
const { name, arguments: args } = request.params;
|
|
1424
|
+
try {
|
|
1425
|
+
// Wrap handleTool with auto-receipt instrumentation.
|
|
1426
|
+
// The middleware measures latency, generates a receipt draft from the outcome,
|
|
1427
|
+
// and submits it asynchronously — the tool response is never delayed.
|
|
1428
|
+
// ep_configure_auto_receipt itself is excluded from auto-receipt to avoid
|
|
1429
|
+
// a bootstrap loop where enabling the feature immediately records itself.
|
|
1430
|
+
const isMetaTool = name === 'ep_configure_auto_receipt';
|
|
1431
|
+
const invoker = isMetaTool
|
|
1432
|
+
? (a) => handleTool(name, a)
|
|
1433
|
+
: autoReceipt.wrap(name, (a) => handleTool(name, a));
|
|
1434
|
+
|
|
1435
|
+
const result = await invoker(args || {});
|
|
1436
|
+
return { content: [{ type: 'text', text: result }] };
|
|
1437
|
+
} catch (err) {
|
|
1438
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
412
1439
|
}
|
|
413
|
-
|
|
414
|
-
}
|
|
1440
|
+
});
|
|
415
1441
|
|
|
416
1442
|
// =============================================================================
|
|
417
|
-
//
|
|
1443
|
+
// MCP Resources
|
|
418
1444
|
// =============================================================================
|
|
419
1445
|
|
|
420
|
-
const
|
|
1446
|
+
const RESOURCES = [
|
|
421
1447
|
{
|
|
422
|
-
|
|
423
|
-
|
|
1448
|
+
uri: 'entity://{id}',
|
|
1449
|
+
name: 'Entity Trust Profile',
|
|
1450
|
+
description: 'Full trust profile for any entity by ID or slug. Equivalent to ep_trust_profile tool but as a resource.',
|
|
1451
|
+
mimeType: 'application/json',
|
|
424
1452
|
},
|
|
425
1453
|
{
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
1454
|
+
uri: 'score://{id}',
|
|
1455
|
+
name: 'Entity Trust Score',
|
|
1456
|
+
description: 'Current trust confidence and score breakdown for an entity.',
|
|
1457
|
+
mimeType: 'application/json',
|
|
1458
|
+
},
|
|
1459
|
+
{
|
|
1460
|
+
uri: 'receipt://{id}',
|
|
1461
|
+
name: 'Receipt',
|
|
1462
|
+
description: 'Full receipt data including hash, provenance, and verification status.',
|
|
1463
|
+
mimeType: 'application/json',
|
|
1464
|
+
},
|
|
1465
|
+
{
|
|
1466
|
+
uri: 'delegation://{id}',
|
|
1467
|
+
name: 'Delegation Record',
|
|
1468
|
+
description: 'Delegation details: principal, agent, scope, expiry, and status.',
|
|
1469
|
+
mimeType: 'application/json',
|
|
1470
|
+
},
|
|
1471
|
+
];
|
|
1472
|
+
|
|
1473
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: RESOURCES }));
|
|
1474
|
+
|
|
1475
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1476
|
+
const { uri } = request.params;
|
|
1477
|
+
let data;
|
|
1478
|
+
let text;
|
|
1479
|
+
|
|
1480
|
+
if (uri.startsWith('entity://')) {
|
|
1481
|
+
const id = uri.slice('entity://'.length);
|
|
1482
|
+
data = await epFetch(`/api/trust/profile/${encodeURIComponent(id)}`);
|
|
1483
|
+
text = JSON.stringify(data, null, 2);
|
|
1484
|
+
} else if (uri.startsWith('score://')) {
|
|
1485
|
+
const id = uri.slice('score://'.length);
|
|
1486
|
+
data = await epFetch(`/api/trust/profile/${encodeURIComponent(id)}`);
|
|
1487
|
+
text = JSON.stringify({
|
|
1488
|
+
entity_id: data.entity_id,
|
|
1489
|
+
confidence: data.current_confidence,
|
|
1490
|
+
effective_evidence: data.effective_evidence_current,
|
|
1491
|
+
established: data.historical_establishment,
|
|
1492
|
+
compat_score: data.compat_score,
|
|
1493
|
+
}, null, 2);
|
|
1494
|
+
} else if (uri.startsWith('receipt://')) {
|
|
1495
|
+
const id = uri.slice('receipt://'.length);
|
|
1496
|
+
data = await epFetch(`/api/verify/${encodeURIComponent(id)}`);
|
|
1497
|
+
text = JSON.stringify(data, null, 2);
|
|
1498
|
+
} else if (uri.startsWith('delegation://')) {
|
|
1499
|
+
const id = uri.slice('delegation://'.length);
|
|
1500
|
+
data = await epFetch(`/api/delegations/${encodeURIComponent(id)}/verify`);
|
|
1501
|
+
text = JSON.stringify(data, null, 2);
|
|
1502
|
+
} else {
|
|
1503
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
429
1504
|
}
|
|
430
|
-
);
|
|
431
1505
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}));
|
|
1506
|
+
return { contents: [{ uri, mimeType: 'application/json', text }] };
|
|
1507
|
+
});
|
|
435
1508
|
|
|
436
|
-
|
|
1509
|
+
// =============================================================================
|
|
1510
|
+
// MCP Prompts
|
|
1511
|
+
// =============================================================================
|
|
1512
|
+
|
|
1513
|
+
const PROMPTS = [
|
|
1514
|
+
{
|
|
1515
|
+
name: 'trust_decision',
|
|
1516
|
+
description: 'Get a structured prompt for making a trust-based routing or payment decision about an entity.',
|
|
1517
|
+
arguments: [
|
|
1518
|
+
{ name: 'entity_id', description: 'Entity to evaluate', required: true },
|
|
1519
|
+
{ name: 'action', description: 'Action being considered (e.g. process_payment, install_plugin)', required: true },
|
|
1520
|
+
{ name: 'value_usd', description: 'Transaction value in USD', required: false },
|
|
1521
|
+
],
|
|
1522
|
+
},
|
|
1523
|
+
{
|
|
1524
|
+
name: 'receipt_quality_check',
|
|
1525
|
+
description: 'Get a prompt for evaluating the quality and accuracy of a receipt before submission.',
|
|
1526
|
+
arguments: [
|
|
1527
|
+
{ name: 'entity_id', description: 'Entity the receipt is about', required: true },
|
|
1528
|
+
{ name: 'transaction_ref', description: 'Transaction reference', required: true },
|
|
1529
|
+
],
|
|
1530
|
+
},
|
|
1531
|
+
{
|
|
1532
|
+
name: 'install_decision',
|
|
1533
|
+
description: 'Get a structured prompt for deciding whether to install a software package or plugin.',
|
|
1534
|
+
arguments: [
|
|
1535
|
+
{ name: 'entity_id', description: 'Software entity ID', required: true },
|
|
1536
|
+
{ name: 'install_context', description: 'Where it will be installed (e.g. private_repo, production_server)', required: false },
|
|
1537
|
+
],
|
|
1538
|
+
},
|
|
1539
|
+
];
|
|
1540
|
+
|
|
1541
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: PROMPTS }));
|
|
1542
|
+
|
|
1543
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
437
1544
|
const { name, arguments: args } = request.params;
|
|
438
|
-
|
|
439
|
-
|
|
1545
|
+
|
|
1546
|
+
if (name === 'trust_decision') {
|
|
1547
|
+
const entity_id = args?.entity_id || '[entity_id]';
|
|
1548
|
+
const action = args?.action || '[action]';
|
|
1549
|
+
const value = args?.value_usd ? ` (value: $${args.value_usd})` : '';
|
|
1550
|
+
return {
|
|
1551
|
+
description: `Trust decision for ${entity_id}`,
|
|
1552
|
+
messages: [{
|
|
1553
|
+
role: 'user',
|
|
1554
|
+
content: {
|
|
1555
|
+
type: 'text',
|
|
1556
|
+
text: `I need to decide whether to allow entity "${entity_id}" to perform "${action}"${value}.\n\n` +
|
|
1557
|
+
`Please:\n` +
|
|
1558
|
+
`1. Call ep_trust_gate with entity_id="${entity_id}", action="${action}"${args?.value_usd ? `, value_usd=${args.value_usd}` : ''}\n` +
|
|
1559
|
+
`2. If the gate passes, call ep_trust_profile to get the full profile\n` +
|
|
1560
|
+
`3. Summarize: ALLOW, REVIEW, or DENY, with the key trust signals that drove the decision\n` +
|
|
1561
|
+
`4. If DENY, explain what the entity would need to do to qualify`,
|
|
1562
|
+
},
|
|
1563
|
+
}],
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
if (name === 'receipt_quality_check') {
|
|
1568
|
+
const entity_id = args?.entity_id || '[entity_id]';
|
|
1569
|
+
const ref = args?.transaction_ref || '[transaction_ref]';
|
|
440
1570
|
return {
|
|
441
|
-
|
|
1571
|
+
description: `Receipt quality check for ${entity_id}`,
|
|
1572
|
+
messages: [{
|
|
1573
|
+
role: 'user',
|
|
1574
|
+
content: {
|
|
1575
|
+
type: 'text',
|
|
1576
|
+
text: `Before I submit a receipt for entity "${entity_id}" (ref: ${ref}), help me ensure it's accurate:\n\n` +
|
|
1577
|
+
`1. Call ep_trust_profile to see their current trust state\n` +
|
|
1578
|
+
`2. Ask me: What was the agent_behavior? (completed/retried_same/retried_different/abandoned/disputed)\n` +
|
|
1579
|
+
`3. Ask me: What signal scores should I set? (delivery_accuracy, product_accuracy, price_integrity, return_processing — each 0-100)\n` +
|
|
1580
|
+
`4. Warn me if any signals seem inconsistent with the agent_behavior\n` +
|
|
1581
|
+
`5. Only submit with ep_submit_receipt when I confirm the data is accurate`,
|
|
1582
|
+
},
|
|
1583
|
+
}],
|
|
442
1584
|
};
|
|
443
|
-
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
if (name === 'install_decision') {
|
|
1588
|
+
const entity_id = args?.entity_id || '[entity_id]';
|
|
1589
|
+
const ctx = args?.install_context || 'production';
|
|
444
1590
|
return {
|
|
445
|
-
|
|
446
|
-
|
|
1591
|
+
description: `Install decision for ${entity_id}`,
|
|
1592
|
+
messages: [{
|
|
1593
|
+
role: 'user',
|
|
1594
|
+
content: {
|
|
1595
|
+
type: 'text',
|
|
1596
|
+
text: `Should I install "${entity_id}" in my ${ctx} environment?\n\n` +
|
|
1597
|
+
`Please:\n` +
|
|
1598
|
+
`1. Call ep_install_preflight with entity_id="${entity_id}" (pre-action enforcement)\n` +
|
|
1599
|
+
`2. Call ep_lineage to check for suspicious continuity gaps\n` +
|
|
1600
|
+
`3. Call ep_trust_profile for full behavioral history\n` +
|
|
1601
|
+
`4. Give me a clear INSTALL / REVIEW / DENY recommendation with reasons\n` +
|
|
1602
|
+
`5. If REVIEW or DENY, list specific questions to investigate before proceeding`,
|
|
1603
|
+
},
|
|
1604
|
+
}],
|
|
447
1605
|
};
|
|
448
1606
|
}
|
|
1607
|
+
|
|
1608
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
449
1609
|
});
|
|
450
1610
|
|
|
1611
|
+
// =============================================================================
|
|
451
1612
|
// Start
|
|
1613
|
+
// =============================================================================
|
|
1614
|
+
|
|
452
1615
|
const transport = new StdioServerTransport();
|
|
453
1616
|
await server.connect(transport);
|
|
1617
|
+
|
|
1618
|
+
// Exported for integration tests only.
|
|
1619
|
+
export { handleTool };
|