remnote-mcp-server 0.14.0 → 0.14.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/CHANGELOG.md +36 -0
- package/README.md +45 -21
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +16 -0
- package/dist/cli.js.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/remnote-cli/client/mcp-server-client.js +1 -1
- package/dist/websocket-server.d.ts +5 -0
- package/dist/websocket-server.js +64 -10
- package/dist/websocket-server.js.map +1 -1
- package/mcpb/remnote-local/.mcpbignore +5 -0
- package/mcpb/remnote-local/README.md +26 -0
- package/mcpb/remnote-local/manifest.json +87 -0
- package/mcpb/remnote-local/package.json +11 -0
- package/mcpb/remnote-local/remnote-local.mcpb +0 -0
- package/mcpb/remnote-local/server/index.js +300 -0
- package/package.json +12 -8
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,42 @@ Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.14.2] - 2026-05-08
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Reject RemNote bridge WebSocket connections that do not send a compatible bridge `hello.version`, with a clearer
|
|
15
|
+
disconnect reason and server log message pointing users to `MCP/OpenClaw Automation Bridge`.
|
|
16
|
+
- Added prominent README troubleshooting guidance for wrong or incompatible RemNote Marketplace plugin installs,
|
|
17
|
+
including the related `quentintou/remnote-mcp-bridge#8` report.
|
|
18
|
+
- Updated Claude Desktop / Cowork setup docs to clarify that local MCPB works for Cowork in the Claude Desktop app
|
|
19
|
+
when desktop extensions are enabled, while remote connectors remain required for web/mobile, cloud-hosted clients,
|
|
20
|
+
and managed deployments without local MCPB.
|
|
21
|
+
|
|
22
|
+
## [0.14.1] - 2026-05-08
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Added a `remnote-local` MCPB package for Claude Desktop that proxies stdio MCP calls to a locally running
|
|
27
|
+
`remnote-mcp-server` Streamable HTTP endpoint without public HTTPS, including setup docs, screenshots, and official
|
|
28
|
+
MCPB references.
|
|
29
|
+
- Added `remnote-mcp-server mcpb-path` to print the bundled Claude Desktop extension path after npm installation.
|
|
30
|
+
- Added the `remnote-mcp-stdio` executable for local MCP clients that consume stdio servers, including help/version
|
|
31
|
+
output and smoke-test commands in the configuration guide.
|
|
32
|
+
- Added Codex TUI and Codex.app configuration documentation covering Streamable HTTP MCP, `remnote-mcp-stdio`, and
|
|
33
|
+
`remnote-cli` skill setup.
|
|
34
|
+
- Added MCP protocol compatibility guidance clarifying that `2025-11-25` initialize requests are supported and separate
|
|
35
|
+
from bridge/server package versions.
|
|
36
|
+
- Added clearer stdio proxy prerequisites and cross-links between the generic stdio MCP client section and the Codex
|
|
37
|
+
stdio setup example.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- Reordered the README AI client setup list to include Codex and prioritize the local Claude Desktop MCPB path before
|
|
42
|
+
remote connector setup.
|
|
43
|
+
- Updated documentation and agent repo maps to reflect that the old standalone `remnote-cli` repository is
|
|
44
|
+
discontinued and the maintained CLI lives in this package.
|
|
45
|
+
|
|
10
46
|
## [0.14.0] - 2026-05-07
|
|
11
47
|
|
|
12
48
|
### Added
|
package/README.md
CHANGED
|
@@ -11,13 +11,20 @@ plugin](https://github.com/robert7/remnote-mcp-bridge).
|
|
|
11
11
|
|
|
12
12
|
> If you run into any issues, please [report them here](https://github.com/robert7/remnote-mcp-server/issues).
|
|
13
13
|
|
|
14
|
+
> **Connection issue? Check the RemNote plugin first.** The RemNote Marketplace may show multiple similarly named
|
|
15
|
+
> `MCP Bridge` plugins. For this server, install the official **MCP/OpenClaw Automation Bridge** plugin by Robert
|
|
16
|
+
> Spiegel and keep it on the same `0.x` minor version line as `remnote-mcp-server` (for example `0.14.x` with
|
|
17
|
+
> `0.14.x`). Wrong plugin flavors or incompatible marketplace copies can connect briefly and then disconnect with a
|
|
18
|
+
> `1008` compatibility message. See the related upstream marketplace confusion report:
|
|
19
|
+
> [quentintou/remnote-mcp-bridge#8](https://github.com/quentintou/remnote-mcp-bridge/issues/8).
|
|
20
|
+
|
|
14
21
|
## What is This?
|
|
15
22
|
|
|
16
23
|
The RemNote MCP Server enables AI assistants like Claude Code to interact directly with your RemNote knowledge base
|
|
17
24
|
through the Model Context Protocol (MCP). The same npm package also provides `remnote-cli`, a command-line MCP client
|
|
18
|
-
for local scripts and coding harnesses
|
|
19
|
-
|
|
20
|
-
commands.
|
|
25
|
+
for local scripts and coding harnesses, and `remnote-mcp-stdio`, a stdio MCP proxy for clients that cannot consume
|
|
26
|
+
Streamable HTTP directly. Create notes, hierarchical markdown trees, and RemNote-native flashcards; search and read
|
|
27
|
+
your knowledge base; update existing notes; and maintain your daily journal through MCP tools or shell commands.
|
|
21
28
|
|
|
22
29
|
## Demo
|
|
23
30
|
|
|
@@ -29,10 +36,11 @@ This system consists of **two separate runtime components** that work together:
|
|
|
29
36
|
|
|
30
37
|
1. **[RemNote Automation Bridge](https://github.com/robert7/remnote-mcp-bridge)** - A RemNote plugin that runs in your
|
|
31
38
|
browser or RemNote desktop app and exposes RemNote API functionality via WebSocket
|
|
32
|
-
2. **RemNote MCP Server** (this project) - A standalone server package that provides
|
|
33
|
-
|
|
39
|
+
2. **RemNote MCP Server** (this project) - A standalone server package that provides `remnote-mcp-server` for MCP
|
|
40
|
+
HTTP clients, `remnote-mcp-stdio` for stdio MCP clients, and `remnote-cli` for command-line workflows
|
|
34
41
|
|
|
35
|
-
The `remnote-cli`
|
|
42
|
+
The `remnote-cli` and `remnote-mcp-stdio` commands are not second RemNote-facing servers. They call the MCP endpoint
|
|
43
|
+
exposed by `remnote-mcp-server`.
|
|
36
44
|
|
|
37
45
|
For the detailed bridge connection lifecycle, retry phases, and wake-up triggers, use the bridge repo as the source of
|
|
38
46
|
truth: [Connection Lifecycle Guide](https://github.com/robert7/remnote-mcp-bridge/blob/main/docs/guides/connection-lifecycle.md).
|
|
@@ -41,12 +49,14 @@ truth: [Connection Lifecycle Guide](https://github.com/robert7/remnote-mcp-bridg
|
|
|
41
49
|
|
|
42
50
|
```text
|
|
43
51
|
AI agents (HTTP) -> MCP HTTP Server :3001 -> WebSocket Server :3002 -> RemNote Plugin -> RemNote
|
|
52
|
+
AI agents (stdio) -> remnote-mcp-stdio -> MCP HTTP Server :3001 -> WebSocket Server :3002 -> RemNote Plugin -> RemNote
|
|
44
53
|
CLI commands -> remnote-cli -> MCP HTTP Server :3001 -> WebSocket Server :3002 -> RemNote Plugin -> RemNote
|
|
45
54
|
```
|
|
46
55
|
|
|
47
56
|
The server acts as a bridge:
|
|
48
57
|
|
|
49
58
|
- Communicates with AI agents via Streamable HTTP transport (MCP protocol) - supports both local and remote access
|
|
59
|
+
- Provides `remnote-mcp-stdio` as a local stdio MCP proxy for clients that need stdio transport
|
|
50
60
|
- Provides `remnote-cli` as a bundled command-line MCP client for local automation
|
|
51
61
|
- HTTP server (port 3001) manages MCP sessions for multiple concurrent agents
|
|
52
62
|
- WebSocket server (port 3002) connects to the RemNote browser plugin
|
|
@@ -55,9 +65,11 @@ The server acts as a bridge:
|
|
|
55
65
|
**Multi-Agent Support:** Multiple AI agents can connect simultaneously to the same RemNote knowledge base. Each agent
|
|
56
66
|
gets its own MCP session while sharing the WebSocket bridge.
|
|
57
67
|
|
|
58
|
-
**Remote Access:** By default, the server binds to localhost (127.0.0.1) for local AI agents.
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
**Remote Access:** By default, the server binds to localhost (127.0.0.1) for local AI agents. Claude Desktop and Claude
|
|
69
|
+
Cowork can use the bundled local MCPB extension when desktop extensions are enabled. Cloud-based clients, web/mobile
|
|
70
|
+
surfaces, and managed Claude deployments without local MCPB require remote access—use tunneling tools like ngrok to
|
|
71
|
+
expose the HTTP endpoint securely. The WebSocket connection always stays local for security. See
|
|
72
|
+
[Remote Access Guide](docs/guides/remote-access.md) for setup.
|
|
61
73
|
|
|
62
74
|
## Features
|
|
63
75
|
|
|
@@ -79,18 +91,20 @@ connection always stays local for security. See [Remote Access Guide](docs/guide
|
|
|
79
91
|
npm install -g remnote-mcp-server
|
|
80
92
|
```
|
|
81
93
|
|
|
82
|
-
The package installs
|
|
94
|
+
The package installs these commands:
|
|
83
95
|
|
|
84
96
|
```bash
|
|
85
97
|
remnote-mcp-server --version
|
|
86
98
|
remnote-cli --version
|
|
99
|
+
remnote-mcp-stdio --version
|
|
87
100
|
```
|
|
88
101
|
|
|
89
102
|
### 2. Install the RemNote Plugin
|
|
90
103
|
|
|
91
|
-
Install the [
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
Install the official [MCP/OpenClaw Automation Bridge plugin](https://github.com/robert7/remnote-mcp-bridge) in your
|
|
105
|
+
RemNote app. If installing from the RemNote Marketplace, verify the plugin name and author; similarly named
|
|
106
|
+
`MCP Bridge` variants may be incompatible with this server and cause connection loops or `1008` disconnects. Configure
|
|
107
|
+
the plugin to connect to `ws://127.0.0.1:3002`.
|
|
94
108
|
|
|
95
109
|
### 3. Start the Server
|
|
96
110
|
|
|
@@ -101,7 +115,7 @@ remnote-mcp-server
|
|
|
101
115
|
Expected output:
|
|
102
116
|
|
|
103
117
|
```text
|
|
104
|
-
RemNote MCP Server v0.14.
|
|
118
|
+
RemNote MCP Server v0.14.1 listening { wsPort: 3002, httpPort: 3001 }
|
|
105
119
|
```
|
|
106
120
|
|
|
107
121
|
Keep this terminal running.
|
|
@@ -109,10 +123,13 @@ Keep this terminal running.
|
|
|
109
123
|
### 4. Configure Your AI Client
|
|
110
124
|
|
|
111
125
|
- [Configuration Guide](docs/guides/configuration.md) - Overview and generic setup
|
|
126
|
+
- [Codex TUI / Codex.app](docs/guides/configuration-codex.md) - HTTP MCP, stdio proxy, and `remnote-cli` skill setup
|
|
127
|
+
- [Claude Desktop / Cowork Local MCPB](docs/guides/configuration-claude-desktop-local-mcpb.md) - Preferred local desktop setup, no public HTTPS required
|
|
128
|
+
- [Claude Desktop / Cowork Remote Connector](docs/guides/configuration-claude-desktop-cowork.md) - Remote connector setup when local MCPB is not applicable
|
|
129
|
+
- [Claude Code CLI](docs/guides/configuration-claude-code-CLI.md) - Claude Code local MCP setup
|
|
112
130
|
- [ChatGPT](docs/guides/configuration-chatgpt.md) - ChatGPT Apps configuration
|
|
113
|
-
- [Claude Desktop / Cowork](docs/guides/configuration-claude-desktop-cowork.md) - Remote connector setup (requires remote access setup)
|
|
114
|
-
- [Claude Code CLI](docs/guides/configuration-claude-code-CLI.md) - Detailed Claude Code CLI configuration
|
|
115
131
|
- [Accomplish](docs/guides/configuration-accomplish.md) - Accomplish (Openwork) configuration
|
|
132
|
+
- [Generic stdio MCP clients](docs/guides/configuration.md#stdio-mcp-clients) - Use `remnote-mcp-stdio`
|
|
116
133
|
|
|
117
134
|
## Documentation
|
|
118
135
|
|
|
@@ -124,6 +141,7 @@ Keep this terminal running.
|
|
|
124
141
|
\- Match server version to installed bridge plugin version (`0.x` semver)
|
|
125
142
|
- **[Bridge Connection Lifecycle](https://github.com/robert7/remnote-mcp-bridge/blob/main/docs/guides/connection-lifecycle.md)** - Canonical bridge connect/retry behavior
|
|
126
143
|
- **[Configuration Guide](docs/guides/configuration.md)** - Configure Claude Code CLI, Accomplish, and other clients
|
|
144
|
+
- **[Codex Configuration Guide](docs/guides/configuration-codex.md)** - Set up Codex TUI and Codex.app with RemNote
|
|
127
145
|
- **[ChatGPT Configuration Guide](docs/guides/configuration-chatgpt.md)** - Set up ChatGPT Apps with your MCP server
|
|
128
146
|
- **[Demo & Screenshots](docs/demo.md)** - See the server in action with different AI clients
|
|
129
147
|
|
|
@@ -132,7 +150,8 @@ Keep this terminal running.
|
|
|
132
150
|
- **[CLI Options Reference](docs/guides/cli-options.md)** - Command-line options and environment variables
|
|
133
151
|
- **[remnote-cli Command Reference](docs/guides/cli-command-reference.md)** - Shell command reference for the bundled CLI
|
|
134
152
|
- **[MCP Tools Reference](docs/guides/tools-reference.md)** - Detailed reference for all 9 RemNote tools
|
|
135
|
-
- **[Remote Access Setup](docs/guides/remote-access.md)** - Expose server for
|
|
153
|
+
- **[Remote Access Setup](docs/guides/remote-access.md)** - Expose server for cloud clients or remote connector flows
|
|
154
|
+
(ngrok, etc.)
|
|
136
155
|
|
|
137
156
|
### Help & Advanced
|
|
138
157
|
|
|
@@ -163,14 +182,21 @@ Tools that declare an `outputSchema` return MCP `structuredContent` plus a JSON
|
|
|
163
182
|
See the [MCP tools specification](https://modelcontextprotocol.io/specification/2025-11-25/server/tools) for the
|
|
164
183
|
protocol contract.
|
|
165
184
|
|
|
185
|
+
The server uses `@modelcontextprotocol/sdk` and supports current MCP protocol negotiation, including `2025-11-25`.
|
|
186
|
+
Do not confuse MCP protocol versions with `remnote-mcp-server` or bridge plugin package versions; package versions use
|
|
187
|
+
`0.x` semver and should usually match by minor line.
|
|
188
|
+
|
|
166
189
|
See the [Tools Reference](docs/guides/tools-reference.md) for detailed usage and examples.
|
|
167
190
|
|
|
168
191
|
## Supported AI Clients
|
|
169
192
|
|
|
170
193
|
- **[Claude Code CLI](https://claude.com/claude-code)** - Local terminal-based agent
|
|
171
|
-
- **
|
|
194
|
+
- **Codex TUI / Codex.app** - Local OpenAI coding agent clients
|
|
195
|
+
- **Claude Desktop / Cowork** - Local MCPB clients when desktop extensions are enabled, or remote connector clients
|
|
196
|
+
when local MCPB is not applicable
|
|
172
197
|
- **[Accomplish](https://github.com/accomplish-ai/accomplish)** - Task-based MCP client (formerly Openwork)
|
|
173
198
|
- **Any MCP client** supporting Streamable HTTP transport
|
|
199
|
+
- **Any local MCP client** supporting stdio transport through `remnote-mcp-stdio`
|
|
174
200
|
- **Any local command runner** that can call `remnote-cli`
|
|
175
201
|
|
|
176
202
|
## Example Usage
|
|
@@ -253,7 +279,7 @@ See the [Troubleshooting Guide](docs/guides/troubleshooting.md) for detailed sol
|
|
|
253
279
|
git clone https://github.com/robert7/remnote-mcp-server.git
|
|
254
280
|
cd remnote-mcp-server
|
|
255
281
|
./link-cli.sh
|
|
256
|
-
# Later, remove the local links for
|
|
282
|
+
# Later, remove the local links for package executables:
|
|
257
283
|
./unlink-cli.sh
|
|
258
284
|
```
|
|
259
285
|
|
|
@@ -274,8 +300,6 @@ For the canonical workflow for updating and running shared live integration cove
|
|
|
274
300
|
## Related Projects
|
|
275
301
|
|
|
276
302
|
- [RemNote Automation Bridge](https://github.com/robert7/remnote-mcp-bridge) - Browser plugin for RemNote integration
|
|
277
|
-
- [Legacy remnote-cli repository](https://github.com/robert7/remnote-cli) - Historical source; the maintained CLI now
|
|
278
|
-
ships from this package
|
|
279
303
|
- [Model Context Protocol](https://modelcontextprotocol.io/) - Open protocol for AI-application integration
|
|
280
304
|
|
|
281
305
|
## License
|
package/dist/cli.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export interface CliOptions {
|
|
|
9
9
|
requestLog?: string;
|
|
10
10
|
responseLog?: string;
|
|
11
11
|
}
|
|
12
|
+
export declare function getBundledMcpbPath(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Handle utility commands that do not start the MCP server.
|
|
15
|
+
*/
|
|
16
|
+
export declare function handleUtilityCommand(argv?: string[]): boolean;
|
|
12
17
|
/**
|
|
13
18
|
* Parse CLI arguments and return typed options
|
|
14
19
|
*/
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
3
4
|
const require = createRequire(import.meta.url);
|
|
4
5
|
const packageJson = require('../package.json');
|
|
5
6
|
const validLogLevels = ['debug', 'info', 'warn', 'error'];
|
|
7
|
+
const MCPB_PATH_COMMAND = 'mcpb-path';
|
|
8
|
+
export function getBundledMcpbPath() {
|
|
9
|
+
return fileURLToPath(new URL('../mcpb/remnote-local/remnote-local.mcpb', import.meta.url));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Handle utility commands that do not start the MCP server.
|
|
13
|
+
*/
|
|
14
|
+
export function handleUtilityCommand(argv = process.argv) {
|
|
15
|
+
if (argv[2] !== MCPB_PATH_COMMAND) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
console.log(getBundledMcpbPath());
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
6
21
|
/**
|
|
7
22
|
* Parse CLI arguments and return typed options
|
|
8
23
|
*/
|
|
@@ -12,6 +27,7 @@ export function parseCliArgs() {
|
|
|
12
27
|
.name('remnote-mcp-server')
|
|
13
28
|
.description('MCP server bridge for RemNote knowledge base')
|
|
14
29
|
.version(packageJson.version)
|
|
30
|
+
.addHelpText('after', '\nCommands:\n mcpb-path Print the bundled Claude Desktop MCPB extension path')
|
|
15
31
|
.option('--ws-port <number>', 'WebSocket port (default: 3002, env: REMNOTE_WS_PORT)', parsePort)
|
|
16
32
|
.option('--http-port <number>', 'HTTP MCP port (default: 3001, env: REMNOTE_HTTP_PORT)', parsePort)
|
|
17
33
|
.option('--http-host <host>', 'HTTP server bind address (default: 127.0.0.1, env: REMNOTE_HTTP_HOST). Use 0.0.0.0 for Docker/VPS deployments', validateHost)
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import { WebSocketServer } from './websocket-server.js';
|
|
4
4
|
import { HttpMcpServer } from './http-server.js';
|
|
5
|
-
import { parseCliArgs } from './cli.js';
|
|
5
|
+
import { handleUtilityCommand, parseCliArgs } from './cli.js';
|
|
6
6
|
import { getConfig } from './config.js';
|
|
7
7
|
import { createLogger, ensureLogDirectory, createRequestResponseLogger } from './logger.js';
|
|
8
8
|
const require = createRequire(import.meta.url);
|
|
@@ -69,6 +69,9 @@ async function main() {
|
|
|
69
69
|
process.on('SIGINT', shutdown);
|
|
70
70
|
process.on('SIGTERM', shutdown);
|
|
71
71
|
}
|
|
72
|
+
if (handleUtilityCommand()) {
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
72
75
|
main().catch((error) => {
|
|
73
76
|
// Pre-logger error handling
|
|
74
77
|
console.error('[MCP Server] Fatal error:', error);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ export class McpServerClient {
|
|
|
19
19
|
clientInfo;
|
|
20
20
|
client = null;
|
|
21
21
|
transport = null;
|
|
22
|
-
constructor(mcpUrl, clientInfo = { name: 'remnote-cli', version: '0.14.
|
|
22
|
+
constructor(mcpUrl, clientInfo = { name: 'remnote-cli', version: '0.14.1' }) {
|
|
23
23
|
this.mcpUrl = normalizeMcpUrl(mcpUrl);
|
|
24
24
|
this.clientInfo = clientInfo;
|
|
25
25
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Logger } from './logger.js';
|
|
2
2
|
export declare const REQUEST_TIMEOUT_MS = 15000;
|
|
3
|
+
export declare const HELLO_TIMEOUT_MS = 2000;
|
|
3
4
|
export declare class WebSocketServer {
|
|
4
5
|
private wss;
|
|
5
6
|
private client;
|
|
@@ -10,6 +11,8 @@ export declare class WebSocketServer {
|
|
|
10
11
|
private responseLogger;
|
|
11
12
|
private serverVersion;
|
|
12
13
|
private bridgeVersion;
|
|
14
|
+
private clientAccepted;
|
|
15
|
+
private helloTimeout;
|
|
13
16
|
private pendingRequests;
|
|
14
17
|
private connectCallbacks;
|
|
15
18
|
private disconnectCallbacks;
|
|
@@ -23,4 +26,6 @@ export declare class WebSocketServer {
|
|
|
23
26
|
onClientConnect(callback: () => void): void;
|
|
24
27
|
onClientDisconnect(callback: () => void): void;
|
|
25
28
|
private handleMessage;
|
|
29
|
+
private rejectBridge;
|
|
30
|
+
private clearHelloTimeout;
|
|
26
31
|
}
|
package/dist/websocket-server.js
CHANGED
|
@@ -2,6 +2,10 @@ import { WebSocketServer as WSServer, WebSocket } from 'ws';
|
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import { checkVersionCompatibility } from './version-compat.js';
|
|
4
4
|
export const REQUEST_TIMEOUT_MS = 15000;
|
|
5
|
+
export const HELLO_TIMEOUT_MS = 2000;
|
|
6
|
+
const POLICY_VIOLATION = 1008;
|
|
7
|
+
const INCOMPATIBLE_BRIDGE_REASON = 'Wrong/incompatible RemNote plugin installed. Install MCP/OpenClaw Automation Bridge matching server.';
|
|
8
|
+
const BRIDGE_REJECTION_LOG_PREFIX = `Rejecting bridge connection: ${INCOMPATIBLE_BRIDGE_REASON}`;
|
|
5
9
|
export class WebSocketServer {
|
|
6
10
|
wss = null;
|
|
7
11
|
client = null;
|
|
@@ -12,6 +16,8 @@ export class WebSocketServer {
|
|
|
12
16
|
responseLogger = null;
|
|
13
17
|
serverVersion;
|
|
14
18
|
bridgeVersion = null;
|
|
19
|
+
clientAccepted = false;
|
|
20
|
+
helloTimeout = null;
|
|
15
21
|
pendingRequests = new Map();
|
|
16
22
|
connectCallbacks = [];
|
|
17
23
|
disconnectCallbacks = [];
|
|
@@ -37,11 +43,18 @@ export class WebSocketServer {
|
|
|
37
43
|
// Only allow single client connection
|
|
38
44
|
if (this.client && this.client.readyState === WebSocket.OPEN) {
|
|
39
45
|
this.logger.warn('Rejecting connection: client already connected');
|
|
40
|
-
ws.close(
|
|
46
|
+
ws.close(POLICY_VIOLATION, 'Only one client allowed');
|
|
41
47
|
return;
|
|
42
48
|
}
|
|
43
49
|
this.client = ws;
|
|
50
|
+
this.clientAccepted = false;
|
|
44
51
|
this.logger.info('WebSocket client connected');
|
|
52
|
+
this.helloTimeout = setTimeout(() => {
|
|
53
|
+
if (this.client === ws && !this.clientAccepted && ws.readyState === WebSocket.OPEN) {
|
|
54
|
+
this.logger.warn(`${BRIDGE_REJECTION_LOG_PREFIX} Detail: hello timeout.`);
|
|
55
|
+
ws.close(POLICY_VIOLATION, INCOMPATIBLE_BRIDGE_REASON);
|
|
56
|
+
}
|
|
57
|
+
}, HELLO_TIMEOUT_MS);
|
|
45
58
|
setImmediate(() => {
|
|
46
59
|
if (ws.readyState !== WebSocket.OPEN) {
|
|
47
60
|
return;
|
|
@@ -52,7 +65,6 @@ export class WebSocketServer {
|
|
|
52
65
|
version: this.serverVersion,
|
|
53
66
|
}));
|
|
54
67
|
});
|
|
55
|
-
this.connectCallbacks.forEach((cb) => cb());
|
|
56
68
|
ws.on('message', (data) => {
|
|
57
69
|
try {
|
|
58
70
|
this.handleMessage(data.toString());
|
|
@@ -63,15 +75,22 @@ export class WebSocketServer {
|
|
|
63
75
|
});
|
|
64
76
|
ws.on('close', () => {
|
|
65
77
|
this.logger.info('WebSocket client disconnected');
|
|
66
|
-
|
|
67
|
-
this.
|
|
78
|
+
const wasAccepted = this.clientAccepted;
|
|
79
|
+
if (this.client === ws) {
|
|
80
|
+
this.client = null;
|
|
81
|
+
this.bridgeVersion = null;
|
|
82
|
+
this.clientAccepted = false;
|
|
83
|
+
this.clearHelloTimeout();
|
|
84
|
+
}
|
|
68
85
|
// Reject all pending requests
|
|
69
86
|
for (const [_id, pending] of this.pendingRequests.entries()) {
|
|
70
87
|
clearTimeout(pending.timeout);
|
|
71
88
|
pending.reject(new Error('Connection lost'));
|
|
72
89
|
}
|
|
73
90
|
this.pendingRequests.clear();
|
|
74
|
-
|
|
91
|
+
if (wasAccepted) {
|
|
92
|
+
this.disconnectCallbacks.forEach((cb) => cb());
|
|
93
|
+
}
|
|
75
94
|
});
|
|
76
95
|
ws.on('error', (error) => {
|
|
77
96
|
this.logger.error({ error }, 'WebSocket client error');
|
|
@@ -84,6 +103,9 @@ export class WebSocketServer {
|
|
|
84
103
|
if (this.client) {
|
|
85
104
|
this.client.close();
|
|
86
105
|
this.client = null;
|
|
106
|
+
this.bridgeVersion = null;
|
|
107
|
+
this.clientAccepted = false;
|
|
108
|
+
this.clearHelloTimeout();
|
|
87
109
|
}
|
|
88
110
|
if (this.wss) {
|
|
89
111
|
this.wss.close(() => {
|
|
@@ -98,7 +120,7 @@ export class WebSocketServer {
|
|
|
98
120
|
});
|
|
99
121
|
}
|
|
100
122
|
async sendRequest(action, payload) {
|
|
101
|
-
if (!this.
|
|
123
|
+
if (!this.isConnected()) {
|
|
102
124
|
throw new Error('RemNote plugin not connected. Please ensure the plugin is installed and running.');
|
|
103
125
|
}
|
|
104
126
|
const id = randomUUID();
|
|
@@ -152,7 +174,7 @@ export class WebSocketServer {
|
|
|
152
174
|
});
|
|
153
175
|
}
|
|
154
176
|
isConnected() {
|
|
155
|
-
return this.client !== null && this.client.readyState === WebSocket.OPEN;
|
|
177
|
+
return this.client !== null && this.client.readyState === WebSocket.OPEN && this.clientAccepted;
|
|
156
178
|
}
|
|
157
179
|
getBridgeVersion() {
|
|
158
180
|
return this.bridgeVersion;
|
|
@@ -174,12 +196,20 @@ export class WebSocketServer {
|
|
|
174
196
|
}, 'Received message');
|
|
175
197
|
// Handle hello from bridge plugin
|
|
176
198
|
if ('type' in message && message.type === 'hello') {
|
|
177
|
-
|
|
178
|
-
|
|
199
|
+
if (typeof message.version !== 'string') {
|
|
200
|
+
this.rejectBridge('Bridge hello missing version', INCOMPATIBLE_BRIDGE_REASON);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
179
203
|
const warning = checkVersionCompatibility(this.serverVersion, message.version);
|
|
180
204
|
if (warning) {
|
|
181
|
-
this.
|
|
205
|
+
this.rejectBridge(warning, INCOMPATIBLE_BRIDGE_REASON);
|
|
206
|
+
return;
|
|
182
207
|
}
|
|
208
|
+
this.bridgeVersion = message.version;
|
|
209
|
+
this.clientAccepted = true;
|
|
210
|
+
this.clearHelloTimeout();
|
|
211
|
+
this.logger.info({ bridgeVersion: message.version }, 'Bridge identified');
|
|
212
|
+
this.connectCallbacks.forEach((cb) => cb());
|
|
183
213
|
return;
|
|
184
214
|
}
|
|
185
215
|
// Handle pong response to ping
|
|
@@ -195,6 +225,10 @@ export class WebSocketServer {
|
|
|
195
225
|
}
|
|
196
226
|
// Handle response to our request
|
|
197
227
|
if ('id' in message) {
|
|
228
|
+
if (!this.clientAccepted) {
|
|
229
|
+
this.rejectBridge('Bridge sent response before compatible hello', INCOMPATIBLE_BRIDGE_REASON);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
198
232
|
const response = message;
|
|
199
233
|
const pending = this.pendingRequests.get(response.id);
|
|
200
234
|
if (pending) {
|
|
@@ -211,10 +245,30 @@ export class WebSocketServer {
|
|
|
211
245
|
this.logger.warn({ id: response.id }, 'Unknown request ID');
|
|
212
246
|
}
|
|
213
247
|
}
|
|
248
|
+
if (!this.clientAccepted) {
|
|
249
|
+
this.rejectBridge('Bridge sent message before compatible hello', INCOMPATIBLE_BRIDGE_REASON);
|
|
250
|
+
}
|
|
214
251
|
}
|
|
215
252
|
catch (error) {
|
|
216
253
|
this.logger.error({ error }, 'Error parsing message');
|
|
254
|
+
if (!this.clientAccepted) {
|
|
255
|
+
this.rejectBridge('Bridge sent invalid JSON before compatible hello', INCOMPATIBLE_BRIDGE_REASON);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
rejectBridge(detail, closeReason) {
|
|
260
|
+
this.logger.warn({ detail }, BRIDGE_REJECTION_LOG_PREFIX);
|
|
261
|
+
this.clearHelloTimeout();
|
|
262
|
+
if (this.client && this.client.readyState === WebSocket.OPEN) {
|
|
263
|
+
this.client.close(POLICY_VIOLATION, closeReason);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
clearHelloTimeout() {
|
|
267
|
+
if (!this.helloTimeout) {
|
|
268
|
+
return;
|
|
217
269
|
}
|
|
270
|
+
clearTimeout(this.helloTimeout);
|
|
271
|
+
this.helloTimeout = null;
|
|
218
272
|
}
|
|
219
273
|
}
|
|
220
274
|
//# sourceMappingURL=websocket-server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-server.js","sourceRoot":"","sources":["../src/websocket-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,IAAI,QAAQ,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"websocket-server.js","sourceRoot":"","sources":["../src/websocket-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# RemNote Local MCPB
|
|
2
|
+
|
|
3
|
+
Claude Desktop extension that exposes a local `remnote-mcp-server` to Claude Desktop and eligible Claude Cowork
|
|
4
|
+
desktop sessions without a public HTTPS tunnel.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
1. Install and start `remnote-mcp-server`.
|
|
9
|
+
2. Open RemNote with the Automation Bridge plugin enabled and connected.
|
|
10
|
+
3. Install this extension's `.mcpb` file in Claude Desktop.
|
|
11
|
+
4. Keep the default MCP URL unless your server uses a custom port:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
http://127.0.0.1:3001/mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This extension does not start or supervise `remnote-mcp-server`. It is a stdio proxy that forwards Claude Desktop and
|
|
18
|
+
eligible Claude Cowork tool calls to the local Streamable HTTP endpoint.
|
|
19
|
+
|
|
20
|
+
## Installed Package Path
|
|
21
|
+
|
|
22
|
+
When installed from npm, print the bundled `.mcpb` path with:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
remnote-mcp-server mcpb-path
|
|
26
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": "0.3",
|
|
3
|
+
"name": "remnote-local",
|
|
4
|
+
"display_name": "RemNote Local",
|
|
5
|
+
"version": "0.14.1",
|
|
6
|
+
"description": "Use a locally running RemNote MCP Server from Claude Desktop or Cowork without a public HTTPS tunnel.",
|
|
7
|
+
"long_description": "RemNote Local is a Claude Desktop extension that exposes RemNote MCP tools over stdio and forwards calls to a local remnote-mcp-server Streamable HTTP endpoint. It can also be used by Claude Cowork in Claude Desktop when desktop extensions are enabled. Start remnote-mcp-server and keep the RemNote Automation Bridge plugin connected before using the tools.",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Robert Spiegel",
|
|
10
|
+
"email": "nightingale7@gmail.com"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/robert7/remnote-mcp-server.git"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/robert7/remnote-mcp-server",
|
|
17
|
+
"documentation": "https://github.com/robert7/remnote-mcp-server/tree/main/docs/guides",
|
|
18
|
+
"support": "https://github.com/robert7/remnote-mcp-server/issues",
|
|
19
|
+
"server": {
|
|
20
|
+
"type": "node",
|
|
21
|
+
"entry_point": "server/index.js",
|
|
22
|
+
"mcp_config": {
|
|
23
|
+
"command": "node",
|
|
24
|
+
"args": ["${__dirname}/server/index.js"],
|
|
25
|
+
"env": {
|
|
26
|
+
"REMNOTE_MCP_URL": "${user_config.mcp_url}"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"tools": [
|
|
31
|
+
{
|
|
32
|
+
"name": "remnote_create_note",
|
|
33
|
+
"description": "Create a new note in RemNote."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "remnote_search",
|
|
37
|
+
"description": "Search the RemNote knowledge base."
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "remnote_search_by_tag",
|
|
41
|
+
"description": "Find notes by tag."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "remnote_read_note",
|
|
45
|
+
"description": "Read a specific note from RemNote by Rem ID."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "remnote_update_note",
|
|
49
|
+
"description": "Update an existing note in RemNote."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "remnote_append_journal",
|
|
53
|
+
"description": "Append content to today's daily document in RemNote."
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "remnote_read_table",
|
|
57
|
+
"description": "Read an Advanced Table from RemNote."
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "remnote_get_playbook",
|
|
61
|
+
"description": "Get an operations playbook for MCP agents."
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "remnote_status",
|
|
65
|
+
"description": "Check bridge connection health and write-policy capabilities."
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"tools_generated": true,
|
|
69
|
+
"keywords": ["remnote", "mcp", "notes", "knowledge-base", "local"],
|
|
70
|
+
"license": "MIT",
|
|
71
|
+
"compatibility": {
|
|
72
|
+
"claude_desktop": ">=1.0.0",
|
|
73
|
+
"platforms": ["darwin", "win32", "linux"],
|
|
74
|
+
"runtimes": {
|
|
75
|
+
"node": ">=20.0.0"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"user_config": {
|
|
79
|
+
"mcp_url": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"title": "Local MCP URL",
|
|
82
|
+
"description": "Local remnote-mcp-server Streamable HTTP endpoint.",
|
|
83
|
+
"default": "http://127.0.0.1:3001/mcp",
|
|
84
|
+
"required": true
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "remnote-local-mcpb",
|
|
3
|
+
"version": "0.14.2",
|
|
4
|
+
"description": "Claude Desktop MCPB stdio proxy for a local RemNote MCP Server",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": true,
|
|
7
|
+
"main": "server/index.js",
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
import { realpathSync } from 'node:fs';
|
|
5
|
+
import { fileURLToPath, URL, pathToFileURL } from 'node:url';
|
|
6
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
7
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
8
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
11
|
+
|
|
12
|
+
export const DEFAULT_MCP_URL = 'http://127.0.0.1:3001/mcp';
|
|
13
|
+
export const SERVER_INFO = { name: 'remnote-mcp-stdio', version: '0.14.1' };
|
|
14
|
+
|
|
15
|
+
export const FALLBACK_TOOLS = [
|
|
16
|
+
{
|
|
17
|
+
name: 'remnote_create_note',
|
|
18
|
+
description:
|
|
19
|
+
'Create a new note in RemNote with optional content, parent, and tags. Supports hierarchical markdown in content and flashcard syntax.',
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
title: { type: 'string', description: 'The title of the note' },
|
|
24
|
+
content: { type: 'string', description: 'Content as plain text or hierarchical markdown' },
|
|
25
|
+
parentId: { type: 'string', description: 'Parent Rem ID' },
|
|
26
|
+
tags: { type: 'array', items: { type: 'string' }, description: 'Tags to apply' },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'remnote_search',
|
|
32
|
+
description: 'Search the RemNote knowledge base.',
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
query: { type: 'string', description: 'Search query text' },
|
|
37
|
+
limit: { type: 'number', description: 'Maximum results' },
|
|
38
|
+
includeContent: { type: 'string', enum: ['none', 'markdown', 'structured'] },
|
|
39
|
+
depth: { type: 'number', description: 'Depth of child hierarchy to render' },
|
|
40
|
+
childLimit: { type: 'number', description: 'Maximum children per level' },
|
|
41
|
+
maxContentLength: { type: 'number', description: 'Maximum rendered content length' },
|
|
42
|
+
},
|
|
43
|
+
required: ['query'],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'remnote_search_by_tag',
|
|
48
|
+
description: 'Find notes by tag.',
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
tag: { type: 'string', description: 'Tag name to search' },
|
|
53
|
+
limit: { type: 'number', description: 'Maximum results' },
|
|
54
|
+
includeContent: { type: 'string', enum: ['none', 'markdown', 'structured'] },
|
|
55
|
+
depth: { type: 'number', description: 'Depth of child hierarchy to render' },
|
|
56
|
+
childLimit: { type: 'number', description: 'Maximum children per level' },
|
|
57
|
+
maxContentLength: { type: 'number', description: 'Maximum rendered content length' },
|
|
58
|
+
},
|
|
59
|
+
required: ['tag'],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'remnote_read_note',
|
|
64
|
+
description: 'Read a specific note from RemNote by its Rem ID.',
|
|
65
|
+
inputSchema: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
remId: { type: 'string', description: 'The Rem ID to read' },
|
|
69
|
+
depth: { type: 'number', description: 'Depth of child hierarchy to render' },
|
|
70
|
+
includeContent: { type: 'string', enum: ['none', 'markdown', 'structured'] },
|
|
71
|
+
childLimit: { type: 'number', description: 'Maximum children per level' },
|
|
72
|
+
maxContentLength: { type: 'number', description: 'Maximum rendered content length' },
|
|
73
|
+
},
|
|
74
|
+
required: ['remId'],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'remnote_update_note',
|
|
79
|
+
description: 'Update an existing note in RemNote.',
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties: {
|
|
83
|
+
remId: { type: 'string', description: 'The Rem ID to update' },
|
|
84
|
+
title: { type: 'string', description: 'New title' },
|
|
85
|
+
appendContent: { type: 'string', description: 'Content to append as children' },
|
|
86
|
+
replaceContent: { type: 'string', description: 'Content to replace direct children' },
|
|
87
|
+
addTags: { type: 'array', items: { type: 'string' }, description: 'Tags to add' },
|
|
88
|
+
removeTags: { type: 'array', items: { type: 'string' }, description: 'Tags to remove' },
|
|
89
|
+
},
|
|
90
|
+
required: ['remId'],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'remnote_append_journal',
|
|
95
|
+
description: "Append content to today's daily document in RemNote.",
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
content: { type: 'string', description: "Content to append to today's daily document" },
|
|
100
|
+
timestamp: { type: 'boolean', description: 'Include timestamp' },
|
|
101
|
+
},
|
|
102
|
+
required: ['content'],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'remnote_read_table',
|
|
107
|
+
description: 'Read an Advanced Table from RemNote by exact title or Rem ID.',
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: {
|
|
111
|
+
tableRemId: { type: 'string', description: 'Table Rem ID' },
|
|
112
|
+
tableTitle: { type: 'string', description: 'Exact Advanced Table title' },
|
|
113
|
+
limit: { type: 'number', description: 'Maximum rows to return' },
|
|
114
|
+
offset: { type: 'number', description: '0-based row offset for pagination' },
|
|
115
|
+
propertyFilter: {
|
|
116
|
+
type: 'array',
|
|
117
|
+
items: { type: 'string' },
|
|
118
|
+
description: 'Only return these property/column names',
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'remnote_get_playbook',
|
|
125
|
+
description: 'Get an operations playbook for MCP agents.',
|
|
126
|
+
inputSchema: { type: 'object', properties: {} },
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'remnote_status',
|
|
130
|
+
description:
|
|
131
|
+
'Check bridge connection health, compatibility warnings, and write-policy capabilities.',
|
|
132
|
+
inputSchema: { type: 'object', properties: {} },
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
export function normalizeMcpUrl(value) {
|
|
137
|
+
const trimmed = String(value || DEFAULT_MCP_URL).trim();
|
|
138
|
+
if (trimmed.endsWith('/mcp')) {
|
|
139
|
+
return trimmed;
|
|
140
|
+
}
|
|
141
|
+
return `${trimmed.replace(/\/+$/, '')}/mcp`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function createSdkHttpClient(mcpUrl, clientInfo = SERVER_INFO) {
|
|
145
|
+
const transport = new StreamableHTTPClientTransport(new URL(mcpUrl));
|
|
146
|
+
const client = new Client(clientInfo);
|
|
147
|
+
return { client, transport };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export class RemNoteLocalProxy {
|
|
151
|
+
constructor(options = {}) {
|
|
152
|
+
this.mcpUrl = normalizeMcpUrl(options.mcpUrl ?? process.env.REMNOTE_MCP_URL);
|
|
153
|
+
this.createClient = options.createClient ?? createSdkHttpClient;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async listTools() {
|
|
157
|
+
try {
|
|
158
|
+
return await this.withClient((client) => client.listTools());
|
|
159
|
+
} catch {
|
|
160
|
+
return { tools: FALLBACK_TOOLS };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async callTool(params) {
|
|
165
|
+
try {
|
|
166
|
+
return await this.withClient((client) => client.callTool(params));
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return {
|
|
169
|
+
isError: true,
|
|
170
|
+
content: [{ type: 'text', text: this.formatConnectionError(error) }],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
registerHandlers(server) {
|
|
176
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => this.listTools());
|
|
177
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) =>
|
|
178
|
+
this.callTool({
|
|
179
|
+
name: request.params.name,
|
|
180
|
+
arguments: request.params.arguments ?? {},
|
|
181
|
+
})
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async withClient(operation) {
|
|
186
|
+
const { client, transport } = this.createClient(this.mcpUrl, SERVER_INFO);
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
await client.connect(transport);
|
|
190
|
+
return await operation(client);
|
|
191
|
+
} finally {
|
|
192
|
+
await closeBestEffort(transport, client);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
formatConnectionError(error) {
|
|
197
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
198
|
+
return [
|
|
199
|
+
`Cannot connect to local RemNote MCP Server at ${this.mcpUrl}.`,
|
|
200
|
+
'Start remnote-mcp-server, open RemNote, and ensure the Automation Bridge plugin is connected.',
|
|
201
|
+
`Details: ${detail}`,
|
|
202
|
+
].join(' ');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function createStdioServer(options = {}) {
|
|
207
|
+
const server = new Server(SERVER_INFO, { capabilities: { tools: {} } });
|
|
208
|
+
const proxy = new RemNoteLocalProxy(options);
|
|
209
|
+
proxy.registerHandlers(server);
|
|
210
|
+
return server;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function run() {
|
|
214
|
+
const server = createStdioServer();
|
|
215
|
+
await server.connect(new StdioServerTransport());
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function formatUsage() {
|
|
219
|
+
return [
|
|
220
|
+
'remnote-mcp-stdio - stdio MCP proxy for a local RemNote MCP Server',
|
|
221
|
+
'',
|
|
222
|
+
'Usage:',
|
|
223
|
+
' remnote-mcp-stdio Run stdio MCP transport for an MCP client',
|
|
224
|
+
' remnote-mcp-stdio --help, -h Print this help',
|
|
225
|
+
' remnote-mcp-stdio --version, -V Print version',
|
|
226
|
+
'',
|
|
227
|
+
'This command is normally launched by a stdio MCP client.',
|
|
228
|
+
'Start remnote-mcp-server separately first, then let the RemNote Automation Bridge connect to it.',
|
|
229
|
+
`Default target: ${DEFAULT_MCP_URL}`,
|
|
230
|
+
'',
|
|
231
|
+
'Environment:',
|
|
232
|
+
' REMNOTE_MCP_URL Override the local MCP endpoint',
|
|
233
|
+
].join('\n');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function handleUtilityCommand(argv = process.argv) {
|
|
237
|
+
if (argv.includes('--version') || argv.includes('-V') || argv.includes('-v')) {
|
|
238
|
+
process.stdout.write(`${SERVER_INFO.version}\n`);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
243
|
+
process.stdout.write(`${formatUsage()}\n`);
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function isInteractiveTerminalInvocation(
|
|
251
|
+
argv = process.argv,
|
|
252
|
+
stdin = process.stdin,
|
|
253
|
+
stdout = process.stdout
|
|
254
|
+
) {
|
|
255
|
+
return argv.length <= 2 && Boolean(stdin.isTTY) && Boolean(stdout.isTTY);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function isMainModule(argv = process.argv, moduleUrl = import.meta.url) {
|
|
259
|
+
if (!argv[1]) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
return realpathSync(argv[1]) === realpathSync(fileURLToPath(moduleUrl));
|
|
265
|
+
} catch {
|
|
266
|
+
return pathToFileURL(argv[1]).href === moduleUrl;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function closeBestEffort(transport, client) {
|
|
271
|
+
try {
|
|
272
|
+
if (typeof transport.terminateSession === 'function') {
|
|
273
|
+
await transport.terminateSession();
|
|
274
|
+
}
|
|
275
|
+
} catch {
|
|
276
|
+
// Best-effort cleanup: the proxy process can continue serving future calls.
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
await client.close();
|
|
281
|
+
} catch {
|
|
282
|
+
// Best-effort cleanup: failed closes should not mask the original operation result.
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (isMainModule()) {
|
|
287
|
+
if (handleUtilityCommand()) {
|
|
288
|
+
process.exit(0);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (isInteractiveTerminalInvocation()) {
|
|
292
|
+
process.stderr.write(`${formatUsage()}\n`);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
run().catch((error) => {
|
|
297
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
});
|
|
300
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remnote-mcp-server",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "MCP server bridge for RemNote knowledge base",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"remnote-mcp-server": "dist/index.js",
|
|
9
|
-
"remnote-cli": "dist/remnote-cli/index.js"
|
|
9
|
+
"remnote-cli": "dist/remnote-cli/index.js",
|
|
10
|
+
"remnote-mcp-stdio": "mcpb/remnote-local/server/index.js"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|
|
12
|
-
"build": "tsc && node scripts/chmod-bins.mjs",
|
|
13
|
+
"build": "tsc && node scripts/chmod-bins.mjs && node scripts/build-mcpb.mjs",
|
|
14
|
+
"build:mcpb": "node scripts/build-mcpb.mjs",
|
|
13
15
|
"dev": "tsx watch src/index.ts",
|
|
14
16
|
"start": "node dist/index.js",
|
|
15
17
|
"typecheck": "tsc --noEmit",
|
|
@@ -19,12 +21,13 @@
|
|
|
19
21
|
"test:coverage": "vitest run --coverage",
|
|
20
22
|
"test:integration": "./run-integration-test.sh",
|
|
21
23
|
"test:integration:mcp": "tsx test/integration/run-integration.ts",
|
|
24
|
+
"test:integration:mcpb": "tsx test/integration/run-integration.ts --transport mcpb",
|
|
22
25
|
"test:integration:cli": "tsx test/integration/cli/run-integration.ts",
|
|
23
26
|
"test:ui": "vitest --ui",
|
|
24
|
-
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
25
|
-
"lint:fix": "eslint \"src/**/*.ts\" \"test/**/*.ts\" --fix",
|
|
26
|
-
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
27
|
-
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
27
|
+
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\" \"mcpb/**/*.js\" \"scripts/**/*.mjs\"",
|
|
28
|
+
"lint:fix": "eslint \"src/**/*.ts\" \"test/**/*.ts\" \"mcpb/**/*.js\" \"scripts/**/*.mjs\" --fix",
|
|
29
|
+
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"mcpb/**/*.js\" \"mcpb/**/*.json\" \"mcpb/**/*.md\" \"scripts/**/*.mjs\"",
|
|
30
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\" \"mcpb/**/*.js\" \"mcpb/**/*.json\" \"mcpb/**/*.md\" \"scripts/**/*.mjs\"",
|
|
28
31
|
"quality": "./code-quality.sh",
|
|
29
32
|
"precommit": "./code-quality.sh"
|
|
30
33
|
},
|
|
@@ -39,10 +42,11 @@
|
|
|
39
42
|
"websocket",
|
|
40
43
|
"bridge"
|
|
41
44
|
],
|
|
42
|
-
"author": "
|
|
45
|
+
"author": "Robert Spiegel <nightingale7@gmail.com>",
|
|
43
46
|
"license": "MIT",
|
|
44
47
|
"files": [
|
|
45
48
|
"dist/",
|
|
49
|
+
"mcpb/",
|
|
46
50
|
"README.md",
|
|
47
51
|
"LICENSE",
|
|
48
52
|
"CHANGELOG.md"
|