palaryn 0.1.0 → 0.3.2
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 +243 -588
- package/dist/sdk/typescript/src/client.js +2 -2
- package/dist/sdk/typescript/src/client.js.map +1 -1
- package/dist/src/anomaly/detector.d.ts +7 -4
- package/dist/src/anomaly/detector.d.ts.map +1 -1
- package/dist/src/anomaly/detector.js +22 -12
- package/dist/src/anomaly/detector.js.map +1 -1
- package/dist/src/audit/logger.d.ts +10 -0
- package/dist/src/audit/logger.d.ts.map +1 -1
- package/dist/src/audit/logger.js +52 -38
- package/dist/src/audit/logger.js.map +1 -1
- package/dist/src/auth/routes.d.ts.map +1 -1
- package/dist/src/auth/routes.js +35 -0
- package/dist/src/auth/routes.js.map +1 -1
- package/dist/src/budget/manager.d.ts +5 -0
- package/dist/src/budget/manager.d.ts.map +1 -1
- package/dist/src/budget/manager.js +32 -0
- package/dist/src/budget/manager.js.map +1 -1
- package/dist/src/budget/model-pricing.d.ts +20 -0
- package/dist/src/budget/model-pricing.d.ts.map +1 -0
- package/dist/src/budget/model-pricing.js +107 -0
- package/dist/src/budget/model-pricing.js.map +1 -0
- package/dist/src/budget/usage-extractor.d.ts +3 -1
- package/dist/src/budget/usage-extractor.d.ts.map +1 -1
- package/dist/src/budget/usage-extractor.js +47 -3
- package/dist/src/budget/usage-extractor.js.map +1 -1
- package/dist/src/config/defaults.d.ts.map +1 -1
- package/dist/src/config/defaults.js +65 -13
- package/dist/src/config/defaults.js.map +1 -1
- package/dist/src/dlp/tool-patterns.d.ts +7 -0
- package/dist/src/dlp/tool-patterns.d.ts.map +1 -0
- package/dist/src/dlp/tool-patterns.js +34 -0
- package/dist/src/dlp/tool-patterns.js.map +1 -0
- package/dist/src/executor/filesystem-executor.d.ts +28 -0
- package/dist/src/executor/filesystem-executor.d.ts.map +1 -0
- package/dist/src/executor/filesystem-executor.js +192 -0
- package/dist/src/executor/filesystem-executor.js.map +1 -0
- package/dist/src/executor/http-executor.d.ts.map +1 -1
- package/dist/src/executor/http-executor.js +22 -2
- package/dist/src/executor/http-executor.js.map +1 -1
- package/dist/src/executor/index.d.ts +4 -0
- package/dist/src/executor/index.d.ts.map +1 -1
- package/dist/src/executor/index.js +9 -1
- package/dist/src/executor/index.js.map +1 -1
- package/dist/src/executor/shell-executor.d.ts +22 -0
- package/dist/src/executor/shell-executor.d.ts.map +1 -0
- package/dist/src/executor/shell-executor.js +119 -0
- package/dist/src/executor/shell-executor.js.map +1 -0
- package/dist/src/executor/sql-executor.d.ts +29 -0
- package/dist/src/executor/sql-executor.d.ts.map +1 -0
- package/dist/src/executor/sql-executor.js +114 -0
- package/dist/src/executor/sql-executor.js.map +1 -0
- package/dist/src/executor/websocket-executor.d.ts +26 -0
- package/dist/src/executor/websocket-executor.d.ts.map +1 -0
- package/dist/src/executor/websocket-executor.js +205 -0
- package/dist/src/executor/websocket-executor.js.map +1 -0
- package/dist/src/interceptor/index.d.ts +2 -0
- package/dist/src/interceptor/index.d.ts.map +1 -0
- package/dist/src/interceptor/index.js +6 -0
- package/dist/src/interceptor/index.js.map +1 -0
- package/dist/src/interceptor/provider-interceptor.d.ts +36 -0
- package/dist/src/interceptor/provider-interceptor.d.ts.map +1 -0
- package/dist/src/interceptor/provider-interceptor.js +302 -0
- package/dist/src/interceptor/provider-interceptor.js.map +1 -0
- package/dist/src/mcp/auth-verifier.d.ts.map +1 -1
- package/dist/src/mcp/auth-verifier.js +3 -2
- package/dist/src/mcp/auth-verifier.js.map +1 -1
- package/dist/src/mcp/bridge.d.ts +14 -10
- package/dist/src/mcp/bridge.d.ts.map +1 -1
- package/dist/src/mcp/bridge.js +51 -227
- package/dist/src/mcp/bridge.js.map +1 -1
- package/dist/src/mcp/http-transport.d.ts +2 -0
- package/dist/src/mcp/http-transport.d.ts.map +1 -1
- package/dist/src/mcp/http-transport.js +117 -66
- package/dist/src/mcp/http-transport.js.map +1 -1
- package/dist/src/mcp/internal-auth.d.ts +13 -0
- package/dist/src/mcp/internal-auth.d.ts.map +1 -0
- package/dist/src/mcp/internal-auth.js +12 -0
- package/dist/src/mcp/internal-auth.js.map +1 -0
- package/dist/src/mcp/tool-definitions.d.ts +41 -0
- package/dist/src/mcp/tool-definitions.d.ts.map +1 -0
- package/dist/src/mcp/tool-definitions.js +491 -0
- package/dist/src/mcp/tool-definitions.js.map +1 -0
- package/dist/src/middleware/auth.js.map +1 -1
- package/dist/src/middleware/session.js.map +1 -1
- package/dist/src/middleware/validate.d.ts +8 -0
- package/dist/src/middleware/validate.d.ts.map +1 -1
- package/dist/src/middleware/validate.js +45 -0
- package/dist/src/middleware/validate.js.map +1 -1
- package/dist/src/policy/engine.d.ts +4 -0
- package/dist/src/policy/engine.d.ts.map +1 -1
- package/dist/src/policy/engine.js +117 -0
- package/dist/src/policy/engine.js.map +1 -1
- package/dist/src/saas/routes.d.ts.map +1 -1
- package/dist/src/saas/routes.js +355 -22
- package/dist/src/saas/routes.js.map +1 -1
- package/dist/src/server/app.d.ts.map +1 -1
- package/dist/src/server/app.js +24 -3
- package/dist/src/server/app.js.map +1 -1
- package/dist/src/server/gateway.d.ts.map +1 -1
- package/dist/src/server/gateway.js +17 -0
- package/dist/src/server/gateway.js.map +1 -1
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +18 -0
- package/dist/src/server/index.js.map +1 -1
- package/dist/src/storage/interfaces.d.ts +14 -3
- package/dist/src/storage/interfaces.d.ts.map +1 -1
- package/dist/src/storage/memory.d.ts +2 -0
- package/dist/src/storage/memory.d.ts.map +1 -1
- package/dist/src/storage/memory.js +6 -0
- package/dist/src/storage/memory.js.map +1 -1
- package/dist/src/storage/postgres.d.ts +5 -0
- package/dist/src/storage/postgres.d.ts.map +1 -1
- package/dist/src/storage/postgres.js +16 -0
- package/dist/src/storage/postgres.js.map +1 -1
- package/dist/src/storage/redis.d.ts +10 -0
- package/dist/src/storage/redis.d.ts.map +1 -1
- package/dist/src/storage/redis.js +65 -0
- package/dist/src/storage/redis.js.map +1 -1
- package/dist/src/types/budget.d.ts +4 -0
- package/dist/src/types/budget.d.ts.map +1 -1
- package/dist/src/types/config.d.ts +58 -0
- package/dist/src/types/config.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +1 -0
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/policy.d.ts +11 -1
- package/dist/src/types/policy.d.ts.map +1 -1
- package/dist/src/types/tool-result.d.ts +11 -0
- package/dist/src/types/tool-result.d.ts.map +1 -1
- package/dist/tests/unit/app-routes.test.d.ts +2 -0
- package/dist/tests/unit/app-routes.test.d.ts.map +1 -0
- package/dist/tests/unit/app-routes.test.js +715 -0
- package/dist/tests/unit/app-routes.test.js.map +1 -0
- package/dist/tests/unit/audit-logger.test.js +105 -0
- package/dist/tests/unit/audit-logger.test.js.map +1 -1
- package/dist/tests/unit/auth-providers.test.d.ts +2 -0
- package/dist/tests/unit/auth-providers.test.d.ts.map +1 -0
- package/dist/tests/unit/auth-providers.test.js +279 -0
- package/dist/tests/unit/auth-providers.test.js.map +1 -0
- package/dist/tests/unit/auth-routes-extended.test.d.ts +2 -0
- package/dist/tests/unit/auth-routes-extended.test.d.ts.map +1 -0
- package/dist/tests/unit/auth-routes-extended.test.js +993 -0
- package/dist/tests/unit/auth-routes-extended.test.js.map +1 -0
- package/dist/tests/unit/auth-verifier.test.d.ts +2 -0
- package/dist/tests/unit/auth-verifier.test.d.ts.map +1 -0
- package/dist/tests/unit/auth-verifier.test.js +505 -0
- package/dist/tests/unit/auth-verifier.test.js.map +1 -0
- package/dist/tests/unit/billing-routes.test.d.ts +2 -0
- package/dist/tests/unit/billing-routes.test.d.ts.map +1 -0
- package/dist/tests/unit/billing-routes.test.js +432 -0
- package/dist/tests/unit/billing-routes.test.js.map +1 -0
- package/dist/tests/unit/config-defaults.test.d.ts +2 -0
- package/dist/tests/unit/config-defaults.test.d.ts.map +1 -0
- package/dist/tests/unit/config-defaults.test.js +119 -0
- package/dist/tests/unit/config-defaults.test.js.map +1 -0
- package/dist/tests/unit/defaults.test.js +0 -10
- package/dist/tests/unit/defaults.test.js.map +1 -1
- package/dist/tests/unit/filesystem-executor.test.d.ts +2 -0
- package/dist/tests/unit/filesystem-executor.test.d.ts.map +1 -0
- package/dist/tests/unit/filesystem-executor.test.js +280 -0
- package/dist/tests/unit/filesystem-executor.test.js.map +1 -0
- package/dist/tests/unit/gateway-branches.test.d.ts +2 -0
- package/dist/tests/unit/gateway-branches.test.d.ts.map +1 -0
- package/dist/tests/unit/gateway-branches.test.js +1039 -0
- package/dist/tests/unit/gateway-branches.test.js.map +1 -0
- package/dist/tests/unit/http-executor-branches.test.d.ts +2 -0
- package/dist/tests/unit/http-executor-branches.test.d.ts.map +1 -0
- package/dist/tests/unit/http-executor-branches.test.js +495 -0
- package/dist/tests/unit/http-executor-branches.test.js.map +1 -0
- package/dist/tests/unit/logger.test.d.ts +2 -0
- package/dist/tests/unit/logger.test.d.ts.map +1 -0
- package/dist/tests/unit/logger.test.js +97 -0
- package/dist/tests/unit/logger.test.js.map +1 -0
- package/dist/tests/unit/mcp-internal-auth.test.d.ts +2 -0
- package/dist/tests/unit/mcp-internal-auth.test.d.ts.map +1 -0
- package/dist/tests/unit/mcp-internal-auth.test.js +445 -0
- package/dist/tests/unit/mcp-internal-auth.test.js.map +1 -0
- package/dist/tests/unit/metrics.test.js +102 -0
- package/dist/tests/unit/metrics.test.js.map +1 -1
- package/dist/tests/unit/model-pricing.test.d.ts +2 -0
- package/dist/tests/unit/model-pricing.test.d.ts.map +1 -0
- package/dist/tests/unit/model-pricing.test.js +87 -0
- package/dist/tests/unit/model-pricing.test.js.map +1 -0
- package/dist/tests/unit/oauth-stores.test.d.ts +2 -0
- package/dist/tests/unit/oauth-stores.test.d.ts.map +1 -0
- package/dist/tests/unit/oauth-stores.test.js +260 -0
- package/dist/tests/unit/oauth-stores.test.js.map +1 -0
- package/dist/tests/unit/policy-engine.test.js +466 -0
- package/dist/tests/unit/policy-engine.test.js.map +1 -1
- package/dist/tests/unit/provider-interceptor.test.d.ts +2 -0
- package/dist/tests/unit/provider-interceptor.test.d.ts.map +1 -0
- package/dist/tests/unit/provider-interceptor.test.js +472 -0
- package/dist/tests/unit/provider-interceptor.test.js.map +1 -0
- package/dist/tests/unit/saas-routes-branches.test.d.ts +2 -0
- package/dist/tests/unit/saas-routes-branches.test.d.ts.map +1 -0
- package/dist/tests/unit/saas-routes-branches.test.js +2165 -0
- package/dist/tests/unit/saas-routes-branches.test.js.map +1 -0
- package/dist/tests/unit/saas-routes-crud.test.d.ts +2 -0
- package/dist/tests/unit/saas-routes-crud.test.d.ts.map +1 -0
- package/dist/tests/unit/saas-routes-crud.test.js +332 -0
- package/dist/tests/unit/saas-routes-crud.test.js.map +1 -0
- package/dist/tests/unit/saas-routes-data.test.d.ts +2 -0
- package/dist/tests/unit/saas-routes-data.test.d.ts.map +1 -0
- package/dist/tests/unit/saas-routes-data.test.js +405 -0
- package/dist/tests/unit/saas-routes-data.test.js.map +1 -0
- package/dist/tests/unit/saas-routes.test.js +3 -3
- package/dist/tests/unit/saas-routes.test.js.map +1 -1
- package/dist/tests/unit/shell-executor.test.d.ts +2 -0
- package/dist/tests/unit/shell-executor.test.d.ts.map +1 -0
- package/dist/tests/unit/shell-executor.test.js +145 -0
- package/dist/tests/unit/shell-executor.test.js.map +1 -0
- package/dist/tests/unit/sql-executor.test.d.ts +2 -0
- package/dist/tests/unit/sql-executor.test.d.ts.map +1 -0
- package/dist/tests/unit/sql-executor.test.js +177 -0
- package/dist/tests/unit/sql-executor.test.js.map +1 -0
- package/dist/tests/unit/stream-proxy.test.d.ts +2 -0
- package/dist/tests/unit/stream-proxy.test.d.ts.map +1 -0
- package/dist/tests/unit/stream-proxy.test.js +147 -0
- package/dist/tests/unit/stream-proxy.test.js.map +1 -0
- package/dist/tests/unit/tool-definitions.test.d.ts +2 -0
- package/dist/tests/unit/tool-definitions.test.d.ts.map +1 -0
- package/dist/tests/unit/tool-definitions.test.js +184 -0
- package/dist/tests/unit/tool-definitions.test.js.map +1 -0
- package/dist/tests/unit/usage-extractor.test.js +140 -0
- package/dist/tests/unit/usage-extractor.test.js.map +1 -1
- package/dist/tests/unit/webhook-handler.test.d.ts +2 -0
- package/dist/tests/unit/webhook-handler.test.d.ts.map +1 -0
- package/dist/tests/unit/webhook-handler.test.js +453 -0
- package/dist/tests/unit/webhook-handler.test.js.map +1 -0
- package/dist/tests/unit/webhook-routes.test.d.ts +2 -0
- package/dist/tests/unit/webhook-routes.test.d.ts.map +1 -0
- package/dist/tests/unit/webhook-routes.test.js +69 -0
- package/dist/tests/unit/webhook-routes.test.js.map +1 -0
- package/dist/tests/unit/websocket-executor.test.d.ts +2 -0
- package/dist/tests/unit/websocket-executor.test.d.ts.map +1 -0
- package/dist/tests/unit/websocket-executor.test.js +121 -0
- package/dist/tests/unit/websocket-executor.test.js.map +1 -0
- package/package.json +8 -2
- package/policy-packs/demo_fail.yaml +41 -0
- package/policy-packs/full_tools.yaml +136 -0
- package/src/admin/index.ts +1 -0
- package/src/admin/routes.ts +509 -0
- package/src/admin/templates.ts +572 -0
- package/src/anomaly/detector.ts +730 -0
- package/src/anomaly/index.ts +1 -0
- package/src/approval/manager.ts +569 -0
- package/src/approval/webhook.ts +133 -0
- package/src/audit/logger.ts +490 -0
- package/src/auth/index.ts +5 -0
- package/src/auth/password.ts +21 -0
- package/src/auth/pkce.ts +22 -0
- package/src/auth/providers.ts +208 -0
- package/src/auth/routes.ts +561 -0
- package/src/auth/session.ts +84 -0
- package/src/billing/index.ts +6 -0
- package/src/billing/plan-enforcer.ts +135 -0
- package/src/billing/routes.ts +229 -0
- package/src/billing/stripe-client.ts +58 -0
- package/src/billing/webhook-handler.ts +182 -0
- package/src/billing/webhook-routes.ts +28 -0
- package/src/budget/manager.ts +679 -0
- package/src/budget/model-pricing.ts +119 -0
- package/src/budget/usage-extractor.ts +214 -0
- package/src/cli.ts +91 -0
- package/src/config/defaults.ts +261 -0
- package/src/config/validate.ts +88 -0
- package/src/dlp/composite-scanner.ts +213 -0
- package/src/dlp/index.ts +9 -0
- package/src/dlp/interfaces.ts +34 -0
- package/src/dlp/patterns.ts +30 -0
- package/src/dlp/prompt-injection-backend.ts +181 -0
- package/src/dlp/prompt-injection-patterns.ts +302 -0
- package/src/dlp/regex-backend.ts +181 -0
- package/src/dlp/scanner.ts +502 -0
- package/src/dlp/text-normalizer.ts +225 -0
- package/src/dlp/tool-patterns.ts +35 -0
- package/src/dlp/trufflehog-backend.ts +190 -0
- package/src/executor/filesystem-executor.ts +196 -0
- package/src/executor/http-executor.ts +349 -0
- package/src/executor/index.ts +9 -0
- package/src/executor/interfaces.ts +11 -0
- package/src/executor/noop-executor.ts +23 -0
- package/src/executor/registry.ts +64 -0
- package/src/executor/shell-executor.ts +148 -0
- package/src/executor/slack-executor.ts +176 -0
- package/src/executor/sql-executor.ts +146 -0
- package/src/executor/websocket-executor.ts +211 -0
- package/src/index.ts +24 -0
- package/src/interceptor/index.ts +1 -0
- package/src/interceptor/provider-interceptor.ts +315 -0
- package/src/mcp/auth-verifier.ts +152 -0
- package/src/mcp/bridge.ts +703 -0
- package/src/mcp/http-transport.ts +698 -0
- package/src/mcp/index.ts +9 -0
- package/src/mcp/internal-auth.ts +14 -0
- package/src/mcp/oauth-pages.ts +139 -0
- package/src/mcp/oauth-postgres-stores.ts +278 -0
- package/src/mcp/oauth-provider.ts +536 -0
- package/src/mcp/oauth-stores.ts +202 -0
- package/src/mcp/server.ts +55 -0
- package/src/mcp/tool-definitions.ts +562 -0
- package/src/metrics/collector.ts +357 -0
- package/src/metrics/index.ts +1 -0
- package/src/middleware/auth.ts +814 -0
- package/src/middleware/session.ts +85 -0
- package/src/middleware/validate.ts +130 -0
- package/src/policy/engine.ts +815 -0
- package/src/policy/index.ts +2 -0
- package/src/policy/opa-engine.ts +829 -0
- package/src/proxy/forward-proxy.ts +649 -0
- package/src/proxy/index.ts +1 -0
- package/src/ratelimit/limiter.ts +196 -0
- package/src/replay/engine.ts +142 -0
- package/src/replay/index.ts +1 -0
- package/src/saas/index.ts +1 -0
- package/src/saas/routes.ts +2178 -0
- package/src/server/app.ts +985 -0
- package/src/server/errors.ts +49 -0
- package/src/server/gateway.ts +1130 -0
- package/src/server/index.ts +307 -0
- package/src/server/logger.ts +255 -0
- package/src/server/stream-proxy.ts +202 -0
- package/src/storage/file-persistence.ts +315 -0
- package/src/storage/index.ts +4 -0
- package/src/storage/interfaces.ts +287 -0
- package/src/storage/memory.ts +686 -0
- package/src/storage/postgres.ts +1831 -0
- package/src/storage/redis.ts +835 -0
- package/src/tracing/index.ts +1 -0
- package/src/tracing/provider.ts +100 -0
- package/src/trust/calculator.ts +141 -0
- package/src/trust/index.ts +7 -0
- package/src/types/budget.ts +36 -0
- package/src/types/config.ts +278 -0
- package/src/types/events.ts +41 -0
- package/src/types/express.d.ts +14 -0
- package/src/types/index.ts +7 -0
- package/src/types/policy.ts +83 -0
- package/src/types/stripe-config.ts +11 -0
- package/src/types/subscription.ts +59 -0
- package/src/types/tool-call.ts +47 -0
- package/src/types/tool-result.ts +82 -0
- package/src/types/user.ts +125 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
import { Readable, Writable } from 'stream';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { Gateway } from '../server/gateway';
|
|
4
|
+
import { ToolCall, ToolCallArgs, Actor, Source, ToolInfo } from '../types/tool-call';
|
|
5
|
+
import { ToolResult } from '../types/tool-result';
|
|
6
|
+
import { GatewayConfig } from '../types/config';
|
|
7
|
+
import { DEFAULT_CONFIG } from '../config/defaults';
|
|
8
|
+
import {
|
|
9
|
+
MCPToolHandler,
|
|
10
|
+
MCPToolDefinition,
|
|
11
|
+
HTTP_TOOLS,
|
|
12
|
+
FILE_TOOLS,
|
|
13
|
+
SQL_TOOLS,
|
|
14
|
+
SHELL_TOOLS,
|
|
15
|
+
validateUrlArg,
|
|
16
|
+
} from './tool-definitions';
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// JSON-RPC 2.0 types for the MCP protocol
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
interface JSONRPCRequest {
|
|
23
|
+
jsonrpc: '2.0';
|
|
24
|
+
id: string | number;
|
|
25
|
+
method: string;
|
|
26
|
+
params?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface JSONRPCNotification {
|
|
30
|
+
jsonrpc: '2.0';
|
|
31
|
+
method: string;
|
|
32
|
+
params?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface JSONRPCResponse {
|
|
36
|
+
jsonrpc: '2.0';
|
|
37
|
+
id: string | number | null;
|
|
38
|
+
result?: unknown;
|
|
39
|
+
error?: {
|
|
40
|
+
code: number;
|
|
41
|
+
message: string;
|
|
42
|
+
data?: unknown;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type JSONRPCMessage = JSONRPCRequest | JSONRPCNotification;
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// MCP protocol types (subset needed for tools)
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
/** MCP text content block */
|
|
53
|
+
interface MCPTextContent {
|
|
54
|
+
type: 'text';
|
|
55
|
+
text: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** MCP tool call result */
|
|
59
|
+
interface MCPCallToolResult {
|
|
60
|
+
content: MCPTextContent[];
|
|
61
|
+
isError?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// MCP Bridge configuration
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Configuration for the MCP bridge defaults.
|
|
70
|
+
* These values are used when MCP tool calls do not supply them explicitly.
|
|
71
|
+
*/
|
|
72
|
+
export interface MCPBridgeConfig {
|
|
73
|
+
/** Default workspace ID for tool calls */
|
|
74
|
+
workspace_id?: string;
|
|
75
|
+
/** Default actor for tool calls */
|
|
76
|
+
actor?: Actor;
|
|
77
|
+
/** Default source platform identifier */
|
|
78
|
+
source?: Source;
|
|
79
|
+
/** Default task ID (if not provided, a new UUID is generated per call) */
|
|
80
|
+
task_id?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const DEFAULT_BRIDGE_CONFIG: Required<MCPBridgeConfig> = {
|
|
84
|
+
workspace_id: 'ws_mcp_default',
|
|
85
|
+
actor: { type: 'agent', id: 'mcp-agent', display: 'MCP Agent' },
|
|
86
|
+
source: { platform: 'mcp' },
|
|
87
|
+
task_id: '',
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// MCP protocol constants
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
const LATEST_PROTOCOL_VERSION = '2025-03-26';
|
|
95
|
+
const JSONRPC_VERSION = '2.0';
|
|
96
|
+
|
|
97
|
+
const SERVER_INFO = {
|
|
98
|
+
name: 'palaryn-mcp-bridge',
|
|
99
|
+
version: '1.0.0',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// JSON-RPC error codes
|
|
103
|
+
const PARSE_ERROR = -32700;
|
|
104
|
+
const INVALID_REQUEST = -32600;
|
|
105
|
+
const METHOD_NOT_FOUND = -32601;
|
|
106
|
+
const INVALID_PARAMS = -32602;
|
|
107
|
+
const INTERNAL_ERROR = -32603;
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// StdioTransport - line-delimited JSON-RPC over stdin/stdout
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Reads line-delimited JSON-RPC messages from a readable stream
|
|
115
|
+
* and writes JSON-RPC responses to a writable stream.
|
|
116
|
+
* Follows the MCP stdio transport specification.
|
|
117
|
+
*/
|
|
118
|
+
class StdioTransport {
|
|
119
|
+
static readonly MAX_BUFFER_SIZE = 10 * 1024 * 1024; // 10MB
|
|
120
|
+
static readonly BUFFER_INACTIVITY_TIMEOUT_MS = 30_000; // 30s
|
|
121
|
+
|
|
122
|
+
private input: Readable;
|
|
123
|
+
private output: Writable;
|
|
124
|
+
private buffer: string = '';
|
|
125
|
+
private onMessage: ((msg: JSONRPCMessage) => void) | null = null;
|
|
126
|
+
private bufferTimeoutHandle: ReturnType<typeof setTimeout> | null = null;
|
|
127
|
+
|
|
128
|
+
constructor(input?: Readable, output?: Writable) {
|
|
129
|
+
this.input = input || process.stdin;
|
|
130
|
+
this.output = output || process.stdout;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Start listening for messages on the input stream. */
|
|
134
|
+
start(handler: (msg: JSONRPCMessage) => void): void {
|
|
135
|
+
this.onMessage = handler;
|
|
136
|
+
this.input.setEncoding('utf8');
|
|
137
|
+
this.input.on('data', (chunk: string) => this.onData(chunk));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Send a JSON-RPC response to the output stream. */
|
|
141
|
+
send(response: JSONRPCResponse): void {
|
|
142
|
+
const json = JSON.stringify(response);
|
|
143
|
+
this.output.write(json + '\n');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Stop listening and clean up. */
|
|
147
|
+
close(): void {
|
|
148
|
+
this.input.removeAllListeners('data');
|
|
149
|
+
this.onMessage = null;
|
|
150
|
+
this.clearBufferTimeout();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private clearBufferTimeout(): void {
|
|
154
|
+
if (this.bufferTimeoutHandle) {
|
|
155
|
+
clearTimeout(this.bufferTimeoutHandle);
|
|
156
|
+
this.bufferTimeoutHandle = null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Reset the inactivity timer for partial buffer data. */
|
|
161
|
+
private resetBufferTimeout(): void {
|
|
162
|
+
this.clearBufferTimeout();
|
|
163
|
+
if (this.buffer.length > 0) {
|
|
164
|
+
this.bufferTimeoutHandle = setTimeout(() => {
|
|
165
|
+
if (this.buffer.length > 0) {
|
|
166
|
+
console.error(`[mcp-bridge] Buffer inactivity timeout (${StdioTransport.BUFFER_INACTIVITY_TIMEOUT_MS}ms) with ${this.buffer.length} bytes pending, clearing`);
|
|
167
|
+
this.buffer = '';
|
|
168
|
+
const errorResponse: JSONRPCResponse = {
|
|
169
|
+
jsonrpc: JSONRPC_VERSION,
|
|
170
|
+
id: null,
|
|
171
|
+
error: { code: PARSE_ERROR, message: 'Buffer inactivity timeout: incomplete message cleared' },
|
|
172
|
+
};
|
|
173
|
+
this.send(errorResponse);
|
|
174
|
+
}
|
|
175
|
+
}, StdioTransport.BUFFER_INACTIVITY_TIMEOUT_MS);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private onData(chunk: string): void {
|
|
180
|
+
this.buffer += chunk;
|
|
181
|
+
|
|
182
|
+
// Prevent unbounded buffer growth
|
|
183
|
+
if (this.buffer.length > StdioTransport.MAX_BUFFER_SIZE) {
|
|
184
|
+
console.error('[mcp-bridge] Buffer exceeded maximum size (10MB), clearing');
|
|
185
|
+
this.buffer = '';
|
|
186
|
+
this.clearBufferTimeout();
|
|
187
|
+
const errorResponse: JSONRPCResponse = {
|
|
188
|
+
jsonrpc: JSONRPC_VERSION,
|
|
189
|
+
id: null,
|
|
190
|
+
error: { code: PARSE_ERROR, message: 'Message too large: exceeded 10MB buffer limit' },
|
|
191
|
+
};
|
|
192
|
+
this.send(errorResponse);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Process complete lines
|
|
197
|
+
const lines = this.buffer.split('\n');
|
|
198
|
+
// Keep the last (possibly incomplete) line in the buffer
|
|
199
|
+
this.buffer = lines.pop() || '';
|
|
200
|
+
|
|
201
|
+
for (const line of lines) {
|
|
202
|
+
const trimmed = line.trim();
|
|
203
|
+
if (!trimmed) continue;
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const parsed = JSON.parse(trimmed);
|
|
207
|
+
if (this.onMessage) {
|
|
208
|
+
this.onMessage(parsed as JSONRPCMessage);
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
// Send parse error for invalid JSON
|
|
212
|
+
const errorResponse: JSONRPCResponse = {
|
|
213
|
+
jsonrpc: JSONRPC_VERSION,
|
|
214
|
+
id: null,
|
|
215
|
+
error: { code: PARSE_ERROR, message: 'Parse error: invalid JSON' },
|
|
216
|
+
};
|
|
217
|
+
this.send(errorResponse);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Reset inactivity timer if there's still partial data in the buffer
|
|
222
|
+
this.resetBufferTimeout();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ---------------------------------------------------------------------------
|
|
227
|
+
// MCPBridge - the main bridge class
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* MCPBridge wraps a Palaryn Gateway instance as an MCP server, exposing
|
|
232
|
+
* the gateway's tool execution capabilities through the Model Context Protocol.
|
|
233
|
+
*
|
|
234
|
+
* Communication uses JSON-RPC 2.0 over stdio (line-delimited).
|
|
235
|
+
*
|
|
236
|
+
* Tools are managed via a registry pattern. By default, HTTP tools are
|
|
237
|
+
* registered (http_request, http_get, http_post). Additional tools
|
|
238
|
+
* (file, sql, shell) can be registered dynamically.
|
|
239
|
+
*
|
|
240
|
+
* Each tool constructs a proper ToolCall, runs it through the full gateway
|
|
241
|
+
* pipeline (policy, DLP, budget, rate limiting, execution), and returns
|
|
242
|
+
* the ToolResult as the MCP response.
|
|
243
|
+
*
|
|
244
|
+
* Supported MCP methods:
|
|
245
|
+
* - `initialize` - Protocol handshake (returns server info and capabilities)
|
|
246
|
+
* - `notifications/initialized` - Client acknowledgment (no-op notification)
|
|
247
|
+
* - `tools/list` - List available tools with their JSON schemas
|
|
248
|
+
* - `tools/call` - Execute a tool through the gateway
|
|
249
|
+
* - `ping` - Health check
|
|
250
|
+
*/
|
|
251
|
+
export class MCPBridge {
|
|
252
|
+
private gateway: Gateway;
|
|
253
|
+
private bridgeConfig: Required<MCPBridgeConfig>;
|
|
254
|
+
private transport: StdioTransport | null = null;
|
|
255
|
+
private initialized: boolean = false;
|
|
256
|
+
private toolRegistry: Map<string, MCPToolHandler> = new Map();
|
|
257
|
+
|
|
258
|
+
constructor(gateway: Gateway, bridgeConfig?: MCPBridgeConfig) {
|
|
259
|
+
this.gateway = gateway;
|
|
260
|
+
this.bridgeConfig = {
|
|
261
|
+
...DEFAULT_BRIDGE_CONFIG,
|
|
262
|
+
...bridgeConfig,
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// Register HTTP tools by default
|
|
266
|
+
this.registerTools(HTTP_TOOLS);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Register a single tool handler in the registry.
|
|
271
|
+
*/
|
|
272
|
+
registerTool(handler: MCPToolHandler): void {
|
|
273
|
+
this.toolRegistry.set(handler.definition.name, handler);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Register multiple tool handlers in the registry.
|
|
278
|
+
*/
|
|
279
|
+
registerTools(handlers: MCPToolHandler[]): void {
|
|
280
|
+
for (const handler of handlers) {
|
|
281
|
+
this.registerTool(handler);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Connect via stdio transport (reads from stdin, writes to stdout).
|
|
287
|
+
* This is the standard way to run an MCP server for CLI-based clients.
|
|
288
|
+
* Optionally accepts custom input/output streams for testing.
|
|
289
|
+
*/
|
|
290
|
+
async connectStdio(input?: Readable, output?: Writable): Promise<void> {
|
|
291
|
+
this.transport = new StdioTransport(input, output);
|
|
292
|
+
this.transport.start((msg) => this.handleMessage(msg));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Close the transport and shut down the gateway.
|
|
297
|
+
*/
|
|
298
|
+
async close(): Promise<void> {
|
|
299
|
+
if (this.transport) {
|
|
300
|
+
this.transport.close();
|
|
301
|
+
this.transport = null;
|
|
302
|
+
}
|
|
303
|
+
this.gateway.shutdown();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Returns the underlying Gateway instance.
|
|
308
|
+
*/
|
|
309
|
+
getGateway(): Gateway {
|
|
310
|
+
return this.gateway;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Whether the MCP handshake has been completed.
|
|
315
|
+
*/
|
|
316
|
+
isInitialized(): boolean {
|
|
317
|
+
return this.initialized;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
// Message dispatch
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
|
|
324
|
+
/** Handle an incoming JSON-RPC message (request or notification). */
|
|
325
|
+
private handleMessage(msg: JSONRPCMessage): void {
|
|
326
|
+
// Notifications have no 'id' field
|
|
327
|
+
if (!('id' in msg) || msg.id === undefined) {
|
|
328
|
+
this.handleNotification(msg as JSONRPCNotification);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const request = msg as JSONRPCRequest;
|
|
333
|
+
this.handleRequest(request).catch((err) => {
|
|
334
|
+
this.sendError(request.id, INTERNAL_ERROR, `Internal error: ${err instanceof Error ? err.message : String(err)}`);
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Handle a JSON-RPC notification (no response expected). */
|
|
339
|
+
private handleNotification(msg: JSONRPCNotification): void {
|
|
340
|
+
// The only notification we handle is `notifications/initialized`
|
|
341
|
+
if (msg.method === 'notifications/initialized') {
|
|
342
|
+
// Client acknowledges initialization is complete; no action needed.
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
// Unknown notifications are silently ignored per MCP spec.
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** Handle a JSON-RPC request and send a response. */
|
|
349
|
+
private async handleRequest(request: JSONRPCRequest): Promise<void> {
|
|
350
|
+
switch (request.method) {
|
|
351
|
+
case 'initialize':
|
|
352
|
+
this.handleInitialize(request);
|
|
353
|
+
break;
|
|
354
|
+
case 'tools/list':
|
|
355
|
+
this.handleToolsList(request);
|
|
356
|
+
break;
|
|
357
|
+
case 'tools/call':
|
|
358
|
+
await this.handleToolsCall(request);
|
|
359
|
+
break;
|
|
360
|
+
case 'ping':
|
|
361
|
+
this.sendResult(request.id, {});
|
|
362
|
+
break;
|
|
363
|
+
default:
|
|
364
|
+
this.sendError(request.id, METHOD_NOT_FOUND, `Method not found: ${request.method}`);
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ---------------------------------------------------------------------------
|
|
370
|
+
// MCP method handlers
|
|
371
|
+
// ---------------------------------------------------------------------------
|
|
372
|
+
|
|
373
|
+
/** Handle `initialize` - protocol handshake. */
|
|
374
|
+
private handleInitialize(request: JSONRPCRequest): void {
|
|
375
|
+
this.initialized = true;
|
|
376
|
+
|
|
377
|
+
this.sendResult(request.id, {
|
|
378
|
+
protocolVersion: LATEST_PROTOCOL_VERSION,
|
|
379
|
+
capabilities: {
|
|
380
|
+
tools: {
|
|
381
|
+
listChanged: false,
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
serverInfo: SERVER_INFO,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** Handle `tools/list` - return all registered tool definitions. */
|
|
389
|
+
private handleToolsList(request: JSONRPCRequest): void {
|
|
390
|
+
const tools = Array.from(this.toolRegistry.values()).map(h => h.definition);
|
|
391
|
+
this.sendResult(request.id, { tools });
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** Handle `tools/call` - execute a tool through the gateway. */
|
|
395
|
+
private async handleToolsCall(request: JSONRPCRequest): Promise<void> {
|
|
396
|
+
const params = request.params;
|
|
397
|
+
if (!params || typeof params.name !== 'string') {
|
|
398
|
+
this.sendError(request.id, INVALID_PARAMS, 'Missing required parameter: name');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const toolName = params.name as string;
|
|
403
|
+
const toolArgs = (params.arguments || {}) as Record<string, unknown>;
|
|
404
|
+
|
|
405
|
+
const handler = this.toolRegistry.get(toolName);
|
|
406
|
+
if (!handler) {
|
|
407
|
+
this.sendError(request.id, INVALID_PARAMS, `Unknown tool: ${toolName}`);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
let result: MCPCallToolResult;
|
|
412
|
+
try {
|
|
413
|
+
// Validate required args for HTTP tools (url is required)
|
|
414
|
+
const urlError = validateUrlArg(toolArgs);
|
|
415
|
+
if (urlError && (toolName === 'http_request' || toolName === 'http_get' || toolName === 'http_post')) {
|
|
416
|
+
result = this.toolError(urlError);
|
|
417
|
+
} else {
|
|
418
|
+
const toolCall = handler.buildToolCall(toolArgs, this.bridgeConfig);
|
|
419
|
+
result = await this.executeAndFormat(toolCall);
|
|
420
|
+
}
|
|
421
|
+
} catch (err) {
|
|
422
|
+
result = {
|
|
423
|
+
content: [
|
|
424
|
+
{
|
|
425
|
+
type: 'text',
|
|
426
|
+
text: JSON.stringify({
|
|
427
|
+
error: err instanceof Error ? err.message : String(err),
|
|
428
|
+
status: 'error',
|
|
429
|
+
}, null, 2),
|
|
430
|
+
},
|
|
431
|
+
],
|
|
432
|
+
isError: true,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
this.sendResult(request.id, result);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
440
|
+
// Internal helpers
|
|
441
|
+
// ---------------------------------------------------------------------------
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Build a ToolCall from MCP tool arguments, applying bridge defaults.
|
|
445
|
+
* Kept for backward compatibility with tests that access it via reflection.
|
|
446
|
+
*/
|
|
447
|
+
private buildToolCall(params: {
|
|
448
|
+
toolName: string;
|
|
449
|
+
capability: ToolInfo['capability'];
|
|
450
|
+
args: ToolCallArgs;
|
|
451
|
+
constraints?: { timeout_ms?: number; max_cost_usd?: number };
|
|
452
|
+
context?: { purpose?: string; labels?: string[] };
|
|
453
|
+
}): ToolCall {
|
|
454
|
+
const toolCall: ToolCall = {
|
|
455
|
+
tool_call_id: randomUUID(),
|
|
456
|
+
task_id: this.bridgeConfig.task_id || randomUUID(),
|
|
457
|
+
workspace_id: this.bridgeConfig.workspace_id,
|
|
458
|
+
actor: { ...this.bridgeConfig.actor },
|
|
459
|
+
source: { ...this.bridgeConfig.source },
|
|
460
|
+
tool: {
|
|
461
|
+
name: params.toolName,
|
|
462
|
+
version: '1.0.0',
|
|
463
|
+
capability: params.capability,
|
|
464
|
+
},
|
|
465
|
+
args: params.args,
|
|
466
|
+
timestamp: new Date().toISOString(),
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// Add constraints if any are specified
|
|
470
|
+
if (params.constraints?.timeout_ms != null || params.constraints?.max_cost_usd != null) {
|
|
471
|
+
toolCall.constraints = {};
|
|
472
|
+
if (params.constraints.timeout_ms != null) {
|
|
473
|
+
toolCall.constraints.timeout_ms = params.constraints.timeout_ms;
|
|
474
|
+
}
|
|
475
|
+
if (params.constraints.max_cost_usd != null) {
|
|
476
|
+
toolCall.constraints.max_cost_usd = params.constraints.max_cost_usd;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Add context if any is specified
|
|
481
|
+
if (params.context?.purpose || params.context?.labels) {
|
|
482
|
+
toolCall.context = {};
|
|
483
|
+
if (params.context.purpose) {
|
|
484
|
+
toolCall.context.purpose = params.context.purpose;
|
|
485
|
+
}
|
|
486
|
+
if (params.context.labels) {
|
|
487
|
+
toolCall.context.labels = params.context.labels;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return toolCall;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Execute a ToolCall through the gateway and format as MCP result.
|
|
496
|
+
*/
|
|
497
|
+
private async executeAndFormat(toolCall: ToolCall): Promise<MCPCallToolResult> {
|
|
498
|
+
try {
|
|
499
|
+
const result: ToolResult = await this.gateway.execute(toolCall);
|
|
500
|
+
return this.formatResult(result);
|
|
501
|
+
} catch (err) {
|
|
502
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
503
|
+
return {
|
|
504
|
+
content: [
|
|
505
|
+
{
|
|
506
|
+
type: 'text',
|
|
507
|
+
text: JSON.stringify(
|
|
508
|
+
{
|
|
509
|
+
error: errorMessage,
|
|
510
|
+
tool_call_id: toolCall.tool_call_id,
|
|
511
|
+
status: 'error',
|
|
512
|
+
},
|
|
513
|
+
null,
|
|
514
|
+
2,
|
|
515
|
+
),
|
|
516
|
+
},
|
|
517
|
+
],
|
|
518
|
+
isError: true,
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Convert a gateway ToolResult into an MCP CallToolResult.
|
|
525
|
+
*
|
|
526
|
+
* The result includes:
|
|
527
|
+
* - The tool output body (or error message) as the primary text content
|
|
528
|
+
* - Gateway metadata (status, policy decision, DLP report, budget, timing)
|
|
529
|
+
* as a second text content block for transparency
|
|
530
|
+
*/
|
|
531
|
+
private formatResult(result: ToolResult): MCPCallToolResult {
|
|
532
|
+
const isError = result.status === 'error' || result.status === 'blocked';
|
|
533
|
+
|
|
534
|
+
// Primary content: the actual output or error
|
|
535
|
+
let primaryText: string;
|
|
536
|
+
if (result.error) {
|
|
537
|
+
primaryText = result.error;
|
|
538
|
+
} else if (result.output?.body !== undefined) {
|
|
539
|
+
primaryText =
|
|
540
|
+
typeof result.output.body === 'string'
|
|
541
|
+
? result.output.body
|
|
542
|
+
: JSON.stringify(result.output.body, null, 2);
|
|
543
|
+
} else {
|
|
544
|
+
primaryText = `Request completed with status: ${result.status}`;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Metadata block for gateway transparency
|
|
548
|
+
const metadata = {
|
|
549
|
+
tool_call_id: result.tool_call_id,
|
|
550
|
+
task_id: result.task_id,
|
|
551
|
+
status: result.status,
|
|
552
|
+
policy: result.policy,
|
|
553
|
+
dlp: {
|
|
554
|
+
detected: result.dlp.detected,
|
|
555
|
+
severity: result.dlp.severity,
|
|
556
|
+
redaction_count: result.dlp.redactions.length,
|
|
557
|
+
},
|
|
558
|
+
budget: result.budget,
|
|
559
|
+
timing: result.timing,
|
|
560
|
+
http_status: result.output?.http_status,
|
|
561
|
+
exit_code: result.output?.exit_code,
|
|
562
|
+
rows_affected: result.output?.rows_affected,
|
|
563
|
+
paths: result.output?.paths,
|
|
564
|
+
stderr: result.output?.stderr,
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
return {
|
|
568
|
+
content: [
|
|
569
|
+
{
|
|
570
|
+
type: 'text',
|
|
571
|
+
text: primaryText,
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
type: 'text',
|
|
575
|
+
text: `--- Gateway Metadata ---\n${JSON.stringify(metadata, null, 2)}`,
|
|
576
|
+
},
|
|
577
|
+
],
|
|
578
|
+
isError,
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Map an HTTP method to a ToolInfo capability level.
|
|
584
|
+
*/
|
|
585
|
+
private methodToCapability(method: string): ToolInfo['capability'] {
|
|
586
|
+
switch (method.toUpperCase()) {
|
|
587
|
+
case 'GET':
|
|
588
|
+
case 'HEAD':
|
|
589
|
+
case 'OPTIONS':
|
|
590
|
+
return 'read';
|
|
591
|
+
case 'POST':
|
|
592
|
+
case 'PUT':
|
|
593
|
+
case 'PATCH':
|
|
594
|
+
return 'write';
|
|
595
|
+
case 'DELETE':
|
|
596
|
+
return 'delete';
|
|
597
|
+
default:
|
|
598
|
+
return 'write';
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Attempt to parse a body string as JSON, falling back to the raw string.
|
|
604
|
+
*/
|
|
605
|
+
private parseBody(body: string): unknown {
|
|
606
|
+
try {
|
|
607
|
+
return JSON.parse(body);
|
|
608
|
+
} catch {
|
|
609
|
+
return body;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Create an MCP tool error result.
|
|
615
|
+
*/
|
|
616
|
+
private toolError(message: string): MCPCallToolResult {
|
|
617
|
+
return {
|
|
618
|
+
content: [{ type: 'text', text: message }],
|
|
619
|
+
isError: true,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Send a successful JSON-RPC response.
|
|
625
|
+
*/
|
|
626
|
+
private sendResult(id: string | number, result: unknown): void {
|
|
627
|
+
if (this.transport) {
|
|
628
|
+
this.transport.send({
|
|
629
|
+
jsonrpc: JSONRPC_VERSION,
|
|
630
|
+
id,
|
|
631
|
+
result,
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Send a JSON-RPC error response.
|
|
638
|
+
*/
|
|
639
|
+
private sendError(id: string | number, code: number, message: string, data?: unknown): void {
|
|
640
|
+
if (this.transport) {
|
|
641
|
+
this.transport.send({
|
|
642
|
+
jsonrpc: JSONRPC_VERSION,
|
|
643
|
+
id,
|
|
644
|
+
error: { code, message, data },
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// ---------------------------------------------------------------------------
|
|
651
|
+
// Convenience entry point
|
|
652
|
+
// ---------------------------------------------------------------------------
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Create a Gateway instance with the given config (or defaults),
|
|
656
|
+
* wrap it in an MCPBridge, and connect via stdio transport.
|
|
657
|
+
*
|
|
658
|
+
* This is the main entry point for running Palaryn as an MCP server.
|
|
659
|
+
*
|
|
660
|
+
* @param gatewayConfig - Full gateway configuration (defaults to DEFAULT_CONFIG with auth disabled)
|
|
661
|
+
* @param bridgeConfig - MCP bridge defaults for workspace, actor, source
|
|
662
|
+
* @returns The connected MCPBridge instance
|
|
663
|
+
*/
|
|
664
|
+
export async function startMCPBridge(
|
|
665
|
+
gatewayConfig?: Partial<GatewayConfig>,
|
|
666
|
+
bridgeConfig?: MCPBridgeConfig,
|
|
667
|
+
): Promise<MCPBridge> {
|
|
668
|
+
// Merge with defaults, disabling auth for MCP (auth is handled by the MCP client/transport)
|
|
669
|
+
const config: GatewayConfig = {
|
|
670
|
+
...DEFAULT_CONFIG,
|
|
671
|
+
...gatewayConfig,
|
|
672
|
+
auth: {
|
|
673
|
+
...DEFAULT_CONFIG.auth,
|
|
674
|
+
enabled: false,
|
|
675
|
+
...gatewayConfig?.auth,
|
|
676
|
+
},
|
|
677
|
+
// Disable console audit output when running as MCP server
|
|
678
|
+
// to avoid polluting stdout (which is the MCP transport channel)
|
|
679
|
+
audit: {
|
|
680
|
+
...DEFAULT_CONFIG.audit,
|
|
681
|
+
console_output: false,
|
|
682
|
+
...gatewayConfig?.audit,
|
|
683
|
+
},
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
const gateway = new Gateway(config);
|
|
687
|
+
const bridge = new MCPBridge(gateway, bridgeConfig);
|
|
688
|
+
|
|
689
|
+
// Register non-HTTP tools based on executor config
|
|
690
|
+
if (config.executor.filesystem?.enabled) {
|
|
691
|
+
bridge.registerTools(FILE_TOOLS);
|
|
692
|
+
}
|
|
693
|
+
if (config.executor.sql?.enabled) {
|
|
694
|
+
bridge.registerTools(SQL_TOOLS);
|
|
695
|
+
}
|
|
696
|
+
if (config.executor.shell?.enabled) {
|
|
697
|
+
bridge.registerTools(SHELL_TOOLS);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
await bridge.connectStdio();
|
|
701
|
+
|
|
702
|
+
return bridge;
|
|
703
|
+
}
|