@datacules/agent-identity-mcp 0.10.0 → 0.11.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/LICENSE +109 -0
- package/dist/cjs/index.js +180 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/tools.js +166 -0
- package/dist/cjs/tools.js.map +1 -0
- package/dist/cjs/transports.js +125 -0
- package/dist/cjs/transports.js.map +1 -0
- package/dist/cjs/types.js +10 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/index.js +161 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/tools.js +163 -0
- package/dist/esm/tools.js.map +1 -0
- package/dist/esm/transports.js +88 -0
- package/dist/esm/transports.js.map +1 -0
- package/dist/esm/types.js +9 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/types/index.d.ts +84 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/tools.d.ts +36 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/transports.d.ts +43 -0
- package/dist/types/transports.d.ts.map +1 -0
- package/dist/types/types.d.ts +87 -0
- package/dist/types/types.d.ts.map +1 -0
- package/package.json +23 -3
- package/src/index.ts +0 -221
- package/src/mcp.test.ts +0 -271
- package/src/tools.ts +0 -217
- package/src/transports.ts +0 -118
- package/src/types.ts +0 -99
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -10
|
@@ -0,0 +1,161 @@
|
|
|
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
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
44
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
45
|
+
import { MemoryCredentialStore } from '@datacules/agent-identity';
|
|
46
|
+
import { ALL_TOOLS } from './tools.js';
|
|
47
|
+
import { StdioServerTransport, startHttpMcpTransport } from './transports.js';
|
|
48
|
+
export * from './types.js';
|
|
49
|
+
export { ALL_TOOLS } from './tools.js';
|
|
50
|
+
/**
|
|
51
|
+
* Create an agent-identity MCP server.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // stdio (Claude Desktop / Claude Code config)
|
|
55
|
+
* const { start } = createAgentIdentityMcpServer({ credentials, rules });
|
|
56
|
+
* await start();
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // HTTP+SSE (hosted / networked)
|
|
60
|
+
* const { start } = createAgentIdentityMcpServer({
|
|
61
|
+
* credentials, rules, transport: 'http', httpPort: 3002,
|
|
62
|
+
* });
|
|
63
|
+
* await start();
|
|
64
|
+
*/
|
|
65
|
+
export function createAgentIdentityMcpServer(config) {
|
|
66
|
+
const { credentials, store: customStore, rules, logger, name = 'agent-identity', version = '0.1.0', transport = 'stdio', httpPort = 3002, httpHost = '127.0.0.1', httpAuthToken, } = config;
|
|
67
|
+
if (!customStore && !credentials) {
|
|
68
|
+
throw new Error('[agent-identity-mcp] Provide either credentials[] or a custom store.');
|
|
69
|
+
}
|
|
70
|
+
const store = customStore ?? new MemoryCredentialStore(credentials);
|
|
71
|
+
const deps = { store, rules, logger };
|
|
72
|
+
// Build the MCP server
|
|
73
|
+
const server = new Server({ name, version }, { capabilities: { tools: {} } });
|
|
74
|
+
// Register tools/list handler
|
|
75
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
76
|
+
tools: ALL_TOOLS.map((t) => ({
|
|
77
|
+
name: t.name,
|
|
78
|
+
description: t.description,
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: 'object',
|
|
81
|
+
// Convert Zod schema to JSON Schema via .shape introspection for
|
|
82
|
+
// simple objects; complex schemas fall back to an open object.
|
|
83
|
+
properties: extractJsonSchemaProperties(t.inputSchema),
|
|
84
|
+
required: extractRequiredKeys(t.inputSchema),
|
|
85
|
+
},
|
|
86
|
+
})),
|
|
87
|
+
}));
|
|
88
|
+
// Register tools/call handler
|
|
89
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
90
|
+
const tool = ALL_TOOLS.find((t) => t.name === request.params.name);
|
|
91
|
+
if (!tool) {
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: 'text', text: JSON.stringify({ error: `Unknown tool: ${request.params.name}` }) }],
|
|
94
|
+
isError: true,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return tool.handler(request.params.arguments ?? {}, deps);
|
|
98
|
+
});
|
|
99
|
+
let stopFn = null;
|
|
100
|
+
const start = async () => {
|
|
101
|
+
if (transport === 'stdio') {
|
|
102
|
+
const stdioTransport = new StdioServerTransport();
|
|
103
|
+
await server.connect(stdioTransport);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
stopFn = await startHttpMcpTransport({
|
|
107
|
+
server,
|
|
108
|
+
port: httpPort,
|
|
109
|
+
host: httpHost,
|
|
110
|
+
authToken: httpAuthToken,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const stop = async () => {
|
|
115
|
+
stopFn?.();
|
|
116
|
+
await server.close();
|
|
117
|
+
};
|
|
118
|
+
return { server, start, stop };
|
|
119
|
+
}
|
|
120
|
+
// ─── JSON Schema helpers (lightweight — no ajv dependency) ───────────────────
|
|
121
|
+
function extractJsonSchemaProperties(schema) {
|
|
122
|
+
try {
|
|
123
|
+
const shape = schema?._def?.shape?.() ?? schema?.shape ?? {};
|
|
124
|
+
const props = {};
|
|
125
|
+
for (const [key, val] of Object.entries(shape)) {
|
|
126
|
+
props[key] = zodToJsonSchemaNode(val);
|
|
127
|
+
}
|
|
128
|
+
return props;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return {};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function extractRequiredKeys(schema) {
|
|
135
|
+
try {
|
|
136
|
+
const shape = schema?._def?.shape?.() ?? schema?.shape ?? {};
|
|
137
|
+
return Object.entries(shape)
|
|
138
|
+
.filter(([, v]) => {
|
|
139
|
+
const typeName = v?._def?.typeName;
|
|
140
|
+
return typeName !== 'ZodOptional' && typeName !== 'ZodDefault';
|
|
141
|
+
})
|
|
142
|
+
.map(([k]) => k);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function zodToJsonSchemaNode(zodNode) {
|
|
149
|
+
const typeName = zodNode?._def?.typeName;
|
|
150
|
+
switch (typeName) {
|
|
151
|
+
case 'ZodString': return { type: 'string' };
|
|
152
|
+
case 'ZodNumber': return { type: 'number' };
|
|
153
|
+
case 'ZodBoolean': return { type: 'boolean' };
|
|
154
|
+
case 'ZodEnum': return { type: 'string', enum: zodNode._def.values };
|
|
155
|
+
case 'ZodOptional':
|
|
156
|
+
case 'ZodDefault': return zodToJsonSchemaNode(zodNode._def.innerType);
|
|
157
|
+
case 'ZodObject': return { type: 'object', properties: extractJsonSchemaProperties(zodNode) };
|
|
158
|
+
default: return { type: 'string' };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
import { z } from 'zod';
|
|
15
|
+
import { createRouterFromStore } from '@datacules/agent-identity';
|
|
16
|
+
// ─── Shared Zod schemas ───────────────────────────────────────────────────────
|
|
17
|
+
const SupportedProviderSchema = z.enum(['openai', 'anthropic', 'gemini', 'mistral', 'local']);
|
|
18
|
+
const ResourceKindSchema = z.enum(['shared', 'personal']);
|
|
19
|
+
const MigrationPhaseSchema = z.enum(['dry-run', 'extract', 'transform', 'load', 'verify', 'rollback']);
|
|
20
|
+
const BaseContextSchema = z.object({
|
|
21
|
+
userId: z.string().min(1),
|
|
22
|
+
resourceId: z.string().min(1),
|
|
23
|
+
resourceKind: ResourceKindSchema,
|
|
24
|
+
provider: SupportedProviderSchema,
|
|
25
|
+
model: z.string().min(1),
|
|
26
|
+
action: z.string().min(1),
|
|
27
|
+
traceId: z.string().min(1),
|
|
28
|
+
sessionId: z.string().optional(),
|
|
29
|
+
requestedAt: z.string().optional(),
|
|
30
|
+
parentTraceId: z.string().optional(),
|
|
31
|
+
mcpSessionId: z.string().optional(),
|
|
32
|
+
mcpClientId: z.string().optional(),
|
|
33
|
+
});
|
|
34
|
+
const ResolveCredentialSchema = BaseContextSchema;
|
|
35
|
+
const ResolveMigrationSchema = BaseContextSchema.extend({
|
|
36
|
+
migrationId: z.string().min(1),
|
|
37
|
+
phase: MigrationPhaseSchema,
|
|
38
|
+
sourceResourceId: z.string().min(1),
|
|
39
|
+
targetResourceId: z.string().min(1),
|
|
40
|
+
batchIndex: z.number().int().nonnegative().optional(),
|
|
41
|
+
totalBatches: z.number().int().positive().optional(),
|
|
42
|
+
dryRun: z.boolean(),
|
|
43
|
+
});
|
|
44
|
+
// ─── Helper ───────────────────────────────────────────────────────────────────
|
|
45
|
+
function ok(data) {
|
|
46
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
47
|
+
}
|
|
48
|
+
function err(message) {
|
|
49
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: message }) }], isError: true };
|
|
50
|
+
}
|
|
51
|
+
// ─── resolve_credential ───────────────────────────────────────────────────────
|
|
52
|
+
const resolveCredentialTool = {
|
|
53
|
+
name: 'resolve_credential',
|
|
54
|
+
description: 'Resolve the correct credential for an agent request. ' +
|
|
55
|
+
'Provide the full AgentRequestContext (userId, resourceId, resourceKind, provider, model, action, traceId). ' +
|
|
56
|
+
'Returns the resolved credential metadata — never the raw secret.',
|
|
57
|
+
inputSchema: ResolveCredentialSchema,
|
|
58
|
+
async handler(input, { store, rules, logger }) {
|
|
59
|
+
const parsed = ResolveCredentialSchema.safeParse(input);
|
|
60
|
+
if (!parsed.success)
|
|
61
|
+
return err(`Validation error: ${parsed.error.message}`);
|
|
62
|
+
const ctx = {
|
|
63
|
+
...parsed.data,
|
|
64
|
+
requestedAt: parsed.data.requestedAt ?? new Date().toISOString(),
|
|
65
|
+
};
|
|
66
|
+
const router = createRouterFromStore(store, rules, logger);
|
|
67
|
+
const resolved = router.resolve(ctx);
|
|
68
|
+
if (!resolved)
|
|
69
|
+
return err('No credential resolved — no routing rule matched this context.');
|
|
70
|
+
return ok({
|
|
71
|
+
ok: true,
|
|
72
|
+
credentialId: resolved.credentialId,
|
|
73
|
+
kind: resolved.kind,
|
|
74
|
+
resolvedFor: resolved.resolvedFor,
|
|
75
|
+
// ref is intentionally omitted — never surface raw refs over MCP
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
// ─── resolve_migration_credential ─────────────────────────────────────────────
|
|
80
|
+
const resolveMigrationCredentialTool = {
|
|
81
|
+
name: 'resolve_migration_credential',
|
|
82
|
+
description: 'Resolve source and target credentials for a data migration workflow. ' +
|
|
83
|
+
'Requires a full MigrationContext including migrationId, phase, sourceResourceId, targetResourceId, and dryRun flag. ' +
|
|
84
|
+
'Returns both resolved credential metadata objects — never raw secrets.',
|
|
85
|
+
inputSchema: ResolveMigrationSchema,
|
|
86
|
+
async handler(input, { store, rules, logger }) {
|
|
87
|
+
const parsed = ResolveMigrationSchema.safeParse(input);
|
|
88
|
+
if (!parsed.success)
|
|
89
|
+
return err(`Validation error: ${parsed.error.message}`);
|
|
90
|
+
const ctx = {
|
|
91
|
+
...parsed.data,
|
|
92
|
+
requestedAt: parsed.data.requestedAt ?? new Date().toISOString(),
|
|
93
|
+
};
|
|
94
|
+
const router = createRouterFromStore(store, rules, logger);
|
|
95
|
+
const pair = router.resolvePair(ctx);
|
|
96
|
+
if (!pair)
|
|
97
|
+
return err('No credential pair resolved — check that routing rules cover both sourceResourceId and targetResourceId.');
|
|
98
|
+
return ok({
|
|
99
|
+
ok: true,
|
|
100
|
+
migrationId: pair.migrationId,
|
|
101
|
+
source: { credentialId: pair.source.credentialId, kind: pair.source.kind, resolvedFor: pair.source.resolvedFor },
|
|
102
|
+
target: { credentialId: pair.target.credentialId, kind: pair.target.kind, resolvedFor: pair.target.resolvedFor },
|
|
103
|
+
expiresAt: pair.expiresAt ?? null,
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
// ─── list_credentials ─────────────────────────────────────────────────────────
|
|
108
|
+
const listCredentialsTool = {
|
|
109
|
+
name: 'list_credentials',
|
|
110
|
+
description: 'List all active credentials registered with this agent-identity server. ' +
|
|
111
|
+
'Returns safe metadata only (id, kind, name, scope, status, expiresAt). ' +
|
|
112
|
+
'Raw refs and secrets are never included.',
|
|
113
|
+
inputSchema: z.object({
|
|
114
|
+
kind: z.enum(['fixed', 'user-delegated']).optional().describe('Filter by credential kind'),
|
|
115
|
+
}),
|
|
116
|
+
async handler(input, { store }) {
|
|
117
|
+
const parsed = z.object({ kind: z.enum(['fixed', 'user-delegated']).optional() }).safeParse(input);
|
|
118
|
+
if (!parsed.success)
|
|
119
|
+
return err(`Validation error: ${parsed.error.message}`);
|
|
120
|
+
const creds = parsed.data.kind
|
|
121
|
+
? await store.listByKind(parsed.data.kind)
|
|
122
|
+
: await store.listActive();
|
|
123
|
+
const safe = creds.map(({ id, kind, name, scope, status, expiresAt }) => ({
|
|
124
|
+
id, kind, name, scope, status, expiresAt: expiresAt ?? null,
|
|
125
|
+
}));
|
|
126
|
+
return ok({ count: safe.length, credentials: safe });
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
// ─── list_rules ───────────────────────────────────────────────────────────────
|
|
130
|
+
const listRulesTool = {
|
|
131
|
+
name: 'list_rules',
|
|
132
|
+
description: 'List all routing rules registered with this agent-identity server, ' +
|
|
133
|
+
'ordered by priority (highest first). Useful for debugging credential routing.',
|
|
134
|
+
inputSchema: z.object({}),
|
|
135
|
+
async handler(_input, { rules }) {
|
|
136
|
+
const sorted = [...rules].sort((a, b) => b.priority - a.priority);
|
|
137
|
+
return ok({ count: sorted.length, rules: sorted });
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
// ─── health ───────────────────────────────────────────────────────────────────
|
|
141
|
+
const healthTool = {
|
|
142
|
+
name: 'health',
|
|
143
|
+
description: 'Check whether the agent-identity MCP server is healthy and how many credentials/rules are loaded.',
|
|
144
|
+
inputSchema: z.object({}),
|
|
145
|
+
async handler(_input, { store, rules }) {
|
|
146
|
+
const active = await store.listActive();
|
|
147
|
+
return ok({
|
|
148
|
+
status: 'ok',
|
|
149
|
+
credentialsLoaded: active.length,
|
|
150
|
+
rulesLoaded: rules.length,
|
|
151
|
+
timestamp: new Date().toISOString(),
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
// ─── Export all tools ─────────────────────────────────────────────────────────
|
|
156
|
+
export const ALL_TOOLS = [
|
|
157
|
+
resolveCredentialTool,
|
|
158
|
+
resolveMigrationCredentialTool,
|
|
159
|
+
listCredentialsTool,
|
|
160
|
+
listRulesTool,
|
|
161
|
+
healthTool,
|
|
162
|
+
];
|
|
163
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
14
|
+
export { StdioServerTransport };
|
|
15
|
+
/**
|
|
16
|
+
* Start an HTTP + SSE MCP transport.
|
|
17
|
+
*
|
|
18
|
+
* Session lifecycle:
|
|
19
|
+
* 1. Client opens GET /sse — transport creates an SSEServerTransport,
|
|
20
|
+
* connects it to the MCP Server, and starts streaming.
|
|
21
|
+
* 2. Client POSTs tool calls to POST /messages?sessionId=<id>.
|
|
22
|
+
* 3. Transport routes each message to the correct session and replies
|
|
23
|
+
* via the open SSE stream.
|
|
24
|
+
*
|
|
25
|
+
* Returns a cleanup function that closes the HTTP server.
|
|
26
|
+
*/
|
|
27
|
+
export async function startHttpMcpTransport(options) {
|
|
28
|
+
const { server, port = 3002, host = '127.0.0.1', authToken } = options;
|
|
29
|
+
// Session registry: sessionId → active SSEServerTransport
|
|
30
|
+
const sessions = new Map();
|
|
31
|
+
const http = await import('node:http');
|
|
32
|
+
function authenticate(req, res) {
|
|
33
|
+
if (!authToken)
|
|
34
|
+
return true;
|
|
35
|
+
const header = req.headers['authorization'] ?? '';
|
|
36
|
+
if (header === `Bearer ${authToken}`)
|
|
37
|
+
return true;
|
|
38
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
39
|
+
res.end(JSON.stringify({ error: 'Unauthorized — valid Bearer token required' }));
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const httpServer = http.createServer(async (req, res) => {
|
|
43
|
+
const url = new URL(req.url ?? '/', `http://${host}:${port}`);
|
|
44
|
+
// CORS for browser-based MCP clients
|
|
45
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
46
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
47
|
+
res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type');
|
|
48
|
+
if (req.method === 'OPTIONS') {
|
|
49
|
+
res.writeHead(204);
|
|
50
|
+
res.end();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!authenticate(req, res))
|
|
54
|
+
return;
|
|
55
|
+
// ── SSE session init ──────────────────────────────────────────────────
|
|
56
|
+
if (req.method === 'GET' && url.pathname === '/sse') {
|
|
57
|
+
const transport = new SSEServerTransport('/messages', res);
|
|
58
|
+
sessions.set(transport.sessionId, transport);
|
|
59
|
+
req.on('close', () => sessions.delete(transport.sessionId));
|
|
60
|
+
await server.connect(transport);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// ── Incoming tool call message ────────────────────────────────────────
|
|
64
|
+
if (req.method === 'POST' && url.pathname === '/messages') {
|
|
65
|
+
const sessionId = url.searchParams.get('sessionId') ?? '';
|
|
66
|
+
const transport = sessions.get(sessionId);
|
|
67
|
+
if (!transport) {
|
|
68
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
69
|
+
res.end(JSON.stringify({ error: `Session ${sessionId} not found or expired` }));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
await transport.handlePostMessage(req, res);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// ── Health probe (GET /) ──────────────────────────────────────────────
|
|
76
|
+
if (req.method === 'GET' && url.pathname === '/') {
|
|
77
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
78
|
+
res.end(JSON.stringify({ ok: true, sessions: sessions.size, transport: 'http+sse' }));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
res.writeHead(404);
|
|
82
|
+
res.end();
|
|
83
|
+
});
|
|
84
|
+
await new Promise((resolve) => httpServer.listen(port, host, resolve));
|
|
85
|
+
console.error(`[agent-identity-mcp] HTTP+SSE transport listening on http://${host}:${port}`);
|
|
86
|
+
return () => httpServer.close();
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=transports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transports.js","sourceRoot":"","sources":["../../src/transports.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
44
|
+
import type { AuditLogger, Credential, CredentialStore, RoutingRule } from '@datacules/agent-identity';
|
|
45
|
+
import type { AgentIdentityMcpServerOptions } from './types.js';
|
|
46
|
+
export * from './types.js';
|
|
47
|
+
export { ALL_TOOLS } from './tools.js';
|
|
48
|
+
export interface AgentIdentityMcpServerInit {
|
|
49
|
+
/**
|
|
50
|
+
* Credential array (convenience). Wrapped in a MemoryCredentialStore
|
|
51
|
+
* unless `store` is also provided, in which case `store` takes precedence.
|
|
52
|
+
*/
|
|
53
|
+
credentials?: Credential[];
|
|
54
|
+
/** Custom CredentialStore (e.g. VaultCredentialStore, AwsCredentialStore) */
|
|
55
|
+
store?: CredentialStore;
|
|
56
|
+
rules: RoutingRule[];
|
|
57
|
+
logger?: AuditLogger;
|
|
58
|
+
}
|
|
59
|
+
export type AgentIdentityMcpServerConfig = AgentIdentityMcpServerInit & AgentIdentityMcpServerOptions;
|
|
60
|
+
export interface AgentIdentityMcpServerHandle {
|
|
61
|
+
/** MCP Server instance (use to attach additional request handlers if needed) */
|
|
62
|
+
server: Server;
|
|
63
|
+
/** Start the server and connect the configured transport. Resolves when ready. */
|
|
64
|
+
start: () => Promise<void>;
|
|
65
|
+
/** Stop / clean up (closes HTTP server for http transport; no-op for stdio) */
|
|
66
|
+
stop: () => Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create an agent-identity MCP server.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* // stdio (Claude Desktop / Claude Code config)
|
|
73
|
+
* const { start } = createAgentIdentityMcpServer({ credentials, rules });
|
|
74
|
+
* await start();
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // HTTP+SSE (hosted / networked)
|
|
78
|
+
* const { start } = createAgentIdentityMcpServer({
|
|
79
|
+
* credentials, rules, transport: 'http', httpPort: 3002,
|
|
80
|
+
* });
|
|
81
|
+
* await start();
|
|
82
|
+
*/
|
|
83
|
+
export declare function createAgentIdentityMcpServer(config: AgentIdentityMcpServerConfig): AgentIdentityMcpServerHandle;
|
|
84
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAIvG,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAEhE,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,6EAA6E;IAC7E,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,4BAA4B,GAAG,0BAA0B,GAAG,6BAA6B,CAAC;AAEtG,MAAM,WAAW,4BAA4B;IAC3C,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,+EAA+E;IAC/E,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,4BAA4B,GACnC,4BAA4B,CA8E9B"}
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
import { z } from 'zod';
|
|
15
|
+
import type { AuditLogger, CredentialStore, RoutingRule } from '@datacules/agent-identity';
|
|
16
|
+
export interface McpToolDefinition {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
inputSchema: z.ZodTypeAny;
|
|
20
|
+
handler: (input: unknown, deps: ToolDeps) => Promise<McpToolResult>;
|
|
21
|
+
}
|
|
22
|
+
export interface McpToolResult {
|
|
23
|
+
content: Array<{
|
|
24
|
+
type: 'text';
|
|
25
|
+
text: string;
|
|
26
|
+
}>;
|
|
27
|
+
isError?: boolean;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface ToolDeps {
|
|
31
|
+
store: CredentialStore;
|
|
32
|
+
rules: RoutingRule[];
|
|
33
|
+
logger?: AuditLogger;
|
|
34
|
+
}
|
|
35
|
+
export declare const ALL_TOOLS: McpToolDefinition[];
|
|
36
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EACV,WAAW,EAEX,eAAe,EACf,WAAW,EACZ,MAAM,2BAA2B,CAAC;AAqCnC,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC,UAAU,CAAC;IAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;CACrE;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAwID,eAAO,MAAM,SAAS,EAAE,iBAAiB,EAMxC,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
|
+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
14
|
+
export { StdioServerTransport };
|
|
15
|
+
export interface HttpMcpTransportOptions {
|
|
16
|
+
/** MCP Server instance to connect the transport to */
|
|
17
|
+
server: Server;
|
|
18
|
+
/** TCP port to listen on (default: 3002) */
|
|
19
|
+
port?: number;
|
|
20
|
+
/** Bind address (default: '127.0.0.1') */
|
|
21
|
+
host?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Optional static bearer token.
|
|
24
|
+
* Requests without a matching Authorization: Bearer <token> header are
|
|
25
|
+
* rejected with 401. Omit to allow unauthenticated connections (only
|
|
26
|
+
* appropriate on a private network / localhost).
|
|
27
|
+
*/
|
|
28
|
+
authToken?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Start an HTTP + SSE MCP transport.
|
|
32
|
+
*
|
|
33
|
+
* Session lifecycle:
|
|
34
|
+
* 1. Client opens GET /sse — transport creates an SSEServerTransport,
|
|
35
|
+
* connects it to the MCP Server, and starts streaming.
|
|
36
|
+
* 2. Client POSTs tool calls to POST /messages?sessionId=<id>.
|
|
37
|
+
* 3. Transport routes each message to the correct session and replies
|
|
38
|
+
* via the open SSE stream.
|
|
39
|
+
*
|
|
40
|
+
* Returns a cleanup function that closes the HTTP server.
|
|
41
|
+
*/
|
|
42
|
+
export declare function startHttpMcpTransport(options: HttpMcpTransportOptions): Promise<() => void>;
|
|
43
|
+
//# sourceMappingURL=transports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transports.d.ts","sourceRoot":"","sources":["../../src/transports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGxE,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAIhC,MAAM,WAAW,uBAAuB;IACtC,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAoEjG"}
|