oom-x402-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/mcp.js +203 -0
- package/package.json +14 -0
- package/products-all.json +16489 -0
package/mcp.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
4
|
+
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
5
|
+
const {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
ListResourcesRequestSchema,
|
|
9
|
+
ReadResourceRequestSchema,
|
|
10
|
+
} = require('@modelcontextprotocol/sdk/types.js');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
// Load products-all.json at startup
|
|
15
|
+
const productsPath = path.join(__dirname, 'products-all.json');
|
|
16
|
+
let productsData = [];
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const rawData = fs.readFileSync(productsPath, 'utf8');
|
|
20
|
+
productsData = JSON.parse(rawData);
|
|
21
|
+
console.error(`Loaded ${productsData.length} endpoints from products-all.json`);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error(`Error loading products-all.json: ${error.message}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Create MCP server
|
|
28
|
+
const server = new Server(
|
|
29
|
+
{
|
|
30
|
+
name: 'x402-endpoint-catalog',
|
|
31
|
+
version: '1.0.0',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
capabilities: {
|
|
35
|
+
tools: {},
|
|
36
|
+
resources: {},
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// List available tools
|
|
42
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
43
|
+
return {
|
|
44
|
+
tools: [
|
|
45
|
+
{
|
|
46
|
+
name: 'search_endpoints',
|
|
47
|
+
description: 'Search for API endpoints by query string. Searches across endpoint names, descriptions, and categories.',
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
query: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
description: 'Search query to match against endpoint names, descriptions, and categories',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
required: ['query'],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'list_categories',
|
|
61
|
+
description: 'List all unique endpoint categories with their counts',
|
|
62
|
+
inputSchema: {
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Handle tool calls
|
|
72
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
73
|
+
const { name, arguments: args } = request.params;
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
if (name === 'search_endpoints') {
|
|
77
|
+
const { query } = args;
|
|
78
|
+
const searchQuery = query.toLowerCase();
|
|
79
|
+
|
|
80
|
+
// Search across name, description, and category
|
|
81
|
+
const results = productsData.filter((endpoint) => {
|
|
82
|
+
return (
|
|
83
|
+
endpoint.name?.toLowerCase().includes(searchQuery) ||
|
|
84
|
+
endpoint.description?.toLowerCase().includes(searchQuery) ||
|
|
85
|
+
endpoint.category?.toLowerCase().includes(searchQuery) ||
|
|
86
|
+
endpoint.id?.toLowerCase().includes(searchQuery)
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
content: [
|
|
92
|
+
{
|
|
93
|
+
type: 'text',
|
|
94
|
+
text: JSON.stringify(
|
|
95
|
+
{
|
|
96
|
+
query,
|
|
97
|
+
count: results.length,
|
|
98
|
+
results: results.map((ep) => ({
|
|
99
|
+
id: ep.id,
|
|
100
|
+
name: ep.name,
|
|
101
|
+
category: ep.category,
|
|
102
|
+
price_usd: ep.price_usd,
|
|
103
|
+
description: ep.description,
|
|
104
|
+
route_path: ep.route_path,
|
|
105
|
+
source_url: ep.source_url,
|
|
106
|
+
response_fields: ep.response_fields,
|
|
107
|
+
})),
|
|
108
|
+
},
|
|
109
|
+
null,
|
|
110
|
+
2
|
|
111
|
+
),
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
} else if (name === 'list_categories') {
|
|
116
|
+
// Count endpoints per category
|
|
117
|
+
const categoryCounts = {};
|
|
118
|
+
productsData.forEach((endpoint) => {
|
|
119
|
+
const category = endpoint.category || 'uncategorized';
|
|
120
|
+
categoryCounts[category] = (categoryCounts[category] || 0) + 1;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Convert to sorted array
|
|
124
|
+
const categories = Object.entries(categoryCounts)
|
|
125
|
+
.map(([category, count]) => ({ category, count }))
|
|
126
|
+
.sort((a, b) => b.count - a.count);
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
131
|
+
type: 'text',
|
|
132
|
+
text: JSON.stringify(
|
|
133
|
+
{
|
|
134
|
+
total_categories: categories.length,
|
|
135
|
+
total_endpoints: productsData.length,
|
|
136
|
+
categories,
|
|
137
|
+
},
|
|
138
|
+
null,
|
|
139
|
+
2
|
|
140
|
+
),
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
} else {
|
|
145
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
return {
|
|
149
|
+
content: [
|
|
150
|
+
{
|
|
151
|
+
type: 'text',
|
|
152
|
+
text: JSON.stringify({ error: error.message }),
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
isError: true,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// List available resources
|
|
161
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
162
|
+
return {
|
|
163
|
+
resources: [
|
|
164
|
+
{
|
|
165
|
+
uri: 'endpoint_catalog',
|
|
166
|
+
name: 'Endpoint Catalog',
|
|
167
|
+
description: 'Complete catalog of all available API endpoints with full details',
|
|
168
|
+
mimeType: 'application/json',
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Handle resource reads
|
|
175
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
176
|
+
const { uri } = request.params;
|
|
177
|
+
|
|
178
|
+
if (uri === 'endpoint_catalog') {
|
|
179
|
+
return {
|
|
180
|
+
contents: [
|
|
181
|
+
{
|
|
182
|
+
uri,
|
|
183
|
+
mimeType: 'application/json',
|
|
184
|
+
text: JSON.stringify(productsData, null, 2),
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
};
|
|
188
|
+
} else {
|
|
189
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Start server with stdio transport
|
|
194
|
+
async function main() {
|
|
195
|
+
const transport = new StdioServerTransport();
|
|
196
|
+
await server.connect(transport);
|
|
197
|
+
console.error('x402 MCP server running on stdio');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
main().catch((error) => {
|
|
201
|
+
console.error('Fatal error:', error);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "oom-x402-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for the Orders of Magnitude x402 endpoint catalog — search 1,459 pay-per-call API endpoints",
|
|
5
|
+
"main": "mcp.js",
|
|
6
|
+
"bin": { "oom-x402-mcp": "mcp.js" },
|
|
7
|
+
"files": ["mcp.js", "products-all.json"],
|
|
8
|
+
"publishConfig": { "access": "public" },
|
|
9
|
+
"keywords": ["mcp", "model-context-protocol", "x402", "api-catalog", "agents"],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "*"
|
|
13
|
+
}
|
|
14
|
+
}
|