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

Viewing files at commit c099f79dc606Commit detailsView latest

connectors/botpress/src/mcp.ts 81 lines · 2.6 KB · typescript
1
import * as sdk from '@botpress/sdk';
2
3
type Json = Record<string, any>;
4
5
const rpcResponse = (body: string): Json => {
6
  const candidates = body
7
    .split('\n')
8
    .filter((line) => line.startsWith('data:'))
9
    .map((line) => line.slice(5).trim())
10
    .filter(Boolean);
11
  const raw = candidates.at(-1) ?? body;
12
  try {
13
    return JSON.parse(raw) as Json;
14
  } catch {
15
    throw new sdk.RuntimeError('OpenClasp returned an invalid MCP response');
16
  }
17
};
18
19
export const callOpenClaspTool = async <T = Json>(
20
  baseUrl: string,
21
  token: string,
22
  name: string,
23
  args: Json = {},
24
): Promise<T> => {
25
  if (!token) throw new sdk.RuntimeError('OpenClasp pairing is not complete');
26
  const response = await fetch(`${baseUrl.replace(/\/$/, '')}/mcp`, {
27
    method: 'POST',
28
    headers: {
29
      authorization: `Bearer ${token}`,
30
      accept: 'application/json, text/event-stream',
31
      'content-type': 'application/json',
32
      'mcp-protocol-version': '2025-06-18',
33
    },
34
    body: JSON.stringify({
35
      jsonrpc: '2.0',
36
      id: crypto.randomUUID(),
37
      method: 'tools/call',
38
      params: { name, arguments: args },
39
    }),
40
  });
41
  const payload = rpcResponse(await response.text());
42
  if (!response.ok || payload.error)
43
    throw new sdk.RuntimeError(
44
      String(payload.error?.message ?? `OpenClasp MCP HTTP ${response.status}`),
45
    );
46
  const result = payload.result as Json | undefined;
47
  if (result?.isError)
48
    throw new sdk.RuntimeError(String(result.content?.[0]?.text ?? 'OpenClasp tool failed'));
49
  const text = result?.content?.find((item: Json) => item.type === 'text')?.text;
50
  if (typeof text !== 'string') return result as T;
51
  try {
52
    return JSON.parse(text) as T;
53
  } catch {
54
    return text as T;
55
  }
56
};
57
58
export const sendMcpA2ARequest = async (value: Json): Promise<boolean> => {
59
  const a2a = value.a2a as Json | undefined;
60
  const endpoint = a2a?.endpoint;
61
  const bearerToken = a2a?.bearerToken;
62
  const request = a2a?.request ?? a2a?.requestTemplate;
63
  if (typeof endpoint !== 'string' || typeof bearerToken !== 'string' || !request) return false;
64
  const response = await fetch(endpoint, {
65
    method: 'POST',
66
    headers: {
67
      authorization: `Bearer ${bearerToken}`,
68
      'content-type': 'application/json',
69
      'A2A-Extensions': 'https://openclasp.dev/extensions/trust/v0.1',
70
    },
71
    body: JSON.stringify({
72
      jsonrpc: '2.0',
73
      id: crypto.randomUUID(),
74
      method: 'message/send',
75
      params: request.params ?? { message: request.message },
76
    }),
77
  });
78
  if (!response.ok)
79
    throw new sdk.RuntimeError(`Peer A2A endpoint returned HTTP ${response.status}`);
80
  return true;
81
};