@0xmaxma/claude-gateway 1.2.12 → 1.2.15
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/dist/agent/runner.d.ts +19 -3
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +47 -2
- package/dist/agent/runner.js.map +1 -1
- package/dist/api/router.d.ts.map +1 -1
- package/dist/api/router.js +51 -6
- package/dist/api/router.js.map +1 -1
- package/dist/cron/manager.d.ts.map +1 -1
- package/dist/cron/manager.js +2 -1
- package/dist/cron/manager.js.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/session/process.d.ts.map +1 -1
- package/dist/session/process.js +4 -0
- package/dist/session/process.js.map +1 -1
- package/dist/skills/sync.d.ts +13 -4
- package/dist/skills/sync.d.ts.map +1 -1
- package/dist/skills/sync.js +104 -24
- package/dist/skills/sync.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/mcp/server.ts +2 -0
- package/mcp/tools/api/module.ts +87 -0
- package/mcp/tools/browser/module.ts +24 -5
- package/mcp/tools/browser/skills/open-browser/SKILL.md +4 -8
- package/package.json +1 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ToolModule, McpToolDefinition, McpToolResult, ToolVisibility } from '../../types';
|
|
2
|
+
|
|
3
|
+
export class ApiModule implements ToolModule {
|
|
4
|
+
id = 'api';
|
|
5
|
+
toolVisibility: ToolVisibility = 'current-channel';
|
|
6
|
+
|
|
7
|
+
isEnabled(): boolean {
|
|
8
|
+
return process.env.GATEWAY_ORIGIN_CHANNEL === 'api';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getTools(): McpToolDefinition[] {
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
name: 'api_reply',
|
|
15
|
+
description:
|
|
16
|
+
'Attach files to the current API session response. ' +
|
|
17
|
+
'Use to include screenshots or images in the reply returned to the API caller. ' +
|
|
18
|
+
'Files must be absolute paths already saved to the session media directory.',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
files: {
|
|
23
|
+
type: 'array',
|
|
24
|
+
items: { type: 'string' },
|
|
25
|
+
description: 'Absolute file paths to attach as images.',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
required: ['files'],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async handleTool(name: string, args: Record<string, unknown>): Promise<McpToolResult> {
|
|
35
|
+
if (name === 'api_reply') {
|
|
36
|
+
return this.handleApiReply(args);
|
|
37
|
+
}
|
|
38
|
+
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private async handleApiReply(args: Record<string, unknown>): Promise<McpToolResult> {
|
|
42
|
+
const files = (args.files as string[] | undefined) ?? [];
|
|
43
|
+
if (!files.length) {
|
|
44
|
+
return { content: [{ type: 'text', text: 'No files provided.' }] };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const apiUrl = process.env.GATEWAY_API_URL;
|
|
48
|
+
const agentId = process.env.GATEWAY_AGENT_ID;
|
|
49
|
+
const sessionId = process.env.GATEWAY_SESSION_ID;
|
|
50
|
+
const apiKey = process.env.GATEWAY_API_KEY;
|
|
51
|
+
|
|
52
|
+
if (!apiUrl || !agentId || !sessionId) {
|
|
53
|
+
return {
|
|
54
|
+
content: [{ type: 'text', text: 'api_reply: missing GATEWAY_API_URL, GATEWAY_AGENT_ID, or GATEWAY_SESSION_ID' }],
|
|
55
|
+
isError: true,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const res = await fetch(
|
|
61
|
+
`${apiUrl}/v1/agents/${encodeURIComponent(agentId)}/sessions/${encodeURIComponent(sessionId)}/attachments`,
|
|
62
|
+
{
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: {
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
...(apiKey ? { 'X-Api-Key': apiKey } : {}),
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify({ files }),
|
|
69
|
+
signal: AbortSignal.timeout(5000),
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
const text = await res.text().catch(() => '');
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: 'text', text: `api_reply: gateway returned ${res.status}: ${text}` }],
|
|
76
|
+
isError: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return { content: [{ type: 'text', text: `Attached ${files.length} file(s).` }] };
|
|
80
|
+
} catch (err) {
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: 'text', text: `api_reply: ${(err as Error).message}` }],
|
|
83
|
+
isError: true,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -24,16 +24,37 @@ export class BrowserModule implements ToolModule {
|
|
|
24
24
|
|
|
25
25
|
const result = await callGetpodBrowser(name, args);
|
|
26
26
|
|
|
27
|
+
if (name === 'browser_create_session' && !result.isError) {
|
|
28
|
+
const textBlock = result.content[0] as { type: string; text: string } | undefined;
|
|
29
|
+
if (textBlock?.type === 'text') {
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(textBlock.text) as Record<string, unknown>;
|
|
32
|
+
delete parsed['stream_url'];
|
|
33
|
+
return { content: [{ type: 'text', text: JSON.stringify(parsed) }] };
|
|
34
|
+
} catch {
|
|
35
|
+
// return as-is if parsing fails
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
if (name === 'browser_screenshot' && !result.isError) {
|
|
28
41
|
// getpod-browser returns {type:"image", data: base64, mimeType:"image/jpeg"}.
|
|
29
|
-
// Decode and save
|
|
42
|
+
// Decode and save so callers can attach the file path directly.
|
|
43
|
+
// For API sessions, save to the session media dir so files are HTTP-accessible.
|
|
30
44
|
const block = result.content[0] as Record<string, string> | undefined;
|
|
31
45
|
const b64 = block?.['data'] ?? '';
|
|
32
46
|
const mime = block?.['mimeType'] ?? 'image/jpeg';
|
|
33
47
|
if (b64) {
|
|
34
48
|
const ext = mime.includes('png') ? 'png' : 'jpg';
|
|
35
49
|
const sid = (args.session_id as string | undefined) ?? 'default';
|
|
36
|
-
const
|
|
50
|
+
const mediaDir = process.env.GATEWAY_SESSION_MEDIA_DIR;
|
|
51
|
+
let filePath: string;
|
|
52
|
+
if (mediaDir) {
|
|
53
|
+
fs.mkdirSync(mediaDir, { recursive: true });
|
|
54
|
+
filePath = path.join(mediaDir, `browser_shot_${sid}_${Date.now()}.${ext}`);
|
|
55
|
+
} else {
|
|
56
|
+
filePath = path.join('/tmp', `browser_shot_${sid}.${ext}`);
|
|
57
|
+
}
|
|
37
58
|
fs.writeFileSync(filePath, Buffer.from(b64, 'base64'));
|
|
38
59
|
return { content: [{ type: 'text', text: filePath }] };
|
|
39
60
|
}
|
|
@@ -136,9 +157,7 @@ async function callGetpodBrowser(
|
|
|
136
157
|
const browserToolDefs: McpToolDefinition[] = [
|
|
137
158
|
{
|
|
138
159
|
name: 'browser_create_session',
|
|
139
|
-
description:
|
|
140
|
-
'Create or resume a browser session. Returns stream_url and status. ' +
|
|
141
|
-
'IMPORTANT: After creating a session, always share the stream_url with the user so they can open the browser in their client.',
|
|
160
|
+
description: 'Create or resume a browser session. Returns session status.',
|
|
142
161
|
inputSchema: {
|
|
143
162
|
type: 'object',
|
|
144
163
|
properties: {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: open-browser
|
|
3
|
-
description: "
|
|
3
|
+
description: "ALWAYS invoke this skill when user says 'browser [site]', 'open [site]', or asks to navigate to a website. Never call MCP browser tools directly."
|
|
4
4
|
user-invocable: true
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# open-browser
|
|
8
8
|
|
|
9
|
-
When user says "
|
|
9
|
+
When user says "open X in browser", "navigate to X", "open chrome", "browser to X", "switch to tab X", etc.
|
|
10
10
|
|
|
11
11
|
## Rules
|
|
12
12
|
|
|
@@ -21,13 +21,9 @@ When user says "เปิด X", "navigate to X", "open X in browser", "switch t
|
|
|
21
21
|
|
|
22
22
|
Call `mcp__gateway__browser_create_session` with NO arguments (session_id is auto-injected).
|
|
23
23
|
|
|
24
|
-
Result contains
|
|
24
|
+
Result contains session status only.
|
|
25
25
|
|
|
26
|
-
### Step 2 —
|
|
27
|
-
|
|
28
|
-
Reply on Telegram right away with the stream_url so the user can open the browser.
|
|
29
|
-
|
|
30
|
-
### Step 3 — Check current tabs
|
|
26
|
+
### Step 2 — Check current tabs
|
|
31
27
|
|
|
32
28
|
Call `mcp__gateway__browser_tabs` — returns list of `{tab_id, url, title}`.
|
|
33
29
|
|