metaphore-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.
- package/README.md +77 -0
- package/index.js +151 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# METAPHORE MCP Server
|
|
2
|
+
|
|
3
|
+
**Turn any technical concept into a clear human story — right inside Claude Code.**
|
|
4
|
+
|
|
5
|
+
[](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
### 1. Get your METAPHORE key
|
|
10
|
+
|
|
11
|
+
Sign up at [metaphore.app](https://metaphore.app) → Account → copy your `mkey`.
|
|
12
|
+
|
|
13
|
+
### 2. Add to Claude Code
|
|
14
|
+
|
|
15
|
+
Run:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
claude mcp add metaphore -e METAPHORE_KEY=mkey_xxxxx -- npx -y metaphore-mcp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or manually add to your `.claude/mcp.json`:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"metaphore": {
|
|
27
|
+
"command": "npx",
|
|
28
|
+
"args": ["-y", "metaphore-mcp"],
|
|
29
|
+
"env": {
|
|
30
|
+
"METAPHORE_KEY": "mkey_xxxxx"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Replace `mkey_xxxxx` with your actual key.
|
|
38
|
+
|
|
39
|
+
### 3. Use it
|
|
40
|
+
|
|
41
|
+
In Claude Code, ask it to use the metaphore tool:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
Use metaphore to explain what a reverse proxy is
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Or with parameters:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
Use metaphore to explain our microservices architecture --audience ceo --depth short
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Parameters
|
|
54
|
+
|
|
55
|
+
| Parameter | Values | Description |
|
|
56
|
+
|-----------|--------|-------------|
|
|
57
|
+
| `input` | any text | **Required.** The technical concept to transform |
|
|
58
|
+
| `audience` | `ceo`, `junior-dev`, `12-year-old`, `founder`, `operator`, `salesperson` | Who the explanation is for |
|
|
59
|
+
| `style` | `building`, `restaurant`, `postal`, `hospital`, `workshop`, `theater` | Analogy domain |
|
|
60
|
+
| `depth` | `short`, `full` (default) | One paragraph vs. full 3-act story |
|
|
61
|
+
| `objective` | `understand`, `decide`, `debug`, `explain-to-someone`, `learn` | What the reader needs to do next |
|
|
62
|
+
| `language` | Any language code (`fr`, `es`, `de`, ...) | Override output language |
|
|
63
|
+
|
|
64
|
+
## Pricing
|
|
65
|
+
|
|
66
|
+
Your METAPHORE key includes **300 free IDE transforms/month**. Upgrade to Pro ($5/month) for unlimited access at [metaphore.app](https://metaphore.app).
|
|
67
|
+
|
|
68
|
+
## Requirements
|
|
69
|
+
|
|
70
|
+
- Node.js 18+
|
|
71
|
+
- A METAPHORE key ([get one free](https://metaphore.app))
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
[CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
|
76
|
+
|
|
77
|
+
Created by [Philippe Nerette](https://metaphore.app).
|
package/index.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
const API_URL = process.env.METAPHORE_API_URL || "https://api.metaphore.app";
|
|
8
|
+
const METAPHORE_KEY = process.env.METAPHORE_KEY;
|
|
9
|
+
|
|
10
|
+
if (!METAPHORE_KEY) {
|
|
11
|
+
process.stderr.write(
|
|
12
|
+
"Error: METAPHORE_KEY environment variable is required.\n" +
|
|
13
|
+
"Get your key at https://metaphore.app → Account → METAPHORE Key (mkey)\n"
|
|
14
|
+
);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const server = new McpServer({
|
|
19
|
+
name: "metaphore",
|
|
20
|
+
version: "1.0.0",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
server.registerTool(
|
|
24
|
+
"metaphore",
|
|
25
|
+
{
|
|
26
|
+
title: "METAPHORE",
|
|
27
|
+
description:
|
|
28
|
+
"Turn any technical concept, architecture, incident, data flow, or LLM output into a clear human story. " +
|
|
29
|
+
"Returns a structured narrative with hero, conflict, resolution, reality mapping, and moral.",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
input: z
|
|
32
|
+
.string()
|
|
33
|
+
.describe(
|
|
34
|
+
"The technical concept or input to transform into a human story"
|
|
35
|
+
),
|
|
36
|
+
audience: z
|
|
37
|
+
.enum([
|
|
38
|
+
"ceo",
|
|
39
|
+
"junior-dev",
|
|
40
|
+
"12-year-old",
|
|
41
|
+
"founder",
|
|
42
|
+
"operator",
|
|
43
|
+
"salesperson",
|
|
44
|
+
])
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Who the explanation is for"),
|
|
47
|
+
style: z
|
|
48
|
+
.enum([
|
|
49
|
+
"building",
|
|
50
|
+
"restaurant",
|
|
51
|
+
"postal",
|
|
52
|
+
"hospital",
|
|
53
|
+
"workshop",
|
|
54
|
+
"theater",
|
|
55
|
+
])
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("Analogy domain / narrative world"),
|
|
58
|
+
depth: z
|
|
59
|
+
.enum(["short", "full"])
|
|
60
|
+
.optional()
|
|
61
|
+
.describe("Short (1 paragraph) or full (3-act story). Default: full"),
|
|
62
|
+
objective: z
|
|
63
|
+
.enum(["understand", "decide", "debug", "explain-to-someone", "learn"])
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("What the reader needs to do next"),
|
|
66
|
+
language: z
|
|
67
|
+
.string()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe(
|
|
70
|
+
"Output language code (e.g. fr, es, de). Auto-detected from input by default"
|
|
71
|
+
),
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
async ({ input, audience, style, depth, objective, language }) => {
|
|
75
|
+
const body = {
|
|
76
|
+
input,
|
|
77
|
+
mkey: METAPHORE_KEY,
|
|
78
|
+
channel: "ide",
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (audience) body.audience = audience;
|
|
82
|
+
if (style) body.style = style;
|
|
83
|
+
if (depth) body.depth = depth;
|
|
84
|
+
if (objective) body.objective = objective;
|
|
85
|
+
if (language) body.language = language;
|
|
86
|
+
|
|
87
|
+
let response;
|
|
88
|
+
try {
|
|
89
|
+
response = await fetch(`${API_URL}/transform`, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: { "Content-Type": "application/json" },
|
|
92
|
+
body: JSON.stringify(body),
|
|
93
|
+
});
|
|
94
|
+
} catch (err) {
|
|
95
|
+
return {
|
|
96
|
+
content: [
|
|
97
|
+
{
|
|
98
|
+
type: "text",
|
|
99
|
+
text: `Connection error: could not reach ${API_URL}. Check your network.`,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
isError: true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
const status = response.status;
|
|
108
|
+
let message;
|
|
109
|
+
try {
|
|
110
|
+
const data = await response.json();
|
|
111
|
+
message = data.error || data.message || response.statusText;
|
|
112
|
+
} catch {
|
|
113
|
+
message = response.statusText;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (status === 401) {
|
|
117
|
+
message = `Invalid METAPHORE_KEY. Check your key at https://metaphore.app → Account.`;
|
|
118
|
+
} else if (status === 403) {
|
|
119
|
+
message = `Quota exceeded. Upgrade to Pro at https://metaphore.app or add your own LLM key.`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
content: [{ type: "text", text: `Error ${status}: ${message}` }],
|
|
124
|
+
isError: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const data = await response.json();
|
|
129
|
+
|
|
130
|
+
const meta = [];
|
|
131
|
+
if (data.model) meta.push(`Model: ${data.model}`);
|
|
132
|
+
if (data.provider) meta.push(`Provider: ${data.provider}`);
|
|
133
|
+
if (data.usage) {
|
|
134
|
+
const { inputTokens, outputTokens } = data.usage;
|
|
135
|
+
if (inputTokens || outputTokens) {
|
|
136
|
+
meta.push(`Tokens: ${inputTokens || 0} in / ${outputTokens || 0} out`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const text = meta.length
|
|
141
|
+
? `${data.result}\n\n---\n_${meta.join(" · ")}_`
|
|
142
|
+
: data.result;
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
content: [{ type: "text", text }],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const transport = new StdioServerTransport();
|
|
151
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "metaphore-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "METAPHORE MCP server — turn any technical concept into a clear human story, right inside Claude Code",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"metaphore-mcp": "index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "index.js",
|
|
10
|
+
"license": "CC-BY-NC-SA-4.0",
|
|
11
|
+
"author": "Philippe Nerette",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"metaphore",
|
|
14
|
+
"mcp",
|
|
15
|
+
"claude",
|
|
16
|
+
"model-context-protocol",
|
|
17
|
+
"technical-writing",
|
|
18
|
+
"analogy",
|
|
19
|
+
"explanation"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/metaphore-app/metaphore-mcp"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
30
|
+
"zod": "^3.25.0"
|
|
31
|
+
}
|
|
32
|
+
}
|