sigelo-mcp 0.1.4 → 0.1.5

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 CHANGED
@@ -13,9 +13,9 @@ never in the plugin.
13
13
 
14
14
  | Tool | Does |
15
15
  |---|---|
16
- | `sigelo_invite` | a one-line code for another agent's operator |
16
+ | `sigelo_invite` | a short code for another agent's operator |
17
17
  | `sigelo_accept` | answer a code; pairs once the reply verifies |
18
- | `sigelo_contacts` | contacts, pairing ids, `re_pair`, pending codes |
18
+ | `sigelo_contacts` | contacts, four words, `re_pair`, pending codes |
19
19
  | `sigelo_send` | an end-to-end encrypted message |
20
20
  | `sigelo_inbox` | messages and events since the last call |
21
21
  | `sigelo_forget_contact` | revoke a contact, locally |
package/contact.mjs CHANGED
@@ -23,10 +23,11 @@ const PAGE = 24000;
23
23
  const str = (d) => ({ type: 'string', description: d });
24
24
  const int = (d, maximum) => ({ type: 'integer', minimum: 0, ...(maximum !== undefined && { maximum }), description: d });
25
25
  // Whoever holds a code can answer it, once: a leaked code pairs the inviter with a stranger. The
26
- // pairing id (kdf.pairId of the pairing key, which binds both original DIDs) is what two operators
27
- // compare to know their agents paired with each other, as sigelo-agent's accept and contacts show it.
28
- // All 32 digits: someone who swapped the code chooses the encapsulation and can grind a short prefix.
29
- const CONFIRM = 'Show your operator the contact\'s DID and pairing id (pair): they compare all 32 digits with the other operator.';
26
+ // four words (fingerprint: contact/short.mjs, from the pairing id, kdf.pairId of the pairing key,
27
+ // which binds both original DIDs) are what two operators read to each other to know their agents
28
+ // paired with each other, as sigelo-agent's accept, inbox and contacts show them. 44 bits: the
29
+ // 32-digit pair id stays beside them for whoever wants all 128 (THREAT-MODEL.md §2.10).
30
+ const CONFIRM = 'Show your operator the contact\'s DID and its four words (fingerprint): they read them to the other operator; the same words on both sides = nobody, not even the relay, swapped the pairing.';
30
31
  // The proof of work runs at an identity's first login: about 23 s on average at 22 bits on a
31
32
  // phone-class CPU, 92 s at 24 (design/relay-plan.md §11), past some clients' tool timeouts.
32
33
  const POW = 'A first call can take minutes: your first login to the relay solves a proof of work. If your client gives up, call again.';
@@ -34,14 +35,14 @@ const PROFILE = { identity: str('optional profile name [a-z0-9_-], e.g. your sub
34
35
  const obj = (props = {}, required = []) => ({ type: 'object', properties: { ...props, ...PROFILE }, required, additionalProperties: false });
35
36
 
36
37
  const TOOLS = [
37
- ['sigelo_invite', `Make a one-line invite code (sgl1…) for another agent to become your contact. Give the code to its operator out of band; it works once and expires (default 24 h). The pairing completes at your next sigelo_inbox after they accept. ${POW} ${RELAY_DATA}`,
38
- obj({ name: str('the name you will call the new contact'), ttl: int('seconds the code lives, at most 604800 (7 days)') })],
38
+ ['sigelo_invite', `Make an invite for another agent to become your contact: a short code (short, e.g. K7F3-Q9ZP: a person can say it on the phone; 10 minutes, once, at your relay) and the long one (code, sgl1…: for agent channels; default 24 h). Give one to its operator out of band. long: true makes the long code only. The pairing completes at your next sigelo_inbox after they accept. ${POW} ${RELAY_DATA}`,
39
+ obj({ name: str('the name you will call the new contact'), ttl: int('seconds the long code lives, at most 604800 (7 days)'), long: { type: 'boolean', description: 'true: the long code only, no short code' } })],
39
40
  ['sigelo_accept', `Accept an invite code from another agent's operator: sends your answer (whoever holds the code learns your DID); pairs once the inviter's reply verifies against the code. Returns connected with the pairing id, or waiting until the inviter's agent next connects (sigelo_inbox then reports the pairing). ${CONFIRM} ${POW} ${RELAY_DATA}`,
40
- obj({ code: str('the sgl1… code, exactly as received'), name: str('the name you will call this contact'), wait: int(`seconds to wait for the reply, default 10, at most ${WAIT_MAX}`, WAIT_MAX) }, ['code'])],
41
- ['sigelo_contacts', `Your contacts (name, original DID, current DID, revoked, relay, messages queued for them, pairing id, re_pair) and your pending invites and answers. re_pair not null = after a recovery (own_recovery: yours; contact_recovery: theirs) the pairing key may be a thief's: nothing is sent to that contact until you pair again (sigelo_invite, they accept). ${CONFIRM}`, obj()],
41
+ obj({ code: str('the short code (K7F3-Q9ZP; case, I/L/O and dashes do not matter) or the sgl1… code, exactly as received'), name: str('the name you will call this contact'), wait: int(`seconds to wait for the reply, default 10, at most ${WAIT_MAX}`, WAIT_MAX) }, ['code'])],
42
+ ['sigelo_contacts', `Your contacts (name, original DID, current DID, revoked, relay, messages queued for them, pairing id, four words, re_pair) and your pending invites and answers. re_pair not null = after a recovery (own_recovery: yours; contact_recovery: theirs) the pairing key may be a thief's: nothing is sent to that contact until you pair again (sigelo_invite, they accept). ${CONFIRM}`, obj()],
42
43
  ['sigelo_send', `Send an end-to-end encrypted message to a contact, by name or DID. The relay sees only ciphertext. status sent = the relay accepted it (it holds it while the contact reads that mailbox); queued = kept here, it goes by itself at your next call that connects (detail says why: the contact's agent does not hold its mailbox yet, as right after a pairing until it next connects, or is away; or waiting for its fresh prekeys): do not send it again. Error not_paired = your answer to its code waits for the inviter's agent. Error re_pair = pair again first (see sigelo_contacts). ${RELAY_DATA}`,
43
44
  obj({ to: str('contact name or DID'), text: str('the message'), label: str('optional conversation label, e.g. "billing"') }, ['to', 'text'])],
44
- ['sigelo_inbox', `Messages from your contacts since you last asked (each returned once), and events: pairings done or failed (with why), contacts' key rotations, re_pair (pair again after a recovery), refused (your queued messages to such a contact; or a contact's messages signed by a key its recovery overruled, maybe a thief's), waiting (for a contact's prekeys or its agent). last_resort = weaker forward secrecy. dropped: refusals by check. more > 0: call again now. ${DATA} ${RELAY_DATA}`,
45
+ ['sigelo_inbox', `Messages from your contacts since you last asked (each returned once), and events: pairings done (with the four words to show your operator) or failed (with why), contacts' key rotations, re_pair (pair again after a recovery), refused (your queued messages to such a contact; or a contact's messages signed by a key its recovery overruled, maybe a thief's), waiting (for a contact's prekeys or its agent). last_resort = weaker forward secrecy. dropped: refusals by check. more > 0: call again now. ${DATA} ${RELAY_DATA}`,
45
46
  obj({ wait: int(`seconds to wait for a first message, default 0, at most ${WAIT_MAX}`, WAIT_MAX) })],
46
47
  ['sigelo_forget_contact', 'Forget a contact: revoked in your store for good, its mailboxes given up at the relay. Local: the other side is not told.',
47
48
  obj({ did: str('the contact\'s DID "did:sigelo:z…"') }, ['did'])],
@@ -120,8 +121,12 @@ export async function load({ installed, id, env = process.env, clock }) {
120
121
  return { text, isError: false };
121
122
  };
122
123
  const handlers = {
123
- sigelo_invite: serial(({ name, ttl }, path) => carrier(path).invite({ name, ttl })),
124
- // A code never contains whitespace; one pasted from a wrapped mail line may.
124
+ sigelo_invite: serial(({ name, ttl, long }, path) => {
125
+ if (long !== undefined && typeof long !== 'boolean') throw new Error('long: true or false');
126
+ return carrier(path).invite({ name, ttl, short: long !== true });
127
+ }),
128
+ // A code never contains whitespace; one pasted from a wrapped mail line may, and a short one
129
+ // typed as heard ("k7f3 q9zp"). carrier.accept tells the two apart by shape.
125
130
  sigelo_accept: serial(async ({ code, name, wait }, path) => {
126
131
  const r = await carrier(path).accept(text(code, 'code').replace(/\s+/g, ''), { name, wait: seconds(wait, 'wait', 10) });
127
132
  return r.contact ? { ...r, contact: { ...r.contact, pair: pairOf(path, r.contact.did) } } : r;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigelo-mcp",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "mcpName": "io.github.csigelo/sigelo",
5
5
  "description": "MCP server: portable, offline-verifiable identity for AI agents (DID, challenges, attestations, bundles), optional Monero wallet",
6
6
  "keywords": [
@@ -43,8 +43,8 @@
43
43
  "prepublishOnly": "node -e \"if(process.env.SIGELO_PUBLISH!=='1'){console.error('refusing: the npm account and names are an Owner decision (ROADMAP D1); publish the release/build.sh tarballs with SIGELO_PUBLISH=1');process.exit(1)}\""
44
44
  },
45
45
  "dependencies": {
46
- "sigelo": "^0.1.4",
47
- "sigelo-agent": "^0.1.4",
48
- "sigelo-spend": "^0.1.4"
46
+ "sigelo": "^0.1.5",
47
+ "sigelo-agent": "^0.1.5",
48
+ "sigelo-spend": "^0.1.5"
49
49
  }
50
50
  }
package/server.mjs CHANGED
@@ -25,7 +25,7 @@ console.log = console.info = console.debug = console.error;
25
25
 
26
26
  const MODERN = ['2026-07-28']; // per-request _meta era
27
27
  const LEGACY = ['2025-11-25', '2025-06-18', '2025-03-26', '2024-11-05']; // initialize era
28
- const SERVER_INFO = { name: 'sigelo', version: '0.1.4' };
28
+ const SERVER_INFO = { name: 'sigelo', version: '0.1.5' };
29
29
  const PV = 'io.modelcontextprotocol/protocolVersion';
30
30
  const env = process.env;
31
31
  const WALLET = Boolean(env.SIGELO_WALLET_URL && env.SIGELO_WALLET_TOKEN);