@pipeworx/mcp-gunbroker 0.1.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/LICENSE +21 -0
- package/README.md +55 -0
- package/package.json +20 -0
- package/server.json +18 -0
- package/src/index.ts +191 -0
- package/tsconfig.json +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pipeworx
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# mcp-gunbroker
|
|
2
|
+
|
|
3
|
+
GunBroker MCP Pack — wraps the GunBroker firearms-marketplace API.
|
|
4
|
+
|
|
5
|
+
Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 250+ live data sources.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | Description |
|
|
10
|
+
|------|-------------|
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
Add to your MCP client (Claude Desktop, Cursor, Windsurf, etc.):
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"mcpServers": {
|
|
19
|
+
"gunbroker": {
|
|
20
|
+
"url": "https://gateway.pipeworx.io/gunbroker/mcp"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or connect to the full Pipeworx gateway for access to all 250+ data sources:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"pipeworx": {
|
|
32
|
+
"url": "https://gateway.pipeworx.io/mcp"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Using with ask_pipeworx
|
|
39
|
+
|
|
40
|
+
Instead of calling tools directly, you can ask questions in plain English:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
ask_pipeworx({ question: "your question about Gunbroker data" })
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The gateway picks the right tool and fills the arguments automatically.
|
|
47
|
+
|
|
48
|
+
## More
|
|
49
|
+
|
|
50
|
+
- [All tools and guides](https://github.com/pipeworx-io/examples)
|
|
51
|
+
- [pipeworx.io](https://pipeworx.io)
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pipeworx/mcp-gunbroker",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "GunBroker MCP Pack — wraps the GunBroker firearms-marketplace API.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"keywords": ["mcp", "mcp-server", "model-context-protocol", "pipeworx", "gunbroker"],
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/pipeworx-io/mcp-gunbroker"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"typecheck": "tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.7.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.pipeworx-io/gunbroker",
|
|
4
|
+
"title": "Gunbroker",
|
|
5
|
+
"description": "GunBroker MCP Pack — wraps the GunBroker firearms-marketplace API.",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"websiteUrl": "https://pipeworx.io/packs/gunbroker",
|
|
8
|
+
"repository": {
|
|
9
|
+
"url": "https://github.com/pipeworx-io/mcp-gunbroker",
|
|
10
|
+
"source": "github"
|
|
11
|
+
},
|
|
12
|
+
"remotes": [
|
|
13
|
+
{
|
|
14
|
+
"type": "streamable-http",
|
|
15
|
+
"url": "https://gateway.pipeworx.io/gunbroker/mcp"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
interface McpToolDefinition {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: 'object';
|
|
6
|
+
properties: Record<string, unknown>;
|
|
7
|
+
required?: string[];
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface McpToolExport {
|
|
12
|
+
tools: McpToolDefinition[];
|
|
13
|
+
callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>;
|
|
14
|
+
meter?: { credits: number };
|
|
15
|
+
cost?: Record<string, unknown>;
|
|
16
|
+
provider?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* GunBroker MCP Pack — wraps the GunBroker firearms-marketplace API.
|
|
21
|
+
*
|
|
22
|
+
* BYO credentials (nothing is stored; every call is stateless):
|
|
23
|
+
* - _apiKey: a GunBroker developer key, sent in the X-DevKey header on EVERY
|
|
24
|
+
* request. Required for all tools. Request one from GunBroker API Support.
|
|
25
|
+
* - username + password: required ONLY for seller tools (seller_orders). Used
|
|
26
|
+
* to mint a short-lived access token via POST /Users/AccessToken, which is
|
|
27
|
+
* then passed in the X-AccessToken header alongside X-DevKey.
|
|
28
|
+
*
|
|
29
|
+
* Endpoints used: GET /Items (search), GET /Items/{itemID} (detail),
|
|
30
|
+
* GET /OrdersSold (seller, token-gated), POST /Users/AccessToken (token mint).
|
|
31
|
+
*
|
|
32
|
+
* Base defaults to PRODUCTION (https://api.gunbroker.com/v1). GunBroker also
|
|
33
|
+
* runs a sandbox at https://api.sandbox.gunbroker.com/v1 with separate dev keys
|
|
34
|
+
* and accounts; this pack targets production.
|
|
35
|
+
*
|
|
36
|
+
* Quirks verified against the live API:
|
|
37
|
+
* - A custom User-Agent header is mandatory — without it GunBroker returns
|
|
38
|
+
* 403 text/plain "Custom User Agent string required per GunBroker API Support".
|
|
39
|
+
* - Missing/invalid X-DevKey returns 401 JSON with a developerMessage field.
|
|
40
|
+
* Because error bodies may be JSON or plain text, we surface the raw body text.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const BASE = 'https://api.gunbroker.com/v1';
|
|
45
|
+
const UA = 'pipeworx-mcp-gunbroker/1.0 (+https://pipeworx.io)';
|
|
46
|
+
|
|
47
|
+
function devHeaders(apiKey: string): Record<string, string> {
|
|
48
|
+
return {
|
|
49
|
+
'X-DevKey': apiKey,
|
|
50
|
+
'User-Agent': UA,
|
|
51
|
+
Accept: 'application/json',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function gbGet(
|
|
56
|
+
apiKey: string,
|
|
57
|
+
path: string,
|
|
58
|
+
params?: URLSearchParams,
|
|
59
|
+
accessToken?: string,
|
|
60
|
+
): Promise<unknown> {
|
|
61
|
+
const qs = params?.toString();
|
|
62
|
+
const url = `${BASE}${path}${qs ? `?${qs}` : ''}`;
|
|
63
|
+
const headers = devHeaders(apiKey);
|
|
64
|
+
if (accessToken) headers['X-AccessToken'] = accessToken;
|
|
65
|
+
const res = await fetch(url, { headers });
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
const body = await res.text();
|
|
68
|
+
throw new Error(`GunBroker: ${res.status} ${body.slice(0, 200)}`);
|
|
69
|
+
}
|
|
70
|
+
return res.json();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Exchange username + password for a seller access token. Stateless: the token
|
|
75
|
+
* is minted on every seller call and never persisted.
|
|
76
|
+
*/
|
|
77
|
+
async function getAccessToken(apiKey: string, username: string, password: string): Promise<string> {
|
|
78
|
+
const res = await fetch(`${BASE}/Users/AccessToken`, {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { ...devHeaders(apiKey), 'Content-Type': 'application/json' },
|
|
81
|
+
body: JSON.stringify({ username, password }),
|
|
82
|
+
});
|
|
83
|
+
if (!res.ok) {
|
|
84
|
+
const body = await res.text();
|
|
85
|
+
throw new Error(`GunBroker: ${res.status} ${body.slice(0, 200)}`);
|
|
86
|
+
}
|
|
87
|
+
const data = (await res.json()) as Record<string, unknown>;
|
|
88
|
+
const token = (data.accessToken ?? data.AccessToken) as string | undefined;
|
|
89
|
+
if (!token) throw new Error(`GunBroker: token exchange returned no accessToken`);
|
|
90
|
+
return token;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// -- Tool definitions --------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
const tools: McpToolExport['tools'] = [
|
|
96
|
+
{
|
|
97
|
+
name: 'gunbroker_search_items',
|
|
98
|
+
description:
|
|
99
|
+
'Search active GunBroker firearms-marketplace listings by keyword (e.g. "Glock 19", "AR-15 lower", "Henri 22 rifle"). Returns matching live listings with item IDs, titles, current/buy-now prices, bid counts, and time left. Public — needs only a GunBroker developer key (_apiKey). Supports pagination and sort.',
|
|
100
|
+
inputSchema: {
|
|
101
|
+
type: 'object' as const,
|
|
102
|
+
properties: {
|
|
103
|
+
_apiKey: { type: 'string', description: 'GunBroker developer key (X-DevKey). Required.' },
|
|
104
|
+
Keywords: { type: 'string', description: 'Search keywords for active listings.' },
|
|
105
|
+
PageSize: { type: 'number', description: 'Results per page (default 25, max 300).' },
|
|
106
|
+
PageIndex: { type: 'number', description: 'Page number, 1-based (default 1).' },
|
|
107
|
+
Sort: {
|
|
108
|
+
type: 'number',
|
|
109
|
+
description:
|
|
110
|
+
'GunBroker sort code (e.g. 0=best match, 1=ending soonest, 13=lowest price). Optional.',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
required: ['_apiKey'],
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'gunbroker_get_item',
|
|
118
|
+
description:
|
|
119
|
+
'Get full details for a single GunBroker listing by item ID — title, description, prices, seller, shipping, condition, category, and time remaining. Public — needs only a GunBroker developer key (_apiKey).',
|
|
120
|
+
inputSchema: {
|
|
121
|
+
type: 'object' as const,
|
|
122
|
+
properties: {
|
|
123
|
+
_apiKey: { type: 'string', description: 'GunBroker developer key (X-DevKey). Required.' },
|
|
124
|
+
itemID: { type: 'string', description: 'GunBroker numeric item ID.' },
|
|
125
|
+
},
|
|
126
|
+
required: ['_apiKey', 'itemID'],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'gunbroker_seller_orders',
|
|
131
|
+
description:
|
|
132
|
+
"List the authenticated seller's sold orders (GunBroker /OrdersSold) — order IDs, buyers, item titles, totals, payment and shipping status. Seller tool: needs a GunBroker developer key (_apiKey) PLUS the seller's GunBroker username and password, which are exchanged for a short-lived access token on each call.",
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: 'object' as const,
|
|
135
|
+
properties: {
|
|
136
|
+
_apiKey: { type: 'string', description: 'GunBroker developer key (X-DevKey). Required.' },
|
|
137
|
+
username: { type: 'string', description: 'GunBroker seller account username. Required.' },
|
|
138
|
+
password: { type: 'string', description: 'GunBroker seller account password. Required.' },
|
|
139
|
+
PageSize: { type: 'number', description: 'Results per page (default 25, max 300).' },
|
|
140
|
+
PageIndex: { type: 'number', description: 'Page number, 1-based (default 1).' },
|
|
141
|
+
TimeFrame: {
|
|
142
|
+
type: 'number',
|
|
143
|
+
description:
|
|
144
|
+
'GunBroker time-frame filter for sold orders (e.g. recent N days). Optional.',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
required: ['_apiKey', 'username', 'password'],
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
// -- callTool dispatcher -----------------------------------------------------
|
|
153
|
+
|
|
154
|
+
async function callTool(name: string, args: Record<string, unknown>): Promise<unknown> {
|
|
155
|
+
const apiKey = args._apiKey as string | undefined;
|
|
156
|
+
const username = args.username as string | undefined;
|
|
157
|
+
const password = args.password as string | undefined;
|
|
158
|
+
delete args._context;
|
|
159
|
+
delete args._apiKey;
|
|
160
|
+
delete args.username;
|
|
161
|
+
delete args.password;
|
|
162
|
+
|
|
163
|
+
if (!apiKey) throw new Error('_apiKey (GunBroker developer key) is required for all GunBroker tools');
|
|
164
|
+
|
|
165
|
+
switch (name) {
|
|
166
|
+
case 'gunbroker_search_items': {
|
|
167
|
+
const params = new URLSearchParams();
|
|
168
|
+
if (args.Keywords) params.set('Keywords', String(args.Keywords));
|
|
169
|
+
params.set('PageSize', String(Math.min(300, (args.PageSize as number) ?? 25)));
|
|
170
|
+
params.set('PageIndex', String((args.PageIndex as number) ?? 1));
|
|
171
|
+
if (args.Sort !== undefined) params.set('Sort', String(args.Sort));
|
|
172
|
+
return gbGet(apiKey, '/Items', params);
|
|
173
|
+
}
|
|
174
|
+
case 'gunbroker_get_item':
|
|
175
|
+
return gbGet(apiKey, `/Items/${encodeURIComponent(String(args.itemID))}`);
|
|
176
|
+
case 'gunbroker_seller_orders': {
|
|
177
|
+
if (!username || !password)
|
|
178
|
+
throw new Error('username and password are required for gunbroker_seller_orders');
|
|
179
|
+
const token = await getAccessToken(apiKey, username, password);
|
|
180
|
+
const params = new URLSearchParams();
|
|
181
|
+
params.set('PageSize', String(Math.min(300, (args.PageSize as number) ?? 25)));
|
|
182
|
+
params.set('PageIndex', String((args.PageIndex as number) ?? 1));
|
|
183
|
+
if (args.TimeFrame !== undefined) params.set('TimeFrame', String(args.TimeFrame));
|
|
184
|
+
return gbGet(apiKey, '/OrdersSold', params, token);
|
|
185
|
+
}
|
|
186
|
+
default:
|
|
187
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export default { tools, callTool, meter: { credits: 1 } } satisfies McpToolExport;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"declaration": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|