nicepick-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 (3) hide show
  1. package/README.md +39 -0
  2. package/index.mjs +119 -0
  3. package/package.json +27 -0
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # NicePick MCP — the verdict layer for AI agents
2
+
3
+ Your docs tool (Context7) tells an agent how to use the library it *already chose*.
4
+ **NicePick tells it which one to choose.**
5
+
6
+ 900+ structured, opinionated tool/library/framework comparisons. No hedging — every
7
+ query returns a winner and a decisive reason. Backed by [nicepick.dev](https://nicepick.dev).
8
+
9
+ ## Tools
10
+
11
+ - **`compare_tools(tool_a, tool_b)`** — head-to-head verdict. `postgresql` vs `mysql`,
12
+ `react` vs `svelte`, `vite` vs `webpack`. Returns the pick, why, and when to use each.
13
+ - **`recommend_tool(query)`** — best pick for a use-case. `"best vector database"`,
14
+ `"best python web framework"`.
15
+
16
+ ## Install (Claude Desktop / Cursor / any MCP client)
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "nicepick": {
22
+ "command": "npx",
23
+ "args": ["-y", "nicepick-mcp"]
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ Or run directly: `node index.mjs` (talks MCP over stdio).
30
+
31
+ ## Why
32
+
33
+ LLMs hallucinate non-existent packages in ~20% of generated code. Picking the *right*
34
+ real tool is a decision they're bad at and there's no current source for — registries
35
+ rank by popularity, docs assume you've chosen, and chat is un-opinionated. NicePick is
36
+ the structured, opinionated verdict — the one shape a model can't reliably produce from
37
+ training data.
38
+
39
+ MIT. No API key required.
package/index.mjs ADDED
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+ // NicePick MCP server — the opinionated "which tool should I pick?" decision layer
3
+ // for AI agents. Context7 (57.8k stars) owns docs-for-a-library-you-already-chose;
4
+ // NOBODY owns the verdict ("X vs Y — pick X because…"). NicePick has 900 structured
5
+ // opinionated comparisons + taxonomy — exactly the corpus an LLM can't reliably
6
+ // produce from training data (it hallucinates dead packages ~20% of the time).
7
+ //
8
+ // Tools exposed:
9
+ // compare_tools(a, b) → Nice Pick's verdict on a head-to-head
10
+ // recommend_tool(query) → best pick for a use-case / category
11
+ //
12
+ // Backed by the live public API at nicepick.dev/api/recommend (CORS, no key needed).
13
+ // Distribute via the MCP registry + Smithery/Glama/PulseMCP. Install:
14
+ // npx @nicepick/mcp (once published) or run this file directly with node.
15
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
16
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
17
+ import {
18
+ CallToolRequestSchema,
19
+ ListToolsRequestSchema,
20
+ } from "@modelcontextprotocol/sdk/types.js";
21
+
22
+ const BASE = process.env.NICEPICK_API || "https://nicepick.dev";
23
+
24
+ async function api(path) {
25
+ const res = await fetch(`${BASE}${path}`, {
26
+ headers: { "user-agent": "nicepick-mcp/1.0" },
27
+ });
28
+ if (!res.ok) throw new Error(`NicePick API ${res.status}`);
29
+ return res.json();
30
+ }
31
+
32
+ const server = new Server(
33
+ { name: "nicepick", version: "1.0.0" },
34
+ { capabilities: { tools: {} } }
35
+ );
36
+
37
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
38
+ tools: [
39
+ {
40
+ name: "compare_tools",
41
+ description:
42
+ "Get Nice Pick's opinionated verdict on two tools/libraries/frameworks head-to-head (e.g. 'postgres' vs 'mysql', 'react' vs 'svelte'). Returns the winner, a decisive reason, and when to use each. Use this when an agent or user must choose between two specific options and wants a real verdict, not a hedge.",
43
+ inputSchema: {
44
+ type: "object",
45
+ properties: {
46
+ tool_a: { type: "string", description: "First tool/library/framework (e.g. 'postgresql')" },
47
+ tool_b: { type: "string", description: "Second tool/library/framework (e.g. 'mysql')" },
48
+ },
49
+ required: ["tool_a", "tool_b"],
50
+ },
51
+ },
52
+ {
53
+ name: "recommend_tool",
54
+ description:
55
+ "Get Nice Pick's recommendation for a use-case or category (e.g. 'best react state management', 'best vector database', 'best python web framework'). Returns the top pick plus ranked alternatives with a decisive rationale.",
56
+ inputSchema: {
57
+ type: "object",
58
+ properties: {
59
+ query: { type: "string", description: "The use-case or 'best X' query" },
60
+ },
61
+ required: ["query"],
62
+ },
63
+ },
64
+ ],
65
+ }));
66
+
67
+ function fmtComparison(d) {
68
+ let out = `## Nice Pick: ${d.pick} wins (${d.title})\n\n`;
69
+ if (d.nice_says) out += `**Verdict:** ${d.nice_says}\n\n`;
70
+ if (d.pick_reason) out += `${d.pick_reason}\n\n`;
71
+ if (d.verdict) {
72
+ if (d.verdict.useTool1If) out += `- Use **${d.tool1?.name || "the first"}** if: ${d.verdict.useTool1If}\n`;
73
+ if (d.verdict.useTool2If) out += `- Use **${d.tool2?.name || "the second"}** if: ${d.verdict.useTool2If}\n`;
74
+ }
75
+ out += `\n— ${BASE}/compare/${d.slug || ""}`;
76
+ return out;
77
+ }
78
+
79
+ function fmtRecommendation(d) {
80
+ let out = `## Nice Pick: ${d.pick}\n\n`;
81
+ if (d.nice_says) out += `${d.nice_says}\n\n`;
82
+ if (Array.isArray(d.tools) && d.tools.length) {
83
+ out += `Ranked:\n`;
84
+ for (const t of d.tools.slice(0, 5)) {
85
+ out += `${t.rank}. **${t.name}**${t.tagline ? ` — ${t.tagline}` : ""}\n`;
86
+ }
87
+ }
88
+ return out;
89
+ }
90
+
91
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
92
+ const { name, arguments: args } = req.params;
93
+ try {
94
+ if (name === "compare_tools") {
95
+ const a = encodeURIComponent(args.tool_a.toLowerCase().trim());
96
+ const b = encodeURIComponent(args.tool_b.toLowerCase().trim());
97
+ const d = await api(`/api/recommend?a=${a}&b=${b}`);
98
+ const text =
99
+ d.type === "comparison"
100
+ ? fmtComparison(d)
101
+ : `Nice Pick doesn't have a head-to-head on those two yet. Closest: ${JSON.stringify(d).slice(0, 200)}`;
102
+ return { content: [{ type: "text", text }] };
103
+ }
104
+ if (name === "recommend_tool") {
105
+ const q = encodeURIComponent(args.query.trim());
106
+ const d = await api(`/api/recommend?q=${q}`);
107
+ const text =
108
+ d.type === "comparison" ? fmtComparison(d) : fmtRecommendation(d);
109
+ return { content: [{ type: "text", text }] };
110
+ }
111
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
112
+ } catch (e) {
113
+ return { content: [{ type: "text", text: `NicePick error: ${e.message}` }], isError: true };
114
+ }
115
+ });
116
+
117
+ const transport = new StdioServerTransport();
118
+ await server.connect(transport);
119
+ console.error("NicePick MCP server running (stdio)");
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "nicepick-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Opinionated 'which tool should I pick?' verdicts for AI agents — the decision layer next to your docs. 900+ structured tool/library/framework comparisons.",
5
+ "type": "module",
6
+ "bin": {
7
+ "nicepick-mcp": "index.mjs"
8
+ },
9
+ "main": "index.mjs",
10
+ "files": ["index.mjs", "README.md"],
11
+ "keywords": [
12
+ "mcp",
13
+ "model-context-protocol",
14
+ "ai-agents",
15
+ "tool-comparison",
16
+ "tech-stack",
17
+ "developer-tools",
18
+ "claude",
19
+ "cursor"
20
+ ],
21
+ "engines": { "node": ">=18" },
22
+ "dependencies": {
23
+ "@modelcontextprotocol/sdk": "^1.0.0"
24
+ },
25
+ "license": "MIT",
26
+ "homepage": "https://nicepick.dev"
27
+ }