melchizedek-agents 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +9 -0
- package/CHANGELOG.md +141 -0
- package/DOCUMENTATION.md +107 -10
- package/ONBOARDING.md +50 -11
- package/QUICKSTART.md +1 -1
- package/README.md +22 -2
- package/config/agents/examples/connectors.yaml +82 -0
- package/config/agents/syndicate.schema.json +324 -26
- package/dist/lib/a2a/app.js +3 -0
- package/dist/lib/a2a/oauthSetup.js +20 -5
- package/dist/lib/chatgpt/adapter.d.ts +50 -0
- package/dist/lib/chatgpt/adapter.js +107 -0
- package/dist/lib/chatgpt/cli.d.ts +23 -0
- package/dist/lib/chatgpt/cli.js +77 -0
- package/dist/lib/chatgpt/oauth.d.ts +103 -0
- package/dist/lib/chatgpt/oauth.js +448 -0
- package/dist/lib/chatgpt/state.d.ts +98 -0
- package/dist/lib/chatgpt/state.js +145 -0
- package/dist/lib/chatgpt/store.d.ts +24 -0
- package/dist/lib/chatgpt/store.js +94 -0
- package/dist/lib/compile.js +40 -25
- package/dist/lib/dispatch.js +6 -2
- package/dist/lib/doctor.d.ts +34 -3
- package/dist/lib/doctor.js +63 -5
- package/dist/lib/loadSyndicate.d.ts +6 -0
- package/dist/lib/mcp/server.d.ts +195 -0
- package/dist/lib/mcp/server.js +564 -0
- package/dist/lib/memory/supabaseMemoryService.js +2 -2
- package/dist/lib/models/adapterResolver.js +6 -1
- package/dist/lib/models/gateway.d.ts +1 -1
- package/dist/lib/models/gateway.js +4 -2
- package/dist/lib/onboarding.d.ts +4 -3
- package/dist/lib/onboarding.js +48 -9
- package/dist/lib/runtime/syndicateTurn.js +30 -16
- package/dist/lib/syndicateSchema.d.ts +146 -8
- package/dist/lib/syndicateSchema.js +118 -11
- package/dist/lib/tools/credentialUses.d.ts +7 -1
- package/dist/lib/tools/credentialUses.js +10 -2
- package/dist/lib/tools/generateImageTool.js +2 -1
- package/dist/lib/tools/inspectImageTool.js +1 -1
- package/dist/lib/tools/mcpToolFactory.d.ts +29 -1
- package/dist/lib/tools/mcpToolFactory.js +71 -25
- package/dist/lib/tools/mcpTransports.d.ts +12 -0
- package/dist/lib/tools/mcpTransports.js +11 -0
- package/dist/lib/tools/oauthConsent.d.ts +18 -3
- package/dist/lib/tools/oauthConsent.js +49 -14
- package/dist/lib/tools/oauthDiscovery.d.ts +202 -0
- package/dist/lib/tools/oauthDiscovery.js +616 -0
- package/dist/lib/tools/oauthTools.d.ts +44 -12
- package/dist/lib/tools/oauthTools.js +79 -22
- package/dist/lib/tools/xApiSearchTool.js +2 -2
- package/dist/scripts/a2a_server.js +8 -0
- package/dist/scripts/assistant_worker.js +11 -1
- package/dist/scripts/doctor.d.ts +1 -1
- package/dist/scripts/doctor.js +1 -1
- package/dist/scripts/mcp_server.d.ts +12 -0
- package/dist/scripts/mcp_server.js +345 -0
- package/dist/scripts/setup.d.ts +2 -0
- package/dist/scripts/setup.js +27 -0
- package/dist/scripts/syndicate_chat.js +12 -3
- package/package.json +5 -2
- package/skills/README.md +2 -2
- package/skills/briefs/skills-readme.md +1 -1
- package/skills/melchizedek-onboard/SKILL.md +28 -6
- package/skills/melchizedek-onboard-keys/SKILL.md +17 -5
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/mcp/server.ts — syndicates served as MCP tools (ADR 0125).
|
|
3
|
+
*
|
|
4
|
+
* `createMcpServer(options)` makes each loaded syndicate one MCP tool, so an
|
|
5
|
+
* MCP client (Claude Code, Codex, a custom connector) asks a syndicate the way
|
|
6
|
+
* it calls any other tool: `{ message, session_id? }` in, the turn's answer
|
|
7
|
+
* out, with the session id it can pass back to continue the conversation.
|
|
8
|
+
* One more tool, `melch_resume`, answers a turn that paused: an approval
|
|
9
|
+
* (`adk_request_confirmation`), a workflow question (`ask_user`), or an OAuth
|
|
10
|
+
* consent.
|
|
11
|
+
*
|
|
12
|
+
* WHY it drives the A2A executor: every turn semantic the A2A server applies
|
|
13
|
+
* (budgets, the concurrency caps, one turn at a time per conversation, the
|
|
14
|
+
* approval answer read from the stored events, the deadline, cancellation,
|
|
15
|
+
* guards and the ledger inside `runSyndicateTurn`, the task record and audit
|
|
16
|
+
* event, memory ingestion after the reply) lives in `SyndicateExecutor`
|
|
17
|
+
* (lib/a2a/executor.ts). This module hands it one task per tool call through
|
|
18
|
+
* a collecting event bus and reads the final status back, so the MCP surface
|
|
19
|
+
* cannot drift from the A2A one. Nothing here calls the turn runner itself.
|
|
20
|
+
*
|
|
21
|
+
* Transports: `serveMcpStdio` (one local client over stdin/stdout) and
|
|
22
|
+
* `mcpHttpApp` (Streamable HTTP, stateful sessions, behind the bearer rule
|
|
23
|
+
* the A2A server applies). `mcpBindProblem` is the refusal the bin applies
|
|
24
|
+
* before listening: a bind beyond loopback without a secret never starts.
|
|
25
|
+
*
|
|
26
|
+
* Logging: the executor's lines (task ids by prefix, tool names, counts) and
|
|
27
|
+
* this module's own (tool names, session ids by prefix). A user's message,
|
|
28
|
+
* an answer, a secret or a model key is never logged here.
|
|
29
|
+
*/
|
|
30
|
+
import type { Readable, Writable } from 'node:stream';
|
|
31
|
+
import type { Express } from 'express';
|
|
32
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
33
|
+
import type { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
34
|
+
import type { CompileOptions } from '../compile.ts';
|
|
35
|
+
import type { SyndicateYamlConfig } from '../loadSyndicate.ts';
|
|
36
|
+
import type { SessionService } from '../runtime/sessions.ts';
|
|
37
|
+
import type { MemoryService } from '../runtime/memoryService.ts';
|
|
38
|
+
import type { TurnLock } from '../a2a/turnLock.ts';
|
|
39
|
+
import type { Policy } from '../a2a/policy.ts';
|
|
40
|
+
import type { TaskRecord } from '../observability/metrics.ts';
|
|
41
|
+
import type { AuditSink } from '../observability/audit.ts';
|
|
42
|
+
import type { ToolCredentials } from '../tools/oauthConsent.ts';
|
|
43
|
+
/** The tool that answers a paused turn. Never a syndicate's tool name. */
|
|
44
|
+
export declare const RESUME_TOOL = "melch_resume";
|
|
45
|
+
/** A session id a caller hands back: what this server issues, and A2A context ids. Linear, no nesting. */
|
|
46
|
+
export declare const SESSION_ID_PATTERN: RegExp;
|
|
47
|
+
/** One syndicate this server exposes. */
|
|
48
|
+
export interface McpSyndicate {
|
|
49
|
+
/** The tool name: the file id (`research_desk` for research_desk.yaml), [A-Za-z0-9_-]{1,64}. */
|
|
50
|
+
id: string;
|
|
51
|
+
config: SyndicateYamlConfig;
|
|
52
|
+
}
|
|
53
|
+
/** Who is calling: what the A2A identity middleware would have resolved. */
|
|
54
|
+
export interface McpCaller {
|
|
55
|
+
/** The scope the caller's sessions and memory are stored under. */
|
|
56
|
+
scopeKey: string;
|
|
57
|
+
/** The caller's name for logs, budgets and the per-caller cap ('local', 'shared-secret', …). */
|
|
58
|
+
caller: string;
|
|
59
|
+
/** The request's source address, for the audit trail only. */
|
|
60
|
+
sourceIp?: string;
|
|
61
|
+
}
|
|
62
|
+
/** The stdio caller: this machine's own user. */
|
|
63
|
+
export declare const LOCAL_CALLER: McpCaller;
|
|
64
|
+
export interface McpServerOptions {
|
|
65
|
+
/** THE DELIBERATE ACT OF EXPOSURE: only these syndicates are tools. */
|
|
66
|
+
syndicates: McpSyndicate[];
|
|
67
|
+
/** The MCP server name in the handshake. Default `melchizedek`. */
|
|
68
|
+
name?: string;
|
|
69
|
+
/** The version in the handshake. */
|
|
70
|
+
version?: string;
|
|
71
|
+
/** The conversation store. Default: in process (lost on exit). */
|
|
72
|
+
sessionService?: SessionService;
|
|
73
|
+
/** Long-term memory, for syndicates whose memory_system is long-term. */
|
|
74
|
+
memoryService?: MemoryService;
|
|
75
|
+
/** Model resolution; default the registry on the server's own environment. */
|
|
76
|
+
resolveModel?: CompileOptions['resolveModel'];
|
|
77
|
+
/** Wall-clock budget per turn, ms. 0 or undefined = none. */
|
|
78
|
+
taskTimeoutMs?: number;
|
|
79
|
+
/** Concurrent turns across every syndicate. 0 = unlimited. */
|
|
80
|
+
maxConcurrentTasks?: number;
|
|
81
|
+
/** Concurrent turns for one scope. Default 4 (the A2A default); 0 = unlimited. */
|
|
82
|
+
maxConcurrentPerScope?: number;
|
|
83
|
+
/** Concurrent turns for one caller. Default 0 (unlimited). */
|
|
84
|
+
maxConcurrentPerCaller?: number;
|
|
85
|
+
/** Budgets: admit each turn, record what it spent (lib/a2a/policy.ts). */
|
|
86
|
+
policy?: Policy;
|
|
87
|
+
/** One turn at a time per conversation. Default: a lock in this process. */
|
|
88
|
+
turnLock?: TurnLock;
|
|
89
|
+
turnLockWaitMs?: number;
|
|
90
|
+
/** The sealed per-user tool credentials and the consent step (ADR 0072, ADR 0085). */
|
|
91
|
+
toolCredentials?: ToolCredentials;
|
|
92
|
+
onTaskEnd?: (record: TaskRecord) => void;
|
|
93
|
+
audit?: AuditSink;
|
|
94
|
+
log?: (message: string) => void;
|
|
95
|
+
warn?: (message: string) => void;
|
|
96
|
+
}
|
|
97
|
+
/** What one paused turn waits for, as the executor published it. */
|
|
98
|
+
export type McpPause = {
|
|
99
|
+
kind: 'approval';
|
|
100
|
+
id: string;
|
|
101
|
+
agent: string;
|
|
102
|
+
tool: string;
|
|
103
|
+
args: Record<string, unknown>;
|
|
104
|
+
path?: string[];
|
|
105
|
+
} | {
|
|
106
|
+
kind: 'input';
|
|
107
|
+
id: string;
|
|
108
|
+
node: string;
|
|
109
|
+
message: string;
|
|
110
|
+
options?: unknown[];
|
|
111
|
+
} | {
|
|
112
|
+
kind: 'consent';
|
|
113
|
+
id: string;
|
|
114
|
+
agent: string;
|
|
115
|
+
provider: string;
|
|
116
|
+
authorizationUrl: string;
|
|
117
|
+
scopes?: unknown;
|
|
118
|
+
};
|
|
119
|
+
/** The outcome of one tool call, before it becomes an MCP result. */
|
|
120
|
+
export interface McpTurnOutcome {
|
|
121
|
+
status: 'completed' | 'input-required' | 'failed' | 'canceled' | 'rejected';
|
|
122
|
+
sessionId: string;
|
|
123
|
+
text: string;
|
|
124
|
+
pause?: McpPause;
|
|
125
|
+
}
|
|
126
|
+
export interface MelchMcpServer {
|
|
127
|
+
/** The tools/list entries: one per syndicate, then melch_resume. */
|
|
128
|
+
tools(): Tool[];
|
|
129
|
+
/** A low-level MCP Server for one connection, bound to its caller. */
|
|
130
|
+
connect(caller: McpCaller): Server;
|
|
131
|
+
/** Answers one tools/call; the transports go through `connect`, tests may call it directly. */
|
|
132
|
+
call(caller: McpCaller, name: string, args: unknown, extra?: {
|
|
133
|
+
signal?: AbortSignal;
|
|
134
|
+
onProgress?: (message: string) => void;
|
|
135
|
+
}): Promise<CallToolResult>;
|
|
136
|
+
/** Stop admitting turns; resolve when running ones finish or graceMs passes. Returns how many were canceled. */
|
|
137
|
+
shutdown(graceMs: number): Promise<number>;
|
|
138
|
+
/** Turns running now. */
|
|
139
|
+
readonly inFlight: number;
|
|
140
|
+
}
|
|
141
|
+
/** A syndicate's tool name from its file id: anything outside [A-Za-z0-9_-] becomes `_`. */
|
|
142
|
+
export declare function toolNameFor(fileId: string): string;
|
|
143
|
+
export declare function createMcpServer(options: McpServerOptions): MelchMcpServer;
|
|
144
|
+
/**
|
|
145
|
+
* Serve one local client over stdio. stdout carries the protocol and nothing
|
|
146
|
+
* else: the bin sends every console line to stderr before anything loads.
|
|
147
|
+
*/
|
|
148
|
+
export declare function serveMcpStdio(mcp: MelchMcpServer, opts?: {
|
|
149
|
+
caller?: McpCaller;
|
|
150
|
+
stdin?: Readable;
|
|
151
|
+
stdout?: Writable;
|
|
152
|
+
}): Promise<Server>;
|
|
153
|
+
/**
|
|
154
|
+
* Why a Streamable HTTP bind must not start, or undefined. The A2A server's
|
|
155
|
+
* rule (ADR 0039), without its opt-out: beyond loopback a secret is required.
|
|
156
|
+
*/
|
|
157
|
+
export declare function mcpBindProblem(opts: {
|
|
158
|
+
host: string;
|
|
159
|
+
secret?: string;
|
|
160
|
+
minSecretLength?: number;
|
|
161
|
+
}): string | undefined;
|
|
162
|
+
export interface McpHttpOptions {
|
|
163
|
+
/** The bind address; decides the Host-header check. Default 127.0.0.1. */
|
|
164
|
+
host?: string;
|
|
165
|
+
/** The bound port, for the loopback Host-header check. */
|
|
166
|
+
port?: number;
|
|
167
|
+
/** The bearer every request must present. Required beyond loopback (mcpBindProblem). */
|
|
168
|
+
secret?: string;
|
|
169
|
+
/** Host names a non-loopback bind answers to (the Host header), e.g. ['mcp.example.com']. */
|
|
170
|
+
allowedHosts?: string[];
|
|
171
|
+
/** Requests per minute per client IP (240, as the contract servers). */
|
|
172
|
+
rateLimitPerMinute?: number;
|
|
173
|
+
/** Failed authentications per 15 minutes per IP before the IP is blocked (30, as A2A). */
|
|
174
|
+
authFailureMax?: number;
|
|
175
|
+
/** Express trust proxy (false). */
|
|
176
|
+
trustProxy?: number | boolean | string;
|
|
177
|
+
/** JSON body limit ("1mb"). */
|
|
178
|
+
bodyLimit?: string;
|
|
179
|
+
/** Open MCP sessions at once (256); a new one past it is refused. */
|
|
180
|
+
maxSessions?: number;
|
|
181
|
+
/** A session idle this long is closed (30 minutes). */
|
|
182
|
+
sessionIdleMs?: number;
|
|
183
|
+
/** Mounted before the bearer check (the OAuth consent callback, which a browser reaches). */
|
|
184
|
+
beforeAuth?: (app: Express) => void;
|
|
185
|
+
warn?: (message: string) => void;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Streamable HTTP at POST/GET/DELETE /mcp, stateful (Mcp-Session-Id), so a
|
|
189
|
+
* cancellation reaches the request it names. Order: the Host check (DNS
|
|
190
|
+
* rebinding on loopback), the request rate limit, the failed-auth limiter,
|
|
191
|
+
* the constant-time bearer check, the body parser, then the transport.
|
|
192
|
+
*/
|
|
193
|
+
export declare function mcpHttpApp(mcp: MelchMcpServer, opts?: McpHttpOptions): Express & {
|
|
194
|
+
closeSessions: () => Promise<void>;
|
|
195
|
+
};
|