@topolo/mcp 0.7.2 → 0.9.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.
package/README.md CHANGED
@@ -79,15 +79,43 @@ ready-made agent guide that covers both the CLI and this MCP.
79
79
  | ---------------------------- | ----------------------------------------------------- |
80
80
  | `TOPOLO_API_KEY` | Platform API key (preferred) |
81
81
  | `TOPOLO_ACCESS_TOKEN` | Short-lived JWT (dev/testing) |
82
+ | `TOPOLO_ENV` | Credential environment (production by default) |
83
+ | `TOPOLO_CONTEXT` | Pin one stored personal/org context without activating it |
82
84
  | `TOPOLO_AGENT_NAME` | Human-readable agent label for audit logs |
83
85
  | `TOPOLO_SERVICE_URL_<ID>` | Override a service base URL (e.g. `_AUTH`, `_MAIL`) |
84
86
 
85
87
  Exactly one credential var must be set. If both are present, `TOPOLO_API_KEY`
86
88
  wins.
87
89
 
90
+ For concurrent organization or environment operation, run one named MCP server
91
+ per stored context. `TOPOLO_CONTEXT` accepts an exact key from `topolo context
92
+ list --json` (for example `personal` or `org:org_topolo`) or a unique stored
93
+ organization id, slug, or name. The server reads and refreshes that credential
94
+ without changing the CLI's active context, so separate agent processes cannot
95
+ race one shared context pointer:
96
+
97
+ ```json
98
+ {
99
+ "mcpServers": {
100
+ "topolo-topolo": {
101
+ "command": "topolo-mcp",
102
+ "env": { "TOPOLO_ENV": "production", "TOPOLO_CONTEXT": "org:org_topolo" }
103
+ },
104
+ "topolo-personal": {
105
+ "command": "topolo-mcp",
106
+ "env": { "TOPOLO_ENV": "production", "TOPOLO_CONTEXT": "personal" }
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ Do not combine `TOPOLO_CONTEXT` with an injected API key or access token: those
113
+ credentials already carry their own immutable context.
114
+
88
115
  ## Startup sequence
89
116
 
90
- 1. Resolve the credential from env. **Refuse to start** if none is set.
117
+ 1. Resolve an injected credential, a pinned stored context, or the environment's
118
+ active stored credential. **Refuse to start** if none is available.
91
119
  2. Call `GET /api/me` to load the user's granted scopes + role.
92
120
  3. Connect the stdio transport and begin serving MCP requests.
93
121
  4. Advertise a fixed, bounded discovery/invocation surface. Agents search one
package/dist/dispatch.js CHANGED
@@ -260,6 +260,148 @@ var TOOLS = [
260
260
  );
261
261
  }
262
262
  },
263
+ {
264
+ name: "topolo_validate_action",
265
+ title: "Validate Topolo action input",
266
+ description: "Validates input locally against the exact JSON Schema published for one credential-scoped action. Does not invoke the action.",
267
+ requiredScopes: [],
268
+ destructive: false,
269
+ inputSchema: {
270
+ type: "object",
271
+ properties: {
272
+ actionId: { type: "string", minLength: 1 },
273
+ input: { type: "object", additionalProperties: true }
274
+ },
275
+ required: ["actionId"],
276
+ additionalProperties: false
277
+ },
278
+ handler: async (topolo, args) => {
279
+ const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
280
+ if (!actionId) throw new Error("actionId is required.");
281
+ const input = args["input"];
282
+ if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
283
+ throw new Error("input must be an object.");
284
+ }
285
+ return topolo.client.validateActionInput(actionId, input ?? {});
286
+ }
287
+ },
288
+ {
289
+ name: "topolo_plan_action",
290
+ title: "Plan one Topolo action",
291
+ description: "Validates one credential-scoped action and returns its exact request, confirmation state, verification steps, rollback action, and next actions without executing it.",
292
+ requiredScopes: [],
293
+ destructive: false,
294
+ inputSchema: {
295
+ type: "object",
296
+ properties: {
297
+ actionId: { type: "string", minLength: 1 },
298
+ input: { type: "object", additionalProperties: true },
299
+ resourceType: { type: "string", description: "Optional app-local resource alias, such as workspace." },
300
+ resourceId: { type: "string", description: "Resource id. Required when resourceType is provided." },
301
+ confirm: { type: "boolean", description: "Include confirmation in the plan without executing the action." }
302
+ },
303
+ required: ["actionId"],
304
+ additionalProperties: false
305
+ },
306
+ handler: async (topolo, args) => {
307
+ const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
308
+ if (!actionId) throw new Error("actionId is required.");
309
+ const input = args["input"];
310
+ if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
311
+ throw new Error("input must be an object.");
312
+ }
313
+ const resource = optionalResourceContext(args);
314
+ return topolo.client.planAction(
315
+ actionId,
316
+ input ?? {},
317
+ {
318
+ confirm: args["confirm"] === true,
319
+ ...resource ? { resource } : {}
320
+ }
321
+ );
322
+ }
323
+ },
324
+ {
325
+ name: "topolo_get_action_examples",
326
+ title: "Get Topolo action examples and documentation",
327
+ description: "Returns the public documentation URL and inline examples published for one action.",
328
+ requiredScopes: [],
329
+ destructive: false,
330
+ inputSchema: {
331
+ type: "object",
332
+ properties: { actionId: { type: "string", minLength: 1 } },
333
+ required: ["actionId"],
334
+ additionalProperties: false
335
+ },
336
+ handler: async (topolo, args) => {
337
+ const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
338
+ if (!actionId) throw new Error("actionId is required.");
339
+ const action = await topolo.client.getAction(actionId);
340
+ return {
341
+ actionId: action.actionId,
342
+ docsUrl: action.agentContract?.docsUrl ?? null,
343
+ examples: action.agentContract?.examples ?? []
344
+ };
345
+ }
346
+ },
347
+ {
348
+ name: "topolo_discover_capabilities",
349
+ title: "Discover one Topolo application capability contract",
350
+ description: "Discovers capability and schema actions published by one application. Use the actionService value returned by topolo_search_applications as service.",
351
+ requiredScopes: [],
352
+ destructive: false,
353
+ inputSchema: {
354
+ type: "object",
355
+ properties: { service: { type: "string", minLength: 1 } },
356
+ required: ["service"],
357
+ additionalProperties: false
358
+ },
359
+ handler: async (topolo, args) => {
360
+ const service = typeof args["service"] === "string" ? args["service"].trim() : "";
361
+ if (!service) throw new Error("service is required.");
362
+ return topolo.client.searchActions({ service, query: "capabilities", limit: 100 });
363
+ }
364
+ },
365
+ {
366
+ name: "topolo_get_resource_types",
367
+ title: "Get application resource-scoping types",
368
+ description: "Returns the canonical resource-scoping metadata published for one application, including aliases accepted by resource-scoped API keys and action calls.",
369
+ requiredScopes: [],
370
+ destructive: false,
371
+ inputSchema: {
372
+ type: "object",
373
+ properties: { application: { type: "string", minLength: 1 } },
374
+ required: ["application"],
375
+ additionalProperties: false
376
+ },
377
+ handler: async (topolo, args) => {
378
+ const application = typeof args["application"] === "string" ? args["application"].trim() : "";
379
+ if (!application) throw new Error("application is required.");
380
+ const entry = await topolo.client.getService(application);
381
+ return {
382
+ application: { appId: entry.appId, slug: entry.slug, name: entry.name },
383
+ resourceTypes: entry.apiKeyResources
384
+ };
385
+ }
386
+ },
387
+ {
388
+ name: "topolo_list_resources",
389
+ title: "List bindable Topolo resource ids",
390
+ description: "Returns credential-scoped resource ids for one application. OAuth credentials use the published Auth resource catalog; API keys return only their own bound resources.",
391
+ requiredScopes: [],
392
+ destructive: false,
393
+ inputSchema: {
394
+ type: "object",
395
+ properties: { application: { type: "string", minLength: 1 } },
396
+ required: ["application"],
397
+ additionalProperties: false
398
+ },
399
+ handler: async (topolo, args) => {
400
+ const application = typeof args["application"] === "string" ? args["application"].trim() : "";
401
+ if (!application) throw new Error("application is required.");
402
+ return topolo.client.listBindableResources(application);
403
+ }
404
+ },
263
405
  {
264
406
  name: "topolo_list_application_requirements",
265
407
  title: "List Topolo application build requirements",