@linwheel/mcp-server 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.
Files changed (68) hide show
  1. package/README.md +219 -0
  2. package/dist/client.d.ts +24 -0
  3. package/dist/client.js +36 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +79 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/tools/analyze.d.ts +3 -0
  9. package/dist/tools/analyze.js +16 -0
  10. package/dist/tools/analyze.js.map +1 -0
  11. package/dist/tools/brand-styles.d.ts +3 -0
  12. package/dist/tools/brand-styles.js +108 -0
  13. package/dist/tools/brand-styles.js.map +1 -0
  14. package/dist/tools/bundle.d.ts +3 -0
  15. package/dist/tools/bundle.js +55 -0
  16. package/dist/tools/bundle.js.map +1 -0
  17. package/dist/tools/draft.d.ts +3 -0
  18. package/dist/tools/draft.js +41 -0
  19. package/dist/tools/draft.js.map +1 -0
  20. package/dist/tools/index.d.ts +3 -0
  21. package/dist/tools/index.js +46 -0
  22. package/dist/tools/index.js.map +1 -0
  23. package/dist/tools/post-approve.d.ts +3 -0
  24. package/dist/tools/post-approve.js +13 -0
  25. package/dist/tools/post-approve.js.map +1 -0
  26. package/dist/tools/post-carousel-companion.d.ts +3 -0
  27. package/dist/tools/post-carousel-companion.js +47 -0
  28. package/dist/tools/post-carousel-companion.js.map +1 -0
  29. package/dist/tools/post-carousel-schedule.d.ts +3 -0
  30. package/dist/tools/post-carousel-schedule.js +48 -0
  31. package/dist/tools/post-carousel-schedule.js.map +1 -0
  32. package/dist/tools/post-carousel-status.d.ts +3 -0
  33. package/dist/tools/post-carousel-status.js +12 -0
  34. package/dist/tools/post-carousel-status.js.map +1 -0
  35. package/dist/tools/post-carousel.d.ts +3 -0
  36. package/dist/tools/post-carousel.js +28 -0
  37. package/dist/tools/post-carousel.js.map +1 -0
  38. package/dist/tools/post-comment.d.ts +3 -0
  39. package/dist/tools/post-comment.js +68 -0
  40. package/dist/tools/post-comment.js.map +1 -0
  41. package/dist/tools/post-get.d.ts +3 -0
  42. package/dist/tools/post-get.js +12 -0
  43. package/dist/tools/post-get.js.map +1 -0
  44. package/dist/tools/post-image.d.ts +3 -0
  45. package/dist/tools/post-image.js +25 -0
  46. package/dist/tools/post-image.js.map +1 -0
  47. package/dist/tools/post-schedule.d.ts +3 -0
  48. package/dist/tools/post-schedule.js +20 -0
  49. package/dist/tools/post-schedule.js.map +1 -0
  50. package/dist/tools/post-update.d.ts +3 -0
  51. package/dist/tools/post-update.js +22 -0
  52. package/dist/tools/post-update.js.map +1 -0
  53. package/dist/tools/posts-list.d.ts +3 -0
  54. package/dist/tools/posts-list.js +45 -0
  55. package/dist/tools/posts-list.js.map +1 -0
  56. package/dist/tools/refine.d.ts +3 -0
  57. package/dist/tools/refine.js +34 -0
  58. package/dist/tools/refine.js.map +1 -0
  59. package/dist/tools/reshape.d.ts +3 -0
  60. package/dist/tools/reshape.js +42 -0
  61. package/dist/tools/reshape.js.map +1 -0
  62. package/dist/tools/split.d.ts +3 -0
  63. package/dist/tools/split.js +29 -0
  64. package/dist/tools/split.js.map +1 -0
  65. package/dist/tools/voice-profiles.d.ts +3 -0
  66. package/dist/tools/voice-profiles.js +63 -0
  67. package/dist/tools/voice-profiles.js.map +1 -0
  68. package/package.json +47 -0
package/README.md ADDED
@@ -0,0 +1,219 @@
1
+ <div align="center">
2
+
3
+ <img src="https://img.shields.io/badge/MCP-Model_Context_Protocol-5A67D8?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0tMiAxNWwtNS01IDEuNDEtMS40MUwxMCAxNC4xN2w3LjU5LTcuNTlMMTkgOGwtOSA5eiIvPjwvc3ZnPg==&logoColor=white" alt="MCP" />
4
+
5
+ # @linwheel/mcp-server
6
+
7
+ ### Draft, schedule, and publish LinkedIn posts from Claude.
8
+
9
+ [![npm](https://img.shields.io/badge/npm-@linwheel/mcp--server-CB3837?style=for-the-badge&logo=npm&logoColor=white)](https://www.npmjs.com/package/@linwheel/mcp-server)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)
12
+
13
+ **Your LinkedIn publishing workflow, inside your editor.**
14
+
15
+ [Quick Start](#-quick-start) · [Tools](#-tools) · [Configuration](#-configuration) · [Dashboard](https://www.linwheel.io/dashboard)
16
+
17
+ ---
18
+
19
+ </div>
20
+
21
+ ## The Problem
22
+
23
+ You write in your editor. You think in your editor. But to publish on LinkedIn, you context-switch to a browser, reformat everything, schedule manually, and hope you remembered to add that follow-up comment.
24
+
25
+ ## The Solution
26
+
27
+ **LinWheel MCP** brings LinkedIn publishing into Claude Code. Draft a post, generate a carousel, schedule it for Tuesday at 9:45 AM, attach a follow-up comment — all without leaving your terminal.
28
+
29
+ > *"Draft a post about our new API design patterns. Schedule it for tomorrow at 10 AM. Add a carousel. Attach a comment with the docs link at T+30."*
30
+
31
+ One prompt. Four tools. Zero context switches.
32
+
33
+ ---
34
+
35
+ ## ⚡ Quick Start
36
+
37
+ ### 1. Install
38
+
39
+ ```bash
40
+ npx -y @linwheel/mcp-server
41
+ ```
42
+
43
+ ### 2. Get an API key
44
+
45
+ 1. **Sign up** at [linwheel.io/login](https://www.linwheel.io/login)
46
+ 2. Go to **[Settings](https://www.linwheel.io/settings)**
47
+ 3. Click **Generate API Key** — copy the `lw_sk_...` key (shown once)
48
+
49
+ ### 3. Configure Claude Code
50
+
51
+ ```bash
52
+ claude mcp add linwheel -- npx -y @linwheel/mcp-server
53
+ ```
54
+
55
+ Then add your key to `.mcp.json`:
56
+
57
+ ```json
58
+ {
59
+ "mcpServers": {
60
+ "linwheel": {
61
+ "command": "npx",
62
+ "args": ["-y", "@linwheel/mcp-server"],
63
+ "env": {
64
+ "LINWHEEL_API_KEY": "lw_sk_your_key_here"
65
+ }
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### 4. Verify
72
+
73
+ Restart Claude Code, then:
74
+
75
+ > *"List my LinWheel posts"*
76
+
77
+ If it works, you're in.
78
+
79
+ ---
80
+
81
+ ## 🛠️ Tools
82
+
83
+ 22 tools across 5 categories. Claude picks the right one — just describe what you want.
84
+
85
+ ### Content Creation
86
+
87
+ | Tool | What it does |
88
+ |------|-------------|
89
+ | `linwheel_draft` | Create a post draft |
90
+ | `linwheel_analyze` | Score text for LinkedIn potential |
91
+ | `linwheel_reshape` | Reshape content through 7 angles (contrarian, field note, etc.) |
92
+ | `linwheel_refine` | Polish text with light / medium / heavy intensity |
93
+ | `linwheel_split` | Break long content into a post series |
94
+ | `linwheel_bundle` | Create post + image + carousel atomically |
95
+
96
+ ### Publishing
97
+
98
+ | Tool | What it does |
99
+ |------|-------------|
100
+ | `linwheel_posts_list` | List drafts with filters |
101
+ | `linwheel_post_get` | Get full post details |
102
+ | `linwheel_post_update` | Edit post text and settings |
103
+ | `linwheel_post_approve` | Approve for auto-publish |
104
+ | `linwheel_post_schedule` | Set publication time |
105
+
106
+ ### Visual Content
107
+
108
+ | Tool | What it does |
109
+ |------|-------------|
110
+ | `linwheel_post_image` | Generate cover image |
111
+ | `linwheel_post_carousel` | Create typographic carousel slides + PDF |
112
+ | `linwheel_post_carousel_companion` | Auto-generate carousel from post text |
113
+ | `linwheel_post_carousel_status` | Check carousel state |
114
+ | `linwheel_post_carousel_schedule` | Schedule carousel publication |
115
+
116
+ ### Engagement
117
+
118
+ | Tool | What it does |
119
+ |------|-------------|
120
+ | `linwheel_post_comment` | Attach auto-firing follow-up comment |
121
+ | `linwheel_post_comments_list` | List queued comments |
122
+ | `linwheel_post_comment_update` | Edit a scheduled comment |
123
+ | `linwheel_post_comment_delete` | Remove a scheduled comment |
124
+
125
+ ### Configuration
126
+
127
+ | Tool | What it does |
128
+ |------|-------------|
129
+ | `linwheel_voice_profiles` | Manage writing voice profiles |
130
+ | `linwheel_brand_styles` | Manage brand style templates |
131
+
132
+ ---
133
+
134
+ ## 🔧 Configuration
135
+
136
+ ### Environment Variables
137
+
138
+ | Variable | Required | Default | Description |
139
+ |----------|:--------:|---------|-------------|
140
+ | `LINWHEEL_API_KEY` | ✅ | — | Your API key (`lw_sk_...`) |
141
+ | `LINWHEEL_BASE_URL` | | `https://www.linwheel.io` | API endpoint |
142
+ | `LINWHEEL_SIGNING_SECRET` | | — | HMAC signing secret (optional) |
143
+ | `LINWHEEL_TIMEOUT_MS` | | `60000` | Request timeout in ms |
144
+
145
+ ### Claude Desktop
146
+
147
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
148
+
149
+ ```json
150
+ {
151
+ "mcpServers": {
152
+ "linwheel": {
153
+ "command": "npx",
154
+ "args": ["-y", "@linwheel/mcp-server"],
155
+ "env": {
156
+ "LINWHEEL_API_KEY": "lw_sk_your_key_here"
157
+ }
158
+ }
159
+ }
160
+ }
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 🚀 No API Key?
166
+
167
+ Start without a key and the server exposes a single `linwheel_setup` tool that walks you through setup. Just ask Claude:
168
+
169
+ > *"Help me set up LinWheel"*
170
+
171
+ ---
172
+
173
+ ## 📋 Example Workflows
174
+
175
+ **Daily publishing habit:**
176
+ > *"Draft a field note about today's debugging session. Schedule for tomorrow at 9 AM ET. Generate a carousel. Add a comment with the repo link at T+30."*
177
+
178
+ **Content planning:**
179
+ > *"Show me all scheduled posts for next week. Move the Tuesday post to Thursday. Add a contrarian follow-up for Wednesday."*
180
+
181
+ **Batch creation:**
182
+ > *"Take this blog post and reshape it into 3 LinkedIn angles. Draft all three. Schedule them for Mon/Wed/Fri at 10 AM."*
183
+
184
+ ---
185
+
186
+ ## 🏗️ Architecture
187
+
188
+ ```
189
+ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
190
+ │ Claude Code │────▶│ MCP Server │────▶│ LinWheel API │
191
+ │ (your editor) │◀────│ (stdio) │◀────│ (linwheel.io) │
192
+ └─────────────────┘ └──────────────────┘ └────────┬────────┘
193
+
194
+ ┌────────▼────────┐
195
+ │ LinkedIn │
196
+ │ (publish) │
197
+ └─────────────────┘
198
+ ```
199
+
200
+ The MCP server is a thin client. All heavy lifting (LLM calls, image generation, PDF compilation, LinkedIn API) happens server-side on LinWheel.
201
+
202
+ ---
203
+
204
+ ## 📖 Links
205
+
206
+ | Resource | URL |
207
+ |----------|-----|
208
+ | **Dashboard** | [linwheel.io/dashboard](https://www.linwheel.io/dashboard) |
209
+ | **Full Setup Guide** | [docs/mcp-setup.md](https://github.com/Peleke/linwheel/blob/main/docs/mcp-setup.md) |
210
+ | **Agent API Reference** | [docs/agent-api-setup.md](https://github.com/Peleke/linwheel/blob/main/docs/agent-api-setup.md) |
211
+ | **GitHub** | [github.com/Peleke/linwheel](https://github.com/Peleke/linwheel) |
212
+
213
+ ---
214
+
215
+ <div align="center">
216
+
217
+ Built with [Model Context Protocol](https://modelcontextprotocol.io) · Powered by [Claude](https://claude.ai)
218
+
219
+ </div>
@@ -0,0 +1,24 @@
1
+ /**
2
+ * MCP client — thin wrapper over @linwheel/sdk.
3
+ *
4
+ * Preserves the .post()/.get()/.patch()/.delete() convenience methods
5
+ * used by individual MCP tool handlers while delegating all HTTP logic
6
+ * to the SDK.
7
+ */
8
+ import { LinWheelApiError } from "@linwheel/sdk";
9
+ export { LinWheelApiError };
10
+ export interface LinWheelClientConfig {
11
+ baseUrl: string;
12
+ apiKey: string;
13
+ signingSecret?: string;
14
+ timeoutMs?: number;
15
+ }
16
+ export declare class LinWheelClient {
17
+ private sdk;
18
+ constructor(config: LinWheelClientConfig);
19
+ post<T = unknown>(path: string, body: unknown): Promise<T>;
20
+ get<T = unknown>(path: string, query?: Record<string, string>): Promise<T>;
21
+ put<T = unknown>(path: string, body: unknown): Promise<T>;
22
+ patch<T = unknown>(path: string, body: unknown): Promise<T>;
23
+ delete<T = unknown>(path: string): Promise<T>;
24
+ }
package/dist/client.js ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * MCP client — thin wrapper over @linwheel/sdk.
3
+ *
4
+ * Preserves the .post()/.get()/.patch()/.delete() convenience methods
5
+ * used by individual MCP tool handlers while delegating all HTTP logic
6
+ * to the SDK.
7
+ */
8
+ import { LinWheel, LinWheelApiError, } from "@linwheel/sdk";
9
+ export { LinWheelApiError };
10
+ export class LinWheelClient {
11
+ sdk;
12
+ constructor(config) {
13
+ this.sdk = new LinWheel({
14
+ apiKey: config.apiKey,
15
+ baseUrl: config.baseUrl,
16
+ signingSecret: config.signingSecret,
17
+ timeoutMs: config.timeoutMs,
18
+ });
19
+ }
20
+ post(path, body) {
21
+ return this.sdk.request("POST", path, body);
22
+ }
23
+ get(path, query) {
24
+ return this.sdk.request("GET", path, undefined, query);
25
+ }
26
+ put(path, body) {
27
+ return this.sdk.request("PUT", path, body);
28
+ }
29
+ patch(path, body) {
30
+ return this.sdk.request("PATCH", path, body);
31
+ }
32
+ delete(path) {
33
+ return this.sdk.request("DELETE", path);
34
+ }
35
+ }
36
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,QAAQ,EACR,gBAAgB,GAEjB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAS5B,MAAM,OAAO,cAAc;IACjB,GAAG,CAAW;IAEtB,YAAY,MAA4B;QACtC,IAAI,CAAC,GAAG,GAAG,IAAI,QAAQ,CAAC;YACtB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAc,IAAY,EAAE,IAAa;QAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,GAAG,CACD,IAAY,EACZ,KAA8B;QAE9B,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC;IAED,GAAG,CAAc,IAAY,EAAE,IAAa;QAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAc,IAAY,EAAE,IAAa;QAC5C,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,CAAc,IAAY;QAC9B,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,79 @@
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 { LinWheelClient } from "./client.js";
5
+ import { registerAllTools } from "./tools/index.js";
6
+ const BASE_URL = process.env.LINWHEEL_BASE_URL ?? "https://www.linwheel.io";
7
+ const API_KEY = process.env.LINWHEEL_API_KEY;
8
+ const server = new McpServer({ name: "linwheel", version: "0.1.0" }, { capabilities: { tools: {} } });
9
+ if (API_KEY) {
10
+ const client = new LinWheelClient({
11
+ baseUrl: BASE_URL,
12
+ apiKey: API_KEY,
13
+ signingSecret: process.env.LINWHEEL_SIGNING_SECRET,
14
+ timeoutMs: parseInt(process.env.LINWHEEL_TIMEOUT_MS ?? "60000", 10),
15
+ });
16
+ registerAllTools(server, client);
17
+ }
18
+ else {
19
+ // No API key — register setup tool only
20
+ server.tool("linwheel_setup", "Get started with LinWheel. Returns step-by-step instructions to create an account and configure your API key. Call this first.", {}, async () => {
21
+ const configExample = JSON.stringify({
22
+ mcpServers: {
23
+ linwheel: {
24
+ command: "npx",
25
+ args: ["-y", "@linwheel/mcp-server"],
26
+ env: {
27
+ LINWHEEL_API_KEY: "lw_sk_paste_your_key_here",
28
+ },
29
+ },
30
+ },
31
+ }, null, 2);
32
+ return {
33
+ content: [
34
+ {
35
+ type: "text",
36
+ text: [
37
+ "# LinWheel Setup",
38
+ "",
39
+ "No API key configured. Follow these steps:",
40
+ "",
41
+ "## 1. Create an account",
42
+ `Sign up at: ${BASE_URL}/login`,
43
+ "",
44
+ "## 2. Generate an API key",
45
+ `Go to: ${BASE_URL}/settings`,
46
+ 'Click "Generate API Key" and copy the key (starts with lw_sk_).',
47
+ "The key is shown only once — save it somewhere safe.",
48
+ "",
49
+ "## 3. Add to your MCP config",
50
+ "",
51
+ "**Claude Code** — run this in your terminal:",
52
+ "```",
53
+ 'claude mcp add linwheel -- npx -y @linwheel/mcp-server',
54
+ "```",
55
+ "Then set the env var in your `.mcp.json`:",
56
+ "",
57
+ "```json",
58
+ configExample,
59
+ "```",
60
+ "",
61
+ "## 4. Restart Claude Code",
62
+ "After updating config, restart Claude Code to load LinWheel tools.",
63
+ "",
64
+ "## 5. Verify",
65
+ 'Ask Claude: "List my LinWheel posts" — if it works, you\'re all set.',
66
+ "",
67
+ `Dashboard: ${BASE_URL}/dashboard`,
68
+ `Docs: https://github.com/Peleke/linwheel/tree/main/mcp#readme`,
69
+ ].join("\n"),
70
+ },
71
+ ],
72
+ };
73
+ });
74
+ console.error("LinWheel MCP: No LINWHEEL_API_KEY set. Only linwheel_setup tool available.");
75
+ }
76
+ const transport = new StdioServerTransport();
77
+ await server.connect(transport);
78
+ console.error("LinWheel MCP server running on stdio");
79
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,yBAAyB,CAAC;AAC5E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;AAE7C,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,EACtC,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAC;AAEF,IAAI,OAAO,EAAE,CAAC;IACZ,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC;QAChC,OAAO,EAAE,QAAQ;QACjB,MAAM,EAAE,OAAO;QACf,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,uBAAuB;QAClD,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,OAAO,EAAE,EAAE,CAAC;KACpE,CAAC,CAAC;IAEH,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACnC,CAAC;KAAM,CAAC;IACN,wCAAwC;IACxC,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,gIAAgI,EAChI,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAClC;YACE,UAAU,EAAE;gBACV,QAAQ,EAAE;oBACR,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,CAAC,IAAI,EAAE,sBAAsB,CAAC;oBACpC,GAAG,EAAE;wBACH,gBAAgB,EAAE,2BAA2B;qBAC9C;iBACF;aACF;SACF,EACD,IAAI,EACJ,CAAC,CACF,CAAC;QAEF,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE;wBACJ,kBAAkB;wBAClB,EAAE;wBACF,4CAA4C;wBAC5C,EAAE;wBACF,yBAAyB;wBACzB,eAAe,QAAQ,QAAQ;wBAC/B,EAAE;wBACF,2BAA2B;wBAC3B,UAAU,QAAQ,WAAW;wBAC7B,iEAAiE;wBACjE,sDAAsD;wBACtD,EAAE;wBACF,8BAA8B;wBAC9B,EAAE;wBACF,8CAA8C;wBAC9C,KAAK;wBACL,wDAAwD;wBACxD,KAAK;wBACL,2CAA2C;wBAC3C,EAAE;wBACF,SAAS;wBACT,aAAa;wBACb,KAAK;wBACL,EAAE;wBACF,2BAA2B;wBAC3B,oEAAoE;wBACpE,EAAE;wBACF,cAAc;wBACd,sEAAsE;wBACtE,EAAE;wBACF,cAAc,QAAQ,YAAY;wBAClC,+DAA+D;qBAChE,CAAC,IAAI,CAAC,IAAI,CAAC;iBACb;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,OAAO,CAAC,KAAK,CACX,4EAA4E,CAC7E,CAAC;AACJ,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAEhC,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LinWheelClient } from "../client.js";
3
+ export declare function register(server: McpServer, client: LinWheelClient): void;
@@ -0,0 +1,16 @@
1
+ import { z } from "zod";
2
+ export function register(server, client) {
3
+ server.tool("linwheel_analyze", "Analyze text content for LinkedIn posting potential. Returns topic relevance scores, suggested angles with hook ideas, a LinkedIn fit score (1-10), extractable insights, and recommended post count. Use this first to understand what angles work best for the content.", {
4
+ text: z.string().describe("The text content to analyze"),
5
+ context: z
6
+ .string()
7
+ .optional()
8
+ .describe('Optional context about the content, e.g. "buildlog entry about shipping an MCP server"'),
9
+ }, async ({ text, context }) => {
10
+ const result = await client.post("/api/agent/analyze", { text, context });
11
+ return {
12
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
13
+ };
14
+ });
15
+ }
16
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/tools/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,QAAQ,CAAC,MAAiB,EAAE,MAAsB;IAChE,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,2QAA2Q,EAC3Q;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;QACxD,OAAO,EAAE,CAAC;aACP,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,wFAAwF,CACzF;KACJ,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;QAC1B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC1E,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC5E,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LinWheelClient } from "../client.js";
3
+ export declare function register(server: McpServer, client: LinWheelClient): void;
@@ -0,0 +1,108 @@
1
+ import { z } from "zod";
2
+ const colorSchema = z.object({
3
+ hex: z.string().regex(/^#[0-9A-Fa-f]{6}$/).describe("Hex color code (e.g. '#4DCFC9')"),
4
+ name: z.string().optional().describe("Human-readable color name (e.g. 'Teal Accent')"),
5
+ usage: z.enum(["primary", "secondary", "accent", "background"]).optional().describe("How this color is used"),
6
+ });
7
+ export function register(server, client) {
8
+ // List brand styles
9
+ server.tool("linwheel_brand_styles_list", "List all brand style profiles. Shows which profile is currently active. The active brand style controls image generation: 'branded_typographic' renders locally with SVG patterns (zero T2I calls), other approaches use AI image generation.", {}, async () => {
10
+ const result = await client.get("/api/agent/brand-styles");
11
+ return {
12
+ content: [
13
+ { type: "text", text: JSON.stringify(result, null, 2) },
14
+ ],
15
+ };
16
+ });
17
+ // Create brand style
18
+ server.tool("linwheel_brand_style_create", `Create a brand style profile for consistent visual identity across all generated images and carousels.
19
+
20
+ Key field: imageryApproach — set to 'branded_typographic' for black-background renders with SVG pattern textures (zero T2I API calls). Other options ('photography', 'illustration', 'abstract', '3d_render', 'mixed') use AI image generation.
21
+
22
+ For branded_typographic, also set patternPreset ('dot_grid', 'circuit_lines', or 'diagonal_hash') and primaryColors with at least one accent color.`, {
23
+ name: z.string().describe("Name for the brand style (e.g. 'Endstation Dark')"),
24
+ imageryApproach: z.enum([
25
+ "photography", "illustration", "abstract", "3d_render", "mixed", "branded_typographic",
26
+ ]).describe("Visual style approach. Use 'branded_typographic' for black-bg + SVG pattern renders (no T2I calls)"),
27
+ primaryColors: z.array(colorSchema).min(1).describe("Primary brand colors (at least 1). For branded_typographic, the accent color is used for pattern overlays."),
28
+ isActive: z.boolean().optional().default(true).describe("Set as the active brand style (default true). Only one can be active."),
29
+ patternPreset: z.enum(["dot_grid", "circuit_lines", "diagonal_hash", "none"]).optional().describe("SVG pattern preset for branded_typographic (default: 'dot_grid')"),
30
+ logoUrl: z.string().optional().describe("URL to logo image (must be from allowed domains)"),
31
+ logoPosition: z.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).optional().describe("Logo placement on images (default: 'bottom-right')"),
32
+ logoScale: z.enum(["small", "medium", "large"]).optional().describe("Logo size (default: 'medium')"),
33
+ description: z.string().optional().describe("Description or notes about this style"),
34
+ colorMood: z.string().optional().describe("Color mood description (e.g. 'warm and energetic')"),
35
+ stylePrefix: z.string().optional().describe("Prefix added to all image prompts"),
36
+ styleSuffix: z.string().optional().describe("Suffix added to all image prompts"),
37
+ moodDescriptors: z.array(z.string()).optional().describe("Mood keywords (e.g. ['bold', 'minimal', 'tech'])"),
38
+ negativeConcepts: z.array(z.string()).optional().describe("Things to avoid in generated images"),
39
+ }, async (params) => {
40
+ const result = await client.post("/api/agent/brand-styles", params);
41
+ return {
42
+ content: [
43
+ { type: "text", text: JSON.stringify(result, null, 2) },
44
+ ],
45
+ };
46
+ });
47
+ // Update brand style
48
+ server.tool("linwheel_brand_style_update", "Update an existing brand style profile. Requires all required fields (name, imageryApproach, primaryColors) — this is a full replacement, not a partial patch.", {
49
+ profileId: z.string().describe("The brand style profile ID to update"),
50
+ name: z.string().describe("Name for the brand style"),
51
+ imageryApproach: z.enum([
52
+ "photography", "illustration", "abstract", "3d_render", "mixed", "branded_typographic",
53
+ ]).describe("Visual style approach"),
54
+ primaryColors: z.array(colorSchema).min(1).describe("Primary brand colors (at least 1)"),
55
+ isActive: z.boolean().optional().describe("Set as active brand style"),
56
+ patternPreset: z.enum(["dot_grid", "circuit_lines", "diagonal_hash", "none"]).optional(),
57
+ logoUrl: z.string().optional(),
58
+ logoPosition: z.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).optional(),
59
+ logoScale: z.enum(["small", "medium", "large"]).optional(),
60
+ description: z.string().optional(),
61
+ colorMood: z.string().optional(),
62
+ stylePrefix: z.string().optional(),
63
+ styleSuffix: z.string().optional(),
64
+ moodDescriptors: z.array(z.string()).optional(),
65
+ negativeConcepts: z.array(z.string()).optional(),
66
+ }, async ({ profileId, ...body }) => {
67
+ const result = await client.put(`/api/agent/brand-styles/${profileId}`, body);
68
+ return {
69
+ content: [
70
+ { type: "text", text: JSON.stringify(result, null, 2) },
71
+ ],
72
+ };
73
+ });
74
+ // Delete brand style
75
+ server.tool("linwheel_brand_style_delete", "Delete a brand style profile by ID. Cannot delete the active profile — deactivate it first or activate another.", {
76
+ profileId: z.string().describe("The brand style profile ID to delete"),
77
+ }, async ({ profileId }) => {
78
+ const result = await client.delete(`/api/agent/brand-styles/${profileId}`);
79
+ return {
80
+ content: [
81
+ { type: "text", text: JSON.stringify(result, null, 2) },
82
+ ],
83
+ };
84
+ });
85
+ // Activate brand style
86
+ server.tool("linwheel_brand_style_activate", "Set a brand style as active. The active brand style is applied to all image and carousel generation. Only one brand style can be active at a time. When 'branded_typographic' is active, all image generation uses local rendering with SVG patterns instead of T2I APIs.", {
87
+ profileId: z.string().describe("The brand style profile ID to activate"),
88
+ }, async ({ profileId }) => {
89
+ const result = await client.post(`/api/agent/brand-styles/${profileId}/activate`, {});
90
+ return {
91
+ content: [
92
+ { type: "text", text: JSON.stringify(result, null, 2) },
93
+ ],
94
+ };
95
+ });
96
+ // Deactivate brand style
97
+ server.tool("linwheel_brand_style_deactivate", "Deactivate a brand style profile. After deactivation, image generation will use default settings (T2I providers) instead of branded rendering.", {
98
+ profileId: z.string().describe("The brand style profile ID to deactivate"),
99
+ }, async ({ profileId }) => {
100
+ const result = await client.delete(`/api/agent/brand-styles/${profileId}/activate`);
101
+ return {
102
+ content: [
103
+ { type: "text", text: JSON.stringify(result, null, 2) },
104
+ ],
105
+ };
106
+ });
107
+ }
108
+ //# sourceMappingURL=brand-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-styles.js","sourceRoot":"","sources":["../../src/tools/brand-styles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LinWheelClient } from "../client.js";
3
+ export declare function register(server: McpServer, client: LinWheelClient): void;
@@ -0,0 +1,55 @@
1
+ import { z } from "zod";
2
+ export function register(server, client) {
3
+ server.tool("linwheel_bundle", "Create a post with hero image and/or carousel in one atomic call. At least one of image or carousel must be provided. Good for creating polished posts ready for review.", {
4
+ fullText: z.string().describe("The full post text"),
5
+ hook: z.string().optional().describe("Opening hook line"),
6
+ postType: z.string().optional().describe("Content angle"),
7
+ approved: z.boolean().optional().describe("Pre-approve (default: false)"),
8
+ autoPublish: z.boolean().optional().describe("Auto-publish when scheduled (default: true)"),
9
+ scheduledAt: z.string().optional().describe("ISO 8601 scheduled time"),
10
+ imageHeadlineText: z
11
+ .string()
12
+ .optional()
13
+ .describe("Headline text for the hero image (max 200 chars). Omit to skip image generation."),
14
+ imageStylePreset: z
15
+ .enum([
16
+ "typographic_minimal",
17
+ "gradient_text",
18
+ "dark_mode",
19
+ "accent_bar",
20
+ "abstract_shapes",
21
+ ])
22
+ .optional()
23
+ .describe("Style preset for hero image (default: typographic_minimal)"),
24
+ carouselSlides: z
25
+ .array(z.object({
26
+ headlineText: z.string().describe("Slide headline text"),
27
+ caption: z.string().optional().describe("Optional slide caption"),
28
+ }))
29
+ .optional()
30
+ .describe("Carousel slides (1-10). Omit to skip carousel."),
31
+ }, async ({ fullText, hook, postType, approved, autoPublish, scheduledAt, imageHeadlineText, imageStylePreset, carouselSlides, }) => {
32
+ const body = {
33
+ fullText,
34
+ hook,
35
+ postType,
36
+ approved,
37
+ autoPublish,
38
+ scheduledAt,
39
+ };
40
+ if (imageHeadlineText) {
41
+ body.image = {
42
+ headlineText: imageHeadlineText,
43
+ stylePreset: imageStylePreset,
44
+ };
45
+ }
46
+ if (carouselSlides) {
47
+ body.carousel = { slides: carouselSlides };
48
+ }
49
+ const result = await client.post("/api/agent/bundle", body);
50
+ return {
51
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
52
+ };
53
+ });
54
+ }
55
+ //# sourceMappingURL=bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../src/tools/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,QAAQ,CAAC,MAAiB,EAAE,MAAsB;IAChE,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,0KAA0K,EAC1K;QACE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;QACnD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QACzD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC;QACzD,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;QACzE,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC;QAC3F,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;QACtE,iBAAiB,EAAE,CAAC;aACjB,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,kFAAkF,CAAC;QAC/F,gBAAgB,EAAE,CAAC;aAChB,IAAI,CAAC;YACJ,qBAAqB;YACrB,eAAe;YACf,WAAW;YACX,YAAY;YACZ,iBAAiB;SAClB,CAAC;aACD,QAAQ,EAAE;aACV,QAAQ,CAAC,4DAA4D,CAAC;QACzE,cAAc,EAAE,CAAC;aACd,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;YACP,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;YACxD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;SAClE,CAAC,CACH;aACA,QAAQ,EAAE;aACV,QAAQ,CAAC,gDAAgD,CAAC;KAC9D,EACD,KAAK,EAAE,EACL,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,GACf,EAAE,EAAE;QACH,MAAM,IAAI,GAA4B;YACpC,QAAQ;YACR,IAAI;YACJ,QAAQ;YACR,QAAQ;YACR,WAAW;YACX,WAAW;SACZ,CAAC;QACF,IAAI,iBAAiB,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,GAAG;gBACX,YAAY,EAAE,iBAAiB;gBAC/B,WAAW,EAAE,gBAAgB;aAC9B,CAAC;QACJ,CAAC;QACD,IAAI,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;QAC7C,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;QAC5D,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC5E,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LinWheelClient } from "../client.js";
3
+ export declare function register(server: McpServer, client: LinWheelClient): void;
@@ -0,0 +1,41 @@
1
+ import { z } from "zod";
2
+ export function register(server, client) {
3
+ server.tool("linwheel_draft", "Create a manual draft post in LinWheel. Use this when you have the final post text ready. For generating posts from raw content, use linwheel_reshape instead.", {
4
+ fullText: z
5
+ .string()
6
+ .describe("The full post text (max 3000 chars for LinkedIn)"),
7
+ hook: z
8
+ .string()
9
+ .optional()
10
+ .describe("Opening hook line (auto-extracted from first line if omitted)"),
11
+ postType: z
12
+ .string()
13
+ .optional()
14
+ .describe("Content angle (default: field_note)"),
15
+ approved: z
16
+ .boolean()
17
+ .optional()
18
+ .describe("Pre-approve the post (default: false)"),
19
+ autoPublish: z
20
+ .boolean()
21
+ .optional()
22
+ .describe("Auto-publish when scheduled time arrives (default: true)"),
23
+ scheduledAt: z
24
+ .string()
25
+ .optional()
26
+ .describe("ISO 8601 datetime for scheduled publishing"),
27
+ }, async ({ fullText, hook, postType, approved, autoPublish, scheduledAt }) => {
28
+ const result = await client.post("/api/agent/draft", {
29
+ fullText,
30
+ hook,
31
+ postType,
32
+ approved,
33
+ autoPublish,
34
+ scheduledAt,
35
+ });
36
+ return {
37
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
38
+ };
39
+ });
40
+ }
41
+ //# sourceMappingURL=draft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/tools/draft.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,QAAQ,CAAC,MAAiB,EAAE,MAAsB;IAChE,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,gKAAgK,EAChK;QACE,QAAQ,EAAE,CAAC;aACR,MAAM,EAAE;aACR,QAAQ,CAAC,kDAAkD,CAAC;QAC/D,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,+DAA+D,CAAC;QAC5E,QAAQ,EAAE,CAAC;aACR,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,qCAAqC,CAAC;QAClD,QAAQ,EAAE,CAAC;aACR,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,uCAAuC,CAAC;QACpD,WAAW,EAAE,CAAC;aACX,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,0DAA0D,CAAC;QACvE,WAAW,EAAE,CAAC;aACX,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,4CAA4C,CAAC;KAC1D,EACD,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE,EAAE;QACzE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE;YACnD,QAAQ;YACR,IAAI;YACJ,QAAQ;YACR,QAAQ;YACR,WAAW;YACX,WAAW;SACZ,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC5E,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LinWheelClient } from "../client.js";
3
+ export declare function registerAllTools(server: McpServer, client: LinWheelClient): void;