@dobbyai/mcp-external 1.0.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -14
- package/dist/__tests__/agent-mail-tools.test.d.ts +8 -0
- package/dist/__tests__/agent-mail-tools.test.d.ts.map +1 -0
- package/dist/__tests__/agent-mail-tools.test.js +255 -0
- package/dist/__tests__/agent-mail-tools.test.js.map +1 -0
- package/dist/__tests__/annotations.test.d.ts +8 -0
- package/dist/__tests__/annotations.test.d.ts.map +1 -0
- package/dist/__tests__/annotations.test.js +108 -0
- package/dist/__tests__/annotations.test.js.map +1 -0
- package/dist/__tests__/auth.test.d.ts +7 -0
- package/dist/__tests__/auth.test.d.ts.map +1 -0
- package/dist/__tests__/auth.test.js +161 -0
- package/dist/__tests__/auth.test.js.map +1 -0
- package/dist/__tests__/doctor.test.d.ts +16 -0
- package/dist/__tests__/doctor.test.d.ts.map +1 -0
- package/dist/__tests__/doctor.test.js +258 -0
- package/dist/__tests__/doctor.test.js.map +1 -0
- package/dist/__tests__/external-agents-tools.test.d.ts +11 -0
- package/dist/__tests__/external-agents-tools.test.d.ts.map +1 -0
- package/dist/__tests__/external-agents-tools.test.js +619 -0
- package/dist/__tests__/external-agents-tools.test.js.map +1 -0
- package/dist/__tests__/gateway-bearer.test.d.ts +12 -0
- package/dist/__tests__/gateway-bearer.test.d.ts.map +1 -0
- package/dist/__tests__/gateway-bearer.test.js +112 -0
- package/dist/__tests__/gateway-bearer.test.js.map +1 -0
- package/dist/__tests__/gateway-key-resolver.test.d.ts +10 -0
- package/dist/__tests__/gateway-key-resolver.test.d.ts.map +1 -0
- package/dist/__tests__/gateway-key-resolver.test.js +125 -0
- package/dist/__tests__/gateway-key-resolver.test.js.map +1 -0
- package/dist/__tests__/http-concurrency.test.d.ts +20 -0
- package/dist/__tests__/http-concurrency.test.d.ts.map +1 -0
- package/dist/__tests__/http-concurrency.test.js +254 -0
- package/dist/__tests__/http-concurrency.test.js.map +1 -0
- package/dist/__tests__/integration/protocol.test.d.ts +8 -0
- package/dist/__tests__/integration/protocol.test.d.ts.map +1 -0
- package/dist/__tests__/integration/protocol.test.js +110 -0
- package/dist/__tests__/integration/protocol.test.js.map +1 -0
- package/dist/__tests__/mcp-integrations.test.d.ts +8 -0
- package/dist/__tests__/mcp-integrations.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-integrations.test.js +365 -0
- package/dist/__tests__/mcp-integrations.test.js.map +1 -0
- package/dist/__tests__/memory-graph-tools.test.d.ts +8 -0
- package/dist/__tests__/memory-graph-tools.test.d.ts.map +1 -0
- package/dist/__tests__/memory-graph-tools.test.js +254 -0
- package/dist/__tests__/memory-graph-tools.test.js.map +1 -0
- package/dist/__tests__/registry-dispatch.test.d.ts +11 -0
- package/dist/__tests__/registry-dispatch.test.d.ts.map +1 -0
- package/dist/__tests__/registry-dispatch.test.js +30 -0
- package/dist/__tests__/registry-dispatch.test.js.map +1 -0
- package/dist/__tests__/registry-listing-parity.test.d.ts +2 -0
- package/dist/__tests__/registry-listing-parity.test.d.ts.map +1 -0
- package/dist/__tests__/registry-listing-parity.test.js +89 -0
- package/dist/__tests__/registry-listing-parity.test.js.map +1 -0
- package/dist/__tests__/role-cap.test.d.ts +2 -0
- package/dist/__tests__/role-cap.test.d.ts.map +1 -0
- package/dist/__tests__/role-cap.test.js +113 -0
- package/dist/__tests__/role-cap.test.js.map +1 -0
- package/dist/__tests__/schedule-tools.test.d.ts +8 -0
- package/dist/__tests__/schedule-tools.test.d.ts.map +1 -0
- package/dist/__tests__/schedule-tools.test.js +285 -0
- package/dist/__tests__/schedule-tools.test.js.map +1 -0
- package/dist/__tests__/server-gateway-key.test.d.ts +18 -0
- package/dist/__tests__/server-gateway-key.test.d.ts.map +1 -0
- package/dist/__tests__/server-gateway-key.test.js +148 -0
- package/dist/__tests__/server-gateway-key.test.js.map +1 -0
- package/dist/__tests__/server-user-scoped.test.d.ts +34 -0
- package/dist/__tests__/server-user-scoped.test.d.ts.map +1 -0
- package/dist/__tests__/server-user-scoped.test.js +577 -0
- package/dist/__tests__/server-user-scoped.test.js.map +1 -0
- package/dist/__tests__/task-id-pattern.test.d.ts +12 -0
- package/dist/__tests__/task-id-pattern.test.d.ts.map +1 -0
- package/dist/__tests__/task-id-pattern.test.js +43 -0
- package/dist/__tests__/task-id-pattern.test.js.map +1 -0
- package/dist/__tests__/tiers.test.d.ts +7 -0
- package/dist/__tests__/tiers.test.d.ts.map +1 -0
- package/dist/__tests__/tiers.test.js +199 -0
- package/dist/__tests__/tiers.test.js.map +1 -0
- package/dist/__tests__/tools/hosted-agents.test.d.ts +9 -0
- package/dist/__tests__/tools/hosted-agents.test.d.ts.map +1 -0
- package/dist/__tests__/tools/hosted-agents.test.js +301 -0
- package/dist/__tests__/tools/hosted-agents.test.js.map +1 -0
- package/dist/__tests__/tools/llm-providers.test.d.ts +10 -0
- package/dist/__tests__/tools/llm-providers.test.d.ts.map +1 -0
- package/dist/__tests__/tools/llm-providers.test.js +105 -0
- package/dist/__tests__/tools/llm-providers.test.js.map +1 -0
- package/dist/__tests__/tools/services.test.js +139 -98
- package/dist/__tests__/tools/services.test.js.map +1 -1
- package/dist/__tests__/tools.test.d.ts +7 -0
- package/dist/__tests__/tools.test.d.ts.map +1 -0
- package/dist/__tests__/tools.test.js +152 -0
- package/dist/__tests__/tools.test.js.map +1 -0
- package/dist/__tests__/transport.test.d.ts +7 -0
- package/dist/__tests__/transport.test.d.ts.map +1 -0
- package/dist/__tests__/transport.test.js +397 -0
- package/dist/__tests__/transport.test.js.map +1 -0
- package/dist/__tests__/validate-credentials.test.d.ts +9 -0
- package/dist/__tests__/validate-credentials.test.d.ts.map +1 -0
- package/dist/__tests__/validate-credentials.test.js +172 -0
- package/dist/__tests__/validate-credentials.test.js.map +1 -0
- package/dist/__tests__/web-eval-tools.test.d.ts +8 -0
- package/dist/__tests__/web-eval-tools.test.d.ts.map +1 -0
- package/dist/__tests__/web-eval-tools.test.js +241 -0
- package/dist/__tests__/web-eval-tools.test.js.map +1 -0
- package/dist/__tests__/whoami.test.d.ts +11 -0
- package/dist/__tests__/whoami.test.d.ts.map +1 -0
- package/dist/__tests__/whoami.test.js +194 -0
- package/dist/__tests__/whoami.test.js.map +1 -0
- package/dist/__tests__/workflow-tools.test.d.ts +8 -0
- package/dist/__tests__/workflow-tools.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-tools.test.js +261 -0
- package/dist/__tests__/workflow-tools.test.js.map +1 -0
- package/dist/__tests__/workloads.test.d.ts +9 -0
- package/dist/__tests__/workloads.test.d.ts.map +1 -0
- package/dist/__tests__/workloads.test.js +169 -0
- package/dist/__tests__/workloads.test.js.map +1 -0
- package/dist/auth/oauth-provider.d.ts +34 -0
- package/dist/auth/oauth-provider.d.ts.map +1 -0
- package/dist/auth/oauth-provider.js +75 -0
- package/dist/auth/oauth-provider.js.map +1 -0
- package/dist/auth/token-verifier.d.ts +45 -0
- package/dist/auth/token-verifier.d.ts.map +1 -0
- package/dist/auth/token-verifier.js +160 -0
- package/dist/auth/token-verifier.js.map +1 -0
- package/dist/connectors/claude-desktop.d.ts.map +1 -1
- package/dist/connectors/claude-desktop.js +3 -2
- package/dist/connectors/claude-desktop.js.map +1 -1
- package/dist/connectors/cursor.d.ts.map +1 -1
- package/dist/connectors/cursor.js +3 -2
- package/dist/connectors/cursor.js.map +1 -1
- package/dist/connectors/setup.d.ts.map +1 -1
- package/dist/connectors/setup.js +2 -1
- package/dist/connectors/setup.js.map +1 -1
- package/dist/connectors/vscode.d.ts.map +1 -1
- package/dist/connectors/vscode.js +3 -2
- package/dist/connectors/vscode.js.map +1 -1
- package/dist/connectors/windsurf.d.ts.map +1 -1
- package/dist/connectors/windsurf.js +3 -2
- package/dist/connectors/windsurf.js.map +1 -1
- package/dist/doctor.d.ts +34 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +134 -0
- package/dist/doctor.js.map +1 -0
- package/dist/http-server.d.ts +56 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +455 -0
- package/dist/http-server.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +96 -171
- package/dist/index.js.map +1 -1
- package/dist/lib/consts.d.ts +21 -0
- package/dist/lib/consts.d.ts.map +1 -0
- package/dist/lib/consts.js +30 -0
- package/dist/lib/consts.js.map +1 -0
- package/dist/lib/dobby-client.d.ts +60 -3
- package/dist/lib/dobby-client.d.ts.map +1 -1
- package/dist/lib/dobby-client.js +145 -44
- package/dist/lib/dobby-client.js.map +1 -1
- package/dist/lib/gateway-key-resolver.d.ts +38 -0
- package/dist/lib/gateway-key-resolver.d.ts.map +1 -0
- package/dist/lib/gateway-key-resolver.js +59 -0
- package/dist/lib/gateway-key-resolver.js.map +1 -0
- package/dist/lib/log.d.ts +25 -0
- package/dist/lib/log.d.ts.map +1 -0
- package/dist/lib/log.js +36 -0
- package/dist/lib/log.js.map +1 -0
- package/dist/server.d.ts +175 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1017 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/agent-mail.d.ts +112 -0
- package/dist/tools/agent-mail.d.ts.map +1 -0
- package/dist/tools/agent-mail.js +118 -0
- package/dist/tools/agent-mail.js.map +1 -0
- package/dist/tools/agent-versions.d.ts +93 -0
- package/dist/tools/agent-versions.d.ts.map +1 -0
- package/dist/tools/agent-versions.js +99 -0
- package/dist/tools/agent-versions.js.map +1 -0
- package/dist/tools/approvals.d.ts +24 -34
- package/dist/tools/approvals.d.ts.map +1 -1
- package/dist/tools/approvals.js +24 -81
- package/dist/tools/approvals.js.map +1 -1
- package/dist/tools/claude-code-sync.d.ts +40 -0
- package/dist/tools/claude-code-sync.d.ts.map +1 -0
- package/dist/tools/claude-code-sync.js +52 -0
- package/dist/tools/claude-code-sync.js.map +1 -0
- package/dist/tools/control-plane.d.ts +118 -0
- package/dist/tools/control-plane.d.ts.map +1 -0
- package/dist/tools/control-plane.js +135 -0
- package/dist/tools/control-plane.js.map +1 -0
- package/dist/tools/crew-builder.d.ts +24 -66
- package/dist/tools/crew-builder.d.ts.map +1 -1
- package/dist/tools/crew-builder.js +24 -185
- package/dist/tools/crew-builder.js.map +1 -1
- package/dist/tools/crewai.d.ts +24 -63
- package/dist/tools/crewai.d.ts.map +1 -1
- package/dist/tools/crewai.js +24 -145
- package/dist/tools/crewai.js.map +1 -1
- package/dist/tools/exa.d.ts +126 -0
- package/dist/tools/exa.d.ts.map +1 -0
- package/dist/tools/exa.js +125 -0
- package/dist/tools/exa.js.map +1 -0
- package/dist/tools/external-agents.d.ts +440 -0
- package/dist/tools/external-agents.d.ts.map +1 -0
- package/dist/tools/external-agents.js +386 -0
- package/dist/tools/external-agents.js.map +1 -0
- package/dist/tools/github-mcp.d.ts +228 -0
- package/dist/tools/github-mcp.d.ts.map +1 -0
- package/dist/tools/github-mcp.js +235 -0
- package/dist/tools/github-mcp.js.map +1 -0
- package/dist/tools/governance.d.ts +273 -0
- package/dist/tools/governance.d.ts.map +1 -0
- package/dist/tools/governance.js +286 -0
- package/dist/tools/governance.js.map +1 -0
- package/dist/tools/hosted-agents.d.ts +216 -0
- package/dist/tools/hosted-agents.d.ts.map +1 -0
- package/dist/tools/hosted-agents.js +214 -0
- package/dist/tools/hosted-agents.js.map +1 -0
- package/dist/tools/index.d.ts +6335 -191
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +441 -172
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/jira.d.ts +60 -176
- package/dist/tools/jira.d.ts.map +1 -1
- package/dist/tools/jira.js +60 -365
- package/dist/tools/jira.js.map +1 -1
- package/dist/tools/llm-providers.d.ts +50 -0
- package/dist/tools/llm-providers.d.ts.map +1 -0
- package/dist/tools/llm-providers.js +41 -0
- package/dist/tools/llm-providers.js.map +1 -0
- package/dist/tools/mcp-metrics.d.ts +47 -0
- package/dist/tools/mcp-metrics.d.ts.map +1 -0
- package/dist/tools/mcp-metrics.js +53 -0
- package/dist/tools/mcp-metrics.js.map +1 -0
- package/dist/tools/mcp-tools.d.ts +12 -29
- package/dist/tools/mcp-tools.d.ts.map +1 -1
- package/dist/tools/mcp-tools.js +12 -131
- package/dist/tools/mcp-tools.js.map +1 -1
- package/dist/tools/mem0.d.ts +127 -0
- package/dist/tools/mem0.d.ts.map +1 -0
- package/dist/tools/mem0.js +155 -0
- package/dist/tools/mem0.js.map +1 -0
- package/dist/tools/memory-graph.d.ts +151 -0
- package/dist/tools/memory-graph.d.ts.map +1 -0
- package/dist/tools/memory-graph.js +159 -0
- package/dist/tools/memory-graph.js.map +1 -0
- package/dist/tools/memory-stores.d.ts +141 -0
- package/dist/tools/memory-stores.d.ts.map +1 -0
- package/dist/tools/memory-stores.js +147 -0
- package/dist/tools/memory-stores.js.map +1 -0
- package/dist/tools/messages.d.ts +12 -20
- package/dist/tools/messages.d.ts.map +1 -1
- package/dist/tools/messages.js +12 -49
- package/dist/tools/messages.js.map +1 -1
- package/dist/tools/moltbook.d.ts +328 -0
- package/dist/tools/moltbook.d.ts.map +1 -0
- package/dist/tools/moltbook.js +334 -0
- package/dist/tools/moltbook.js.map +1 -0
- package/dist/tools/monitoring-attribution.d.ts +71 -0
- package/dist/tools/monitoring-attribution.d.ts.map +1 -0
- package/dist/tools/monitoring-attribution.js +74 -0
- package/dist/tools/monitoring-attribution.js.map +1 -0
- package/dist/tools/ops.d.ts +139 -0
- package/dist/tools/ops.d.ts.map +1 -0
- package/dist/tools/ops.js +151 -0
- package/dist/tools/ops.js.map +1 -0
- package/dist/tools/orchestrations.d.ts +109 -0
- package/dist/tools/orchestrations.d.ts.map +1 -0
- package/dist/tools/orchestrations.js +115 -0
- package/dist/tools/orchestrations.js.map +1 -0
- package/dist/tools/outcomes.d.ts +138 -0
- package/dist/tools/outcomes.d.ts.map +1 -0
- package/dist/tools/outcomes.js +136 -0
- package/dist/tools/outcomes.js.map +1 -0
- package/dist/tools/schedules.d.ts +149 -0
- package/dist/tools/schedules.d.ts.map +1 -0
- package/dist/tools/schedules.js +155 -0
- package/dist/tools/schedules.js.map +1 -0
- package/dist/tools/services.d.ts +6 -16
- package/dist/tools/services.d.ts.map +1 -1
- package/dist/tools/services.js +6 -135
- package/dist/tools/services.js.map +1 -1
- package/dist/tools/sessions.d.ts +124 -0
- package/dist/tools/sessions.d.ts.map +1 -0
- package/dist/tools/sessions.js +130 -0
- package/dist/tools/sessions.js.map +1 -0
- package/dist/tools/setup-status.d.ts +29 -0
- package/dist/tools/setup-status.d.ts.map +1 -0
- package/dist/tools/setup-status.js +35 -0
- package/dist/tools/setup-status.js.map +1 -0
- package/dist/tools/tasks.d.ts +44 -56
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +48 -212
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/tiers.d.ts +55 -0
- package/dist/tools/tiers.d.ts.map +1 -0
- package/dist/tools/tiers.js +239 -0
- package/dist/tools/tiers.js.map +1 -0
- package/dist/tools/unified-agents.d.ts +43 -0
- package/dist/tools/unified-agents.d.ts.map +1 -0
- package/dist/tools/unified-agents.js +46 -0
- package/dist/tools/unified-agents.js.map +1 -0
- package/dist/tools/web-eval.d.ts +129 -0
- package/dist/tools/web-eval.d.ts.map +1 -0
- package/dist/tools/web-eval.js +136 -0
- package/dist/tools/web-eval.js.map +1 -0
- package/dist/tools/workflow.d.ts +105 -0
- package/dist/tools/workflow.d.ts.map +1 -0
- package/dist/tools/workflow.js +111 -0
- package/dist/tools/workflow.js.map +1 -0
- package/dist/tools/workloads.d.ts +81 -0
- package/dist/tools/workloads.d.ts.map +1 -0
- package/dist/tools/workloads.js +81 -0
- package/dist/tools/workloads.js.map +1 -0
- package/package.json +13 -7
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DobbyClient.gatewayBearer Tests
|
|
3
|
+
*
|
|
4
|
+
* gatewayBearer() picks the credential for GATEWAY-scoped endpoints (routes
|
|
5
|
+
* guarded by authenticateGatewayRequest hard-require a gk_* key):
|
|
6
|
+
* lazy provider (HTTP mode) > static gatewayKey (STDIO) > apiKey (legacy).
|
|
7
|
+
*
|
|
8
|
+
* Also proves at the helper level that a gateway fetch helper sends the
|
|
9
|
+
* RESOLVED key — not the OAuth session token — when a provider is present.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=gateway-bearer.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-bearer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/gateway-bearer.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DobbyClient.gatewayBearer Tests
|
|
3
|
+
*
|
|
4
|
+
* gatewayBearer() picks the credential for GATEWAY-scoped endpoints (routes
|
|
5
|
+
* guarded by authenticateGatewayRequest hard-require a gk_* key):
|
|
6
|
+
* lazy provider (HTTP mode) > static gatewayKey (STDIO) > apiKey (legacy).
|
|
7
|
+
*
|
|
8
|
+
* Also proves at the helper level that a gateway fetch helper sends the
|
|
9
|
+
* RESOLVED key — not the OAuth session token — when a provider is present.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
12
|
+
import { createRegistry, toMcpResult } from '@dobbyai/capability-core';
|
|
13
|
+
import { DobbyClient } from '../lib/dobby-client.js';
|
|
14
|
+
// ADR-019: the legacy handleCrewAIHealth is gone — dispatch goes through the
|
|
15
|
+
// 'crewai_health' registry capability. Its handler is a verbatim copy (same
|
|
16
|
+
// callGatewayProxy → gatewayBearer() path), so the same real DobbyClient is
|
|
17
|
+
// passed through ctx and the outgoing-header assertions stay unchanged.
|
|
18
|
+
const registry = createRegistry();
|
|
19
|
+
describe('DobbyClient.gatewayBearer', () => {
|
|
20
|
+
const base = {
|
|
21
|
+
apiUrl: 'https://api.test',
|
|
22
|
+
apiKey: 'sk_live_api_key',
|
|
23
|
+
tenantId: 'tenant_1',
|
|
24
|
+
};
|
|
25
|
+
it('prefers the lazy provider over static gatewayKey and apiKey', async () => {
|
|
26
|
+
const client = new DobbyClient({
|
|
27
|
+
...base,
|
|
28
|
+
gatewayKey: 'gk_static_key',
|
|
29
|
+
getGatewayKey: async () => 'gk_tmp_provider_key',
|
|
30
|
+
});
|
|
31
|
+
expect(await client.gatewayBearer()).toBe('gk_tmp_provider_key');
|
|
32
|
+
});
|
|
33
|
+
it('falls back to the static gatewayKey when the provider resolves undefined', async () => {
|
|
34
|
+
const client = new DobbyClient({
|
|
35
|
+
...base,
|
|
36
|
+
gatewayKey: 'gk_static_key',
|
|
37
|
+
getGatewayKey: async () => undefined,
|
|
38
|
+
});
|
|
39
|
+
expect(await client.gatewayBearer()).toBe('gk_static_key');
|
|
40
|
+
});
|
|
41
|
+
it('uses the static gatewayKey when no provider is configured (STDIO)', async () => {
|
|
42
|
+
const client = new DobbyClient({ ...base, gatewayKey: 'gk_static_key' });
|
|
43
|
+
expect(await client.gatewayBearer()).toBe('gk_static_key');
|
|
44
|
+
});
|
|
45
|
+
it('falls back to apiKey when neither provider nor static key is configured', async () => {
|
|
46
|
+
const client = new DobbyClient(base);
|
|
47
|
+
expect(await client.gatewayBearer()).toBe('sk_live_api_key');
|
|
48
|
+
});
|
|
49
|
+
it('invokes a memoized provider only ONCE across two gatewayBearer calls', async () => {
|
|
50
|
+
// Mirrors the per-session memoization in server.ts:
|
|
51
|
+
// sessionGatewayKey ??= resolveSessionGatewayKey(...)
|
|
52
|
+
let sessionGatewayKey;
|
|
53
|
+
const resolve = vi.fn().mockResolvedValue('gk_tmp_once');
|
|
54
|
+
const client = new DobbyClient({
|
|
55
|
+
...base,
|
|
56
|
+
getGatewayKey: () => (sessionGatewayKey ??= resolve()),
|
|
57
|
+
});
|
|
58
|
+
expect(await client.gatewayBearer()).toBe('gk_tmp_once');
|
|
59
|
+
expect(await client.gatewayBearer()).toBe('gk_tmp_once');
|
|
60
|
+
expect(resolve).toHaveBeenCalledTimes(1);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('gateway fetch helpers use gatewayBearer()', () => {
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
vi.unstubAllGlobals();
|
|
66
|
+
});
|
|
67
|
+
it('crewai callGatewayProxy sends the RESOLVED gk_tmp_, not the OAuth session token', async () => {
|
|
68
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
69
|
+
ok: true,
|
|
70
|
+
json: () => Promise.resolve({
|
|
71
|
+
response: { status: 'healthy' },
|
|
72
|
+
connection: { connection_id: 'conn_1', provider_id: 'crewai', status: 'active' },
|
|
73
|
+
latency_ms: 5,
|
|
74
|
+
}),
|
|
75
|
+
});
|
|
76
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
77
|
+
const client = new DobbyClient({
|
|
78
|
+
apiUrl: 'https://api.test',
|
|
79
|
+
apiKey: 'dobby_at_session_token',
|
|
80
|
+
tenantId: 'tenant_1',
|
|
81
|
+
getGatewayKey: async () => 'gk_tmp_resolved',
|
|
82
|
+
});
|
|
83
|
+
const result = toMcpResult(await registry.get('crewai_health').handler({ client }, {}));
|
|
84
|
+
expect(result.isError).toBeUndefined();
|
|
85
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
86
|
+
const [url, init] = fetchMock.mock.calls[0];
|
|
87
|
+
expect(url).toBe('https://api.test/api/v1/gateway/agents/proxy');
|
|
88
|
+
expect(init.headers.Authorization).toBe('Bearer gk_tmp_resolved');
|
|
89
|
+
// The session token must NOT leak into any header of a gateway call
|
|
90
|
+
expect(JSON.stringify(init.headers)).not.toContain('dobby_at_session_token');
|
|
91
|
+
});
|
|
92
|
+
it('crewai callGatewayProxy keeps sending the gk_ apiKey for genuine gateway-key clients', async () => {
|
|
93
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
94
|
+
ok: true,
|
|
95
|
+
json: () => Promise.resolve({
|
|
96
|
+
response: { status: 'healthy' },
|
|
97
|
+
connection: {},
|
|
98
|
+
latency_ms: 3,
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
102
|
+
const client = new DobbyClient({
|
|
103
|
+
apiUrl: 'https://api.test',
|
|
104
|
+
apiKey: 'gk_user_direct',
|
|
105
|
+
tenantId: 'tenant_1',
|
|
106
|
+
});
|
|
107
|
+
await registry.get('crewai_health').handler({ client }, {});
|
|
108
|
+
const [, init] = fetchMock.mock.calls[0];
|
|
109
|
+
expect(init.headers.Authorization).toBe('Bearer gk_user_direct');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=gateway-bearer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-bearer.test.js","sourceRoot":"","sources":["../../src/__tests__/gateway-bearer.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,6EAA6E;AAC7E,4EAA4E;AAC5E,4EAA4E;AAC5E,wEAAwE;AACxE,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;AAElC,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,MAAM,IAAI,GAAG;QACX,MAAM,EAAE,kBAAkB;QAC1B,MAAM,EAAE,iBAAiB;QACzB,QAAQ,EAAE,UAAU;KACrB,CAAC;IAEF,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC;YAC7B,GAAG,IAAI;YACP,UAAU,EAAE,eAAe;YAC3B,aAAa,EAAE,KAAK,IAAI,EAAE,CAAC,qBAAqB;SACjD,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC;YAC7B,GAAG,IAAI;YACP,UAAU,EAAE,eAAe;YAC3B,aAAa,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS;SACrC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,CAAC;QAEzE,MAAM,CAAC,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;QAErC,MAAM,CAAC,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,oDAAoD;QACpD,wDAAwD;QACxD,IAAI,iBAA0D,CAAC;QAC/D,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC;YAC7B,GAAG,IAAI;YACP,aAAa,EAAE,GAAG,EAAE,CAAC,CAAC,iBAAiB,KAAK,OAAO,EAAE,CAAC;SACvD,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzD,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACzD,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,KAAK,IAAI,EAAE;QAC/F,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC;YAC1C,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,GAAG,EAAE,CACT,OAAO,CAAC,OAAO,CAAC;gBACd,QAAQ,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;gBAC/B,UAAU,EAAE,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE;gBAChF,UAAU,EAAE,CAAC;aACd,CAAC;SACL,CAAC,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAElC,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC;YAC7B,MAAM,EAAE,kBAAkB;YAC1B,MAAM,EAAE,wBAAwB;YAChC,QAAQ,EAAE,UAAU;YACpB,aAAa,EAAE,KAAK,IAAI,EAAE,CAAC,iBAAiB;SAC7C,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,WAAW,CACxB,MAAM,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAC7D,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;QAEvC,MAAM,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAClE,oEAAoE;QACpE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAChD,wBAAwB,CACzB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,KAAK,IAAI,EAAE;QACpG,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC;YAC1C,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,GAAG,EAAE,CACT,OAAO,CAAC,OAAO,CAAC;gBACd,QAAQ,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;gBAC/B,UAAU,EAAE,EAAE;gBACd,UAAU,EAAE,CAAC;aACd,CAAC;SACL,CAAC,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAElC,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC;YAC7B,MAAM,EAAE,kBAAkB;YAC1B,MAAM,EAAE,gBAAgB;YACxB,QAAQ,EAAE,UAAU;SACrB,CAAC,CAAC;QAEH,MAAM,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QAE7D,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Gateway Key Resolver Tests
|
|
3
|
+
*
|
|
4
|
+
* resolveSessionGatewayKey exchanges an MCP HTTP session token (dobby_at_* /
|
|
5
|
+
* sk_live_*) for a tenant+user-bound gk_tmp_* gateway key via the web app's
|
|
6
|
+
* internal endpoint. It must fail SOFT (return undefined) on every failure
|
|
7
|
+
* mode, log why exactly once, and never log the session token or the key.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=gateway-key-resolver.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-key-resolver.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/gateway-key-resolver.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Gateway Key Resolver Tests
|
|
3
|
+
*
|
|
4
|
+
* resolveSessionGatewayKey exchanges an MCP HTTP session token (dobby_at_* /
|
|
5
|
+
* sk_live_*) for a tenant+user-bound gk_tmp_* gateway key via the web app's
|
|
6
|
+
* internal endpoint. It must fail SOFT (return undefined) on every failure
|
|
7
|
+
* mode, log why exactly once, and never log the session token or the key.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
10
|
+
import { resolveSessionGatewayKey } from '../lib/gateway-key-resolver.js';
|
|
11
|
+
const API_URL = 'https://web.test';
|
|
12
|
+
const SESSION_TOKEN = 'dobby_at_secret_session_token';
|
|
13
|
+
const RESOLVED_KEY = 'gk_tmp_resolved_key_123';
|
|
14
|
+
const INTERNAL_SECRET = 'internal-secret-for-tests';
|
|
15
|
+
describe('resolveSessionGatewayKey', () => {
|
|
16
|
+
let consoleErrorSpy;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.stubEnv('INTERNAL_SERVICE_SECRET', INTERNAL_SECRET);
|
|
19
|
+
consoleErrorSpy = vi
|
|
20
|
+
.spyOn(console, 'error')
|
|
21
|
+
.mockImplementation(() => { });
|
|
22
|
+
});
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
vi.unstubAllEnvs();
|
|
25
|
+
vi.unstubAllGlobals();
|
|
26
|
+
vi.restoreAllMocks();
|
|
27
|
+
});
|
|
28
|
+
it('returns data.gateway_key on 200 — POST carries the session token as Bearer + the internal secret header', async () => {
|
|
29
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
30
|
+
ok: true,
|
|
31
|
+
json: () => Promise.resolve({
|
|
32
|
+
success: true,
|
|
33
|
+
data: {
|
|
34
|
+
gateway_key: RESOLVED_KEY,
|
|
35
|
+
key_id: 'key_abc',
|
|
36
|
+
expires_at: '2026-07-12T12:00:00Z',
|
|
37
|
+
scopes: ['agents:read'],
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
42
|
+
const key = await resolveSessionGatewayKey({
|
|
43
|
+
apiUrl: API_URL,
|
|
44
|
+
token: SESSION_TOKEN,
|
|
45
|
+
});
|
|
46
|
+
expect(key).toBe(RESOLVED_KEY);
|
|
47
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
48
|
+
const [url, init] = fetchMock.mock.calls[0];
|
|
49
|
+
expect(url).toBe(`${API_URL}/api/v1/internal/mcp/gateway-key`);
|
|
50
|
+
expect(init.method).toBe('POST');
|
|
51
|
+
expect(init.headers['x-internal-secret']).toBe(INTERNAL_SECRET);
|
|
52
|
+
expect(init.headers.Authorization).toBe(`Bearer ${SESSION_TOKEN}`);
|
|
53
|
+
expect(init.headers['Content-Type']).toBe('application/json');
|
|
54
|
+
expect(init.body).toBe('{}');
|
|
55
|
+
// Success path logs nothing
|
|
56
|
+
expect(consoleErrorSpy).not.toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
it('returns undefined on a non-OK response — logs the HTTP status, never the token', async () => {
|
|
59
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
60
|
+
ok: false,
|
|
61
|
+
status: 401,
|
|
62
|
+
json: () => Promise.resolve({ error: 'unauthorized', message: 'bad token' }),
|
|
63
|
+
}));
|
|
64
|
+
const key = await resolveSessionGatewayKey({
|
|
65
|
+
apiUrl: API_URL,
|
|
66
|
+
token: SESSION_TOKEN,
|
|
67
|
+
});
|
|
68
|
+
expect(key).toBeUndefined();
|
|
69
|
+
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
|
|
70
|
+
const logged = consoleErrorSpy.mock.calls[0].join(' ');
|
|
71
|
+
expect(logged).toContain('401');
|
|
72
|
+
expect(logged).not.toContain(SESSION_TOKEN);
|
|
73
|
+
});
|
|
74
|
+
it('returns undefined WITHOUT calling fetch when INTERNAL_SERVICE_SECRET is missing', async () => {
|
|
75
|
+
vi.stubEnv('INTERNAL_SERVICE_SECRET', '');
|
|
76
|
+
const fetchMock = vi.fn();
|
|
77
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
78
|
+
const key = await resolveSessionGatewayKey({
|
|
79
|
+
apiUrl: API_URL,
|
|
80
|
+
token: SESSION_TOKEN,
|
|
81
|
+
});
|
|
82
|
+
expect(key).toBeUndefined();
|
|
83
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
84
|
+
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
|
|
85
|
+
expect(consoleErrorSpy.mock.calls[0].join(' ')).toContain('INTERNAL_SERVICE_SECRET');
|
|
86
|
+
});
|
|
87
|
+
it('returns undefined on a network error (no retries)', async () => {
|
|
88
|
+
const fetchMock = vi.fn().mockRejectedValue(new Error('ECONNREFUSED'));
|
|
89
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
90
|
+
const key = await resolveSessionGatewayKey({
|
|
91
|
+
apiUrl: API_URL,
|
|
92
|
+
token: SESSION_TOKEN,
|
|
93
|
+
});
|
|
94
|
+
expect(key).toBeUndefined();
|
|
95
|
+
expect(fetchMock).toHaveBeenCalledTimes(1); // no retries
|
|
96
|
+
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
|
|
97
|
+
expect(consoleErrorSpy.mock.calls[0].join(' ')).toContain('ECONNREFUSED');
|
|
98
|
+
});
|
|
99
|
+
it('returns undefined when the 200 body has no data.gateway_key (malformed body)', async () => {
|
|
100
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
101
|
+
ok: true,
|
|
102
|
+
json: () => Promise.resolve({ success: true, data: {} }),
|
|
103
|
+
}));
|
|
104
|
+
const key = await resolveSessionGatewayKey({
|
|
105
|
+
apiUrl: API_URL,
|
|
106
|
+
token: SESSION_TOKEN,
|
|
107
|
+
});
|
|
108
|
+
expect(key).toBeUndefined();
|
|
109
|
+
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
|
|
110
|
+
expect(consoleErrorSpy.mock.calls[0].join(' ')).toContain('gateway_key');
|
|
111
|
+
});
|
|
112
|
+
it('returns undefined when the 200 body is not JSON at all', async () => {
|
|
113
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
114
|
+
ok: true,
|
|
115
|
+
json: () => Promise.reject(new Error('Unexpected token < in JSON')),
|
|
116
|
+
}));
|
|
117
|
+
const key = await resolveSessionGatewayKey({
|
|
118
|
+
apiUrl: API_URL,
|
|
119
|
+
token: SESSION_TOKEN,
|
|
120
|
+
});
|
|
121
|
+
expect(key).toBeUndefined();
|
|
122
|
+
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=gateway-key-resolver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-key-resolver.test.js","sourceRoot":"","sources":["../../src/__tests__/gateway-key-resolver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streamable-HTTP concurrency correlation tests
|
|
3
|
+
*
|
|
4
|
+
* Regression for the 2026-07-18 customer incident: a burst of concurrent tool
|
|
5
|
+
* calls on ONE session followed by a retry produced a CROSS-LABELED response —
|
|
6
|
+
* the list_services payload delivered as the get_task result. Server-side
|
|
7
|
+
* mechanism: the SDK correlates JSON-RPC responses to HTTP streams by the
|
|
8
|
+
* CLIENT-chosen request id (`_requestToStreamMapping`) and silently OVERWRITES
|
|
9
|
+
* the mapping when a second POST on the same session reuses an id that is
|
|
10
|
+
* still in flight — the first handler's late response is then written to the
|
|
11
|
+
* SECOND request's SSE stream with a matching id, so the client trusts a
|
|
12
|
+
* payload that belongs to a different tool call. http-server.ts rejects
|
|
13
|
+
* in-flight id reuse with an explicit 400 instead.
|
|
14
|
+
*
|
|
15
|
+
* These tests also pin the SDK-private location of the in-flight map: if an
|
|
16
|
+
* SDK upgrade moves `_requestToStreamMapping`, the guard silently disarms and
|
|
17
|
+
* the "rejected with 400" test here fails loudly.
|
|
18
|
+
*/
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=http-concurrency.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-concurrency.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/http-concurrency.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streamable-HTTP concurrency correlation tests
|
|
3
|
+
*
|
|
4
|
+
* Regression for the 2026-07-18 customer incident: a burst of concurrent tool
|
|
5
|
+
* calls on ONE session followed by a retry produced a CROSS-LABELED response —
|
|
6
|
+
* the list_services payload delivered as the get_task result. Server-side
|
|
7
|
+
* mechanism: the SDK correlates JSON-RPC responses to HTTP streams by the
|
|
8
|
+
* CLIENT-chosen request id (`_requestToStreamMapping`) and silently OVERWRITES
|
|
9
|
+
* the mapping when a second POST on the same session reuses an id that is
|
|
10
|
+
* still in flight — the first handler's late response is then written to the
|
|
11
|
+
* SECOND request's SSE stream with a matching id, so the client trusts a
|
|
12
|
+
* payload that belongs to a different tool call. http-server.ts rejects
|
|
13
|
+
* in-flight id reuse with an explicit 400 instead.
|
|
14
|
+
*
|
|
15
|
+
* These tests also pin the SDK-private location of the in-flight map: if an
|
|
16
|
+
* SDK upgrade moves `_requestToStreamMapping`, the guard silently disarms and
|
|
17
|
+
* the "rejected with 400" test here fails loudly.
|
|
18
|
+
*/
|
|
19
|
+
import { describe, it, expect } from "vitest";
|
|
20
|
+
function deferred() {
|
|
21
|
+
let resolve;
|
|
22
|
+
const promise = new Promise((r) => (resolve = r));
|
|
23
|
+
return { promise, resolve };
|
|
24
|
+
}
|
|
25
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
26
|
+
/**
|
|
27
|
+
* Stub issuer + real HTTP server wired to a minimal MCP server exposing
|
|
28
|
+
* controllable tools: `echo` (optional delay_ms), `slow_tool` (blocks on the
|
|
29
|
+
* test-held gate), `fast_tool` (immediate). Every tool echoes back its own
|
|
30
|
+
* name + the caller's `marker` argument so a response can be traced to the
|
|
31
|
+
* exact request that produced it.
|
|
32
|
+
*/
|
|
33
|
+
async function withToolServer(run) {
|
|
34
|
+
const express = (await import("express")).default;
|
|
35
|
+
const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
|
|
36
|
+
const { ListToolsRequestSchema, CallToolRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
|
|
37
|
+
const { startHttpServer } = await import("../http-server.js");
|
|
38
|
+
const issuer = express();
|
|
39
|
+
issuer.use(express.json());
|
|
40
|
+
issuer.post("/oauth/token/introspect", (_req, res) => {
|
|
41
|
+
res.json({
|
|
42
|
+
active: true,
|
|
43
|
+
user_id: "pu_conc",
|
|
44
|
+
org_id: "org_conc",
|
|
45
|
+
tenant_id: "tenant_conc",
|
|
46
|
+
scope: "mcp:*",
|
|
47
|
+
client_id: "key_conc",
|
|
48
|
+
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
const issuerServer = issuer.listen(0);
|
|
52
|
+
const issuerPort = issuerServer.address().port;
|
|
53
|
+
const gate = deferred();
|
|
54
|
+
const { server, tokenVerifier } = await startHttpServer({
|
|
55
|
+
port: 0,
|
|
56
|
+
issuerUrl: `http://127.0.0.1:${issuerPort}`,
|
|
57
|
+
mcpPublicUrl: "http://127.0.0.1:0",
|
|
58
|
+
createMcpServer: async () => {
|
|
59
|
+
const srv = new Server({ name: "conc-server", version: "0.0.0" }, { capabilities: { tools: {} } });
|
|
60
|
+
srv.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
61
|
+
tools: [
|
|
62
|
+
{
|
|
63
|
+
name: "echo",
|
|
64
|
+
description: "echo",
|
|
65
|
+
inputSchema: { type: "object" },
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "slow_tool",
|
|
69
|
+
description: "blocks until the test releases the gate",
|
|
70
|
+
inputSchema: { type: "object" },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "fast_tool",
|
|
74
|
+
description: "returns immediately",
|
|
75
|
+
inputSchema: { type: "object" },
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
}));
|
|
79
|
+
srv.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
80
|
+
const name = req.params.name;
|
|
81
|
+
const args = (req.params.arguments ?? {});
|
|
82
|
+
if (name === "slow_tool")
|
|
83
|
+
await gate.promise;
|
|
84
|
+
if (name === "echo" && typeof args.delay_ms === "number") {
|
|
85
|
+
await sleep(args.delay_ms);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
content: [
|
|
89
|
+
{
|
|
90
|
+
type: "text",
|
|
91
|
+
text: JSON.stringify({ tool: name, marker: args.marker ?? null }),
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return srv;
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
const mcpPort = server.address().port;
|
|
100
|
+
const mcpUrl = `http://127.0.0.1:${mcpPort}/mcp`;
|
|
101
|
+
const baseHeaders = {
|
|
102
|
+
Authorization: "Bearer sk_live_conc_test",
|
|
103
|
+
"Content-Type": "application/json",
|
|
104
|
+
Accept: "application/json, text/event-stream",
|
|
105
|
+
};
|
|
106
|
+
const initRes = await fetch(mcpUrl, {
|
|
107
|
+
method: "POST",
|
|
108
|
+
headers: baseHeaders,
|
|
109
|
+
body: JSON.stringify({
|
|
110
|
+
jsonrpc: "2.0",
|
|
111
|
+
id: 1,
|
|
112
|
+
method: "initialize",
|
|
113
|
+
params: {
|
|
114
|
+
protocolVersion: "2025-03-26",
|
|
115
|
+
capabilities: {},
|
|
116
|
+
clientInfo: { name: "conc-probe", version: "0" },
|
|
117
|
+
},
|
|
118
|
+
}),
|
|
119
|
+
});
|
|
120
|
+
expect(initRes.status).toBe(200);
|
|
121
|
+
const sessionId = initRes.headers.get("mcp-session-id");
|
|
122
|
+
expect(sessionId).toBeTruthy();
|
|
123
|
+
await initRes.text(); // drain the initialize SSE stream
|
|
124
|
+
try {
|
|
125
|
+
await run({
|
|
126
|
+
mcpUrl,
|
|
127
|
+
headers: { ...baseHeaders, "Mcp-Session-Id": sessionId },
|
|
128
|
+
gate,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
gate.resolve(); // never leave a handler blocked across tests
|
|
133
|
+
tokenVerifier.clearCache();
|
|
134
|
+
server.close();
|
|
135
|
+
issuerServer.close();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Extract the LAST JSON-RPC message from an SSE response body. */
|
|
139
|
+
function parseSse(text) {
|
|
140
|
+
const dataLines = text.split("\n").filter((l) => l.startsWith("data: "));
|
|
141
|
+
expect(dataLines.length).toBeGreaterThan(0);
|
|
142
|
+
return JSON.parse(dataLines[dataLines.length - 1].slice(6));
|
|
143
|
+
}
|
|
144
|
+
function callBody(id, tool, args = {}) {
|
|
145
|
+
return JSON.stringify({
|
|
146
|
+
jsonrpc: "2.0",
|
|
147
|
+
id,
|
|
148
|
+
method: "tools/call",
|
|
149
|
+
params: { name: tool, arguments: args },
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
describe("Streamable-HTTP concurrent request/response correlation", () => {
|
|
153
|
+
it("N concurrent tools/call on ONE session — each response arrives on its own request's stream with matching id AND payload", async () => {
|
|
154
|
+
await withToolServer(async ({ mcpUrl, headers }) => {
|
|
155
|
+
// Inverted delays: the FIRST request finishes LAST, forcing fully
|
|
156
|
+
// out-of-order completion across the shared transport.
|
|
157
|
+
const delays = [250, 200, 150, 100, 0];
|
|
158
|
+
const results = await Promise.all(delays.map((delay_ms, i) => fetch(mcpUrl, {
|
|
159
|
+
method: "POST",
|
|
160
|
+
headers,
|
|
161
|
+
body: callBody(10 + i, "echo", { marker: `m${i}`, delay_ms }),
|
|
162
|
+
}).then(async (res) => ({
|
|
163
|
+
i,
|
|
164
|
+
status: res.status,
|
|
165
|
+
text: await res.text(),
|
|
166
|
+
}))));
|
|
167
|
+
for (const { i, status, text } of results) {
|
|
168
|
+
expect(status).toBe(200);
|
|
169
|
+
const msg = parseSse(text);
|
|
170
|
+
// The JSON-RPC id must match the request THIS stream carried…
|
|
171
|
+
expect(msg.id).toBe(10 + i);
|
|
172
|
+
// …and the payload must come from THIS request's handler invocation.
|
|
173
|
+
const payload = JSON.parse(msg.result.content[0].text);
|
|
174
|
+
expect(payload.tool).toBe("echo");
|
|
175
|
+
expect(payload.marker).toBe(`m${i}`);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
it("reusing an IN-FLIGHT JSON-RPC id on the same session is rejected with 400 — never cross-delivered", async () => {
|
|
180
|
+
await withToolServer(async ({ mcpUrl, headers, gate }) => {
|
|
181
|
+
// First call blocks server-side; fetch resolves once SSE headers arrive,
|
|
182
|
+
// which happens strictly AFTER the SDK registered id→stream — so the
|
|
183
|
+
// collision below is deterministic, no sleeps needed.
|
|
184
|
+
const firstRes = await fetch(mcpUrl, {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers,
|
|
187
|
+
body: callBody(7, "slow_tool", { marker: "original" }),
|
|
188
|
+
});
|
|
189
|
+
expect(firstRes.status).toBe(200);
|
|
190
|
+
const firstTextP = firstRes.text();
|
|
191
|
+
// A second POST reusing the in-flight id — the retry-after-reconnect
|
|
192
|
+
// shape from the 2026-07-18 incident. Must be rejected loudly, not
|
|
193
|
+
// spliced into the first request's correlation slot.
|
|
194
|
+
const second = await fetch(mcpUrl, {
|
|
195
|
+
method: "POST",
|
|
196
|
+
headers,
|
|
197
|
+
body: callBody(7, "fast_tool", { marker: "retry" }),
|
|
198
|
+
});
|
|
199
|
+
expect(second.status).toBe(400);
|
|
200
|
+
const err = (await second.json());
|
|
201
|
+
expect(err.error.message).toContain("already in flight");
|
|
202
|
+
// The original request still completes normally with ITS OWN payload.
|
|
203
|
+
gate.resolve();
|
|
204
|
+
const msg = parseSse(await firstTextP);
|
|
205
|
+
expect(msg.id).toBe(7);
|
|
206
|
+
const payload = JSON.parse(msg.result.content[0].text);
|
|
207
|
+
expect(payload.tool).toBe("slow_tool");
|
|
208
|
+
expect(payload.marker).toBe("original");
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
it("reusing an id AFTER its request completed is allowed (mapping is cleaned on completion)", async () => {
|
|
212
|
+
// Deliberate minimal guard: only the dangerous in-flight subset is
|
|
213
|
+
// rejected, so clients that reset id counters between turns keep working.
|
|
214
|
+
await withToolServer(async ({ mcpUrl, headers }) => {
|
|
215
|
+
for (const marker of ["first", "second"]) {
|
|
216
|
+
const res = await fetch(mcpUrl, {
|
|
217
|
+
method: "POST",
|
|
218
|
+
headers,
|
|
219
|
+
body: callBody(42, "fast_tool", { marker }),
|
|
220
|
+
});
|
|
221
|
+
expect(res.status).toBe(200);
|
|
222
|
+
const msg = parseSse(await res.text());
|
|
223
|
+
expect(msg.id).toBe(42);
|
|
224
|
+
expect(JSON.parse(msg.result.content[0].text).marker).toBe(marker);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
it("duplicate ids WITHIN one batch POST are rejected with 400", async () => {
|
|
229
|
+
await withToolServer(async ({ mcpUrl, headers }) => {
|
|
230
|
+
const res = await fetch(mcpUrl, {
|
|
231
|
+
method: "POST",
|
|
232
|
+
headers,
|
|
233
|
+
body: JSON.stringify([
|
|
234
|
+
{
|
|
235
|
+
jsonrpc: "2.0",
|
|
236
|
+
id: 9,
|
|
237
|
+
method: "tools/call",
|
|
238
|
+
params: { name: "fast_tool", arguments: { marker: "a" } },
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
jsonrpc: "2.0",
|
|
242
|
+
id: 9,
|
|
243
|
+
method: "tools/call",
|
|
244
|
+
params: { name: "fast_tool", arguments: { marker: "b" } },
|
|
245
|
+
},
|
|
246
|
+
]),
|
|
247
|
+
});
|
|
248
|
+
expect(res.status).toBe(400);
|
|
249
|
+
const err = (await res.json());
|
|
250
|
+
expect(err.error.message).toContain("already in flight");
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
//# sourceMappingURL=http-concurrency.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-concurrency.test.js","sourceRoot":"","sources":["../../src/__tests__/http-concurrency.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/protocol.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|