relay_write
Write a message to a shared AD4M perspective, making it instantly visible across all terminals connected to the same AD4M executor.
Instructions
Write a cross-terminal relay message to AD4M. Both terminals share the same AD4M executor so state is immediately visible.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| perspective_uuid | Yes | Perspective UUID (use ClaudeMemory UUID) | |
| message | Yes | Message to relay | |
| session_id | No | Terminal/session identifier (default: 'default') |
Implementation Reference
- src/index.ts:460-483 (registration)Registration of the 'relay_write' tool via server.tool() with name, description, and Zod schema.
server.tool("relay_write", "Write a cross-terminal relay message to AD4M. Both terminals share the same AD4M executor so state is immediately visible.", { perspective_uuid: z.string().describe("Perspective UUID (use ClaudeMemory UUID)"), message: z.string().describe("Message to relay"), session_id: z.string().optional().describe("Terminal/session identifier (default: 'default')"), }, async ({ perspective_uuid, message, session_id = "default" }) => { const data = await gql( `mutation M($uuid: String!, $link: LinkInput!) { perspectiveAddLink(uuid: $uuid, link: $link) { author timestamp data { source predicate target } } }`, { uuid: perspective_uuid, link: { source: `franc://relay/${session_id}`, predicate: "franc://relay", target: `literal://${message}`, } } ); return ok(data.perspectiveAddLink); } ); - src/index.ts:462-466 (schema)Input schema for relay_write: perspective_uuid (string), message (string), session_id (optional string, defaults to 'default').
{ perspective_uuid: z.string().describe("Perspective UUID (use ClaudeMemory UUID)"), message: z.string().describe("Message to relay"), session_id: z.string().optional().describe("Terminal/session identifier (default: 'default')"), }, - src/index.ts:467-482 (handler)Handler function that executes the relay_write logic: sends a GraphQL mutation to add a link with source=franc://relay/{session_id}, predicate=franc://relay, target=literal://{message}.
async ({ perspective_uuid, message, session_id = "default" }) => { const data = await gql( `mutation M($uuid: String!, $link: LinkInput!) { perspectiveAddLink(uuid: $uuid, link: $link) { author timestamp data { source predicate target } } }`, { uuid: perspective_uuid, link: { source: `franc://relay/${session_id}`, predicate: "franc://relay", target: `literal://${message}`, } } ); return ok(data.perspectiveAddLink); } - src/index.ts:154-156 (helper)Helper function 'ok' that wraps data in the MCP text content response format, used by the relay_write handler to return results.
function ok(data: unknown) { return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] }; } - src/index.ts:132-152 (helper)Helper function 'gql' that sends GraphQL queries to the AD4M executor, used by the relay_write handler to persist the relay message.
async function gql(query: string, variables: Record<string, unknown> = {}): Promise<GqlResult> { const resp = await fetch(AD4M_GQL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, variables }), signal: AbortSignal.timeout(10_000), }); if (!resp.ok) throw new Error(`AD4M HTTP ${resp.status}: ${await resp.text()}`); const json = await resp.json() as { data?: GqlResult; errors?: { message: string }[] }; if (json.errors?.length) { const msg = json.errors[0].message; if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) { throw new Error("AD4M executor not reachable. Start it with: ad4m serve --port 4000"); } if (msg.includes("Unauthorized") || msg.includes("not unlocked")) { throw new Error(`Agent is locked. Unlock with:\ncurl -X POST ${AD4M_GQL} -H 'Content-Type: application/json' -d '{"query":"mutation { agentUnlock(passphrase: \\"YOUR_PASSPHRASE\\") { isUnlocked } }"}'`); } throw new Error(msg); } return json.data ?? {}; }