agent-gateway-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +303 -0
- package/dist/auth.d.ts +9 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +272 -0
- package/dist/auth.js.map +1 -0
- package/dist/caller.d.ts +9 -0
- package/dist/caller.d.ts.map +1 -0
- package/dist/caller.js +110 -0
- package/dist/caller.js.map +1 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +288 -0
- package/dist/config.js.map +1 -0
- package/dist/discovery.d.ts +6 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +97 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +509 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +3 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +206 -0
- package/dist/init.js.map +1 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +27 -0
- package/src/auth.ts +329 -0
- package/src/caller.ts +130 -0
- package/src/config.ts +340 -0
- package/src/discovery.ts +144 -0
- package/src/index.ts +610 -0
- package/src/init.ts +254 -0
- package/src/types.ts +165 -0
- package/tsconfig.json +19 -0
package/src/init.ts
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import http from "http";
|
|
4
|
+
import {
|
|
5
|
+
loadConfig,
|
|
6
|
+
saveConfig,
|
|
7
|
+
storeIdentity,
|
|
8
|
+
isInitialized,
|
|
9
|
+
getIdentity,
|
|
10
|
+
syncTokensFromCloud,
|
|
11
|
+
setRegistryUrl,
|
|
12
|
+
} from "./config.js";
|
|
13
|
+
import type { UserIdentity, IdentityProvider } from "./types.js";
|
|
14
|
+
|
|
15
|
+
const IDENTITY_PROVIDERS: Record<IdentityProvider, string> = {
|
|
16
|
+
google: "Google",
|
|
17
|
+
github: "GitHub",
|
|
18
|
+
microsoft: "Microsoft",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
interface InitOptions {
|
|
22
|
+
registry?: string;
|
|
23
|
+
provider?: IdentityProvider;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parseArgs(): InitOptions {
|
|
27
|
+
const args = process.argv.slice(2);
|
|
28
|
+
const opts: InitOptions = {};
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < args.length; i++) {
|
|
31
|
+
if (args[i] === "--registry" && args[i + 1]) {
|
|
32
|
+
opts.registry = args[i + 1];
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
if (args[i] === "--provider" && args[i + 1]) {
|
|
36
|
+
opts.provider = args[i + 1] as IdentityProvider;
|
|
37
|
+
i++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return opts;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function init(): Promise<void> {
|
|
45
|
+
const opts = parseArgs();
|
|
46
|
+
|
|
47
|
+
if (opts.registry) {
|
|
48
|
+
setRegistryUrl(opts.registry);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const config = loadConfig();
|
|
52
|
+
const registryUrl = config.registry_url;
|
|
53
|
+
|
|
54
|
+
console.log("");
|
|
55
|
+
console.log(" ╔══════════════════════════════════════╗");
|
|
56
|
+
console.log(" ║ Agent Gateway — Setup ║");
|
|
57
|
+
console.log(" ╚══════════════════════════════════════╝");
|
|
58
|
+
console.log("");
|
|
59
|
+
|
|
60
|
+
if (isInitialized()) {
|
|
61
|
+
const identity = getIdentity()!;
|
|
62
|
+
console.log(` Already signed in as ${identity.email} (${identity.provider})`);
|
|
63
|
+
console.log("");
|
|
64
|
+
|
|
65
|
+
// Sync connections
|
|
66
|
+
console.log(" Syncing connections from cloud...");
|
|
67
|
+
const sync = await syncTokensFromCloud();
|
|
68
|
+
if (sync.success) {
|
|
69
|
+
console.log(` Synced ${sync.count} new connection(s).`);
|
|
70
|
+
} else {
|
|
71
|
+
console.log(` Sync skipped: ${sync.error}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log("");
|
|
75
|
+
console.log(" You're all set! Your agent can now use the gateway.");
|
|
76
|
+
console.log("");
|
|
77
|
+
printMcpConfig(registryUrl);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Determine provider
|
|
82
|
+
const provider = opts.provider ?? "google";
|
|
83
|
+
const providerName = IDENTITY_PROVIDERS[provider] ?? provider;
|
|
84
|
+
|
|
85
|
+
console.log(` Signing in with ${providerName}...`);
|
|
86
|
+
console.log("");
|
|
87
|
+
|
|
88
|
+
// Start OAuth flow with registry
|
|
89
|
+
const port = config.auth_callback_port;
|
|
90
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
91
|
+
const state = Math.random().toString(36).substring(2, 15);
|
|
92
|
+
|
|
93
|
+
const authUrl = `${registryUrl}/auth/init?provider=${provider}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${state}`;
|
|
94
|
+
|
|
95
|
+
// Start local callback server
|
|
96
|
+
const identityPromise = new Promise<UserIdentity>((resolve, reject) => {
|
|
97
|
+
const timeout = setTimeout(() => {
|
|
98
|
+
server.close();
|
|
99
|
+
reject(new Error("Authentication timeout — no callback received within 120 seconds."));
|
|
100
|
+
}, 120000);
|
|
101
|
+
|
|
102
|
+
const server = http.createServer(async (req, res) => {
|
|
103
|
+
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
104
|
+
|
|
105
|
+
if (url.pathname !== "/callback") {
|
|
106
|
+
res.writeHead(404);
|
|
107
|
+
res.end("Not found");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const error = url.searchParams.get("error");
|
|
112
|
+
if (error) {
|
|
113
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
114
|
+
res.end(html("Authentication Failed", `<p>${error}</p><p>You can close this tab.</p>`));
|
|
115
|
+
clearTimeout(timeout);
|
|
116
|
+
server.close();
|
|
117
|
+
reject(new Error(`Auth error: ${error}`));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const returnedState = url.searchParams.get("state");
|
|
122
|
+
if (returnedState !== state) {
|
|
123
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
124
|
+
res.end(html("Invalid Callback", "<p>State mismatch. Please try again.</p>"));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Registry returns identity data in the callback
|
|
129
|
+
const registryToken = url.searchParams.get("token");
|
|
130
|
+
const refreshToken = url.searchParams.get("refresh_token");
|
|
131
|
+
const email = url.searchParams.get("email");
|
|
132
|
+
const name = url.searchParams.get("name");
|
|
133
|
+
const providerId = url.searchParams.get("provider_id");
|
|
134
|
+
|
|
135
|
+
if (!registryToken || !email || !providerId) {
|
|
136
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
137
|
+
res.end(html("Invalid Callback", "<p>Missing authentication data. Please try again.</p>"));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const identity: UserIdentity = {
|
|
142
|
+
provider,
|
|
143
|
+
provider_id: providerId,
|
|
144
|
+
email,
|
|
145
|
+
name: name ?? undefined,
|
|
146
|
+
registry_token: registryToken,
|
|
147
|
+
registry_refresh_token: refreshToken ?? undefined,
|
|
148
|
+
connected_at: new Date().toISOString(),
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
152
|
+
res.end(html(
|
|
153
|
+
"Connected to Agent Gateway!",
|
|
154
|
+
`<p>Signed in as <strong>${email}</strong></p><p>You can close this tab and return to your terminal.</p>`
|
|
155
|
+
));
|
|
156
|
+
|
|
157
|
+
clearTimeout(timeout);
|
|
158
|
+
server.close();
|
|
159
|
+
resolve(identity);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
server.listen(port, () => {
|
|
163
|
+
// Server ready
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Open browser
|
|
168
|
+
try {
|
|
169
|
+
const open = (await import("open")).default;
|
|
170
|
+
await open(authUrl);
|
|
171
|
+
console.log(" Browser opened for sign-in.");
|
|
172
|
+
} catch {
|
|
173
|
+
console.log(` Open this URL in your browser:`);
|
|
174
|
+
console.log("");
|
|
175
|
+
console.log(` ${authUrl}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
console.log("");
|
|
179
|
+
console.log(` Waiting for sign-in on http://localhost:${port}/callback...`);
|
|
180
|
+
console.log("");
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const identity = await identityPromise;
|
|
184
|
+
storeIdentity(identity);
|
|
185
|
+
|
|
186
|
+
console.log(` Signed in as ${identity.email}`);
|
|
187
|
+
console.log("");
|
|
188
|
+
|
|
189
|
+
// Sync existing connections from cloud
|
|
190
|
+
console.log(" Syncing connections from cloud...");
|
|
191
|
+
const sync = await syncTokensFromCloud();
|
|
192
|
+
if (sync.success) {
|
|
193
|
+
if (sync.count > 0) {
|
|
194
|
+
console.log(` Restored ${sync.count} connection(s) from your account.`);
|
|
195
|
+
} else {
|
|
196
|
+
console.log(" No existing connections found. Start fresh!");
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
console.log(" Cloud sync skipped (will retry on next run).");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
console.log("");
|
|
203
|
+
console.log(" Setup complete! Add this to your MCP client config:");
|
|
204
|
+
console.log("");
|
|
205
|
+
printMcpConfig(registryUrl);
|
|
206
|
+
} catch (err) {
|
|
207
|
+
console.error(` Setup failed: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function printMcpConfig(registryUrl: string): void {
|
|
213
|
+
const config: Record<string, unknown> = {
|
|
214
|
+
mcpServers: {
|
|
215
|
+
gateway: {
|
|
216
|
+
command: "agent-gateway-mcp",
|
|
217
|
+
args: registryUrl !== "https://agentdns.dev"
|
|
218
|
+
? ["--registry", registryUrl]
|
|
219
|
+
: [],
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
console.log(" " + JSON.stringify(config, null, 2).split("\n").join("\n "));
|
|
225
|
+
console.log("");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function html(title: string, body: string): string {
|
|
229
|
+
return `<!DOCTYPE html>
|
|
230
|
+
<html>
|
|
231
|
+
<head>
|
|
232
|
+
<meta charset="utf-8">
|
|
233
|
+
<title>${title}</title>
|
|
234
|
+
<style>
|
|
235
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; background: #0a0a0a; color: #e5e5e5; }
|
|
236
|
+
.card { text-align: center; padding: 3rem; border: 1px solid #262626; border-radius: 12px; background: #171717; max-width: 400px; }
|
|
237
|
+
h2 { color: #10b981; margin-bottom: 1rem; }
|
|
238
|
+
p { color: #a3a3a3; line-height: 1.6; }
|
|
239
|
+
strong { color: #e5e5e5; }
|
|
240
|
+
</style>
|
|
241
|
+
</head>
|
|
242
|
+
<body>
|
|
243
|
+
<div class="card">
|
|
244
|
+
<h2>${title}</h2>
|
|
245
|
+
${body}
|
|
246
|
+
</div>
|
|
247
|
+
</body>
|
|
248
|
+
</html>`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
init().catch((err) => {
|
|
252
|
+
console.error(`Fatal error: ${err}`);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
});
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// ─── Manifest types ──────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
export interface ManifestCapability {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
detail_url: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ManifestAuth {
|
|
10
|
+
type: "oauth2" | "api_key" | "none";
|
|
11
|
+
authorization_url?: string;
|
|
12
|
+
token_url?: string;
|
|
13
|
+
scopes?: string[];
|
|
14
|
+
header?: string;
|
|
15
|
+
prefix?: string;
|
|
16
|
+
setup_url?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ManifestPricing {
|
|
20
|
+
type: "free" | "freemium" | "paid";
|
|
21
|
+
plans?: Array<{ name: string; price: string; limits: string }>;
|
|
22
|
+
plans_url?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface Manifest {
|
|
26
|
+
spec_version: string;
|
|
27
|
+
name: string;
|
|
28
|
+
description: string;
|
|
29
|
+
base_url: string;
|
|
30
|
+
auth: ManifestAuth;
|
|
31
|
+
pricing?: ManifestPricing;
|
|
32
|
+
capabilities: ManifestCapability[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CapabilityDetail {
|
|
36
|
+
name: string;
|
|
37
|
+
description: string;
|
|
38
|
+
endpoint: string;
|
|
39
|
+
method: string;
|
|
40
|
+
parameters: Array<{
|
|
41
|
+
name: string;
|
|
42
|
+
type: string;
|
|
43
|
+
description: string;
|
|
44
|
+
required: boolean;
|
|
45
|
+
example: unknown;
|
|
46
|
+
}>;
|
|
47
|
+
request_example: {
|
|
48
|
+
method: string;
|
|
49
|
+
url: string;
|
|
50
|
+
headers: Record<string, string>;
|
|
51
|
+
body?: unknown;
|
|
52
|
+
};
|
|
53
|
+
response_example: {
|
|
54
|
+
status: number;
|
|
55
|
+
body: unknown;
|
|
56
|
+
};
|
|
57
|
+
auth_scopes?: string[];
|
|
58
|
+
rate_limits?: {
|
|
59
|
+
requests_per_minute?: number;
|
|
60
|
+
daily_limit?: number;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ─── Identity & auth ─────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
export type IdentityProvider = "google" | "github" | "microsoft";
|
|
67
|
+
|
|
68
|
+
export interface UserIdentity {
|
|
69
|
+
provider: IdentityProvider;
|
|
70
|
+
provider_id: string;
|
|
71
|
+
email: string;
|
|
72
|
+
name?: string;
|
|
73
|
+
avatar_url?: string;
|
|
74
|
+
registry_token: string; // JWT from registry after OAuth
|
|
75
|
+
registry_refresh_token?: string;
|
|
76
|
+
connected_at: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface StoredToken {
|
|
80
|
+
domain: string;
|
|
81
|
+
type: "oauth2" | "api_key";
|
|
82
|
+
access_token: string;
|
|
83
|
+
refresh_token?: string;
|
|
84
|
+
expires_at?: number;
|
|
85
|
+
scopes?: string[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface Connection {
|
|
89
|
+
domain: string;
|
|
90
|
+
service_name: string;
|
|
91
|
+
auth_type: string;
|
|
92
|
+
token?: StoredToken;
|
|
93
|
+
subscription?: {
|
|
94
|
+
plan: string;
|
|
95
|
+
status: "active" | "cancelled";
|
|
96
|
+
};
|
|
97
|
+
connected_at: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ─── Cloud sync ──────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
export interface CloudSyncState {
|
|
103
|
+
last_synced_at?: string;
|
|
104
|
+
sync_token?: string; // ETag or version for incremental sync
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface CloudTokenBundle {
|
|
108
|
+
tokens: Record<string, StoredToken>;
|
|
109
|
+
connections: Record<string, Connection>;
|
|
110
|
+
synced_at: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ─── Cache ───────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
export interface CacheEntry<T> {
|
|
116
|
+
data: T;
|
|
117
|
+
cached_at: number;
|
|
118
|
+
ttl: number; // milliseconds
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const CACHE_TTLS = {
|
|
122
|
+
manifest: 24 * 60 * 60 * 1000, // 24 hours
|
|
123
|
+
capability: 60 * 60 * 1000, // 1 hour
|
|
124
|
+
discovery: 15 * 60 * 1000, // 15 minutes
|
|
125
|
+
} as const;
|
|
126
|
+
|
|
127
|
+
// ─── Config ──────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
export interface GatewayConfig {
|
|
130
|
+
registry_url: string;
|
|
131
|
+
auth_callback_port: number;
|
|
132
|
+
identity?: UserIdentity;
|
|
133
|
+
cloud_sync?: CloudSyncState;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ─── Registry API types ──────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
export interface DiscoverResult {
|
|
139
|
+
service: {
|
|
140
|
+
name: string;
|
|
141
|
+
domain: string;
|
|
142
|
+
description: string;
|
|
143
|
+
base_url: string;
|
|
144
|
+
auth_type: string;
|
|
145
|
+
pricing_type: string;
|
|
146
|
+
verified: boolean;
|
|
147
|
+
};
|
|
148
|
+
matching_capabilities: Array<{
|
|
149
|
+
name: string;
|
|
150
|
+
description: string;
|
|
151
|
+
detail_url: string;
|
|
152
|
+
}>;
|
|
153
|
+
all_capabilities: Array<{
|
|
154
|
+
name: string;
|
|
155
|
+
description: string;
|
|
156
|
+
detail_url: string;
|
|
157
|
+
}>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface RegistryDiscoverResponse {
|
|
161
|
+
success: boolean;
|
|
162
|
+
query: string;
|
|
163
|
+
result_count: number;
|
|
164
|
+
data: DiscoverResult[];
|
|
165
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist"]
|
|
19
|
+
}
|