snow-flow 11.0.0 → 11.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "11.0.0",
3
+ "version": "11.0.2",
4
4
  "name": "snow-flow",
5
5
  "description": "Snow-Flow - ServiceNow Multi-Agent Development Framework powered by AI",
6
6
  "license": "Elastic-2.0",
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * MCP HTTP Server — Bun entry point
4
+ * ----------------------------------------------------------------------
5
+ * Starts the Hono-based MCP server on a fixed port and wires up an HTTP
6
+ * callback-based `ContextResolver` (see http-resolver.ts) so that tenant
7
+ * credential resolution lives in the deployment that owns the tenant DB
8
+ * (e.g. snow-flow-enterprise's portal backend), not in this image.
9
+ *
10
+ * Env vars (all required at runtime):
11
+ * MCP_RESOLVER_URL — full URL of the downstream resolver endpoint
12
+ * MCP_INTERNAL_TOKEN — shared secret sent on `X-Internal-Auth`
13
+ * MCP_HTTP_PORT — listen port (default 8082)
14
+ *
15
+ * Intended runtime: Bun. The image is built from `Dockerfile.mcp-http`
16
+ * and published as `ghcr.io/groeimetai/snow-flow-mcp-http`. Callers that
17
+ * need to self-host can also run it via `bun run http-entry.ts`.
18
+ */
19
+
20
+ import { toolRegistry } from "../shared/tool-registry.js"
21
+ import { createHttpApp } from "./http.js"
22
+ import { createHttpResolver } from "./http-resolver.js"
23
+ import { mcpDebug, mcpWarn } from "../../shared/mcp-debug.js"
24
+
25
+ async function main(): Promise<void> {
26
+ const resolverUrl = process.env.MCP_RESOLVER_URL
27
+ const internalToken = process.env.MCP_INTERNAL_TOKEN
28
+ const port = Number(process.env.MCP_HTTP_PORT ?? 8082)
29
+
30
+ if (!resolverUrl || !internalToken) {
31
+ throw new Error(
32
+ "mcp-http entry requires MCP_RESOLVER_URL and MCP_INTERNAL_TOKEN env vars. " +
33
+ "See transports/http-resolver.ts for the contract the endpoint must implement.",
34
+ )
35
+ }
36
+
37
+ mcpDebug("[mcp-http] discovering tools…")
38
+ const discovery = await toolRegistry.initialize()
39
+ mcpDebug(
40
+ `[mcp-http] tool discovery done: ${discovery.toolsRegistered} registered, ` +
41
+ `${discovery.toolsFailed} failed across ${discovery.domains.length} domains`,
42
+ )
43
+ if (discovery.toolsFailed > 0) {
44
+ for (const err of discovery.errors) {
45
+ mcpWarn(`[mcp-http] tool load failed: ${err.filePath} — ${err.error}`)
46
+ }
47
+ }
48
+
49
+ const app = createHttpApp({
50
+ resolveContext: createHttpResolver({ url: resolverUrl, internalToken }),
51
+ })
52
+
53
+ // Bun's native server. Runs on Node too via `@hono/node-server`, but the
54
+ // published image is Bun-based so we use the built-in server here.
55
+ const server = (globalThis as any).Bun?.serve?.({ fetch: app.fetch, port })
56
+ if (!server) {
57
+ throw new Error(
58
+ "Bun.serve is not available. This entry point is intended for the Bun runtime " +
59
+ "shipped with Dockerfile.mcp-http.",
60
+ )
61
+ }
62
+
63
+ mcpDebug(`[mcp-http] listening on :${port}`)
64
+ mcpDebug(`[mcp-http] resolver endpoint: ${resolverUrl}`)
65
+
66
+ const shutdown = async (signal: string) => {
67
+ mcpDebug(`[mcp-http] received ${signal}, stopping…`)
68
+ try {
69
+ await server.stop?.(true)
70
+ } catch {
71
+ // ignore
72
+ }
73
+ process.exit(0)
74
+ }
75
+
76
+ process.on("SIGINT", () => {
77
+ shutdown("SIGINT").catch(() => process.exit(1))
78
+ })
79
+ process.on("SIGTERM", () => {
80
+ shutdown("SIGTERM").catch(() => process.exit(1))
81
+ })
82
+ }
83
+
84
+ main().catch((err: unknown) => {
85
+ const msg = err instanceof Error ? err.message : String(err)
86
+ // eslint-disable-next-line no-console
87
+ console.error(`[mcp-http] fatal: ${msg}`)
88
+ process.exit(1)
89
+ })
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Generic HTTP-callback context resolver.
3
+ *
4
+ * Called by the mcp-http container for every inbound MCP request. Instead
5
+ * of resolving the tenant + credentials locally (which would require the
6
+ * OS image to know about the portal DB schema + KMS), this helper forwards
7
+ * the inbound Bearer JWT to a caller-configured endpoint that performs the
8
+ * real resolution. The endpoint is expected to return a JSON body shaped
9
+ * exactly like `RequestContext`.
10
+ *
11
+ * Design intent
12
+ * -------------
13
+ * The OS repo ships the MCP infrastructure but knows nothing about how
14
+ * tenants are identified or where their ServiceNow credentials live. A
15
+ * downstream deployment (e.g. snow-flow-enterprise) runs this image as the
16
+ * `mcp-http` container, points `MCP_RESOLVER_URL` at its own HTTP endpoint,
17
+ * and hands out a shared-secret token so only the mcp-http container can
18
+ * call that endpoint.
19
+ *
20
+ * Env vars consumed by `createHttpResolver()`:
21
+ * MCP_RESOLVER_URL — full URL of the downstream resolver endpoint
22
+ * MCP_INTERNAL_TOKEN — shared secret the endpoint verifies on the
23
+ * `X-Internal-Auth` header. Keeps the endpoint
24
+ * closed to anyone else on the docker network.
25
+ */
26
+
27
+ import { ContextResolver } from "../handlers/types.js"
28
+
29
+ export interface HttpResolverOptions {
30
+ /**
31
+ * Full URL of the resolver endpoint. Example:
32
+ * http://portal:3000/api/internal/mcp-resolve
33
+ */
34
+ url: string
35
+
36
+ /**
37
+ * Shared secret set in `X-Internal-Auth` on every call. Must match the
38
+ * value configured on the resolver endpoint side.
39
+ */
40
+ internalToken: string
41
+
42
+ /**
43
+ * Per-call timeout in ms. Defaults to 5000.
44
+ */
45
+ timeoutMs?: number
46
+ }
47
+
48
+ /**
49
+ * Build a `ContextResolver` that forwards each inbound request to
50
+ * `opts.url`. The returned function is the exact type `createHttpApp`
51
+ * expects for its `resolveContext` dependency.
52
+ */
53
+ export const createHttpResolver = (opts: HttpResolverOptions): ContextResolver => {
54
+ const timeoutMs = opts.timeoutMs ?? 5000
55
+
56
+ return async (request: any) => {
57
+ const headers = (request as any)?.headers ?? {}
58
+ // Forward the caller's Bearer token verbatim — the resolver endpoint
59
+ // is the thing that knows how to verify it.
60
+ const authorization =
61
+ headers.authorization ?? headers.Authorization ?? headers.AUTHORIZATION ?? ""
62
+
63
+ const controller = new AbortController()
64
+ const timer = setTimeout(() => controller.abort(), timeoutMs)
65
+
66
+ try {
67
+ const response = await fetch(opts.url, {
68
+ method: "POST",
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ "X-Internal-Auth": opts.internalToken,
72
+ },
73
+ body: JSON.stringify({ authorization }),
74
+ signal: controller.signal,
75
+ })
76
+
77
+ if (!response.ok) {
78
+ const body = await response.text().catch(() => "")
79
+ throw new Error(
80
+ `mcp-resolver endpoint returned ${response.status}: ${body.slice(0, 200)}`,
81
+ )
82
+ }
83
+
84
+ return (await response.json()) as Awaited<ReturnType<ContextResolver>>
85
+ } finally {
86
+ clearTimeout(timer)
87
+ }
88
+ }
89
+ }