@mifactory-bot/spec-api 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/index.js +98 -0
  2. package/package.json +18 -0
  3. package/server.json +37 -0
package/index.js ADDED
@@ -0,0 +1,98 @@
1
+ const express = require('express');
2
+ const bodyParser = require('body-parser');
3
+ const Stripe = require('stripe');
4
+ const fetch = require('node-fetch');
5
+
6
+ const app = express();
7
+ app.use(bodyParser.json());
8
+
9
+ const API_KEY = process.env.API_KEY || 'secret-api-key';
10
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
11
+ const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
12
+
13
+ app.use((req, res, next) => {
14
+ const key = req.headers['x-api-key'];
15
+ if (!key || key !== API_KEY) {
16
+ return res.status(401).json({ error: 'Unauthorized' });
17
+ }
18
+ next();
19
+ });
20
+
21
+ async function callClaudeApi(prompt) {
22
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
23
+ method: 'POST',
24
+ headers: {
25
+ 'Content-Type': 'application/json',
26
+ 'x-api-key': ANTHROPIC_API_KEY
27
+ },
28
+ body: JSON.stringify({
29
+ model: 'claude-haiku-3-5',
30
+ messages: [{ role: 'user', content: prompt }]
31
+ })
32
+ });
33
+ if (!response.ok) {
34
+ const text = await response.text();
35
+ throw new Error('Claude API error: ' + text);
36
+ }
37
+ const data = await response.json();
38
+ return data.completion || data.choices?.[0]?.message?.content || '';
39
+ }
40
+
41
+ function buildConvertPrompt(text) {
42
+ return `Given the following document text:\n"""\n${text}\n"""\nExtract and structure it as an agent-readable specification with these fields:\n- Objective\n- Acceptance criteria\n- Constraints\n- Subtasks\n- Success metrics\nReturn the specification in JSON format.`;
43
+ }
44
+
45
+ function buildValidatePrompt(spec) {
46
+ return `You are a quality assessor for agent-readable specifications.
47
+ Given this JSON specification:\n${JSON.stringify(spec, null, 2)}
48
+ Provide a quality score from 0 to 100 and suggestions to improve it in JSON format.`;
49
+ }
50
+
51
+ app.post('/spec/convert', async (req, res) => {
52
+ const { documentText, paymentMethodId } = req.body;
53
+ if (!documentText || !paymentMethodId) {
54
+ return res.status(400).json({ error: 'Missing documentText or paymentMethodId' });
55
+ }
56
+ try {
57
+ // Charge $0.10 USD
58
+ await stripe.paymentIntents.create({
59
+ amount: 10, // cents
60
+ currency: 'usd',
61
+ payment_method: paymentMethodId,
62
+ confirm: true
63
+ });
64
+ const prompt = buildConvertPrompt(documentText);
65
+ const spec = await callClaudeApi(prompt);
66
+ res.json({ specification: JSON.parse(spec) });
67
+ } catch (err) {
68
+ res.status(500).json({ error: err.message });
69
+ }
70
+ });
71
+
72
+ app.post('/spec/validate', async (req, res) => {
73
+ const { specification, paymentMethodId } = req.body;
74
+ if (!specification || !paymentMethodId) {
75
+ return res.status(400).json({ error: 'Missing specification or paymentMethodId' });
76
+ }
77
+ try {
78
+ // Charge $0.05 USD
79
+ await stripe.paymentIntents.create({
80
+ amount: 5, // cents
81
+ currency: 'usd',
82
+ payment_method: paymentMethodId,
83
+ confirm: true
84
+ });
85
+ const prompt = buildValidatePrompt(specification);
86
+ const result = await callClaudeApi(prompt);
87
+ res.json({ validation: JSON.parse(result) });
88
+ } catch (err) {
89
+ res.status(500).json({ error: err.message });
90
+ }
91
+ });
92
+
93
+ app.get('/health', (req, res) => {
94
+ res.json({ status: 'ok' });
95
+ });
96
+
97
+ const PORT = process.env.PORT || 3000;
98
+ app.listen(PORT, () => console.log(`spec-api listening on port ${PORT}`));
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@mifactory-bot/spec-api",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "start": "node index.js"
7
+ },
8
+ "dependencies": {
9
+ "express": "^4.18.2",
10
+ "stripe": "^14.0.0",
11
+ "node-fetch": "^2.7.0"
12
+ },
13
+ "mcpName": "io.github.mifactory-bot/mifactory-spec-api",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/mifactory-bot/mifactory-spec-api.git"
17
+ }
18
+ }
package/server.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.mifactory-bot/mifactory-spec-api",
4
+ "description": "Convert any document into agent-readable specs. Accepts criteria, constraints and subtasks. $0.10/convert.",
5
+ "repository": {
6
+ "url": "https://github.com/mifactory-bot/mifactory-spec-api",
7
+ "source": "github"
8
+ },
9
+ "version": "1.0.0",
10
+ "packages": [
11
+ {
12
+ "registryType": "npm",
13
+ "identifier": "@mifactory-bot/spec-api",
14
+ "version": "1.0.0",
15
+ "transport": {
16
+ "type": "streamable-http",
17
+ "url": "https://spec-api-mcp.vercel.app/spec/convert"
18
+ },
19
+ "environmentVariables": [
20
+ {
21
+ "name": "STRIPE_SECRET_KEY",
22
+ "description": "Stripe secret key",
23
+ "isRequired": true,
24
+ "isSecret": true,
25
+ "format": "string"
26
+ },
27
+ {
28
+ "name": "ANTHROPIC_API_KEY",
29
+ "description": "Anthropic API key",
30
+ "isRequired": true,
31
+ "isSecret": true,
32
+ "format": "string"
33
+ }
34
+ ]
35
+ }
36
+ ]
37
+ }