telique-mcp 1.0.0 → 1.0.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.
package/dist/index.js CHANGED
@@ -1,14 +1,19 @@
1
1
  #!/usr/bin/env node
2
- import { loadConfig } from "./config.js";
2
+ import { stdin } from "node:process";
3
3
  const subcommand = process.argv[2];
4
- if (subcommand === "setup") {
4
+ // If run with "setup" arg, or directly in a terminal (not piped by an MCP client),
5
+ // launch the interactive setup flow.
6
+ const isInteractiveTerminal = subcommand !== "serve" && stdin.isTTY === true;
7
+ if (subcommand === "setup" || isInteractiveTerminal) {
5
8
  const { runSetup } = await import("./setup.js");
6
9
  await runSetup();
7
10
  }
8
11
  else {
12
+ // MCP server mode — stdin is piped JSON-RPC from the MCP client
9
13
  const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
10
14
  const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
11
15
  const { TeliqueClient } = await import("./client.js");
16
+ const { loadConfig } = await import("./config.js");
12
17
  const { setAnonymousMode } = await import("./utils/formatting.js");
13
18
  const { registerRoutelinkTools } = await import("./tools/routelink.js");
14
19
  const { registerLrnTools } = await import("./tools/lrn.js");
package/dist/setup.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createInterface } from "node:readline/promises";
2
2
  import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
3
+ import { execSync, execFileSync } from "node:child_process";
3
4
  import { stdin, stdout } from "node:process";
4
5
  import { CONFIG_DIR, CONFIG_FILE } from "./config.js";
5
6
  const REGISTER_URL = "https://telique.ringer.tel/register";
@@ -13,8 +14,8 @@ export async function runSetup() {
13
14
  console.log(` Found existing API key: ${maskToken(existing)}`);
14
15
  const keep = await rl.question(" Keep this key? (Y/n): ");
15
16
  if (keep.toLowerCase() !== "n") {
16
- console.log("\n ✓ Keeping existing configuration.\n");
17
- printMcpConfig(existing);
17
+ console.log("\n ✓ Keeping existing configuration.");
18
+ await registerWithClients(rl, existing);
18
19
  rl.close();
19
20
  return;
20
21
  }
@@ -44,26 +45,154 @@ export async function runSetup() {
44
45
  }
45
46
  saveToken(trimmed);
46
47
  console.log(` ✓ Token validated`);
47
- console.log(` ✓ Saved to ${CONFIG_FILE}\n`);
48
- printMcpConfig(trimmed);
48
+ console.log(` ✓ Saved to ${CONFIG_FILE}`);
49
+ await registerWithClients(rl, trimmed);
49
50
  break;
50
51
  }
51
52
  case "2": {
52
53
  console.log(`\n Opening ${REGISTER_URL} ...\n`);
53
54
  await openBrowser(REGISTER_URL);
54
- console.log(" After creating your account, run this command again with your API key.\n");
55
+ console.log(" After creating your account, run `telique-mcp setup` again.\n");
55
56
  break;
56
57
  }
57
58
  case "3":
58
59
  default: {
59
60
  console.log("\n ✓ Skipped. Running in anonymous mode (10 ops/min).");
60
- console.log(` Get an API key anytime at ${REGISTER_URL}\n`);
61
- printMcpConfig(null);
61
+ console.log(` Get an API key anytime at ${REGISTER_URL}`);
62
+ await registerWithClients(rl, null);
62
63
  break;
63
64
  }
64
65
  }
65
66
  rl.close();
66
67
  }
68
+ async function registerWithClients(rl, token) {
69
+ const clients = detectMcpClients();
70
+ if (clients.length === 0) {
71
+ console.log("\n No supported MCP clients detected.\n");
72
+ printManualConfig(token);
73
+ return;
74
+ }
75
+ console.log("\n Detected MCP clients:\n");
76
+ clients.forEach((c, i) => console.log(` [${i + 1}] ${c.name}`));
77
+ console.log(` [A] All of the above`);
78
+ console.log(` [S] Skip — I'll configure manually`);
79
+ console.log();
80
+ const answer = await rl.question(" Register with which client? > ");
81
+ const trimmed = answer.trim().toUpperCase();
82
+ if (trimmed === "S") {
83
+ printManualConfig(token);
84
+ return;
85
+ }
86
+ const selected = trimmed === "A"
87
+ ? clients
88
+ : clients.filter((_, i) => trimmed === String(i + 1));
89
+ if (selected.length === 0) {
90
+ printManualConfig(token);
91
+ return;
92
+ }
93
+ for (const client of selected) {
94
+ const success = client.register(token);
95
+ if (success) {
96
+ console.log(` ✓ Registered with ${client.name}`);
97
+ }
98
+ else {
99
+ console.log(` ✗ Failed to register with ${client.name}`);
100
+ }
101
+ }
102
+ console.log("\n Done! Restart your MCP client to load the Telique tools.\n");
103
+ }
104
+ function detectMcpClients() {
105
+ const clients = [];
106
+ // Claude Code
107
+ if (commandExists("claude")) {
108
+ clients.push({
109
+ name: "Claude Code",
110
+ register: (token) => registerClaudeCode(token),
111
+ });
112
+ }
113
+ // Claude Desktop
114
+ const claudeDesktopConfig = getClaudeDesktopConfigPath();
115
+ if (claudeDesktopConfig && existsSync(claudeDesktopConfig)) {
116
+ clients.push({
117
+ name: "Claude Desktop",
118
+ register: (token) => registerJsonConfig(claudeDesktopConfig, token),
119
+ });
120
+ }
121
+ return clients;
122
+ }
123
+ function registerClaudeCode(token) {
124
+ try {
125
+ // Remove existing entry first (ignore errors if not found)
126
+ try {
127
+ execSync("claude mcp remove -s user telique 2>/dev/null", {
128
+ stdio: "ignore",
129
+ });
130
+ }
131
+ catch {
132
+ // ignore
133
+ }
134
+ const args = ["mcp", "add", "-s", "user", "telique"];
135
+ if (token) {
136
+ args.push("-e", `TELIQUE_API_TOKEN=${token}`);
137
+ }
138
+ args.push("--", "npx", "-y", "telique-mcp");
139
+ execFileSync("claude", args, { stdio: "ignore" });
140
+ return true;
141
+ }
142
+ catch {
143
+ return false;
144
+ }
145
+ }
146
+ function registerJsonConfig(configPath, token) {
147
+ try {
148
+ let config = {};
149
+ try {
150
+ config = JSON.parse(readFileSync(configPath, "utf-8"));
151
+ }
152
+ catch {
153
+ // start fresh
154
+ }
155
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
156
+ config.mcpServers = {};
157
+ }
158
+ const servers = config.mcpServers;
159
+ const entry = {
160
+ command: "npx",
161
+ args: ["-y", "telique-mcp"],
162
+ };
163
+ if (token) {
164
+ entry.env = { TELIQUE_API_TOKEN: token };
165
+ }
166
+ servers.telique = entry;
167
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
168
+ return true;
169
+ }
170
+ catch {
171
+ return false;
172
+ }
173
+ }
174
+ function getClaudeDesktopConfigPath() {
175
+ const home = process.env.HOME || process.env.USERPROFILE || "";
176
+ switch (process.platform) {
177
+ case "darwin":
178
+ return `${home}/Library/Application Support/Claude/claude_desktop_config.json`;
179
+ case "win32":
180
+ return `${process.env.APPDATA}/Claude/claude_desktop_config.json`;
181
+ case "linux":
182
+ return `${home}/.config/Claude/claude_desktop_config.json`;
183
+ default:
184
+ return null;
185
+ }
186
+ }
187
+ function commandExists(cmd) {
188
+ try {
189
+ execSync(`which ${cmd} 2>/dev/null`, { stdio: "ignore" });
190
+ return true;
191
+ }
192
+ catch {
193
+ return false;
194
+ }
195
+ }
67
196
  function loadExistingToken() {
68
197
  try {
69
198
  const raw = readFileSync(CONFIG_FILE, "utf-8");
@@ -76,11 +205,11 @@ function loadExistingToken() {
76
205
  }
77
206
  function saveToken(token) {
78
207
  mkdirSync(CONFIG_DIR, { recursive: true });
79
- const config = existsSync(CONFIG_FILE)
208
+ const existing = existsSync(CONFIG_FILE)
80
209
  ? JSON.parse(readFileSync(CONFIG_FILE, "utf-8"))
81
210
  : {};
82
- config.apiToken = token;
83
- writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
211
+ existing.apiToken = token;
212
+ writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2) + "\n");
84
213
  }
85
214
  async function validateToken(token) {
86
215
  try {
@@ -108,7 +237,7 @@ async function openBrowser(url) {
108
237
  : "xdg-open";
109
238
  exec(`${cmd} ${url}`);
110
239
  }
111
- function printMcpConfig(token) {
240
+ function printManualConfig(token) {
112
241
  const env = {};
113
242
  if (token) {
114
243
  env.TELIQUE_API_TOKEN = token;
@@ -122,7 +251,7 @@ function printMcpConfig(token) {
122
251
  },
123
252
  },
124
253
  };
125
- console.log(" Add this to your MCP client configuration:\n");
254
+ console.log("\n Add this to your MCP client configuration:\n");
126
255
  console.log(JSON.stringify(config, null, 2)
127
256
  .split("\n")
128
257
  .map((line) => " " + line)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telique-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "MCP server for Telique telecom APIs (RouteLink, LRN, CNAM, LERG)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,12 +8,14 @@
8
8
  "telique-mcp": "dist/index.js"
9
9
  },
10
10
  "files": [
11
- "dist"
11
+ "dist",
12
+ "postinstall.js"
12
13
  ],
13
14
  "scripts": {
14
15
  "build": "tsc",
15
16
  "start": "node dist/index.js",
16
17
  "dev": "tsx src/index.ts",
18
+ "postinstall": "node postinstall.js",
17
19
  "prepublishOnly": "npm run build"
18
20
  },
19
21
  "keywords": [
package/postinstall.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ console.log(`
3
+ ┌─────────────────────────────────────────────────┐
4
+ │ │
5
+ │ telique-mcp installed successfully! │
6
+ │ │
7
+ │ Get started: │
8
+ │ telique-mcp setup │
9
+ │ │
10
+ │ Or use immediately in anonymous mode │
11
+ │ (10 ops/min) — no API key required. │
12
+ │ │
13
+ │ Docs: https://github.com/Ringer/telique-mcp │
14
+ │ │
15
+ └─────────────────────────────────────────────────┘
16
+ `);