@topolo/mcp 0.4.9 → 0.4.10

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/dist/dispatch.js CHANGED
@@ -347,14 +347,20 @@ async function buildTopoloTools(topolo, scopes, tools = TOOLS) {
347
347
  const actionTools = actionsToToolDefs(actions);
348
348
  return filterToolsByScopes([...baseTools, ...actionTools], scopes);
349
349
  }
350
+ function effectiveAgentAccess(a) {
351
+ if (a.agentAccess) return a.agentAccess;
352
+ if (a.destructive) return "off";
353
+ if (a.readOnly && !a.requiresConfirmation) return "auto";
354
+ return "confirm";
355
+ }
350
356
  function actionsToToolDefs(actions) {
351
- return actions.map((action) => ({
357
+ return actions.map((action) => ({ action, access: effectiveAgentAccess(action) })).filter(({ access }) => access !== "off").map(({ action, access }) => ({
352
358
  name: action.toolName || toolNameForAction(action),
353
359
  title: action.title,
354
360
  description: action.description,
355
361
  requiredScopes: action.requiredPermission ? [action.requiredPermission] : [],
356
- destructive: action.destructive || action.requiresConfirmation || action.method !== "GET",
357
- inputSchema: actionInputSchema(action),
362
+ destructive: access === "confirm",
363
+ inputSchema: actionInputSchema(action, access),
358
364
  handler: async (topolo, args) => {
359
365
  const confirm = Boolean(args["confirm"]);
360
366
  const input = { ...args };
@@ -368,7 +374,7 @@ function toolNameForAction(action) {
368
374
  const name = action.name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
369
375
  return ["topolo", service, name].filter(Boolean).join("_").slice(0, 96);
370
376
  }
371
- function actionInputSchema(action) {
377
+ function actionInputSchema(action, access = "auto") {
372
378
  const schema = {
373
379
  type: "object",
374
380
  properties: {},
@@ -376,7 +382,7 @@ function actionInputSchema(action) {
376
382
  ...action.inputSchema
377
383
  };
378
384
  const properties = schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties) ? { ...schema.properties } : {};
379
- if (action.requiresConfirmation || action.method !== "GET") {
385
+ if (access === "confirm") {
380
386
  properties.confirm = {
381
387
  type: "boolean",
382
388
  description: "Required for mutating actions. Explicit human-in-the-loop acknowledgement."
package/dist/http.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
2
+ import { TopoloCredential, Topolo } from '@topolo/sdk';
3
+
4
+ /**
5
+ * Hosted, multi-tenant MCP Streamable HTTP transport for the Topolo platform.
6
+ *
7
+ * stdio (index.ts) binds ONE env credential to the whole process. The hosted
8
+ * HTTP transport is the opposite: every request carries its OWN caller
9
+ * credential (Authorization: Bearer <jwt> or X-Api-Key: <key>). We build a
10
+ * per-request Topolo SDK client from THAT credential, introspect it to resolve
11
+ * the caller's scopes, and wire a per-session MCP server via buildServer — the
12
+ * exact same scope-gated tool list + dispatch path used by stdio. There is no
13
+ * process-wide credential fallback; a request with no/invalid credential is
14
+ * rejected with 401.
15
+ *
16
+ * Protocol: implemented by the SDK's WebStandardStreamableHTTPServerTransport,
17
+ * which speaks the MCP Streamable HTTP spec (POST JSON-RPC -> JSON or SSE,
18
+ * Mcp-Session-Id continuity, GET SSE stream, DELETE to terminate) over pure
19
+ * Web-Standard Request/Response — so it runs unchanged in a Cloudflare Worker.
20
+ */
21
+ interface TopoloMcpHttpOptions {
22
+ /** Path the MCP endpoint is mounted at. Defaults to '/mcp'. */
23
+ path?: string;
24
+ /** Per-service URL overrides (e.g. staging). Passed through to createTopolo. */
25
+ serviceUrls?: Record<string, string>;
26
+ /** Per-service TopoloAuth service-id overrides. Passed through to createTopolo. */
27
+ serviceIds?: Record<string, string>;
28
+ /** Human-readable agent label for audit logs (forwarded to the SDK). */
29
+ agentName?: string;
30
+ /**
31
+ * Injected fetch passed to the default createTopolo factory. Defaults to the
32
+ * runtime global fetch. Useful in a Worker (to forward `env`-bound fetch) and
33
+ * in tests. Ignored when `createClient` is supplied.
34
+ */
35
+ fetch?: typeof fetch;
36
+ /**
37
+ * Build the per-request SDK client. Injectable so tests can supply a fake
38
+ * Topolo without real network I/O. Defaults to createTopolo. The credential
39
+ * is always the per-request caller credential — never an env credential.
40
+ */
41
+ createClient?: (credential: TopoloCredential) => Topolo;
42
+ }
43
+ interface McpSession {
44
+ transport: WebStandardStreamableHTTPServerTransport;
45
+ /** Fingerprint of the credential that created this session. */
46
+ fingerprint: string;
47
+ }
48
+ /**
49
+ * Create a Cloudflare-Worker-compatible fetch handler that serves the Topolo
50
+ * MCP over Streamable HTTP. Mount it as the Worker's default export, e.g.:
51
+ *
52
+ * const handler = createTopoloMcpHttpHandler();
53
+ * export default { fetch: handler.fetch };
54
+ */
55
+ declare function createTopoloMcpHttpHandler(options?: TopoloMcpHttpOptions): {
56
+ /** Cloudflare Worker / fetch-compatible entrypoint. */
57
+ fetch: (request: Request) => Promise<Response>;
58
+ /** Exposed for tests/introspection. */
59
+ sessions: Map<string, McpSession>;
60
+ };
61
+ type TopoloMcpHttpHandler = ReturnType<typeof createTopoloMcpHttpHandler>;
62
+
63
+ export { type TopoloMcpHttpHandler, type TopoloMcpHttpOptions, createTopoloMcpHttpHandler };