@muretai/agent-entry 1.10.0 → 1.11.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.
package/README.md CHANGED
@@ -1,10 +1,61 @@
1
1
  # Agent Entry
2
2
 
3
- **`llms.txt` describes your site to an AI agent. An Agent Entry *recognises* one.**
3
+ [![npm](https://img.shields.io/npm/v/@muretai/agent-entry.svg?label=npm)](https://www.npmjs.com/package/@muretai/agent-entry)
4
+ [![LICENSE](https://img.shields.io/badge/LICENSE-MIT-blue.svg)](LICENSE)
4
5
 
5
- It verifies who is knocking, opens an account for them, and answers in the same HTTP
6
- response. No signup form, because the visitor's key already is the account. When that
7
- person replaces their phone, your site still knows it is them.
6
+ **Agents already visit your site. Give them a way to become a customer.**
7
+
8
+ ## Contents
9
+
10
+ - [What this is](#what-this-is)
11
+ - [What you get](#what-you-get)
12
+ - [Who is knocking](#who-is-knocking)
13
+ - [Put it on a site](#put-it-on-a-site)
14
+ - [On serverless](#on-serverless)
15
+ - [On WordPress](#on-wordpress)
16
+ - [Pairs with WebMCP](#pairs-with-webmcp-the-tab-conversation-becomes-a-customer)
17
+ - [In production](#in-production)
18
+ - [This package](#this-package)
19
+
20
+ ## What this is
21
+
22
+ They read your pages and leave. No signup, no cookie, nothing in your analytics.
23
+ Agent Entry is a single module you put on the origin. It publishes an
24
+ [Agent Card](https://a2a-protocol.org/latest/specification/)
25
+ at `/.well-known/agent-card.json` — who you are, what you answer, how to knock —
26
+ and then answers one signed POST: verify the visitor, open an account from their
27
+ key, reply signed, same HTTP response.
28
+
29
+ No form. The key *is* the account. Come back next month from another device and
30
+ it is still them.
31
+
32
+ Any A2A client works: their agent, `curl`, not a particular router or ours.
33
+
34
+ Keep WebMCP. A person already in the tab keeps the page. This file sits beside
35
+ it and replaces none of it: an Agent Card and a door, so an agent who arrives
36
+ alone can become a customer — one signed POST, a signed reply.
37
+
38
+ `llms.txt` is a brochure — prose, not a contract. MCP is a tool server for an
39
+ app that holds a token. A different purpose.
40
+
41
+ The agent reads the card **before** it knocks (AE-8). The act is then one
42
+ **POST** `message/send` — already formed correctly — and a signed reply comes
43
+ back on that same request (AE-20).
44
+
45
+ A **handoff** is how a tool on the page, or a tool server, can send the visitor
46
+ *here*: the result names this entry's DID, and the next act is a signed knock at
47
+ this origin. This file does not follow a handoff and does not parse
48
+ `_meta.handoff`. It is where one lands. The `to` in that envelope must be the
49
+ DID printed at startup — the same seed as the card. A visitor (or any router
50
+ they run) is who honours the pointer.
51
+
52
+ | | |
53
+ |---|---|
54
+ | **Who installs this** | Website owners who want agents as *customers* — a returning identity — not only as crawlers. |
55
+ | **Not for** | People writing the visiting agent. This package does not find doors, MCP servers, or WebMCP tools; it *is* a door. |
56
+ | **The problem** | A GET-only document cannot recognise anyone. A signup form does not work for an agent. The first signed POST has to *be* the account. |
57
+
58
+ ![A person in the tab keeps WebMCP. An agent alone can become a customer at the door beside it.](diagrams/become.png)
8
59
 
9
60
  One file. Zero dependencies. No database. Node 20+.
10
61
 
@@ -38,9 +89,26 @@ That is the whole integration. `responder` is called with a verified envelope an
38
89
  what to say back; everything else — signatures, replay, rate limiting, the account
39
90
  ledger — is handled for you.
40
91
 
92
+ ### Not an HTTP 402 challenge
93
+
94
+ [x402](https://x402.org/) v2 over HTTP teaches by **error**. The client asks for the
95
+ resource; the server answers `402 Payment Required` with a `PAYMENT-REQUIRED` header; the
96
+ client **retries** the same resource with `PAYMENT-SIGNATURE`. The error is how the
97
+ client learns what to do next.
98
+
99
+ Agent Entry does the reverse. The Agent Card states the terms **before** anyone knocks
100
+ (AE-8). An HTTP-402-style challenge structurally cannot do that. The first POST is already
101
+ formed correctly and is answered `200` with a signed reply (AE-20). This package does not
102
+ speak x402 and does not return HTTP 402. A protocol refusal here is HTTP `200` plus a
103
+ JSON-RPC error (AE-19). If someone POSTs with no signature at all, `-32001` still carries
104
+ `data.accepts` — the same scheme object as the card — so they can succeed on the next POST
105
+ (AE-24). That is a fallback, not the design.
106
+
107
+ ![x402: request, HTTP 402, retry with payment. Agent Entry: GET the card as 200, then POST already correct, answered 200.](diagrams/x402.svg)
108
+
41
109
  ---
42
110
 
43
- ## What you actually get
111
+ ## What you get
44
112
 
45
113
  **A caller you can trust.** Every message arrives with an Ed25519 signature over six
46
114
  frozen fields. The sender's DID *is* their public key (`did:key`), so verification needs
@@ -69,7 +137,7 @@ resolves it and files them under `owner_did`, so a replaced phone is not a new c
69
137
  Your entry does not need to poll or be told: a node that carries the account learns it on
70
138
  its own, and refuses that key.
71
139
 
72
- ## Say what your door answers
140
+ ### Say what your door answers
73
141
 
74
142
  A visiting agent reads your card **before** it knocks. Left alone, that card says something
75
143
  answers here and nothing about what it answers, so the visitor has to guess and learns your
@@ -84,7 +152,7 @@ createAgentEntry({
84
152
  description: 'Ask what a shoot costs, what the studio does, and how to book. '
85
153
  + 'The answer comes back in the same HTTP response, signed by this domain.',
86
154
  tags: ['studio', 'booking', 'signed', 'inline-reply'],
87
- examples: ['Do you shoot weddings?', 'What does a half-day cost?', 'How do I book?'],
155
+ examples: ['Do you shoot weddings?', 'How much is a half-day?', 'Book Saturday 14:00'],
88
156
  }],
89
157
  });
90
158
  ```
@@ -99,6 +167,14 @@ visitor you will get, so drive your examples through your own responder in your
99
167
  **Declare only what the responder does:** a skill that mentions booking, on an entry that
100
168
  answers questions and hands off nothing, is a signed claim you cannot keep.
101
169
 
170
+ Turning logged knocks into that menu is an offline owner loop, not part of the
171
+ runtime — see [`spec/skill-distill.md`](spec/skill-distill.md). On this machine
172
+ only: `npm run distill` measures whether a proposed menu would have made the
173
+ next first knock useful. `observer: fileSink()` (from
174
+ `scripts/distill/record.mjs`) appends POST outcomes to `var/traces.jsonl`.
175
+ Nothing is uploaded, and the Distiller is never imported by
176
+ `muretai-agent-entry.mjs`.
177
+
102
178
  ### The rest of the settings
103
179
 
104
180
  | option | default | what it does |
@@ -122,12 +198,12 @@ answers questions and hands off nothing, is a signed claim you cannot keep.
122
198
  `seedHex` and `baseUrl` are the two an entry refuses to start without: the seed **is** the
123
199
  address, and the url it publishes must equal the origin the visitor dialled.
124
200
 
125
- ## Who is knocking — observation, never identity
201
+ ## Who is knocking
126
202
 
127
- The person who found you often never opens a browser: they hand your link to their
128
- agent, and the agent fetches your card and knocks. That traffic is invisible to every
129
- page-view metric you have — the only place it can be seen is the door itself. So the
130
- door counts it:
203
+ Observation, never identity. The person who found you often never opens a browser:
204
+ they hand your link to their agent, and the agent fetches your card and knocks.
205
+ That traffic is invisible to every page-view metric you have — the only place it
206
+ can be seen is the door itself. So the door counts it:
131
207
 
132
208
  ```js
133
209
  entry.stats()
@@ -199,7 +275,9 @@ signature over the transport proves who fetched — not who wrote the text, and
199
275
  captured header set is replayable until it expires (minutes), which is why `wba_did`
200
276
  is identification, never authorship.
201
277
 
202
- ## Install
278
+ ## Put it on a site
279
+
280
+ ### Install
203
281
 
204
282
  ```bash
205
283
  npm i @muretai/agent-entry
@@ -219,7 +297,7 @@ upgrade — the ledger is your customer list, and more features stand on keeping
219
297
  an analytics tool covers statistics without one. Both are described under
220
298
  [Before you put it in production](#before-you-put-it-in-production).
221
299
 
222
- ## Put one on a site you already have
300
+ ### Put one on a site you already have
223
301
 
224
302
  A visiting agent knows only your **domain**, so the three paths it walks are fixed — it
225
303
  cannot be told to look elsewhere:
@@ -352,8 +430,9 @@ has already changed the bytes the signature covers, and the only diagnostic anyo
352
430
 
353
431
  ### 3. A reverse proxy — for a site that is not Node at all
354
432
 
355
- WordPress, Rails, a static build. Run the entry as one small process and route three
356
- locations to it:
433
+ Rails, a static build, anything that is not WordPress and not a function. Run the
434
+ entry as one small process and route three locations to it. WordPress has its own
435
+ plugin — [On WordPress](#on-wordpress).
357
436
 
358
437
  ```nginx
359
438
  location = /.well-known/agent-card.json { proxy_pass http://127.0.0.1:8788; }
@@ -364,16 +443,35 @@ location = / {
364
443
  }
365
444
  ```
366
445
 
367
- ### Serverless
446
+ ### On serverless
447
+
448
+ The round-trip shape fits a single function: one signed POST in, one signed reply out.
449
+ What does not fit is the state. A function instance keeps nothing between requests, so
450
+ the replay set, the device→owner pins and the ledger have to live in a store the
451
+ platform keeps, not in memory.
452
+
453
+ This package still runs in process. For Cloudflare Workers, Vercel and Netlify, use
454
+ the deploy templates — this door plus one store adapter per platform:
455
+
456
+ [github.com/muretai/agent-entry-serverless](https://github.com/muretai/agent-entry-serverless)
457
+
458
+ They vendor a library build that includes the `store` seam those adapters need. Do not
459
+ replace that file with an older published copy of this package: versions without the
460
+ seam accept the option and silently ignore it, which puts the three stateful rules
461
+ back into per-instance memory — the failures those templates exist to prevent.
462
+
463
+ Until you are on one of those templates, use one of the long-lived shapes above.
464
+
465
+ ### On WordPress
368
466
 
369
- The round-trip shape fits a single function well, and `handleRequestAsync` is exactly the
370
- handler signature those platforms want. Two things must be settled first, because a
371
- serverless instance keeps nothing between requests: the seed has to come from a secret
372
- environment variable, and the ledger, the device→owner pins and the replay guard have to
373
- live in your own store rather than in memory. A `store` hook for that is the next release;
374
- until then, use one of the three long-lived shapes above.
467
+ If the site is WordPress, do not proxy this Node file in front of it. The plugin is a
468
+ third implementation of the same contract it publishes the card, answers the signed
469
+ POST, and with WooCommerce lets an agent ask about the catalogue. `GET /` stays your
470
+ site.
375
471
 
376
- ## Run the example
472
+ [github.com/muretai/agent-entry-wordpress](https://github.com/muretai/agent-entry-wordpress)
473
+
474
+ ### Run the example
377
475
 
378
476
  ```bash
379
477
  node examples/server.mjs # prints its DID and card URL
@@ -385,7 +483,7 @@ your site's identity**), `AGENT_ENTRY_PORT` (8788), `AGENT_ENTRY_BASE_URL`,
385
483
  no account), `AGENT_ENTRY_PREFER` (your order of the ways in, as one JSON array — see
386
484
  `prefer` above; an invalid list refuses to start).
387
485
 
388
- ## What `baseUrl` may be
486
+ ### What `baseUrl` may be
389
487
 
390
488
  `baseUrl` must be the URL visitors actually dial: it is what your signed card claims, and
391
489
  a card naming a different origin proves nothing about yours.
@@ -422,7 +520,7 @@ Two rules worth knowing before you pick a URL:
422
520
  parser punycodes a host and Python's does not, so the two implementations would otherwise
423
521
  sign different bytes for the same site.
424
522
 
425
- ## One host, many agents
523
+ ### One host, many agents
426
524
 
427
525
  A domain can hold a **fleet** — a front desk, support, sales — each its own agent, its own
428
526
  key, its own address, each contactable directly. Give each one a `baseUrl` that carries its
@@ -474,7 +572,7 @@ const fwd = (entry) => async (req, res) => {
474
572
  };
475
573
  ```
476
574
 
477
- ## Which domains this entry speaks for
575
+ ### Which domains this entry speaks for
478
576
 
479
577
  An entry can name the domains it belongs to:
480
578
 
@@ -505,6 +603,11 @@ Set it from the environment with `AGENT_ENTRY_DOMAINS=studio.example,support.stu
505
603
 
506
604
  ## Pairs with WebMCP: the tab conversation becomes a customer
507
605
 
606
+ WebMCP is not MCP. MCP is a tool *server* over HTTP (a token, a session). WebMCP is tools
607
+ *in the page*, running as whoever is in that browser tab. This package implements neither.
608
+ A site may run WebMCP, an MCP server, and an Agent Entry at once; a headless agent should
609
+ use the last two, not scrape the first.
610
+
508
611
  If your page already exposes [WebMCP](https://github.com/MiguelsPizza/WebMCP) tools, you have
509
612
  one door open: an agent **inside a visitor's browser** can call `check_stock` or `inquire`
510
613
  while that person is on the page. That is useful and it is also temporary — close the tab and
@@ -517,10 +620,11 @@ An Agent Entry is the second door, and it is the one that keeps something:
517
620
  | **WebMCP tools** | a person's agent, in a tab, right now | an answer in the moment |
518
621
  | **Agent Entry** | an agent alone, from anywhere, at any hour | a customer you still recognise next month |
519
622
 
520
- **They connect.** When a WebMCP tool call reaches the point of actually wanting something —
521
- a booking, a quote, a follow-up — the tool returns a small envelope naming your site's DID,
522
- and the visitor's agent then sends a **signed message to your own origin**, where your Agent
523
- Entry receives it:
623
+ **They connect by a handoff.** When a WebMCP (or MCP) tool call reaches the point of
624
+ actually wanting something — a booking, a quote, a follow-up — the tool returns a small
625
+ envelope naming your site's DID. The visitor's agent then sends a **signed message to your
626
+ own origin**, where your Agent Entry receives it. That envelope is a handoff: this package
627
+ is the landing, not the follower.
524
628
 
525
629
  ```js
526
630
  navigator.modelContext.registerTool({
@@ -549,7 +653,9 @@ account layer resolves the owner behind both keys.
549
653
  A search engine makes your site **findable**. An Agent Entry makes it **answerable** — and
550
654
  makes the visitor someone you can recognise the next time.
551
655
 
552
- ## Before you put it in production
656
+ ## In production
657
+
658
+ ### Before you put it in production
553
659
 
554
660
  **Nothing here is needed to start** — an entry runs, and every exchange stays correct,
555
661
  on its in-process state alone; some installers have read this section as a prerequisite,
@@ -600,7 +706,7 @@ What the entry now handles for you at the HTTP layer, so you do not have to:
600
706
  which says a server must accept the absolute form: this endpoint answers exactly the
601
707
  address its card names, and the refusal says so.
602
708
 
603
- ## Counting visits without handing over your customer list
709
+ ### Counting visits without handing over your customer list
604
710
 
605
711
  You will want to know how many agents knocked, how many came back, and what they asked. All
606
712
  three are answerable — and how you answer them decides whether you are counting your visitors
@@ -685,7 +791,9 @@ disclosure that arrives after the visit is not a disclosure, it is a receipt.
685
791
  And if you decide to send raw DIDs anyway, that is your call to make — but say so on the card,
686
792
  in the same breath, in plain words.
687
793
 
688
- ## Two implementations, pinned to each other
794
+ ## This package
795
+
796
+ ### Two implementations, pinned to each other
689
797
 
690
798
  This module is not alone. A Python reference implements the same contract, and the two are
691
799
  held to **identical verdicts** by an acceptance suite: it runs the same attack battery
@@ -727,9 +835,12 @@ for byte. No network, no checkout of ours, nothing to ask us for:
727
835
  npm test
728
836
  ```
729
837
 
730
- Write a third implementation and point it at the same vectors.
838
+ Write another implementation and point it at the same vectors.
839
+ [Agent Entry for WordPress](https://github.com/muretai/agent-entry-wordpress)
840
+ is one that already does — a PHP door, not this file wrapped, held to the same
841
+ golden bytes.
731
842
 
732
- ## Contributing
843
+ ### Contributing
733
844
 
734
845
  This repo is a **published mirror**, rendered out of a private working repository — not the
735
846
  place the next change is written. A pull request opened here will not merge: the next release
@@ -740,7 +851,7 @@ That is not a closed door. **Open an issue** — a bug, a wire-vector disagreeme
740
851
  docs are wrong, a design question — and it gets read and, where it's right, becomes the next
741
852
  release here. That path works; a PR against these files does not.
742
853
 
743
- ## What this is part of
854
+ ### What this is part of
744
855
 
745
856
  [Muretai](https://muretai.com) is a network where AI agents that belong to *different
746
857
  people* can find and talk to each other — with identity, introductions and trust, rather
@@ -750,7 +861,19 @@ that has to stay awake.
750
861
  You do not need the rest of the network to use this file. It is useful on its own the
751
862
  moment an agent knocks.
752
863
 
753
- ## Questions
864
+ This package is the Node door. Two other install paths speak the same contract and do
865
+ not require this file on the host:
866
+
867
+ - [Agent Entry on serverless](https://github.com/muretai/agent-entry-serverless) —
868
+ Cloudflare Workers, Vercel, Netlify
869
+ - [Agent Entry for WordPress](https://github.com/muretai/agent-entry-wordpress) —
870
+ the CMS, including WooCommerce
871
+
872
+ Visitors do not need [Agent Web Router](https://github.com/muretai/agent-web-router).
873
+ That package is one way an agent *finds* doors; this package *is* a door. Either works
874
+ alone. Installing one never implies the other.
875
+
876
+ ### Questions
754
877
 
755
878
  Ask — there is no wrong question about this, and the answers usually improve the docs.
756
879