@topolo/mcp 0.1.1 → 0.1.2

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 +100 -47
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,12 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import {
7
- CallToolRequestSchema,
8
- ListToolsRequestSchema
9
- } from "@modelcontextprotocol/sdk/types.js";
10
5
 
11
6
  // node_modules/@topolo/sdk/dist/index.js
12
7
  function applyAuthHeaders(headers, credential) {
@@ -86,6 +81,7 @@ var TopoloClient = class {
86
81
  requireConfirmForWrites;
87
82
  timeoutMs;
88
83
  fetchImpl;
84
+ debug;
89
85
  constructor(options) {
90
86
  if (!options.credential) throw new TopoloAuthError("credential is required");
91
87
  if (!options.agent?.clientName) throw new TopoloAuthError("agent.clientName is required");
@@ -95,6 +91,14 @@ var TopoloClient = class {
95
91
  this.requireConfirmForWrites = options.requireConfirmForWrites !== false;
96
92
  this.timeoutMs = options.timeoutMs ?? 3e4;
97
93
  this.fetchImpl = options.fetch ?? fetch;
94
+ this.debug = options.debug;
95
+ }
96
+ emit(event) {
97
+ if (!this.debug) return;
98
+ try {
99
+ this.debug(event);
100
+ } catch {
101
+ }
98
102
  }
99
103
  /**
100
104
  * Low-level JSON request. Prefer the typed module helpers (identity, crm, ...)
@@ -121,27 +125,56 @@ var TopoloClient = class {
121
125
  const platformServiceId = PLATFORM_SERVICE_IDS[opts.service];
122
126
  if (platformServiceId) headers.set("X-Service-ID", platformServiceId);
123
127
  applyAuthHeaders(headers, this.credential);
124
- applyAuditHeaders(headers, this.agent, generateRequestId());
128
+ const requestId = generateRequestId();
129
+ applyAuditHeaders(headers, this.agent, requestId);
125
130
  if (opts.headers) {
126
131
  for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
127
132
  }
128
133
  const controller = new AbortController();
129
134
  const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
130
135
  const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
136
+ const urlStr = url.toString();
137
+ const startedAt = Date.now();
138
+ this.emit({ phase: "request", method, service: opts.service, path: opts.path, url: urlStr, requestId });
131
139
  let res;
132
140
  try {
133
- res = await this.fetchImpl(url.toString(), {
141
+ res = await this.fetchImpl(urlStr, {
134
142
  method,
135
143
  headers,
136
144
  body: opts.body !== void 0 ? JSON.stringify(opts.body) : null,
137
145
  signal
138
146
  });
147
+ } catch (err) {
148
+ this.emit({
149
+ phase: "error",
150
+ method,
151
+ service: opts.service,
152
+ path: opts.path,
153
+ url: urlStr,
154
+ requestId,
155
+ durationMs: Date.now() - startedAt,
156
+ error: err instanceof Error ? err.message : String(err)
157
+ });
158
+ throw err;
139
159
  } finally {
140
160
  clearTimeout(timeoutId);
141
161
  }
142
162
  const contentType = res.headers.get("Content-Type") ?? "";
143
163
  const parsed = contentType.includes("application/json") ? await res.json().catch(() => null) : await res.text().catch(() => null);
144
164
  if (!res.ok) {
165
+ const durationMs = Date.now() - startedAt;
166
+ const message = describeError(parsed, `HTTP ${res.status}`);
167
+ this.emit({
168
+ phase: "error",
169
+ method,
170
+ service: opts.service,
171
+ path: opts.path,
172
+ url: urlStr,
173
+ requestId,
174
+ durationMs,
175
+ error: message,
176
+ status: res.status
177
+ });
145
178
  if (res.status === 401) throw new TopoloAuthError(describeError(parsed, "Unauthorized"));
146
179
  if (res.status === 403) {
147
180
  throw new TopoloPermissionError(
@@ -151,6 +184,16 @@ var TopoloClient = class {
151
184
  }
152
185
  throw new TopoloHttpError(opts.service, opts.path, res.status, parsed);
153
186
  }
187
+ this.emit({
188
+ phase: "response",
189
+ method,
190
+ service: opts.service,
191
+ path: opts.path,
192
+ url: urlStr,
193
+ requestId,
194
+ status: res.status,
195
+ durationMs: Date.now() - startedAt
196
+ });
154
197
  return parsed;
155
198
  }
156
199
  /**
@@ -308,6 +351,13 @@ function resolveServiceUrlsFromEnv() {
308
351
  return Object.keys(out).length > 0 ? out : void 0;
309
352
  }
310
353
 
354
+ // src/server.ts
355
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
356
+ import {
357
+ CallToolRequestSchema,
358
+ ListToolsRequestSchema
359
+ } from "@modelcontextprotocol/sdk/types.js";
360
+
311
361
  // src/gating.ts
312
362
  function hasScope(set, required) {
313
363
  if (set.role === "super_admin") return true;
@@ -409,37 +459,12 @@ function filterToolsByScopes(tools, scopes) {
409
459
  // src/version.ts
410
460
  var MCP_VERSION = "0.1.0";
411
461
 
412
- // src/index.ts
413
- async function main() {
414
- const credential = resolveCredentialFromEnv();
415
- if (!credential) {
416
- process.stderr.write(
417
- "TopoloMCP: TOPOLO_API_KEY or TOPOLO_ACCESS_TOKEN must be set in the environment.\n"
418
- );
419
- process.exit(1);
420
- }
421
- const agentName = process.env.TOPOLO_AGENT_NAME;
422
- const serviceUrls = resolveServiceUrlsFromEnv();
423
- const topolo = createTopolo({
424
- credential,
425
- agent: {
426
- clientName: "topolo-mcp",
427
- clientVersion: MCP_VERSION,
428
- ...agentName ? { agentName } : {}
429
- },
430
- serviceUrls,
431
- requireConfirmForWrites: true
432
- });
433
- const scopes = await loadScopes(topolo);
434
- const availableTools = filterToolsByScopes(TOOLS, scopes);
462
+ // src/server.ts
463
+ function buildServer(topolo, scopes, tools = TOOLS) {
464
+ const availableTools = filterToolsByScopes(tools, scopes);
435
465
  const server = new Server(
436
- {
437
- name: "topolo",
438
- version: MCP_VERSION
439
- },
440
- {
441
- capabilities: { tools: {} }
442
- }
466
+ { name: "topolo", version: MCP_VERSION },
467
+ { capabilities: { tools: {} } }
443
468
  );
444
469
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
445
470
  tools: availableTools.map((t) => ({
@@ -469,9 +494,7 @@ async function main() {
469
494
  try {
470
495
  const result = await tool.handler(topolo, params.arguments ?? {});
471
496
  return {
472
- content: [
473
- { type: "text", text: JSON.stringify(result, null, 2) }
474
- ]
497
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
475
498
  };
476
499
  } catch (err) {
477
500
  if (err instanceof TopoloPermissionError) {
@@ -483,10 +506,46 @@ async function main() {
483
506
  return toolError(err instanceof Error ? err.message : String(err));
484
507
  }
485
508
  });
509
+ return server;
510
+ }
511
+ function advertisedToolNames(scopes, tools = TOOLS) {
512
+ return filterToolsByScopes(tools, scopes).map((t) => t.name);
513
+ }
514
+ function toolError(message) {
515
+ return {
516
+ isError: true,
517
+ content: [{ type: "text", text: message }]
518
+ };
519
+ }
520
+
521
+ // src/index.ts
522
+ async function main() {
523
+ const credential = resolveCredentialFromEnv();
524
+ if (!credential) {
525
+ process.stderr.write(
526
+ "TopoloMCP: TOPOLO_API_KEY or TOPOLO_ACCESS_TOKEN must be set in the environment.\n"
527
+ );
528
+ process.exit(1);
529
+ }
530
+ const agentName = process.env.TOPOLO_AGENT_NAME;
531
+ const serviceUrls = resolveServiceUrlsFromEnv();
532
+ const topolo = createTopolo({
533
+ credential,
534
+ agent: {
535
+ clientName: "topolo-mcp",
536
+ clientVersion: MCP_VERSION,
537
+ ...agentName ? { agentName } : {}
538
+ },
539
+ serviceUrls,
540
+ requireConfirmForWrites: true
541
+ });
542
+ const scopes = await loadScopes(topolo);
543
+ const server = buildServer(topolo, scopes);
486
544
  const transport = new StdioServerTransport();
487
545
  await server.connect(transport);
546
+ const toolNames = advertisedToolNames(scopes);
488
547
  process.stderr.write(
489
- `TopoloMCP v${MCP_VERSION} connected. Tools available: ${availableTools.map((t) => t.name).join(", ") || "(none)"}
548
+ `TopoloMCP v${MCP_VERSION} connected. Tools available: ${toolNames.join(", ") || "(none)"}
490
549
  `
491
550
  );
492
551
  }
@@ -506,12 +565,6 @@ async function loadScopes(topolo) {
506
565
  throw err;
507
566
  }
508
567
  }
509
- function toolError(message) {
510
- return {
511
- isError: true,
512
- content: [{ type: "text", text: message }]
513
- };
514
- }
515
568
  main().catch((err) => {
516
569
  process.stderr.write(`TopoloMCP fatal: ${err instanceof Error ? err.message : String(err)}
517
570
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
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": {