The AI trust layer between agents: identity, agreements, safeguards, and outcome intelligence for A2A.

Viewing files at commit 68c3fd2f8af0Commit detailsView latest

packages/mcp-server/src/server.ts 191 lines · 5.8 KB · typescript
1
import { McpServer } from '@modelcontextprotocol/server';
2
import { z } from 'zod';
3
import { createIdentity, FixtureFactCheckProvider, TrustEngine } from '../../core/src/index.js';
4
import {
5
  AgentIdentitySchema,
6
  FeedbackSchema,
7
  InteractionEventSchema,
8
  ReceiptSchema,
9
} from '../../protocol/src/index.js';
10
11
export const OPENCLASP_TOOL_NAMES = [
12
  'openclasp_create_identity',
13
  'openclasp_register_agent',
14
  'openclasp_get_profile',
15
  'openclasp_assess_counterparty',
16
  'openclasp_begin_interaction',
17
  'openclasp_record_event',
18
  'openclasp_check_claim',
19
  'openclasp_validate_commitment',
20
  'openclasp_suggest_resolution',
21
  'openclasp_complete_interaction',
22
  'openclasp_submit_feedback',
23
  'openclasp_raise_dispute',
24
  'openclasp_verify_receipt',
25
] as const;
26
27
const text = (value: unknown) => ({
28
  content: [{ type: 'text' as const, text: JSON.stringify(value, null, 2) }],
29
});
30
31
export function buildMcpServer(engine = new TrustEngine()) {
32
  const server = new McpServer(
33
    { name: 'openclasp', version: '0.1.0' },
34
    {
35
      instructions:
36
        'Use OpenClasp to verify counterparties, record signed structured events, and request contextual risk decisions. Never send raw private conversations.',
37
    },
38
  );
39
  return registerOpenClaspTools(server, engine);
40
}
41
42
export function registerOpenClaspTools(server: McpServer, engine = new TrustEngine()) {
43
  const factChecker = new FixtureFactCheckProvider();
44
  const keys = new Map<string, unknown>();
45
46
  server.registerTool(
47
    OPENCLASP_TOOL_NAMES[0],
48
    {
49
      description: 'Create a local Ed25519 agent identity',
50
      inputSchema: z.object({
51
        agentId: z.string(),
52
        operatorRef: z.string(),
53
        capabilities: z.array(z.string()),
54
      }),
55
    },
56
    async (input) => {
57
      const created = createIdentity(input);
58
      keys.set(input.agentId, created.keyPair);
59
      return text(created);
60
    },
61
  );
62
  server.registerTool(
63
    OPENCLASP_TOOL_NAMES[1],
64
    {
65
      description: 'Register and verify a signed agent identity',
66
      inputSchema: AgentIdentitySchema,
67
    },
68
    async (input) => text(engine.registerAgent(input)),
69
  );
70
  server.registerTool(
71
    OPENCLASP_TOOL_NAMES[2],
72
    {
73
      description: 'Get a task-specific behavioural profile',
74
      inputSchema: z.object({ agentId: z.string(), version: z.string(), taskCategory: z.string() }),
75
    },
76
    async (input) => text(engine.getRisk(input.agentId, input.version, input.taskCategory)),
77
  );
78
  server.registerTool(
79
    OPENCLASP_TOOL_NAMES[3],
80
    {
81
      description: 'Assess a counterparty in context',
82
      inputSchema: z.object({
83
        envelope: z.any(),
84
        action: z.string(),
85
        dataClasses: z.array(z.string()).optional(),
86
        humanApproved: z.boolean().optional(),
87
      }),
88
    },
89
    async (input) => text(engine.assess(input as any)),
90
  );
91
  server.registerTool(
92
    OPENCLASP_TOOL_NAMES[4],
93
    {
94
      description: 'Begin an interaction and return its identifier',
95
      inputSchema: z.object({
96
        purpose: z.string(),
97
        parties: z.array(z.string()),
98
        taskCategory: z.string(),
99
      }),
100
    },
101
    async (input) =>
102
      text({ interactionId: crypto.randomUUID(), status: 'contract_required', ...input }),
103
  );
104
  server.registerTool(
105
    OPENCLASP_TOOL_NAMES[5],
106
    {
107
      description: 'Validate and record a signed structured event',
108
      inputSchema: InteractionEventSchema,
109
    },
110
    async (input) => text(engine.recordEvent(input)),
111
  );
112
  server.registerTool(
113
    OPENCLASP_TOOL_NAMES[6],
114
    {
115
      description: 'Check an objective factual claim',
116
      inputSchema: z.object({ claim: z.string(), permission: z.boolean().optional() }),
117
    },
118
    async (input) => text(await factChecker.check(input.claim, input.permission)),
119
  );
120
  server.registerTool(
121
    OPENCLASP_TOOL_NAMES[7],
122
    {
123
      description: 'Check whether a commitment is represented by a signed event',
124
      inputSchema: z.object({ interactionId: z.string().uuid(), commitment: z.string() }),
125
    },
126
    async (input) =>
127
      text({
128
        valid: [...engine.events.values()].some(
129
          (event) =>
130
            event.interactionId === input.interactionId &&
131
            event.eventType === 'commitment' &&
132
            JSON.stringify(event.payload).includes(input.commitment),
133
        ),
134
      }),
135
  );
136
  server.registerTool(
137
    OPENCLASP_TOOL_NAMES[8],
138
    {
139
      description: 'Suggest attributable resolutions for a consented conflict',
140
      inputSchema: z.object({ conflictId: z.string() }),
141
    },
142
    async (input) => text(engine.conflicts.get(input.conflictId)?.possibleResolutions ?? []),
143
  );
144
  server.registerTool(
145
    OPENCLASP_TOOL_NAMES[9],
146
    { description: 'Submit a signed completion receipt', inputSchema: ReceiptSchema },
147
    async (input) => text(engine.submitReceipt(input)),
148
  );
149
  server.registerTool(
150
    OPENCLASP_TOOL_NAMES[10],
151
    { description: 'Submit receipt-backed structured feedback', inputSchema: FeedbackSchema },
152
    async (input) => text(engine.submitFeedback(input)),
153
  );
154
  server.registerTool(
155
    OPENCLASP_TOOL_NAMES[11],
156
    {
157
      description: 'Create a mutually consented dispute',
158
      inputSchema: z.object({
159
        interactionId: z.string().uuid(),
160
        issue: z.string(),
161
        participants: z.array(z.string()).min(2),
162
      }),
163
    },
164
    async (input) =>
165
      text(
166
        engine.createConflict({
167
          interactionId: input.interactionId,
168
          issue: input.issue,
169
          participants: input.participants,
170
          positions: {},
171
          evidence: [],
172
          contractClauses: [],
173
          missingInformation: [],
174
          possibleResolutions: [],
175
        }),
176
      ),
177
  );
178
  server.registerTool(
179
    OPENCLASP_TOOL_NAMES[12],
180
    { description: 'Verify a signed interaction receipt', inputSchema: ReceiptSchema },
181
    async (input) => {
182
      try {
183
        engine.submitReceipt(input);
184
        return text({ valid: true });
185
      } catch {
186
        return text({ valid: false });
187
      }
188
    },
189
  );
190
  return server;
191
}