remark-mcp 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Freddie Wheeler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # remark-mcp
2
+
3
+ **Connect Claude to Remark — push documents in for provocation-driven review, pull them back with their full thinking trail.**
4
+
5
+ `remark-mcp` is a small [MCP](https://modelcontextprotocol.io) server. It lets any Claude Code or Claude Desktop session push a document into [Remark](https://remark.freddie.nyc) — optionally with a *context brief* capturing the session's reasoning — and pull any Remark document back as a single composed markdown artifact, including every remark and its status. Work is born in a Claude session, handed to Remark for deeper thinking, and referenceable from any later session.
6
+
7
+ ## Quickstart
8
+
9
+ 1. **Create a Bridge token** in Remark → Settings → Bridge tokens (copy it once — it is shown only once).
10
+ 2. **Add the server to Claude Code**, passing the token via env:
11
+ ```sh
12
+ claude mcp add remark --env REMARK_TOKEN=rmk_your_token_here -- npx -y remark-mcp
13
+ ```
14
+ 3. **Use it**: in a Claude session, say *"push this document to Remark with a brief covering what we decided"* — you'll get back a link. Later, *"pull my latest Remark doc"* or *"what did Remark say about it?"*
15
+
16
+ That's it. The token lives in your client config, never on the command line or in a log.
17
+
18
+ ---
19
+
20
+ ## Configuration
21
+
22
+ All configuration is via environment variables — never argv, so your token never lands in a process list.
23
+
24
+ | Variable | Required | Default | Notes |
25
+ | --- | --- | --- | --- |
26
+ | `REMARK_TOKEN` | Yes | — | A Bridge token from Remark → Settings → Bridge tokens (`rmk_…`). |
27
+ | `REMARK_URL` | No | `https://remark.freddie.nyc` | Point at a different Remark instance if you self-host. |
28
+
29
+ The server fails fast at startup with a clear message if `REMARK_TOKEN` is missing.
30
+
31
+ ## Client setup
32
+
33
+ The supported (QA'd) clients are **Claude Code** and **Claude Desktop**. Cursor and VS Code use the same standard MCP config and should work, but are documented-not-guaranteed.
34
+
35
+ ### Claude Code
36
+
37
+ ```sh
38
+ claude mcp add remark --env REMARK_TOKEN=rmk_your_token_here -- npx -y remark-mcp
39
+ ```
40
+
41
+ ### Claude Desktop
42
+
43
+ **Option A — one-click bundle (`.mcpb`):** download the `remark-mcp.mcpb` file from the [latest release](https://github.com/freddie-wheeler/remark-mcp/releases), double-click it to install into Claude Desktop, and paste your token when prompted. No terminal required.
44
+
45
+ **Option B — config file:** add this to `claude_desktop_config.json` (Settings → Developer → Edit Config):
46
+
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "remark": {
51
+ "command": "npx",
52
+ "args": ["-y", "remark-mcp"],
53
+ "env": { "REMARK_TOKEN": "rmk_your_token_here" }
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ Restart Claude Desktop after editing.
60
+
61
+ ### Cursor
62
+
63
+ Add to `~/.cursor/mcp.json` (or the project `.cursor/mcp.json`):
64
+
65
+ ```json
66
+ {
67
+ "mcpServers": {
68
+ "remark": {
69
+ "command": "npx",
70
+ "args": ["-y", "remark-mcp"],
71
+ "env": { "REMARK_TOKEN": "rmk_your_token_here" }
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### VS Code
78
+
79
+ Add to your user or workspace MCP config:
80
+
81
+ ```json
82
+ {
83
+ "servers": {
84
+ "remark": {
85
+ "command": "npx",
86
+ "args": ["-y", "remark-mcp"],
87
+ "env": { "REMARK_TOKEN": "rmk_your_token_here" }
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ ### Run from source (development / pre-release)
94
+
95
+ Before the npm package is published you can run the server straight from a clone. Requires [Bun](https://bun.sh).
96
+
97
+ ```sh
98
+ git clone https://github.com/freddie-wheeler/remark-mcp.git
99
+ cd remark-mcp
100
+ bun install
101
+ # then point your client at the local entry:
102
+ claude mcp add remark --env REMARK_TOKEN=rmk_your_token_here -- bun run /absolute/path/to/remark-mcp/src/index.ts
103
+ ```
104
+
105
+ ## Creating a Bridge token
106
+
107
+ 1. Open Remark and go to **Settings → Bridge tokens**.
108
+ 2. Click **Create token**, give it a name (e.g. "Claude Code"), and confirm.
109
+ 3. **Copy the `rmk_…` value immediately** — it is shown exactly once and cannot be retrieved later. If you lose it, revoke it and create another.
110
+ 4. Provide it to your MCP client as `REMARK_TOKEN` (see the config blocks above).
111
+
112
+ To rotate: create a new token, update `REMARK_TOKEN`, reconnect the server, then revoke the old one.
113
+
114
+ ![Where to find Bridge tokens in Remark Settings](docs/bridge-token.png)
115
+
116
+ ## Tools
117
+
118
+ ### `remark_push`
119
+
120
+ Push a document into Remark for review. Triggers on *"push / send / transfer this to Remark"*.
121
+
122
+ | Arg | Required | Description |
123
+ | --- | --- | --- |
124
+ | `markdown` | Yes | The full document as markdown. |
125
+ | `title` | No | Defaults to the first heading or line. |
126
+ | `goal` | No | What you want from the review (e.g. "find the weak assumptions"). |
127
+ | `context_brief` | No (recommended) | A markdown summary of the session: decisions, rejected paths, open questions. Stored with the document. |
128
+
129
+ > *"Push this strategy memo to Remark with a brief covering what we decided and what we ruled out."*
130
+
131
+ ### `remark_list`
132
+
133
+ List your Remark documents, newest first (id, title, goal, lens, updated-at). No arguments.
134
+
135
+ > *"What documents do I have in Remark?"*
136
+
137
+ ### `remark_get`
138
+
139
+ Pull a document back as a composed artifact — header, context briefs, content, and the full provocation trail with statuses and replies. Triggers on *"get / pull / reference my Remark doc"* and *"what did Remark say about…"*.
140
+
141
+ | Arg | Required | Description |
142
+ | --- | --- | --- |
143
+ | `document_id` | Yes | The document to fetch (use `remark_list` to find it). |
144
+ | `format` | No | `markdown` (default) or `json`. |
145
+
146
+ > *"Pull document abc123 from Remark and tell me what was dismissed and why."*
147
+
148
+ ## Security & privacy
149
+
150
+ - **No telemetry.** This client sends nothing anywhere except your own Remark instance. It holds no state, writes no files, and keeps no logs of its own.
151
+ - **Token via env only.** `REMARK_TOKEN` is read from the environment, never accepted on the command line (which would leak it into process lists), and never written to any log.
152
+ - **Content stays between you and Remark.** Document content and context briefs flow only between your machine and your own Remark account. They are never logged by this server.
153
+ - **Consent.** Whether your sessions contribute to Remark's learning is governed by your Remark account's consent setting, not by this client.
154
+
155
+ ## Troubleshooting
156
+
157
+ - **"token invalid or revoked (401)"** — the token is wrong, expired, or revoked. Create a fresh one in Remark → Settings → Bridge tokens and update `REMARK_TOKEN`, then reconnect the server.
158
+ - **"could not reach Remark at …"** — check `REMARK_URL` (default `https://remark.freddie.nyc`) and your network. The error names the exact URL tried.
159
+ - **"document not found (404)"** — the `document_id` is wrong or belongs to another account. Run `remark_list` to get valid ids.
160
+ - **`npx` / Node not found** — the `npx` route needs Node 18+ on your PATH. Either install Node, or use the *Run from source* route above with Bun.
161
+ - **Server won't start** — if `REMARK_TOKEN` is unset the server exits immediately with a message pointing you to Settings. Confirm your client is passing the `env` block.
162
+
163
+ ## Development
164
+
165
+ ```sh
166
+ bun install
167
+ bun run typecheck # tsc --noEmit
168
+ bun run build # emits dist/
169
+ bun run start # run the server on stdio
170
+ ```
171
+
172
+ ## License
173
+
174
+ [MIT](LICENSE)
package/dist/client.js ADDED
@@ -0,0 +1,128 @@
1
+ // A failure whose `message` is safe to show the user as-is. Thrown for every
2
+ // mapped error condition so the server layer never has to interpret raw
3
+ // fetch/HTTP internals.
4
+ export class RemarkError extends Error {
5
+ }
6
+ export class RemarkClient {
7
+ token;
8
+ baseUrl;
9
+ constructor(config) {
10
+ this.token = config.token;
11
+ this.baseUrl = config.baseUrl;
12
+ }
13
+ // POST /api/bridge/documents — create, or update in place when document_id
14
+ // is supplied.
15
+ async push(input) {
16
+ const body = { markdown: input.markdown };
17
+ if (input.title)
18
+ body.title = input.title;
19
+ if (input.goal)
20
+ body.goal = input.goal;
21
+ if (input.lens_doc_type)
22
+ body.lens_doc_type = input.lens_doc_type;
23
+ if (input.lens_stage)
24
+ body.lens_stage = input.lens_stage;
25
+ if (input.context_brief)
26
+ body.context_brief = input.context_brief;
27
+ if (input.document_id)
28
+ body.document_id = input.document_id;
29
+ const res = await this.fetch('/api/bridge/documents', {
30
+ method: 'POST',
31
+ headers: { 'Content-Type': 'application/json' },
32
+ body: JSON.stringify(body),
33
+ });
34
+ if (!res.ok) {
35
+ throw await this.mapError(res, { documentId: input.document_id });
36
+ }
37
+ const data = (await this.json(res));
38
+ if (!data.id || !data.url) {
39
+ throw new RemarkError(`Remark returned an unexpected response to the push (HTTP ${res.status}).`);
40
+ }
41
+ return { id: data.id, url: data.url };
42
+ }
43
+ // GET /api/bridge/documents — the caller's documents, newest first.
44
+ async list() {
45
+ const res = await this.fetch('/api/bridge/documents', { method: 'GET' });
46
+ if (!res.ok) {
47
+ throw await this.mapError(res, {});
48
+ }
49
+ const data = (await this.json(res));
50
+ return data.documents ?? [];
51
+ }
52
+ // GET /api/bridge/documents/[id] — the composed read. `format: 'json'`
53
+ // returns the structured shape; markdown (default) returns the composed
54
+ // artifact as a single string.
55
+ async get(documentId, format) {
56
+ const suffix = format === 'json' ? '?format=json' : '';
57
+ const path = `/api/bridge/documents/${encodeURIComponent(documentId)}${suffix}`;
58
+ const res = await this.fetch(path, { method: 'GET' });
59
+ if (!res.ok) {
60
+ throw await this.mapError(res, { documentId });
61
+ }
62
+ if (format === 'json') {
63
+ return { format: 'json', body: await this.json(res) };
64
+ }
65
+ return { format: 'markdown', body: await res.text() };
66
+ }
67
+ // --- internals ----------------------------------------------------------
68
+ async fetch(path, init) {
69
+ const url = `${this.baseUrl}${path}`;
70
+ try {
71
+ return await fetch(url, {
72
+ ...init,
73
+ headers: {
74
+ ...(init.headers ?? {}),
75
+ Authorization: `Bearer ${this.token}`,
76
+ },
77
+ });
78
+ }
79
+ catch {
80
+ // Network-layer failure (DNS, connection refused, TLS). Name the URL
81
+ // tried so the user can spot a wrong REMARK_URL or an offline host.
82
+ throw new RemarkError(`Could not reach Remark at ${this.baseUrl}. Check REMARK_URL and your ` +
83
+ `network connection. (Tried ${init.method ?? 'GET'} ${url}.)`);
84
+ }
85
+ }
86
+ async json(res) {
87
+ try {
88
+ return await res.json();
89
+ }
90
+ catch {
91
+ throw new RemarkError(`Remark returned a response that could not be parsed as JSON (HTTP ${res.status}).`);
92
+ }
93
+ }
94
+ // Maps an HTTP error status to a user-facing message. 401 and 404 are
95
+ // product surfaces with specific guidance; everything else is reported with
96
+ // the status and any server-provided message, but never a stack trace.
97
+ async mapError(res, ctx) {
98
+ if (res.status === 401) {
99
+ return new RemarkError('Remark rejected the token (401): it is invalid or has been revoked. ' +
100
+ 'Create a new one in Remark → Settings → Bridge tokens and set it as ' +
101
+ 'REMARK_TOKEN.');
102
+ }
103
+ if (res.status === 404) {
104
+ const which = ctx.documentId ? ` "${ctx.documentId}"` : '';
105
+ return new RemarkError(`Remark could not find that document${which} (404): the id does not exist or does not belong to your account.`);
106
+ }
107
+ // Other statuses: surface the server's message when present, else a
108
+ // generic line. Read the body defensively — it may be JSON or text.
109
+ let detail = '';
110
+ try {
111
+ const text = await res.text();
112
+ if (text) {
113
+ try {
114
+ const parsed = JSON.parse(text);
115
+ detail = parsed.error ?? text;
116
+ }
117
+ catch {
118
+ detail = text;
119
+ }
120
+ }
121
+ }
122
+ catch {
123
+ // ignore — no readable body
124
+ }
125
+ const suffix = detail ? ` — ${detail}` : '';
126
+ return new RemarkError(`Remark request failed (HTTP ${res.status})${suffix}.`);
127
+ }
128
+ }
package/dist/config.js ADDED
@@ -0,0 +1,30 @@
1
+ // Configuration is env-only. Tokens are NEVER accepted via argv (they would
2
+ // leak into process lists) and NEVER logged.
3
+ export const DEFAULT_REMARK_URL = 'https://remark.freddie.nyc';
4
+ // The eight lens document types the Remark app recognises (mirrors
5
+ // `DOC_TYPES` in the remark repo). `lens_stage` is free text, not an enum.
6
+ export const LENS_DOC_TYPES = [
7
+ 'strategy-memo',
8
+ 'prd',
9
+ 'decision-memo',
10
+ 'launch-plan',
11
+ 'postmortem',
12
+ 'vision-belief',
13
+ 'essay',
14
+ 'other',
15
+ ];
16
+ // Reads and validates configuration from the environment. Throws with a
17
+ // user-facing message (no token echoed) when REMARK_TOKEN is absent so the
18
+ // server fails fast at startup.
19
+ export function loadConfig(env = process.env) {
20
+ const token = env.REMARK_TOKEN?.trim();
21
+ if (!token) {
22
+ throw new Error('REMARK_TOKEN is not set. Create a Bridge token in Remark → Settings → ' +
23
+ 'Bridge tokens, then provide it to this server as the REMARK_TOKEN ' +
24
+ 'environment variable (never on the command line).');
25
+ }
26
+ const rawUrl = env.REMARK_URL?.trim() || DEFAULT_REMARK_URL;
27
+ // Strip any trailing slash so path joins are predictable.
28
+ const baseUrl = rawUrl.replace(/\/+$/, '');
29
+ return { token, baseUrl };
30
+ }
package/dist/index.js ADDED
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { z } from 'zod';
5
+ import { RemarkClient, RemarkError } from './client.js';
6
+ import { LENS_DOC_TYPES, loadConfig } from './config.js';
7
+ // remark-mcp — a stdio MCP server that connects Claude to Remark. It is a thin
8
+ // wrapper over the Remark Bridge API: push a document in (optionally with a
9
+ // context brief carrying the session's reasoning), list your documents, and
10
+ // pull any document back as a composed markdown artifact including its full
11
+ // provocation trail.
12
+ //
13
+ // This process speaks JSON-RPC over stdout — nothing else may be written
14
+ // there. All diagnostics go to stderr via console.error, and never include
15
+ // document content or token material.
16
+ const VERSION = '0.1.0';
17
+ // Wraps a client call so every thrown error reaches the model as a clean,
18
+ // actionable message (isError result) rather than a transport-level failure.
19
+ async function toolResult(fn) {
20
+ try {
21
+ return { content: [{ type: 'text', text: await fn() }] };
22
+ }
23
+ catch (err) {
24
+ const message = err instanceof RemarkError
25
+ ? err.message
26
+ : 'Unexpected error talking to Remark. Check REMARK_URL and that the ' +
27
+ 'Remark service is reachable.';
28
+ return { content: [{ type: 'text', text: message }], isError: true };
29
+ }
30
+ }
31
+ function buildServer(config) {
32
+ const client = new RemarkClient(config);
33
+ const server = new McpServer({ name: 'remark-mcp', version: VERSION });
34
+ // --- remark_push -------------------------------------------------------
35
+ server.registerTool('remark_push', {
36
+ title: 'Push a document to Remark',
37
+ description: 'Push, send, or transfer a document to Remark for provocation-driven ' +
38
+ 'review. Use this when the user says to "push/send/transfer this to ' +
39
+ 'Remark", or wants a piece of writing (a memo, PRD, strategy doc, ' +
40
+ 'essay, proposal) reviewed in Remark. Pass the full document as ' +
41
+ 'markdown. Strongly prefer to include a `context_brief`: a short ' +
42
+ 'summary of the session that produced the document — what was decided, ' +
43
+ 'which paths were rejected and why, and any open questions — so the ' +
44
+ "reviewer inherits the session's reasoning. Supply `document_id` to " +
45
+ 'update a document you pushed earlier instead of creating a new one.',
46
+ inputSchema: {
47
+ markdown: z
48
+ .string()
49
+ .describe('The full document content as markdown. Required.'),
50
+ title: z
51
+ .string()
52
+ .optional()
53
+ .describe('Document title. Defaults to the first heading or line if omitted.'),
54
+ goal: z
55
+ .string()
56
+ .optional()
57
+ .describe('What the author wants from this document — e.g. "sharpen the ' +
58
+ 'argument", "find the weak assumptions". Guides the review.'),
59
+ lens_doc_type: z
60
+ .enum(LENS_DOC_TYPES)
61
+ .optional()
62
+ .describe(`The kind of document, which tunes the review lens. One of: ${LENS_DOC_TYPES.join(', ')}. Omit to let Remark infer it.`),
63
+ lens_stage: z
64
+ .string()
65
+ .optional()
66
+ .describe('Free-text maturity stage — e.g. "early exploration", ' +
67
+ '"pre-users", "ready to ship". Optional.'),
68
+ context_brief: z
69
+ .string()
70
+ .optional()
71
+ .describe('A markdown summary of the session that produced this document: ' +
72
+ 'key decisions, rejected alternatives and why, and open ' +
73
+ 'questions. Highly recommended — it is stored with the document ' +
74
+ 'and gives the review its reasoning trail.'),
75
+ document_id: z
76
+ .string()
77
+ .optional()
78
+ .describe('The id of an existing Remark document to update in place. Omit ' +
79
+ 'to create a new document.'),
80
+ },
81
+ }, (args) => toolResult(async () => {
82
+ const { id, url } = await client.push(args);
83
+ const verb = args.document_id ? 'Updated' : 'Pushed to';
84
+ return `${verb} Remark. Document id: ${id}\nOpen it: ${url}`;
85
+ }));
86
+ // --- remark_list -------------------------------------------------------
87
+ server.registerTool('remark_list', {
88
+ title: 'List your Remark documents',
89
+ description: "List the documents in the user's Remark account, newest first. Use " +
90
+ "this to find a document's id before pulling it back with remark_get, " +
91
+ 'or when the user asks "what\'s in my Remark", "what documents do I ' +
92
+ 'have in Remark", or wants to pick one to reference.',
93
+ inputSchema: {},
94
+ }, () => toolResult(async () => {
95
+ const docs = await client.list();
96
+ if (docs.length === 0) {
97
+ return 'No documents in your Remark account yet.';
98
+ }
99
+ const lines = docs.map((d) => {
100
+ const title = d.title?.trim() || 'Untitled';
101
+ const parts = [`- ${title} — id: ${d.id}`];
102
+ if (d.goal?.trim())
103
+ parts.push(` goal: ${d.goal.trim()}`);
104
+ if (d.lens?.trim())
105
+ parts.push(` lens: ${d.lens.trim()}`);
106
+ if (d.updated_at)
107
+ parts.push(` updated: ${d.updated_at}`);
108
+ return parts.join('\n');
109
+ });
110
+ return `${docs.length} document(s):\n${lines.join('\n')}`;
111
+ }));
112
+ // --- remark_get --------------------------------------------------------
113
+ server.registerTool('remark_get', {
114
+ title: 'Get a Remark document with its thinking trail',
115
+ description: 'Get, pull, or reference a Remark document as a composed markdown ' +
116
+ 'artifact: its header, any context briefs, the content, and the full ' +
117
+ 'provocation trail — every remark with its status (kept, dismissed ' +
118
+ 'with reason, suggested, accepted, rejected, resolved) and replies. ' +
119
+ 'Use this when the user says "get/pull/reference my Remark doc", or ' +
120
+ 'asks "what did Remark say about…", "what did the provocations ' +
121
+ 'raise", or "what was dismissed and why". Find the id first with ' +
122
+ 'remark_list if you do not have it.',
123
+ inputSchema: {
124
+ document_id: z
125
+ .string()
126
+ .describe('The id of the Remark document to fetch. Required.'),
127
+ format: z
128
+ .enum(['markdown', 'json'])
129
+ .optional()
130
+ .describe('Output format. "markdown" (default) returns the composed ' +
131
+ 'artifact; "json" returns the same data structured.'),
132
+ },
133
+ }, (args) => toolResult(async () => {
134
+ const format = args.format ?? 'markdown';
135
+ const result = await client.get(args.document_id, format);
136
+ if (result.format === 'json') {
137
+ return JSON.stringify(result.body, null, 2);
138
+ }
139
+ return result.body;
140
+ }));
141
+ return server;
142
+ }
143
+ async function main() {
144
+ let config;
145
+ try {
146
+ config = loadConfig();
147
+ }
148
+ catch (err) {
149
+ // Fail fast with a clear message on stderr; do not start the transport.
150
+ console.error(`remark-mcp: ${err instanceof Error ? err.message : String(err)}`);
151
+ process.exit(1);
152
+ }
153
+ const server = buildServer(config);
154
+ const transport = new StdioServerTransport();
155
+ await server.connect(transport);
156
+ console.error(`remark-mcp ${VERSION} ready (target ${config.baseUrl}). Waiting for requests on stdio.`);
157
+ }
158
+ main().catch((err) => {
159
+ console.error(`remark-mcp: fatal — ${err instanceof Error ? err.message : String(err)}`);
160
+ process.exit(1);
161
+ });
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "remark-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Connect Claude to Remark — push documents in for provocation-driven review, pull them back with their full thinking trail.",
5
+ "license": "MIT",
6
+ "author": "Freddie Wheeler",
7
+ "mcpName": "io.github.freddie-wheeler/remark-mcp",
8
+ "type": "module",
9
+ "homepage": "https://github.com/freddie-wheeler/remark-mcp#readme",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/freddie-wheeler/remark-mcp.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/freddie-wheeler/remark-mcp/issues"
16
+ },
17
+ "keywords": [
18
+ "mcp",
19
+ "model-context-protocol",
20
+ "remark",
21
+ "claude"
22
+ ],
23
+ "bin": {
24
+ "remark-mcp": "dist/index.js"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "scripts": {
35
+ "build": "tsc",
36
+ "typecheck": "tsc --noEmit",
37
+ "lint": "biome check src/",
38
+ "format": "biome check --write src/",
39
+ "start": "bun run src/index.ts",
40
+ "dev": "bun run src/index.ts",
41
+ "prepublishOnly": "bun run build"
42
+ },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.12.0",
45
+ "zod": "^3.23.8"
46
+ },
47
+ "devDependencies": {
48
+ "@biomejs/biome": "1.9.4",
49
+ "@types/node": "^22.0.0",
50
+ "typescript": "^5.5.0"
51
+ }
52
+ }