tokrepo-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.
Files changed (3) hide show
  1. package/README.md +88 -0
  2. package/bin/server.js +323 -0
  3. package/package.json +34 -0
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # TokRepo MCP Server
2
+
3
+ > Search, browse, and install AI assets from [TokRepo](https://tokrepo.com) — the open registry for AI skills, prompts, MCP configs, scripts, and workflows.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@tokrepo/mcp-server)](https://www.npmjs.com/package/@tokrepo/mcp-server)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Quick Start
9
+
10
+ ### Claude Code
11
+ ```bash
12
+ claude mcp add tokrepo -- npx @tokrepo/mcp-server
13
+ ```
14
+
15
+ ### Cursor / Windsurf
16
+ Add to your MCP config (`~/.cursor/mcp.json`):
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "tokrepo": {
21
+ "command": "npx",
22
+ "args": ["@tokrepo/mcp-server"]
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ ### OpenAI Codex / Gemini CLI
29
+ ```bash
30
+ codex --mcp-server tokrepo -- npx @tokrepo/mcp-server
31
+ gemini settings mcp add tokrepo -- npx @tokrepo/mcp-server
32
+ ```
33
+
34
+ ## What It Does
35
+
36
+ Once connected, your AI assistant can:
37
+
38
+ - **Search** 200+ curated AI assets by keyword or category
39
+ - **Browse** trending assets, filter by type (MCP, Skill, Prompt, Agent, Script)
40
+ - **Get details** — full documentation, install instructions, and metadata
41
+ - **Install** — get raw content ready to save or execute
42
+
43
+ ## Available Tools
44
+
45
+ | Tool | Description |
46
+ |------|-------------|
47
+ | `tokrepo_search` | Search assets by keyword and tag |
48
+ | `tokrepo_detail` | Get full asset details by UUID |
49
+ | `tokrepo_install` | Get raw installable content |
50
+ | `tokrepo_trending` | Browse popular/latest assets |
51
+
52
+ ## Example Conversations
53
+
54
+ ```
55
+ You: "Find me a good MCP server for databases"
56
+ AI: [calls tokrepo_search] → Shows DBHub, Supabase MCP, PostgreSQL MCP with install commands
57
+
58
+ You: "What's trending on TokRepo?"
59
+ AI: [calls tokrepo_trending] → Shows top assets by popularity
60
+
61
+ You: "Install that cursor rules asset"
62
+ AI: [calls tokrepo_install] → Returns raw content ready to save
63
+ ```
64
+
65
+ ## Why TokRepo?
66
+
67
+ TokRepo is the **open registry for AI assets** — like npm for packages, but for AI skills, prompts, MCP configs, and workflows.
68
+
69
+ - **200+ curated assets** — quality-reviewed, not a dump
70
+ - **Agent-native** — every asset has machine-readable install contracts
71
+ - **Universal** — works with Claude Code, Cursor, Codex, Gemini CLI, and any MCP client
72
+ - **CLI available** — `npx tokrepo search "query"` / `npx tokrepo install <uuid>`
73
+
74
+ ## Requirements
75
+
76
+ - Node.js >= 18
77
+ - Internet connection (queries tokrepo.com API)
78
+
79
+ ## Links
80
+
81
+ - **Website**: [tokrepo.com](https://tokrepo.com)
82
+ - **CLI**: [npm: tokrepo](https://www.npmjs.com/package/tokrepo)
83
+ - **GitHub**: [tokrepo/mcp-server](https://github.com/tokrepo/mcp-server)
84
+ - **API**: [tokrepo.com/.well-known/tokrepo.json](https://tokrepo.com/.well-known/tokrepo.json)
85
+
86
+ ## License
87
+
88
+ MIT
package/bin/server.js ADDED
@@ -0,0 +1,323 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * TokRepo MCP Server
5
+ *
6
+ * Search, browse, and install AI assets from TokRepo via the Model Context Protocol.
7
+ * Works with Claude Code, Cursor, Codex, Gemini CLI, and any MCP client.
8
+ *
9
+ * Usage:
10
+ * claude mcp add tokrepo -- npx @tokrepo/mcp-server
11
+ * npx @tokrepo/mcp-server
12
+ */
13
+
14
+ const https = require('https');
15
+ const readline = require('readline');
16
+
17
+ const API_BASE = 'https://api.tokrepo.com';
18
+ const TOKREPO_URL = 'https://tokrepo.com';
19
+
20
+ // ─── MCP Protocol (JSON-RPC over stdio) ───
21
+
22
+ const SERVER_INFO = {
23
+ name: 'tokrepo',
24
+ version: '1.0.0',
25
+ };
26
+
27
+ const CAPABILITIES = {
28
+ tools: {},
29
+ };
30
+
31
+ const TOOLS = [
32
+ {
33
+ name: 'tokrepo_search',
34
+ description: 'Search TokRepo for AI assets (skills, prompts, MCP configs, scripts, workflows). Returns matching assets with titles, descriptions, tags, stars, and install commands. Use this when the user asks to find AI tools, MCP servers, Claude skills, prompts, or workflows.',
35
+ inputSchema: {
36
+ type: 'object',
37
+ properties: {
38
+ query: {
39
+ type: 'string',
40
+ description: 'Search keywords (e.g. "cursor rules", "mcp database", "claude skill code review")',
41
+ },
42
+ tag: {
43
+ type: 'string',
44
+ description: 'Optional tag filter: agent, coding, efficiency, cost-saving, methodology, data-analysis, writing, marketing, learning, research',
45
+ enum: ['agent', 'coding', 'efficiency', 'cost-saving', 'methodology', 'data-analysis', 'writing', 'marketing', 'learning', 'research'],
46
+ },
47
+ limit: {
48
+ type: 'number',
49
+ description: 'Max results (default 10, max 20)',
50
+ default: 10,
51
+ },
52
+ },
53
+ required: ['query'],
54
+ },
55
+ },
56
+ {
57
+ name: 'tokrepo_detail',
58
+ description: 'Get full details of a TokRepo asset by UUID, including description, content, tags, install instructions, and metadata.',
59
+ inputSchema: {
60
+ type: 'object',
61
+ properties: {
62
+ uuid: {
63
+ type: 'string',
64
+ description: 'Asset UUID (from search results)',
65
+ },
66
+ },
67
+ required: ['uuid'],
68
+ },
69
+ },
70
+ {
71
+ name: 'tokrepo_install',
72
+ description: 'Get the install command and raw content for a TokRepo asset. Returns the content ready to be saved to a file or executed.',
73
+ inputSchema: {
74
+ type: 'object',
75
+ properties: {
76
+ uuid: {
77
+ type: 'string',
78
+ description: 'Asset UUID',
79
+ },
80
+ },
81
+ required: ['uuid'],
82
+ },
83
+ },
84
+ {
85
+ name: 'tokrepo_trending',
86
+ description: 'Get trending/popular AI assets on TokRepo. Use when user asks for recommended or popular AI tools.',
87
+ inputSchema: {
88
+ type: 'object',
89
+ properties: {
90
+ sort: {
91
+ type: 'string',
92
+ description: 'Sort order',
93
+ enum: ['popular', 'latest', 'views', 'stars'],
94
+ default: 'popular',
95
+ },
96
+ limit: {
97
+ type: 'number',
98
+ description: 'Max results (default 10)',
99
+ default: 10,
100
+ },
101
+ },
102
+ },
103
+ },
104
+ ];
105
+
106
+ // ─── HTTP Helper ───
107
+
108
+ function apiGet(path) {
109
+ return new Promise((resolve, reject) => {
110
+ const url = `${API_BASE}${path}`;
111
+ const req = https.get(url, { headers: { Accept: 'application/json' }, timeout: 10000 }, (res) => {
112
+ let body = '';
113
+ res.on('data', (chunk) => { body += chunk; });
114
+ res.on('end', () => {
115
+ try {
116
+ const data = JSON.parse(body);
117
+ resolve(data);
118
+ } catch (e) {
119
+ reject(new Error(`Invalid JSON from ${path}`));
120
+ }
121
+ });
122
+ });
123
+ req.on('error', reject);
124
+ req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
125
+ });
126
+ }
127
+
128
+ function apiGetText(path) {
129
+ return new Promise((resolve, reject) => {
130
+ const url = `${API_BASE}${path}`;
131
+ const req = https.get(url, { headers: { Accept: 'text/plain' }, timeout: 10000 }, (res) => {
132
+ let body = '';
133
+ res.on('data', (chunk) => { body += chunk; });
134
+ res.on('end', () => resolve(body));
135
+ });
136
+ req.on('error', reject);
137
+ req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
138
+ });
139
+ }
140
+
141
+ // ─── Tool Handlers ───
142
+
143
+ async function handleSearch(args) {
144
+ const { query, tag, limit = 10 } = args;
145
+ const params = new URLSearchParams({
146
+ keyword: query,
147
+ page: '1',
148
+ page_size: String(Math.min(limit, 20)),
149
+ sort_by: 'popular',
150
+ });
151
+ if (tag) {
152
+ // Map tag name to tag_id (approximate)
153
+ const tagMap = { agent: 11, coding: 7, efficiency: 10, 'cost-saving': 12, methodology: 15, 'data-analysis': 14, writing: 1, marketing: 16, learning: 17, research: 8 };
154
+ if (tagMap[tag]) params.set('tag_id', String(tagMap[tag]));
155
+ }
156
+
157
+ const res = await apiGet(`/api/v1/tokenboard/workflows/list?${params}`);
158
+ if (res.code !== 200 || !res.data?.list?.length) {
159
+ return { content: [{ type: 'text', text: `No assets found for "${query}". Try broader keywords.` }] };
160
+ }
161
+
162
+ const items = res.data.list.slice(0, limit);
163
+ const lines = items.map((item, i) => {
164
+ const tags = (item.tags || []).map(t => t.name || t.slug).join(', ');
165
+ return [
166
+ `${i + 1}. **${item.title}**`,
167
+ ` ${item.description || ''}`,
168
+ ` Tags: ${tags || 'general'} | ★ ${item.vote_count || 0} | 👁 ${item.view_count || 0}`,
169
+ ` Install: \`tokrepo install ${item.uuid}\``,
170
+ ` URL: ${TOKREPO_URL}/en/workflows/${item.uuid}`,
171
+ ].join('\n');
172
+ });
173
+
174
+ const text = `Found ${res.data.total} assets for "${query}" (showing ${items.length}):\n\n${lines.join('\n\n')}`;
175
+ return { content: [{ type: 'text', text }] };
176
+ }
177
+
178
+ async function handleDetail(args) {
179
+ const { uuid } = args;
180
+ const res = await apiGet(`/api/v1/tokenboard/workflows/detail?uuid=${encodeURIComponent(uuid)}`);
181
+ if (res.code !== 200 || !res.data?.workflow) {
182
+ return { content: [{ type: 'text', text: `Asset not found: ${uuid}` }] };
183
+ }
184
+
185
+ const w = res.data.workflow;
186
+ const tags = (w.tags || []).map(t => t.name || t.slug).join(', ');
187
+ const steps = (w.steps || []).map((s, i) => {
188
+ const content = s.prompt_template || s.description || '';
189
+ return `### Step ${i + 1}: ${s.title}\n${content.substring(0, 500)}${content.length > 500 ? '...' : ''}`;
190
+ }).join('\n\n');
191
+
192
+ const text = [
193
+ `# ${w.title}`,
194
+ ``,
195
+ `**Description**: ${w.description}`,
196
+ `**Tags**: ${tags}`,
197
+ `**Stars**: ${w.vote_count || 0} | **Views**: ${w.view_count || 0} | **Forks**: ${w.fork_count || 0}`,
198
+ `**Author**: ${w.author_name || 'Anonymous'}`,
199
+ `**URL**: ${TOKREPO_URL}/en/workflows/${w.uuid}`,
200
+ `**Install**: \`tokrepo install ${w.uuid}\``,
201
+ ``,
202
+ steps,
203
+ ].join('\n');
204
+
205
+ return { content: [{ type: 'text', text }] };
206
+ }
207
+
208
+ async function handleInstall(args) {
209
+ const { uuid } = args;
210
+ try {
211
+ const raw = await apiGetText(`/api/v1/tokenboard/workflows/raw?uuid=${encodeURIComponent(uuid)}`);
212
+ if (!raw || raw.includes('"code":')) {
213
+ return { content: [{ type: 'text', text: `Could not fetch raw content for ${uuid}. Try: tokrepo install ${uuid}` }] };
214
+ }
215
+ return { content: [{ type: 'text', text: `# Raw content for asset ${uuid}\n\nInstall via CLI: \`npx tokrepo install ${uuid}\`\n\n---\n\n${raw}` }] };
216
+ } catch (e) {
217
+ return { content: [{ type: 'text', text: `Error fetching asset: ${e.message}. Try: tokrepo install ${uuid}` }] };
218
+ }
219
+ }
220
+
221
+ async function handleTrending(args) {
222
+ const { sort = 'popular', limit = 10 } = args;
223
+ const params = new URLSearchParams({
224
+ page: '1',
225
+ page_size: String(Math.min(limit, 20)),
226
+ sort_by: sort,
227
+ });
228
+
229
+ const res = await apiGet(`/api/v1/tokenboard/workflows/list?${params}`);
230
+ if (res.code !== 200 || !res.data?.list?.length) {
231
+ return { content: [{ type: 'text', text: 'No trending assets found.' }] };
232
+ }
233
+
234
+ const items = res.data.list.slice(0, limit);
235
+ const lines = items.map((item, i) => {
236
+ const tags = (item.tags || []).map(t => t.name || t.slug).join(', ');
237
+ return `${i + 1}. **${item.title}** — ${item.description || ''}\n ${tags} | ★ ${item.vote_count || 0} | 👁 ${item.view_count || 0} | Install: \`tokrepo install ${item.uuid}\``;
238
+ });
239
+
240
+ const text = `Trending AI assets on TokRepo (${sort}):\n\n${lines.join('\n\n')}\n\nBrowse more: ${TOKREPO_URL}`;
241
+ return { content: [{ type: 'text', text }] };
242
+ }
243
+
244
+ // ─── MCP JSON-RPC Handler ───
245
+
246
+ async function handleRequest(msg) {
247
+ const { id, method, params } = msg;
248
+
249
+ switch (method) {
250
+ case 'initialize':
251
+ return { jsonrpc: '2.0', id, result: { protocolVersion: '2024-11-05', capabilities: CAPABILITIES, serverInfo: SERVER_INFO } };
252
+
253
+ case 'notifications/initialized':
254
+ return null; // no response for notifications
255
+
256
+ case 'tools/list':
257
+ return { jsonrpc: '2.0', id, result: { tools: TOOLS } };
258
+
259
+ case 'tools/call': {
260
+ const { name, arguments: args } = params || {};
261
+ let result;
262
+ try {
263
+ switch (name) {
264
+ case 'tokrepo_search': result = await handleSearch(args || {}); break;
265
+ case 'tokrepo_detail': result = await handleDetail(args || {}); break;
266
+ case 'tokrepo_install': result = await handleInstall(args || {}); break;
267
+ case 'tokrepo_trending': result = await handleTrending(args || {}); break;
268
+ default: result = { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
269
+ }
270
+ } catch (e) {
271
+ result = { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true };
272
+ }
273
+ return { jsonrpc: '2.0', id, result };
274
+ }
275
+
276
+ case 'ping':
277
+ return { jsonrpc: '2.0', id, result: {} };
278
+
279
+ default:
280
+ return { jsonrpc: '2.0', id, error: { code: -32601, message: `Method not found: ${method}` } };
281
+ }
282
+ }
283
+
284
+ // ─── Stdio Transport ───
285
+
286
+ function main() {
287
+ const rl = readline.createInterface({ input: process.stdin, terminal: false });
288
+ let buffer = '';
289
+
290
+ process.stdin.on('data', (chunk) => {
291
+ buffer += chunk.toString();
292
+ // Process complete JSON-RPC messages (newline-delimited)
293
+ const lines = buffer.split('\n');
294
+ buffer = lines.pop() || '';
295
+ for (const line of lines) {
296
+ const trimmed = line.trim();
297
+ if (!trimmed) continue;
298
+ try {
299
+ const msg = JSON.parse(trimmed);
300
+ handleRequest(msg).then((response) => {
301
+ if (response) {
302
+ process.stdout.write(JSON.stringify(response) + '\n');
303
+ }
304
+ }).catch((e) => {
305
+ process.stdout.write(JSON.stringify({
306
+ jsonrpc: '2.0',
307
+ id: msg.id || null,
308
+ error: { code: -32603, message: e.message },
309
+ }) + '\n');
310
+ });
311
+ } catch (e) {
312
+ // Skip malformed JSON
313
+ }
314
+ }
315
+ });
316
+
317
+ process.stdin.on('end', () => process.exit(0));
318
+
319
+ // Log to stderr (not stdout, which is the MCP transport)
320
+ process.stderr.write('TokRepo MCP Server v1.0.0 started\n');
321
+ }
322
+
323
+ main();
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "tokrepo-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for TokRepo — search, browse, and install AI assets from any MCP client (Claude Code, Cursor, Codex, Gemini CLI).",
5
+ "bin": {
6
+ "tokrepo-mcp-server": "bin/server.js"
7
+ },
8
+ "files": [
9
+ "bin/"
10
+ ],
11
+ "license": "MIT",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/tokrepo/mcp-server.git"
15
+ },
16
+ "homepage": "https://tokrepo.com",
17
+ "keywords": [
18
+ "mcp",
19
+ "mcp-server",
20
+ "tokrepo",
21
+ "ai-assets",
22
+ "claude",
23
+ "cursor",
24
+ "codex",
25
+ "gemini",
26
+ "skills",
27
+ "prompts",
28
+ "workflows",
29
+ "agent"
30
+ ],
31
+ "engines": {
32
+ "node": ">=18"
33
+ }
34
+ }