@opencx/mcp 1.11.88 → 1.12.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/dist/api-client.d.ts +3 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +2 -2
- package/dist/api-client.js.map +1 -1
- package/dist/create-server-options.spec.d.ts +2 -0
- package/dist/create-server-options.spec.d.ts.map +1 -0
- package/dist/create-server-options.spec.js +67 -0
- package/dist/create-server-options.spec.js.map +1 -0
- package/dist/http.d.ts +40 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +70 -0
- package/dist/http.js.map +1 -0
- package/dist/http.spec.d.ts +2 -0
- package/dist/http.spec.d.ts.map +1 -0
- package/dist/http.spec.js +178 -0
- package/dist/http.spec.js.map +1 -0
- package/dist/index.js +18 -15
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +11 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +5 -3
- package/dist/server.js.map +1 -1
- package/dist/spec-helpers.d.ts +39 -0
- package/dist/spec-helpers.d.ts.map +1 -0
- package/dist/spec-helpers.js +37 -0
- package/dist/spec-helpers.js.map +1 -0
- package/dist/tools/firecrawl.d.ts +3 -1
- package/dist/tools/firecrawl.d.ts.map +1 -1
- package/dist/tools/firecrawl.js +14 -8
- package/dist/tools/firecrawl.js.map +1 -1
- package/package.json +16 -1
package/dist/api-client.d.ts
CHANGED
|
@@ -19,7 +19,9 @@ export type OpenCXClient = Client<paths> & {
|
|
|
19
19
|
readonly coworkerSessionId: string | undefined;
|
|
20
20
|
postMultipart(path: string, formData: FormData): Promise<JsonValue>;
|
|
21
21
|
};
|
|
22
|
-
export declare function createOpenCXClient(apiKey: string, baseUrl: string
|
|
22
|
+
export declare function createOpenCXClient(apiKey: string, baseUrl: string, options?: {
|
|
23
|
+
coworkerSessionId?: string;
|
|
24
|
+
}): OpenCXClient;
|
|
23
25
|
/**
|
|
24
26
|
* Wraps a value as an MCP text-content result. Handles `undefined` from 204
|
|
25
27
|
* responses by falling back to `{}` so `JSON.stringify` always produces a string.
|
package/dist/api-client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA,OAAqB,EAAE,KAAK,MAAM,EAAmB,MAAM,eAAe,CAAC;AAC3E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAElE,qBAAa,QAAS,SAAQ,KAAK;aAEf,MAAM,EAAE,MAAM;aACd,UAAU,EAAE,MAAM;aAClB,IAAI,EAAE,MAAM;gBAFZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM;CAK/B;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAchD;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG;IACzC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CACrE,CAAC;AAEF,wBAAgB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA,OAAqB,EAAE,KAAK,MAAM,EAAmB,MAAM,eAAe,CAAC;AAC3E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAElE,qBAAa,QAAS,SAAQ,KAAK;aAEf,MAAM,EAAE,MAAM;aACd,UAAU,EAAE,MAAM;aAClB,IAAI,EAAE,MAAM;gBAFZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM;CAK/B;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAchD;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG;IACzC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CACrE,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3C,YAAY,CA4Cd;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,SAAS,GAAG;IAAE,OAAO,EAAE,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAEjG"}
|
package/dist/api-client.js
CHANGED
|
@@ -35,12 +35,12 @@ export function safePath(segment) {
|
|
|
35
35
|
}
|
|
36
36
|
return encodeURIComponent(decoded);
|
|
37
37
|
}
|
|
38
|
-
export function createOpenCXClient(apiKey, baseUrl) {
|
|
38
|
+
export function createOpenCXClient(apiKey, baseUrl, options = {}) {
|
|
39
39
|
const normalizedBase = baseUrl.replace(/\/$/, '');
|
|
40
40
|
const headers = {
|
|
41
41
|
Authorization: `Bearer ${apiKey}`,
|
|
42
42
|
};
|
|
43
|
-
const coworkerSessionId =
|
|
43
|
+
const { coworkerSessionId } = options;
|
|
44
44
|
if (coworkerSessionId) {
|
|
45
45
|
headers['x-opencx-coworker-session-id'] = coworkerSessionId;
|
|
46
46
|
}
|
package/dist/api-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,YAA8C,MAAM,eAAe,CAAC;AAE3E,OAAO,EAAE,eAAe,EAAkB,MAAM,iBAAiB,CAAC;AAElE,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEf;IACA;IACA;IAHlB,YACkB,MAAc,EACd,UAAkB,EAClB,IAAY;QAE5B,KAAK,CAAC,oBAAoB,MAAM,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC,CAAC;QAJ3C,WAAM,GAAN,MAAM,CAAQ;QACd,eAAU,GAAV,UAAU,CAAQ;QAClB,SAAI,GAAJ,IAAI,CAAQ;QAG5B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACpF,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,GAAG,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,GAAG,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,GAAG,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AAOD,MAAM,UAAU,kBAAkB,
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-server-options.spec.d.ts","sourceRoot":"","sources":["../src/create-server-options.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
2
|
+
import { connectInMemory, fetchCallHeaders, fetchCallUrl, toolTextResultSchema, } from './spec-helpers.js';
|
|
3
|
+
function jsonResponse(body) {
|
|
4
|
+
return new Response(body, { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
5
|
+
}
|
|
6
|
+
function stubFetch(body = '{}') {
|
|
7
|
+
const fetchSpy = vi.fn().mockResolvedValue(jsonResponse(body));
|
|
8
|
+
vi.stubGlobal('fetch', fetchSpy);
|
|
9
|
+
return fetchSpy;
|
|
10
|
+
}
|
|
11
|
+
describe('createServer options', () => {
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.unstubAllEnvs();
|
|
14
|
+
vi.restoreAllMocks();
|
|
15
|
+
});
|
|
16
|
+
it('never reads FIRECRAWL_API_KEY from ambient process env (multi-tenant leak guard)', async () => {
|
|
17
|
+
vi.stubEnv('FIRECRAWL_API_KEY', 'ambient-env-key-must-not-leak');
|
|
18
|
+
const fetchSpy = stubFetch();
|
|
19
|
+
const { client, cleanup } = await connectInMemory();
|
|
20
|
+
const result = toolTextResultSchema.parse(await client.callTool({ name: 'web_search', arguments: { query: 'anything' } }));
|
|
21
|
+
expect(result.isError).toBe(true);
|
|
22
|
+
expect(result.content[0].text).toBe('FIRECRAWL_API_KEY not configured');
|
|
23
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
24
|
+
await cleanup();
|
|
25
|
+
});
|
|
26
|
+
it('uses an explicitly granted Firecrawl key', async () => {
|
|
27
|
+
const fetchSpy = stubFetch(JSON.stringify({ success: true, data: [] }));
|
|
28
|
+
const { client, cleanup } = await connectInMemory({ firecrawlApiKey: 'granted-key' });
|
|
29
|
+
const result = toolTextResultSchema.parse(await client.callTool({ name: 'web_search', arguments: { query: 'opencx' } }));
|
|
30
|
+
expect(result.isError).toBeFalsy();
|
|
31
|
+
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
32
|
+
const call = fetchSpy.mock.calls[0];
|
|
33
|
+
expect(fetchCallUrl(call)).toContain('api.firecrawl.dev');
|
|
34
|
+
expect(fetchCallHeaders(call).get('authorization')).toBe('Bearer granted-key');
|
|
35
|
+
await cleanup();
|
|
36
|
+
});
|
|
37
|
+
it('returns an empty-result string when Firecrawl succeeds without a data field', async () => {
|
|
38
|
+
stubFetch(JSON.stringify({ success: true }));
|
|
39
|
+
const { client, cleanup } = await connectInMemory({ firecrawlApiKey: 'granted-key' });
|
|
40
|
+
const result = toolTextResultSchema.parse(await client.callTool({ name: 'web_search', arguments: { query: 'nothing' } }));
|
|
41
|
+
// `text` must stay a string — JSON.stringify(undefined) would produce a
|
|
42
|
+
// non-string value and fail MCP output validation at the protocol layer.
|
|
43
|
+
expect(result.isError).toBeFalsy();
|
|
44
|
+
expect(result.content[0].text).toBe('[]');
|
|
45
|
+
await cleanup();
|
|
46
|
+
});
|
|
47
|
+
it('sends the coworker session id header when explicitly provided', async () => {
|
|
48
|
+
const fetchSpy = stubFetch(JSON.stringify({ name: 'org', created_at: null, updated_at: null }));
|
|
49
|
+
const { client, cleanup } = await connectInMemory({ coworkerSessionId: 'session-42' });
|
|
50
|
+
await client.callTool({ name: 'get_organization', arguments: {} });
|
|
51
|
+
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
52
|
+
const headers = fetchCallHeaders(fetchSpy.mock.calls[0]);
|
|
53
|
+
expect(headers.get('x-opencx-coworker-session-id')).toBe('session-42');
|
|
54
|
+
await cleanup();
|
|
55
|
+
});
|
|
56
|
+
it('never reads OPENCX_COWORKER_SESSION_ID from ambient process env', async () => {
|
|
57
|
+
vi.stubEnv('OPENCX_COWORKER_SESSION_ID', 'ambient-session-must-not-leak');
|
|
58
|
+
const fetchSpy = stubFetch(JSON.stringify({ name: 'org', created_at: null, updated_at: null }));
|
|
59
|
+
const { client, cleanup } = await connectInMemory();
|
|
60
|
+
await client.callTool({ name: 'get_organization', arguments: {} });
|
|
61
|
+
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
62
|
+
const headers = fetchCallHeaders(fetchSpy.mock.calls[0]);
|
|
63
|
+
expect(headers.get('x-opencx-coworker-session-id')).toBeNull();
|
|
64
|
+
await cleanup();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=create-server-options.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-server-options.spec.js","sourceRoot":"","sources":["../src/create-server-options.spec.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { JsonValue } from './utils/json.js';
|
|
3
|
+
export declare const COWORKER_SESSION_HEADER = "x-opencx-coworker-session-id";
|
|
4
|
+
/** Extract the token from an `Authorization: Bearer <token>` header value. */
|
|
5
|
+
export declare function extractBearer(header: string | string[] | undefined): string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* Handle a single stateless Streamable HTTP MCP request.
|
|
8
|
+
*
|
|
9
|
+
* Creates a fresh server + transport per request (no session state), so it can
|
|
10
|
+
* be mounted behind any Node HTTP server. Both the standalone CLI (`--http`)
|
|
11
|
+
* and the hosted backend endpoint (`api.open.cx/mcp`) route through here.
|
|
12
|
+
*
|
|
13
|
+
* The caller is responsible for authentication — extract and validate the
|
|
14
|
+
* bearer token before calling this, then pass it as `apiKey` so tools call the
|
|
15
|
+
* public API on the caller's behalf. Everything else that is per-request
|
|
16
|
+
* (coworker attribution header, error envelope, teardown) is owned here so
|
|
17
|
+
* hosts cannot drift.
|
|
18
|
+
*/
|
|
19
|
+
export declare function handleStreamableHttp(options: {
|
|
20
|
+
apiKey: string;
|
|
21
|
+
baseUrl: string;
|
|
22
|
+
req: IncomingMessage;
|
|
23
|
+
res: ServerResponse;
|
|
24
|
+
/**
|
|
25
|
+
* Body already parsed by the host framework (e.g. Fastify). Omit when the
|
|
26
|
+
* body is still unread on the request stream.
|
|
27
|
+
*/
|
|
28
|
+
parsedBody?: JsonValue;
|
|
29
|
+
/**
|
|
30
|
+
* Fallback companion attribution when the request carries no
|
|
31
|
+
* `x-opencx-coworker-session-id` header. The header always wins.
|
|
32
|
+
*/
|
|
33
|
+
coworkerSessionId?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Enables the Firecrawl tools for this request. The host decides per caller
|
|
36
|
+
* — never resolved from ambient process env (multi-tenant leak).
|
|
37
|
+
*/
|
|
38
|
+
firecrawlApiKey?: string;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,eAAO,MAAM,uBAAuB,iCAAiC,CAAC;AAEtE,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAIvF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,eAAe,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDhB"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
2
|
+
import { createServer } from './server.js';
|
|
3
|
+
export const COWORKER_SESSION_HEADER = 'x-opencx-coworker-session-id';
|
|
4
|
+
/** Extract the token from an `Authorization: Bearer <token>` header value. */
|
|
5
|
+
export function extractBearer(header) {
|
|
6
|
+
if (typeof header !== 'string')
|
|
7
|
+
return undefined;
|
|
8
|
+
const match = header.match(/^Bearer\s+(.+)$/i);
|
|
9
|
+
return match?.[1];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Handle a single stateless Streamable HTTP MCP request.
|
|
13
|
+
*
|
|
14
|
+
* Creates a fresh server + transport per request (no session state), so it can
|
|
15
|
+
* be mounted behind any Node HTTP server. Both the standalone CLI (`--http`)
|
|
16
|
+
* and the hosted backend endpoint (`api.open.cx/mcp`) route through here.
|
|
17
|
+
*
|
|
18
|
+
* The caller is responsible for authentication — extract and validate the
|
|
19
|
+
* bearer token before calling this, then pass it as `apiKey` so tools call the
|
|
20
|
+
* public API on the caller's behalf. Everything else that is per-request
|
|
21
|
+
* (coworker attribution header, error envelope, teardown) is owned here so
|
|
22
|
+
* hosts cannot drift.
|
|
23
|
+
*/
|
|
24
|
+
export async function handleStreamableHttp(options) {
|
|
25
|
+
const { apiKey, baseUrl, req, res, parsedBody, firecrawlApiKey } = options;
|
|
26
|
+
// Stateless mode has no session to push server-initiated messages from, yet
|
|
27
|
+
// the SDK would answer GET with a 200 SSE stream that idles forever — each
|
|
28
|
+
// connected client would pin a full per-request server in memory. 405 is the
|
|
29
|
+
// spec-sanctioned "no SSE stream here"; SDK clients fall back to POST-only.
|
|
30
|
+
if (req.method === 'GET') {
|
|
31
|
+
res.writeHead(405, { Allow: 'POST, DELETE' });
|
|
32
|
+
res.end();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const coworkerHeader = req.headers[COWORKER_SESSION_HEADER];
|
|
36
|
+
const coworkerSessionId = typeof coworkerHeader === 'string' && coworkerHeader !== ''
|
|
37
|
+
? coworkerHeader
|
|
38
|
+
: options.coworkerSessionId;
|
|
39
|
+
try {
|
|
40
|
+
const server = createServer(apiKey, baseUrl, { coworkerSessionId, firecrawlApiKey });
|
|
41
|
+
const transport = new StreamableHTTPServerTransport({
|
|
42
|
+
sessionIdGenerator: undefined,
|
|
43
|
+
});
|
|
44
|
+
res.on('close', () => {
|
|
45
|
+
transport.close().catch((_e) => {
|
|
46
|
+
console.error('Failed to close MCP HTTP transport', { _e });
|
|
47
|
+
});
|
|
48
|
+
server.close().catch((_e) => {
|
|
49
|
+
console.error('Failed to close MCP server', { _e });
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
await server.connect(transport);
|
|
53
|
+
await transport.handleRequest(req, res, parsedBody);
|
|
54
|
+
}
|
|
55
|
+
catch (_e) {
|
|
56
|
+
console.error('MCP HTTP request failed', { _e });
|
|
57
|
+
if (!res.headersSent && !res.destroyed) {
|
|
58
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
59
|
+
res.end(JSON.stringify({
|
|
60
|
+
jsonrpc: '2.0',
|
|
61
|
+
error: { code: -32603, message: 'Internal server error' },
|
|
62
|
+
id: null,
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
else if (!res.writableEnded) {
|
|
66
|
+
res.end();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.spec.d.ts","sourceRoot":"","sources":["../src/http.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { createServer as createHttpServer } from 'node:http';
|
|
2
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
3
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
4
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { extractBearer, handleStreamableHttp } from './http.js';
|
|
7
|
+
import { toolTextResultSchema } from './spec-helpers.js';
|
|
8
|
+
import { jsonValueSchema } from './utils/json.js';
|
|
9
|
+
const addressSchema = z.object({ port: z.number() });
|
|
10
|
+
const ORG_BODY = { name: 'http-test-org', created_at: null, updated_at: null };
|
|
11
|
+
function listenPort(server) {
|
|
12
|
+
return addressSchema.parse(server.address()).port;
|
|
13
|
+
}
|
|
14
|
+
async function connectClient(url, bearer, extraHeaders = {}) {
|
|
15
|
+
const client = new Client({ name: 'http-spec-client', version: '0.0.0' });
|
|
16
|
+
const transport = new StreamableHTTPClientTransport(new URL(url), {
|
|
17
|
+
requestInit: { headers: { Authorization: `Bearer ${bearer}`, ...extraHeaders } },
|
|
18
|
+
});
|
|
19
|
+
await client.connect(transport);
|
|
20
|
+
return client;
|
|
21
|
+
}
|
|
22
|
+
describe('handleStreamableHttp', () => {
|
|
23
|
+
/** Stub OpenCX public API — records auth + coworker headers of every call. */
|
|
24
|
+
let apiStub;
|
|
25
|
+
let apiBaseUrl;
|
|
26
|
+
const seenApiCalls = [];
|
|
27
|
+
const hosts = [];
|
|
28
|
+
/**
|
|
29
|
+
* Host wiring mirrors index.ts's `--http` mode. The one axis under test per
|
|
30
|
+
* host: whether the body is pre-parsed by the framework (Fastify path) or
|
|
31
|
+
* left on the stream (raw CLI path), plus an optional coworker fallback.
|
|
32
|
+
*/
|
|
33
|
+
async function startHost(options) {
|
|
34
|
+
const host = createHttpServer(async (req, res) => {
|
|
35
|
+
const bearer = extractBearer(req.headers.authorization);
|
|
36
|
+
if (!bearer) {
|
|
37
|
+
res.writeHead(401);
|
|
38
|
+
res.end();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
let parsedBody;
|
|
42
|
+
if (options.parseBody) {
|
|
43
|
+
const chunks = [];
|
|
44
|
+
for await (const chunk of req) {
|
|
45
|
+
chunks.push(Buffer.from(chunk));
|
|
46
|
+
}
|
|
47
|
+
const raw = Buffer.concat(chunks).toString('utf8');
|
|
48
|
+
parsedBody = raw ? jsonValueSchema.parse(JSON.parse(raw)) : undefined;
|
|
49
|
+
}
|
|
50
|
+
await handleStreamableHttp({
|
|
51
|
+
apiKey: bearer,
|
|
52
|
+
baseUrl: apiBaseUrl,
|
|
53
|
+
req,
|
|
54
|
+
res,
|
|
55
|
+
parsedBody,
|
|
56
|
+
coworkerSessionId: options.coworkerSessionId,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
await new Promise((resolve) => host.listen(0, '127.0.0.1', resolve));
|
|
60
|
+
hosts.push(host);
|
|
61
|
+
return `http://127.0.0.1:${listenPort(host)}/`;
|
|
62
|
+
}
|
|
63
|
+
/** Raw request stream host (CLI `--http` path). */
|
|
64
|
+
let streamedUrl;
|
|
65
|
+
/** Framework-parsed body host (backend/Fastify path). */
|
|
66
|
+
let parsedUrl;
|
|
67
|
+
beforeAll(async () => {
|
|
68
|
+
apiStub = createHttpServer((req, res) => {
|
|
69
|
+
const coworkerHeader = req.headers['x-opencx-coworker-session-id'];
|
|
70
|
+
seenApiCalls.push({
|
|
71
|
+
auth: typeof req.headers.authorization === 'string' ? req.headers.authorization : undefined,
|
|
72
|
+
coworkerSessionId: typeof coworkerHeader === 'string' ? coworkerHeader : undefined,
|
|
73
|
+
});
|
|
74
|
+
if (req.method === 'GET' && req.url === '/org') {
|
|
75
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
76
|
+
res.end(JSON.stringify(ORG_BODY));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
80
|
+
res.end(JSON.stringify({ error: 'not found' }));
|
|
81
|
+
});
|
|
82
|
+
await new Promise((resolve) => apiStub.listen(0, '127.0.0.1', resolve));
|
|
83
|
+
apiBaseUrl = `http://127.0.0.1:${listenPort(apiStub)}`;
|
|
84
|
+
streamedUrl = await startHost({});
|
|
85
|
+
parsedUrl = await startHost({ parseBody: true });
|
|
86
|
+
});
|
|
87
|
+
afterAll(async () => {
|
|
88
|
+
for (const server of [...hosts, apiStub]) {
|
|
89
|
+
await new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())));
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
function apiCallsFor(bearer) {
|
|
93
|
+
return seenApiCalls.filter((c) => c.auth === `Bearer ${bearer}`);
|
|
94
|
+
}
|
|
95
|
+
it('serves initialize + tools/list over a raw request stream', async () => {
|
|
96
|
+
const client = await connectClient(streamedUrl, 'stream-key');
|
|
97
|
+
const { tools } = await client.listTools();
|
|
98
|
+
expect(tools.length).toBeGreaterThan(100);
|
|
99
|
+
expect(tools.map((t) => t.name)).toContain('get_organization');
|
|
100
|
+
await client.close();
|
|
101
|
+
});
|
|
102
|
+
it('serves tool calls and forwards the caller bearer to the API', async () => {
|
|
103
|
+
const client = await connectClient(streamedUrl, 'stream-key');
|
|
104
|
+
const result = toolTextResultSchema.parse(await client.callTool({ name: 'get_organization', arguments: {} }));
|
|
105
|
+
expect(JSON.parse(result.content[0].text)).toEqual(ORG_BODY);
|
|
106
|
+
expect(apiCallsFor('stream-key').length).toBeGreaterThan(0);
|
|
107
|
+
await client.close();
|
|
108
|
+
});
|
|
109
|
+
it('serves the same protocol when the host framework pre-parses the body', async () => {
|
|
110
|
+
const client = await connectClient(parsedUrl, 'parsed-key');
|
|
111
|
+
const { tools } = await client.listTools();
|
|
112
|
+
expect(tools.length).toBeGreaterThan(100);
|
|
113
|
+
const result = toolTextResultSchema.parse(await client.callTool({ name: 'get_organization', arguments: {} }));
|
|
114
|
+
expect(JSON.parse(result.content[0].text)).toEqual(ORG_BODY);
|
|
115
|
+
expect(apiCallsFor('parsed-key').length).toBeGreaterThan(0);
|
|
116
|
+
await client.close();
|
|
117
|
+
});
|
|
118
|
+
it('isolates concurrent callers — each request uses its own bearer', async () => {
|
|
119
|
+
const [clientA, clientB] = await Promise.all([
|
|
120
|
+
connectClient(streamedUrl, 'key-a'),
|
|
121
|
+
connectClient(streamedUrl, 'key-b'),
|
|
122
|
+
]);
|
|
123
|
+
const before = seenApiCalls.length;
|
|
124
|
+
await Promise.all([
|
|
125
|
+
clientA.callTool({ name: 'get_organization', arguments: {} }),
|
|
126
|
+
clientB.callTool({ name: 'get_organization', arguments: {} }),
|
|
127
|
+
]);
|
|
128
|
+
const newAuths = seenApiCalls.slice(before).map((c) => c.auth);
|
|
129
|
+
expect(newAuths).toHaveLength(2);
|
|
130
|
+
expect(new Set(newAuths)).toEqual(new Set(['Bearer key-a', 'Bearer key-b']));
|
|
131
|
+
await Promise.all([clientA.close(), clientB.close()]);
|
|
132
|
+
});
|
|
133
|
+
it('forwards the coworker session id header to API calls', async () => {
|
|
134
|
+
const client = await connectClient(streamedUrl, 'coworker-key', {
|
|
135
|
+
'x-opencx-coworker-session-id': 'coworker-session-42',
|
|
136
|
+
});
|
|
137
|
+
await client.callTool({ name: 'get_organization', arguments: {} });
|
|
138
|
+
expect(apiCallsFor('coworker-key')[0]?.coworkerSessionId).toBe('coworker-session-42');
|
|
139
|
+
await client.close();
|
|
140
|
+
});
|
|
141
|
+
it('sends no coworker header when neither caller nor host provides one', async () => {
|
|
142
|
+
const client = await connectClient(streamedUrl, 'no-coworker-key');
|
|
143
|
+
await client.callTool({ name: 'get_organization', arguments: {} });
|
|
144
|
+
const calls = apiCallsFor('no-coworker-key');
|
|
145
|
+
expect(calls.length).toBeGreaterThan(0);
|
|
146
|
+
expect(calls[0]?.coworkerSessionId).toBeUndefined();
|
|
147
|
+
await client.close();
|
|
148
|
+
});
|
|
149
|
+
it('falls back to the host-provided coworker session id when the caller sends no header', async () => {
|
|
150
|
+
const fallbackUrl = await startHost({ coworkerSessionId: 'host-env-session' });
|
|
151
|
+
const client = await connectClient(fallbackUrl, 'fallback-key');
|
|
152
|
+
await client.callTool({ name: 'get_organization', arguments: {} });
|
|
153
|
+
expect(apiCallsFor('fallback-key')[0]?.coworkerSessionId).toBe('host-env-session');
|
|
154
|
+
await client.close();
|
|
155
|
+
});
|
|
156
|
+
it('lets a per-request coworker header win over the host fallback', async () => {
|
|
157
|
+
const fallbackUrl = await startHost({ coworkerSessionId: 'host-env-session' });
|
|
158
|
+
const client = await connectClient(fallbackUrl, 'override-key', {
|
|
159
|
+
'x-opencx-coworker-session-id': 'per-request-session',
|
|
160
|
+
});
|
|
161
|
+
await client.callTool({ name: 'get_organization', arguments: {} });
|
|
162
|
+
expect(apiCallsFor('override-key')[0]?.coworkerSessionId).toBe('per-request-session');
|
|
163
|
+
await client.close();
|
|
164
|
+
});
|
|
165
|
+
it('rejects GET with 405 — stateless mode offers no SSE stream to pin', async () => {
|
|
166
|
+
const response = await fetch(streamedUrl, {
|
|
167
|
+
method: 'GET',
|
|
168
|
+
headers: {
|
|
169
|
+
Authorization: 'Bearer stream-key',
|
|
170
|
+
Accept: 'text/event-stream',
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
expect(response.status).toBe(405);
|
|
174
|
+
expect(response.headers.get('allow')).toBe('POST, DELETE');
|
|
175
|
+
await response.body?.cancel();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
//# sourceMappingURL=http.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.spec.js","sourceRoot":"","sources":["../src/http.spec.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createServer as createHttpServer } from 'node:http';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import {
|
|
4
|
+
import { extractBearer, handleStreamableHttp } from './http.js';
|
|
5
5
|
import { createServer } from './server.js';
|
|
6
6
|
const DEFAULT_BASE_URL = 'https://api.open.cx';
|
|
7
7
|
function isHttpMode() {
|
|
8
8
|
return process.argv.includes('--http') || process.env.MCP_TRANSPORT === 'http';
|
|
9
9
|
}
|
|
10
|
-
function extractBearer(req) {
|
|
11
|
-
const auth = req.headers['authorization'];
|
|
12
|
-
if (typeof auth !== 'string')
|
|
13
|
-
return undefined;
|
|
14
|
-
const match = auth.match(/^Bearer\s+(.+)$/i);
|
|
15
|
-
return match?.[1];
|
|
16
|
-
}
|
|
17
10
|
function startHttpMode() {
|
|
18
11
|
const baseUrl = resolveBaseUrl();
|
|
19
12
|
const port = Number(process.env.MCP_HTTP_PORT) || 3100;
|
|
@@ -30,18 +23,25 @@ function startHttpMode() {
|
|
|
30
23
|
res.end('Not found');
|
|
31
24
|
return;
|
|
32
25
|
}
|
|
33
|
-
const bearer = extractBearer(req);
|
|
26
|
+
const bearer = extractBearer(req.headers.authorization);
|
|
34
27
|
if (!bearer) {
|
|
35
28
|
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
36
29
|
res.end(JSON.stringify({ error: 'Missing Authorization: Bearer <token>' }));
|
|
37
30
|
return;
|
|
38
31
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
// Standalone server = single-tenant: the operator's own env applies to
|
|
33
|
+
// every caller. Do NOT expose this server to untrusted bearer tokens with
|
|
34
|
+
// FIRECRAWL_API_KEY set — all callers would share (and bill) that key.
|
|
35
|
+
// The env coworker session id is a fallback; a per-request
|
|
36
|
+
// x-opencx-coworker-session-id header always wins (handled in http.ts).
|
|
37
|
+
await handleStreamableHttp({
|
|
38
|
+
apiKey: bearer,
|
|
39
|
+
baseUrl,
|
|
40
|
+
req,
|
|
41
|
+
res,
|
|
42
|
+
coworkerSessionId: process.env.OPENCX_COWORKER_SESSION_ID,
|
|
43
|
+
firecrawlApiKey: process.env.FIRECRAWL_API_KEY,
|
|
42
44
|
});
|
|
43
|
-
await mcpServer.connect(transport);
|
|
44
|
-
await transport.handleRequest(req, res);
|
|
45
45
|
});
|
|
46
46
|
httpServer.listen(port, '0.0.0.0', () => {
|
|
47
47
|
console.log(`OpenCX MCP HTTP server listening on http://0.0.0.0:${port}/mcp`);
|
|
@@ -66,7 +66,10 @@ function startStdioMode() {
|
|
|
66
66
|
process.exit(1);
|
|
67
67
|
}
|
|
68
68
|
const baseUrl = resolveBaseUrl();
|
|
69
|
-
const server = createServer(apiKey, baseUrl
|
|
69
|
+
const server = createServer(apiKey, baseUrl, {
|
|
70
|
+
coworkerSessionId: process.env.OPENCX_COWORKER_SESSION_ID,
|
|
71
|
+
firecrawlApiKey: process.env.FIRECRAWL_API_KEY,
|
|
72
|
+
});
|
|
70
73
|
const transport = new StdioServerTransport();
|
|
71
74
|
server.connect(transport).catch((err) => {
|
|
72
75
|
console.error('Failed to start OpenCX MCP server:', err);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
export
|
|
2
|
+
export interface CreateServerOptions {
|
|
3
|
+
/** Forwarded as `x-opencx-coworker-session-id` on every API call (companion attribution). */
|
|
4
|
+
coworkerSessionId?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Enables the Firecrawl web_search/web_scrape tools. Threaded explicitly —
|
|
7
|
+
* never resolved from ambient process env, so a multi-tenant host can grant
|
|
8
|
+
* it per caller (see registerFirecrawlTools).
|
|
9
|
+
*/
|
|
10
|
+
firecrawlApiKey?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function createServer(apiKey: string, baseUrl: string, options?: CreateServerOptions): McpServer;
|
|
3
13
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA0CpE,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA0CpE,MAAM,WAAW,mBAAmB;IAClC,6FAA6F;IAC7F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,mBAAwB,GAChC,SAAS,CAmDX"}
|
package/dist/server.js
CHANGED
|
@@ -39,12 +39,14 @@ import { registerAskQuestionsTools } from './tools/ask-questions.js';
|
|
|
39
39
|
import { registerTodoTools } from './tools/todos.js';
|
|
40
40
|
import { registerOnboardingTools } from './tools/onboarding.js';
|
|
41
41
|
import { registerFirecrawlTools } from './tools/firecrawl.js';
|
|
42
|
-
export function createServer(apiKey, baseUrl) {
|
|
42
|
+
export function createServer(apiKey, baseUrl, options = {}) {
|
|
43
43
|
const server = new McpServer({
|
|
44
44
|
name: 'OpenCX',
|
|
45
45
|
version: '0.1.0',
|
|
46
46
|
});
|
|
47
|
-
const client = createOpenCXClient(apiKey, baseUrl
|
|
47
|
+
const client = createOpenCXClient(apiKey, baseUrl, {
|
|
48
|
+
coworkerSessionId: options.coworkerSessionId,
|
|
49
|
+
});
|
|
48
50
|
registerActionTools(server, client);
|
|
49
51
|
registerAutopilotTools(server, client);
|
|
50
52
|
registerBlocklistTools(server, client);
|
|
@@ -83,7 +85,7 @@ export function createServer(apiKey, baseUrl) {
|
|
|
83
85
|
registerAskQuestionsTools(server, client);
|
|
84
86
|
registerTodoTools(server, client);
|
|
85
87
|
registerOnboardingTools(server, client);
|
|
86
|
-
registerFirecrawlTools(server);
|
|
88
|
+
registerFirecrawlTools(server, { apiKey: options.firecrawlApiKey });
|
|
87
89
|
return server;
|
|
88
90
|
}
|
|
89
91
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,+BAA+B,EAAE,MAAM,gCAAgC,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { type CreateServerOptions } from './server.js';
|
|
4
|
+
/** The MCP text-content envelope produced by `jsonContent()` and `error()`. */
|
|
5
|
+
export declare const toolTextResultSchema: z.ZodObject<{
|
|
6
|
+
content: z.ZodTuple<[z.ZodObject<{
|
|
7
|
+
type: z.ZodLiteral<"text">;
|
|
8
|
+
text: z.ZodString;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
text: string;
|
|
11
|
+
type: "text";
|
|
12
|
+
}, {
|
|
13
|
+
text: string;
|
|
14
|
+
type: "text";
|
|
15
|
+
}>], null>;
|
|
16
|
+
isError: z.ZodOptional<z.ZodBoolean>;
|
|
17
|
+
}, "strip", z.ZodTypeAny, {
|
|
18
|
+
content: [{
|
|
19
|
+
text: string;
|
|
20
|
+
type: "text";
|
|
21
|
+
}];
|
|
22
|
+
isError?: boolean | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
content: [{
|
|
25
|
+
text: string;
|
|
26
|
+
type: "text";
|
|
27
|
+
}];
|
|
28
|
+
isError?: boolean | undefined;
|
|
29
|
+
}>;
|
|
30
|
+
/** One recorded call of a `fetch`-typed mock: `[input, init?]`. */
|
|
31
|
+
export type FetchCall = [input: RequestInfo | URL, init?: RequestInit];
|
|
32
|
+
export declare function fetchCallUrl(call: FetchCall): string;
|
|
33
|
+
export declare function fetchCallHeaders(call: FetchCall): Headers;
|
|
34
|
+
/** Connect an in-memory MCP client to a fresh `createServer` instance. */
|
|
35
|
+
export declare function connectInMemory(options?: CreateServerOptions): Promise<{
|
|
36
|
+
client: Client;
|
|
37
|
+
cleanup: () => Promise<void>;
|
|
38
|
+
}>;
|
|
39
|
+
//# sourceMappingURL=spec-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-helpers.d.ts","sourceRoot":"","sources":["../src/spec-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAErE,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;EAG/B,CAAC;AAEH,mEAAmE;AACnE,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC;AAEvE,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAIpD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAIzD;AAED,0EAA0E;AAC1E,wBAAsB,eAAe,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAaD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { createServer } from './server.js';
|
|
5
|
+
/** The MCP text-content envelope produced by `jsonContent()` and `error()`. */
|
|
6
|
+
export const toolTextResultSchema = z.object({
|
|
7
|
+
content: z.tuple([z.object({ type: z.literal('text'), text: z.string() })]),
|
|
8
|
+
isError: z.boolean().optional(),
|
|
9
|
+
});
|
|
10
|
+
export function fetchCallUrl(call) {
|
|
11
|
+
const [input] = call;
|
|
12
|
+
if (input instanceof Request)
|
|
13
|
+
return input.url;
|
|
14
|
+
return input instanceof URL ? input.href : input;
|
|
15
|
+
}
|
|
16
|
+
export function fetchCallHeaders(call) {
|
|
17
|
+
const [input, init] = call;
|
|
18
|
+
if (input instanceof Request)
|
|
19
|
+
return input.headers;
|
|
20
|
+
return new Headers(init?.headers);
|
|
21
|
+
}
|
|
22
|
+
/** Connect an in-memory MCP client to a fresh `createServer` instance. */
|
|
23
|
+
export async function connectInMemory(options = {}) {
|
|
24
|
+
const server = createServer('test-api-key', 'https://api.test.com', options);
|
|
25
|
+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
26
|
+
await server.connect(serverTransport);
|
|
27
|
+
const client = new Client({ name: 'spec-client', version: '0.0.0' });
|
|
28
|
+
await client.connect(clientTransport);
|
|
29
|
+
return {
|
|
30
|
+
client,
|
|
31
|
+
cleanup: async () => {
|
|
32
|
+
await client.close();
|
|
33
|
+
await server.close();
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=spec-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-helpers.js","sourceRoot":"","sources":["../src/spec-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAA4B,MAAM,aAAa,CAAC;AAErE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3E,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAKH,MAAM,UAAU,YAAY,CAAC,IAAe;IAC1C,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACrB,IAAI,KAAK,YAAY,OAAO;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC;IAC/C,OAAO,KAAK,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAe;IAC9C,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3B,IAAI,KAAK,YAAY,OAAO;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC;IACnD,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,UAA+B,EAAE;IAIrE,MAAM,MAAM,GAAG,YAAY,CAAC,cAAc,EAAE,sBAAsB,EAAE,OAAO,CAAC,CAAC;IAC7E,MAAM,CAAC,eAAe,EAAE,eAAe,CAAC,GAAG,iBAAiB,CAAC,gBAAgB,EAAE,CAAC;IAChF,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACrE,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IACtC,OAAO;QACL,MAAM;QACN,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
export declare function registerFirecrawlTools(server: McpServer
|
|
2
|
+
export declare function registerFirecrawlTools(server: McpServer, options?: {
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
}): void;
|
|
3
5
|
//# sourceMappingURL=firecrawl.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firecrawl.d.ts","sourceRoot":"","sources":["../../src/tools/firecrawl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"firecrawl.d.ts","sourceRoot":"","sources":["../../src/tools/firecrawl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA8DzE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,QAmE1F"}
|
package/dist/tools/firecrawl.js
CHANGED
|
@@ -53,7 +53,13 @@ function formatScrapeResult(data) {
|
|
|
53
53
|
parts.push(data.markdown);
|
|
54
54
|
return parts.join('\n\n') || 'No content extracted';
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
// The key is threaded explicitly (never read from ambient process env here):
|
|
57
|
+
// in a multi-tenant host like the backend /mcp endpoint, the process env holds
|
|
58
|
+
// OUR paid key — reading it ambiently would hand Firecrawl to every caller.
|
|
59
|
+
// Entrypoints decide: the CLI passes its own env, the backend passes the key
|
|
60
|
+
// only for companion (coworker ephemeral token) callers.
|
|
61
|
+
export function registerFirecrawlTools(server, options = {}) {
|
|
62
|
+
const firecrawlApiKey = options.apiKey;
|
|
57
63
|
server.registerTool('web_search', {
|
|
58
64
|
description: `Search the web and get full page content in one call. Returns titles, URLs, descriptions, and optionally full markdown of each result. This is your PRIMARY tool for website research — use it instead of WebFetch. Supports site-scoped queries like "site:example.com FAQ".
|
|
59
65
|
|
|
@@ -75,19 +81,20 @@ Use when: researching a business during onboarding, finding FAQ/pricing/policy p
|
|
|
75
81
|
.describe('Fetch full markdown content of each result (default: true)'),
|
|
76
82
|
},
|
|
77
83
|
}, async ({ query, limit, scrape }) => {
|
|
78
|
-
|
|
79
|
-
if (!apiKey)
|
|
84
|
+
if (!firecrawlApiKey)
|
|
80
85
|
return error('FIRECRAWL_API_KEY not configured');
|
|
81
86
|
const body = { query, limit };
|
|
82
87
|
if (scrape)
|
|
83
88
|
body.scrapeOptions = { formats: ['markdown'] };
|
|
84
|
-
const res = await firecrawlFetch('/search',
|
|
89
|
+
const res = await firecrawlFetch('/search', firecrawlApiKey, body);
|
|
85
90
|
if (!res.ok)
|
|
86
91
|
return error(`Firecrawl API error: ${res.status} ${res.statusText}`);
|
|
87
92
|
const data = firecrawlResponseSchema.parse(await res.json());
|
|
88
93
|
if (!data.success)
|
|
89
94
|
return error(data.error ?? 'Search failed');
|
|
90
|
-
|
|
95
|
+
// `data` is optional in the response schema — stringify(undefined) would
|
|
96
|
+
// yield a non-string `text` and fail MCP output validation.
|
|
97
|
+
return { content: [{ type: 'text', text: JSON.stringify(data.data ?? [], null, 2) }] };
|
|
91
98
|
});
|
|
92
99
|
server.registerTool('web_scrape', {
|
|
93
100
|
description: 'Scrape a single webpage and return clean markdown. Handles JavaScript-rendered pages. Use when you already have the exact URL (e.g. user gave you their homepage). For discovering pages, use web_search instead.',
|
|
@@ -100,10 +107,9 @@ Use when: researching a business during onboarding, finding FAQ/pricing/policy p
|
|
|
100
107
|
.describe('Extract only main content, excluding nav/footer/sidebar'),
|
|
101
108
|
},
|
|
102
109
|
}, async ({ url, only_main_content }) => {
|
|
103
|
-
|
|
104
|
-
if (!apiKey)
|
|
110
|
+
if (!firecrawlApiKey)
|
|
105
111
|
return error('FIRECRAWL_API_KEY not configured');
|
|
106
|
-
const res = await firecrawlFetch('/scrape',
|
|
112
|
+
const res = await firecrawlFetch('/scrape', firecrawlApiKey, {
|
|
107
113
|
url,
|
|
108
114
|
formats: ['markdown'],
|
|
109
115
|
onlyMainContent: only_main_content,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firecrawl.js","sourceRoot":"","sources":["../../src/tools/firecrawl.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAmB,MAAM,kBAAkB,CAAC;AAEpE,MAAM,cAAc,GAAG,8BAA8B,CAAC;AACtD,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAEpC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;IACpB,IAAI,EAAE,eAAe,CAAC,QAAQ,EAAE;IAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC,CAAC;AAEH,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,QAAQ,EAAE,CAAC;SACR,MAAM,CAAC;QACN,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC5B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAClC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KACjC,CAAC;SACD,WAAW,EAAE;SACb,QAAQ,EAAE;CACd,CAAC,CAAC;AAEH,SAAS,KAAK,CAAC,OAAe;IAC5B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,MAAc,EAAE,IAAgB;IAC1E,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,oBAAoB,CAAC,CAAC;IAC3E,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,CAAC,GAAG,cAAc,GAAG,IAAI,EAAE,EAAE;YAC7C,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE;YAClF,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAC1B,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,IAA+C;IACzE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC3B,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,IAAI,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,WAAW;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1D,IAAI,IAAI,CAAC,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QACzD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7C,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,sBAAsB,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAiB;
|
|
1
|
+
{"version":3,"file":"firecrawl.js","sourceRoot":"","sources":["../../src/tools/firecrawl.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opencx/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "MCP server for OpenCX — manage contacts, sessions, training, and more from Claude Desktop, Cursor, or any MCP client.",
|
|
6
6
|
"type": "module",
|
|
@@ -8,6 +8,21 @@
|
|
|
8
8
|
"opencx-mcp": "./dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"main": "./dist/index.js",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./server": {
|
|
17
|
+
"types": "./dist/server.d.ts",
|
|
18
|
+
"default": "./dist/server.js"
|
|
19
|
+
},
|
|
20
|
+
"./http": {
|
|
21
|
+
"types": "./dist/http.d.ts",
|
|
22
|
+
"default": "./dist/http.js"
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
11
26
|
"files": [
|
|
12
27
|
"dist"
|
|
13
28
|
],
|