@datacules/agent-identity-mcp 0.2.1
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 +101 -0
- package/bin/server.js +67 -0
- package/package.json +39 -0
- package/src/index.ts +221 -0
- package/src/tools.ts +217 -0
- package/src/transports.ts +118 -0
- package/src/types.ts +99 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# @datacules/agent-identity-mcp
|
|
2
|
+
|
|
3
|
+
MCP server adapter for [`@datacules/agent-identity`](../../core). Exposes credential resolution as MCP tools so any MCP-capable client — **Claude Desktop, Claude Code, Cursor, Windsurf**, or a custom agent — can resolve credentials without touching the HTTP REST API.
|
|
4
|
+
|
|
5
|
+
## Tools exposed
|
|
6
|
+
|
|
7
|
+
| Tool | Description |
|
|
8
|
+
|------|-------------|
|
|
9
|
+
| `resolve_credential` | Resolve the correct credential for an `AgentRequestContext` |
|
|
10
|
+
| `resolve_migration_credential` | Resolve source + target credential pair for a migration workflow |
|
|
11
|
+
| `list_credentials` | List active credentials (safe metadata — no raw refs or secrets) |
|
|
12
|
+
| `list_rules` | List routing rules ordered by priority |
|
|
13
|
+
| `health` | Liveness check + loaded credential/rule counts |
|
|
14
|
+
|
|
15
|
+
## Transports
|
|
16
|
+
|
|
17
|
+
| Mode | Use case |
|
|
18
|
+
|------|----------|
|
|
19
|
+
| `stdio` (default) | Claude Desktop, Claude Code, Cursor, Windsurf config blocks |
|
|
20
|
+
| `http+sse` | Hosted / networked deployments reachable over HTTP |
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
### As a library (stdio)
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { createAgentIdentityMcpServer } from '@datacules/agent-identity-mcp';
|
|
28
|
+
import type { Credential, RoutingRule } from '@datacules/agent-identity';
|
|
29
|
+
|
|
30
|
+
const credentials: Credential[] = [ /* ... */ ];
|
|
31
|
+
const rules: RoutingRule[] = [ /* ... */ ];
|
|
32
|
+
|
|
33
|
+
const { start } = createAgentIdentityMcpServer({ credentials, rules });
|
|
34
|
+
await start(); // reads from stdin, writes to stdout
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### As a library (HTTP + SSE)
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const { start, stop } = createAgentIdentityMcpServer({
|
|
41
|
+
credentials,
|
|
42
|
+
rules,
|
|
43
|
+
transport: 'http',
|
|
44
|
+
httpPort: 3002,
|
|
45
|
+
httpAuthToken: process.env.MCP_AUTH_TOKEN, // optional
|
|
46
|
+
});
|
|
47
|
+
await start();
|
|
48
|
+
// Server now accepts SSE at GET http://127.0.0.1:3002/sse
|
|
49
|
+
// and tool calls at POST http://127.0.0.1:3002/messages?sessionId=<id>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### With a custom CredentialStore
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { VaultCredentialStore } from '@datacules/agent-identity-store-vault';
|
|
56
|
+
|
|
57
|
+
const store = new VaultCredentialStore({ address: process.env.VAULT_ADDR!, token: process.env.VAULT_TOKEN! });
|
|
58
|
+
const { start } = createAgentIdentityMcpServer({ store, rules });
|
|
59
|
+
await start();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Claude Desktop config
|
|
63
|
+
|
|
64
|
+
Add to `~/.claude/claude_desktop_config.json`:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"mcpServers": {
|
|
69
|
+
"agent-identity": {
|
|
70
|
+
"command": "npx",
|
|
71
|
+
"args": ["@datacules/agent-identity-mcp"],
|
|
72
|
+
"env": {
|
|
73
|
+
"AGENT_IDENTITY_CREDENTIALS": "<base64-encoded JSON array of Credential objects>",
|
|
74
|
+
"AGENT_IDENTITY_RULES": "<base64-encoded JSON array of RoutingRule objects>"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
To encode your data:
|
|
82
|
+
```bash
|
|
83
|
+
echo '[{"id":"cred-1", ...}]' | base64
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Claude Code config
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
claude mcp add agent-identity -e AGENT_IDENTITY_CREDENTIALS=<base64> -e AGENT_IDENTITY_RULES=<base64> -- npx @datacules/agent-identity-mcp
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Environment variables (standalone CLI)
|
|
93
|
+
|
|
94
|
+
| Variable | Default | Description |
|
|
95
|
+
|----------|---------|-------------|
|
|
96
|
+
| `AGENT_IDENTITY_CREDENTIALS` | required | Base64-encoded JSON array of `Credential` objects |
|
|
97
|
+
| `AGENT_IDENTITY_RULES` | required | Base64-encoded JSON array of `RoutingRule` objects |
|
|
98
|
+
| `MCP_TRANSPORT` | `stdio` | `stdio` or `http` |
|
|
99
|
+
| `MCP_HTTP_PORT` | `3002` | HTTP port (only when `MCP_TRANSPORT=http`) |
|
|
100
|
+
| `MCP_HTTP_HOST` | `127.0.0.1` | Bind address |
|
|
101
|
+
| `MCP_HTTP_AUTH_TOKEN` | — | Optional bearer token for HTTP auth |
|
package/bin/server.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Standalone CLI entry point for @datacules/agent-identity-mcp.
|
|
4
|
+
*
|
|
5
|
+
* Credentials and rules are loaded from environment variables:
|
|
6
|
+
*
|
|
7
|
+
* AGENT_IDENTITY_CREDENTIALS Base64-encoded JSON array of Credential objects
|
|
8
|
+
* AGENT_IDENTITY_RULES Base64-encoded JSON array of RoutingRule objects
|
|
9
|
+
* MCP_TRANSPORT 'stdio' (default) | 'http'
|
|
10
|
+
* MCP_HTTP_PORT HTTP port when MCP_TRANSPORT=http (default: 3002)
|
|
11
|
+
* MCP_HTTP_HOST HTTP host (default: 127.0.0.1)
|
|
12
|
+
* MCP_HTTP_AUTH_TOKEN Optional bearer token for HTTP transport
|
|
13
|
+
*
|
|
14
|
+
* Example Claude Desktop config:
|
|
15
|
+
* {
|
|
16
|
+
* "mcpServers": {
|
|
17
|
+
* "agent-identity": {
|
|
18
|
+
* "command": "npx",
|
|
19
|
+
* "args": ["@datacules/agent-identity-mcp"],
|
|
20
|
+
* "env": {
|
|
21
|
+
* "AGENT_IDENTITY_CREDENTIALS": "<base64-json>",
|
|
22
|
+
* "AGENT_IDENTITY_RULES": "<base64-json>"
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { createAgentIdentityMcpServer } from '../dist/esm/index.js';
|
|
30
|
+
|
|
31
|
+
function loadBase64Json(envVar, name) {
|
|
32
|
+
const raw = process.env[envVar];
|
|
33
|
+
if (!raw) {
|
|
34
|
+
console.error(`[agent-identity-mcp] ${name}: ${envVar} is not set. Provide base64-encoded JSON.`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(Buffer.from(raw, 'base64').toString('utf8'));
|
|
39
|
+
} catch (e) {
|
|
40
|
+
console.error(`[agent-identity-mcp] ${name}: Failed to parse ${envVar} — ${e.message}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const credentials = loadBase64Json('AGENT_IDENTITY_CREDENTIALS', 'credentials');
|
|
46
|
+
const rules = loadBase64Json('AGENT_IDENTITY_RULES', 'rules');
|
|
47
|
+
|
|
48
|
+
const transport = process.env.MCP_TRANSPORT ?? 'stdio';
|
|
49
|
+
const httpPort = parseInt(process.env.MCP_HTTP_PORT ?? '3002', 10);
|
|
50
|
+
const httpHost = process.env.MCP_HTTP_HOST ?? '127.0.0.1';
|
|
51
|
+
const httpAuthToken = process.env.MCP_HTTP_AUTH_TOKEN;
|
|
52
|
+
|
|
53
|
+
const { start } = createAgentIdentityMcpServer({
|
|
54
|
+
credentials,
|
|
55
|
+
rules,
|
|
56
|
+
transport,
|
|
57
|
+
httpPort,
|
|
58
|
+
httpHost,
|
|
59
|
+
httpAuthToken,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
process.on('uncaughtException', (err) => {
|
|
63
|
+
console.error('[agent-identity-mcp] uncaughtException:', err);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await start();
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@datacules/agent-identity-mcp",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "MCP server adapter for @datacules/agent-identity — expose credential resolution as MCP tools",
|
|
6
|
+
"main": "./dist/cjs/index.js",
|
|
7
|
+
"module": "./dist/esm/index.js",
|
|
8
|
+
"types": "./dist/types/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"agent-identity-mcp": "./bin/server.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc -p tsconfig.build.json",
|
|
14
|
+
"type-check": "tsc --noEmit",
|
|
15
|
+
"start": "node bin/server.js",
|
|
16
|
+
"start:http": "MCP_TRANSPORT=http node bin/server.js"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@datacules/agent-identity": "^0.1.0"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.10.0",
|
|
23
|
+
"zod": "^3.22.4"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@datacules/agent-identity": "*",
|
|
27
|
+
"typescript": "^5"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"mcp",
|
|
34
|
+
"model-context-protocol",
|
|
35
|
+
"agent-identity",
|
|
36
|
+
"credential",
|
|
37
|
+
"datacules"
|
|
38
|
+
]
|
|
39
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @datacules/agent-identity-mcp
|
|
3
|
+
*
|
|
4
|
+
* Exposes agent-identity credential resolution as an MCP server.
|
|
5
|
+
* Any MCP-capable client — Claude Desktop, Claude Code, Cursor,
|
|
6
|
+
* Windsurf, or a custom agent — can call the following tools:
|
|
7
|
+
*
|
|
8
|
+
* resolve_credential — resolves a credential for an AgentRequestContext
|
|
9
|
+
* resolve_migration_credential — resolves source+target pair for MigrationContext
|
|
10
|
+
* list_credentials — lists active credentials (safe metadata only)
|
|
11
|
+
* list_rules — lists routing rules (highest priority first)
|
|
12
|
+
* health — liveness + loaded credential/rule counts
|
|
13
|
+
*
|
|
14
|
+
* Supports two transports:
|
|
15
|
+
* stdio — stdin/stdout, compatible with Claude Desktop / Claude Code / Cursor configs
|
|
16
|
+
* http+sse — HTTP Server-Sent Events for hosted deployments
|
|
17
|
+
*
|
|
18
|
+
* Quick start (stdio):
|
|
19
|
+
* import { createAgentIdentityMcpServer } from '@datacules/agent-identity-mcp';
|
|
20
|
+
* const { start } = createAgentIdentityMcpServer({ credentials, rules });
|
|
21
|
+
* await start(); // reads from stdin, writes to stdout
|
|
22
|
+
*
|
|
23
|
+
* Quick start (HTTP):
|
|
24
|
+
* const { start } = createAgentIdentityMcpServer({
|
|
25
|
+
* credentials, rules, transport: 'http', httpPort: 3002
|
|
26
|
+
* });
|
|
27
|
+
* await start();
|
|
28
|
+
*
|
|
29
|
+
* Claude Desktop config snippet (~/.claude/claude_desktop_config.json):
|
|
30
|
+
* {
|
|
31
|
+
* "mcpServers": {
|
|
32
|
+
* "agent-identity": {
|
|
33
|
+
* "command": "npx",
|
|
34
|
+
* "args": ["@datacules/agent-identity-mcp"],
|
|
35
|
+
* "env": {
|
|
36
|
+
* "AGENT_IDENTITY_CREDENTIALS": "<base64-encoded JSON>",
|
|
37
|
+
* "AGENT_IDENTITY_RULES": "<base64-encoded JSON>"
|
|
38
|
+
* }
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
45
|
+
import {
|
|
46
|
+
CallToolRequestSchema,
|
|
47
|
+
ListToolsRequestSchema,
|
|
48
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
49
|
+
import type { AuditLogger, Credential, CredentialStore, RoutingRule } from '@datacules/agent-identity';
|
|
50
|
+
import { MemoryCredentialStore } from '@datacules/agent-identity';
|
|
51
|
+
import { ALL_TOOLS, type ToolDeps } from './tools.js';
|
|
52
|
+
import { StdioServerTransport, startHttpMcpTransport } from './transports.js';
|
|
53
|
+
import type { AgentIdentityMcpServerOptions } from './types.js';
|
|
54
|
+
|
|
55
|
+
export * from './types.js';
|
|
56
|
+
export { ALL_TOOLS } from './tools.js';
|
|
57
|
+
|
|
58
|
+
// ─── Server factory ───────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
export interface AgentIdentityMcpServerInit {
|
|
61
|
+
/**
|
|
62
|
+
* Credential array (convenience). Wrapped in a MemoryCredentialStore
|
|
63
|
+
* unless `store` is also provided, in which case `store` takes precedence.
|
|
64
|
+
*/
|
|
65
|
+
credentials?: Credential[];
|
|
66
|
+
/** Custom CredentialStore (e.g. VaultCredentialStore, AwsCredentialStore) */
|
|
67
|
+
store?: CredentialStore;
|
|
68
|
+
rules: RoutingRule[];
|
|
69
|
+
logger?: AuditLogger;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type AgentIdentityMcpServerConfig = AgentIdentityMcpServerInit & AgentIdentityMcpServerOptions;
|
|
73
|
+
|
|
74
|
+
export interface AgentIdentityMcpServerHandle {
|
|
75
|
+
/** MCP Server instance (use to attach additional request handlers if needed) */
|
|
76
|
+
server: Server;
|
|
77
|
+
/** Start the server and connect the configured transport. Resolves when ready. */
|
|
78
|
+
start: () => Promise<void>;
|
|
79
|
+
/** Stop / clean up (closes HTTP server for http transport; no-op for stdio) */
|
|
80
|
+
stop: () => Promise<void>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Create an agent-identity MCP server.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* // stdio (Claude Desktop / Claude Code config)
|
|
88
|
+
* const { start } = createAgentIdentityMcpServer({ credentials, rules });
|
|
89
|
+
* await start();
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* // HTTP+SSE (hosted / networked)
|
|
93
|
+
* const { start } = createAgentIdentityMcpServer({
|
|
94
|
+
* credentials, rules, transport: 'http', httpPort: 3002,
|
|
95
|
+
* });
|
|
96
|
+
* await start();
|
|
97
|
+
*/
|
|
98
|
+
export function createAgentIdentityMcpServer(
|
|
99
|
+
config: AgentIdentityMcpServerConfig
|
|
100
|
+
): AgentIdentityMcpServerHandle {
|
|
101
|
+
const {
|
|
102
|
+
credentials,
|
|
103
|
+
store: customStore,
|
|
104
|
+
rules,
|
|
105
|
+
logger,
|
|
106
|
+
name = 'agent-identity',
|
|
107
|
+
version = '0.1.0',
|
|
108
|
+
transport = 'stdio',
|
|
109
|
+
httpPort = 3002,
|
|
110
|
+
httpHost = '127.0.0.1',
|
|
111
|
+
httpAuthToken,
|
|
112
|
+
} = config;
|
|
113
|
+
|
|
114
|
+
if (!customStore && !credentials) {
|
|
115
|
+
throw new Error('[agent-identity-mcp] Provide either credentials[] or a custom store.');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const store: CredentialStore =
|
|
119
|
+
customStore ?? new MemoryCredentialStore(credentials!);
|
|
120
|
+
|
|
121
|
+
const deps: ToolDeps = { store, rules, logger };
|
|
122
|
+
|
|
123
|
+
// Build the MCP server
|
|
124
|
+
const server = new Server(
|
|
125
|
+
{ name, version },
|
|
126
|
+
{ capabilities: { tools: {} } }
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// Register tools/list handler
|
|
130
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
131
|
+
tools: ALL_TOOLS.map((t) => ({
|
|
132
|
+
name: t.name,
|
|
133
|
+
description: t.description,
|
|
134
|
+
inputSchema: {
|
|
135
|
+
type: 'object' as const,
|
|
136
|
+
// Convert Zod schema to JSON Schema via .shape introspection for
|
|
137
|
+
// simple objects; complex schemas fall back to an open object.
|
|
138
|
+
properties: extractJsonSchemaProperties(t.inputSchema),
|
|
139
|
+
required: extractRequiredKeys(t.inputSchema),
|
|
140
|
+
},
|
|
141
|
+
})),
|
|
142
|
+
}));
|
|
143
|
+
|
|
144
|
+
// Register tools/call handler
|
|
145
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
146
|
+
const tool = ALL_TOOLS.find((t) => t.name === request.params.name);
|
|
147
|
+
if (!tool) {
|
|
148
|
+
return {
|
|
149
|
+
content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown tool: ${request.params.name}` }) }],
|
|
150
|
+
isError: true,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return tool.handler(request.params.arguments ?? {}, deps);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
let stopFn: (() => void) | null = null;
|
|
157
|
+
|
|
158
|
+
const start = async (): Promise<void> => {
|
|
159
|
+
if (transport === 'stdio') {
|
|
160
|
+
const stdioTransport = new StdioServerTransport();
|
|
161
|
+
await server.connect(stdioTransport);
|
|
162
|
+
} else {
|
|
163
|
+
stopFn = await startHttpMcpTransport({
|
|
164
|
+
server,
|
|
165
|
+
port: httpPort,
|
|
166
|
+
host: httpHost,
|
|
167
|
+
authToken: httpAuthToken,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const stop = async (): Promise<void> => {
|
|
173
|
+
stopFn?.();
|
|
174
|
+
await server.close();
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
return { server, start, stop };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ─── JSON Schema helpers (lightweight — no ajv dependency) ───────────────────
|
|
181
|
+
|
|
182
|
+
function extractJsonSchemaProperties(schema: any): Record<string, unknown> {
|
|
183
|
+
try {
|
|
184
|
+
const shape = schema?._def?.shape?.() ?? schema?.shape ?? {};
|
|
185
|
+
const props: Record<string, unknown> = {};
|
|
186
|
+
for (const [key, val] of Object.entries(shape)) {
|
|
187
|
+
props[key] = zodToJsonSchemaNode(val);
|
|
188
|
+
}
|
|
189
|
+
return props;
|
|
190
|
+
} catch {
|
|
191
|
+
return {};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function extractRequiredKeys(schema: any): string[] {
|
|
196
|
+
try {
|
|
197
|
+
const shape = schema?._def?.shape?.() ?? schema?.shape ?? {};
|
|
198
|
+
return Object.entries(shape)
|
|
199
|
+
.filter(([, v]: [string, any]) => {
|
|
200
|
+
const typeName = v?._def?.typeName;
|
|
201
|
+
return typeName !== 'ZodOptional' && typeName !== 'ZodDefault';
|
|
202
|
+
})
|
|
203
|
+
.map(([k]) => k);
|
|
204
|
+
} catch {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function zodToJsonSchemaNode(zodNode: any): Record<string, unknown> {
|
|
210
|
+
const typeName = zodNode?._def?.typeName;
|
|
211
|
+
switch (typeName) {
|
|
212
|
+
case 'ZodString': return { type: 'string' };
|
|
213
|
+
case 'ZodNumber': return { type: 'number' };
|
|
214
|
+
case 'ZodBoolean': return { type: 'boolean' };
|
|
215
|
+
case 'ZodEnum': return { type: 'string', enum: zodNode._def.values };
|
|
216
|
+
case 'ZodOptional':
|
|
217
|
+
case 'ZodDefault': return zodToJsonSchemaNode(zodNode._def.innerType);
|
|
218
|
+
case 'ZodObject': return { type: 'object', properties: extractJsonSchemaProperties(zodNode) };
|
|
219
|
+
default: return { type: 'string' };
|
|
220
|
+
}
|
|
221
|
+
}
|
package/src/tools.ts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool handler implementations for @datacules/agent-identity.
|
|
3
|
+
*
|
|
4
|
+
* Each exported function corresponds to one MCP tool:
|
|
5
|
+
* resolve_credential — resolves credential for AgentRequestContext
|
|
6
|
+
* resolve_migration_credential — resolves source+target pair for MigrationContext
|
|
7
|
+
* list_credentials — lists active credentials (safe metadata, no raw refs)
|
|
8
|
+
* list_rules — lists routing rules
|
|
9
|
+
* health — liveness check
|
|
10
|
+
*
|
|
11
|
+
* Tool schemas use Zod and are exported as McpToolSchema objects so the
|
|
12
|
+
* server index can register them with a single loop.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import { createRouterFromStore, MemoryCredentialStore } from '@datacules/agent-identity';
|
|
17
|
+
import type {
|
|
18
|
+
AuditLogger,
|
|
19
|
+
Credential,
|
|
20
|
+
CredentialStore,
|
|
21
|
+
RoutingRule,
|
|
22
|
+
} from '@datacules/agent-identity';
|
|
23
|
+
|
|
24
|
+
// ─── Shared Zod schemas ───────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
const SupportedProviderSchema = z.enum(['openai', 'anthropic', 'gemini', 'mistral', 'local']);
|
|
27
|
+
const ResourceKindSchema = z.enum(['shared', 'personal']);
|
|
28
|
+
const MigrationPhaseSchema = z.enum(['dry-run', 'extract', 'transform', 'load', 'verify', 'rollback']);
|
|
29
|
+
|
|
30
|
+
const BaseContextSchema = z.object({
|
|
31
|
+
userId: z.string().min(1),
|
|
32
|
+
resourceId: z.string().min(1),
|
|
33
|
+
resourceKind: ResourceKindSchema,
|
|
34
|
+
provider: SupportedProviderSchema,
|
|
35
|
+
model: z.string().min(1),
|
|
36
|
+
action: z.string().min(1),
|
|
37
|
+
traceId: z.string().min(1),
|
|
38
|
+
sessionId: z.string().optional(),
|
|
39
|
+
requestedAt: z.string().optional(),
|
|
40
|
+
parentTraceId: z.string().optional(),
|
|
41
|
+
mcpSessionId: z.string().optional(),
|
|
42
|
+
mcpClientId: z.string().optional(),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const ResolveCredentialSchema = BaseContextSchema;
|
|
46
|
+
|
|
47
|
+
const ResolveMigrationSchema = BaseContextSchema.extend({
|
|
48
|
+
migrationId: z.string().min(1),
|
|
49
|
+
phase: MigrationPhaseSchema,
|
|
50
|
+
sourceResourceId: z.string().min(1),
|
|
51
|
+
targetResourceId: z.string().min(1),
|
|
52
|
+
batchIndex: z.number().int().nonnegative().optional(),
|
|
53
|
+
totalBatches: z.number().int().positive().optional(),
|
|
54
|
+
dryRun: z.boolean(),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// ─── Tool descriptor type ─────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
export interface McpToolDefinition {
|
|
60
|
+
name: string;
|
|
61
|
+
description: string;
|
|
62
|
+
inputSchema: z.ZodTypeAny;
|
|
63
|
+
handler: (input: unknown, deps: ToolDeps) => Promise<McpToolResult>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface McpToolResult {
|
|
67
|
+
content: Array<{ type: 'text'; text: string }>;
|
|
68
|
+
isError?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ToolDeps {
|
|
72
|
+
store: CredentialStore;
|
|
73
|
+
rules: RoutingRule[];
|
|
74
|
+
logger?: AuditLogger;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Helper ───────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
function ok(data: unknown): McpToolResult {
|
|
80
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function err(message: string): McpToolResult {
|
|
84
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: message }) }], isError: true };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─── resolve_credential ───────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
const resolveCredentialTool: McpToolDefinition = {
|
|
90
|
+
name: 'resolve_credential',
|
|
91
|
+
description:
|
|
92
|
+
'Resolve the correct credential for an agent request. ' +
|
|
93
|
+
'Provide the full AgentRequestContext (userId, resourceId, resourceKind, provider, model, action, traceId). ' +
|
|
94
|
+
'Returns the resolved credential metadata — never the raw secret.',
|
|
95
|
+
inputSchema: ResolveCredentialSchema,
|
|
96
|
+
async handler(input, { store, rules, logger }) {
|
|
97
|
+
const parsed = ResolveCredentialSchema.safeParse(input);
|
|
98
|
+
if (!parsed.success) return err(`Validation error: ${parsed.error.message}`);
|
|
99
|
+
|
|
100
|
+
const ctx = {
|
|
101
|
+
...parsed.data,
|
|
102
|
+
requestedAt: parsed.data.requestedAt ?? new Date().toISOString(),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const router = createRouterFromStore(store, rules, logger);
|
|
106
|
+
const resolved = router.resolve(ctx);
|
|
107
|
+
if (!resolved) return err('No credential resolved — no routing rule matched this context.');
|
|
108
|
+
|
|
109
|
+
return ok({
|
|
110
|
+
ok: true,
|
|
111
|
+
credentialId: resolved.credentialId,
|
|
112
|
+
kind: resolved.kind,
|
|
113
|
+
resolvedFor: resolved.resolvedFor,
|
|
114
|
+
// ref is intentionally omitted — never surface raw refs over MCP
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// ─── resolve_migration_credential ─────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
const resolveMigrationCredentialTool: McpToolDefinition = {
|
|
122
|
+
name: 'resolve_migration_credential',
|
|
123
|
+
description:
|
|
124
|
+
'Resolve source and target credentials for a data migration workflow. ' +
|
|
125
|
+
'Requires a full MigrationContext including migrationId, phase, sourceResourceId, targetResourceId, and dryRun flag. ' +
|
|
126
|
+
'Returns both resolved credential metadata objects — never raw secrets.',
|
|
127
|
+
inputSchema: ResolveMigrationSchema,
|
|
128
|
+
async handler(input, { store, rules, logger }) {
|
|
129
|
+
const parsed = ResolveMigrationSchema.safeParse(input);
|
|
130
|
+
if (!parsed.success) return err(`Validation error: ${parsed.error.message}`);
|
|
131
|
+
|
|
132
|
+
const ctx = {
|
|
133
|
+
...parsed.data,
|
|
134
|
+
requestedAt: parsed.data.requestedAt ?? new Date().toISOString(),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const router = createRouterFromStore(store, rules, logger);
|
|
138
|
+
const pair = router.resolvePair(ctx);
|
|
139
|
+
if (!pair) return err('No credential pair resolved — check that routing rules cover both sourceResourceId and targetResourceId.');
|
|
140
|
+
|
|
141
|
+
return ok({
|
|
142
|
+
ok: true,
|
|
143
|
+
migrationId: pair.migrationId,
|
|
144
|
+
source: { credentialId: pair.source.credentialId, kind: pair.source.kind, resolvedFor: pair.source.resolvedFor },
|
|
145
|
+
target: { credentialId: pair.target.credentialId, kind: pair.target.kind, resolvedFor: pair.target.resolvedFor },
|
|
146
|
+
expiresAt: pair.expiresAt ?? null,
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// ─── list_credentials ─────────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
const listCredentialsTool: McpToolDefinition = {
|
|
154
|
+
name: 'list_credentials',
|
|
155
|
+
description:
|
|
156
|
+
'List all active credentials registered with this agent-identity server. ' +
|
|
157
|
+
'Returns safe metadata only (id, kind, name, scope, status, expiresAt). ' +
|
|
158
|
+
'Raw refs and secrets are never included.',
|
|
159
|
+
inputSchema: z.object({
|
|
160
|
+
kind: z.enum(['fixed', 'user-delegated']).optional().describe('Filter by credential kind'),
|
|
161
|
+
}),
|
|
162
|
+
async handler(input, { store }) {
|
|
163
|
+
const parsed = z.object({ kind: z.enum(['fixed', 'user-delegated']).optional() }).safeParse(input);
|
|
164
|
+
if (!parsed.success) return err(`Validation error: ${parsed.error.message}`);
|
|
165
|
+
|
|
166
|
+
const creds = parsed.data.kind
|
|
167
|
+
? await store.listByKind(parsed.data.kind)
|
|
168
|
+
: await store.listActive();
|
|
169
|
+
|
|
170
|
+
const safe = creds.map(({ id, kind, name, scope, status, expiresAt }) => ({
|
|
171
|
+
id, kind, name, scope, status, expiresAt: expiresAt ?? null,
|
|
172
|
+
}));
|
|
173
|
+
|
|
174
|
+
return ok({ count: safe.length, credentials: safe });
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// ─── list_rules ───────────────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
const listRulesTool: McpToolDefinition = {
|
|
181
|
+
name: 'list_rules',
|
|
182
|
+
description:
|
|
183
|
+
'List all routing rules registered with this agent-identity server, ' +
|
|
184
|
+
'ordered by priority (highest first). Useful for debugging credential routing.',
|
|
185
|
+
inputSchema: z.object({}),
|
|
186
|
+
async handler(_input, { rules }) {
|
|
187
|
+
const sorted = [...rules].sort((a, b) => b.priority - a.priority);
|
|
188
|
+
return ok({ count: sorted.length, rules: sorted });
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// ─── health ───────────────────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
const healthTool: McpToolDefinition = {
|
|
195
|
+
name: 'health',
|
|
196
|
+
description: 'Check whether the agent-identity MCP server is healthy and how many credentials/rules are loaded.',
|
|
197
|
+
inputSchema: z.object({}),
|
|
198
|
+
async handler(_input, { store, rules }) {
|
|
199
|
+
const active = await store.listActive();
|
|
200
|
+
return ok({
|
|
201
|
+
status: 'ok',
|
|
202
|
+
credentialsLoaded: active.length,
|
|
203
|
+
rulesLoaded: rules.length,
|
|
204
|
+
timestamp: new Date().toISOString(),
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// ─── Export all tools ─────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
export const ALL_TOOLS: McpToolDefinition[] = [
|
|
212
|
+
resolveCredentialTool,
|
|
213
|
+
resolveMigrationCredentialTool,
|
|
214
|
+
listCredentialsTool,
|
|
215
|
+
listRulesTool,
|
|
216
|
+
healthTool,
|
|
217
|
+
];
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport helpers for @datacules/agent-identity-mcp.
|
|
3
|
+
*
|
|
4
|
+
* Provides two transports:
|
|
5
|
+
* - StdioServerTransport : stdin/stdout, for Claude Desktop / Claude Code / Cursor config
|
|
6
|
+
* - createHttpMcpTransport: HTTP + SSE, for hosted / networked deployments
|
|
7
|
+
*
|
|
8
|
+
* The HTTP transport adds optional bearer-token auth. The SSE endpoint at
|
|
9
|
+
* GET /sse initialises a session; the message endpoint at POST /messages
|
|
10
|
+
* receives client tool calls and posts responses back over the SSE stream.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
14
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
15
|
+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
16
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
17
|
+
|
|
18
|
+
export { StdioServerTransport };
|
|
19
|
+
|
|
20
|
+
// ─── HTTP + SSE Transport ─────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
export interface HttpMcpTransportOptions {
|
|
23
|
+
/** MCP Server instance to connect the transport to */
|
|
24
|
+
server: Server;
|
|
25
|
+
/** TCP port to listen on (default: 3002) */
|
|
26
|
+
port?: number;
|
|
27
|
+
/** Bind address (default: '127.0.0.1') */
|
|
28
|
+
host?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Optional static bearer token.
|
|
31
|
+
* Requests without a matching Authorization: Bearer <token> header are
|
|
32
|
+
* rejected with 401. Omit to allow unauthenticated connections (only
|
|
33
|
+
* appropriate on a private network / localhost).
|
|
34
|
+
*/
|
|
35
|
+
authToken?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Start an HTTP + SSE MCP transport.
|
|
40
|
+
*
|
|
41
|
+
* Session lifecycle:
|
|
42
|
+
* 1. Client opens GET /sse — transport creates an SSEServerTransport,
|
|
43
|
+
* connects it to the MCP Server, and starts streaming.
|
|
44
|
+
* 2. Client POSTs tool calls to POST /messages?sessionId=<id>.
|
|
45
|
+
* 3. Transport routes each message to the correct session and replies
|
|
46
|
+
* via the open SSE stream.
|
|
47
|
+
*
|
|
48
|
+
* Returns a cleanup function that closes the HTTP server.
|
|
49
|
+
*/
|
|
50
|
+
export async function startHttpMcpTransport(options: HttpMcpTransportOptions): Promise<() => void> {
|
|
51
|
+
const { server, port = 3002, host = '127.0.0.1', authToken } = options;
|
|
52
|
+
|
|
53
|
+
// Session registry: sessionId → active SSEServerTransport
|
|
54
|
+
const sessions = new Map<string, SSEServerTransport>();
|
|
55
|
+
|
|
56
|
+
const http = await import('node:http');
|
|
57
|
+
|
|
58
|
+
function authenticate(req: IncomingMessage, res: ServerResponse): boolean {
|
|
59
|
+
if (!authToken) return true;
|
|
60
|
+
const header = req.headers['authorization'] ?? '';
|
|
61
|
+
if (header === `Bearer ${authToken}`) return true;
|
|
62
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
63
|
+
res.end(JSON.stringify({ error: 'Unauthorized — valid Bearer token required' }));
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const httpServer = http.createServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
68
|
+
const url = new URL(req.url ?? '/', `http://${host}:${port}`);
|
|
69
|
+
|
|
70
|
+
// CORS for browser-based MCP clients
|
|
71
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
72
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
73
|
+
res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type');
|
|
74
|
+
if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }
|
|
75
|
+
|
|
76
|
+
if (!authenticate(req, res)) return;
|
|
77
|
+
|
|
78
|
+
// ── SSE session init ──────────────────────────────────────────────────
|
|
79
|
+
if (req.method === 'GET' && url.pathname === '/sse') {
|
|
80
|
+
const transport = new SSEServerTransport('/messages', res);
|
|
81
|
+
sessions.set(transport.sessionId, transport);
|
|
82
|
+
|
|
83
|
+
req.on('close', () => sessions.delete(transport.sessionId));
|
|
84
|
+
|
|
85
|
+
await server.connect(transport);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── Incoming tool call message ────────────────────────────────────────
|
|
90
|
+
if (req.method === 'POST' && url.pathname === '/messages') {
|
|
91
|
+
const sessionId = url.searchParams.get('sessionId') ?? '';
|
|
92
|
+
const transport = sessions.get(sessionId);
|
|
93
|
+
|
|
94
|
+
if (!transport) {
|
|
95
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
96
|
+
res.end(JSON.stringify({ error: `Session ${sessionId} not found or expired` }));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await transport.handlePostMessage(req, res);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ── Health probe (GET /) ──────────────────────────────────────────────
|
|
105
|
+
if (req.method === 'GET' && url.pathname === '/') {
|
|
106
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
107
|
+
res.end(JSON.stringify({ ok: true, sessions: sessions.size, transport: 'http+sse' }));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
res.writeHead(404); res.end();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
await new Promise<void>((resolve) => httpServer.listen(port, host, resolve));
|
|
115
|
+
console.error(`[agent-identity-mcp] HTTP+SSE transport listening on http://${host}:${port}`);
|
|
116
|
+
|
|
117
|
+
return () => httpServer.close();
|
|
118
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP-specific type extensions for @datacules/agent-identity.
|
|
3
|
+
*
|
|
4
|
+
* McpRequestContext extends AgentRequestContext with the MCP session and
|
|
5
|
+
* client identifiers so audit logs can trace a credential resolution back
|
|
6
|
+
* to the exact MCP session that triggered it.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { AgentRequestContext, MigrationContext } from '@datacules/agent-identity';
|
|
10
|
+
|
|
11
|
+
// ─── MCP Request Context ──────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* AgentRequestContext enriched with MCP session metadata.
|
|
15
|
+
* Pass this to AgentIdentityMcpServer.resolve() for full audit trail.
|
|
16
|
+
*/
|
|
17
|
+
export interface McpRequestContext extends AgentRequestContext {
|
|
18
|
+
/** MCP session ID from the active transport session */
|
|
19
|
+
mcpSessionId: string;
|
|
20
|
+
/** MCP client identifier (e.g. 'claude-desktop', 'cursor', 'windsurf') */
|
|
21
|
+
mcpClientId?: string;
|
|
22
|
+
/** MCP protocol version negotiated during handshake */
|
|
23
|
+
mcpProtocolVersion?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* MigrationContext enriched with MCP session metadata.
|
|
28
|
+
*/
|
|
29
|
+
export interface McpMigrationContext extends MigrationContext {
|
|
30
|
+
mcpSessionId: string;
|
|
31
|
+
mcpClientId?: string;
|
|
32
|
+
mcpProtocolVersion?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ─── Tool I/O shapes (validated by Zod in tools.ts) ──────────────────────────
|
|
36
|
+
|
|
37
|
+
export interface ResolveCredentialInput {
|
|
38
|
+
userId: string;
|
|
39
|
+
resourceId: string;
|
|
40
|
+
resourceKind: 'shared' | 'personal';
|
|
41
|
+
provider: 'openai' | 'anthropic' | 'gemini' | 'mistral' | 'local';
|
|
42
|
+
model: string;
|
|
43
|
+
action: string;
|
|
44
|
+
traceId: string;
|
|
45
|
+
sessionId?: string;
|
|
46
|
+
requestedAt?: string;
|
|
47
|
+
parentTraceId?: string;
|
|
48
|
+
// MCP extensions
|
|
49
|
+
mcpSessionId?: string;
|
|
50
|
+
mcpClientId?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ResolveMigrationInput {
|
|
54
|
+
userId: string;
|
|
55
|
+
resourceId: string;
|
|
56
|
+
resourceKind: 'shared' | 'personal';
|
|
57
|
+
provider: 'openai' | 'anthropic' | 'gemini' | 'mistral' | 'local';
|
|
58
|
+
model: string;
|
|
59
|
+
action: string;
|
|
60
|
+
traceId: string;
|
|
61
|
+
migrationId: string;
|
|
62
|
+
phase: 'dry-run' | 'extract' | 'transform' | 'load' | 'verify' | 'rollback';
|
|
63
|
+
sourceResourceId: string;
|
|
64
|
+
targetResourceId: string;
|
|
65
|
+
batchIndex?: number;
|
|
66
|
+
totalBatches?: number;
|
|
67
|
+
dryRun: boolean;
|
|
68
|
+
requestedAt?: string;
|
|
69
|
+
mcpSessionId?: string;
|
|
70
|
+
mcpClientId?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─── MCP Server Options ───────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
export interface AgentIdentityMcpServerOptions {
|
|
76
|
+
/**
|
|
77
|
+
* Server name sent during MCP handshake.
|
|
78
|
+
* Appears in the client's tool list as the server label.
|
|
79
|
+
*/
|
|
80
|
+
name?: string;
|
|
81
|
+
/** Semantic version string reported during handshake */
|
|
82
|
+
version?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Transport mode.
|
|
85
|
+
* - 'stdio' : reads from stdin / writes to stdout (default; use for
|
|
86
|
+
* Claude Desktop, Claude Code, Cursor, Windsurf configs)
|
|
87
|
+
* - 'http' : HTTP + SSE transport on the specified port
|
|
88
|
+
*/
|
|
89
|
+
transport?: 'stdio' | 'http';
|
|
90
|
+
/** Port for HTTP+SSE transport (default: 3002) */
|
|
91
|
+
httpPort?: number;
|
|
92
|
+
/** Host for HTTP+SSE transport (default: '127.0.0.1') */
|
|
93
|
+
httpHost?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Optional bearer token required in MCP HTTP requests.
|
|
96
|
+
* Ignored for stdio transport.
|
|
97
|
+
*/
|
|
98
|
+
httpAuthToken?: string;
|
|
99
|
+
}
|