pastebin-mcp 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.
Files changed (2) hide show
  1. package/dist/index.js +223 -0
  2. package/package.json +32 -0
package/dist/index.js ADDED
@@ -0,0 +1,223 @@
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 { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import Database from "better-sqlite3";
6
+ import { nanoid } from "nanoid";
7
+ import { join, dirname } from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ // ---------------------------------------------------------------------------
10
+ // Database setup
11
+ // ---------------------------------------------------------------------------
12
+ const DB_PATH = process.env.SNIPPETS_DB_PATH ??
13
+ join(dirname(fileURLToPath(import.meta.url)), "..", "snippets.db");
14
+ const db = new Database(DB_PATH);
15
+ db.pragma("journal_mode = WAL");
16
+ db.exec(`
17
+ CREATE TABLE IF NOT EXISTS snippets (
18
+ id TEXT PRIMARY KEY,
19
+ title TEXT NOT NULL,
20
+ content TEXT NOT NULL,
21
+ language TEXT,
22
+ tags TEXT DEFAULT '[]',
23
+ created_at TEXT NOT NULL,
24
+ updated_at TEXT NOT NULL
25
+ );
26
+ `);
27
+ function parseSnippet(row) {
28
+ return { ...row, tags: JSON.parse(row.tags ?? "[]") };
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Prepared statements
32
+ // ---------------------------------------------------------------------------
33
+ const stmts = {
34
+ insert: db.prepare(`
35
+ INSERT INTO snippets (id, title, content, language, tags, created_at, updated_at)
36
+ VALUES (@id, @title, @content, @language, @tags, @created_at, @updated_at)
37
+ `),
38
+ getById: db.prepare(`SELECT * FROM snippets WHERE id = ?`),
39
+ listAll: db.prepare(`SELECT * FROM snippets ORDER BY created_at DESC`),
40
+ listByTag: db.prepare(`SELECT * FROM snippets WHERE tags LIKE ? ORDER BY created_at DESC`),
41
+ listByLanguage: db.prepare(`SELECT * FROM snippets WHERE language = ? ORDER BY created_at DESC`),
42
+ listByTagAndLanguage: db.prepare(`SELECT * FROM snippets WHERE tags LIKE ? AND language = ? ORDER BY created_at DESC`),
43
+ search: db.prepare(`SELECT * FROM snippets WHERE title LIKE ? OR content LIKE ? ORDER BY created_at DESC`),
44
+ deleteById: db.prepare(`DELETE FROM snippets WHERE id = ?`),
45
+ };
46
+ // ---------------------------------------------------------------------------
47
+ // MCP Server
48
+ // ---------------------------------------------------------------------------
49
+ const server = new Server({ name: "pastebin-mcp", version: "1.0.0" }, { capabilities: { tools: {} } });
50
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
51
+ tools: [
52
+ {
53
+ name: "create_snippet",
54
+ description: "Save a new code snippet to the vault.",
55
+ inputSchema: {
56
+ type: "object",
57
+ properties: {
58
+ title: { type: "string", description: "Short title for the snippet" },
59
+ content: { type: "string", description: "The code or text content" },
60
+ language: { type: "string", description: "Programming language (e.g. typescript)" },
61
+ tags: { type: "array", items: { type: "string" }, description: "Optional tags" },
62
+ },
63
+ required: ["title", "content"],
64
+ },
65
+ },
66
+ {
67
+ name: "get_snippet",
68
+ description: "Retrieve a snippet by its ID.",
69
+ inputSchema: {
70
+ type: "object",
71
+ properties: {
72
+ id: { type: "string", description: "Snippet ID" },
73
+ },
74
+ required: ["id"],
75
+ },
76
+ },
77
+ {
78
+ name: "list_snippets",
79
+ description: "List all snippets, optionally filtered by tag and/or language.",
80
+ inputSchema: {
81
+ type: "object",
82
+ properties: {
83
+ tag: { type: "string", description: "Filter by tag" },
84
+ language: { type: "string", description: "Filter by language" },
85
+ },
86
+ },
87
+ },
88
+ {
89
+ name: "search_snippets",
90
+ description: "Full-text search across snippet titles and content.",
91
+ inputSchema: {
92
+ type: "object",
93
+ properties: {
94
+ query: { type: "string", description: "Search term" },
95
+ },
96
+ required: ["query"],
97
+ },
98
+ },
99
+ {
100
+ name: "update_snippet",
101
+ description: "Update one or more fields of an existing snippet by ID.",
102
+ inputSchema: {
103
+ type: "object",
104
+ properties: {
105
+ id: { type: "string", description: "Snippet ID to update" },
106
+ title: { type: "string" },
107
+ content: { type: "string" },
108
+ language: { type: "string" },
109
+ tags: { type: "array", items: { type: "string" } },
110
+ },
111
+ required: ["id"],
112
+ },
113
+ },
114
+ {
115
+ name: "delete_snippet",
116
+ description: "Permanently delete a snippet by ID.",
117
+ inputSchema: {
118
+ type: "object",
119
+ properties: {
120
+ id: { type: "string", description: "Snippet ID to delete" },
121
+ },
122
+ required: ["id"],
123
+ },
124
+ },
125
+ ],
126
+ }));
127
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
128
+ const { name, arguments: args = {} } = request.params;
129
+ switch (name) {
130
+ case "create_snippet": {
131
+ const id = nanoid();
132
+ const now = new Date().toISOString();
133
+ stmts.insert.run({
134
+ id,
135
+ title: args.title,
136
+ content: args.content,
137
+ language: args.language ?? null,
138
+ tags: JSON.stringify(args.tags ?? []),
139
+ created_at: now,
140
+ updated_at: now,
141
+ });
142
+ const row = stmts.getById.get(id);
143
+ return { content: [{ type: "text", text: JSON.stringify(parseSnippet(row), null, 2) }] };
144
+ }
145
+ case "get_snippet": {
146
+ const row = stmts.getById.get(args.id);
147
+ if (!row) {
148
+ return { content: [{ type: "text", text: `No snippet found with id: ${args.id}` }], isError: true };
149
+ }
150
+ return { content: [{ type: "text", text: JSON.stringify(parseSnippet(row), null, 2) }] };
151
+ }
152
+ case "list_snippets": {
153
+ const tag = args.tag;
154
+ const language = args.language;
155
+ let rows;
156
+ if (tag && language) {
157
+ rows = stmts.listByTagAndLanguage.all(`%"${tag}"%`, language);
158
+ }
159
+ else if (tag) {
160
+ rows = stmts.listByTag.all(`%"${tag}"%`);
161
+ }
162
+ else if (language) {
163
+ rows = stmts.listByLanguage.all(language);
164
+ }
165
+ else {
166
+ rows = stmts.listAll.all();
167
+ }
168
+ return { content: [{ type: "text", text: JSON.stringify(rows.map(parseSnippet), null, 2) }] };
169
+ }
170
+ case "search_snippets": {
171
+ const pattern = `%${args.query}%`;
172
+ const rows = stmts.search.all(pattern, pattern);
173
+ return { content: [{ type: "text", text: JSON.stringify(rows.map(parseSnippet), null, 2) }] };
174
+ }
175
+ case "update_snippet": {
176
+ const existing = stmts.getById.get(args.id);
177
+ if (!existing) {
178
+ return { content: [{ type: "text", text: `No snippet found with id: ${args.id}` }], isError: true };
179
+ }
180
+ const updates = [];
181
+ const values = { id: args.id };
182
+ if (args.title !== undefined) {
183
+ updates.push("title = @title");
184
+ values.title = args.title;
185
+ }
186
+ if (args.content !== undefined) {
187
+ updates.push("content = @content");
188
+ values.content = args.content;
189
+ }
190
+ if (args.language !== undefined) {
191
+ updates.push("language = @language");
192
+ values.language = args.language;
193
+ }
194
+ if (args.tags !== undefined) {
195
+ updates.push("tags = @tags");
196
+ values.tags = JSON.stringify(args.tags);
197
+ }
198
+ if (updates.length === 0) {
199
+ return { content: [{ type: "text", text: "No fields to update were provided." }], isError: true };
200
+ }
201
+ updates.push("updated_at = @updated_at");
202
+ values.updated_at = new Date().toISOString();
203
+ db.prepare(`UPDATE snippets SET ${updates.join(", ")} WHERE id = @id`).run(values);
204
+ const row = stmts.getById.get(args.id);
205
+ return { content: [{ type: "text", text: JSON.stringify(parseSnippet(row), null, 2) }] };
206
+ }
207
+ case "delete_snippet": {
208
+ const existing = stmts.getById.get(args.id);
209
+ if (!existing) {
210
+ return { content: [{ type: "text", text: `No snippet found with id: ${args.id}` }], isError: true };
211
+ }
212
+ stmts.deleteById.run(args.id);
213
+ return { content: [{ type: "text", text: `Snippet ${args.id} deleted successfully.` }] };
214
+ }
215
+ default:
216
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
217
+ }
218
+ });
219
+ // ---------------------------------------------------------------------------
220
+ // Start
221
+ // ---------------------------------------------------------------------------
222
+ const transport = new StdioServerTransport();
223
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "pastebin-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for storing and retrieving code snippets via SQLite",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "pastebin-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/YOUR_USERNAME/pastebin-mcp.git"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "start": "node dist/index.js",
20
+ "dev": "tsc && node dist/index.js"
21
+ },
22
+ "dependencies": {
23
+ "@modelcontextprotocol/sdk": "^1.27.1",
24
+ "better-sqlite3": "^12.8.0",
25
+ "nanoid": "^5.0.7"
26
+ },
27
+ "devDependencies": {
28
+ "@types/better-sqlite3": "^7.6.13",
29
+ "@types/node": "^22.0.0",
30
+ "typescript": "^5.8.2"
31
+ }
32
+ }