wflow-mcp 1.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/README.md +52 -0
- package/dist/auth.d.ts +6 -0
- package/dist/auth.js +15 -0
- package/dist/gateway.d.ts +24 -0
- package/dist/gateway.js +42 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +65 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# wflow MCP Server
|
|
2
|
+
|
|
3
|
+
MCP server for AI assistants — extensible tool platform for the wflow ecosystem.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### 1. Get your API key
|
|
8
|
+
|
|
9
|
+
Ask your team for the `WFLOW_MCP_API_KEY` (matches the `MASTER_KEY` in the wflow Convex ecosystem).
|
|
10
|
+
|
|
11
|
+
### 2. Add to Claude Code
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
claude mcp add -s user -e WFLOW_MCP_API_KEY=your-secret-key wflow-product-mcp -- npx -y wflow-mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
That's it. Tools are loaded dynamically from the backend — no config needed.
|
|
18
|
+
|
|
19
|
+
### 3. Verify
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
claude mcp list
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
You should see `wflow-product-mcp` in the list.
|
|
26
|
+
|
|
27
|
+
## Available Tools
|
|
28
|
+
|
|
29
|
+
Tools are fetched dynamically from the backend at startup. Currently available:
|
|
30
|
+
|
|
31
|
+
### `productboard_check_existence`
|
|
32
|
+
|
|
33
|
+
Semantic duplicate search across features, insights, tasks, and bugs. Uses vector search + LLM validation.
|
|
34
|
+
|
|
35
|
+
**Parameters:**
|
|
36
|
+
- `description` (string) — What you're searching for
|
|
37
|
+
- `type` (enum) — `feature`, `insight`, `jira_task`, or `jira_bug`
|
|
38
|
+
|
|
39
|
+
**Example usage in Claude Code:**
|
|
40
|
+
> "Check if there's already a feature for invoice approval on line items"
|
|
41
|
+
|
|
42
|
+
## Architecture
|
|
43
|
+
|
|
44
|
+
This package is a thin pass-through client. It contains no business logic or internal API URLs. All tool definitions and routing logic live on the backend — adding new tools requires no package update.
|
|
45
|
+
|
|
46
|
+
## Development
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git clone https://github.com/wflowcom/product_mcp.git
|
|
50
|
+
cd product_mcp/mcp-server
|
|
51
|
+
npm install && npm run build
|
|
52
|
+
```
|
package/dist/auth.d.ts
ADDED
package/dist/auth.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication for the MCP server.
|
|
3
|
+
* Validates WFLOW_MCP_API_KEY env var on startup.
|
|
4
|
+
* Also used as Authorization header when calling Convex HTTP Actions.
|
|
5
|
+
*/
|
|
6
|
+
export function getApiKey() {
|
|
7
|
+
const key = process.env.WFLOW_MCP_API_KEY;
|
|
8
|
+
if (!key) {
|
|
9
|
+
console.error("ERROR: WFLOW_MCP_API_KEY environment variable is required.\n" +
|
|
10
|
+
"Set it before running the MCP server:\n" +
|
|
11
|
+
' export WFLOW_MCP_API_KEY="your-secret-key"');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
return key;
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin gateway client — communicates with the product_mcp Convex backend.
|
|
3
|
+
* The MCP client only knows this one URL; all routing happens server-side.
|
|
4
|
+
*/
|
|
5
|
+
export interface ToolDefinition {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
inputSchema: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
interface InvokeResult {
|
|
11
|
+
content: Array<{
|
|
12
|
+
type: string;
|
|
13
|
+
text: string;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Fetch available tool definitions from the gateway.
|
|
18
|
+
*/
|
|
19
|
+
export declare function fetchTools(gatewayUrl: string): Promise<ToolDefinition[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Invoke a tool on the gateway.
|
|
22
|
+
*/
|
|
23
|
+
export declare function invokeTool(gatewayUrl: string, apiKey: string, toolName: string, args: Record<string, unknown>): Promise<InvokeResult>;
|
|
24
|
+
export {};
|
package/dist/gateway.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin gateway client — communicates with the product_mcp Convex backend.
|
|
3
|
+
* The MCP client only knows this one URL; all routing happens server-side.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Fetch available tool definitions from the gateway.
|
|
7
|
+
*/
|
|
8
|
+
export async function fetchTools(gatewayUrl) {
|
|
9
|
+
const response = await fetch(`${gatewayUrl}/api/mcp/tools`, {
|
|
10
|
+
method: "GET",
|
|
11
|
+
headers: { "Content-Type": "application/json" },
|
|
12
|
+
});
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw new Error(`Gateway returned ${response.status} ${response.statusText}`);
|
|
15
|
+
}
|
|
16
|
+
const result = await response.json();
|
|
17
|
+
if (!result.success || !Array.isArray(result.data)) {
|
|
18
|
+
throw new Error(result.error || "Invalid response from gateway");
|
|
19
|
+
}
|
|
20
|
+
return result.data;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Invoke a tool on the gateway.
|
|
24
|
+
*/
|
|
25
|
+
export async function invokeTool(gatewayUrl, apiKey, toolName, args) {
|
|
26
|
+
const response = await fetch(`${gatewayUrl}/api/mcp/invoke`, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
Authorization: apiKey,
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({ tool: toolName, arguments: args }),
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
throw new Error(`Gateway returned ${response.status} ${response.statusText}`);
|
|
36
|
+
}
|
|
37
|
+
const result = await response.json();
|
|
38
|
+
if (!result.content) {
|
|
39
|
+
throw new Error(result.error || "Unknown error from gateway");
|
|
40
|
+
}
|
|
41
|
+
return { content: result.content };
|
|
42
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* wflow MCP server — thin pass-through client.
|
|
4
|
+
*
|
|
5
|
+
* Fetches tool definitions from the Convex gateway at startup,
|
|
6
|
+
* registers them dynamically, and forwards all invocations.
|
|
7
|
+
* Contains no business logic or internal API URLs.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* wflow MCP server — thin pass-through client.
|
|
4
|
+
*
|
|
5
|
+
* Fetches tool definitions from the Convex gateway at startup,
|
|
6
|
+
* registers them dynamically, and forwards all invocations.
|
|
7
|
+
* Contains no business logic or internal API URLs.
|
|
8
|
+
*/
|
|
9
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
10
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
+
import { ListToolsRequestSchema, CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
12
|
+
import { getApiKey } from "./auth.js";
|
|
13
|
+
import { fetchTools, invokeTool, } from "./gateway.js";
|
|
14
|
+
const GATEWAY_URL = "https://zealous-rhinoceros-886.eu-west-1.convex.site";
|
|
15
|
+
const apiKey = getApiKey();
|
|
16
|
+
// Fetch tool definitions from the gateway at startup
|
|
17
|
+
let tools = [];
|
|
18
|
+
try {
|
|
19
|
+
tools = await fetchTools(GATEWAY_URL);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
// stderr only — stdout is reserved for MCP protocol
|
|
23
|
+
console.error(`Warning: Failed to fetch tools from gateway: ${error instanceof Error ? error.message : String(error)}`);
|
|
24
|
+
console.error("MCP server starting with no tools available.");
|
|
25
|
+
}
|
|
26
|
+
const server = new Server({ name: "wflow-mcp", version: "1.1.0" }, { capabilities: { tools: { listChanged: false } } });
|
|
27
|
+
// tools/list — return definitions fetched from gateway
|
|
28
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
29
|
+
tools: tools.map((t) => ({
|
|
30
|
+
name: t.name,
|
|
31
|
+
description: t.description,
|
|
32
|
+
inputSchema: t.inputSchema,
|
|
33
|
+
})),
|
|
34
|
+
}));
|
|
35
|
+
// tools/call — forward invocation to gateway
|
|
36
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
37
|
+
const { name, arguments: args } = request.params;
|
|
38
|
+
const knownTool = tools.find((t) => t.name === name);
|
|
39
|
+
if (!knownTool) {
|
|
40
|
+
return {
|
|
41
|
+
content: [
|
|
42
|
+
{ type: "text", text: `Error: Unknown tool "${name}"` },
|
|
43
|
+
],
|
|
44
|
+
isError: true,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const result = await invokeTool(GATEWAY_URL, apiKey, name, args ?? {});
|
|
49
|
+
return { content: result.content };
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: `Error invoking tool: ${error instanceof Error ? error.message : String(error)}`,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
isError: true,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
// Start stdio transport
|
|
64
|
+
const transport = new StdioServerTransport();
|
|
65
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wflow-mcp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "wflow MCP server for AI assistants — extensible tool platform",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"wflow-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"prepare": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^22.15.0",
|
|
23
|
+
"typescript": "^5.9.3"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"private": false
|
|
32
|
+
}
|