@tangle-network/hub-sdk 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -6
- package/dist/bridge-D8_lAKaW.js +81 -0
- package/dist/bridge-D8_lAKaW.js.map +1 -0
- package/dist/client-C-JbtG50.d.ts +2571 -0
- package/dist/client-C-JbtG50.d.ts.map +1 -0
- package/dist/client-gF4iRjd3.js +1579 -0
- package/dist/client-gF4iRjd3.js.map +1 -0
- package/dist/index.d.ts +3 -2568
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1568
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +119 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +482 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/stdio.d.ts +1 -0
- package/dist/mcp/stdio.js +16 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/package.json +14 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/mcp/profile.ts","../../src/mcp/routes.ts","../../src/mcp/tools.ts","../../src/mcp/resolver.ts","../../src/mcp/scope.ts"],"sourcesContent":["import type {\n AgentProfile,\n AgentProfileMcpServer,\n} from \"@tangle-network/agent-interface\";\n\n/** Keep authored identity separate from runtime-only grant materialization. */\nexport function lowerProfileConnections(\n profile: AgentProfile | undefined,\n runtimeMcp: Record<string, AgentProfileMcpServer>,\n): { profile?: AgentProfile; workspaceProfile?: AgentProfile } {\n if (!profile) return {};\n const { connections: _connections, ...withoutConnections } = profile;\n const collisions = Object.keys(runtimeMcp).filter(\n (name) => name in (profile.mcp ?? {}),\n );\n if (collisions.length)\n throw new Error(\n `MCP server ${collisions.join(\", \")} is declared by both the profile and the effective runtime; rename the profile server so it cannot be shadowed`,\n );\n return {\n profile: withoutConnections,\n workspaceProfile: {\n ...withoutConnections,\n ...(Object.keys(runtimeMcp).length\n ? { mcp: { ...profile.mcp, ...runtimeMcp } }\n : {}),\n },\n };\n}\n","import { Hono } from \"hono\";\nimport { z } from \"zod\";\nimport type { HubMcpScope } from \"./scope.js\";\n\nexport function createHubMcpRoutes(scope: HubMcpScope): Hono {\n const routes = new Hono();\n\n const callToolSchema = z.object({\n name: z.string().min(1),\n arguments: z.unknown().optional(),\n });\n\n routes.get(\"/grants/:token/tools\", async (c) => {\n const token = c.req.param(\"token\");\n try {\n const tools = await scope.listTools(token);\n return c.json({ tools, connectionId: scope.connectionId(token) });\n } catch (error) {\n return hubMcpError(\n c,\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n });\n\n routes.post(\"/grants/:token/call\", async (c) => {\n const token = c.req.param(\"token\");\n const parsed = callToolSchema.safeParse(\n await c.req.json().catch(() => null),\n );\n if (!parsed.success) {\n return c.json(\n {\n content: [\n { type: \"text\", text: \"Invalid Hub MCP tool call request\" },\n ],\n isError: true,\n },\n 400,\n );\n }\n\n try {\n const result = await scope.callTool(\n token,\n parsed.data.name,\n parsed.data.arguments ?? {},\n );\n return c.json(result);\n } catch (error) {\n return hubMcpError(\n c,\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n });\n\n return routes;\n}\n\nfunction hubMcpError(c: import(\"hono\").Context, error: Error) {\n const message = error.message;\n return c.json(\n {\n content: [{ type: \"text\", text: message }],\n isError: true,\n },\n 403,\n );\n}\n","import { HubSdkError } from \"../client.js\";\nimport type { HubConnection } from \"../connection-types.js\";\nimport type { HubTool } from \"../tool-types.js\";\n\nexport type HubMcpToolDescriptor = {\n name: string;\n path: string;\n description: string;\n inputSchema: Record<string, unknown>;\n};\n\nexport type HubMcpToolResult = {\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n /**\n * Machine-readable error detail for a failed call (MCP `structuredContent`).\n * Carries the hub error `code` and `details` (e.g. the `approval` object with\n * `id`/`connectionId` for a `HUB_APPROVAL_REQUIRED` gate) so consumers can act\n * programmatically — the flattened `content` text alone drops that structure.\n */\n structuredContent?: Record<string, unknown>;\n};\n\nexport function assertConnectionIsActive(\n connection: HubConnection | undefined,\n connectionId: string,\n): asserts connection is HubConnection {\n if (!connection) {\n throw new HubSdkError({\n code: \"HUB_CONNECTION_MISSING\",\n message: `Hub connection \"${connectionId}\" not found for this principal`,\n details: { connectionId },\n });\n }\n if (connection.status !== \"active\") {\n throw new HubSdkError({\n code:\n connection.status === \"revoked\"\n ? \"HUB_CONNECTION_REVOKED\"\n : \"HUB_CONNECTION_MISSING\",\n message: `Hub connection \"${connectionId}\" is \"${connection.status}\", expected \"active\"`,\n details: {\n connectionId,\n providerId: connection.providerId,\n status: connection.status,\n },\n });\n }\n}\n\nexport function toHubMcpToolName(path: string): string {\n return path.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nexport function readInputSchema(tool: HubTool): Record<string, unknown> {\n if (\n tool.inputSchema &&\n typeof tool.inputSchema === \"object\" &&\n !Array.isArray(tool.inputSchema)\n ) {\n return tool.inputSchema as Record<string, unknown>;\n }\n return { type: \"object\", properties: {} };\n}\n\nexport function errorResult(message: string): HubMcpToolResult {\n return {\n content: [{ type: \"text\", text: message }],\n isError: true,\n };\n}\n","import type { AgentProfileConnection } from \"@tangle-network/agent-interface\";\nimport {\n type HubClient,\n HubSdkError,\n isHubUnauthenticated,\n} from \"../client.js\";\nimport type { HubConnection } from \"../connection-types.js\";\nimport type { HubTool } from \"../tool-types.js\";\nimport type {\n HubMcpAuthRetry,\n HubMcpScope,\n HubMcpScopeOptions,\n} from \"./scope.js\";\nimport {\n assertConnectionIsActive,\n type HubMcpToolDescriptor,\n toHubMcpToolName,\n} from \"./tools.js\";\n\n/**\n * Wildcard token. As a `connectionId` it means \"every active connection of the\n * principal\"; as the sole `capabilities` entry it means \"every tool the\n * connection exposes\". Both are expanded at session-spawn from cached\n * per-provider catalog searches (see `expandConnectionRefs`). The web dashboard emits\n * `{ connectionId: \"*\", capabilities: [\"*\"] }` to attach all connected hub\n * integrations; explicit CLI grants name concrete connections + paths.\n */\nconst WILDCARD = \"*\";\n\n/**\n * Whether every ref is the full attach-all sentinel — `{ connectionId: \"*\",\n * capabilities: [\"*\"] }`, naming neither a connection nor a capability.\n *\n * Such a request asserts nothing: \"give me whatever integrations I happen to\n * have\" is satisfied by an empty toolbelt, so resolving it to no servers is a\n * legitimate answer rather than a swallowed failure. Every other shape names\n * something that must exist — a concrete connection, or a concrete capability\n * fanned across connections — and stays fail-loud, mixed sets included.\n */\nexport function isAttachAllRequest(\n refs: readonly AgentProfileConnection[],\n): boolean {\n return (\n refs.length > 0 &&\n refs.every(\n (ref) =>\n ref.connectionId === WILDCARD &&\n ref.capabilities.length === 1 &&\n ref.capabilities[0] === WILDCARD,\n )\n );\n}\n\n/**\n * A connection reference with the wildcards resolved to concrete values.\n * `descriptors` is set only for wildcard-expanded capability sets, where the\n * provider match is already guaranteed by enumeration and the grant carries\n * the pre-resolved tool list (no per-path `tools.describe`).\n */\ntype ConcreteConnectionRef = {\n connectionId: string;\n capabilities: string[];\n alias?: string;\n descriptors?: HubMcpToolDescriptor[];\n /** True when this ref came from a `connectionId: \"*\"` fan-out (best-effort). */\n fromWildcardConnection: boolean;\n};\n\n/**\n * Resolution result. `mcp` is the runtime MCP server map the backend\n * installs; `grantTokens` are the opaque sidecar-local grant handles that\n * MUST be revoked when the owning backend shuts down (see\n * `SdkBackend.shutdown`).\n */\nexport type ResolvedHubConnections<T> = {\n mcp: Record<string, T>;\n grantTokens: string[];\n /** Stable identity of the concrete connections and tools behind this result. */\n resolutionKey: string;\n /**\n * Set when the result is an EMPTY toolbelt standing in for a failure the\n * request tolerated (see the attach-all rule on `isAttachAllRequest`). The\n * session runs, but with none of the tenant's integrations attached, so this\n * must be surfaced — a silently empty toolbelt is exactly the trade the\n * resolver's fail-loud posture exists to avoid.\n */\n degraded?: {\n reason: \"hub-unauthenticated\";\n message: string;\n requestShape: \"attach-all\";\n };\n};\n\ntype ResolutionHost<T> = {\n grants: Pick<HubMcpScope, \"register\" | \"revoke\">;\n hash: HubMcpScopeOptions[\"hash\"];\n withAuthRetry: HubMcpAuthRetry;\n serverForGrant(token: string): T;\n};\n\n/** Resolve required grants once, with rollback before the host's auth retry. */\nexport async function resolveConnectionsToMcp<T>(\n client: HubClient,\n connections: readonly AgentProfileConnection[],\n host: ResolutionHost<T>,\n): Promise<ResolvedHubConnections<T>> {\n const empty = (): ResolvedHubConnections<T> => ({\n mcp: {},\n grantTokens: [],\n resolutionKey: host.hash([]),\n });\n if (connections.length === 0) return empty();\n try {\n return await host.withAuthRetry(() =>\n resolveConnectionsOnce(client, connections, host),\n );\n } catch (error) {\n if (!isHubUnauthenticated(error) || !isAttachAllRequest(connections))\n throw error;\n return {\n ...empty(),\n degraded: {\n reason: \"hub-unauthenticated\",\n message:\n \"Hub rejected the current credential and it could not be refreshed; no Hub integrations were attached\",\n requestShape: \"attach-all\",\n },\n };\n }\n}\n\nasync function resolveConnectionsOnce<T>(\n client: HubClient,\n connections: readonly AgentProfileConnection[],\n host: ResolutionHost<T>,\n): Promise<ResolvedHubConnections<T>> {\n // Cached connections.list — one round-trip per resolve call. The hub\n // returns all of the principal's connections; we index by id so the\n // per-entry validation below is O(1).\n const listed = await client.connections.list();\n const connectionsById = new Map<string, HubConnection>();\n for (const conn of listed.connections) {\n connectionsById.set(conn.id, conn);\n }\n\n // Resolve `*` connection / `*` capability wildcards into concrete refs\n // before the grant loop. Wildcard expansion shares one cached\n // provider-scoped catalog search per provider across all refs.\n const concreteRefs = await expandConnectionRefs(\n client,\n connections,\n listed.connections,\n );\n const resolutionKey = host.hash(\n concreteRefs\n .map((ref) => ({\n connectionId: ref.connectionId,\n providerId: connectionsById.get(ref.connectionId)?.providerId,\n alias:\n ref.alias ??\n defaultAliasFor(\n connectionsById.get(ref.connectionId)?.providerId ?? \"\",\n ref.connectionId,\n ref.capabilities,\n ),\n capabilities: [...ref.capabilities].sort(),\n descriptors: ref.descriptors\n ? [...ref.descriptors].sort((a, b) => a.path.localeCompare(b.path))\n : undefined,\n }))\n .sort((a, b) =>\n `${a.connectionId}:${a.alias ?? \"\"}`.localeCompare(\n `${b.connectionId}:${b.alias ?? \"\"}`,\n ),\n ),\n );\n\n // Server alias must be unique. Two entries with the same alias would\n // produce a name-collision in the runtime MCP server map and quietly\n // shadow each other; fail-loud at the resolver instead.\n const seenAliases = new Map<string, string>();\n const mcp: Record<string, T> = {};\n const grantTokens: string[] = [];\n\n try {\n for (const ref of concreteRefs) {\n const conn = connectionsById.get(ref.connectionId);\n assertConnectionIsActive(conn, ref.connectionId);\n\n // Wildcard-expanded refs carry pre-resolved descriptors enumerated from\n // the catalog, so the provider match is already guaranteed; only explicit\n // capability paths need per-path validation. We do this serially so the\n // first bad path produces a deterministic error message; concurrent\n // describe calls would race and surface an arbitrary one.\n if (!ref.descriptors) {\n for (const capabilityPath of ref.capabilities) {\n const described = await client.tools.describe(capabilityPath);\n const tool = described.tool;\n if (\n tool.requiredConnectionProviderId !== undefined &&\n tool.requiredConnectionProviderId !== conn.providerId\n ) {\n throw new HubSdkError({\n code: \"HUB_INVALID_INPUT\",\n message:\n `Capability \"${capabilityPath}\" requires provider ` +\n `\"${tool.requiredConnectionProviderId}\" but connection ` +\n `\"${ref.connectionId}\" is \"${conn.providerId}\"`,\n details: {\n connectionId: ref.connectionId,\n providerId: conn.providerId,\n capabilityPath,\n requiredProviderId: tool.requiredConnectionProviderId,\n },\n });\n }\n }\n }\n\n const alias =\n ref.alias ??\n defaultAliasFor(conn.providerId, ref.connectionId, ref.capabilities);\n const previousConnectionId = seenAliases.get(alias);\n if (previousConnectionId !== undefined) {\n throw new HubSdkError({\n code: \"HUB_INVALID_INPUT\",\n message:\n `Duplicate MCP alias \"${alias}\" — each hub connection grant must have a unique alias ` +\n `(connections \"${previousConnectionId}\" and \"${ref.connectionId}\")`,\n details: {\n alias,\n connectionId: ref.connectionId,\n previousConnectionId,\n },\n });\n }\n seenAliases.set(alias, ref.connectionId);\n\n const grantToken = host.grants.register({\n client,\n connectionId: ref.connectionId,\n capabilities: ref.capabilities,\n tools: ref.descriptors,\n });\n grantTokens.push(grantToken);\n\n mcp[alias] = host.serverForGrant(grantToken);\n }\n } catch (error) {\n for (const token of grantTokens) {\n host.grants.revoke(token);\n }\n throw error;\n }\n\n return { mcp, grantTokens, resolutionKey };\n}\n\n/**\n * Expand `*` wildcards into concrete connection refs.\n *\n * - `connectionId: \"*\"` fans out to one ref per ACTIVE connection, carrying\n * the original capability set. This is the web dashboard's \"attach all my\n * connected integrations\" sentinel — best-effort, so a connection that\n * resolves to zero tools is dropped rather than producing an empty server.\n * - `capabilities: [\"*\"]` enumerates the connection's tools from a cached\n * provider-scoped `tools.search(\"\", { provider })` and emits the concrete\n * paths plus pre-built descriptors. An EXPLICIT\n * connection that exposes no tools fails loud (the caller named it); a\n * wildcard-connection one is skipped.\n *\n * Explicit (non-wildcard) refs pass through untouched for the existing\n * fail-loud validation path.\n */\nasync function expandConnectionRefs(\n client: HubClient,\n refs: readonly AgentProfileConnection[],\n allConnections: readonly HubConnection[],\n): Promise<ConcreteConnectionRef[]> {\n const active = allConnections.filter((c) => c.status === \"active\");\n\n // One search per distinct provider, cached by providerId so N connections\n // on the same provider still cost one request. The search MUST be\n // provider-scoped: an unfiltered `tools.search(\"\")` caps the native\n // (non-substrate) arm at its default shortlist of 20, so a provider that\n // lives only in that arm — GitHub, which the substrate deliberately\n // excludes — contributes zero rows and would be silently skipped. The\n // explicit limit is the contract maximum, covering native providers with\n // more than 20 actions.\n const catalogByProvider = new Map<string, Promise<HubTool[]>>();\n const loadCatalog = (providerId: string): Promise<HubTool[]> => {\n let cached = catalogByProvider.get(providerId);\n if (cached === undefined) {\n cached = client.tools\n .search(\"\", { provider: providerId, limit: 200 })\n .then((res) => res.tools ?? []);\n catalogByProvider.set(providerId, cached);\n }\n return cached;\n };\n\n // Step 1 — fan out `connectionId: \"*\"` across active connections.\n const connExpanded = refs.flatMap<{\n connectionId: string;\n capabilities: string[];\n alias?: string;\n fromWildcardConnection: boolean;\n }>((ref) => {\n if (ref.connectionId === WILDCARD) {\n return active.map((conn) => ({\n connectionId: conn.id,\n capabilities: [...ref.capabilities],\n fromWildcardConnection: true,\n }));\n }\n return [\n {\n connectionId: ref.connectionId,\n capabilities: [...ref.capabilities],\n alias: ref.alias,\n fromWildcardConnection: false,\n },\n ];\n });\n\n // Prewarm — start one in-flight search per distinct provider before the\n // sequential expansion loop below, so N distinct providers cost one\n // round-trip of latency instead of N. The no-op catch stops a provider\n // that fails while the loop is still awaiting an earlier one from\n // surfacing as an unhandled rejection; the loop's own await of the cached\n // promise still observes the real rejection.\n for (const ref of connExpanded) {\n if (ref.capabilities.length === 1 && ref.capabilities[0] === WILDCARD) {\n const conn = connectionsByIdLookup(allConnections, ref.connectionId);\n if (conn?.status === \"active\") {\n loadCatalog(conn.providerId).catch(() => {});\n }\n }\n }\n\n // Step 2 — expand `capabilities: [\"*\"]` to the connection's concrete tools.\n const out: ConcreteConnectionRef[] = [];\n for (const ref of connExpanded) {\n const isCapabilityWildcard =\n ref.capabilities.length === 1 && ref.capabilities[0] === WILDCARD;\n if (!isCapabilityWildcard) {\n out.push({\n connectionId: ref.connectionId,\n capabilities: ref.capabilities,\n alias: ref.alias,\n fromWildcardConnection: ref.fromWildcardConnection,\n });\n continue;\n }\n\n const conn = connectionsByIdLookup(allConnections, ref.connectionId);\n if (!conn || conn.status !== \"active\") {\n // Explicit connection with a `*` capability but missing/inactive: leave\n // the wildcard in place so the grant loop's assertConnectionIsActive\n // fails loud with the right diagnostic. (A wildcard-connection ref can\n // never reach here — it was built from the active list.)\n out.push({\n connectionId: ref.connectionId,\n capabilities: [WILDCARD],\n alias: ref.alias,\n fromWildcardConnection: ref.fromWildcardConnection,\n });\n continue;\n }\n\n const tools = (await loadCatalog(conn.providerId)).filter(\n (t) =>\n t.connectionStatus === \"connected\" &&\n (t.requiredConnectionProviderId ?? t.providerId) === conn.providerId,\n );\n const descriptors = buildToolDescriptors(tools);\n\n if (descriptors.length === 0) {\n if (ref.fromWildcardConnection) {\n // Best-effort fan-out: a connected provider with no catalog tools is\n // simply not attached.\n\n continue;\n }\n throw new HubSdkError({\n code: \"HUB_INVALID_INPUT\",\n message:\n `Connection \"${ref.connectionId}\" (${conn.providerId}) exposes no ` +\n \"tools — cannot expand capability `*`\",\n details: {\n connectionId: ref.connectionId,\n providerId: conn.providerId,\n },\n });\n }\n\n out.push({\n connectionId: ref.connectionId,\n capabilities: descriptors.map((d) => d.path),\n alias: ref.alias,\n descriptors,\n fromWildcardConnection: ref.fromWildcardConnection,\n });\n }\n\n return out;\n}\n\nfunction connectionsByIdLookup(\n connections: readonly HubConnection[],\n id: string,\n): HubConnection | undefined {\n return connections.find((c) => c.id === id);\n}\n\n/**\n * Build MCP tool descriptors from catalog search results, deduping by the\n * sanitized MCP tool name (hub paths with the same leaf collapse, matching the\n * grant bridge's own naming).\n */\nfunction buildToolDescriptors(\n tools: readonly HubTool[],\n): HubMcpToolDescriptor[] {\n const seen = new Set<string>();\n const out: HubMcpToolDescriptor[] = [];\n for (const tool of tools) {\n const name = toHubMcpToolName(tool.path);\n if (seen.has(name)) continue;\n seen.add(name);\n out.push({\n name,\n path: tool.path,\n description: tool.description ?? tool.title ?? tool.path,\n inputSchema:\n tool.inputSchema &&\n typeof tool.inputSchema === \"object\" &&\n !Array.isArray(tool.inputSchema)\n ? (tool.inputSchema as Record<string, unknown>)\n : { type: \"object\", properties: {} },\n });\n }\n return out;\n}\n\nexport function defaultAliasFor(\n providerId: string,\n connectionId: string,\n capabilityPaths: readonly string[],\n): string {\n // Alias shape: `<provider>-<conn-shorthand>-<capability-leaf>`, readable in\n // opencode's MCP server list. Determinism is what matters — the same\n // (provider, connectionId, capability) tuple always produces the same\n // alias, so a fresh resolve call after token expiry keeps the same name.\n //\n // The shorthand must also be unique per connection. Hub ids look like\n // `hubconn_<provider>_<random>`, so their first characters are the same for\n // every connection of a provider and only the random tail tells them apart;\n // the shorthand therefore comes from the tail. Only letters and digits\n // survive: base64url tails carry `_` and `-`, and a `__` inside a server\n // name breaks the `mcp__<server>__<tool>` names codex and claude-code build\n // for MCP tools.\n const bare = connectionId\n .replace(/^(hubconn_|conn_)/, \"\")\n .replace(new RegExp(`^${escapeRegExp(providerId)}_`), \"\")\n .replace(/[^a-zA-Z0-9]/g, \"\");\n const shortConn = bare.slice(-8);\n const leaf =\n capabilityPaths.length === 1\n ? (capabilityPaths[0]?.split(\".\").pop() ?? capabilityPaths[0])\n : \"tools\";\n return `${providerId}-${shortConn}-${leaf}`\n .toLowerCase()\n .replace(/[^a-z0-9_-]+/g, \"-\");\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { randomBytes } from \"node:crypto\";\nimport type { AgentProfileConnection } from \"@tangle-network/agent-interface\";\nimport { type HubClient, HubSdkError } from \"../client.js\";\nimport {\n type ResolvedHubConnections,\n resolveConnectionsToMcp,\n} from \"./resolver.js\";\nimport {\n errorResult,\n type HubMcpToolDescriptor,\n type HubMcpToolResult,\n readInputSchema,\n toHubMcpToolName,\n} from \"./tools.js\";\n\nexport type HubMcpAuthRetry = <T>(operation: () => Promise<T>) => Promise<T>;\nexport interface HubMcpScopeOptions {\n hash(value: object | string): string;\n withAuthRetry?: HubMcpAuthRetry;\n}\ntype HubMcpGrant = {\n client: HubClient;\n connectionId: string;\n capabilities: readonly string[];\n tools?: HubMcpToolDescriptor[];\n};\n\n/** Opaque grants owned by one embedded host; close revokes only this scope. */\nexport class HubMcpScope {\n private readonly grants = new Map<string, HubMcpGrant>();\n private closed = false;\n private readonly withAuthRetry: HubMcpAuthRetry;\n constructor(private readonly options: HubMcpScopeOptions) {\n this.withAuthRetry = options.withAuthRetry ?? ((operation) => operation());\n }\n close(): void {\n this.closed = true;\n this.grants.clear();\n }\n resolveConnectionsToMcp<T>(\n client: HubClient,\n connections: readonly AgentProfileConnection[],\n serverForGrant: (token: string) => T,\n ): Promise<ResolvedHubConnections<T>> {\n return resolveConnectionsToMcp(client, connections, {\n grants: this,\n hash: this.options.hash,\n withAuthRetry: this.withAuthRetry,\n serverForGrant,\n });\n }\n register(input: {\n client: HubClient;\n connectionId: string;\n capabilities: readonly string[];\n /**\n * Pre-resolved tool descriptors. When set, `listTools` returns\n * these verbatim instead of calling `tools.describe` per capability path.\n * The resolver pre-populates this for wildcard (`*`) grants, where it has\n * already enumerated the connection's tools from a single `tools.search`,\n * so the bridge does zero describe round-trips. `tools[].path` is still the\n * authoritative allowlist `callTool` dispatches against.\n */\n tools?: readonly HubMcpToolDescriptor[];\n }): string {\n if (this.closed)\n throw new HubSdkError({\n code: \"HUB_POLICY_DENIED\",\n message: \"Hub MCP scope is closed\",\n });\n const token = `hmcp_${randomBytes(32).toString(\"base64url\")}`;\n this.grants.set(token, {\n client: input.client,\n connectionId: input.connectionId,\n capabilities: [...input.capabilities],\n tools: input.tools ? [...input.tools] : undefined,\n });\n return token;\n }\n\n /**\n * Revoke a single grant token. Returns true when a grant was removed. Safe\n * to call on an already-revoked or never-issued token (returns false) so\n * shutdown paths can revoke unconditionally without double-free risk.\n */\n revoke(token: string): boolean {\n return this.grants.delete(token);\n }\n\n /** Trusted sidecar metadata for the native exact-effect permission hook. */\n connectionId(token: string): string {\n return this.readGrant(token).connectionId;\n }\n\n async listTools(token: string): Promise<HubMcpToolDescriptor[]> {\n const grant = this.readGrant(token);\n if (grant.tools) return grant.tools;\n\n const resolved: HubMcpToolDescriptor[] = [];\n const seenNames = new Set<string>();\n for (const path of grant.capabilities) {\n // A grant outlives the credential it was registered with: the sandbox's\n // Hub key expires mid-session, so a describe on a long-running session can\n // 401 long after the spawn that resolved it succeeded.\n const described = await this.withAuthRetry(() => {\n this.readGrant(token);\n return grant.client.tools.describe(path);\n });\n const tool = described.tool;\n const name = toHubMcpToolName(path);\n if (seenNames.has(name)) {\n throw new HubSdkError({\n code: \"HUB_INVALID_INPUT\",\n message: `Duplicate MCP tool name \"${name}\" for Hub grant`,\n details: { connectionId: grant.connectionId, path },\n });\n }\n seenNames.add(name);\n resolved.push({\n name,\n path,\n description: tool.description ?? tool.title ?? path,\n inputSchema: readInputSchema(tool),\n });\n }\n\n this.readGrant(token);\n grant.tools = resolved;\n return resolved;\n }\n\n async callTool(\n token: string,\n name: string,\n // oxlint-disable-next-line anti-slop/no-unknown-parameters -- MCP boundary: the argument bag is the tool's own schema, validated by Hub at invoke time\n args: unknown,\n ): Promise<HubMcpToolResult> {\n const grant = this.readGrant(token);\n const tools = await this.listTools(token);\n const tool = tools.find((candidate) => candidate.name === name);\n if (!tool) {\n return errorResult(`Unknown tool: ${name}`);\n }\n\n let effectInvocation: string | undefined;\n let effectArgs = args ?? {};\n if (\n effectArgs &&\n typeof effectArgs === \"object\" &&\n !Array.isArray(effectArgs) &&\n Object.hasOwn(effectArgs, \"__tangle_effect_invocation\")\n ) {\n const { __tangle_effect_invocation: invocation, ...original } =\n effectArgs as Record<string, unknown>;\n if (\n typeof invocation !== \"string\" ||\n !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(invocation)\n )\n return errorResult(\"Invalid native effect invocation\");\n effectInvocation = invocation;\n effectArgs = original;\n }\n this.readGrant(token);\n try {\n // The refresh lane heals the SPAWN path, but an agent that first reaches\n // for a Hub tool an hour into a session hits the expiry here instead —\n // this is the user-facing call, so it needs the same retry. The client\n // reads its credential per request, so re-running the invoke is enough;\n // there is no client to rebuild.\n const result = await this.withAuthRetry(() => {\n this.readGrant(token);\n return grant.client.tools.invoke(tool.path, effectArgs, {\n connectionId: grant.connectionId,\n ...(effectInvocation\n ? { idempotencyKey: `line-effect:${effectInvocation}` }\n : {}),\n });\n });\n return {\n content: [\n { type: \"text\", text: JSON.stringify(result.result ?? result) },\n ],\n };\n } catch (error) {\n if (error instanceof HubSdkError) {\n const approval =\n error.code === \"HUB_APPROVAL_REQUIRED\"\n ? (error.details as { approval?: unknown } | undefined)?.approval\n : undefined;\n // MCP clients commonly pass only content text to the agent and transcript.\n // Include the approval reference there as well as in structuredContent.\n return {\n content: [\n {\n type: \"text\",\n text: approval\n ? JSON.stringify({\n code: error.code,\n message: error.message,\n approval,\n })\n : `${error.code}: ${error.message}`,\n },\n ],\n isError: true,\n structuredContent: {\n code: error.code,\n message: error.message,\n ...(error.details ? { details: error.details } : {}),\n },\n };\n }\n const message = error instanceof Error ? error.message : String(error);\n return errorResult(message);\n }\n }\n\n private readGrant(token: string): HubMcpGrant {\n const grant = this.grants.get(token);\n if (!grant) {\n throw new HubSdkError({\n code: \"HUB_POLICY_DENIED\",\n message: \"Unknown Hub MCP grant token\",\n });\n }\n return grant;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as createHubMcpBridgeServer, t as HttpHubMcpBridgeClient } from "../bridge-D8_lAKaW.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
//#region src/mcp/stdio.ts
|
|
5
|
+
const server = createHubMcpBridgeServer(new HttpHubMcpBridgeClient(readRequiredEnv("TANGLE_HUB_MCP_ENDPOINT")));
|
|
6
|
+
const transport = new StdioServerTransport();
|
|
7
|
+
await server.connect(transport);
|
|
8
|
+
function readRequiredEnv(name) {
|
|
9
|
+
const value = process.env[name];
|
|
10
|
+
if (!value || value.trim().length === 0) throw new Error(`${name} is required`);
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export {};
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=stdio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.js","names":[],"sources":["../../src/mcp/stdio.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createHubMcpBridgeServer, HttpHubMcpBridgeClient } from \"./bridge.js\";\n\nconst server = createHubMcpBridgeServer(\n new HttpHubMcpBridgeClient(readRequiredEnv(\"TANGLE_HUB_MCP_ENDPOINT\")),\n);\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n\nfunction readRequiredEnv(name: string): string {\n const value = process.env[name];\n if (!value || value.trim().length === 0) {\n throw new Error(`${name} is required`);\n }\n return value;\n}\n"],"mappings":";;;;AAKA,MAAM,SAAS,yBACb,IAAI,uBAAuB,gBAAgB,0BAA0B,CAAC,CACvE;AACD,MAAM,YAAY,IAAI,sBAAsB;AAC5C,MAAM,OAAO,QAAQ,UAAU;AAE/B,SAAS,gBAAgB,MAAsB;CAC7C,MAAM,QAAQ,QAAQ,IAAI;AAC1B,KAAI,CAAC,SAAS,MAAM,MAAM,CAAC,WAAW,EACpC,OAAM,IAAI,MAAM,GAAG,KAAK,cAAc;AAExC,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/hub-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Typed SDK for Tangle Hub tool discovery and execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"import": "./dist/index.js",
|
|
11
11
|
"types": "./dist/index.d.ts"
|
|
12
|
-
}
|
|
12
|
+
},
|
|
13
|
+
"./mcp": {
|
|
14
|
+
"import": "./dist/mcp/index.js",
|
|
15
|
+
"types": "./dist/mcp/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./mcp/stdio": "./dist/mcp/stdio.js"
|
|
13
18
|
},
|
|
14
19
|
"repository": {
|
|
15
20
|
"type": "git",
|
|
@@ -24,18 +29,21 @@
|
|
|
24
29
|
"dist"
|
|
25
30
|
],
|
|
26
31
|
"dependencies": {
|
|
27
|
-
"@tangle-network/agent-core": "^1.0.0"
|
|
32
|
+
"@tangle-network/agent-core": "^1.0.0",
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
34
|
+
"@tangle-network/agent-interface": "^5.10.2",
|
|
35
|
+
"hono": "^4.13.13",
|
|
36
|
+
"zod": "4.6.6"
|
|
28
37
|
},
|
|
29
38
|
"devDependencies": {
|
|
30
39
|
"@types/node": "25.6.0",
|
|
31
|
-
"hono": "^4.13.13",
|
|
32
40
|
"tsdown": "0.21.10",
|
|
33
41
|
"typescript": "^6.0.3",
|
|
34
42
|
"vitest": "^4.1.12"
|
|
35
43
|
},
|
|
36
44
|
"tangleRelease": {
|
|
37
|
-
"sourceSha": "
|
|
38
|
-
"mergedAt": "2026-10-
|
|
45
|
+
"sourceSha": "d1dba72e134ad99b1bebcc956b2b93cb5482306b",
|
|
46
|
+
"mergedAt": "2026-10-28T02:38:45-06:00"
|
|
39
47
|
},
|
|
40
48
|
"scripts": {
|
|
41
49
|
"build": "tsdown",
|