opuscode-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.
Files changed (3) hide show
  1. package/README.md +144 -0
  2. package/index.js +167 -0
  3. package/package.json +17 -0
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # OpusCode MCP
2
+
3
+ MCP (Model Context Protocol) server that provides **web search** and **image understanding** tools for AI coding agents — Claude Code, Cursor, OpenCode, and more.
4
+
5
+ ## Quick Install
6
+
7
+ ### Claude Code (recommended)
8
+
9
+ ```bash
10
+ claude mcp add -s user OpusCode \
11
+ --env OPUSCODE_API_KEY=your_key \
12
+ --env OPUSCODE_URL=https://your-server.com \
13
+ -- npx opuscode-mcp
14
+ ```
15
+
16
+ ### Global install
17
+
18
+ ```bash
19
+ npm install -g opuscode-mcp
20
+ ```
21
+
22
+ ## Manual Configuration
23
+
24
+ ### Claude Code
25
+
26
+ Add to your Claude Code MCP config (`~/.claude/claude_desktop_config.json`):
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "OpusCode": {
32
+ "command": "npx",
33
+ "args": ["opuscode-mcp"],
34
+ "env": {
35
+ "OPUSCODE_API_KEY": "your_key",
36
+ "OPUSCODE_URL": "https://your-server.com"
37
+ }
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ ### Cursor
44
+
45
+ Add to Cursor MCP settings (`.cursor/mcp.json`):
46
+
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "OpusCode": {
51
+ "command": "npx",
52
+ "args": ["opuscode-mcp"],
53
+ "env": {
54
+ "OPUSCODE_API_KEY": "your_key",
55
+ "OPUSCODE_URL": "https://your-server.com"
56
+ }
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ### OpenCode
63
+
64
+ Add to your OpenCode config:
65
+
66
+ ```json
67
+ {
68
+ "mcp": {
69
+ "OpusCode": {
70
+ "command": "npx",
71
+ "args": ["opuscode-mcp"],
72
+ "env": {
73
+ "OPUSCODE_API_KEY": "your_key",
74
+ "OPUSCODE_URL": "https://your-server.com"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ ## Tools
82
+
83
+ | Tool | Description |
84
+ |---|---|
85
+ | `web_search` | Search the web for real-time information. Best with 3–5 keywords. |
86
+ | `understand_image` | Analyze images using AI. Accepts URLs, local file paths, or base64 data URLs. |
87
+
88
+ ## Usage Examples
89
+
90
+ ### Web Search
91
+
92
+ ```
93
+ Search for: "Node.js 22 new features 2025"
94
+ ```
95
+
96
+ The `web_search` tool accepts a `query` string and returns structured search results from the web.
97
+
98
+ ### Image Understanding
99
+
100
+ ```
101
+ Prompt: "Describe this UI mockup and list all the components"
102
+ Image source: "./screenshot.png"
103
+ ```
104
+
105
+ The `understand_image` tool accepts:
106
+
107
+ - **`prompt`** — What you want to know about the image
108
+ - **`image_source`** — One of:
109
+ - An HTTP/HTTPS URL (`https://example.com/image.png`)
110
+ - A local file path (`./diagram.png` or `/home/user/photo.jpg`)
111
+ - A base64 data URL (`data:image/png;base64,...`)
112
+
113
+ Supported formats: JPEG, PNG, WebP, GIF, BMP, SVG.
114
+
115
+ ## Environment Variables
116
+
117
+ | Variable | Required | Default | Description |
118
+ |---|---|---|---|
119
+ | `OPUSCODE_API_KEY` | Yes | — | API key for authentication |
120
+ | `OPUSCODE_URL` | No | `http://localhost:3001` | OpusCode proxy server URL |
121
+
122
+ ## Troubleshooting
123
+
124
+ ### "Error: Proxy request failed: 401"
125
+
126
+ Your `OPUSCODE_API_KEY` is missing or invalid. Make sure it's set in your environment or MCP config.
127
+
128
+ ### "Error: Proxy request failed: ECONNREFUSED"
129
+
130
+ The OpusCode proxy server is not running or `OPUSCODE_URL` points to the wrong address. Verify the server is up and the URL is correct.
131
+
132
+ ### "Error: File not found"
133
+
134
+ When using `understand_image` with a local file path, ensure the path is correct relative to where the MCP server process is running.
135
+
136
+ ### Tools not showing up
137
+
138
+ 1. Restart your AI coding tool after adding the MCP config.
139
+ 2. Check that `npx opuscode-mcp` runs without errors in your terminal.
140
+ 3. Verify your MCP config JSON is valid.
141
+
142
+ ## License
143
+
144
+ MIT
package/index.js ADDED
@@ -0,0 +1,167 @@
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 { CallToolRequestSchema, ListToolsRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const API_KEY = process.env.OPUSCODE_API_KEY || '';
10
+ const BASE_URL = (process.env.OPUSCODE_URL || 'http://localhost:3001').replace(/\/+$/, '');
11
+
12
+ const MIME_TYPES = {
13
+ '.jpg': 'image/jpeg',
14
+ '.jpeg': 'image/jpeg',
15
+ '.png': 'image/png',
16
+ '.gif': 'image/gif',
17
+ '.webp': 'image/webp',
18
+ '.bmp': 'image/bmp',
19
+ '.svg': 'image/svg+xml',
20
+ };
21
+
22
+ async function processImageSource(imageSource) {
23
+ let source = imageSource.trim();
24
+
25
+ // Strip leading @ (common in curl-style references)
26
+ if (source.startsWith('@')) {
27
+ source = source.slice(1);
28
+ }
29
+
30
+ // Already a data URL — pass through
31
+ if (source.startsWith('data:')) {
32
+ return source;
33
+ }
34
+
35
+ // HTTP(S) URL — download and convert
36
+ if (source.startsWith('http://') || source.startsWith('https://')) {
37
+ const res = await fetch(source);
38
+ if (!res.ok) {
39
+ throw new Error(`Failed to download image: ${res.status} ${res.statusText}`);
40
+ }
41
+ const contentType = res.headers.get('content-type') || 'image/png';
42
+ const buffer = Buffer.from(await res.arrayBuffer());
43
+ const base64 = buffer.toString('base64');
44
+ return `data:${contentType};base64,${base64}`;
45
+ }
46
+
47
+ // Local file path
48
+ const resolved = path.resolve(source);
49
+ if (!fs.existsSync(resolved)) {
50
+ throw new Error(`File not found: ${resolved}`);
51
+ }
52
+ const ext = path.extname(resolved).toLowerCase();
53
+ const mime = MIME_TYPES[ext] || 'image/png';
54
+ const fileBuffer = fs.readFileSync(resolved);
55
+ const base64 = fileBuffer.toString('base64');
56
+ return `data:${mime};base64,${base64}`;
57
+ }
58
+
59
+ async function callProxy(endpoint, body) {
60
+ const url = `${BASE_URL}${endpoint}`;
61
+ const headers = { 'Content-Type': 'application/json' };
62
+ if (API_KEY) {
63
+ headers['Authorization'] = `Bearer ${API_KEY}`;
64
+ }
65
+
66
+ const res = await fetch(url, {
67
+ method: 'POST',
68
+ headers,
69
+ body: JSON.stringify(body),
70
+ });
71
+
72
+ if (!res.ok) {
73
+ const text = await res.text().catch(() => '');
74
+ throw new Error(`Proxy request failed: ${res.status} ${res.statusText} — ${text}`);
75
+ }
76
+
77
+ return res.json();
78
+ }
79
+
80
+ const server = new Server(
81
+ { name: 'opuscode-mcp', version: '1.0.0' },
82
+ { capabilities: { tools: {} } }
83
+ );
84
+
85
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
86
+ tools: [
87
+ {
88
+ name: 'web_search',
89
+ description:
90
+ 'Search the web for real-time information. Use 3-5 keywords for best results. For time-sensitive topics, include the current date.',
91
+ inputSchema: {
92
+ type: 'object',
93
+ properties: {
94
+ query: {
95
+ type: 'string',
96
+ description: 'Search query (3-5 keywords recommended)',
97
+ },
98
+ },
99
+ required: ['query'],
100
+ },
101
+ },
102
+ {
103
+ name: 'understand_image',
104
+ description:
105
+ 'Analyze images with AI. Supports JPEG, PNG, WebP. Accepts HTTP URLs, local file paths, or base64 data URLs.',
106
+ inputSchema: {
107
+ type: 'object',
108
+ properties: {
109
+ prompt: {
110
+ type: 'string',
111
+ description: 'What you want to know about the image',
112
+ },
113
+ image_source: {
114
+ type: 'string',
115
+ description:
116
+ 'Image source: an HTTP/HTTPS URL, a local file path, or a base64 data URL',
117
+ },
118
+ },
119
+ required: ['prompt', 'image_source'],
120
+ },
121
+ },
122
+ ],
123
+ }));
124
+
125
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
126
+ const { name, arguments: args } = request.params;
127
+
128
+ try {
129
+ if (name === 'web_search') {
130
+ const { query } = args;
131
+ if (!query) {
132
+ return { content: [{ type: 'text', text: 'Error: "query" parameter is required.' }], isError: true };
133
+ }
134
+
135
+ const result = await callProxy('/tools/web_search', { query });
136
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
137
+ }
138
+
139
+ if (name === 'understand_image') {
140
+ const { prompt, image_source } = args;
141
+ if (!prompt || !image_source) {
142
+ return {
143
+ content: [{ type: 'text', text: 'Error: "prompt" and "image_source" parameters are required.' }],
144
+ isError: true,
145
+ };
146
+ }
147
+
148
+ const imageUrl = await processImageSource(image_source);
149
+ const result = await callProxy('/tools/understand_image', { prompt, image_url: imageUrl });
150
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
151
+ }
152
+
153
+ return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
154
+ } catch (err) {
155
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
156
+ }
157
+ });
158
+
159
+ async function main() {
160
+ const transport = new StdioServerTransport();
161
+ await server.connect(transport);
162
+ }
163
+
164
+ main().catch((err) => {
165
+ console.error('Fatal error starting OpusCode MCP server:', err);
166
+ process.exit(1);
167
+ });
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "opuscode-mcp",
3
+ "version": "1.0.0",
4
+ "description": "OpusCode MCP server — web search and image understanding tools for AI coding agents",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "opuscode-mcp": "./index.js"
8
+ },
9
+ "keywords": ["mcp", "opuscode", "web-search", "image-understanding", "ai-coding"],
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "@modelcontextprotocol/sdk": "^1.0.0"
13
+ },
14
+ "engines": {
15
+ "node": ">=18.0.0"
16
+ }
17
+ }