@proof-holdings/mcp-server 1.0.0 → 1.2.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 (113) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +220 -217
  3. package/dist/authBoundary.d.ts +193 -0
  4. package/dist/authBoundary.d.ts.map +1 -0
  5. package/dist/authBoundary.js +341 -0
  6. package/dist/authBoundary.js.map +1 -0
  7. package/dist/factory.d.ts +35 -0
  8. package/dist/factory.d.ts.map +1 -0
  9. package/dist/factory.js +130 -0
  10. package/dist/factory.js.map +1 -0
  11. package/dist/http.d.ts +78 -3
  12. package/dist/http.d.ts.map +1 -1
  13. package/dist/http.js +251 -6
  14. package/dist/http.js.map +1 -1
  15. package/dist/remote.d.ts +111 -0
  16. package/dist/remote.d.ts.map +1 -0
  17. package/dist/remote.js +789 -0
  18. package/dist/remote.js.map +1 -0
  19. package/dist/server.js +11 -57
  20. package/dist/server.js.map +1 -1
  21. package/dist/toolAnnotations.d.ts +27 -0
  22. package/dist/toolAnnotations.d.ts.map +1 -0
  23. package/dist/toolAnnotations.js +158 -0
  24. package/dist/toolAnnotations.js.map +1 -0
  25. package/dist/tools/{projects.d.ts → accounts.d.ts} +1 -1
  26. package/dist/tools/accounts.d.ts.map +1 -0
  27. package/dist/tools/accounts.js +70 -0
  28. package/dist/tools/accounts.js.map +1 -0
  29. package/dist/tools/api-keys.d.ts.map +1 -1
  30. package/dist/tools/api-keys.js +14 -5
  31. package/dist/tools/api-keys.js.map +1 -1
  32. package/dist/tools/auth-flows.d.ts +4 -0
  33. package/dist/tools/auth-flows.d.ts.map +1 -0
  34. package/dist/tools/auth-flows.js +34 -0
  35. package/dist/tools/auth-flows.js.map +1 -0
  36. package/dist/tools/auth.js +1 -1
  37. package/dist/tools/auth.js.map +1 -1
  38. package/dist/tools/authorizations.d.ts +4 -0
  39. package/dist/tools/authorizations.d.ts.map +1 -0
  40. package/dist/tools/authorizations.js +111 -0
  41. package/dist/tools/authorizations.js.map +1 -0
  42. package/dist/tools/circles.d.ts +4 -0
  43. package/dist/tools/circles.d.ts.map +1 -0
  44. package/dist/tools/circles.js +215 -0
  45. package/dist/tools/circles.js.map +1 -0
  46. package/dist/tools/confirmations.d.ts +4 -0
  47. package/dist/tools/confirmations.d.ts.map +1 -0
  48. package/dist/tools/confirmations.js +86 -0
  49. package/dist/tools/confirmations.js.map +1 -0
  50. package/dist/tools/delegation-verify-outcomes.d.ts +23 -0
  51. package/dist/tools/delegation-verify-outcomes.d.ts.map +1 -0
  52. package/dist/tools/delegation-verify-outcomes.js +51 -0
  53. package/dist/tools/delegation-verify-outcomes.js.map +1 -0
  54. package/dist/tools/delegation-verify.d.ts +24 -0
  55. package/dist/tools/delegation-verify.d.ts.map +1 -0
  56. package/dist/tools/delegation-verify.js +192 -0
  57. package/dist/tools/delegation-verify.js.map +1 -0
  58. package/dist/tools/delegations.d.ts +4 -0
  59. package/dist/tools/delegations.d.ts.map +1 -0
  60. package/dist/tools/delegations.js +84 -0
  61. package/dist/tools/delegations.js.map +1 -0
  62. package/dist/tools/domains.d.ts.map +1 -1
  63. package/dist/tools/domains.js +1 -2
  64. package/dist/tools/domains.js.map +1 -1
  65. package/dist/tools/hitl-keys.d.ts +4 -0
  66. package/dist/tools/hitl-keys.d.ts.map +1 -0
  67. package/dist/tools/hitl-keys.js +52 -0
  68. package/dist/tools/hitl-keys.js.map +1 -0
  69. package/dist/tools/hitl.d.ts +4 -0
  70. package/dist/tools/hitl.d.ts.map +1 -0
  71. package/dist/tools/hitl.js +151 -0
  72. package/dist/tools/hitl.js.map +1 -0
  73. package/dist/tools/phones.js +1 -1
  74. package/dist/tools/phones.js.map +1 -1
  75. package/dist/tools/profiles.d.ts.map +1 -1
  76. package/dist/tools/profiles.js +73 -0
  77. package/dist/tools/profiles.js.map +1 -1
  78. package/dist/tools/proof-me.d.ts +4 -0
  79. package/dist/tools/proof-me.d.ts.map +1 -0
  80. package/dist/tools/proof-me.js +36 -0
  81. package/dist/tools/proof-me.js.map +1 -0
  82. package/dist/tools/proofs.d.ts.map +1 -1
  83. package/dist/tools/proofs.js +9 -6
  84. package/dist/tools/proofs.js.map +1 -1
  85. package/dist/tools/render-auth-link.d.ts +3 -0
  86. package/dist/tools/render-auth-link.d.ts.map +1 -0
  87. package/dist/tools/render-auth-link.js +30 -0
  88. package/dist/tools/render-auth-link.js.map +1 -0
  89. package/dist/tools/sessions.js +5 -5
  90. package/dist/tools/sessions.js.map +1 -1
  91. package/dist/tools/settings.d.ts.map +1 -1
  92. package/dist/tools/settings.js +77 -2
  93. package/dist/tools/settings.js.map +1 -1
  94. package/dist/tools/twofa.d.ts.map +1 -1
  95. package/dist/tools/twofa.js +16 -3
  96. package/dist/tools/twofa.js.map +1 -1
  97. package/dist/tools/user-requests.d.ts.map +1 -1
  98. package/dist/tools/user-requests.js +1 -2
  99. package/dist/tools/user-requests.js.map +1 -1
  100. package/dist/tools/verification-requests.d.ts.map +1 -1
  101. package/dist/tools/verification-requests.js +40 -13
  102. package/dist/tools/verification-requests.js.map +1 -1
  103. package/dist/tools/verifications.d.ts.map +1 -1
  104. package/dist/tools/verifications.js +59 -12
  105. package/dist/tools/verifications.js.map +1 -1
  106. package/dist/types.d.ts +18 -0
  107. package/dist/types.d.ts.map +1 -1
  108. package/dist/types.js +114 -5
  109. package/dist/types.js.map +1 -1
  110. package/package.json +11 -5
  111. package/dist/tools/projects.d.ts.map +0 -1
  112. package/dist/tools/projects.js +0 -159
  113. package/dist/tools/projects.js.map +0 -1
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Remote entrypoint: the Proof MCP server over Streamable HTTP (`h-mcp-remote`, SC-3 + SC-4).
4
+ *
5
+ * Deployed as its own service, NOT as a route inside the existing backend. The reason is
6
+ * structural rather than stylistic: `src/cluster.ts` forks one worker per CPU and round-robins
7
+ * between them, while a Streamable HTTP session lives in the memory of ONE process. Mounting this
8
+ * on the clustered backend would lose sessions by default, not in an edge case. The deployment
9
+ * therefore pins requests to a replica by the REPLICA PREFIX this server puts in every session id
10
+ * it mints (`<replica>.<uuid>`); see `replicaId` below for why hashing the id itself cannot work,
11
+ * and `k8s/mcp-remote/nginx-mcp.conf` for the routing that consumes the prefix. That routing is
12
+ * deliberately NOT in the base nginx configmap — see that directory's README for the ingress it
13
+ * would otherwise take down, and `src/__tests__/drift/mcp-remote-deployment.test.ts`, which asserts
14
+ * the base stays free of it.
15
+ *
16
+ * ANONYMOUS FIRST, KEYED ON REQUEST (`h-mcp-oauth-remote-wiring`). Every connection starts with the
17
+ * keyless surface and the ability to log in through the normal flows; a client that has been
18
+ * through the OAuth ceremony (`h-mcp-oauth-server`) presents the API key it was granted, and this
19
+ * server reads it from the `Authorization` header ONLY. A `?api_key=` query parameter is never
20
+ * read: a token in a URL is copied into proxy logs and `Referer`, and the standard clients all send
21
+ * a header. The token is resolved ONCE, at open, by self-inspection (`GET /api/v1/me/api-key`) —
22
+ * purely so the refusal is legible; nothing is cached from it, and the key travels to the backend
23
+ * on every call, which is what makes a revocation take effect immediately rather than at session
24
+ * TTL. Where the 401 lands is a measurement, not a preference: on the TOOL CALL that needs an
25
+ * account, because a 401 on `initialize` reached the human as a connection timeout and cost the
26
+ * anonymous connection entirely (`docs/mcp-oauth-client-probe.md`).
27
+ *
28
+ * Each connection needs its OWN `HttpClient` for two reasons now: `captureSetCookie` writes a login
29
+ * session onto the instance, and the API key of one connection must never be reachable from
30
+ * another. The boundary between keyless and keyed tools lives in `./authBoundary.js`.
31
+ */
32
+ import { type Server } from 'node:http';
33
+ import { HttpClient } from './http.js';
34
+ export interface RemoteServerOptions {
35
+ baseUrl: string;
36
+ /** Maximum concurrent MCP sessions. Each one registers the full tool set, so this is a memory bound. */
37
+ maxSessions?: number;
38
+ /** Idle time after which a session is closed and dropped. */
39
+ sessionTtlMs?: number;
40
+ sweepIntervalMs?: number;
41
+ /**
42
+ * Which replica this process is. It is PREFIXED to every session id it mints, and the proxy
43
+ * routes on that prefix.
44
+ *
45
+ * This is not decoration, it is the only thing that makes sticky routing possible. Hashing the
46
+ * session id — the obvious reading of "sticky by mcp-session-id" — cannot work: the replica is
47
+ * chosen when the connection OPENS, and at that moment no session id exists yet, so the hash of
48
+ * the id minted afterwards lands wherever it lands. Measured on a two-replica nginx stand, that
49
+ * arrangement sent 45 of 60 follow-up requests to a replica that had never heard of the session.
50
+ * Carrying the replica in the id turns the routing decision into a lookup instead of a guess.
51
+ */
52
+ replicaId?: string;
53
+ }
54
+ export interface RemoteServer {
55
+ httpServer: Server;
56
+ sessionCount(): number;
57
+ /** Test seam: the live per-session HTTP clients, to assert they are distinct instances. */
58
+ debugClients(): HttpClient[];
59
+ close(): Promise<void>;
60
+ }
61
+ /**
62
+ * Ceiling on the burst, expressed FLEET-WIDE because the bucket it spends is fleet-wide.
63
+ *
64
+ * The burst below is sized off `maxSessions`, which is per replica — and the bucket it lands in is
65
+ * `ipRateLimit(300, 60, 'me')` keyed on the pod's egress IP, i.e. shared by every replica. So the
66
+ * figure that matters is `replicas × burst`, and stating the burst per replica beside a rate stated
67
+ * fleet-wide is how those two drift apart: at `replicas: 3` and the default cap, an unbounded burst
68
+ * is 300 — the entire minute's bucket, spent at exactly the moment those sessions also begin their
69
+ * ordinary `/me/*` calls through it. `src/__tests__/drift/mcp-remote-deployment.test.ts` derives
70
+ * `replicas × min(MCP_MAX_SESSIONS, RESOLVE_BURST_CEILING)` — the EFFECTIVE burst, not this
71
+ * constant alone — against the backend's own literal, so SCALING THE FLEET reddens it instead of
72
+ * surfacing as 429s in production. Raising THIS constant is caught elsewhere and deliberately so:
73
+ * while the manifest cap sits at or below the ceiling, the effective burst does not move, and the
74
+ * unit case in `mcp/__tests__/remote.test.ts` that runs a cap ABOVE the ceiling is what holds it.
75
+ */
76
+ export declare const RESOLVE_BURST_CEILING = 100;
77
+ /**
78
+ * A token bucket over the self-inspection above. Process-local on purpose: what it protects is this
79
+ * process's own outbound channel, and a shared counter would need a store this service does not
80
+ * have and must not grow for a defensive bound.
81
+ *
82
+ * Exported for its own test. The arithmetic below has a failure mode no HTTP-level case can reach
83
+ * cheaply — a clock that steps BACKWARDS — and the previous version's clock seam was a default
84
+ * parameter on an unexported function, i.e. unreachable from a test even in principle. That is the
85
+ * same shape `positiveIntEnv` above was fixed for once already.
86
+ */
87
+ export declare function createResolveBudget(maxSessions: number, now?: () => number): {
88
+ /** True when a self-inspection may be sent; consumes one token. */
89
+ take(): boolean;
90
+ };
91
+ /**
92
+ * The replica's own index: `MCP_REPLICA_ID` if set, otherwise the ordinal a StatefulSet puts at the
93
+ * end of the pod hostname (`mcp-remote-1` → `1`). Falls back to `0` for a single process, which is
94
+ * what the stdio-style local run and the tests get.
95
+ */
96
+ /**
97
+ * Reads a positive-integer setting, falling back to the built-in default on anything else.
98
+ *
99
+ * A typo must not disable the bound it configures. `Number('abc')` is NaN, and both
100
+ * `size >= NaN` and `lastSeen < NaN` are false — so an unparseable value would fail OPEN on exactly
101
+ * the two limits this server relies on, with the pod still passing every probe. INTEGER rather than
102
+ * merely finite-and-positive, because `MCP_SESSION_TTL_MS=0.5` would sweep every session on the
103
+ * first tick and `MCP_MAX_SESSIONS=0.5` would silently mean one.
104
+ *
105
+ * Exported and at module scope so it can be tested — the previous version was a closure inside the
106
+ * entrypoint guard, unreachable from a test even in principle, which left the fix one edit wide.
107
+ */
108
+ export declare function positiveIntEnv(name: string, env?: NodeJS.ProcessEnv): number | undefined;
109
+ export declare function resolveReplicaId(explicit?: string, hostname?: string): string;
110
+ export declare function createRemoteServer(options: RemoteServerOptions): RemoteServer;
111
+ //# sourceMappingURL=remote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAiBjG,OAAO,EAAY,UAAU,EAAE,MAAM,WAAW,CAAC;AAEjD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,wGAAwG;IACxG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,IAAI,MAAM,CAAC;IACvB,2FAA2F;IAC3F,YAAY,IAAI,UAAU,EAAE,CAAC;IAC7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AA6RD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAkBzC;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,MAAgC;IAMhG,mEAAmE;YAC3D,OAAO;EAclB;AAoGD;;;;GAIG;AACH;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAarG;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAI5F;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CA0T7E"}