omnipost-mcp 0.1.0 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnipost-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "MCP server for OmniPost: lets Claude Code, Claude Desktop, Cursor and other MCP clients publish articles, image posts and videos to 40+ Chinese and overseas platforms through the OmniPost desktop app running on your machine.",
5
5
  "mcpName": "io.github.OmniGoAI/omnipost",
6
6
  "type": "module",
package/src/bridge.js CHANGED
@@ -21,6 +21,11 @@ export const DOWNLOAD_URL = 'https://omnigoai.com/en/download/omnipost/';
21
21
  // and retry it, uploading the same content twice.
22
22
  export const CALL_TIMEOUT_MS = 6 * 3600 * 1000;
23
23
 
24
+ // Number of tool calls currently in progress. The process waits for these before exiting, so a
25
+ // client that closes stdin right after sending a request still gets its answer.
26
+ let inFlightCalls = 0;
27
+ export const inFlight = () => inFlightCalls;
28
+
24
29
  // Node's built-in fetch drops a response body that stays idle for 5 minutes. The app's SSE stream
25
30
  // is silent while a long publish is running, so use a dispatcher without body/header timeouts.
26
31
  const longLivedAgent = new Agent({ bodyTimeout: 0, headersTimeout: 0, keepAliveTimeout: 60_000 });
@@ -81,7 +86,7 @@ export class AppConnection {
81
86
  async get() {
82
87
  if (this.client) return this.client;
83
88
  const endpoint = this.resolve();
84
- const client = new Client({ name: 'omnipost-mcp-bridge', version: '0.1.0' });
89
+ const client = new Client({ name: 'omnipost-mcp-bridge', version: '0.1.2' });
85
90
  client.onclose = () => { if (this.client === client) this.client = null; };
86
91
  const connecting = client.connect(this.transportFactory(endpoint));
87
92
  let timer;
@@ -112,7 +117,7 @@ function shortCause(err) {
112
117
  return String(err?.cause?.code || err?.code || err?.message || err).slice(0, 160);
113
118
  }
114
119
 
115
- export function createBridgeServer({ connection = new AppConnection(), snapshot = loadSnapshot(), version = '0.1.0' } = {}) {
120
+ export function createBridgeServer({ connection = new AppConnection(), snapshot = loadSnapshot(), version = '0.1.2' } = {}) {
116
121
  const server = new Server(
117
122
  { name: 'omnipost', version },
118
123
  {
@@ -136,6 +141,15 @@ export function createBridgeServer({ connection = new AppConnection(), snapshot
136
141
  });
137
142
 
138
143
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
144
+ inFlightCalls += 1;
145
+ try {
146
+ return await handleCall(request);
147
+ } finally {
148
+ inFlightCalls -= 1;
149
+ }
150
+ });
151
+
152
+ async function handleCall(request) {
139
153
  const { name, arguments: args } = request.params;
140
154
  let client;
141
155
  try {
@@ -162,10 +176,10 @@ export function createBridgeServer({ connection = new AppConnection(), snapshot
162
176
  isError: true,
163
177
  };
164
178
  }
165
- });
179
+ }
166
180
 
167
181
  // When the AI client goes away, drop the link to the app too, so nothing is left open.
168
- server.onclose = () => { connection.reset(); };
182
+ server.onclose = () => { if (inFlightCalls === 0) connection.reset(); };
169
183
 
170
184
  return server;
171
185
  }
package/src/index.js CHANGED
@@ -3,12 +3,16 @@
3
3
  // Claude Code: claude mcp add omnipost -- npx -y omnipost-mcp
4
4
  // Other clients: { "command": "npx", "args": ["-y", "omnipost-mcp"] }
5
5
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6
- import { createBridgeServer, resolveEndpoint, shutdown } from './bridge.js';
6
+ import { createBridgeServer, resolveEndpoint, shutdown, inFlight } from './bridge.js';
7
7
 
8
8
  let exiting = false;
9
- async function exit(code = 0) {
9
+ async function exit(code = 0, { immediate = false } = {}) {
10
10
  if (exiting) return;
11
11
  exiting = true;
12
+ // Let calls that are already running finish and send their answers first. The short pause lets a
13
+ // request that arrived just before stdin closed get registered as in flight.
14
+ if (!immediate) await new Promise((r) => setTimeout(r, 300));
15
+ while (!immediate && inFlight() > 0) await new Promise((r) => setTimeout(r, 200));
12
16
  await shutdown();
13
17
  process.exit(code);
14
18
  }
@@ -22,12 +26,12 @@ async function main() {
22
26
  // The client closing our stdin means it is done with us.
23
27
  process.stdin.on('end', () => exit(0));
24
28
  process.stdin.on('close', () => exit(0));
25
- process.on('SIGINT', () => exit(0));
26
- process.on('SIGTERM', () => exit(0));
29
+ process.on('SIGINT', () => exit(0, { immediate: true }));
30
+ process.on('SIGTERM', () => exit(0, { immediate: true }));
27
31
  process.stderr.write(`[omnipost-mcp] ready, forwarding to ${resolveEndpoint()}\n`);
28
32
  }
29
33
 
30
34
  main().catch((err) => {
31
35
  process.stderr.write(`[omnipost-mcp] failed to start: ${err?.stack || err}\n`);
32
- exit(1);
36
+ exit(1, { immediate: true });
33
37
  });