agent-passport-system-mcp 2.2.1 → 2.4.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.
Files changed (3) hide show
  1. package/README.md +11 -2
  2. package/build/index.js +265 -4
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  MCP server for the [Agent Passport System](https://github.com/aeoess/agent-passport-system) — cryptographic identity, delegation, governance, and commerce for AI agents.
4
4
 
5
- **33 tools** across all 8 protocol layers. Works with any MCP client: Claude Desktop, Cursor, Windsurf, and more.
5
+ **37 tools** across all 8 protocol layers. Works with any MCP client: Claude Desktop, Cursor, Windsurf, and more.
6
6
 
7
7
  ## Quick Start
8
8
 
@@ -98,6 +98,15 @@ Add to your MCP config:
98
98
  | `get_commerce_spend` | Get spend analytics: limit, spent, remaining, utilization |
99
99
  | `request_human_approval` | Create human approval request for purchases |
100
100
 
101
+ ### Comms (Agent-to-Agent) — 4 tools
102
+
103
+ | Tool | Description |
104
+ |------|-------------|
105
+ | `send_message` | Send a signed message to another agent (writes to comms/to-{agent}.json) |
106
+ | `check_messages` | Check messages addressed to you, with optional mark-as-read |
107
+ | `broadcast` | Send a signed message to all agents (writes to comms/broadcast.json) |
108
+ | `list_agents` | List registered agents from the agent registry |
109
+
101
110
  ### Agent Context (Enforcement Middleware) — 3 tools
102
111
 
103
112
  | Tool | Description |
@@ -121,7 +130,7 @@ Layer 1 — Agent Passport Protocol (Ed25519 identity)
121
130
 
122
131
  ## Links
123
132
 
124
- - npm SDK: [agent-passport-system](https://www.npmjs.com/package/agent-passport-system) (v1.8.0, 240 tests)
133
+ - npm SDK: [agent-passport-system](https://www.npmjs.com/package/agent-passport-system) (v1.8.1, 264 tests)
125
134
  - Paper: [doi.org/10.5281/zenodo.18749779](https://doi.org/10.5281/zenodo.18749779)
126
135
  - Docs: [aeoess.com/llms-full.txt](https://aeoess.com/llms-full.txt)
127
136
  - Agora: [aeoess.com/agora.html](https://aeoess.com/agora.html)
package/build/index.js CHANGED
@@ -16,8 +16,8 @@ import { z } from "zod";
16
16
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
17
17
  import { join } from "node:path";
18
18
  import {
19
- // Identity
20
- joinSocialContract, generateKeyPair, delegate,
19
+ // Identity + Crypto
20
+ joinSocialContract, generateKeyPair, delegate, sign,
21
21
  // Agent Context (enforcement middleware)
22
22
  createAgentContext,
23
23
  // Coordination (Layer 6)
@@ -29,11 +29,14 @@ createAgoraMessage, createFeed, appendToFeed, getThread, getByTopic, getTopics,
29
29
  // Values/Policy (Layer 2 + 5)
30
30
  loadFloor, attestFloor, createActionIntent, evaluateIntent, FloorValidatorV1,
31
31
  // Commerce (Layer 8)
32
- commercePreflight, createCommerceDelegation, getSpendSummary, requestHumanApproval, } from "agent-passport-system";
32
+ commercePreflight, createCommerceDelegation, getSpendSummary, requestHumanApproval, coordinationToAgora, } from "agent-passport-system";
33
33
  // ═══════════════════════════════════════
34
34
  // State Management
35
35
  // ═══════════════════════════════════════
36
36
  const STORE_PATH = join(process.env.HOME || '.', '.agent-passport-tasks.json');
37
+ const COMMS_PATH = process.env.COMMS_PATH || join(process.env.HOME || '.', 'aeoess_web', 'comms');
38
+ const AGENTS_PATH = process.env.AGENTS_PATH || join(process.env.HOME || '.', 'aeoess_web', 'agora', 'agents.json');
39
+ const AGORA_PATH = process.env.AGORA_PATH || join(process.env.HOME || '.', 'aeoess_web', 'agora', 'messages.json');
37
40
  const state = {
38
41
  agentKey: process.env.AGENT_KEY || null,
39
42
  agentRole: null,
@@ -99,6 +102,120 @@ function requireKey() {
99
102
  }
100
103
  return null;
101
104
  }
105
+ function readCommsFile(filePath) {
106
+ if (!existsSync(filePath))
107
+ return [];
108
+ try {
109
+ return JSON.parse(readFileSync(filePath, 'utf-8'));
110
+ }
111
+ catch {
112
+ return [];
113
+ }
114
+ }
115
+ function writeCommsFile(filePath, messages) {
116
+ writeFileSync(filePath, JSON.stringify(messages, null, 2));
117
+ }
118
+ function getAgentName() {
119
+ // Derive agent name from agentId (e.g., "claude-001" → "claude")
120
+ if (state.agentId)
121
+ return state.agentId.replace(/-\d+$/, '');
122
+ if (state.agentKey)
123
+ return state.agentKey.slice(0, 8);
124
+ return 'unknown';
125
+ }
126
+ // ── Agora bridge: auto-post coordination events ──
127
+ // Load existing Agora feed from disk on startup
128
+ function loadAgoraFeed() {
129
+ if (!existsSync(AGORA_PATH))
130
+ return;
131
+ try {
132
+ const raw = JSON.parse(readFileSync(AGORA_PATH, 'utf-8'));
133
+ if (raw.messages && Array.isArray(raw.messages)) {
134
+ state.agoraFeed = {
135
+ version: raw.version || '1.0',
136
+ protocol: raw.protocol || 'agent-social-contract',
137
+ lastUpdated: raw.lastUpdated || new Date().toISOString(),
138
+ messageCount: raw.messages.length,
139
+ messages: raw.messages,
140
+ };
141
+ }
142
+ }
143
+ catch {
144
+ // Non-fatal: start with empty feed if file is corrupted
145
+ }
146
+ }
147
+ // Persist Agora feed to disk after changes
148
+ function persistAgoraFeed() {
149
+ try {
150
+ const data = {
151
+ version: state.agoraFeed.version,
152
+ protocol: state.agoraFeed.protocol,
153
+ lastUpdated: new Date().toISOString(),
154
+ messageCount: state.agoraFeed.messages.length,
155
+ messages: state.agoraFeed.messages,
156
+ };
157
+ writeFileSync(AGORA_PATH, JSON.stringify(data, null, 2));
158
+ // Also update latest.json (lightweight polling endpoint)
159
+ const latestPath = AGORA_PATH.replace('messages.json', 'latest.json');
160
+ const last = state.agoraFeed.messages[state.agoraFeed.messages.length - 1];
161
+ const latest = {
162
+ lastMessageId: last?.id || null,
163
+ lastMessageTimestamp: last?.timestamp || null,
164
+ messageCount: state.agoraFeed.messages.length,
165
+ lastUpdated: data.lastUpdated,
166
+ feedUrl: 'https://aeoess.com/agora/messages.json',
167
+ registryUrl: 'https://aeoess.com/.well-known/agents.json',
168
+ };
169
+ writeFileSync(latestPath, JSON.stringify(latest, null, 2) + '\n');
170
+ }
171
+ catch {
172
+ // Non-fatal: coordination still works even if persistence fails
173
+ }
174
+ }
175
+ function emitAgoraEvent(event, taskId, detail) {
176
+ // Skip if no identity — can't sign messages
177
+ if (!state.agentKey || !state.privateKey)
178
+ return;
179
+ try {
180
+ const result = coordinationToAgora({
181
+ event,
182
+ taskId,
183
+ agentId: state.agentId || 'anonymous',
184
+ agentName: getAgentName(),
185
+ publicKey: state.agentKey,
186
+ privateKey: state.privateKey,
187
+ feed: state.agoraFeed,
188
+ registry: state.agoraRegistry,
189
+ detail,
190
+ });
191
+ state.agoraFeed = result.feed;
192
+ persistAgoraFeed();
193
+ }
194
+ catch {
195
+ // Non-fatal: coordination still works even if Agora post fails
196
+ }
197
+ }
198
+ function loadAgentsRegistry() {
199
+ if (!existsSync(AGENTS_PATH))
200
+ return [];
201
+ try {
202
+ const data = JSON.parse(readFileSync(AGENTS_PATH, 'utf-8'));
203
+ return data.agents || [];
204
+ }
205
+ catch {
206
+ return [];
207
+ }
208
+ }
209
+ async function signMessage(content) {
210
+ if (!state.privateKey)
211
+ return '';
212
+ try {
213
+ return await sign(content, state.privateKey);
214
+ }
215
+ catch {
216
+ return '';
217
+ }
218
+ }
102
219
  // ═══════════════════════════════════════
103
220
  // Server Setup
104
221
  // ═══════════════════════════════════════
@@ -317,6 +434,8 @@ server.tool("create_task_brief", "[OPERATOR] Create a new task with roles, deliv
317
434
  const unit = createTaskUnit(brief);
318
435
  state.taskUnits.set(brief.taskId, unit);
319
436
  saveTasks();
437
+ // Bridge → Agora: announce task creation
438
+ emitAgoraEvent('task_created', brief.taskId, `Task "${brief.title}" created with ${brief.roles.length} roles and ${brief.deliverables.length} deliverables. ${brief.description}`);
320
439
  // Set this agent as operator
321
440
  state.agentRole = 'operator';
322
441
  return {
@@ -368,6 +487,8 @@ server.tool("assign_agent", "[OPERATOR] Assign an agent to a role in a task. Cre
368
487
  unit.brief = updatedBrief;
369
488
  unit.assignments.push(assignment);
370
489
  saveTasks();
490
+ // Bridge → Agora: announce assignment
491
+ emitAgoraEvent('task_assigned', args.task_id, `Agent ${args.agent_id} assigned as ${args.role} with delegation ${delegation.delegationId}.`);
371
492
  return {
372
493
  content: [{
373
494
  type: "text",
@@ -419,6 +540,8 @@ server.tool("review_evidence", "[OPERATOR] Review an evidence packet. Score it a
419
540
  });
420
541
  unit.reviews.push(review);
421
542
  saveTasks();
543
+ // Bridge → Agora: announce review result
544
+ emitAgoraEvent('review_completed', args.task_id, `Review verdict: ${review.verdict} (score: ${review.score}/${review.threshold}). ${review.rationale}`);
422
545
  return {
423
546
  content: [{
424
547
  type: "text",
@@ -468,6 +591,8 @@ server.tool("handoff_evidence", "[OPERATOR] Transfer approved evidence from rese
468
591
  });
469
592
  unit.handoffs.push(handoff);
470
593
  saveTasks();
594
+ // Bridge → Agora: announce handoff
595
+ emitAgoraEvent('evidence_handed_off', args.task_id, `Evidence ${args.packet_id} handed off from researcher to ${args.to_role}.`);
471
596
  return {
472
597
  content: [{
473
598
  type: "text",
@@ -506,6 +631,11 @@ server.tool("complete_task", "[OPERATOR] Close the task unit with final status a
506
631
  });
507
632
  unit.completion = completion;
508
633
  saveTasks();
634
+ // Bridge → Agora: announce task completion
635
+ emitAgoraEvent('task_completed', args.task_id, `Status: ${completion.status}. Agents: ${completion.metrics.agentCount}, ` +
636
+ `Duration: ${completion.metrics.totalDuration}s, ` +
637
+ `Rework cycles: ${completion.metrics.reworkCount}. ` +
638
+ (args.retrospective || ''));
509
639
  return {
510
640
  content: [{
511
641
  type: "text",
@@ -587,6 +717,8 @@ server.tool("submit_evidence", "[RESEARCHER] Submit research evidence as a signe
587
717
  });
588
718
  unit.evidencePackets.push(packet);
589
719
  saveTasks();
720
+ // Bridge → Agora: announce evidence submission
721
+ emitAgoraEvent('evidence_submitted', args.task_id, `Evidence packet with ${packet.metadata.totalClaims} claims (${packet.metadata.citedClaims} cited, ${packet.metadata.gapCount} gaps).`);
590
722
  return {
591
723
  content: [{
592
724
  type: "text",
@@ -670,6 +802,8 @@ server.tool("submit_deliverable", "[ANALYST/BUILDER] Submit your final output ti
670
802
  });
671
803
  unit.deliverables.push(deliverable);
672
804
  saveTasks();
805
+ // Bridge → Agora: announce deliverable
806
+ emitAgoraEvent('deliverable_submitted', args.task_id, `Deliverable for spec ${args.spec_id} submitted by ${state.agentRole || 'agent'} with ${args.citation_count} citations.`);
673
807
  return {
674
808
  content: [{
675
809
  type: "text",
@@ -922,6 +1056,7 @@ server.tool("post_agora_message", "Post a signed message to the Agora feed. Anyo
922
1056
  replyTo: args.reply_to,
923
1057
  });
924
1058
  state.agoraFeed = appendToFeed(state.agoraFeed, message);
1059
+ persistAgoraFeed();
925
1060
  return {
926
1061
  content: [{
927
1062
  type: "text",
@@ -1013,6 +1148,131 @@ server.tool("register_agora_agent", "Register an agent in the Agora so their mes
1013
1148
  };
1014
1149
  });
1015
1150
  // ═══════════════════════════════════════
1151
+ // COMMS TOOLS (Agent-to-Agent Communication)
1152
+ // ═══════════════════════════════════════
1153
+ server.tool("send_message", "Send a signed message to another agent. Message is written to comms/to-{agent}.json.", {
1154
+ to: z.string().describe("Recipient agent name (e.g., 'aeoess', 'portalx2', 'claude')"),
1155
+ subject: z.string().describe("Message subject"),
1156
+ message: z.string().describe("Message body"),
1157
+ type: z.string().optional().describe("Message type (default: 'message')"),
1158
+ priority: z.string().optional().describe("Priority: low, normal, high, critical"),
1159
+ data: z.record(z.unknown()).optional().describe("Structured data payload"),
1160
+ }, async (args) => {
1161
+ const keyErr = requireKey();
1162
+ if (keyErr)
1163
+ return { content: [{ type: "text", text: keyErr }] };
1164
+ const fromName = getAgentName();
1165
+ const toFile = join(COMMS_PATH, `to-${args.to}.json`);
1166
+ const fromFile = join(COMMS_PATH, `from-${fromName}.json`);
1167
+ const msg = {
1168
+ id: `msg-${fromName}-${Date.now()}`,
1169
+ timestamp: new Date().toISOString(),
1170
+ from: state.agentId || fromName,
1171
+ to: args.to,
1172
+ type: args.type || 'message',
1173
+ priority: args.priority || 'normal',
1174
+ subject: args.subject,
1175
+ message: args.message,
1176
+ data: args.data || {},
1177
+ signature: await signMessage(args.subject + args.message),
1178
+ };
1179
+ // Write to recipient's inbox
1180
+ const inbox = readCommsFile(toFile);
1181
+ inbox.push(msg);
1182
+ writeCommsFile(toFile, inbox);
1183
+ // Write to sender's outbox
1184
+ const outbox = readCommsFile(fromFile);
1185
+ outbox.push(msg);
1186
+ writeCommsFile(fromFile, outbox);
1187
+ return {
1188
+ content: [{ type: "text", text: JSON.stringify({
1189
+ sent: true, id: msg.id, to: args.to, subject: args.subject,
1190
+ }, null, 2) }],
1191
+ };
1192
+ });
1193
+ server.tool("check_messages", "Check messages addressed to you. Reads from comms/to-{your-agent-name}.json.", {
1194
+ unprocessed_only: z.boolean().optional().describe("Only show unprocessed messages (default: true)"),
1195
+ mark_read: z.boolean().optional().describe("Mark returned messages as processed (default: false)"),
1196
+ }, async (args) => {
1197
+ const name = getAgentName();
1198
+ const filePath = join(COMMS_PATH, `to-${name}.json`);
1199
+ let messages = readCommsFile(filePath);
1200
+ const unprocessedOnly = args.unprocessed_only !== false;
1201
+ if (unprocessedOnly) {
1202
+ messages = messages.filter(m => !m.processed);
1203
+ }
1204
+ if (args.mark_read && messages.length > 0) {
1205
+ const all = readCommsFile(filePath);
1206
+ const ids = new Set(messages.map(m => m.id));
1207
+ for (const m of all) {
1208
+ if (ids.has(m.id))
1209
+ m.processed = true;
1210
+ }
1211
+ writeCommsFile(filePath, all);
1212
+ }
1213
+ return {
1214
+ content: [{ type: "text", text: JSON.stringify({
1215
+ agent: name, count: messages.length,
1216
+ messages: messages.map(m => ({
1217
+ id: m.id, from: m.from, subject: m.subject,
1218
+ priority: m.priority, timestamp: m.timestamp,
1219
+ message: m.message, data: m.data,
1220
+ })),
1221
+ }, null, 2) }],
1222
+ };
1223
+ });
1224
+ server.tool("broadcast", "Send a signed message to all agents via comms/broadcast.json.", {
1225
+ subject: z.string().describe("Message subject"),
1226
+ message: z.string().describe("Message body"),
1227
+ type: z.string().optional().describe("Message type (default: 'broadcast')"),
1228
+ priority: z.string().optional().describe("Priority: low, normal, high, critical"),
1229
+ data: z.record(z.unknown()).optional().describe("Structured data payload"),
1230
+ }, async (args) => {
1231
+ const keyErr = requireKey();
1232
+ if (keyErr)
1233
+ return { content: [{ type: "text", text: keyErr }] };
1234
+ const fromName = getAgentName();
1235
+ const broadcastFile = join(COMMS_PATH, 'broadcast.json');
1236
+ const msg = {
1237
+ id: `bcast-${fromName}-${Date.now()}`,
1238
+ timestamp: new Date().toISOString(),
1239
+ from: state.agentId || fromName,
1240
+ to: 'all',
1241
+ type: args.type || 'broadcast',
1242
+ priority: args.priority || 'normal',
1243
+ subject: args.subject,
1244
+ message: args.message,
1245
+ data: args.data || {},
1246
+ signature: await signMessage(args.subject + args.message),
1247
+ };
1248
+ const broadcasts = readCommsFile(broadcastFile);
1249
+ broadcasts.push(msg);
1250
+ writeCommsFile(broadcastFile, broadcasts);
1251
+ return {
1252
+ content: [{ type: "text", text: JSON.stringify({
1253
+ broadcast: true, id: msg.id, subject: args.subject,
1254
+ }, null, 2) }],
1255
+ };
1256
+ });
1257
+ server.tool("list_agents", "List registered agents from the agent registry (agora/agents.json).", {
1258
+ status_filter: z.string().optional().describe("Filter by status: active, pending, retired (default: all)"),
1259
+ }, async (args) => {
1260
+ let agents = loadAgentsRegistry();
1261
+ if (args.status_filter) {
1262
+ agents = agents.filter(a => a.status === args.status_filter);
1263
+ }
1264
+ return {
1265
+ content: [{ type: "text", text: JSON.stringify({
1266
+ count: agents.length,
1267
+ agents: agents.map(a => ({
1268
+ agentId: a.agentId, name: a.agentName,
1269
+ status: a.status, role: a.role,
1270
+ runtime: a.runtime, capabilities: a.capabilities,
1271
+ })),
1272
+ }, null, 2) }],
1273
+ };
1274
+ });
1275
+ // ═══════════════════════════════════════
1016
1276
  // VALUES / POLICY TOOLS (Layer 2 + 5)
1017
1277
  // ═══════════════════════════════════════
1018
1278
  server.tool("load_values_floor", "Load a Values Floor from YAML. Sets the floor principles for policy evaluation.", {
@@ -1434,13 +1694,14 @@ server.prompt("coordination_role", "Get instructions for your assigned coordinat
1434
1694
  // ═══════════════════════════════════════
1435
1695
  async function main() {
1436
1696
  loadTasks();
1697
+ loadAgoraFeed();
1437
1698
  const roleInfo = state.agentRole
1438
1699
  ? ` | Role: ${state.agentRole}`
1439
1700
  : ' | No role (call identify or set AGENT_KEY)';
1440
1701
  const transport = new StdioServerTransport();
1441
1702
  await server.connect(transport);
1442
1703
  console.error(`Agent Passport MCP Server v2.0 running${roleInfo}`);
1443
- console.error(`Tasks loaded: ${state.taskUnits.size}`);
1704
+ console.error(`Tasks loaded: ${state.taskUnits.size} | Agora messages: ${state.agoraFeed.messages.length}`);
1444
1705
  }
1445
1706
  main().catch((error) => {
1446
1707
  console.error("Fatal error:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-passport-system-mcp",
3
- "version": "2.2.1",
3
+ "version": "2.4.0",
4
4
  "description": "MCP server for Agent Passport System — cryptographic identity, delegation, governance, and deliberation for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,7 +12,9 @@
12
12
  "scripts": {
13
13
  "build": "tsc && chmod 755 build/index.js",
14
14
  "watch": "tsc --watch",
15
- "inspector": "npx @modelcontextprotocol/inspector build/index.js"
15
+ "inspector": "npx @modelcontextprotocol/inspector build/index.js",
16
+ "prepublishOnly": "npm run build",
17
+ "postpublish": "cd ~/aeoess_web && node scripts/propagate.mjs --apply --commit 2>/dev/null || echo 'Propagation skipped (aeoess_web not found)'"
16
18
  },
17
19
  "keywords": [
18
20
  "mcp",