@signdocs-brasil/mcp-server 0.1.0 → 0.3.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 (47) hide show
  1. package/README.md +77 -3
  2. package/dist/bin/http.d.ts +2 -0
  3. package/dist/bin/http.js +35 -0
  4. package/dist/bin/http.js.map +1 -0
  5. package/dist/bin/stdio.js +2 -1
  6. package/dist/bin/stdio.js.map +1 -1
  7. package/dist/client.d.ts +50 -0
  8. package/dist/client.js +71 -0
  9. package/dist/client.js.map +1 -1
  10. package/dist/http/server.d.ts +29 -29
  11. package/dist/http/server.js +160 -36
  12. package/dist/http/server.js.map +1 -1
  13. package/dist/http/shared.d.ts +31 -0
  14. package/dist/http/shared.js +67 -0
  15. package/dist/http/shared.js.map +1 -0
  16. package/dist/index.d.ts +12 -0
  17. package/dist/index.js +13 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/lambda.d.ts +51 -0
  20. package/dist/lambda.js +96 -0
  21. package/dist/lambda.js.map +1 -0
  22. package/dist/schemas.d.ts +6 -6
  23. package/dist/server.d.ts +7 -5
  24. package/dist/server.js +13 -12
  25. package/dist/server.js.map +1 -1
  26. package/dist/tools/documents.d.ts +2 -1
  27. package/dist/tools/documents.js +3 -4
  28. package/dist/tools/documents.js.map +1 -1
  29. package/dist/tools/envelopes.d.ts +2 -1
  30. package/dist/tools/envelopes.js +6 -6
  31. package/dist/tools/envelopes.js.map +1 -1
  32. package/dist/tools/evidence.d.ts +2 -1
  33. package/dist/tools/evidence.js +2 -3
  34. package/dist/tools/evidence.js.map +1 -1
  35. package/dist/tools/signingSessions.d.ts +2 -1
  36. package/dist/tools/signingSessions.js +8 -8
  37. package/dist/tools/signingSessions.js.map +1 -1
  38. package/dist/tools/transactions.d.ts +2 -1
  39. package/dist/tools/transactions.js +4 -5
  40. package/dist/tools/transactions.js.map +1 -1
  41. package/dist/tools/verify.d.ts +2 -1
  42. package/dist/tools/verify.js +5 -7
  43. package/dist/tools/verify.js.map +1 -1
  44. package/dist/tools/webhooks.d.ts +2 -1
  45. package/dist/tools/webhooks.js +5 -6
  46. package/dist/tools/webhooks.js.map +1 -1
  47. package/package.json +22 -6
package/README.md CHANGED
@@ -105,6 +105,76 @@ The server exposes grounding resources the model can read on demand:
105
105
  - `signdocs://policy-profiles` — valid `policyProfile` values and CUSTOM steps
106
106
  - `signdocs://webhook-events` — all subscribable event types
107
107
 
108
+ ## Remote HTTP transport (multi-tenant)
109
+
110
+ The same tools are also served over **Streamable HTTP** so a single deployment
111
+ can serve many AI agents/tenants — each authenticates per session with its own
112
+ SignDocs credentials (no shared secret baked into the server).
113
+
114
+ ```bash
115
+ npm run start:http # or: signdocs-mcp-http (listens on PORT, default 3000)
116
+ # or containerized:
117
+ docker build -t signdocs-mcp . && docker run -p 3000:3000 signdocs-mcp
118
+ ```
119
+
120
+ **Endpoint:** `POST /mcp` (Streamable HTTP). Auth is required on the MCP
121
+ `initialize` request, via the `Authorization` header:
122
+
123
+ - `Authorization: Bearer <token>` — a SignDocs OAuth2 access token (from
124
+ `/oauth2/token`), passed straight through to the API.
125
+ - `Authorization: Basic base64(clientId:clientSecret)` — the server runs the
126
+ `client_credentials` exchange for you.
127
+
128
+ Pick the environment per session with `X-SignDocs-Environment: hml|production`
129
+ (defaults to the server's configured default).
130
+
131
+ The server behaves as an **OAuth 2.0 Resource Server**: it serves
132
+ `GET /.well-known/oauth-protected-resource` (RFC 9728, pointing at the SignDocs
133
+ authorization server) and answers an unauthenticated `initialize` with `401` +
134
+ `WWW-Authenticate`. The SignDocs API remains the authoritative token validator.
135
+ `GET /healthz` is an unauthenticated health probe.
136
+
137
+ Example client config (Bearer):
138
+
139
+ ```json
140
+ {
141
+ "mcpServers": {
142
+ "signdocs-remote": {
143
+ "type": "http",
144
+ "url": "https://your-host.example/mcp",
145
+ "headers": {
146
+ "Authorization": "Bearer <signdocs_access_token>",
147
+ "X-SignDocs-Environment": "hml"
148
+ }
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ **Server env vars:** `PORT`, `HOST`, `SIGNDOCS_ENVIRONMENT` (default env),
155
+ `MCP_PUBLIC_URL` (for resource metadata behind a proxy), `MCP_CORS_ORIGIN`,
156
+ `MCP_DNS_REBINDING_PROTECTION=true` + `MCP_ALLOWED_HOSTS` / `MCP_ALLOWED_ORIGINS`
157
+ (recommended in production).
158
+
159
+ > Sessions are held in process memory, so run a single instance or use sticky
160
+ > routing. For multi-instance/serverless, front it with sticky sessions or swap
161
+ > the session map for a shared store + EventStore (resumability). Deploying onto
162
+ > the existing `external-api` Lambda + API Gateway as a NestedStack is the
163
+ > intended production path.
164
+
165
+ ### AWS Lambda
166
+
167
+ For serverless hosting, `@signdocs-brasil/mcp-server/lambda` exports
168
+ `createLambdaHandler` — an API Gateway HTTP API v2 handler that runs the MCP
169
+ transport **statelessly** (one server per invocation, no session store), with the
170
+ same Bearer/Basic auth. SignDocs hosts this on `mcp-hml.signdocs.com.br` /
171
+ `mcp.signdocs.com.br`.
172
+
173
+ ```ts
174
+ import { createLambdaHandler } from '@signdocs-brasil/mcp-server/lambda';
175
+ export const handler = createLambdaHandler({ defaultEnvironment: 'hml' });
176
+ ```
177
+
108
178
  ## Development
109
179
 
110
180
  ```bash
@@ -116,6 +186,10 @@ npm run inspect # build + launch MCP Inspector against the stdio server
116
186
 
117
187
  ## Roadmap
118
188
 
119
- - **v0.1 (this release):** local stdio server, full tool catalog, env credentials.
120
- - **Phase 2:** remote Streamable-HTTP transport with per-tenant OAuth (the tool
121
- layer is already transport-agnostic see `src/http/server.ts`).
189
+ - **v0.1:** local stdio server, full tool catalog, env credentials.
190
+ - **v0.2 (this release):** remote Streamable-HTTP transport with per-session,
191
+ per-tenant auth (Bearer passthrough or Basic client-credentials) and OAuth
192
+ Resource Server discovery. Tool layer is shared between both transports.
193
+ - **Next:** deploy the HTTP transport onto `external-api` (Lambda + API Gateway
194
+ NestedStack); optional edge JWT validation + shared-store sessions for
195
+ horizontal scale.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import { createHttpServer } from '../http/server.js';
3
+ import { resolveEnvironment } from '../client.js';
4
+ /**
5
+ * Remote HTTP entrypoint. Unlike the stdio server, credentials are NOT read
6
+ * from env — each request carries its own (Bearer token or Basic client
7
+ * credentials), so one deployment serves many tenants.
8
+ */
9
+ function envDefault() {
10
+ try {
11
+ return resolveEnvironment(process.env.SIGNDOCS_ENVIRONMENT);
12
+ }
13
+ catch {
14
+ return 'hml';
15
+ }
16
+ }
17
+ const port = Number(process.env.PORT ?? 3000);
18
+ const host = process.env.HOST ?? '0.0.0.0';
19
+ const server = createHttpServer({
20
+ defaultEnvironment: envDefault(),
21
+ corsOrigin: process.env.MCP_CORS_ORIGIN,
22
+ publicUrl: process.env.MCP_PUBLIC_URL,
23
+ enableDnsRebindingProtection: process.env.MCP_DNS_REBINDING_PROTECTION === 'true',
24
+ allowedHosts: process.env.MCP_ALLOWED_HOSTS?.split(',').map((h) => h.trim()).filter(Boolean),
25
+ allowedOrigins: process.env.MCP_ALLOWED_ORIGINS?.split(',').map((o) => o.trim()).filter(Boolean),
26
+ });
27
+ server.listen(port, host, () => {
28
+ process.stderr.write(`[signdocs-mcp-http] listening on http://${host}:${port}/mcp (default env: ${envDefault()})\n`);
29
+ });
30
+ for (const sig of ['SIGINT', 'SIGTERM']) {
31
+ process.on(sig, () => {
32
+ server.close(() => process.exit(0));
33
+ });
34
+ }
35
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/bin/http.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAoB,MAAM,cAAc,CAAC;AAEpE;;;;GAIG;AACH,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,SAAS,CAAC;AAE3C,MAAM,MAAM,GAAG,gBAAgB,CAAC;IAC9B,kBAAkB,EAAE,UAAU,EAAE;IAChC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;IACvC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc;IACrC,4BAA4B,EAAE,OAAO,CAAC,GAAG,CAAC,4BAA4B,KAAK,MAAM;IACjF,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IAC5F,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;CACjG,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;IAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,IAAI,IAAI,IAAI,sBAAsB,UAAU,EAAE,KAAK,CAAC,CAAC;AACvH,CAAC,CAAC,CAAC;AAEH,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE;QACnB,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC"}
package/dist/bin/stdio.js CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { createServer } from '../server.js';
4
+ import { getStdioContext } from '../client.js';
4
5
  /**
5
6
  * stdio entrypoint. AI clients (Claude Desktop/Code, Cursor, …) launch this
6
7
  * binary and speak MCP over stdin/stdout. NEVER write to stdout here — it is
7
8
  * the protocol channel; diagnostics go to stderr.
8
9
  */
9
10
  async function main() {
10
- const server = createServer();
11
+ const server = createServer(getStdioContext());
11
12
  const transport = new StdioServerTransport();
12
13
  await server.connect(transport);
13
14
  process.stderr.write('[signdocs-mcp] server started on stdio\n');
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/bin/stdio.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;GAIG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;AACnE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/bin/stdio.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;GAIG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,YAAY,CAAC,eAAe,EAAE,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;AACnE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/client.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { SignDocsBrasilClient } from '@signdocs-brasil/api';
2
+ import type { TokenCache, CachedToken } from '@signdocs-brasil/api';
2
3
  /**
3
4
  * Thin wrapper that turns environment variables into a configured
4
5
  * {@link SignDocsBrasilClient}. The official SDK owns the OAuth2
@@ -33,3 +34,52 @@ export declare function resetClientCache(): void;
33
34
  * link — it must carry the one-time embed token (`clientSecret`) as `?cs=`.
34
35
  */
35
36
  export declare function buildSigningUrl(url: string, clientSecret: string): string;
37
+ /**
38
+ * What every tool handler needs to talk to SignDocs. In the stdio server this
39
+ * is built once from env; in the remote HTTP server it is built per-request so
40
+ * each tenant is fully isolated (no shared client/token across requests).
41
+ */
42
+ export interface ToolContext {
43
+ client: SignDocsBrasilClient;
44
+ environment: Environment;
45
+ }
46
+ /**
47
+ * A {@link TokenCache} pre-seeded with a caller-supplied access token. The SDK's
48
+ * AuthHandler checks the cache before exchanging credentials, so seeding it makes
49
+ * the SDK use the presented bearer directly and never call `/oauth2/token`.
50
+ * The SignDocs API remains the real validator — an invalid/expired token yields
51
+ * a 401 from the API, surfaced to the caller.
52
+ */
53
+ export declare class StaticTokenCache implements TokenCache {
54
+ private readonly token;
55
+ constructor(accessToken: string, ttlMs?: number);
56
+ get(): CachedToken | null;
57
+ set(): void;
58
+ delete(): void;
59
+ }
60
+ export type BuildClientOptions = {
61
+ mode: 'credentials';
62
+ clientId: string;
63
+ clientSecret: string;
64
+ environment: Environment;
65
+ baseUrlOverride?: string;
66
+ scopes?: string[];
67
+ } | {
68
+ mode: 'bearer';
69
+ bearer: string;
70
+ environment: Environment;
71
+ baseUrlOverride?: string;
72
+ scopes?: string[];
73
+ };
74
+ /**
75
+ * Build a fresh, request-scoped SDK client. `credentials` mode lets the SDK run
76
+ * the OAuth2 client_credentials exchange; `bearer` mode passes a pre-issued
77
+ * access token straight through via {@link StaticTokenCache}.
78
+ */
79
+ export declare function buildClient(opts: BuildClientOptions): SignDocsBrasilClient;
80
+ /**
81
+ * Build the tool context for the stdio server from environment variables.
82
+ * Credentials are resolved lazily (on first API call); the environment is read
83
+ * eagerly but does not require credentials.
84
+ */
85
+ export declare function getStdioContext(env?: NodeJS.ProcessEnv): ToolContext;
package/dist/client.js CHANGED
@@ -77,4 +77,75 @@ export function resetClientCache() {
77
77
  export function buildSigningUrl(url, clientSecret) {
78
78
  return `${url}?cs=${encodeURIComponent(clientSecret)}`;
79
79
  }
80
+ /**
81
+ * A {@link TokenCache} pre-seeded with a caller-supplied access token. The SDK's
82
+ * AuthHandler checks the cache before exchanging credentials, so seeding it makes
83
+ * the SDK use the presented bearer directly and never call `/oauth2/token`.
84
+ * The SignDocs API remains the real validator — an invalid/expired token yields
85
+ * a 401 from the API, surfaced to the caller.
86
+ */
87
+ export class StaticTokenCache {
88
+ token;
89
+ constructor(accessToken, ttlMs = 60 * 60 * 1000) {
90
+ this.token = { accessToken, expiresAt: Date.now() + ttlMs };
91
+ }
92
+ get() {
93
+ return this.token;
94
+ }
95
+ set() {
96
+ /* no-op: the token is fixed for this request */
97
+ }
98
+ delete() {
99
+ /* no-op */
100
+ }
101
+ }
102
+ /**
103
+ * Build a fresh, request-scoped SDK client. `credentials` mode lets the SDK run
104
+ * the OAuth2 client_credentials exchange; `bearer` mode passes a pre-issued
105
+ * access token straight through via {@link StaticTokenCache}.
106
+ */
107
+ export function buildClient(opts) {
108
+ const baseUrl = getBaseUrl(opts.environment, opts.baseUrlOverride);
109
+ const scopes = opts.scopes ?? DEFAULT_SCOPES;
110
+ if (opts.mode === 'bearer') {
111
+ return new SignDocsBrasilClient({
112
+ clientId: 'mcp-bearer-passthrough',
113
+ clientSecret: 'unused', // never used: the token cache short-circuits exchange
114
+ baseUrl,
115
+ scopes,
116
+ tokenCache: new StaticTokenCache(opts.bearer),
117
+ });
118
+ }
119
+ return new SignDocsBrasilClient({
120
+ clientId: opts.clientId,
121
+ clientSecret: opts.clientSecret,
122
+ baseUrl,
123
+ scopes,
124
+ });
125
+ }
126
+ /**
127
+ * A client proxy that defers construction until first use, so the stdio server
128
+ * can start and list tools/resources even with no credentials — a missing-cred
129
+ * error surfaces only when a tool actually calls the API.
130
+ */
131
+ function lazyClient(factory) {
132
+ let instance;
133
+ return new Proxy({}, {
134
+ get(_target, prop, receiver) {
135
+ instance ??= factory();
136
+ return Reflect.get(instance, prop, receiver);
137
+ },
138
+ });
139
+ }
140
+ /**
141
+ * Build the tool context for the stdio server from environment variables.
142
+ * Credentials are resolved lazily (on first API call); the environment is read
143
+ * eagerly but does not require credentials.
144
+ */
145
+ export function getStdioContext(env = process.env) {
146
+ return {
147
+ client: lazyClient(() => getClient(env)),
148
+ environment: resolveEnvironment(env.SIGNDOCS_ENVIRONMENT),
149
+ };
150
+ }
80
151
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -1,34 +1,34 @@
1
+ import { type Server } from 'node:http';
2
+ import { type Environment } from '../client.js';
1
3
  /**
2
- * Phase 2 — remote Streamable-HTTP transport (designed, not yet built).
4
+ * Phase 2 — remote Streamable-HTTP transport (stateful sessions).
3
5
  *
4
- * The tool/resource layer in ../server.ts is deliberately transport-agnostic,
5
- * so going remote is purely an entrypoint + auth concern:
6
+ * One deployment serves many tenants. A session is established on the MCP
7
+ * `initialize` request, which MUST carry credentials:
8
+ * - `Authorization: Bearer <token>` → a SignDocs OAuth2 access token, passed through.
9
+ * - `Authorization: Basic <base64(clientId:clientSecret)>` → server runs client_credentials.
10
+ * Environment via `X-SignDocs-Environment: hml|production` (default = server default).
6
11
  *
7
- * import { StreamableHTTPServerTransport } from
8
- * '@modelcontextprotocol/sdk/server/streamableHttp.js';
9
- * const server = createServer();
10
- * const transport = new StreamableHTTPServerTransport({ ...});
11
- * await server.connect(transport);
12
- * // node:http handler → transport.handleRequest(req, res, body)
12
+ * The tenant's SDK client is bound to that session; follow-up requests are routed
13
+ * by the `Mcp-Session-Id` header. Acts as an OAuth Resource Server: advertises the
14
+ * SignDocs authorization server (RFC 9728) and challenges unauthenticated initialize
15
+ * requests with WWW-Authenticate. The SignDocs API is the real token validator.
13
16
  *
14
- * Open design decisions to settle before implementing (see plan):
15
- *
16
- * 1. AUTH / MULTI-TENANCY. Unlike stdio (one set of env credentials), a
17
- * hosted server serves many tenants. The MCP server should act as an
18
- * OAuth Resource Server: each connecting AI presents its own SignDocs
19
- * bearer token (issued by the existing /oauth2/token AS). Validate it with
20
- * the same ES256/KMS verifier external-api uses (auth-middleware.ts) and
21
- * build a PER-REQUEST SDK client scoped to that tenant — do NOT reuse the
22
- * process-wide getClient() singleton, which would cross tenant boundaries.
23
- *
24
- * 2. DEPLOYMENT. Reuse the external-api Lambda + API Gateway pattern via a new
25
- * NestedStack (the Core stack is at the CFN 500-resource limit — follow the
26
- * EnvelopeHandlersStack precedent), or a small container behind the same WAF.
27
- *
28
- * 3. SESSION MODE. Stateless (sessionIdGenerator: undefined) fits serverless;
29
- * confirm against the MCP spec version current at build time, plus DCR /
30
- * protected-resource metadata discovery needs.
31
- *
32
- * Intentionally not wired up yet so the v0.1 stdio package stays dependency-light.
17
+ * Sessions live in process memory, so a single instance (or sticky routing) is
18
+ * assumed. For multi-instance/serverless, front with sticky sessions or swap this
19
+ * map for a shared store + an EventStore for resumability.
33
20
  */
34
- export declare function createHttpServer(): never;
21
+ export interface HttpServerOptions {
22
+ /** Environment when a request doesn't specify one. Default 'hml'. */
23
+ defaultEnvironment?: Environment;
24
+ /** CORS Access-Control-Allow-Origin. Default '*'. */
25
+ corsOrigin?: string;
26
+ /** Public base URL for resource metadata (e.g. https://mcp.signdocs.com.br). Derived from the request if unset. */
27
+ publicUrl?: string;
28
+ /** DNS-rebinding protection — enable in production and pair with allowedHosts/Origins. Default false. */
29
+ enableDnsRebindingProtection?: boolean;
30
+ allowedHosts?: string[];
31
+ allowedOrigins?: string[];
32
+ }
33
+ /** Build (but do not start) the remote HTTP MCP server. Call `.listen(port)`. */
34
+ export declare function createHttpServer(options?: HttpServerOptions): Server;
@@ -1,38 +1,162 @@
1
- /**
2
- * Phase 2 remote Streamable-HTTP transport (designed, not yet built).
3
- *
4
- * The tool/resource layer in ../server.ts is deliberately transport-agnostic,
5
- * so going remote is purely an entrypoint + auth concern:
6
- *
7
- * import { StreamableHTTPServerTransport } from
8
- * '@modelcontextprotocol/sdk/server/streamableHttp.js';
9
- * const server = createServer();
10
- * const transport = new StreamableHTTPServerTransport({ ...});
11
- * await server.connect(transport);
12
- * // node:http handler → transport.handleRequest(req, res, body)
13
- *
14
- * Open design decisions to settle before implementing (see plan):
15
- *
16
- * 1. AUTH / MULTI-TENANCY. Unlike stdio (one set of env credentials), a
17
- * hosted server serves many tenants. The MCP server should act as an
18
- * OAuth Resource Server: each connecting AI presents its own SignDocs
19
- * bearer token (issued by the existing /oauth2/token AS). Validate it with
20
- * the same ES256/KMS verifier external-api uses (auth-middleware.ts) and
21
- * build a PER-REQUEST SDK client scoped to that tenant — do NOT reuse the
22
- * process-wide getClient() singleton, which would cross tenant boundaries.
23
- *
24
- * 2. DEPLOYMENT. Reuse the external-api Lambda + API Gateway pattern via a new
25
- * NestedStack (the Core stack is at the CFN 500-resource limit — follow the
26
- * EnvelopeHandlersStack precedent), or a small container behind the same WAF.
27
- *
28
- * 3. SESSION MODE. Stateless (sessionIdGenerator: undefined) fits serverless;
29
- * confirm against the MCP spec version current at build time, plus DCR /
30
- * protected-resource metadata discovery needs.
31
- *
32
- * Intentionally not wired up yet so the v0.1 stdio package stays dependency-light.
33
- */
34
- export function createHttpServer() {
35
- throw new Error('Remote HTTP transport is not implemented in v0.1. Use the stdio entrypoint (bin/stdio.ts). ' +
36
- 'See src/http/server.ts for the Phase 2 design.');
1
+ import { createServer as createNodeHttpServer, } from 'node:http';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
4
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
5
+ import { createServer as createMcpServer } from '../server.js';
6
+ import { extractAuthFromHeaders, environmentFromHeaders, buildContextForAuth, protectedResourceMetadata as buildProtectedResourceMetadata, wwwAuthenticate, headerValue, UNAUTHORIZED_BODY, } from './shared.js';
7
+ function applyCors(res, opts) {
8
+ res.setHeader('Access-Control-Allow-Origin', opts.corsOrigin);
9
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
10
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Mcp-Session-Id, Mcp-Protocol-Version, X-SignDocs-Environment');
11
+ res.setHeader('Access-Control-Expose-Headers', 'Mcp-Session-Id, WWW-Authenticate');
12
+ }
13
+ function sendJson(res, status, body, extra) {
14
+ res.writeHead(status, { 'Content-Type': 'application/json', ...extra });
15
+ res.end(JSON.stringify(body));
16
+ }
17
+ function publicBase(req, opts) {
18
+ if (opts.publicUrl)
19
+ return opts.publicUrl.replace(/\/$/, '');
20
+ const fwd = req.headers['x-forwarded-proto'];
21
+ const proto = (Array.isArray(fwd) ? fwd[0] : fwd)?.split(',')[0] ?? 'http';
22
+ const host = req.headers.host ?? 'localhost';
23
+ return `${proto}://${host}`;
24
+ }
25
+ function challenge(req, res, opts) {
26
+ res.setHeader('WWW-Authenticate', wwwAuthenticate(`${publicBase(req, opts)}/.well-known/oauth-protected-resource`));
27
+ sendJson(res, 401, UNAUTHORIZED_BODY);
28
+ }
29
+ function readBody(req) {
30
+ return new Promise((resolve, reject) => {
31
+ const chunks = [];
32
+ req.on('data', (c) => chunks.push(c));
33
+ req.on('end', () => {
34
+ const raw = Buffer.concat(chunks).toString('utf8');
35
+ if (!raw)
36
+ return resolve(undefined);
37
+ try {
38
+ resolve(JSON.parse(raw));
39
+ }
40
+ catch (err) {
41
+ reject(err);
42
+ }
43
+ });
44
+ req.on('error', reject);
45
+ });
46
+ }
47
+ async function startSession(req, res, opts, sessions, body) {
48
+ const auth = extractAuthFromHeaders(req.headers);
49
+ if (!auth) {
50
+ challenge(req, res, opts);
51
+ return;
52
+ }
53
+ const environment = environmentFromHeaders(req.headers, opts.defaultEnvironment);
54
+ const ctx = buildContextForAuth(auth, environment);
55
+ const server = createMcpServer(ctx);
56
+ const transport = new StreamableHTTPServerTransport({
57
+ sessionIdGenerator: () => randomUUID(),
58
+ enableJsonResponse: true,
59
+ enableDnsRebindingProtection: opts.enableDnsRebindingProtection ?? false,
60
+ ...(opts.allowedHosts ? { allowedHosts: opts.allowedHosts } : {}),
61
+ ...(opts.allowedOrigins ? { allowedOrigins: opts.allowedOrigins } : {}),
62
+ onsessioninitialized: (sid) => {
63
+ sessions.set(sid, { transport, server });
64
+ },
65
+ onsessionclosed: (sid) => {
66
+ sessions.delete(sid);
67
+ },
68
+ });
69
+ transport.onclose = () => {
70
+ if (transport.sessionId)
71
+ sessions.delete(transport.sessionId);
72
+ };
73
+ await server.connect(transport);
74
+ await transport.handleRequest(req, res, body);
75
+ }
76
+ async function handlePost(req, res, opts, sessions) {
77
+ let body;
78
+ try {
79
+ body = await readBody(req);
80
+ }
81
+ catch {
82
+ sendJson(res, 400, { error: 'invalid_json', error_description: 'Request body is not valid JSON.' });
83
+ return;
84
+ }
85
+ const sessionId = headerValue(req.headers, 'mcp-session-id');
86
+ const existing = sessionId ? sessions.get(sessionId) : undefined;
87
+ if (existing) {
88
+ await existing.transport.handleRequest(req, res, body);
89
+ return;
90
+ }
91
+ if (isInitializeRequest(body)) {
92
+ await startSession(req, res, opts, sessions, body);
93
+ return;
94
+ }
95
+ sendJson(res, 400, {
96
+ error: 'invalid_session',
97
+ error_description: 'Missing or unknown Mcp-Session-Id. Send an initialize request first.',
98
+ });
99
+ }
100
+ async function handle(req, res, opts, sessions) {
101
+ applyCors(res, opts);
102
+ const method = req.method ?? 'GET';
103
+ if (method === 'OPTIONS') {
104
+ res.writeHead(204);
105
+ res.end();
106
+ return;
107
+ }
108
+ const path = new URL(req.url ?? '/', 'http://localhost').pathname;
109
+ if (method === 'GET' && path === '/healthz') {
110
+ sendJson(res, 200, { status: 'ok', transport: 'streamable-http', sessions: sessions.size });
111
+ return;
112
+ }
113
+ if (method === 'GET' && path === '/.well-known/oauth-protected-resource') {
114
+ sendJson(res, 200, buildProtectedResourceMetadata(`${publicBase(req, opts)}/mcp`, opts.defaultEnvironment));
115
+ return;
116
+ }
117
+ if (path !== '/mcp') {
118
+ sendJson(res, 404, { error: 'not_found' });
119
+ return;
120
+ }
121
+ if (method === 'POST') {
122
+ await handlePost(req, res, opts, sessions);
123
+ return;
124
+ }
125
+ // GET (SSE stream) and DELETE (session teardown) require an established session.
126
+ if (method === 'GET' || method === 'DELETE') {
127
+ const sessionId = headerValue(req.headers, 'mcp-session-id');
128
+ const session = sessionId ? sessions.get(sessionId) : undefined;
129
+ if (!session) {
130
+ sendJson(res, 400, { error: 'invalid_session', error_description: 'Unknown or missing Mcp-Session-Id.' });
131
+ return;
132
+ }
133
+ await session.transport.handleRequest(req, res);
134
+ return;
135
+ }
136
+ sendJson(res, 405, { error: 'method_not_allowed' }, { Allow: 'GET, POST, DELETE, OPTIONS' });
137
+ }
138
+ /** Build (but do not start) the remote HTTP MCP server. Call `.listen(port)`. */
139
+ export function createHttpServer(options = {}) {
140
+ const opts = {
141
+ ...options,
142
+ defaultEnvironment: options.defaultEnvironment ?? 'hml',
143
+ corsOrigin: options.corsOrigin ?? '*',
144
+ };
145
+ const sessions = new Map();
146
+ return createNodeHttpServer((req, res) => {
147
+ handle(req, res, opts, sessions).catch((err) => {
148
+ try {
149
+ if (!res.headersSent) {
150
+ sendJson(res, 500, { error: 'internal_error', message: err instanceof Error ? err.message : String(err) });
151
+ }
152
+ else {
153
+ res.end();
154
+ }
155
+ }
156
+ catch {
157
+ /* response already torn down */
158
+ }
159
+ });
160
+ });
37
161
  }
38
162
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,KAAK,CACb,6FAA6F;QAC3F,gDAAgD,CACnD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { type Environment, type ToolContext } from '../client.js';
2
+ /**
3
+ * Transport-agnostic auth/discovery helpers shared by the long-running HTTP
4
+ * server (http/server.ts) and the Lambda adapter (lambda.ts). Everything here
5
+ * works off a plain header map so it serves both Node IncomingMessage headers
6
+ * and API Gateway event headers.
7
+ */
8
+ export type AuthResult = {
9
+ mode: 'bearer';
10
+ bearer: string;
11
+ } | {
12
+ mode: 'credentials';
13
+ clientId: string;
14
+ clientSecret: string;
15
+ };
16
+ export type HeaderMap = Record<string, string | string[] | undefined>;
17
+ export declare function headerValue(headers: HeaderMap, name: string): string | undefined;
18
+ /** Parse the Authorization header into a Bearer token or Basic client credentials. */
19
+ export declare function extractAuthFromHeaders(headers: HeaderMap): AuthResult | null;
20
+ /** Resolve the SignDocs environment from the X-SignDocs-Environment header, else fallback. */
21
+ export declare function environmentFromHeaders(headers: HeaderMap, fallback: Environment): Environment;
22
+ /** Build a request-scoped tool context (fresh SDK client) from parsed auth. */
23
+ export declare function buildContextForAuth(auth: AuthResult, environment: Environment): ToolContext;
24
+ /** RFC 9728 protected-resource metadata pointing at the SignDocs authorization server. */
25
+ export declare function protectedResourceMetadata(resourceUrl: string, environment: Environment): Record<string, unknown>;
26
+ /** WWW-Authenticate challenge value pointing a client at the resource metadata. */
27
+ export declare function wwwAuthenticate(metadataUrl: string): string;
28
+ export declare const UNAUTHORIZED_BODY: {
29
+ error: string;
30
+ error_description: string;
31
+ };