@integraledger/lcp-mcp-server 0.9.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +16 -0
  4. package/README.md +121 -0
  5. package/dist/annotations.d.ts +41 -0
  6. package/dist/annotations.d.ts.map +1 -0
  7. package/dist/annotations.js +41 -0
  8. package/dist/annotations.js.map +1 -0
  9. package/dist/bin.d.ts +3 -0
  10. package/dist/bin.d.ts.map +1 -0
  11. package/dist/bin.js +5 -0
  12. package/dist/bin.js.map +1 -0
  13. package/dist/dispatch.d.ts +53 -0
  14. package/dist/dispatch.d.ts.map +1 -0
  15. package/dist/dispatch.js +81 -0
  16. package/dist/dispatch.js.map +1 -0
  17. package/dist/index.d.ts +9 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +8 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/node-ports.d.ts +21 -0
  22. package/dist/node-ports.d.ts.map +1 -0
  23. package/dist/node-ports.js +33 -0
  24. package/dist/node-ports.js.map +1 -0
  25. package/dist/ports.d.ts +23 -0
  26. package/dist/ports.d.ts.map +1 -0
  27. package/dist/ports.js +2 -0
  28. package/dist/ports.js.map +1 -0
  29. package/dist/server.d.ts +45 -0
  30. package/dist/server.d.ts.map +1 -0
  31. package/dist/server.js +67 -0
  32. package/dist/server.js.map +1 -0
  33. package/dist/stdio.d.ts +15 -0
  34. package/dist/stdio.d.ts.map +1 -0
  35. package/dist/stdio.js +17 -0
  36. package/dist/stdio.js.map +1 -0
  37. package/dist/tools/compute-atrhash.d.ts +15 -0
  38. package/dist/tools/compute-atrhash.d.ts.map +1 -0
  39. package/dist/tools/compute-atrhash.js +66 -0
  40. package/dist/tools/compute-atrhash.js.map +1 -0
  41. package/dist/tools/extract-reference.d.ts +19 -0
  42. package/dist/tools/extract-reference.d.ts.map +1 -0
  43. package/dist/tools/extract-reference.js +65 -0
  44. package/dist/tools/extract-reference.js.map +1 -0
  45. package/dist/tools/generate-legal-context.d.ts +20 -0
  46. package/dist/tools/generate-legal-context.d.ts.map +1 -0
  47. package/dist/tools/generate-legal-context.js +99 -0
  48. package/dist/tools/generate-legal-context.js.map +1 -0
  49. package/dist/tools/place-reference.d.ts +19 -0
  50. package/dist/tools/place-reference.d.ts.map +1 -0
  51. package/dist/tools/place-reference.js +69 -0
  52. package/dist/tools/place-reference.js.map +1 -0
  53. package/dist/tools/scaffold-integration.d.ts +15 -0
  54. package/dist/tools/scaffold-integration.d.ts.map +1 -0
  55. package/dist/tools/scaffold-integration.js +168 -0
  56. package/dist/tools/scaffold-integration.js.map +1 -0
  57. package/dist/tools/verify-before-pay.d.ts +29 -0
  58. package/dist/tools/verify-before-pay.d.ts.map +1 -0
  59. package/dist/tools/verify-before-pay.js +158 -0
  60. package/dist/tools/verify-before-pay.js.map +1 -0
  61. package/dist/version.d.ts +25 -0
  62. package/dist/version.d.ts.map +1 -0
  63. package/dist/version.js +37 -0
  64. package/dist/version.js.map +1 -0
  65. package/dist/well-known.d.ts +33 -0
  66. package/dist/well-known.d.ts.map +1 -0
  67. package/dist/well-known.js +45 -0
  68. package/dist/well-known.js.map +1 -0
  69. package/package.json +73 -0
  70. package/src/annotations.ts +53 -0
  71. package/src/bin.ts +5 -0
  72. package/src/dispatch.ts +122 -0
  73. package/src/index.ts +15 -0
  74. package/src/node-ports.ts +38 -0
  75. package/src/ports.ts +23 -0
  76. package/src/server.ts +70 -0
  77. package/src/stdio.ts +25 -0
  78. package/src/tools/compute-atrhash.ts +87 -0
  79. package/src/tools/extract-reference.ts +86 -0
  80. package/src/tools/generate-legal-context.ts +127 -0
  81. package/src/tools/place-reference.ts +96 -0
  82. package/src/tools/scaffold-integration.ts +183 -0
  83. package/src/tools/verify-before-pay.ts +194 -0
  84. package/src/version.ts +42 -0
  85. package/src/well-known.ts +51 -0
@@ -0,0 +1,9 @@
1
+ export { readOnlyToolAnnotations, type ToolReach, } from "./annotations.js";
2
+ export { manifestSummary, refusalResult, resolveAdapter, } from "./dispatch.js";
3
+ export { isoNow, nodePorts, REVERSE_DOMAIN_ENV } from "./node-ports.js";
4
+ export type { LcpMcpPorts } from "./ports.js";
5
+ export { createLcpMcpServer, LCP_TOOL_NAMES, SERVER_NAME } from "./server.js";
6
+ export { serveLcpStdio } from "./stdio.js";
7
+ export { serverVersion } from "./version.js";
8
+ export { legalContextUrl, WELL_KNOWN_PATH } from "./well-known.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,KAAK,SAAS,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,eAAe,EACf,aAAa,EACb,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACxE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { readOnlyToolAnnotations, } from "./annotations.js";
2
+ export { manifestSummary, refusalResult, resolveAdapter, } from "./dispatch.js";
3
+ export { isoNow, nodePorts, REVERSE_DOMAIN_ENV } from "./node-ports.js";
4
+ export { createLcpMcpServer, LCP_TOOL_NAMES, SERVER_NAME } from "./server.js";
5
+ export { serveLcpStdio } from "./stdio.js";
6
+ export { serverVersion } from "./version.js";
7
+ export { legalContextUrl, WELL_KNOWN_PATH } from "./well-known.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,GAExB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,eAAe,EACf,aAAa,EACb,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAExE,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { LcpMcpPorts } from "./ports.js";
2
+ /** The environment variable a deployment states its own reverse-domain namespace in. */
3
+ export declare const REVERSE_DOMAIN_ENV = "LCP_MCP_REVERSE_DOMAIN";
4
+ /** The wall clock as an ISO-8601 instant — the `now` port the terms fetcher requires. */
5
+ export declare function isoNow(): string;
6
+ /**
7
+ * The Node wiring: the real, guarded terms fetcher plus whatever the environment states.
8
+ *
9
+ * The fetcher is `agent-guard`'s — HTTPS-only, `redirect: "error"`, every resolved address checked public
10
+ * unicast on every network fetch, body capped while streaming, LCP §2.6 cache discipline. This server
11
+ * fetches URLs a COUNTERPARTY chose, so that is not hardening, it is the minimum: bare `fetch` here would
12
+ * turn `lcp_verify_before_pay` into an SSRF primitive an agent can be talked into aiming anywhere.
13
+ *
14
+ * `LCP_MCP_REVERSE_DOMAIN` is absent by default and stays absent. It is only needed by placements whose
15
+ * carrier is namespaced under the deployment's own domain (Mastercard VI's custom Layer-2 constraint type),
16
+ * and LCP §8 canonizes no per-protocol integration profile — so a default here would write Integra's domain
17
+ * into someone else's signed document in every deployment that forgot to set it. Unset, that one placement
18
+ * throws and names what is missing; every other tool is unaffected.
19
+ */
20
+ export declare function nodePorts(env: Readonly<Record<string, string | undefined>>): LcpMcpPorts;
21
+ //# sourceMappingURL=node-ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-ports.d.ts","sourceRoot":"","sources":["../src/node-ports.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,wFAAwF;AACxF,eAAO,MAAM,kBAAkB,2BAA2B,CAAC;AAE3D,yFAAyF;AACzF,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAChD,WAAW,CAUb"}
@@ -0,0 +1,33 @@
1
+ import { makeCachingFetcher, nodeDnsLookup } from "@integraledger/agent-guard";
2
+ /** The environment variable a deployment states its own reverse-domain namespace in. */
3
+ export const REVERSE_DOMAIN_ENV = "LCP_MCP_REVERSE_DOMAIN";
4
+ /** The wall clock as an ISO-8601 instant — the `now` port the terms fetcher requires. */
5
+ export function isoNow() {
6
+ return new Date().toISOString();
7
+ }
8
+ /**
9
+ * The Node wiring: the real, guarded terms fetcher plus whatever the environment states.
10
+ *
11
+ * The fetcher is `agent-guard`'s — HTTPS-only, `redirect: "error"`, every resolved address checked public
12
+ * unicast on every network fetch, body capped while streaming, LCP §2.6 cache discipline. This server
13
+ * fetches URLs a COUNTERPARTY chose, so that is not hardening, it is the minimum: bare `fetch` here would
14
+ * turn `lcp_verify_before_pay` into an SSRF primitive an agent can be talked into aiming anywhere.
15
+ *
16
+ * `LCP_MCP_REVERSE_DOMAIN` is absent by default and stays absent. It is only needed by placements whose
17
+ * carrier is namespaced under the deployment's own domain (Mastercard VI's custom Layer-2 constraint type),
18
+ * and LCP §8 canonizes no per-protocol integration profile — so a default here would write Integra's domain
19
+ * into someone else's signed document in every deployment that forgot to set it. Unset, that one placement
20
+ * throws and names what is missing; every other tool is unaffected.
21
+ */
22
+ export function nodePorts(env) {
23
+ const reverseDomain = env[REVERSE_DOMAIN_ENV];
24
+ return {
25
+ fetcher: makeCachingFetcher({
26
+ httpFetch: fetch,
27
+ now: isoNow,
28
+ lookup: nodeDnsLookup,
29
+ }),
30
+ ...(reverseDomain !== undefined ? { deployment: { reverseDomain } } : {}),
31
+ };
32
+ }
33
+ //# sourceMappingURL=node-ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-ports.js","sourceRoot":"","sources":["../src/node-ports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG/E,wFAAwF;AACxF,MAAM,CAAC,MAAM,kBAAkB,GAAG,wBAAwB,CAAC;AAE3D,yFAAyF;AACzF,MAAM,UAAU,MAAM;IACpB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CACvB,GAAiD;IAEjD,MAAM,aAAa,GAAG,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC9C,OAAO;QACL,OAAO,EAAE,kBAAkB,CAAC;YAC1B,SAAS,EAAE,KAAK;YAChB,GAAG,EAAE,MAAM;YACX,MAAM,EAAE,aAAa;SACtB,CAAC;QACF,GAAG,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { TermsFetcher } from "@integraledger/agent-guard";
2
+ import type { PlacementDeployment } from "@integraledger/lcp-placements";
3
+ /**
4
+ * What a deployment supplies before this server can serve anything.
5
+ *
6
+ * NO DEFAULTS, and both entries are the reason. The fetcher is the buyer gate's own HTTPS-only,
7
+ * SSRF-guarded, size-capped, LCP §2.6-cached implementation (`agent-guard`'s `makeCachingFetcher`) — the
8
+ * URLs this server fetches are chosen by a *counterparty*, so a server that quietly defaulted to bare
9
+ * `fetch` would silently drop the guard that makes those fetches safe. `nodePorts` wires the real one for a
10
+ * Node deployment; a Workers deployment supplies its own `lookup`.
11
+ *
12
+ * `deployment` is genuinely optional rather than defaulted: eight of the nine registered placements are
13
+ * singletons needing nothing from it, and only Mastercard VI's namespaced factory requires a reverse domain.
14
+ * Omitting it makes `lcp_place_reference` throw for that ONE protocol, naming what is missing — which is the
15
+ * registry's own rule, not a second copy of it here.
16
+ */
17
+ export interface LcpMcpPorts {
18
+ /** Fetches terms and discovery documents. See `@integraledger/agent-guard`'s `makeCachingFetcher`. */
19
+ readonly fetcher: TermsFetcher;
20
+ /** The deployment's own reverse-domain namespace, for namespaced placement registrations. */
21
+ readonly deployment?: PlacementDeployment;
22
+ }
23
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEzE;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,sGAAsG;IACtG,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,6FAA6F;IAC7F,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC;CAC3C"}
package/dist/ports.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":""}
@@ -0,0 +1,45 @@
1
+ import { McpServer } from "@modelcontextprotocol/server";
2
+ import type { LcpMcpPorts } from "./ports.js";
3
+ /**
4
+ * The MCP server name, and the reason it is not the one the specification illustrates.
5
+ *
6
+ * LCP v1.38 §C.9 shows a different tool vocabulary (`get_legal_context`, `verify_terms`, …) and says in the
7
+ * same breath that "the standard does not canonize a particular tool registry" and that the MCP stewards
8
+ * are invited to publish canonical names. An appendix example is not an interoperability fact: names an
9
+ * agent host has already learned are, because a host that has learned a name breaks when it changes.
10
+ * Renaming these to match an illustration the specification itself declines to canonize would break working
11
+ * integrations and make nothing canonical in exchange.
12
+ */
13
+ export declare const SERVER_NAME = "lcp-mcp";
14
+ /**
15
+ * Every tool this server exposes, in registration order.
16
+ *
17
+ * Exported so the boundary can be ASSERTED rather than described. LCP §C.9's illustrative table also lists
18
+ * `accept_terms`, `create_agreement`, `get_agreement`, `initiate_dispute` and `get_dispute_status`; none of
19
+ * them is here and none is coming. Recording an acceptance, holding the agreement record, or running the
20
+ * dispute are the roles of a custodian and a forum — we are neither. Integra is the record engine; the
21
+ * custodian of the record and the forum for the dispute are the deployment's and the counterparties' own.
22
+ * A server that offered `initiate_dispute` would be asserting an operating role the mandate forbids, and it
23
+ * is far easier to not add a seventh tool than to remove it after an agent has learned to call it.
24
+ */
25
+ export declare const LCP_TOOL_NAMES: readonly ["lcp_compute_atrhash", "lcp_generate_legal_context", "lcp_verify_before_pay", "lcp_scaffold_integration", "lcp_place_reference", "lcp_extract_reference"];
26
+ /**
27
+ * Build the LCP MCP server.
28
+ *
29
+ * A FACTORY, not a singleton, because that is what the transports want: `serveStdio` and
30
+ * `createMcpHandler` both take a factory and may build one instance per connection or per era. Handing
31
+ * them a shared instance would make one client's state another's.
32
+ *
33
+ * The server ends up declaring `tools` and nothing else, and it declares it by REGISTERING tools rather
34
+ * than by asserting a capability in the constructor. MCP requires a server that supports tools to declare
35
+ * the capability and does not permit declaring one that is not served; the SDK derives the declaration from
36
+ * the registrations, so a constructor-side `{ capabilities: { tools: {} } }` is a second statement of the
37
+ * same fact that cannot be wrong today and could be wrong tomorrow. Measured: removing it changes nothing a
38
+ * client sees, which is exactly why it does not belong.
39
+ *
40
+ * There are no resources and no prompts. §C.9 illustrates both — `lcp://agreement/{id}`,
41
+ * `dispute_evidence_assembly` — and every one of its examples is an agreement or dispute surface, which is
42
+ * the boundary above. When there is a resource to serve that is not one of those, it is declared then.
43
+ */
44
+ export declare function createLcpMcpServer(ports: LcpMcpPorts): McpServer;
45
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAS9C;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,YACzB,qBAAqB,EACrB,4BAA4B,EAC5B,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,CACf,CAAC;AAEX;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAShE"}
package/dist/server.js ADDED
@@ -0,0 +1,67 @@
1
+ import { McpServer } from "@modelcontextprotocol/server";
2
+ import { registerComputeAtrHash } from "./tools/compute-atrhash.js";
3
+ import { registerExtractReference } from "./tools/extract-reference.js";
4
+ import { registerGenerateLegalContext } from "./tools/generate-legal-context.js";
5
+ import { registerPlaceReference } from "./tools/place-reference.js";
6
+ import { registerScaffoldIntegration } from "./tools/scaffold-integration.js";
7
+ import { registerVerifyBeforePay } from "./tools/verify-before-pay.js";
8
+ import { serverVersion } from "./version.js";
9
+ /**
10
+ * The MCP server name, and the reason it is not the one the specification illustrates.
11
+ *
12
+ * LCP v1.38 §C.9 shows a different tool vocabulary (`get_legal_context`, `verify_terms`, …) and says in the
13
+ * same breath that "the standard does not canonize a particular tool registry" and that the MCP stewards
14
+ * are invited to publish canonical names. An appendix example is not an interoperability fact: names an
15
+ * agent host has already learned are, because a host that has learned a name breaks when it changes.
16
+ * Renaming these to match an illustration the specification itself declines to canonize would break working
17
+ * integrations and make nothing canonical in exchange.
18
+ */
19
+ export const SERVER_NAME = "lcp-mcp";
20
+ /**
21
+ * Every tool this server exposes, in registration order.
22
+ *
23
+ * Exported so the boundary can be ASSERTED rather than described. LCP §C.9's illustrative table also lists
24
+ * `accept_terms`, `create_agreement`, `get_agreement`, `initiate_dispute` and `get_dispute_status`; none of
25
+ * them is here and none is coming. Recording an acceptance, holding the agreement record, or running the
26
+ * dispute are the roles of a custodian and a forum — we are neither. Integra is the record engine; the
27
+ * custodian of the record and the forum for the dispute are the deployment's and the counterparties' own.
28
+ * A server that offered `initiate_dispute` would be asserting an operating role the mandate forbids, and it
29
+ * is far easier to not add a seventh tool than to remove it after an agent has learned to call it.
30
+ */
31
+ export const LCP_TOOL_NAMES = [
32
+ "lcp_compute_atrhash",
33
+ "lcp_generate_legal_context",
34
+ "lcp_verify_before_pay",
35
+ "lcp_scaffold_integration",
36
+ "lcp_place_reference",
37
+ "lcp_extract_reference",
38
+ ];
39
+ /**
40
+ * Build the LCP MCP server.
41
+ *
42
+ * A FACTORY, not a singleton, because that is what the transports want: `serveStdio` and
43
+ * `createMcpHandler` both take a factory and may build one instance per connection or per era. Handing
44
+ * them a shared instance would make one client's state another's.
45
+ *
46
+ * The server ends up declaring `tools` and nothing else, and it declares it by REGISTERING tools rather
47
+ * than by asserting a capability in the constructor. MCP requires a server that supports tools to declare
48
+ * the capability and does not permit declaring one that is not served; the SDK derives the declaration from
49
+ * the registrations, so a constructor-side `{ capabilities: { tools: {} } }` is a second statement of the
50
+ * same fact that cannot be wrong today and could be wrong tomorrow. Measured: removing it changes nothing a
51
+ * client sees, which is exactly why it does not belong.
52
+ *
53
+ * There are no resources and no prompts. §C.9 illustrates both — `lcp://agreement/{id}`,
54
+ * `dispute_evidence_assembly` — and every one of its examples is an agreement or dispute surface, which is
55
+ * the boundary above. When there is a resource to serve that is not one of those, it is declared then.
56
+ */
57
+ export function createLcpMcpServer(ports) {
58
+ const server = new McpServer({ name: SERVER_NAME, version: serverVersion() });
59
+ registerComputeAtrHash(server, ports);
60
+ registerGenerateLegalContext(server, ports);
61
+ registerVerifyBeforePay(server, ports);
62
+ registerScaffoldIntegration(server);
63
+ registerPlaceReference(server, ports);
64
+ registerExtractReference(server, ports);
65
+ return server;
66
+ }
67
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAEzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAErC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,qBAAqB;IACrB,4BAA4B;IAC5B,uBAAuB;IACvB,0BAA0B;IAC1B,qBAAqB;IACrB,uBAAuB;CACf,CAAC;AAEX;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAkB;IACnD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC;IAC9E,sBAAsB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACtC,4BAA4B,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5C,uBAAuB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACvC,2BAA2B,CAAC,MAAM,CAAC,CAAC;IACpC,sBAAsB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACtC,wBAAwB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACxC,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type ServeStdioOptions, type StdioServerHandle } from "@modelcontextprotocol/server/stdio";
2
+ import type { LcpMcpPorts } from "./ports.js";
3
+ /**
4
+ * Serve this server over stdio — the transport every desktop agent host speaks.
5
+ *
6
+ * `serveStdio` rather than a hand-wired `StdioServerTransport`: the MCP v2 entry owns the era decision for
7
+ * the connection, pinning ONE instance from the factory for its lifetime, so a 2025-era client and a
8
+ * 2026-07-28 client are both served correctly from the same registration. Hand-wiring a single transport
9
+ * would serve only one of them, and which one would depend on the SDK version rather than on the client.
10
+ *
11
+ * `options` is passed straight through — a test drives a linked in-memory transport through it, and a
12
+ * deployment that binds stdio to a socket per MCP's custom-transport guidance uses the same door.
13
+ */
14
+ export declare function serveLcpStdio(ports: LcpMcpPorts, options?: ServeStdioOptions): StdioServerHandle;
15
+ //# sourceMappingURL=stdio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EAEvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,iBAAiB,CAEnB"}
package/dist/stdio.js ADDED
@@ -0,0 +1,17 @@
1
+ import { serveStdio, } from "@modelcontextprotocol/server/stdio";
2
+ import { createLcpMcpServer } from "./server.js";
3
+ /**
4
+ * Serve this server over stdio — the transport every desktop agent host speaks.
5
+ *
6
+ * `serveStdio` rather than a hand-wired `StdioServerTransport`: the MCP v2 entry owns the era decision for
7
+ * the connection, pinning ONE instance from the factory for its lifetime, so a 2025-era client and a
8
+ * 2026-07-28 client are both served correctly from the same registration. Hand-wiring a single transport
9
+ * would serve only one of them, and which one would depend on the SDK version rather than on the client.
10
+ *
11
+ * `options` is passed straight through — a test drives a linked in-memory transport through it, and a
12
+ * deployment that binds stdio to a socket per MCP's custom-transport guidance uses the same door.
13
+ */
14
+ export function serveLcpStdio(ports, options) {
15
+ return serveStdio(() => createLcpMcpServer(ports), options);
16
+ }
17
+ //# sourceMappingURL=stdio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,UAAU,GACX,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAkB,EAClB,OAA2B;IAE3B,OAAO,UAAU,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { LcpMcpPorts } from "../ports.js";
3
+ /**
4
+ * `lcp_compute_atrhash` — the ATR fingerprint of a terms document, from inline text or a fetchable URL.
5
+ *
6
+ * Grounded on `kernel.hashAtr` (the one implementation of the ATR fingerprint) and
7
+ * `binding-core.encodeLegalContextString` (the one implementation of the §8.1 carrier), rather than on a
8
+ * local `createHash("sha256")` and a template literal. The carrier string it returns is the exact input
9
+ * `lcp_place_reference` takes, so compute → place composes without the agent reformatting anything.
10
+ *
11
+ * EXACTLY ONE of `terms`/`termsUrl`. Neither is a caller who has not said what to hash; both is a caller
12
+ * whose two inputs may disagree, and picking one would silently hash something the caller did not mean.
13
+ */
14
+ export declare function registerComputeAtrHash(server: McpServer, ports: LcpMcpPorts): void;
15
+ //# sourceMappingURL=compute-atrhash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute-atrhash.d.ts","sourceRoot":"","sources":["../../src/tools/compute-atrhash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAyB/C;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,WAAW,GACjB,IAAI,CA0CN"}
@@ -0,0 +1,66 @@
1
+ import { encodeLegalContextString } from "@integraledger/lcp-binding-core";
2
+ import { hashAtr } from "@integraledger/lcp-kernel";
3
+ import { z } from "zod";
4
+ import { readOnlyToolAnnotations } from "../annotations.js";
5
+ const inputSchema = z.object({
6
+ terms: z
7
+ .string()
8
+ .optional()
9
+ .describe("Raw terms text to hash, as UTF-8 bytes. Provide this OR termsUrl, never both."),
10
+ termsUrl: z
11
+ .url()
12
+ .optional()
13
+ .describe("HTTPS URL of the terms document to fetch and hash. Provide this OR terms, never both."),
14
+ });
15
+ const outputSchema = z.object({
16
+ atrHash: z.string().describe("0x-prefixed SHA-256 of the terms bytes."),
17
+ bytes: z.number().int().describe("How many bytes were hashed."),
18
+ reference: z
19
+ .string()
20
+ .describe("The LCP §8.1 carrier string, `lcp:sha256:0x…`."),
21
+ });
22
+ /**
23
+ * `lcp_compute_atrhash` — the ATR fingerprint of a terms document, from inline text or a fetchable URL.
24
+ *
25
+ * Grounded on `kernel.hashAtr` (the one implementation of the ATR fingerprint) and
26
+ * `binding-core.encodeLegalContextString` (the one implementation of the §8.1 carrier), rather than on a
27
+ * local `createHash("sha256")` and a template literal. The carrier string it returns is the exact input
28
+ * `lcp_place_reference` takes, so compute → place composes without the agent reformatting anything.
29
+ *
30
+ * EXACTLY ONE of `terms`/`termsUrl`. Neither is a caller who has not said what to hash; both is a caller
31
+ * whose two inputs may disagree, and picking one would silently hash something the caller did not mean.
32
+ */
33
+ export function registerComputeAtrHash(server, ports) {
34
+ server.registerTool("lcp_compute_atrhash", {
35
+ description: "Compute the LCP ATR hash — the SHA-256 over the exact terms bytes — from inline `terms` text " +
36
+ "or a fetchable `termsUrl`. Returns `{ atrHash, bytes, reference }`, where `reference` is the " +
37
+ "canonical `lcp:sha256:0x…` carrier string. A `termsUrl` is fetched over HTTPS only, is not " +
38
+ "followed through redirects, and is size-capped.",
39
+ inputSchema,
40
+ outputSchema,
41
+ annotations: readOnlyToolAnnotations("Compute LCP ATR hash", "network"),
42
+ }, async (args) => {
43
+ if (args.terms !== undefined && args.termsUrl !== undefined)
44
+ throw new Error("provide exactly one of `terms` or `termsUrl` — both were supplied, and they may disagree");
45
+ let bytes;
46
+ if (args.termsUrl !== undefined)
47
+ bytes = (await ports.fetcher.fetch(args.termsUrl)).bytes;
48
+ else if (args.terms !== undefined)
49
+ bytes = new TextEncoder().encode(args.terms);
50
+ else
51
+ throw new Error("provide exactly one of `terms` or `termsUrl` — neither was supplied");
52
+ const atrHash = await hashAtr(bytes);
53
+ const out = {
54
+ atrHash,
55
+ bytes: bytes.byteLength,
56
+ reference: encodeLegalContextString({ type: "sha256", value: atrHash }),
57
+ };
58
+ return {
59
+ content: [
60
+ { type: "text", text: JSON.stringify(out, null, 2) },
61
+ ],
62
+ structuredContent: out,
63
+ };
64
+ });
65
+ }
66
+ //# sourceMappingURL=compute-atrhash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute-atrhash.js","sourceRoot":"","sources":["../../src/tools/compute-atrhash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEpD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,+EAA+E,CAChF;IACH,QAAQ,EAAE,CAAC;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,CACP,uFAAuF,CACxF;CACJ,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;IACvE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;IAC/D,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,QAAQ,CAAC,gDAAgD,CAAC;CAC9D,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAiB,EACjB,KAAkB;IAElB,MAAM,CAAC,YAAY,CACjB,qBAAqB,EACrB;QACE,WAAW,EACT,+FAA+F;YAC/F,+FAA+F;YAC/F,6FAA6F;YAC7F,iDAAiD;QACnD,WAAW;QACX,YAAY;QACZ,WAAW,EAAE,uBAAuB,CAAC,sBAAsB,EAAE,SAAS,CAAC;KACxE,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;YACzD,MAAM,IAAI,KAAK,CACb,0FAA0F,CAC3F,CAAC;QACJ,IAAI,KAAiB,CAAC;QACtB,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;YAC7B,KAAK,GAAG,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;aACtD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAC/B,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;YAE7C,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;QAEJ,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG;YACV,OAAO;YACP,KAAK,EAAE,KAAK,CAAC,UAAU;YACvB,SAAS,EAAE,wBAAwB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;SACxE,CAAC;QACF,OAAO;YACL,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;aAC9D;YACD,iBAAiB,EAAE,GAAG;SACvB,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { LcpMcpPorts } from "../ports.js";
3
+ /**
4
+ * `lcp_extract_reference` — recover an LCP reference from a commerce protocol's own document.
5
+ *
6
+ * The read half of `lcp_place_reference`, through the same placement registry, so a document one produces the
7
+ * other reads. It is what lets an agent on the receiving side of a counterparty's ACP session, x402
8
+ * challenge or A2A task ask "which terms does this document say govern it?" without knowing where that
9
+ * protocol keeps them.
10
+ *
11
+ * A DOCUMENT WITH NO REFERENCE REFUSES; it never returns a placeholder. That refusal is the answer — the
12
+ * counterparty placed nothing — and an empty string or a null would read as a reference to nothing.
13
+ *
14
+ * The adapter reads the manifest's declared field AND its declared aliases, so a counterparty using a
15
+ * spelling the manifest records as accepted is read rather than refused. Which spellings those are is the
16
+ * manifest's data, not this tool's opinion.
17
+ */
18
+ export declare function registerExtractReference(server: McpServer, ports: LcpMcpPorts): void;
19
+ //# sourceMappingURL=extract-reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-reference.d.ts","sourceRoot":"","sources":["../../src/tools/extract-reference.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAS9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAsB/C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,WAAW,GACjB,IAAI,CAkCN"}
@@ -0,0 +1,65 @@
1
+ import { encodeLegalContextString } from "@integraledger/lcp-binding-core";
2
+ import { supportedProtocols } from "@integraledger/lcp-placements";
3
+ import { z } from "zod";
4
+ import { readOnlyToolAnnotations } from "../annotations.js";
5
+ import { manifestSummary, PLACEMENT_SUMMARY_SCHEMA, refusalResult, resolveAdapter, } from "../dispatch.js";
6
+ const inputSchema = z.object({
7
+ protocol: z
8
+ .string()
9
+ .describe("The commerce protocol this document belongs to. See the description for the set this build reads."),
10
+ document: z
11
+ .record(z.string(), z.unknown())
12
+ .describe("The host protocol's own document, as JSON."),
13
+ });
14
+ const outputSchema = z.object({
15
+ reference: z
16
+ .string()
17
+ .describe("The LCP §8.1 carrier string recovered from the document."),
18
+ type: z.string().describe("The §8.2 carrier type, e.g. `sha256`."),
19
+ value: z.string().describe("The carrier value, e.g. the 0x ATR hash."),
20
+ placement: PLACEMENT_SUMMARY_SCHEMA,
21
+ });
22
+ /**
23
+ * `lcp_extract_reference` — recover an LCP reference from a commerce protocol's own document.
24
+ *
25
+ * The read half of `lcp_place_reference`, through the same placement registry, so a document one produces the
26
+ * other reads. It is what lets an agent on the receiving side of a counterparty's ACP session, x402
27
+ * challenge or A2A task ask "which terms does this document say govern it?" without knowing where that
28
+ * protocol keeps them.
29
+ *
30
+ * A DOCUMENT WITH NO REFERENCE REFUSES; it never returns a placeholder. That refusal is the answer — the
31
+ * counterparty placed nothing — and an empty string or a null would read as a reference to nothing.
32
+ *
33
+ * The adapter reads the manifest's declared field AND its declared aliases, so a counterparty using a
34
+ * spelling the manifest records as accepted is read rather than refused. Which spellings those are is the
35
+ * manifest's data, not this tool's opinion.
36
+ */
37
+ export function registerExtractReference(server, ports) {
38
+ server.registerTool("lcp_extract_reference", {
39
+ description: "Recover the LCP legal-context reference from a commerce protocol's own document, reading the " +
40
+ "field that protocol's placement manifest declares. Supported protocols in this build: " +
41
+ `${supportedProtocols().join(", ")}. Returns the \`lcp:{type}:{value}\` carrier string and where ` +
42
+ "it was found. A document carrying no reference is refused, not answered with an empty value.",
43
+ inputSchema,
44
+ outputSchema,
45
+ annotations: readOnlyToolAnnotations("Extract an LCP reference", "closed"),
46
+ }, (args) => {
47
+ const adapter = resolveAdapter(args.protocol, ports);
48
+ const outcome = adapter.extract(args.document);
49
+ if (!("ok" in outcome))
50
+ return refusalResult(outcome);
51
+ const out = {
52
+ reference: encodeLegalContextString(outcome.value),
53
+ type: outcome.value.type,
54
+ value: outcome.value.value,
55
+ placement: manifestSummary(adapter.manifest),
56
+ };
57
+ return {
58
+ content: [
59
+ { type: "text", text: JSON.stringify(out, null, 2) },
60
+ ],
61
+ structuredContent: out,
62
+ };
63
+ });
64
+ }
65
+ //# sourceMappingURL=extract-reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-reference.js","sourceRoot":"","sources":["../../src/tools/extract-reference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,aAAa,EACb,cAAc,GACf,MAAM,gBAAgB,CAAC;AAGxB,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,QAAQ,CACP,mGAAmG,CACpG;IACH,QAAQ,EAAE,CAAC;SACR,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;SAC/B,QAAQ,CAAC,4CAA4C,CAAC;CAC1D,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,QAAQ,CAAC,0DAA0D,CAAC;IACvE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAClE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;IACtE,SAAS,EAAE,wBAAwB;CACpC,CAAC,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAiB,EACjB,KAAkB;IAElB,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,WAAW,EACT,+FAA+F;YAC/F,wFAAwF;YACxF,GAAG,kBAAkB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,gEAAgE;YAClG,8FAA8F;QAChG,WAAW;QACX,YAAY;QACZ,WAAW,EAAE,uBAAuB,CAClC,0BAA0B,EAC1B,QAAQ,CACT;KACF,EACD,CAAC,IAAI,EAAE,EAAE;QACP,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,CAAC,IAAI,IAAI,OAAO,CAAC;YAAE,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG;YACV,SAAS,EAAE,wBAAwB,CAAC,OAAO,CAAC,KAAK,CAAC;YAClD,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK;YAC1B,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC;SAC7C,CAAC;QACF,OAAO;YACL,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;aAC9D;YACD,iBAAiB,EAAE,GAAG;SACvB,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { LcpMcpPorts } from "../ports.js";
3
+ /**
4
+ * `lcp_generate_legal_context` — build a ready-to-publish `/.well-known/legal-context.json`.
5
+ *
6
+ * Grounded on `discovery.emit`, which drops undefined fields and then VALIDATES what is left, so a profile
7
+ * this tool cannot legally emit fails loudly here rather than 404-ing an agent later. The `atrHash` is
8
+ * `kernel.hashAtr` over the bytes actually served at `termsUrl` — never a value the caller supplies, because
9
+ * a hash the author asserts rather than computes is the one field an author can get wrong and never notice.
10
+ *
11
+ * `termsFormat` is checked against `discovery.KNOWN_TERMS_FORMATS` rather than re-declared as an enum here:
12
+ * the token set is the protocol's, and a second copy in this package would be free to drift from it. The
13
+ * free-string `termsFormat` invites values like `text/plain` and `application/pdf`,
14
+ * neither of which is an LCP §2.5 token.
15
+ *
16
+ * IT RETURNS THE DOCUMENT; IT DOES NOT PUBLISH ONE. Serving it is the deployment's act, on the deployment's
17
+ * origin, under the deployment's name.
18
+ */
19
+ export declare function registerGenerateLegalContext(server: McpServer, ports: LcpMcpPorts): void;
20
+ //# sourceMappingURL=generate-legal-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-legal-context.d.ts","sourceRoot":"","sources":["../../src/tools/generate-legal-context.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAsD/C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,WAAW,GACjB,IAAI,CA2CN"}
@@ -0,0 +1,99 @@
1
+ import { emit, isKnownTermsFormat, KNOWN_TERMS_FORMATS, } from "@integraledger/lcp-discovery";
2
+ import { hashAtr } from "@integraledger/lcp-kernel";
3
+ import { z } from "zod";
4
+ import { readOnlyToolAnnotations } from "../annotations.js";
5
+ const inputSchema = z.object({
6
+ termsUrl: z
7
+ .url()
8
+ .describe("HTTPS URL of the terms document. Fetched, so it must be live."),
9
+ termsFormat: z
10
+ .string()
11
+ .describe(`The LCP §2.5 format token for the terms document — one of: ${KNOWN_TERMS_FORMATS.join(", ")}.`),
12
+ acceptanceRequired: z
13
+ .boolean()
14
+ .optional()
15
+ .describe("Set true where the service requires explicit signed acceptance before transacting (LCP §3, Level 3)."),
16
+ disputeResolution: z
17
+ .record(z.string(), z.unknown())
18
+ .optional()
19
+ .describe("The service's dispute-resolution metadata (LCP §3, Level 4), e.g. { method, jurisdiction }."),
20
+ returns: z.string().optional().describe("URL of the returns policy."),
21
+ api: z
22
+ .url()
23
+ .optional()
24
+ .describe("Entry point to richer legal functionality (LCP §3, Level 4)."),
25
+ });
26
+ const outputSchema = z.object({
27
+ terms: z.string().describe("The terms document URL, echoed from the input."),
28
+ termsFormat: z.string().describe("The LCP §2.5 format token."),
29
+ atrHash: z
30
+ .string()
31
+ .describe("SHA-256 over the bytes served at `terms`, computed here."),
32
+ acceptanceRequired: z
33
+ .boolean()
34
+ .optional()
35
+ .describe("Present only where the input declared it."),
36
+ disputeResolution: z
37
+ .record(z.string(), z.unknown())
38
+ .optional()
39
+ .describe("Present only where the input declared it."),
40
+ returns: z
41
+ .string()
42
+ .optional()
43
+ .describe("Present only where the input declared it."),
44
+ api: z
45
+ .string()
46
+ .optional()
47
+ .describe("Present only where the input declared it."),
48
+ });
49
+ /**
50
+ * `lcp_generate_legal_context` — build a ready-to-publish `/.well-known/legal-context.json`.
51
+ *
52
+ * Grounded on `discovery.emit`, which drops undefined fields and then VALIDATES what is left, so a profile
53
+ * this tool cannot legally emit fails loudly here rather than 404-ing an agent later. The `atrHash` is
54
+ * `kernel.hashAtr` over the bytes actually served at `termsUrl` — never a value the caller supplies, because
55
+ * a hash the author asserts rather than computes is the one field an author can get wrong and never notice.
56
+ *
57
+ * `termsFormat` is checked against `discovery.KNOWN_TERMS_FORMATS` rather than re-declared as an enum here:
58
+ * the token set is the protocol's, and a second copy in this package would be free to drift from it. The
59
+ * free-string `termsFormat` invites values like `text/plain` and `application/pdf`,
60
+ * neither of which is an LCP §2.5 token.
61
+ *
62
+ * IT RETURNS THE DOCUMENT; IT DOES NOT PUBLISH ONE. Serving it is the deployment's act, on the deployment's
63
+ * origin, under the deployment's name.
64
+ */
65
+ export function registerGenerateLegalContext(server, ports) {
66
+ server.registerTool("lcp_generate_legal_context", {
67
+ description: "Build a ready-to-publish `/.well-known/legal-context.json` document. Fetches `termsUrl`, " +
68
+ "computes the ATR hash over the served bytes, validates the result against the LCP discovery " +
69
+ "schema, and returns the document to serve. It does not publish anything.",
70
+ inputSchema,
71
+ outputSchema,
72
+ annotations: readOnlyToolAnnotations("Generate LCP legal-context.json", "network"),
73
+ }, async (args) => {
74
+ if (!isKnownTermsFormat(args.termsFormat))
75
+ throw new Error(`termsFormat "${args.termsFormat}" is not an LCP §2.5 token — expected one of: ${KNOWN_TERMS_FORMATS.join(", ")}`);
76
+ const bytes = (await ports.fetcher.fetch(args.termsUrl)).bytes;
77
+ const profile = {
78
+ terms: args.termsUrl,
79
+ termsFormat: args.termsFormat,
80
+ atrHash: await hashAtr(bytes),
81
+ ...(args.acceptanceRequired !== undefined
82
+ ? { acceptanceRequired: args.acceptanceRequired }
83
+ : {}),
84
+ ...(args.disputeResolution !== undefined
85
+ ? { disputeResolution: args.disputeResolution }
86
+ : {}),
87
+ ...(args.returns !== undefined ? { returns: args.returns } : {}),
88
+ ...(args.api !== undefined ? { api: args.api } : {}),
89
+ };
90
+ const doc = emit(profile);
91
+ return {
92
+ content: [
93
+ { type: "text", text: JSON.stringify(doc, null, 2) },
94
+ ],
95
+ structuredContent: doc,
96
+ };
97
+ });
98
+ }
99
+ //# sourceMappingURL=generate-legal-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-legal-context.js","sourceRoot":"","sources":["../../src/tools/generate-legal-context.ts"],"names":[],"mappings":"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"}