@topolo/mcp 0.1.2 → 0.2.1

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.
Files changed (2) hide show
  1. package/dist/index.js +115 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -58,17 +58,38 @@ var TopoloHttpError = class extends TopoloSdkError {
58
58
  this.path = path;
59
59
  }
60
60
  };
61
- var DEFAULT_SERVICE_URLS = {
61
+ var SERVICES = {
62
+ agent: "https://topolo-agent-worker.topolo.workers.dev",
62
63
  auth: "https://auth.topolo.app",
63
- crm: "https://topolo-crm-worker.topolo.workers.dev"
64
+ bugfix: "https://bugfix.topolo.app",
65
+ bytes: "https://r2-media-manager-worker.topolo.workers.dev",
66
+ chat: "https://chat.topolo.app",
67
+ commerce: "https://topolo-commerce-api.topolo.workers.dev",
68
+ crm: "https://crm-api.topolo.app",
69
+ forecast: "https://forecast-worker-production.topolo.workers.dev",
70
+ learn: "https://topololearn-api.topolo.workers.dev",
71
+ mdm: "https://nodo-mdm-api.topolo.app",
72
+ messaging: "https://messages.topolo.app",
73
+ nexus: "https://topolo-nexus-gateway.topolo.workers.dev",
74
+ one: "https://topoloone-worker.topolo.workers.dev",
75
+ pay: "https://topolo-pay.topolo.workers.dev",
76
+ "quro.api": "https://api.ol0.me",
77
+ "quro.redirect": "https://ol0.me",
78
+ roadmapper: "https://roadmapper-api.topolo.app",
79
+ social_studio: "https://topolo-social-studio-api.topolo.workers.dev",
80
+ socialize: "https://socialize-prod-api.topolo.workers.dev",
81
+ support: "https://support.topolo.app",
82
+ "web.api": "https://web-api.topolo.app",
83
+ "web.runtime": "https://sites.topolo.app"
64
84
  };
65
85
  var PLATFORM_SERVICE_IDS = {
66
86
  crm: "srv_iCwM4jGXcwlj"
67
87
  };
88
+ var DEFAULT_SERVICE_URLS = SERVICES;
68
89
  function resolveServiceUrl(service, overrides) {
69
90
  const override = overrides?.[service];
70
91
  if (override) return override;
71
- const envKey = `TOPOLO_SERVICE_URL_${service.toUpperCase()}`;
92
+ const envKey = `TOPOLO_SERVICE_URL_${service.replace(/\./g, "_").toUpperCase()}`;
72
93
  const envValue = typeof process !== "undefined" ? process.env?.[envKey] : void 0;
73
94
  if (envValue) return envValue;
74
95
  return DEFAULT_SERVICE_URLS[service];
@@ -390,6 +411,8 @@ function splitService(servicePart) {
390
411
  }
391
412
 
392
413
  // src/tools.ts
414
+ var SERVICE_IDS = Object.keys(DEFAULT_SERVICE_URLS).sort();
415
+ var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
393
416
  var TOOLS = [
394
417
  {
395
418
  name: "topolo_whoami",
@@ -404,6 +427,26 @@ var TOOLS = [
404
427
  },
405
428
  handler: async (topolo) => topolo.identity.whoami()
406
429
  },
430
+ {
431
+ name: "topolo_list_services",
432
+ title: "List every Topolo service the agent can reach",
433
+ description: "Returns the service registry the SDK knows about: one entry per platform service with its stable service ID and current base URL. Use this to discover what `topolo_api_call` can address \u2014 the service IDs returned here are the exact values accepted by that tool. Pure metadata: no credential introspection, no permission check, no external call.",
434
+ requiredScopes: [],
435
+ destructive: false,
436
+ inputSchema: {
437
+ type: "object",
438
+ properties: {},
439
+ additionalProperties: false
440
+ },
441
+ handler: async () => {
442
+ const services = Object.keys(DEFAULT_SERVICE_URLS).sort().map((id) => ({
443
+ id,
444
+ defaultUrl: DEFAULT_SERVICE_URLS[id],
445
+ effectiveUrl: resolveServiceUrl(id)
446
+ }));
447
+ return { services };
448
+ }
449
+ },
407
450
  {
408
451
  name: "topolo_crm_list_contacts",
409
452
  title: "List CRM contacts",
@@ -427,6 +470,75 @@ var TOOLS = [
427
470
  });
428
471
  }
429
472
  },
473
+ {
474
+ name: "topolo_api_call",
475
+ title: "Call any Topolo platform service",
476
+ description: `Low-level passthrough to any Topolo service. Use when no domain-specific tool exists for the operation you need. The request is signed with the current credential and scoped to the caller's organization \u2014 the target service will return 403 if your scopes do not permit the operation, even though this tool is advertised. Mutating calls (non-GET methods) require confirm:true. Known services: ${SERVICE_IDS.join(", ")}.`,
477
+ requiredScopes: [],
478
+ destructive: true,
479
+ inputSchema: {
480
+ type: "object",
481
+ properties: {
482
+ service: {
483
+ type: "string",
484
+ enum: SERVICE_IDS,
485
+ description: "Target service ID from the platform registry."
486
+ },
487
+ method: {
488
+ type: "string",
489
+ enum: HTTP_METHODS,
490
+ default: "GET",
491
+ description: "HTTP method. Defaults to GET."
492
+ },
493
+ path: {
494
+ type: "string",
495
+ minLength: 1,
496
+ description: 'Request path including any leading slash, e.g. "/api/contacts".'
497
+ },
498
+ body: {
499
+ description: "JSON request body. Ignored for GET."
500
+ },
501
+ query: {
502
+ type: "object",
503
+ additionalProperties: { type: "string" },
504
+ description: "Query string parameters as string values."
505
+ },
506
+ confirm: {
507
+ type: "boolean",
508
+ description: "Required for non-GET methods. Explicit human-in-the-loop acknowledgement for writes."
509
+ }
510
+ },
511
+ required: ["service", "path"],
512
+ additionalProperties: false
513
+ },
514
+ handler: async (topolo, args) => {
515
+ const service = args["service"];
516
+ const path = args["path"];
517
+ if (typeof service !== "string" || !SERVICE_IDS.includes(service)) {
518
+ throw new Error(`Unknown service "${String(service)}". Known: ${SERVICE_IDS.join(", ")}`);
519
+ }
520
+ if (typeof path !== "string" || !path) {
521
+ throw new Error("path is required");
522
+ }
523
+ const methodRaw = args["method"] ?? "GET";
524
+ if (typeof methodRaw !== "string" || !HTTP_METHODS.includes(methodRaw)) {
525
+ throw new Error(`Unknown HTTP method "${String(methodRaw)}"`);
526
+ }
527
+ const method = methodRaw;
528
+ const query = args["query"];
529
+ const normalisedQuery = query && typeof query === "object" && !Array.isArray(query) ? Object.fromEntries(
530
+ Object.entries(query).map(([k, v]) => [k, String(v)])
531
+ ) : void 0;
532
+ return topolo.client.request({
533
+ service,
534
+ method,
535
+ path,
536
+ body: args["body"],
537
+ ...normalisedQuery ? { query: normalisedQuery } : {},
538
+ confirm: Boolean(args["confirm"])
539
+ });
540
+ }
541
+ },
430
542
  {
431
543
  name: "topolo_crm_get_contact",
432
544
  title: "Fetch a single CRM contact",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Model Context Protocol server for the Topolo platform. Exposes scope-gated tools that third-party agents (Claude, Codex, etc.) can call natively.",
5
5
  "type": "module",
6
6
  "bin": {