crewly 1.20.247 → 1.20.249
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/config/skills/agent/core/list-channels/SKILL.md +61 -0
- package/config/skills/agent/core/list-channels/execute.sh +36 -0
- package/config/skills/agent/core/list-channels/execute.test.sh +17 -0
- package/config/skills/agent/core/reply-channel/SKILL.md +10 -0
- package/config/skills/agent/core/reply-channel/execute.sh +23 -1
- package/config/skills/agent/core/reply-channel/execute.test.sh +25 -0
- package/dist/backend/backend/src/constants.d.ts +56 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +56 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/channels/channels.routes.d.ts +27 -0
- package/dist/backend/backend/src/controllers/channels/channels.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/channels/channels.routes.js +99 -0
- package/dist/backend/backend/src/controllers/channels/channels.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/connector/connector.controller.d.ts +1 -0
- package/dist/backend/backend/src/controllers/connector/connector.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/connector/connector.controller.js +7 -0
- package/dist/backend/backend/src/controllers/connector/connector.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/connector/connector.routes.d.ts +1 -0
- package/dist/backend/backend/src/controllers/connector/connector.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/connector/connector.routes.js +7 -0
- package/dist/backend/backend/src/controllers/connector/connector.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.d.ts +55 -0
- package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.js +148 -0
- package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.js.map +1 -0
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +18 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +5 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/backend/backend/src/services/channels/crewly-channel.service.d.ts +247 -0
- package/dist/backend/backend/src/services/channels/crewly-channel.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/channels/crewly-channel.service.js +631 -0
- package/dist/backend/backend/src/services/channels/crewly-channel.service.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +26 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +40 -2
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
- package/dist/backend/backend/src/services/connector/connector-access.service.d.ts +8 -0
- package/dist/backend/backend/src/services/connector/connector-access.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/connector/connector-access.service.js +17 -0
- package/dist/backend/backend/src/services/connector/connector-access.service.js.map +1 -1
- package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.d.ts +77 -0
- package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.js +132 -0
- package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.js.map +1 -0
- package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.d.ts +75 -0
- package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.js +155 -0
- package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.js.map +1 -0
- package/dist/backend/backend/src/services/connector/remote-mcp.service.d.ts +192 -0
- package/dist/backend/backend/src/services/connector/remote-mcp.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/connector/remote-mcp.service.js +334 -0
- package/dist/backend/backend/src/services/connector/remote-mcp.service.js.map +1 -0
- package/dist/backend/backend/src/services/core/credential-files.d.ts.map +1 -1
- package/dist/backend/backend/src/services/core/credential-files.js +4 -1
- package/dist/backend/backend/src/services/core/credential-files.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-channel-members.d.ts +60 -0
- package/dist/backend/backend/src/services/slack/slack-channel-members.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-channel-members.js +70 -0
- package/dist/backend/backend/src/services/slack/slack-channel-members.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +147 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +423 -12
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.d.ts +10 -0
- package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.js +15 -0
- package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/backend/backend/src/types/crewly-channel.types.d.ts +77 -0
- package/dist/backend/backend/src/types/crewly-channel.types.d.ts.map +1 -0
- package/dist/backend/backend/src/types/crewly-channel.types.js +35 -0
- package/dist/backend/backend/src/types/crewly-channel.types.js.map +1 -0
- package/dist/backend/backend/src/types/slack.types.d.ts +7 -0
- package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/slack.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +56 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +56 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.js +5 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts +26 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js +40 -2
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
- package/dist/cli/backend/src/services/connector/connector-access.service.d.ts +85 -0
- package/dist/cli/backend/src/services/connector/connector-access.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/connector/connector-access.service.js +130 -0
- package/dist/cli/backend/src/services/connector/connector-access.service.js.map +1 -0
- package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.d.ts +77 -0
- package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.js +132 -0
- package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.js.map +1 -0
- package/dist/cli/backend/src/services/connector/remote-mcp.service.d.ts +192 -0
- package/dist/cli/backend/src/services/connector/remote-mcp.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/connector/remote-mcp.service.js +334 -0
- package/dist/cli/backend/src/services/connector/remote-mcp.service.js.map +1 -0
- package/dist/cli/backend/src/services/core/credential-files.d.ts.map +1 -1
- package/dist/cli/backend/src/services/core/credential-files.js +4 -1
- package/dist/cli/backend/src/services/core/credential-files.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-channel-members.d.ts +87 -0
- package/dist/cli/backend/src/services/slack/slack-channel-members.d.ts.map +1 -0
- package/dist/cli/backend/src/services/slack/slack-channel-members.js +124 -0
- package/dist/cli/backend/src/services/slack/slack-channel-members.js.map +1 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +147 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +423 -12
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack.service.d.ts +10 -0
- package/dist/cli/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack.service.js +15 -0
- package/dist/cli/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/cli/backend/src/types/slack.types.d.ts +7 -0
- package/dist/cli/backend/src/types/slack.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/slack.types.js.map +1 -1
- package/frontend/dist/assets/{index-c9630c55.js → index-2db17f1e.js} +292 -292
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand remote MCP servers to an agent at launch.
|
|
3
|
+
*
|
|
4
|
+
* For each server whose `mcp:<id>` allowlist admits the agent's role:
|
|
5
|
+
*
|
|
6
|
+
* | Runtime | How |
|
|
7
|
+
* |---|---|
|
|
8
|
+
* | Claude Code | `--mcp-config '<CREWLY_HOME>/runtime/remote-mcp/<session>/claude-mcp.json'` (adds to the project `.mcp.json`; no `--strict-mcp-config`) |
|
|
9
|
+
* | Codex | `-c "mcp_servers.<id>.url=$(cat '<file>')"` (+ `http_headers`), so the secret never appears on the typed command line |
|
|
10
|
+
* | Gemini CLI, Antigravity, OpenCode, crewly-agent | skipped with a log line |
|
|
11
|
+
*
|
|
12
|
+
* Every file is written under CREWLY_HOME with mode 0600 (directory 0700),
|
|
13
|
+
* never into the project. The returned flags carry only file paths.
|
|
14
|
+
*
|
|
15
|
+
* @module services/connector/remote-mcp-launch.service
|
|
16
|
+
*/
|
|
17
|
+
import * as fs from 'fs/promises';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
|
|
20
|
+
import { LoggerService } from '../core/logger.service.js';
|
|
21
|
+
import { REMOTE_MCP_CONSTANTS, RUNTIME_TYPES } from '../../constants.js';
|
|
22
|
+
import { RemoteMcpService, writeSecretJson } from './remote-mcp.service.js';
|
|
23
|
+
const C = REMOTE_MCP_CONSTANTS;
|
|
24
|
+
/**
|
|
25
|
+
* Quote for a POSIX shell.
|
|
26
|
+
*
|
|
27
|
+
* @param value - Raw value
|
|
28
|
+
* @returns Single-quoted value
|
|
29
|
+
*/
|
|
30
|
+
function shq(value) {
|
|
31
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The per-session directory for launch files.
|
|
35
|
+
*
|
|
36
|
+
* @param crewlyHome - CREWLY_HOME
|
|
37
|
+
* @param sessionName - PTY session name
|
|
38
|
+
* @returns Absolute directory
|
|
39
|
+
*/
|
|
40
|
+
export function remoteMcpSessionDir(crewlyHome, sessionName) {
|
|
41
|
+
const safe = sessionName.replace(/[^A-Za-z0-9._-]/g, '_').replace(/^\.+/, '_') || '_';
|
|
42
|
+
return path.join(crewlyHome, C.RUNTIME_DIR, safe);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Claude Code's `--mcp-config` body.
|
|
46
|
+
*
|
|
47
|
+
* @param servers - Allowed servers
|
|
48
|
+
* @returns `{ mcpServers: { <id>: { type: 'http', url, headers? } } }`
|
|
49
|
+
*/
|
|
50
|
+
export function buildClaudeMcpConfig(servers) {
|
|
51
|
+
const mcpServers = {};
|
|
52
|
+
for (const s of servers) {
|
|
53
|
+
mcpServers[s.id] = { type: 'http', url: s.url, ...(s.headers && Object.keys(s.headers).length ? { headers: s.headers } : {}) };
|
|
54
|
+
}
|
|
55
|
+
return { mcpServers };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A TOML inline table of headers (JSON strings are valid TOML basic strings).
|
|
59
|
+
*
|
|
60
|
+
* @param headers - Header map
|
|
61
|
+
* @returns e.g. `{ "Authorization" = "Bearer x" }`
|
|
62
|
+
*/
|
|
63
|
+
export function toTomlInlineTable(headers) {
|
|
64
|
+
const parts = Object.entries(headers).map(([k, v]) => `${JSON.stringify(k)} = ${JSON.stringify(v)}`);
|
|
65
|
+
return `{ ${parts.join(', ')} }`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Write a 0600 text file.
|
|
69
|
+
*
|
|
70
|
+
* @param file - Destination
|
|
71
|
+
* @param body - Content
|
|
72
|
+
*/
|
|
73
|
+
async function writeSecretText(file, body) {
|
|
74
|
+
await fs.writeFile(file, body, { encoding: 'utf8', mode: 0o600 });
|
|
75
|
+
await fs.chmod(file, 0o600);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Write the launch files for one agent and return the flags that load them.
|
|
79
|
+
* Never throws: on any failure the agent launches without remote MCP and
|
|
80
|
+
* the error is logged (without secrets).
|
|
81
|
+
*
|
|
82
|
+
* @param input - Session, role, runtime
|
|
83
|
+
* @returns Flags and the server ids handed over
|
|
84
|
+
*/
|
|
85
|
+
export async function buildRemoteMcpLaunchFlags(input) {
|
|
86
|
+
const logger = LoggerService.getInstance().createComponentLogger('RemoteMcpLaunch');
|
|
87
|
+
const { sessionName, role, runtimeType } = input;
|
|
88
|
+
const crewlyHome = input.crewlyHome || getCrewlyHomePath();
|
|
89
|
+
const dir = remoteMcpSessionDir(crewlyHome, sessionName);
|
|
90
|
+
try {
|
|
91
|
+
const servers = await (input.service ?? RemoteMcpService.getInstance()).serversForRole(role);
|
|
92
|
+
// Start clean: a server removed or a role narrowed since the last launch must be gone.
|
|
93
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
94
|
+
if (servers.length === 0)
|
|
95
|
+
return { flags: [], servers: [], skipped: 'no-servers' };
|
|
96
|
+
const ids = servers.map((s) => s.id);
|
|
97
|
+
if (runtimeType !== RUNTIME_TYPES.CLAUDE_CODE && runtimeType !== RUNTIME_TYPES.CODEX_CLI) {
|
|
98
|
+
logger.info('Remote MCP servers: not supported for this runtime — skipped', { sessionName, runtimeType, servers: ids });
|
|
99
|
+
return { flags: [], servers: [], skipped: 'unsupported-runtime' };
|
|
100
|
+
}
|
|
101
|
+
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
102
|
+
await fs.chmod(dir, 0o700);
|
|
103
|
+
if (runtimeType === RUNTIME_TYPES.CLAUDE_CODE) {
|
|
104
|
+
const file = path.join(dir, C.CLAUDE_CONFIG_FILE);
|
|
105
|
+
await writeSecretJson(file, buildClaudeMcpConfig(servers));
|
|
106
|
+
logger.info('Remote MCP servers: added via --mcp-config', { sessionName, role, servers: ids });
|
|
107
|
+
return { flags: ['--mcp-config', shq(file)], servers: ids };
|
|
108
|
+
}
|
|
109
|
+
const flags = [];
|
|
110
|
+
for (const s of servers) {
|
|
111
|
+
const urlFile = path.join(dir, `codex-${s.id}-url.toml`);
|
|
112
|
+
await writeSecretText(urlFile, JSON.stringify(s.url));
|
|
113
|
+
flags.push('-c', `"mcp_servers.${s.id}.url=$(cat ${shq(urlFile)})"`);
|
|
114
|
+
if (s.headers && Object.keys(s.headers).length > 0) {
|
|
115
|
+
const headersFile = path.join(dir, `codex-${s.id}-headers.toml`);
|
|
116
|
+
await writeSecretText(headersFile, toTomlInlineTable(s.headers));
|
|
117
|
+
flags.push('-c', `"mcp_servers.${s.id}.http_headers=$(cat ${shq(headersFile)})"`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
logger.info('Remote MCP servers: added via codex -c mcp_servers', { sessionName, role, servers: ids });
|
|
121
|
+
return { flags, servers: ids };
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
logger.error('Remote MCP servers: could not prepare launch files — launching without them', {
|
|
125
|
+
sessionName,
|
|
126
|
+
runtimeType,
|
|
127
|
+
error: err instanceof Error ? err.message : String(err),
|
|
128
|
+
});
|
|
129
|
+
return { flags: [], servers: [], skipped: 'error' };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=remote-mcp-launch.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-mcp-launch.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/connector/remote-mcp-launch.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Test" for a remote MCP server: MCP `initialize`, `notifications/initialized`
|
|
3
|
+
* and `tools/list` over streamable HTTP, reporting the tool count and names.
|
|
4
|
+
*
|
|
5
|
+
* Handles both response shapes the transport allows (a JSON body or a
|
|
6
|
+
* `text/event-stream` with the reply in a `data:` line) and carries the
|
|
7
|
+
* `Mcp-Session-Id` the server hands out. Never puts the URL (or anything of
|
|
8
|
+
* its path) into a result or error.
|
|
9
|
+
*
|
|
10
|
+
* @module services/connector/remote-mcp-probe.service
|
|
11
|
+
*/
|
|
12
|
+
import type { RemoteMcpServer } from './remote-mcp.service.js';
|
|
13
|
+
/** Result of a probe. */
|
|
14
|
+
export type RemoteMcpProbeResult = {
|
|
15
|
+
ok: true;
|
|
16
|
+
serverName?: string;
|
|
17
|
+
toolCount: number;
|
|
18
|
+
tools: string[];
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
error: string;
|
|
22
|
+
};
|
|
23
|
+
/** Minimal fetch signature (injectable for tests). */
|
|
24
|
+
export type FetchLike = (url: string, init: {
|
|
25
|
+
method: string;
|
|
26
|
+
headers: Record<string, string>;
|
|
27
|
+
body?: string;
|
|
28
|
+
signal?: AbortSignal;
|
|
29
|
+
}) => Promise<{
|
|
30
|
+
status: number;
|
|
31
|
+
ok: boolean;
|
|
32
|
+
headers: {
|
|
33
|
+
get(name: string): string | null;
|
|
34
|
+
};
|
|
35
|
+
text(): Promise<string>;
|
|
36
|
+
}>;
|
|
37
|
+
/** A JSON-RPC response. */
|
|
38
|
+
interface JsonRpcResponse {
|
|
39
|
+
jsonrpc?: string;
|
|
40
|
+
id?: number | string | null;
|
|
41
|
+
result?: Record<string, unknown>;
|
|
42
|
+
error?: {
|
|
43
|
+
code?: number;
|
|
44
|
+
message?: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Remove every trace of the URL from a message: the whole URL, its path,
|
|
49
|
+
* and any long path segment (Zoho's key is one).
|
|
50
|
+
*
|
|
51
|
+
* @param message - Raw message
|
|
52
|
+
* @param url - Server URL
|
|
53
|
+
* @returns Safe message
|
|
54
|
+
*/
|
|
55
|
+
export declare function scrubUrl(message: string, url: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* Read a JSON-RPC reply out of a JSON or SSE body.
|
|
58
|
+
*
|
|
59
|
+
* @param contentType - Response content type
|
|
60
|
+
* @param body - Response text
|
|
61
|
+
* @param id - Request id to match
|
|
62
|
+
* @returns The reply, or undefined
|
|
63
|
+
*/
|
|
64
|
+
export declare function parseRpcBody(contentType: string, body: string, id: number): JsonRpcResponse | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Probe a server: initialize, then list its tools (following `nextCursor`
|
|
67
|
+
* up to five pages).
|
|
68
|
+
*
|
|
69
|
+
* @param server - Stored server (URL and headers)
|
|
70
|
+
* @param fetchImpl - fetch (tests)
|
|
71
|
+
* @returns Tool count and names, or a safe error
|
|
72
|
+
*/
|
|
73
|
+
export declare function probeRemoteMcp(server: Pick<RemoteMcpServer, 'url' | 'headers'>, fetchImpl?: FetchLike): Promise<RemoteMcpProbeResult>;
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=remote-mcp-probe.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-mcp-probe.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/connector/remote-mcp-probe.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI/D,yBAAyB;AACzB,MAAM,MAAM,oBAAoB,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GACrE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjC,sDAAsD;AACtD,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC;IAC/I,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC9C,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB,CAAC,CAAC;AAEH,2BAA2B;AAC3B,UAAU,eAAe;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAW7D;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAsBvG;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,KAAK,GAAG,SAAS,CAAC,EAAE,SAAS,GAAE,SAAyC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiE1K"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Test" for a remote MCP server: MCP `initialize`, `notifications/initialized`
|
|
3
|
+
* and `tools/list` over streamable HTTP, reporting the tool count and names.
|
|
4
|
+
*
|
|
5
|
+
* Handles both response shapes the transport allows (a JSON body or a
|
|
6
|
+
* `text/event-stream` with the reply in a `data:` line) and carries the
|
|
7
|
+
* `Mcp-Session-Id` the server hands out. Never puts the URL (or anything of
|
|
8
|
+
* its path) into a result or error.
|
|
9
|
+
*
|
|
10
|
+
* @module services/connector/remote-mcp-probe.service
|
|
11
|
+
*/
|
|
12
|
+
import { REMOTE_MCP_CONSTANTS } from '../../constants.js';
|
|
13
|
+
const C = REMOTE_MCP_CONSTANTS;
|
|
14
|
+
/**
|
|
15
|
+
* Remove every trace of the URL from a message: the whole URL, its path,
|
|
16
|
+
* and any long path segment (Zoho's key is one).
|
|
17
|
+
*
|
|
18
|
+
* @param message - Raw message
|
|
19
|
+
* @param url - Server URL
|
|
20
|
+
* @returns Safe message
|
|
21
|
+
*/
|
|
22
|
+
export function scrubUrl(message, url) {
|
|
23
|
+
let out = message.split(url).join('(server URL)');
|
|
24
|
+
try {
|
|
25
|
+
const u = new URL(url);
|
|
26
|
+
if (u.pathname.length > 1)
|
|
27
|
+
out = out.split(u.pathname).join('/…');
|
|
28
|
+
for (const seg of u.pathname.split('/').filter((s) => s.length >= 8))
|
|
29
|
+
out = out.split(seg).join('…');
|
|
30
|
+
if (u.search)
|
|
31
|
+
out = out.split(u.search).join('?…');
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
/* unparseable URL: whole-string removal above is all we can do */
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read a JSON-RPC reply out of a JSON or SSE body.
|
|
40
|
+
*
|
|
41
|
+
* @param contentType - Response content type
|
|
42
|
+
* @param body - Response text
|
|
43
|
+
* @param id - Request id to match
|
|
44
|
+
* @returns The reply, or undefined
|
|
45
|
+
*/
|
|
46
|
+
export function parseRpcBody(contentType, body, id) {
|
|
47
|
+
const candidates = [];
|
|
48
|
+
if (contentType.includes('text/event-stream')) {
|
|
49
|
+
// Events are separated by blank lines; a data field may span several lines.
|
|
50
|
+
for (const event of body.split(/\r?\n\r?\n/)) {
|
|
51
|
+
const data = event.split(/\r?\n/).filter((l) => l.startsWith('data:')).map((l) => l.slice(5).trimStart()).join('\n');
|
|
52
|
+
if (!data)
|
|
53
|
+
continue;
|
|
54
|
+
try {
|
|
55
|
+
candidates.push(JSON.parse(data));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* not JSON — skip */
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
try {
|
|
64
|
+
candidates.push(JSON.parse(body));
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const flat = candidates.flatMap((c) => (Array.isArray(c) ? c : [c]));
|
|
71
|
+
return flat.find((m) => m && m.id === id);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Probe a server: initialize, then list its tools (following `nextCursor`
|
|
75
|
+
* up to five pages).
|
|
76
|
+
*
|
|
77
|
+
* @param server - Stored server (URL and headers)
|
|
78
|
+
* @param fetchImpl - fetch (tests)
|
|
79
|
+
* @returns Tool count and names, or a safe error
|
|
80
|
+
*/
|
|
81
|
+
export async function probeRemoteMcp(server, fetchImpl = fetch) {
|
|
82
|
+
const { url } = server;
|
|
83
|
+
let sessionId = null;
|
|
84
|
+
let protocolVersion = C.PROTOCOL_VERSION;
|
|
85
|
+
const post = async (payload) => {
|
|
86
|
+
const headers = {
|
|
87
|
+
...(server.headers ?? {}),
|
|
88
|
+
'Content-Type': 'application/json',
|
|
89
|
+
Accept: 'application/json, text/event-stream',
|
|
90
|
+
};
|
|
91
|
+
if (sessionId)
|
|
92
|
+
headers['Mcp-Session-Id'] = sessionId;
|
|
93
|
+
if (payload.method !== 'initialize')
|
|
94
|
+
headers['MCP-Protocol-Version'] = protocolVersion;
|
|
95
|
+
const res = await fetchImpl(url, { method: 'POST', headers, body: JSON.stringify(payload), signal: AbortSignal.timeout(C.TEST_TIMEOUT_MS) });
|
|
96
|
+
const sid = res.headers.get('mcp-session-id');
|
|
97
|
+
if (sid)
|
|
98
|
+
sessionId = sid;
|
|
99
|
+
return { status: res.status, contentType: res.headers.get('content-type') ?? '', body: await res.text() };
|
|
100
|
+
};
|
|
101
|
+
const call = async (id, method, params) => {
|
|
102
|
+
const res = await post({ jsonrpc: '2.0', id, method, params });
|
|
103
|
+
if (res.status === 401 || res.status === 403) {
|
|
104
|
+
throw new Error(`The server refused the request (${res.status}). Check the URL — it may have been regenerated.`);
|
|
105
|
+
}
|
|
106
|
+
if (res.status === 404)
|
|
107
|
+
throw new Error('The server was not found (404). Check the URL.');
|
|
108
|
+
if (res.status < 200 || res.status >= 300)
|
|
109
|
+
throw new Error(`The server answered ${res.status} to ${method}.`);
|
|
110
|
+
const reply = parseRpcBody(res.contentType, res.body, id);
|
|
111
|
+
if (!reply)
|
|
112
|
+
throw new Error(`The server's reply to ${method} was not MCP (is this an MCP server URL?).`);
|
|
113
|
+
if (reply.error)
|
|
114
|
+
throw new Error(`The server returned an error for ${method}: ${String(reply.error.message ?? reply.error.code ?? 'unknown')}`);
|
|
115
|
+
return reply.result ?? {};
|
|
116
|
+
};
|
|
117
|
+
try {
|
|
118
|
+
const init = await call(1, 'initialize', {
|
|
119
|
+
protocolVersion: C.PROTOCOL_VERSION,
|
|
120
|
+
capabilities: {},
|
|
121
|
+
clientInfo: { name: 'crewly', version: '1.0.0' },
|
|
122
|
+
});
|
|
123
|
+
if (typeof init.protocolVersion === 'string')
|
|
124
|
+
protocolVersion = init.protocolVersion;
|
|
125
|
+
const serverInfo = init.serverInfo;
|
|
126
|
+
// Notifications get 202 and no body; a failure here is not fatal.
|
|
127
|
+
await post({ jsonrpc: '2.0', method: 'notifications/initialized' }).catch(() => undefined);
|
|
128
|
+
const names = [];
|
|
129
|
+
let cursor;
|
|
130
|
+
for (let page = 0; page < 5; page++) {
|
|
131
|
+
const result = await call(2 + page, 'tools/list', cursor ? { cursor } : {});
|
|
132
|
+
const tools = Array.isArray(result.tools) ? result.tools : [];
|
|
133
|
+
for (const t of tools)
|
|
134
|
+
if (typeof t?.name === 'string')
|
|
135
|
+
names.push(t.name);
|
|
136
|
+
cursor = typeof result.nextCursor === 'string' && result.nextCursor ? result.nextCursor : undefined;
|
|
137
|
+
if (!cursor)
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
ok: true,
|
|
142
|
+
...(typeof serverInfo?.name === 'string' ? { serverName: scrubUrl(serverInfo.name, url) } : {}),
|
|
143
|
+
toolCount: names.length,
|
|
144
|
+
tools: names.slice(0, C.MAX_TOOL_NAMES),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
const raw = err instanceof Error
|
|
149
|
+
? (err.name === 'TimeoutError' || err.name === 'AbortError' ? 'The server did not answer in time.' : err.message)
|
|
150
|
+
: String(err);
|
|
151
|
+
const cause = err instanceof Error && err.cause instanceof Error ? ` (${err.cause.message})` : '';
|
|
152
|
+
return { ok: false, error: scrubUrl(`${raw}${cause}`, url) };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=remote-mcp-probe.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-mcp-probe.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/connector/remote-mcp-probe.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote MCP servers — streamable-HTTP MCP servers the owner connects once
|
|
3
|
+
* (Zoho MCP first, any other server the same way) and Crewly hands to every
|
|
4
|
+
* agent whose role may use them.
|
|
5
|
+
*
|
|
6
|
+
* Stored at `<CREWLY_HOME>/remote-mcp-servers.json` with mode 0600. The URL
|
|
7
|
+
* (Zoho's carries the server's API key) and any headers are secrets: they
|
|
8
|
+
* are never logged, never returned in full by {@link toView}, and never
|
|
9
|
+
* written into a project directory (a project `.mcp.json` would be
|
|
10
|
+
* committed). Agents get them through per-session files under CREWLY_HOME
|
|
11
|
+
* (see remote-mcp-launch.service).
|
|
12
|
+
*
|
|
13
|
+
* Each server is a gated connector `mcp:<id>` in
|
|
14
|
+
* {@link ConnectorAccessService}: open to every agent until the owner picks
|
|
15
|
+
* roles.
|
|
16
|
+
*
|
|
17
|
+
* @module services/connector/remote-mcp.service
|
|
18
|
+
*/
|
|
19
|
+
import { ConnectorAccessService } from './connector-access.service.js';
|
|
20
|
+
/** Catalog preset a server was added from. */
|
|
21
|
+
export type RemoteMcpProvider = 'zoho' | 'custom';
|
|
22
|
+
/** One stored server (secrets included — never send this to a client). */
|
|
23
|
+
export interface RemoteMcpServer {
|
|
24
|
+
/** Stable id; also the MCP server name agents see and the `mcp:<id>` access key. */
|
|
25
|
+
id: string;
|
|
26
|
+
/** Owner-facing name. */
|
|
27
|
+
label: string;
|
|
28
|
+
/** Streamable-HTTP endpoint (secret). */
|
|
29
|
+
url: string;
|
|
30
|
+
/** Extra request headers (secret values). */
|
|
31
|
+
headers?: Record<string, string>;
|
|
32
|
+
/** Catalog preset. */
|
|
33
|
+
provider?: RemoteMcpProvider;
|
|
34
|
+
/** ISO time it was added. */
|
|
35
|
+
createdAt: string;
|
|
36
|
+
}
|
|
37
|
+
/** What the API returns: no secrets. */
|
|
38
|
+
export interface RemoteMcpServerView {
|
|
39
|
+
id: string;
|
|
40
|
+
label: string;
|
|
41
|
+
provider: RemoteMcpProvider;
|
|
42
|
+
/** Scheme + host only, e.g. `https://crm-123.zohomcp.com/…`. */
|
|
43
|
+
urlMasked: string;
|
|
44
|
+
/** Header names (values withheld). */
|
|
45
|
+
headerNames: string[];
|
|
46
|
+
createdAt: string;
|
|
47
|
+
/** Key of its role allowlist in connector access. */
|
|
48
|
+
connectorId: string;
|
|
49
|
+
}
|
|
50
|
+
/** Input for {@link RemoteMcpService.add}. */
|
|
51
|
+
export interface AddRemoteMcpInput {
|
|
52
|
+
label: string;
|
|
53
|
+
url: string;
|
|
54
|
+
headers?: Record<string, string>;
|
|
55
|
+
provider?: string;
|
|
56
|
+
}
|
|
57
|
+
/** A validation failure the API turns into a 400. */
|
|
58
|
+
export declare class RemoteMcpValidationError extends Error {
|
|
59
|
+
constructor(message: string);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The connector-access id of a server.
|
|
63
|
+
*
|
|
64
|
+
* @param id - Server id
|
|
65
|
+
* @returns `mcp:<id>`
|
|
66
|
+
*/
|
|
67
|
+
export declare function remoteMcpConnectorId(id: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* Show a URL without its secret parts: scheme and host only.
|
|
70
|
+
*
|
|
71
|
+
* @param url - Stored URL
|
|
72
|
+
* @returns e.g. `https://crm-123.zohomcp.com/…`, or `(hidden)` when it does not parse
|
|
73
|
+
*/
|
|
74
|
+
export declare function maskRemoteMcpUrl(url: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Validate a server URL: https (http only for localhost), no whitespace,
|
|
77
|
+
* quotes or backslashes (it is embedded in launch files), bounded length.
|
|
78
|
+
*
|
|
79
|
+
* @param raw - Pasted URL
|
|
80
|
+
* @returns The trimmed URL
|
|
81
|
+
* @throws RemoteMcpValidationError when it is not usable (message never echoes the URL)
|
|
82
|
+
*/
|
|
83
|
+
export declare function validateRemoteMcpUrl(raw: unknown): string;
|
|
84
|
+
/**
|
|
85
|
+
* Validate optional headers: token-shaped names, single-line values.
|
|
86
|
+
*
|
|
87
|
+
* @param raw - Headers object or undefined
|
|
88
|
+
* @returns Clean headers, or undefined when none
|
|
89
|
+
* @throws RemoteMcpValidationError on a bad name or value
|
|
90
|
+
*/
|
|
91
|
+
export declare function validateRemoteMcpHeaders(raw: unknown): Record<string, string> | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Clean a label.
|
|
94
|
+
*
|
|
95
|
+
* @param raw - Label input
|
|
96
|
+
* @returns Trimmed label
|
|
97
|
+
* @throws RemoteMcpValidationError when empty
|
|
98
|
+
*/
|
|
99
|
+
export declare function normalizeRemoteMcpLabel(raw: unknown): string;
|
|
100
|
+
/**
|
|
101
|
+
* Derive an id (MCP server name) from a label: lowercase slug, unique.
|
|
102
|
+
*
|
|
103
|
+
* @param label - Label
|
|
104
|
+
* @param taken - Ids already in use
|
|
105
|
+
* @returns e.g. `zoho`, `zoho-2`
|
|
106
|
+
*/
|
|
107
|
+
export declare function deriveRemoteMcpId(label: string, taken: Set<string>): string;
|
|
108
|
+
/**
|
|
109
|
+
* Turn a stored server into its API shape.
|
|
110
|
+
*
|
|
111
|
+
* @param server - Stored server
|
|
112
|
+
* @returns The view (no URL, no header values)
|
|
113
|
+
*/
|
|
114
|
+
export declare function toView(server: RemoteMcpServer): RemoteMcpServerView;
|
|
115
|
+
/**
|
|
116
|
+
* Reads and writes the remote MCP server store.
|
|
117
|
+
*/
|
|
118
|
+
export declare class RemoteMcpService {
|
|
119
|
+
private static instance;
|
|
120
|
+
private readonly logger;
|
|
121
|
+
private readonly filePath;
|
|
122
|
+
private readonly access;
|
|
123
|
+
private writeChain;
|
|
124
|
+
/**
|
|
125
|
+
* @param crewlyHome - Override the home directory (tests)
|
|
126
|
+
* @param access - Connector access service (tests)
|
|
127
|
+
*/
|
|
128
|
+
constructor(crewlyHome?: string, access?: ConnectorAccessService);
|
|
129
|
+
static getInstance(): RemoteMcpService;
|
|
130
|
+
/** Reset the singleton (tests). */
|
|
131
|
+
static resetInstance(): void;
|
|
132
|
+
/** Absolute path of the store file. */
|
|
133
|
+
getFilePath(): string;
|
|
134
|
+
/**
|
|
135
|
+
* Every stored server, with secrets. Unreadable entries are dropped.
|
|
136
|
+
*
|
|
137
|
+
* @returns Servers in the order they were added
|
|
138
|
+
*/
|
|
139
|
+
list(): Promise<RemoteMcpServer[]>;
|
|
140
|
+
/**
|
|
141
|
+
* One server, with secrets.
|
|
142
|
+
*
|
|
143
|
+
* @param id - Server id
|
|
144
|
+
* @returns The server or undefined
|
|
145
|
+
*/
|
|
146
|
+
get(id: string): Promise<RemoteMcpServer | undefined>;
|
|
147
|
+
/**
|
|
148
|
+
* Add a server.
|
|
149
|
+
*
|
|
150
|
+
* @param input - Label, URL, optional headers and provider
|
|
151
|
+
* @returns The stored server
|
|
152
|
+
* @throws RemoteMcpValidationError on bad input or a full store
|
|
153
|
+
*/
|
|
154
|
+
add(input: AddRemoteMcpInput): Promise<RemoteMcpServer>;
|
|
155
|
+
/**
|
|
156
|
+
* Rename a server (its id, and so its MCP name and allowlist, stay).
|
|
157
|
+
*
|
|
158
|
+
* @param id - Server id
|
|
159
|
+
* @param label - New label
|
|
160
|
+
* @returns The updated server, or undefined when there is none
|
|
161
|
+
*/
|
|
162
|
+
rename(id: string, label: string): Promise<RemoteMcpServer | undefined>;
|
|
163
|
+
/**
|
|
164
|
+
* Remove a server and its allowlist.
|
|
165
|
+
*
|
|
166
|
+
* @param id - Server id
|
|
167
|
+
* @returns True when it existed
|
|
168
|
+
*/
|
|
169
|
+
remove(id: string): Promise<boolean>;
|
|
170
|
+
/**
|
|
171
|
+
* Servers an agent role may use (its `mcp:<id>` allowlist passes).
|
|
172
|
+
*
|
|
173
|
+
* @param role - Agent role
|
|
174
|
+
* @returns Allowed servers, with secrets
|
|
175
|
+
*/
|
|
176
|
+
serversForRole(role: string): Promise<RemoteMcpServer[]>;
|
|
177
|
+
/**
|
|
178
|
+
* Serialised read-modify-write; the file is written 0600.
|
|
179
|
+
*
|
|
180
|
+
* @param fn - Change to apply
|
|
181
|
+
* @returns The change's result
|
|
182
|
+
*/
|
|
183
|
+
private mutate;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Write JSON readable only by the owner (0600), atomically.
|
|
187
|
+
*
|
|
188
|
+
* @param filePath - Destination
|
|
189
|
+
* @param data - JSON value
|
|
190
|
+
*/
|
|
191
|
+
export declare function writeSecretJson(filePath: string, data: unknown): Promise<void>;
|
|
192
|
+
//# sourceMappingURL=remote-mcp.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-mcp.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/connector/remote-mcp.service.ts"],"names":[],"mappings":"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"}
|