@reneturcios/ballpark 0.0.0-stage → 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rene Turcios
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 CHANGED
@@ -1,3 +1,95 @@
1
- # Temporary Holding Version
1
+ # ballpark
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A forwarding client for the published Dribba MCP remote.
4
+ Dribba is a product studio; its remote MCP server exposes
5
+ public studio data — its services, its case studies, a
6
+ ballpark budget calculator, structured company facts, and
7
+ a contact-request tool. There is no installable package
8
+ for that remote, so ballpark gives it a local stdio MCP
9
+ server and a small CLI: `tools/list` is answered locally
10
+ from the verbatim live capture of the remote, and every
11
+ `tools/call` is forwarded to the remote over streamable
12
+ HTTP.
13
+
14
+ The upstream result passes through verbatim, including
15
+ `structuredContent` and any `isError` flag. Failures on
16
+ the way there or back are reported as an error result
17
+ whose text starts with `ballpark error: `.
18
+
19
+ ## Upstream and the override
20
+
21
+ - Upstream: `https://dribba.com/mcp` (pinned).
22
+ - Override: set `DRIBBA_MCP_URL` to point the client at a
23
+ different endpoint. This override is a reconstruction:
24
+ no override is published for the remote-only original.
25
+ - The client handshake is `initialize` (protocol version
26
+ `2025-03-26`, client `ballpark` / `0.1.0`), then
27
+ `notifications/initialized`, then `tools/call`. When the
28
+ upstream issues an `Mcp-Session-Id`, it is honoured on
29
+ every later request. Responses may be plain JSON or an
30
+ SSE stream.
31
+ - Argument validation is local: an unknown tool or
32
+ malformed arguments fail before anything is forwarded.
33
+ `submit_contact_request` is forwarded like the other
34
+ tools; its captured description — which asks for
35
+ explicit user confirmation of the exact name, email and
36
+ message before calling — is served verbatim, and this
37
+ rebuild adds no gating of its own.
38
+
39
+ ## Install and run
40
+
41
+ ```bash
42
+ npx -y @reneturcios/ballpark mcp # stdio MCP server
43
+ npx -y @reneturcios/ballpark --version # 0.1.0
44
+ npx -y @reneturcios/ballpark list_services '{"limit":3}'
45
+ npx -y @reneturcios/ballpark estimate_project '{"platforms":["ios","web"],"complexity":"standard"}'
46
+ npx -y @reneturcios/ballpark list_projects '{"industry":"fintech"}'
47
+ npx -y @reneturcios/ballpark company_facts '{"fields":["offices","distinctions"]}'
48
+ ```
49
+
50
+ Each tool subcommand takes one JSON argument string and
51
+ prints the forwarded result. Exit codes: 0 on success,
52
+ 1 on an error result, 2 on a usage or validation error.
53
+
54
+ ## MCP client config
55
+
56
+ ```json
57
+ {
58
+ "mcpServers": {
59
+ "ballpark": {
60
+ "command": "npx",
61
+ "args": ["-y", "@reneturcios/ballpark", "mcp"]
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## Docker
68
+
69
+ ```bash
70
+ docker run -i ghcr.io/cloudcorprecords/ballpark # stdio MCP server
71
+ docker run --rm ghcr.io/cloudcorprecords/ballpark --version # CLI
72
+ ```
73
+
74
+ ## Tools
75
+
76
+ | Tool | What it does |
77
+ |---|---|
78
+ | `list_services` | List Dribba's services with a one-line summary and URL. |
79
+ | `estimate_project` | Ballpark budget and range for an app/product, mirroring dribba.com's public calculator. Minimum project budget is €30,000. Requires `platforms`. |
80
+ | `list_projects` | List Dribba's case studies (slug, title, industry). |
81
+ | `company_facts` | Structured facts about Dribba: positioning, offices, team, distinctions, clients. |
82
+ | `submit_contact_request` | Submit a real project inquiry to Dribba on behalf of the user. This sends an email and creates a lead — ONLY call it after the user has explicitly confirmed the exact name, email and message to be sent. Dribba replies within 1h. Rate-limited per client. |
83
+
84
+ ## Development
85
+
86
+ ```bash
87
+ npm install
88
+ npm run build # tsc -> dist/
89
+ npm test # tsx --test test/*.test.ts (fake fetch only, no network)
90
+ npm run dev # run the CLI from source
91
+ ```
92
+
93
+ ## License
94
+
95
+ MIT — Copyright (c) 2026 Rene Turcios.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ballpark CLI.
4
+ *
5
+ * ballpark list_services '<json>'
6
+ * ballpark estimate_project '<json>'
7
+ * ballpark list_projects '<json>'
8
+ * ballpark company_facts '<json>'
9
+ * ballpark submit_contact_request '<json>'
10
+ * ballpark mcp
11
+ * ballpark --version
12
+ *
13
+ * Each tool subcommand takes one JSON argument string with
14
+ * the tool's arguments, forwards the call upstream like
15
+ * the MCP path, and prints the forwarded result content.
16
+ * Exit codes: 0 success, 1 on an isError result, 2 usage
17
+ * error (unknown subcommand, non-JSON argument, or
18
+ * arguments that fail the same validation the MCP
19
+ * handler applies).
20
+ */
21
+ import { callTool, VERSION } from "./upstream.js";
22
+ const TOOL_NAMES = [
23
+ "list_services",
24
+ "estimate_project",
25
+ "list_projects",
26
+ "company_facts",
27
+ "submit_contact_request",
28
+ ];
29
+ function usage() {
30
+ console.error(`Usage:
31
+ ballpark <tool> '<json-args>' one of: ${TOOL_NAMES.join(", ")}
32
+ ballpark mcp
33
+ ballpark --version`);
34
+ process.exit(2);
35
+ }
36
+ function renderContent(result) {
37
+ if (result.structuredContent !== undefined) {
38
+ return JSON.stringify(result.structuredContent);
39
+ }
40
+ const content = result.content;
41
+ if (Array.isArray(content)) {
42
+ return content
43
+ .map((item) => {
44
+ if (item !== null &&
45
+ typeof item === "object" &&
46
+ item.type === "text" &&
47
+ typeof item.text === "string") {
48
+ return item.text;
49
+ }
50
+ return JSON.stringify(item);
51
+ })
52
+ .join("\n");
53
+ }
54
+ return typeof content === "string" ? content : JSON.stringify(result);
55
+ }
56
+ async function main() {
57
+ const [cmd, ...rest] = process.argv.slice(2);
58
+ if (cmd === "--version" || cmd === "-v") {
59
+ console.log(VERSION);
60
+ return;
61
+ }
62
+ if (cmd === "mcp") {
63
+ if (rest.length !== 0)
64
+ usage();
65
+ const { startBallparkServer } = await import("./mcp.js");
66
+ await startBallparkServer();
67
+ return;
68
+ }
69
+ if (!cmd || !TOOL_NAMES.includes(cmd))
70
+ usage();
71
+ if (rest.length > 1)
72
+ usage();
73
+ let args = {};
74
+ if (rest.length === 1) {
75
+ try {
76
+ const parsed = JSON.parse(rest[0]);
77
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
78
+ usage();
79
+ args = parsed;
80
+ }
81
+ catch {
82
+ console.error("error: argument must be a JSON object string");
83
+ process.exit(2);
84
+ }
85
+ }
86
+ const { validateToolArgs } = await import("./mcp.js");
87
+ try {
88
+ args = validateToolArgs(cmd, args);
89
+ }
90
+ catch (err) {
91
+ console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
92
+ process.exit(2);
93
+ }
94
+ const result = await callTool(cmd, args);
95
+ if (result.isError) {
96
+ console.error(`error: ${renderContent(result)}`);
97
+ process.exit(1);
98
+ }
99
+ console.log(renderContent(result));
100
+ }
101
+ void main();
102
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ export * from "./upstream.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./types.js";
2
+ export * from "./upstream.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC"}
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * ballpark MCP server. Tools: list_services,
3
+ * estimate_project, list_projects, company_facts,
4
+ * submit_contact_request.
5
+ *
6
+ * Why the low-level Server instead of McpServer: the
7
+ * high-level helper converts zod schemas to JSON Schema
8
+ * and injects keys that are not in this tool set's
9
+ * captured spec, which breaks exact tool-list/schema
10
+ * parity with the live capture. This file serves
11
+ * tools/list from the explicit literals below — the
12
+ * verbatim tool objects from the remote's live
13
+ * tools/list capture — and forwards every tools/call
14
+ * upstream.
15
+ *
16
+ * The handler validates tools/call arguments itself:
17
+ * an unknown tool name or malformed arguments (missing
18
+ * required fields, wrong types, out-of-range values,
19
+ * unexpected properties where the captured schema sets
20
+ * additionalProperties: false) raise McpError with
21
+ * ErrorCode.InvalidParams, and nothing is forwarded.
22
+ * Valid calls are forwarded and the upstream result is
23
+ * returned verbatim, including structuredContent and
24
+ * any isError flag.
25
+ */
26
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
27
+ import { type BallparkClient } from "./upstream.js";
28
+ import type { ToolArgs, ToolDef } from "./types.js";
29
+ export declare const TOOL_DEFS: ToolDef[];
30
+ export declare const TOOL_NAMES: string[];
31
+ /**
32
+ * Validate tools/call arguments against the captured
33
+ * literal schema for `name`. Returns the argument object
34
+ * to forward. Throws McpError(InvalidParams) for an
35
+ * unknown tool or any malformed argument set.
36
+ */
37
+ export declare function validateToolArgs(name: string, args: unknown): ToolArgs;
38
+ export declare function createBallparkServer(client?: BallparkClient): Server;
39
+ export declare function startBallparkServer(client?: BallparkClient): Promise<void>;
package/dist/mcp.js ADDED
@@ -0,0 +1,234 @@
1
+ /**
2
+ * ballpark MCP server. Tools: list_services,
3
+ * estimate_project, list_projects, company_facts,
4
+ * submit_contact_request.
5
+ *
6
+ * Why the low-level Server instead of McpServer: the
7
+ * high-level helper converts zod schemas to JSON Schema
8
+ * and injects keys that are not in this tool set's
9
+ * captured spec, which breaks exact tool-list/schema
10
+ * parity with the live capture. This file serves
11
+ * tools/list from the explicit literals below — the
12
+ * verbatim tool objects from the remote's live
13
+ * tools/list capture — and forwards every tools/call
14
+ * upstream.
15
+ *
16
+ * The handler validates tools/call arguments itself:
17
+ * an unknown tool name or malformed arguments (missing
18
+ * required fields, wrong types, out-of-range values,
19
+ * unexpected properties where the captured schema sets
20
+ * additionalProperties: false) raise McpError with
21
+ * ErrorCode.InvalidParams, and nothing is forwarded.
22
+ * Valid calls are forwarded and the upstream result is
23
+ * returned verbatim, including structuredContent and
24
+ * any isError flag.
25
+ */
26
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
27
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
28
+ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
29
+ import { createClient, VERSION } from "./upstream.js";
30
+ export const TOOL_DEFS = [
31
+ {
32
+ name: "list_services",
33
+ description: "List Dribba's services with a one-line summary and URL.",
34
+ inputSchema: {
35
+ type: "object",
36
+ properties: {
37
+ limit: {
38
+ type: "integer",
39
+ minimum: 1,
40
+ maximum: 50,
41
+ description: "Maximum number of services to return. Omit for all of them.",
42
+ },
43
+ },
44
+ required: [],
45
+ additionalProperties: false,
46
+ },
47
+ },
48
+ {
49
+ name: "estimate_project",
50
+ description: "Ballpark budget and range for an app/product, mirroring dribba.com's public calculator. Minimum project budget is €30,000.",
51
+ inputSchema: {
52
+ type: "object",
53
+ properties: {
54
+ platforms: {
55
+ type: "array",
56
+ items: {
57
+ type: "string",
58
+ enum: ["ios", "android", "web", "desktop"],
59
+ },
60
+ description: "At least one target platform.",
61
+ },
62
+ complexity: {
63
+ type: "string",
64
+ enum: ["mvp", "standard", "complex"],
65
+ },
66
+ design: {
67
+ type: "string",
68
+ enum: ["basic", "custom", "premium"],
69
+ },
70
+ timeline: {
71
+ type: "string",
72
+ enum: ["rush", "normal", "relaxed"],
73
+ },
74
+ },
75
+ required: ["platforms"],
76
+ },
77
+ },
78
+ {
79
+ name: "list_projects",
80
+ description: "List Dribba's case studies (slug, title, industry).",
81
+ inputSchema: {
82
+ type: "object",
83
+ properties: {
84
+ industry: {
85
+ type: "string",
86
+ description: "Case-insensitive substring match on the industry, e.g. \"fintech\". Omit for all industries.",
87
+ },
88
+ limit: {
89
+ type: "integer",
90
+ minimum: 1,
91
+ maximum: 50,
92
+ description: "Maximum number of case studies to return.",
93
+ },
94
+ },
95
+ required: [],
96
+ additionalProperties: false,
97
+ },
98
+ },
99
+ {
100
+ name: "company_facts",
101
+ description: "Structured facts about Dribba: positioning, offices, team, distinctions, clients.",
102
+ inputSchema: {
103
+ type: "object",
104
+ properties: {
105
+ fields: {
106
+ type: "array",
107
+ items: {
108
+ type: "string",
109
+ },
110
+ description: "Subset of fact keys to return (e.g. [\"offices\",\"distinctions\"]). Omit for every fact.",
111
+ },
112
+ },
113
+ required: [],
114
+ additionalProperties: false,
115
+ },
116
+ },
117
+ {
118
+ name: "submit_contact_request",
119
+ description: "Submit a real project inquiry to Dribba on behalf of the user. This sends an email and creates a lead — ONLY call it after the user has explicitly confirmed the exact name, email and message to be sent. Dribba replies within 1h. Rate-limited per client.",
120
+ inputSchema: {
121
+ type: "object",
122
+ properties: {
123
+ name: {
124
+ type: "string",
125
+ description: "Full name of the person making the inquiry.",
126
+ },
127
+ email: {
128
+ type: "string",
129
+ description: "Email address Dribba should reply to.",
130
+ },
131
+ message: {
132
+ type: "string",
133
+ description: "The inquiry, confirmed by the user.",
134
+ },
135
+ company: {
136
+ type: "string",
137
+ description: "Company name (optional).",
138
+ },
139
+ budget: {
140
+ type: "string",
141
+ description: "Approximate budget range, e.g. \"30-60k EUR\" (optional).",
142
+ },
143
+ },
144
+ required: ["name", "email", "message"],
145
+ },
146
+ },
147
+ ];
148
+ export const TOOL_NAMES = TOOL_DEFS.map((t) => t.name);
149
+ function invalid(message) {
150
+ return new McpError(ErrorCode.InvalidParams, message);
151
+ }
152
+ function checkValue(tool, key, value, schema) {
153
+ const type = schema.type;
154
+ if (type === "string") {
155
+ if (typeof value !== "string")
156
+ throw invalid(`${tool}: argument "${key}" must be a string`);
157
+ }
158
+ else if (type === "integer") {
159
+ if (typeof value !== "number" || !Number.isInteger(value)) {
160
+ throw invalid(`${tool}: argument "${key}" must be an integer`);
161
+ }
162
+ if (typeof schema.minimum === "number" && value < schema.minimum) {
163
+ throw invalid(`${tool}: argument "${key}" must be >= ${schema.minimum}`);
164
+ }
165
+ if (typeof schema.maximum === "number" && value > schema.maximum) {
166
+ throw invalid(`${tool}: argument "${key}" must be <= ${schema.maximum}`);
167
+ }
168
+ }
169
+ else if (type === "array") {
170
+ if (!Array.isArray(value))
171
+ throw invalid(`${tool}: argument "${key}" must be an array`);
172
+ const items = schema.items;
173
+ if (items) {
174
+ for (const item of value)
175
+ checkValue(tool, key, item, items);
176
+ }
177
+ return;
178
+ }
179
+ if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
180
+ throw invalid(`${tool}: argument "${key}" must be one of ${schema.enum.join(", ")}`);
181
+ }
182
+ }
183
+ /**
184
+ * Validate tools/call arguments against the captured
185
+ * literal schema for `name`. Returns the argument object
186
+ * to forward. Throws McpError(InvalidParams) for an
187
+ * unknown tool or any malformed argument set.
188
+ */
189
+ export function validateToolArgs(name, args) {
190
+ const def = TOOL_DEFS.find((t) => t.name === name);
191
+ if (!def)
192
+ throw invalid(`Unknown tool: ${name}`);
193
+ const value = args ?? {};
194
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
195
+ throw invalid(`${name}: arguments must be an object`);
196
+ }
197
+ const obj = value;
198
+ const schema = def.inputSchema;
199
+ const properties = schema.properties ?? {};
200
+ for (const req of schema.required ?? []) {
201
+ if (obj[req] === undefined || obj[req] === null) {
202
+ throw invalid(`${name}: missing required argument "${req}"`);
203
+ }
204
+ }
205
+ if (schema.additionalProperties === false) {
206
+ for (const key of Object.keys(obj)) {
207
+ if (!(key in properties))
208
+ throw invalid(`${name}: unexpected argument "${key}"`);
209
+ }
210
+ }
211
+ for (const [key, val] of Object.entries(obj)) {
212
+ const propSchema = properties[key];
213
+ if (propSchema && val !== undefined)
214
+ checkValue(name, key, val, propSchema);
215
+ }
216
+ return obj;
217
+ }
218
+ export function createBallparkServer(client = createClient()) {
219
+ const server = new Server({ name: "ballpark", version: VERSION }, { capabilities: { tools: {} } });
220
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOL_DEFS }));
221
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
222
+ const { name, arguments: args } = request.params;
223
+ const validArgs = validateToolArgs(name, args ?? {});
224
+ const result = await client.callTool(name, validArgs);
225
+ // Upstream result passes through verbatim.
226
+ return result;
227
+ });
228
+ return server;
229
+ }
230
+ export async function startBallparkServer(client) {
231
+ const server = createBallparkServer(client);
232
+ await server.connect(new StdioServerTransport());
233
+ }
234
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * ballpark types.
3
+ *
4
+ * A forwarding client for the published Dribba MCP remote.
5
+ * tools/list is served locally from literal definitions;
6
+ * tools/call is forwarded upstream and the upstream result
7
+ * passes through verbatim.
8
+ */
9
+ /** A tool's arguments, as decoded JSON. */
10
+ export type ToolArgs = Record<string, unknown>;
11
+ /** A JSON Schema object, as captured in the tools/list literal. */
12
+ export type JsonSchema = Record<string, unknown>;
13
+ /** One tool definition, exactly as served by tools/list. */
14
+ export interface ToolDef {
15
+ name: string;
16
+ description: string;
17
+ inputSchema: JsonSchema;
18
+ }
19
+ /**
20
+ * The result shape shared by the MCP handler and the CLI.
21
+ * Extra keys from upstream (for example structuredContent
22
+ * or _meta) are preserved verbatim, hence the index
23
+ * signature.
24
+ */
25
+ export interface ToolResult {
26
+ content?: unknown;
27
+ structuredContent?: Record<string, unknown>;
28
+ isError?: boolean;
29
+ [key: string]: unknown;
30
+ }
31
+ /** The minimal slice of a fetch Response this client relies on. */
32
+ export interface FetchResponseLike {
33
+ ok: boolean;
34
+ status: number;
35
+ headers: {
36
+ get(name: string): string | null;
37
+ };
38
+ text(): Promise<string>;
39
+ }
40
+ /** Injectable fetch, so tests never touch the live network. */
41
+ export type FetchFn = (url: string, init?: {
42
+ method?: string;
43
+ headers?: Record<string, string>;
44
+ body?: string;
45
+ signal?: AbortSignal;
46
+ }) => Promise<FetchResponseLike>;
47
+ /** Injectable dependencies for the upstream client. */
48
+ export interface UpstreamDeps {
49
+ fetchFn?: FetchFn;
50
+ url?: string;
51
+ env?: Record<string, string | undefined>;
52
+ timeoutMs?: number;
53
+ }
package/dist/types.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * ballpark types.
3
+ *
4
+ * A forwarding client for the published Dribba MCP remote.
5
+ * tools/list is served locally from literal definitions;
6
+ * tools/call is forwarded upstream and the upstream result
7
+ * passes through verbatim.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * ballpark core: a forwarding client for the published
3
+ * Dribba MCP remote at https://dribba.com/mcp.
4
+ *
5
+ * The endpoint is pinned by the spec. An override via the
6
+ * DRIBBA_MCP_URL environment variable is a labelled
7
+ * reconstruction: no override is published for this
8
+ * remote-only server.
9
+ *
10
+ * Wire protocol (streamable HTTP, JSON-RPC):
11
+ * initialize (protocolVersion "2025-03-26", clientInfo
12
+ * { name: "ballpark", version: VERSION })
13
+ * -> notifications/initialized -> tools/call
14
+ * The session is established lazily on the first forwarded
15
+ * call and reused for the client's lifetime. When the
16
+ * upstream issues an Mcp-Session-Id response header it is
17
+ * captured and sent on every later request.
18
+ *
19
+ * Responses may be plain JSON or SSE (`data:` lines); the
20
+ * JSON-RPC message whose `id` matches the request is the
21
+ * answer. An upstream `result` passes through verbatim
22
+ * (content, structuredContent, isError and any other keys).
23
+ * Every failure — HTTP error, JSON-RPC error, malformed
24
+ * response, or a throwing fetch — becomes an isError result
25
+ * whose text starts with "ballpark error: " (this rebuild's
26
+ * own error rendering, a labelled reconstruction).
27
+ *
28
+ * All upstream access goes through an injectable fetch, so
29
+ * tests use a fake fetch only and never touch the network.
30
+ */
31
+ import type { ToolArgs, ToolResult, UpstreamDeps } from "./types.js";
32
+ export declare const VERSION = "0.1.0";
33
+ export declare const DEFAULT_UPSTREAM_URL = "https://dribba.com/mcp";
34
+ /** Parse a response body that is plain JSON or an SSE stream. */
35
+ export declare function parseRpcMessage(body: string, wantId: number): Record<string, unknown> | null;
36
+ export declare class BallparkClient {
37
+ private readonly fetchFn;
38
+ private readonly url;
39
+ private readonly timeoutMs;
40
+ private sessionId;
41
+ private sessionPromise;
42
+ private nextId;
43
+ constructor(deps?: UpstreamDeps);
44
+ get upstreamUrl(): string;
45
+ private post;
46
+ private ensureSession;
47
+ /** Forward one tools/call upstream; failures become "ballpark error: ..." results. */
48
+ callTool(name: string, args: ToolArgs): Promise<ToolResult>;
49
+ }
50
+ export declare function createClient(deps?: UpstreamDeps): BallparkClient;
51
+ /** Convenience one-shot forward used by the CLI. */
52
+ export declare function callTool(name: string, args: ToolArgs, deps?: UpstreamDeps): Promise<ToolResult>;
@@ -0,0 +1,167 @@
1
+ export const VERSION = "0.1.0";
2
+ export const DEFAULT_UPSTREAM_URL = "https://dribba.com/mcp";
3
+ const PROTOCOL_VERSION = "2025-03-26";
4
+ const DEFAULT_TIMEOUT_MS = 60000;
5
+ function errorResult(message) {
6
+ return {
7
+ content: [{ type: "text", text: `ballpark error: ${message}` }],
8
+ isError: true,
9
+ };
10
+ }
11
+ /** Parse a response body that is plain JSON or an SSE stream. */
12
+ export function parseRpcMessage(body, wantId) {
13
+ const trimmed = body.trim();
14
+ if (!trimmed)
15
+ return null;
16
+ const candidates = [];
17
+ if (trimmed.startsWith("{")) {
18
+ try {
19
+ candidates.push(JSON.parse(trimmed));
20
+ }
21
+ catch {
22
+ // fall through to the SSE scan below
23
+ }
24
+ }
25
+ if (candidates.length === 0) {
26
+ for (const line of trimmed.split(/\r?\n/)) {
27
+ const t = line.trim();
28
+ if (!t.startsWith("data:"))
29
+ continue;
30
+ const payload = t.slice("data:".length).trim();
31
+ if (!payload || payload === "[DONE]")
32
+ continue;
33
+ try {
34
+ candidates.push(JSON.parse(payload));
35
+ }
36
+ catch {
37
+ // ignore non-JSON data lines
38
+ }
39
+ }
40
+ }
41
+ let fallback = null;
42
+ for (const c of candidates) {
43
+ if (c === null || typeof c !== "object" || Array.isArray(c))
44
+ continue;
45
+ const msg = c;
46
+ if (msg.id === wantId)
47
+ return msg;
48
+ if (fallback === null && ("result" in msg || "error" in msg))
49
+ fallback = msg;
50
+ }
51
+ return fallback;
52
+ }
53
+ export class BallparkClient {
54
+ fetchFn;
55
+ url;
56
+ timeoutMs;
57
+ sessionId = null;
58
+ sessionPromise = null;
59
+ nextId = 1;
60
+ constructor(deps = {}) {
61
+ const env = deps.env ?? process.env;
62
+ this.fetchFn = deps.fetchFn ?? ((url, init) => fetch(url, init));
63
+ // DRIBBA_MCP_URL override: labelled reconstruction.
64
+ this.url = deps.url ?? env.DRIBBA_MCP_URL ?? DEFAULT_UPSTREAM_URL;
65
+ const envTimeout = env.DRIBBA_TIMEOUT_MS ? Number(env.DRIBBA_TIMEOUT_MS) : NaN;
66
+ this.timeoutMs =
67
+ deps.timeoutMs ?? (Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : DEFAULT_TIMEOUT_MS);
68
+ }
69
+ get upstreamUrl() {
70
+ return this.url;
71
+ }
72
+ async post(payload, wantResponse) {
73
+ const controller = new AbortController();
74
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
75
+ try {
76
+ const headers = {
77
+ "Content-Type": "application/json",
78
+ Accept: "application/json, text/event-stream",
79
+ };
80
+ if (this.sessionId)
81
+ headers["Mcp-Session-Id"] = this.sessionId;
82
+ const res = await this.fetchFn(this.url, {
83
+ method: "POST",
84
+ headers,
85
+ body: JSON.stringify(payload),
86
+ signal: controller.signal,
87
+ });
88
+ const issued = res.headers?.get?.("mcp-session-id") ?? res.headers?.get?.("Mcp-Session-Id") ?? null;
89
+ if (issued)
90
+ this.sessionId = issued;
91
+ const text = await res.text();
92
+ if (!res.ok) {
93
+ throw new Error(`upstream HTTP ${res.status}`);
94
+ }
95
+ if (!wantResponse)
96
+ return { msg: null, status: res.status, ok: res.ok };
97
+ const id = payload.id ?? 0;
98
+ return { msg: parseRpcMessage(text, id), status: res.status, ok: res.ok };
99
+ }
100
+ finally {
101
+ clearTimeout(timer);
102
+ }
103
+ }
104
+ ensureSession() {
105
+ if (!this.sessionPromise) {
106
+ this.sessionPromise = (async () => {
107
+ const id = this.nextId++;
108
+ const { msg } = await this.post({
109
+ jsonrpc: "2.0",
110
+ id,
111
+ method: "initialize",
112
+ params: {
113
+ protocolVersion: PROTOCOL_VERSION,
114
+ capabilities: {},
115
+ clientInfo: { name: "ballpark", version: VERSION },
116
+ },
117
+ }, true);
118
+ if (!msg)
119
+ throw new Error("malformed response");
120
+ if (msg.error) {
121
+ const e = msg.error;
122
+ throw new Error(typeof e.message === "string" ? e.message : "upstream error");
123
+ }
124
+ if (!("result" in msg))
125
+ throw new Error("malformed response");
126
+ await this.post({ jsonrpc: "2.0", method: "notifications/initialized" }, false);
127
+ return null;
128
+ })();
129
+ // A failed handshake must not poison later calls.
130
+ this.sessionPromise.catch(() => {
131
+ this.sessionPromise = null;
132
+ });
133
+ }
134
+ return this.sessionPromise;
135
+ }
136
+ /** Forward one tools/call upstream; failures become "ballpark error: ..." results. */
137
+ async callTool(name, args) {
138
+ try {
139
+ await this.ensureSession();
140
+ const id = this.nextId++;
141
+ const { msg } = await this.post({ jsonrpc: "2.0", id, method: "tools/call", params: { name, arguments: args } }, true);
142
+ if (!msg)
143
+ return errorResult("malformed response");
144
+ if (msg.error) {
145
+ const e = msg.error;
146
+ return errorResult(typeof e.message === "string" ? e.message : "upstream error");
147
+ }
148
+ if (!("result" in msg) || msg.result === null || typeof msg.result !== "object") {
149
+ return errorResult("malformed response");
150
+ }
151
+ // Upstream result passes through verbatim.
152
+ return msg.result;
153
+ }
154
+ catch (err) {
155
+ const message = err instanceof Error ? err.message : String(err);
156
+ return errorResult(message);
157
+ }
158
+ }
159
+ }
160
+ export function createClient(deps = {}) {
161
+ return new BallparkClient(deps);
162
+ }
163
+ /** Convenience one-shot forward used by the CLI. */
164
+ export async function callTool(name, args, deps = {}) {
165
+ return createClient(deps).callTool(name, args);
166
+ }
167
+ //# sourceMappingURL=upstream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upstream.js","sourceRoot":"","sources":["../src/upstream.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1 @@
1
- {
2
- "name": "@reneturcios/ballpark",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {"bin":{"ballpark":"dist/cli.js"},"dependencies":{"@modelcontextprotocol/sdk":"^1","zod":"^4"},"description":"Forwarding client for the published Dribba MCP remote: services, case studies, budget estimates, company facts and contact requests, served locally for tools/list and forwarded for tools/call. One bin serves a small CLI and a stdio MCP server.","devDependencies":{"@types/node":"^20","tsx":"^4","typescript":"^5"},"engines":{"node":">=20"},"files":["dist","server.json"],"homepage":"https://github.com/CloudCorpRecords/ballpark#readme","license":"MIT","mcpName":"io.github.CloudCorpRecords/ballpark","name":"@reneturcios/ballpark","private":false,"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/CloudCorpRecords/ballpark.git"},"scripts":{"build":"tsc","dev":"tsx src/cli.ts","test":"tsx --test test/*.test.ts"},"type":"module","version":"0.1.0"}
package/server.json ADDED
@@ -0,0 +1 @@
1
+ {"$schema":"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json","description":"Forwarding client for Dribba studio data: services, case studies, budget estimates, contact.","name":"io.github.CloudCorpRecords/ballpark","packages":[{"identifier":"@reneturcios/ballpark","packageArguments":[{"type":"positional","value":"mcp"}],"registryBaseUrl":"https://registry.npmjs.org","registryType":"npm","runtimeHint":"npx","transport":{"type":"stdio"},"version":"0.1.0"},{"identifier":"ghcr.io/cloudcorprecords/ballpark:0.1.0","packageArguments":[{"type":"positional","value":"mcp"}],"registryType":"oci","runtimeArguments":[{"name":"--rm","type":"named"},{"name":"-i","type":"named"}],"runtimeHint":"docker","transport":{"type":"stdio"}}],"repository":{"source":"github","url":"https://github.com/CloudCorpRecords/ballpark"},"version":"0.1.0"}