@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
package/README.md
CHANGED
|
@@ -132,21 +132,32 @@ Present that approval through the product's decision flow.
|
|
|
132
132
|
Set `approve: true` only after the caller has authorized that action.
|
|
133
133
|
Automatic capability-token minting remains available when the server returns no concrete approval.
|
|
134
134
|
|
|
135
|
-
##
|
|
135
|
+
## OAuth scopes
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
`requestedScopes` is a provider-generic, non-empty unique subset of the connector
|
|
138
|
+
manifest and the host's optional narrowing. Platform rejects empty, duplicate,
|
|
139
|
+
unknown, or widening requests. The exact set is bound to OAuth state; narrowed
|
|
140
|
+
flows persist a connection only when the provider grants that same set, with no
|
|
141
|
+
missing, narrower, or additional grants. Hosts that do not narrow retain their
|
|
142
|
+
existing defaults and provider-grant fallback.
|
|
143
|
+
|
|
144
|
+
The SDK refuses a start response with different scopes, including responses from
|
|
145
|
+
an older Platform deployment. GitHub retains its special contract: the exact
|
|
146
|
+
`read:user`, `repo`, and `read:org` set. Gmail can request manifest-declared
|
|
147
|
+
read-only access when the host permits it:
|
|
142
148
|
|
|
143
149
|
```ts
|
|
144
150
|
const hub = HubClient.fromEnv();
|
|
151
|
+
await hub.connections.start("gmail", {
|
|
152
|
+
requestedScopes: ["https://www.googleapis.com/auth/gmail.readonly"],
|
|
153
|
+
});
|
|
145
154
|
await hub.connections.start("github", {
|
|
146
155
|
requestedScopes: ["read:user", "repo", "read:org"],
|
|
147
156
|
});
|
|
148
157
|
```
|
|
149
158
|
|
|
159
|
+
Omitting `requestedScopes` retains the host-selected or manifest default.
|
|
160
|
+
|
|
150
161
|
## Workflows
|
|
151
162
|
|
|
152
163
|
`hub.workflows` covers the `/v1/workflows` surface — the same resource the
|
|
@@ -420,3 +431,28 @@ const result = await broker.invoke(grant.grantId, connectionId, "github.issues.l
|
|
|
420
431
|
```
|
|
421
432
|
|
|
422
433
|
`invoke` mints a fresh one-use, 60-second broker token and checks its selected scope before execution. The host application must recheck its own current customer enrollment on every call and revoke the app grant when access ends. Do not return broker tokens or application secrets to a browser or persist them in agent profiles.
|
|
434
|
+
|
|
435
|
+
## Embedded Node MCP hosts
|
|
436
|
+
|
|
437
|
+
`@tangle-network/hub-sdk/mcp` owns `HubMcpScope`, connection resolution,
|
|
438
|
+
`createHubMcpRoutes`, the HTTP bridge and `lowerProfileConnections`. The root
|
|
439
|
+
client export remains browser/Worker-safe.
|
|
440
|
+
|
|
441
|
+
Create one scope for the host's lifetime with its existing canonical hash and
|
|
442
|
+
credential-renewal function. Mount `createHubMcpRoutes(scope)` on the host's
|
|
443
|
+
existing listener. `scope.resolveConnectionsToMcp(client, refs, serverForGrant)`
|
|
444
|
+
validates the connections, provider and tool allowlists, rolls back partial
|
|
445
|
+
allocation, and gives the host its native MCP representation. Only a pure
|
|
446
|
+
attach-all request can return an explicitly degraded empty result; required
|
|
447
|
+
connections remain errors. The host surfaces degradation and recovery.
|
|
448
|
+
|
|
449
|
+
Revoke each resolution's `grantTokens` when its run settles. `scope.close()`
|
|
450
|
+
revokes the remaining grants of that instance and prevents new allocation.
|
|
451
|
+
Other scopes and their borrowed clients remain independent.
|
|
452
|
+
|
|
453
|
+
The published `@tangle-network/hub-sdk/mcp/stdio` entry reads only
|
|
454
|
+
`TANGLE_HUB_MCP_ENDPOINT`. Launch it through Node with the opaque callback
|
|
455
|
+
endpoint; do not forward the Hub credential. `lowerProfileConnections` keeps
|
|
456
|
+
the authored adapter profile separate from the ephemeral workspace profile.
|
|
457
|
+
Grant endpoints belong to runtime materialization, never persisted authored
|
|
458
|
+
profiles or trace metadata.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import https from "node:https";
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
//#region src/mcp/bridge.ts
|
|
6
|
+
var HttpHubMcpBridgeClient = class {
|
|
7
|
+
endpoint;
|
|
8
|
+
constructor(endpoint) {
|
|
9
|
+
if (!endpoint || endpoint.trim().length === 0) throw new Error("TANGLE_HUB_MCP_ENDPOINT is required");
|
|
10
|
+
this.endpoint = endpoint.replace(/\/+$/, "");
|
|
11
|
+
}
|
|
12
|
+
async listTools() {
|
|
13
|
+
return (await requestJson("GET", `${this.endpoint}/tools`)).tools;
|
|
14
|
+
}
|
|
15
|
+
async callTool(name, args) {
|
|
16
|
+
return requestJson("POST", `${this.endpoint}/call`, {
|
|
17
|
+
name,
|
|
18
|
+
arguments: args ?? {}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function createHubMcpBridgeServer(client) {
|
|
23
|
+
const server = new Server({
|
|
24
|
+
name: "tangle-hub",
|
|
25
|
+
version: "0.1.0"
|
|
26
|
+
}, { capabilities: { tools: {} } });
|
|
27
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
28
|
+
return { tools: (await client.listTools()).map((tool) => ({
|
|
29
|
+
name: tool.name,
|
|
30
|
+
description: tool.description,
|
|
31
|
+
inputSchema: tool.inputSchema
|
|
32
|
+
})) };
|
|
33
|
+
});
|
|
34
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => client.callTool(request.params.name, request.params.arguments ?? {}));
|
|
35
|
+
return server;
|
|
36
|
+
}
|
|
37
|
+
function requestJson(method, url, body) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const parsed = new URL(url);
|
|
40
|
+
const payload = body ? JSON.stringify(body) : void 0;
|
|
41
|
+
const request = (parsed.protocol === "https:" ? https : http).request({
|
|
42
|
+
protocol: parsed.protocol,
|
|
43
|
+
hostname: parsed.hostname,
|
|
44
|
+
port: parsed.port,
|
|
45
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
46
|
+
method,
|
|
47
|
+
rejectUnauthorized: parsed.hostname !== "127.0.0.1",
|
|
48
|
+
headers: {
|
|
49
|
+
Accept: "application/json",
|
|
50
|
+
...payload ? {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
"Content-Length": Buffer.byteLength(payload).toString()
|
|
53
|
+
} : {}
|
|
54
|
+
}
|
|
55
|
+
}, (response) => {
|
|
56
|
+
const chunks = [];
|
|
57
|
+
response.on("data", (chunk) => {
|
|
58
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
59
|
+
});
|
|
60
|
+
response.on("end", () => {
|
|
61
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
62
|
+
if ((response.statusCode ?? 500) >= 400) {
|
|
63
|
+
reject(new Error(text || `Hub MCP request failed: ${response.statusCode}`));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
resolve(JSON.parse(text));
|
|
68
|
+
} catch (error) {
|
|
69
|
+
reject(error);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
request.on("error", reject);
|
|
74
|
+
if (payload) request.write(payload);
|
|
75
|
+
request.end();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
export { createHubMcpBridgeServer as n, HttpHubMcpBridgeClient as t };
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=bridge-D8_lAKaW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-D8_lAKaW.js","names":[],"sources":["../src/mcp/bridge.ts"],"sourcesContent":["import http from \"node:http\";\nimport https from \"node:https\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { HubMcpToolDescriptor, HubMcpToolResult } from \"./tools.js\";\n\ntype JsonObject = Record<string, unknown>;\n\ninterface HubMcpBridgeClient {\n listTools(): Promise<HubMcpToolDescriptor[]>;\n callTool(name: string, args: unknown): Promise<HubMcpToolResult>;\n}\n\nexport class HttpHubMcpBridgeClient implements HubMcpBridgeClient {\n private readonly endpoint: string;\n\n constructor(endpoint: string) {\n if (!endpoint || endpoint.trim().length === 0) {\n throw new Error(\"TANGLE_HUB_MCP_ENDPOINT is required\");\n }\n this.endpoint = endpoint.replace(/\\/+$/, \"\");\n }\n\n async listTools(): Promise<HubMcpToolDescriptor[]> {\n const response = await requestJson<{ tools: HubMcpToolDescriptor[] }>(\n \"GET\",\n `${this.endpoint}/tools`,\n );\n return response.tools;\n }\n\n async callTool(name: string, args: unknown): Promise<HubMcpToolResult> {\n return requestJson<HubMcpToolResult>(\"POST\", `${this.endpoint}/call`, {\n name,\n arguments: args ?? {},\n });\n }\n}\n\nexport function createHubMcpBridgeServer(client: HubMcpBridgeClient): Server {\n const server = new Server(\n { name: \"tangle-hub\", version: \"0.1.0\" },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n const tools = await client.listTools();\n return {\n tools: tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n })),\n };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) =>\n client.callTool(request.params.name, request.params.arguments ?? {}),\n );\n\n return server;\n}\n\nfunction requestJson<T>(\n method: \"GET\" | \"POST\",\n url: string,\n body?: JsonObject,\n) {\n return new Promise<T>((resolve, reject) => {\n const parsed = new URL(url);\n const payload = body ? JSON.stringify(body) : undefined;\n const transport = parsed.protocol === \"https:\" ? https : http;\n const request = transport.request(\n {\n protocol: parsed.protocol,\n hostname: parsed.hostname,\n port: parsed.port,\n path: `${parsed.pathname}${parsed.search}`,\n method,\n rejectUnauthorized: parsed.hostname !== \"127.0.0.1\",\n headers: {\n Accept: \"application/json\",\n ...(payload\n ? {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(payload).toString(),\n }\n : {}),\n },\n },\n (response) => {\n const chunks: Buffer[] = [];\n response.on(\"data\", (chunk) => {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n });\n response.on(\"end\", () => {\n const text = Buffer.concat(chunks).toString(\"utf8\");\n if ((response.statusCode ?? 500) >= 400) {\n reject(\n new Error(\n text || `Hub MCP request failed: ${response.statusCode}`,\n ),\n );\n return;\n }\n try {\n resolve(JSON.parse(text) as T);\n } catch (error) {\n reject(error);\n }\n });\n },\n );\n request.on(\"error\", reject);\n if (payload) request.write(payload);\n request.end();\n });\n}\n"],"mappings":";;;;;AAgBA,IAAa,yBAAb,MAAkE;CAChE;CAEA,YAAY,UAAkB;AAC5B,MAAI,CAAC,YAAY,SAAS,MAAM,CAAC,WAAW,EAC1C,OAAM,IAAI,MAAM,sCAAsC;AAExD,OAAK,WAAW,SAAS,QAAQ,QAAQ,GAAG;;CAG9C,MAAM,YAA6C;AAKjD,UAAO,MAJgB,YACrB,OACA,GAAG,KAAK,SAAS,QAClB,EACe;;CAGlB,MAAM,SAAS,MAAc,MAA0C;AACrE,SAAO,YAA8B,QAAQ,GAAG,KAAK,SAAS,QAAQ;GACpE;GACA,WAAW,QAAQ,EAAE;GACtB,CAAC;;;AAIN,SAAgB,yBAAyB,QAAoC;CAC3E,MAAM,SAAS,IAAI,OACjB;EAAE,MAAM;EAAc,SAAS;EAAS,EACxC,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE,EAAE,CAChC;AAED,QAAO,kBAAkB,wBAAwB,YAAY;AAE3D,SAAO,EACL,QAAO,MAFW,OAAO,WAAW,EAEvB,KAAK,UAAU;GAC1B,MAAM,KAAK;GACX,aAAa,KAAK;GAClB,aAAa,KAAK;GACnB,EAAE,EACJ;GACD;AAEF,QAAO,kBAAkB,uBAAuB,OAAO,YACrD,OAAO,SAAS,QAAQ,OAAO,MAAM,QAAQ,OAAO,aAAa,EAAE,CAAC,CACrE;AAED,QAAO;;AAGT,SAAS,YACP,QACA,KACA,MACA;AACA,QAAO,IAAI,SAAY,SAAS,WAAW;EACzC,MAAM,SAAS,IAAI,IAAI,IAAI;EAC3B,MAAM,UAAU,OAAO,KAAK,UAAU,KAAK,GAAG,KAAA;EAE9C,MAAM,WADY,OAAO,aAAa,WAAW,QAAQ,MAC/B,QACxB;GACE,UAAU,OAAO;GACjB,UAAU,OAAO;GACjB,MAAM,OAAO;GACb,MAAM,GAAG,OAAO,WAAW,OAAO;GAClC;GACA,oBAAoB,OAAO,aAAa;GACxC,SAAS;IACP,QAAQ;IACR,GAAI,UACA;KACE,gBAAgB;KAChB,kBAAkB,OAAO,WAAW,QAAQ,CAAC,UAAU;KACxD,GACD,EAAE;IACP;GACF,GACA,aAAa;GACZ,MAAM,SAAmB,EAAE;AAC3B,YAAS,GAAG,SAAS,UAAU;AAC7B,WAAO,KAAK,OAAO,SAAS,MAAM,GAAG,QAAQ,OAAO,KAAK,MAAM,CAAC;KAChE;AACF,YAAS,GAAG,aAAa;IACvB,MAAM,OAAO,OAAO,OAAO,OAAO,CAAC,SAAS,OAAO;AACnD,SAAK,SAAS,cAAc,QAAQ,KAAK;AACvC,YACE,IAAI,MACF,QAAQ,2BAA2B,SAAS,aAC7C,CACF;AACD;;AAEF,QAAI;AACF,aAAQ,KAAK,MAAM,KAAK,CAAM;aACvB,OAAO;AACd,YAAO,MAAM;;KAEf;IAEL;AACD,UAAQ,GAAG,SAAS,OAAO;AAC3B,MAAI,QAAS,SAAQ,MAAM,QAAQ;AACnC,UAAQ,KAAK;GACb"}
|