ideablast-mcp-server 1.0.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/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # IdeaBlast MCP Server
2
+
3
+ Connect Claude to IdeaBlast to create ideas, brainstorm, and plan your day — from Claude Desktop or Claude Code.
4
+
5
+ ## Requirements
6
+
7
+ - **OS:** Windows, macOS, or Linux (desktop only — not mobile)
8
+ - **Node.js** v18+ installed ([download](https://nodejs.org))
9
+ - **Claude Desktop** ([download](https://claude.ai/download)) or **Claude Code** (CLI)
10
+ - IdeaBlast open in a browser **on the same PC** where the MCP server runs
11
+
12
+ > **Note:** MCP is a local protocol. The MCP server runs on your machine and communicates via localhost. Claude mobile app does NOT support MCP.
13
+
14
+ ## Quick Start
15
+
16
+ ```bash
17
+ cd mcp-server
18
+ npm install
19
+ npm run build
20
+ ```
21
+
22
+ ## Configure Claude Desktop
23
+
24
+ Add to your Claude Desktop config file:
25
+
26
+ **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
27
+ **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "ideablast": {
33
+ "command": "node",
34
+ "args": ["C:/path/to/IdeaBlast/mcp-server/dist/index.js"]
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ Replace the path with the actual path to your IdeaBlast installation.
41
+
42
+ ## Configure Claude Code
43
+
44
+ ```bash
45
+ claude mcp add ideablast node /path/to/IdeaBlast/mcp-server/dist/index.js
46
+ ```
47
+
48
+ ## Available Tools
49
+
50
+ ### `create_idea`
51
+ Create a single idea in IdeaBlast.
52
+ - `text` (required): The idea content
53
+ - `tags` (optional): Array of tags like `["marketing", "urgent"]`
54
+ - `deadline` (optional): ISO 8601 date string
55
+
56
+ ### `create_sticky_note`
57
+ Create a sticky note for the Daily Board.
58
+ - `text` (required): Note content
59
+ - `color` (optional): `yellow`, `pink`, `green`, `blue`, `orange`, `purple`
60
+
61
+ ### `brainstorm`
62
+ Generate multiple ideas about a topic at once.
63
+ - `topic` (required): What to brainstorm about
64
+ - `ideas` (required): Array of `{ text, tags? }` objects
65
+
66
+ ### `daily_plan`
67
+ Create a set of sticky notes to plan your day.
68
+ - `tasks` (required): Array of `{ text, category? }` objects
69
+ - Categories: `urgent`, `important`, `meeting`, `personal`, `work`, `break`
70
+
71
+ ### `list_pending`
72
+ List all items waiting to be synced to IdeaBlast.
73
+
74
+ ### `clear_pending`
75
+ Clear the sync queue.
76
+
77
+ ## How It Works
78
+
79
+ 1. Claude calls MCP tools to create ideas/notes
80
+ 2. Items are stored in a local JSON file (`data/inbox.json`)
81
+ 3. IdeaBlast (in browser) polls the MCP server via HTTP
82
+ 4. Ideas are imported into IdeaBlast automatically
83
+ 5. Imported items are removed from the queue
84
+
85
+ ## Enable MCP Sync in IdeaBlast
86
+
87
+ 1. Open IdeaBlast in your browser
88
+ 2. Click the **Plug** icon in the header (or find "MCP Sync" in the More menu on mobile)
89
+ 3. The icon turns green when connected to the MCP server
90
+
91
+ ## HTTP API (for debugging)
92
+
93
+ The MCP server also runs an HTTP bridge on `http://127.0.0.1:3456`:
94
+
95
+ - `GET /api/health` - Server status
96
+ - `GET /api/inbox` - Pending items
97
+ - `DELETE /api/inbox/:id` - Remove an item
98
+ - `POST /api/snapshot` - Push idea snapshot (for future tools)
99
+
100
+ ## Development
101
+
102
+ ```bash
103
+ npm run dev # Run with tsx (no build needed)
104
+ npm run build # Build TypeScript
105
+ npm start # Run built version
106
+ ```
107
+
108
+ ## Adding New Tools
109
+
110
+ Create a new file in `src/tools/`:
111
+
112
+ ```typescript
113
+ import { add } from '../inbox.js'
114
+ import type { ToolDefinition } from './index.js'
115
+
116
+ export const myTool: ToolDefinition = {
117
+ name: 'my_tool',
118
+ description: 'What this tool does',
119
+ inputSchema: {
120
+ type: 'object',
121
+ properties: { /* ... */ },
122
+ required: ['text'],
123
+ },
124
+ handler: async (args) => {
125
+ // Your logic here
126
+ return { content: [{ type: 'text', text: 'Result' }] }
127
+ },
128
+ }
129
+ ```
130
+
131
+ Then import and add it to the `allTools` array in `src/tools/index.ts`.
@@ -0,0 +1 @@
1
+ export declare function startHttpBridge(port?: number): void;
@@ -0,0 +1,64 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import { getAll, remove, count, saveSnapshot } from './inbox.js';
4
+ export function startHttpBridge(port = 3456) {
5
+ const app = express();
6
+ // CORS: allow any localhost origin (Vite dev, production, etc.)
7
+ app.use(cors({
8
+ origin: (origin, callback) => {
9
+ if (!origin)
10
+ return callback(null, true); // allow non-browser requests
11
+ if (origin.startsWith('http://localhost') || origin.startsWith('https://localhost')) {
12
+ return callback(null, true);
13
+ }
14
+ // Also allow the production domain
15
+ if (origin.includes('ideablast')) {
16
+ return callback(null, true);
17
+ }
18
+ callback(null, false);
19
+ },
20
+ methods: ['GET', 'DELETE', 'POST', 'OPTIONS'],
21
+ allowedHeaders: ['Content-Type'],
22
+ }));
23
+ app.use(express.json({ limit: '5mb' }));
24
+ // Health check
25
+ app.get('/api/health', (_req, res) => {
26
+ res.json({
27
+ status: 'ok',
28
+ server: 'ideablast-mcp',
29
+ version: '1.0.0',
30
+ pending: count(),
31
+ timestamp: new Date().toISOString(),
32
+ });
33
+ });
34
+ // Get all pending items
35
+ app.get('/api/inbox', (_req, res) => {
36
+ const items = getAll();
37
+ res.json({ items, count: items.length });
38
+ });
39
+ // Acknowledge (consume) an item
40
+ app.delete('/api/inbox/:id', (req, res) => {
41
+ const removed = remove(req.params.id);
42
+ if (removed) {
43
+ res.json({ success: true, id: req.params.id });
44
+ }
45
+ else {
46
+ res.status(404).json({ success: false, error: 'Item not found' });
47
+ }
48
+ });
49
+ // Receive snapshot from browser (for future "read ideas" tools)
50
+ app.post('/api/snapshot', (req, res) => {
51
+ const ideas = req.body?.ideas;
52
+ if (!ideas || !Array.isArray(ideas)) {
53
+ res.status(400).json({ success: false, error: 'Invalid snapshot data' });
54
+ return;
55
+ }
56
+ saveSnapshot(ideas);
57
+ res.json({ success: true, count: ideas.length });
58
+ });
59
+ app.listen(port, '127.0.0.1', () => {
60
+ console.error(`[IdeaBlast MCP] HTTP bridge running on http://127.0.0.1:${port}`);
61
+ console.error(`[IdeaBlast MCP] Health: http://127.0.0.1:${port}/api/health`);
62
+ });
63
+ }
64
+ //# sourceMappingURL=http-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-bridge.js","sourceRoot":"","sources":["../src/http-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { InboxItem, IdeaSnapshot, SnapshotData } from './types.js';
2
+ export declare function getAll(): InboxItem[];
3
+ export declare function add(item: Omit<InboxItem, 'id' | 'createdAt'>): InboxItem;
4
+ export declare function addMany(items: Omit<InboxItem, 'id' | 'createdAt'>[]): InboxItem[];
5
+ export declare function remove(id: string): boolean;
6
+ export declare function clear(): number;
7
+ export declare function count(): number;
8
+ export declare function saveSnapshot(ideas: IdeaSnapshot[]): void;
9
+ export declare function getSnapshot(): SnapshotData | null;
package/dist/inbox.js ADDED
@@ -0,0 +1,96 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { v4 as uuidv4 } from 'uuid';
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const DATA_DIR = path.join(__dirname, '..', 'data');
7
+ const INBOX_PATH = path.join(DATA_DIR, 'inbox.json');
8
+ const SNAPSHOT_PATH = path.join(DATA_DIR, 'snapshot.json');
9
+ function ensureDataDir() {
10
+ if (!fs.existsSync(DATA_DIR)) {
11
+ fs.mkdirSync(DATA_DIR, { recursive: true });
12
+ }
13
+ }
14
+ function readInbox() {
15
+ ensureDataDir();
16
+ if (!fs.existsSync(INBOX_PATH)) {
17
+ return { items: [], lastModified: new Date().toISOString() };
18
+ }
19
+ try {
20
+ const raw = fs.readFileSync(INBOX_PATH, 'utf-8');
21
+ return JSON.parse(raw);
22
+ }
23
+ catch {
24
+ return { items: [], lastModified: new Date().toISOString() };
25
+ }
26
+ }
27
+ function writeInbox(data) {
28
+ ensureDataDir();
29
+ data.lastModified = new Date().toISOString();
30
+ fs.writeFileSync(INBOX_PATH, JSON.stringify(data, null, 2), 'utf-8');
31
+ }
32
+ export function getAll() {
33
+ return readInbox().items;
34
+ }
35
+ export function add(item) {
36
+ const data = readInbox();
37
+ const newItem = {
38
+ ...item,
39
+ id: uuidv4(),
40
+ createdAt: new Date().toISOString(),
41
+ };
42
+ data.items.push(newItem);
43
+ writeInbox(data);
44
+ return newItem;
45
+ }
46
+ export function addMany(items) {
47
+ const data = readInbox();
48
+ const newItems = items.map(item => ({
49
+ ...item,
50
+ id: uuidv4(),
51
+ createdAt: new Date().toISOString(),
52
+ }));
53
+ data.items.push(...newItems);
54
+ writeInbox(data);
55
+ return newItems;
56
+ }
57
+ export function remove(id) {
58
+ const data = readInbox();
59
+ const index = data.items.findIndex(item => item.id === id);
60
+ if (index === -1)
61
+ return false;
62
+ data.items.splice(index, 1);
63
+ writeInbox(data);
64
+ return true;
65
+ }
66
+ export function clear() {
67
+ const data = readInbox();
68
+ const count = data.items.length;
69
+ data.items = [];
70
+ writeInbox(data);
71
+ return count;
72
+ }
73
+ export function count() {
74
+ return readInbox().items.length;
75
+ }
76
+ // Snapshot management (browser pushes its ideas here for Claude to read)
77
+ export function saveSnapshot(ideas) {
78
+ ensureDataDir();
79
+ const data = {
80
+ ideas,
81
+ updatedAt: new Date().toISOString(),
82
+ };
83
+ fs.writeFileSync(SNAPSHOT_PATH, JSON.stringify(data, null, 2), 'utf-8');
84
+ }
85
+ export function getSnapshot() {
86
+ if (!fs.existsSync(SNAPSHOT_PATH))
87
+ return null;
88
+ try {
89
+ const raw = fs.readFileSync(SNAPSHOT_PATH, 'utf-8');
90
+ return JSON.parse(raw);
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ //# sourceMappingURL=inbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbox.js","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { ListToolsRequestSchema, CallToolRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { allTools } from './tools/index.js';
6
+ import { startHttpBridge } from './http-bridge.js';
7
+ const server = new Server({
8
+ name: 'ideablast-mcp',
9
+ version: '1.0.0',
10
+ }, {
11
+ capabilities: {
12
+ tools: {},
13
+ },
14
+ instructions: `You are connected to IdeaBlast, a personal idea management app. Use these tools to add ideas, sticky notes, and plans directly into the user's IdeaBlast app.
15
+
16
+ IMPORTANT: When the user asks you to "create an idea", "add an idea", "save an idea", or anything related to creating/adding ideas in IdeaBlast — use the create_idea tool. Do NOT build apps, write code, or create files. Just call create_idea with the idea text.
17
+
18
+ Examples:
19
+ - "Create an idea about AI" → use create_idea with text about AI
20
+ - "Add an idea about marketing strategies" → use create_idea
21
+ - "Brainstorm ideas about productivity" → use brainstorm
22
+ - "Plan my day" → use daily_plan
23
+ - "Add a sticky note: buy groceries" → use create_sticky_note`,
24
+ });
25
+ // Register tool listing
26
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
27
+ return {
28
+ tools: allTools.map(tool => ({
29
+ name: tool.name,
30
+ description: tool.description,
31
+ inputSchema: tool.inputSchema,
32
+ })),
33
+ };
34
+ });
35
+ // Register tool execution
36
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
37
+ const { name, arguments: args } = request.params;
38
+ const tool = allTools.find(t => t.name === name);
39
+ if (!tool) {
40
+ return {
41
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
42
+ isError: true,
43
+ };
44
+ }
45
+ try {
46
+ return await tool.handler(args || {});
47
+ }
48
+ catch (error) {
49
+ const message = error instanceof Error ? error.message : String(error);
50
+ return {
51
+ content: [{ type: 'text', text: `Error executing ${name}: ${message}` }],
52
+ isError: true,
53
+ };
54
+ }
55
+ });
56
+ // Start HTTP bridge for browser polling (non-blocking)
57
+ startHttpBridge(3456);
58
+ // Connect MCP server via stdio
59
+ async function main() {
60
+ const transport = new StdioServerTransport();
61
+ await server.connect(transport);
62
+ console.error('[IdeaBlast MCP] Server running on stdio');
63
+ }
64
+ main().catch((error) => {
65
+ console.error('[IdeaBlast MCP] Fatal error:', error);
66
+ process.exit(1);
67
+ });
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAElD,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;IACE,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE;QACZ,KAAK,EAAE,EAAE;KACV;IACD,YAAY,EAAE;;;;;;;;;8DAS4C;CAC3D,CACF,CAAA;AAED,wBAAwB;AACxB,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;IAC1D,OAAO;QACL,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC3B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC,CAAC;KACJ,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,0BAA0B;AAC1B,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAA;IAChD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;IAEhD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC;YAC1D,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,IAA+B,IAAI,EAAE,CAAC,CAAA;IAClE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACtE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,IAAI,KAAK,OAAO,EAAE,EAAE,CAAC;YACxE,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,uDAAuD;AACvD,eAAe,CAAC,IAAI,CAAC,CAAA;AAErB,+BAA+B;AAC/B,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;IAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC/B,OAAO,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAA;AAC1D,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAA;IACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ import type { ToolDefinition } from './index.js';
2
+ export declare const brainstorm: ToolDefinition;
@@ -0,0 +1,55 @@
1
+ import { addMany } from '../inbox.js';
2
+ export const brainstorm = {
3
+ name: 'brainstorm',
4
+ description: 'Generate multiple ideas about a topic and send them all to the IdeaBlast app at once. Use this when the user asks to "brainstorm", "generate ideas", or wants several ideas about a topic. Do NOT write code — just call this tool with the topic and an array of creative ideas.',
5
+ inputSchema: {
6
+ type: 'object',
7
+ properties: {
8
+ topic: {
9
+ type: 'string',
10
+ description: 'The topic or theme to brainstorm about',
11
+ },
12
+ ideas: {
13
+ type: 'array',
14
+ items: {
15
+ type: 'object',
16
+ properties: {
17
+ text: { type: 'string', description: 'The idea text' },
18
+ tags: { type: 'array', items: { type: 'string' }, description: 'Tags for this idea' },
19
+ },
20
+ required: ['text'],
21
+ },
22
+ description: 'Array of ideas to create. Each idea has text and optional tags.',
23
+ },
24
+ count: {
25
+ type: 'number',
26
+ description: 'Number of ideas to generate (used as guidance if ideas array is not provided). Default: 5',
27
+ },
28
+ },
29
+ required: ['topic', 'ideas'],
30
+ },
31
+ handler: async (args) => {
32
+ const topic = args.topic;
33
+ const ideas = args.ideas;
34
+ if (!topic || topic.trim().length === 0) {
35
+ return { content: [{ type: 'text', text: 'Error: topic cannot be empty' }], isError: true };
36
+ }
37
+ if (!ideas || ideas.length === 0) {
38
+ return { content: [{ type: 'text', text: 'Error: please provide at least one idea in the ideas array' }], isError: true };
39
+ }
40
+ const items = addMany(ideas.map(idea => ({
41
+ type: 'idea',
42
+ text: idea.text.trim(),
43
+ tags: [...(idea.tags || []), 'brainstorm'],
44
+ source: 'brainstorm',
45
+ })));
46
+ const ideaList = items.map((item, i) => ` ${i + 1}. ${item.text}`).join('\n');
47
+ return {
48
+ content: [{
49
+ type: 'text',
50
+ text: `Brainstorm session complete! Created ${items.length} ideas about "${topic}":\n\n${ideaList}\n\nAll ideas tagged with "brainstorm". They will appear in IdeaBlast when MCP Sync picks them up.`,
51
+ }],
52
+ };
53
+ },
54
+ };
55
+ //# sourceMappingURL=brainstorm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brainstorm.js","sourceRoot":"","sources":["../../src/tools/brainstorm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAGrC,MAAM,CAAC,MAAM,UAAU,GAAmB;IACxC,IAAI,EAAE,YAAY;IAClB,WAAW,EAAE,mRAAmR;IAChS,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,wCAAwC;aACtD;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE;wBACtD,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,oBAAoB,EAAE;qBACtF;oBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;iBACnB;gBACD,WAAW,EAAE,iEAAiE;aAC/E;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,2FAA2F;aACzG;SACF;QACD,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;KAC7B;IACD,OAAO,EAAE,KAAK,EAAE,IAA6B,EAAE,EAAE;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAe,CAAA;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAiD,CAAA;QAEpE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,8BAA8B,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAC7F,CAAC;QAED,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,4DAA4D,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAC3H,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CACnB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjB,IAAI,EAAE,MAAe;YACrB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YACtB,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,YAAY,CAAC;YAC1C,MAAM,EAAE,YAAY;SACrB,CAAC,CAAC,CACJ,CAAA;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAE9E,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,wCAAwC,KAAK,CAAC,MAAM,iBAAiB,KAAK,SAAS,QAAQ,oGAAoG;iBACtM,CAAC;SACH,CAAA;IACH,CAAC;CACF,CAAA"}
@@ -0,0 +1,2 @@
1
+ import type { ToolDefinition } from './index.js';
2
+ export declare const clearPending: ToolDefinition;
@@ -0,0 +1,21 @@
1
+ import { clear } from '../inbox.js';
2
+ export const clearPending = {
3
+ name: 'clear_pending',
4
+ description: 'Clear all pending items from the sync queue. Use this to discard items that haven\'t been synced yet.',
5
+ inputSchema: {
6
+ type: 'object',
7
+ properties: {},
8
+ },
9
+ handler: async () => {
10
+ const removed = clear();
11
+ return {
12
+ content: [{
13
+ type: 'text',
14
+ text: removed > 0
15
+ ? `Cleared ${removed} pending item${removed === 1 ? '' : 's'} from the sync queue.`
16
+ : 'Queue was already empty.',
17
+ }],
18
+ };
19
+ },
20
+ };
21
+ //# sourceMappingURL=clear-pending.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clear-pending.js","sourceRoot":"","sources":["../../src/tools/clear-pending.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAGnC,MAAM,CAAC,MAAM,YAAY,GAAmB;IAC1C,IAAI,EAAE,eAAe;IACrB,WAAW,EAAE,uGAAuG;IACpH,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE,EAAE;KACf;IACD,OAAO,EAAE,KAAK,IAAI,EAAE;QAClB,MAAM,OAAO,GAAG,KAAK,EAAE,CAAA;QAEvB,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,OAAO,GAAG,CAAC;wBACf,CAAC,CAAC,WAAW,OAAO,gBAAgB,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,uBAAuB;wBACnF,CAAC,CAAC,0BAA0B;iBAC/B,CAAC;SACH,CAAA;IACH,CAAC;CACF,CAAA"}
@@ -0,0 +1,2 @@
1
+ import type { ToolDefinition } from './index.js';
2
+ export declare const createIdea: ToolDefinition;
@@ -0,0 +1,46 @@
1
+ import { add } from '../inbox.js';
2
+ export const createIdea = {
3
+ name: 'create_idea',
4
+ description: 'Create a new idea in the IdeaBlast app. Use this when the user asks to "create an idea", "add an idea", "save an idea", or "write an idea" in IdeaBlast. The idea will appear automatically in the app. Do NOT write code or build anything — just call this tool with the idea text.',
5
+ inputSchema: {
6
+ type: 'object',
7
+ properties: {
8
+ text: {
9
+ type: 'string',
10
+ description: 'The idea text. Write the full idea content here, e.g. "Develop an AI-powered personal assistant for daily tasks"',
11
+ },
12
+ tags: {
13
+ type: 'array',
14
+ items: { type: 'string' },
15
+ description: 'Optional tags to categorize the idea (e.g. ["marketing", "urgent"])',
16
+ },
17
+ deadline: {
18
+ type: 'string',
19
+ description: 'Optional deadline in ISO 8601 format (e.g. "2025-12-31T23:59:59.000Z")',
20
+ },
21
+ },
22
+ required: ['text'],
23
+ },
24
+ handler: async (args) => {
25
+ const text = args.text;
26
+ const tags = args.tags;
27
+ const deadline = args.deadline;
28
+ if (!text || text.trim().length === 0) {
29
+ return { content: [{ type: 'text', text: 'Error: idea text cannot be empty' }], isError: true };
30
+ }
31
+ const item = add({
32
+ type: 'idea',
33
+ text: text.trim(),
34
+ tags,
35
+ deadline,
36
+ source: 'manual',
37
+ });
38
+ return {
39
+ content: [{
40
+ type: 'text',
41
+ text: `Idea created successfully!\n\nID: ${item.id}\nText: ${item.text}${tags?.length ? `\nTags: ${tags.join(', ')}` : ''}${deadline ? `\nDeadline: ${deadline}` : ''}\n\nIt will appear in IdeaBlast when MCP Sync picks it up.`,
42
+ }],
43
+ };
44
+ },
45
+ };
46
+ //# sourceMappingURL=create-idea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-idea.js","sourceRoot":"","sources":["../../src/tools/create-idea.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAGjC,MAAM,CAAC,MAAM,UAAU,GAAmB;IACxC,IAAI,EAAE,aAAa;IACnB,WAAW,EAAE,uRAAuR;IACpS,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,kHAAkH;aAChI;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACzB,WAAW,EAAE,qEAAqE;aACnF;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,wEAAwE;aACtF;SACF;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;KACnB;IACD,OAAO,EAAE,KAAK,EAAE,IAA6B,EAAE,EAAE;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAc,CAAA;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,IAA4B,CAAA;QAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAA8B,CAAA;QAEpD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kCAAkC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QACjG,CAAC;QAED,MAAM,IAAI,GAAG,GAAG,CAAC;YACf,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;YACjB,IAAI;YACJ,QAAQ;YACR,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAA;QAEF,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,qCAAqC,IAAI,CAAC,EAAE,WAAW,IAAI,CAAC,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,eAAe,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,4DAA4D;iBAClO,CAAC;SACH,CAAA;IACH,CAAC;CACF,CAAA"}
@@ -0,0 +1,2 @@
1
+ import type { ToolDefinition } from './index.js';
2
+ export declare const createSticky: ToolDefinition;
@@ -0,0 +1,44 @@
1
+ import { add } from '../inbox.js';
2
+ const VALID_COLORS = ['yellow', 'pink', 'green', 'blue', 'orange', 'purple'];
3
+ export const createSticky = {
4
+ name: 'create_sticky_note',
5
+ description: 'Create a sticky note on the IdeaBlast Daily Board. Use this when the user asks to "add a sticky note", "create a reminder", or "add a task" to IdeaBlast. Do NOT write code — just call this tool with the note text.',
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ text: {
10
+ type: 'string',
11
+ description: 'The sticky note text',
12
+ },
13
+ color: {
14
+ type: 'string',
15
+ enum: VALID_COLORS,
16
+ description: 'Sticky note color (default: yellow)',
17
+ },
18
+ },
19
+ required: ['text'],
20
+ },
21
+ handler: async (args) => {
22
+ const text = args.text;
23
+ const color = args.color || 'yellow';
24
+ if (!text || text.trim().length === 0) {
25
+ return { content: [{ type: 'text', text: 'Error: sticky note text cannot be empty' }], isError: true };
26
+ }
27
+ if (!VALID_COLORS.includes(color)) {
28
+ return { content: [{ type: 'text', text: `Error: invalid color. Use one of: ${VALID_COLORS.join(', ')}` }], isError: true };
29
+ }
30
+ const item = add({
31
+ type: 'sticky_note',
32
+ text: text.trim(),
33
+ stickyColor: color,
34
+ source: 'manual',
35
+ });
36
+ return {
37
+ content: [{
38
+ type: 'text',
39
+ text: `Sticky note created!\n\nID: ${item.id}\nText: ${item.text}\nColor: ${color}\n\nIt will appear on today's Daily Board when MCP Sync picks it up.`,
40
+ }],
41
+ };
42
+ },
43
+ };
44
+ //# sourceMappingURL=create-sticky.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-sticky.js","sourceRoot":"","sources":["../../src/tools/create-sticky.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAGjC,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;AAE5E,MAAM,CAAC,MAAM,YAAY,GAAmB;IAC1C,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EAAE,uNAAuN;IACpO,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,sBAAsB;aACpC;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,YAAY;gBAClB,WAAW,EAAE,qCAAqC;aACnD;SACF;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;KACnB;IACD,OAAO,EAAE,KAAK,EAAE,IAA6B,EAAE,EAAE;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAc,CAAA;QAChC,MAAM,KAAK,GAAI,IAAI,CAAC,KAAgB,IAAI,QAAQ,CAAA;QAEhD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yCAAyC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QACxG,CAAC;QAED,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAClC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qCAAqC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAC7H,CAAC;QAED,MAAM,IAAI,GAAG,GAAG,CAAC;YACf,IAAI,EAAE,aAAa;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;YACjB,WAAW,EAAE,KAAK;YAClB,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAA;QAEF,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,+BAA+B,IAAI,CAAC,EAAE,WAAW,IAAI,CAAC,IAAI,YAAY,KAAK,sEAAsE;iBACxJ,CAAC;SACH,CAAA;IACH,CAAC;CACF,CAAA"}
@@ -0,0 +1,2 @@
1
+ import type { ToolDefinition } from './index.js';
2
+ export declare const dailyPlan: ToolDefinition;
@@ -0,0 +1,59 @@
1
+ import { addMany } from '../inbox.js';
2
+ const COLOR_MAP = {
3
+ urgent: 'pink',
4
+ important: 'orange',
5
+ meeting: 'blue',
6
+ personal: 'green',
7
+ work: 'yellow',
8
+ break: 'purple',
9
+ };
10
+ export const dailyPlan = {
11
+ name: 'daily_plan',
12
+ description: 'Create a set of sticky notes for the IdeaBlast Daily Board to plan your day. Use this when the user asks to "plan my day", "organize my tasks", or "create a daily plan" in IdeaBlast. Do NOT write code — just call this tool with the task list.',
13
+ inputSchema: {
14
+ type: 'object',
15
+ properties: {
16
+ tasks: {
17
+ type: 'array',
18
+ items: {
19
+ type: 'object',
20
+ properties: {
21
+ text: { type: 'string', description: 'Task description' },
22
+ category: {
23
+ type: 'string',
24
+ enum: Object.keys(COLOR_MAP),
25
+ description: 'Task category (determines sticky note color)',
26
+ },
27
+ },
28
+ required: ['text'],
29
+ },
30
+ description: 'Array of tasks for the day. Each gets a sticky note with color based on category.',
31
+ },
32
+ },
33
+ required: ['tasks'],
34
+ },
35
+ handler: async (args) => {
36
+ const tasks = args.tasks;
37
+ if (!tasks || tasks.length === 0) {
38
+ return { content: [{ type: 'text', text: 'Error: please provide at least one task' }], isError: true };
39
+ }
40
+ const items = addMany(tasks.map(task => ({
41
+ type: 'sticky_note',
42
+ text: task.text.trim(),
43
+ stickyColor: COLOR_MAP[task.category || 'work'] || 'yellow',
44
+ source: 'daily-plan',
45
+ })));
46
+ const taskList = items.map((item, i) => {
47
+ const task = tasks[i];
48
+ const color = task.category || 'work';
49
+ return ` ${i + 1}. [${color}] ${item.text}`;
50
+ }).join('\n');
51
+ return {
52
+ content: [{
53
+ type: 'text',
54
+ text: `Daily plan created! ${items.length} sticky notes ready:\n\n${taskList}\n\nThey will appear on today's Daily Board when MCP Sync picks them up.`,
55
+ }],
56
+ };
57
+ },
58
+ };
59
+ //# sourceMappingURL=daily-plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daily-plan.js","sourceRoot":"","sources":["../../src/tools/daily-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAGrC,MAAM,SAAS,GAA2B;IACxC,MAAM,EAAE,MAAM;IACd,SAAS,EAAE,QAAQ;IACnB,OAAO,EAAE,MAAM;IACf,QAAQ,EAAE,OAAO;IACjB,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,QAAQ;CAChB,CAAA;AAED,MAAM,CAAC,MAAM,SAAS,GAAmB;IACvC,IAAI,EAAE,YAAY;IAClB,WAAW,EAAE,oPAAoP;IACjQ,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,KAAK,EAAE;gBACL,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE;wBACzD,QAAQ,EAAE;4BACR,IAAI,EAAE,QAAQ;4BACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;4BAC5B,WAAW,EAAE,8CAA8C;yBAC5D;qBACF;oBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;iBACnB;gBACD,WAAW,EAAE,mFAAmF;aACjG;SACF;QACD,QAAQ,EAAE,CAAC,OAAO,CAAC;KACpB;IACD,OAAO,EAAE,KAAK,EAAE,IAA6B,EAAE,EAAE;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAmD,CAAA;QAEtE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yCAAyC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QACxG,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CACnB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjB,IAAI,EAAE,aAAsB;YAC5B,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YACtB,WAAW,EAAE,SAAS,CAAC,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,QAAQ;YAC3D,MAAM,EAAE,YAAY;SACrB,CAAC,CAAC,CACJ,CAAA;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;YACrB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAA;YACrC,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,IAAI,EAAE,CAAA;QAC9C,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEb,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,uBAAuB,KAAK,CAAC,MAAM,2BAA2B,QAAQ,0EAA0E;iBACvJ,CAAC;SACH,CAAA;IACH,CAAC;CACF,CAAA"}
@@ -0,0 +1,17 @@
1
+ export interface ToolDefinition {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: 'object';
6
+ properties: Record<string, unknown>;
7
+ required?: string[];
8
+ };
9
+ handler: (args: Record<string, unknown>) => Promise<{
10
+ content: Array<{
11
+ type: string;
12
+ text: string;
13
+ }>;
14
+ isError?: boolean;
15
+ }>;
16
+ }
17
+ export declare const allTools: ToolDefinition[];
@@ -0,0 +1,16 @@
1
+ import { createIdea } from './create-idea.js';
2
+ import { createSticky } from './create-sticky.js';
3
+ import { brainstorm } from './brainstorm.js';
4
+ import { dailyPlan } from './daily-plan.js';
5
+ import { listPending } from './list-pending.js';
6
+ import { clearPending } from './clear-pending.js';
7
+ // All tools registered here - add new tools by importing and adding to this array
8
+ export const allTools = [
9
+ createIdea,
10
+ createSticky,
11
+ brainstorm,
12
+ dailyPlan,
13
+ listPending,
14
+ clearPending,
15
+ ];
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAgBjD,kFAAkF;AAClF,MAAM,CAAC,MAAM,QAAQ,GAAqB;IACxC,UAAU;IACV,YAAY;IACZ,UAAU;IACV,SAAS;IACT,WAAW;IACX,YAAY;CACb,CAAA"}
@@ -0,0 +1,2 @@
1
+ import type { ToolDefinition } from './index.js';
2
+ export declare const listPending: ToolDefinition;
@@ -0,0 +1,33 @@
1
+ import { getAll, count } from '../inbox.js';
2
+ export const listPending = {
3
+ name: 'list_pending',
4
+ description: 'List all pending items waiting to be synced to IdeaBlast.',
5
+ inputSchema: {
6
+ type: 'object',
7
+ properties: {},
8
+ },
9
+ handler: async () => {
10
+ const items = getAll();
11
+ if (items.length === 0) {
12
+ return {
13
+ content: [{
14
+ type: 'text',
15
+ text: 'No pending items. All ideas have been synced to IdeaBlast (or none have been created yet).',
16
+ }],
17
+ };
18
+ }
19
+ const list = items.map((item, i) => {
20
+ const type = item.type === 'idea' ? '💡' : '📝';
21
+ const tags = item.tags?.length ? ` [${item.tags.join(', ')}]` : '';
22
+ const source = item.source ? ` (via ${item.source})` : '';
23
+ return ` ${i + 1}. ${type} ${item.text}${tags}${source}`;
24
+ }).join('\n');
25
+ return {
26
+ content: [{
27
+ type: 'text',
28
+ text: `${count()} pending items:\n\n${list}\n\nThese will be imported when IdeaBlast has MCP Sync enabled.`,
29
+ }],
30
+ };
31
+ },
32
+ };
33
+ //# sourceMappingURL=list-pending.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-pending.js","sourceRoot":"","sources":["../../src/tools/list-pending.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAG3C,MAAM,CAAC,MAAM,WAAW,GAAmB;IACzC,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE,2DAA2D;IACxE,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE,EAAE;KACf;IACD,OAAO,EAAE,KAAK,IAAI,EAAE;QAClB,MAAM,KAAK,GAAG,MAAM,EAAE,CAAA;QAEtB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,4FAA4F;qBACnG,CAAC;aACH,CAAA;QACH,CAAC;QAED,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YACjC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;YAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;YAClE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;YACzD,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,MAAM,EAAE,CAAA;QAC3D,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEb,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,GAAG,KAAK,EAAE,sBAAsB,IAAI,iEAAiE;iBAC5G,CAAC;SACH,CAAA;IACH,CAAC;CACF,CAAA"}
@@ -0,0 +1,27 @@
1
+ export interface InboxItem {
2
+ id: string;
3
+ type: 'idea' | 'sticky_note';
4
+ text: string;
5
+ tags?: string[];
6
+ deadline?: string;
7
+ stickyColor?: string;
8
+ createdAt: string;
9
+ source?: string;
10
+ }
11
+ export interface InboxData {
12
+ items: InboxItem[];
13
+ lastModified: string;
14
+ }
15
+ export interface IdeaSnapshot {
16
+ id: string;
17
+ text: string;
18
+ tags?: string[];
19
+ isFavorite: boolean;
20
+ isDone?: boolean;
21
+ deadline?: string;
22
+ createdAt: string;
23
+ }
24
+ export interface SnapshotData {
25
+ ideas: IdeaSnapshot[];
26
+ updatedAt: string;
27
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "ideablast-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for IdeaBlast - Create ideas from Claude Desktop",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "ideablast-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/",
12
+ "README.md"
13
+ ],
14
+ "keywords": [
15
+ "mcp",
16
+ "ideablast",
17
+ "claude",
18
+ "ideas",
19
+ "model-context-protocol"
20
+ ],
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/Nurkan1/IdeaBlast"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "start": "node dist/index.js",
29
+ "dev": "npx tsx src/index.ts"
30
+ },
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.12.1",
33
+ "express": "^4.21.2",
34
+ "cors": "^2.8.5",
35
+ "uuid": "^11.1.0"
36
+ },
37
+ "devDependencies": {
38
+ "typescript": "^5.6.3",
39
+ "@types/express": "^5.0.2",
40
+ "@types/cors": "^2.8.17",
41
+ "@types/uuid": "^10.0.0",
42
+ "@types/node": "^22.14.0",
43
+ "tsx": "^4.19.4"
44
+ }
45
+ }