@sync.so/mcp-server 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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +186 -0
  3. package/dist/auth/api-key.d.ts +6 -0
  4. package/dist/auth/api-key.d.ts.map +1 -0
  5. package/dist/auth/api-key.js +6 -0
  6. package/dist/auth/device-auth.d.ts +6 -0
  7. package/dist/auth/device-auth.d.ts.map +1 -0
  8. package/dist/auth/device-auth.js +44 -0
  9. package/dist/auth/index.d.ts +6 -0
  10. package/dist/auth/index.d.ts.map +1 -0
  11. package/dist/auth/index.js +3 -0
  12. package/dist/auth/token-store.d.ts +4 -0
  13. package/dist/auth/token-store.d.ts.map +1 -0
  14. package/dist/auth/token-store.js +34 -0
  15. package/dist/cli.d.ts +3 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +71 -0
  18. package/dist/config.d.ts +9 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +13 -0
  21. package/dist/http-client.d.ts +11 -0
  22. package/dist/http-client.d.ts.map +1 -0
  23. package/dist/http-client.js +43 -0
  24. package/dist/index.d.ts +12 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +6 -0
  27. package/dist/openapi/fetcher.d.ts +4 -0
  28. package/dist/openapi/fetcher.d.ts.map +1 -0
  29. package/dist/openapi/fetcher.js +13 -0
  30. package/dist/openapi/index.d.ts +4 -0
  31. package/dist/openapi/index.d.ts.map +1 -0
  32. package/dist/openapi/index.js +2 -0
  33. package/dist/openapi/parser.d.ts +5 -0
  34. package/dist/openapi/parser.d.ts.map +1 -0
  35. package/dist/openapi/parser.js +101 -0
  36. package/dist/openapi/parser.test.d.ts +2 -0
  37. package/dist/openapi/parser.test.d.ts.map +1 -0
  38. package/dist/openapi/parser.test.js +188 -0
  39. package/dist/openapi/types.d.ts +55 -0
  40. package/dist/openapi/types.d.ts.map +1 -0
  41. package/dist/openapi/types.js +1 -0
  42. package/dist/server.d.ts +4 -0
  43. package/dist/server.d.ts.map +1 -0
  44. package/dist/server.js +65 -0
  45. package/dist/tools/generator.d.ts +12 -0
  46. package/dist/tools/generator.d.ts.map +1 -0
  47. package/dist/tools/generator.js +154 -0
  48. package/dist/tools/generator.test.d.ts +2 -0
  49. package/dist/tools/generator.test.d.ts.map +1 -0
  50. package/dist/tools/generator.test.js +178 -0
  51. package/dist/tools/index.d.ts +6 -0
  52. package/dist/tools/index.d.ts.map +1 -0
  53. package/dist/tools/index.js +3 -0
  54. package/dist/tools/overrides.d.ts +6 -0
  55. package/dist/tools/overrides.d.ts.map +1 -0
  56. package/dist/tools/overrides.js +29 -0
  57. package/dist/tools/registry.d.ts +8 -0
  58. package/dist/tools/registry.d.ts.map +1 -0
  59. package/dist/tools/registry.js +18 -0
  60. package/package.json +54 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Synchronicity Labs, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,186 @@
1
+ # Sync MCP Server
2
+
3
+ An open-source [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server for the [Sync](https://sync.so) API. Gives AI agents — Claude Code, ChatGPT, Cursor, Codex, and any MCP-compatible client — the ability to create lipsync videos, manage assets, check generation status, and more.
4
+
5
+ Tools are **auto-generated from the Sync OpenAPI spec** at startup. As new API endpoints ship, they become available to agents automatically — no server update needed.
6
+
7
+ ## Quick Start
8
+
9
+ ### Claude Code
10
+
11
+ ```bash
12
+ claude mcp add sync -- npx -y @sync.so/mcp-server --api-key YOUR_API_KEY
13
+ ```
14
+
15
+ Or add to `.mcp.json` in your project root:
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "sync": {
21
+ "command": "npx",
22
+ "args": ["-y", "@sync.so/mcp-server"],
23
+ "env": {
24
+ "SYNC_API_KEY": "your-api-key"
25
+ }
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ ### Claude Desktop
32
+
33
+ Add to `claude_desktop_config.json`:
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "sync": {
39
+ "command": "npx",
40
+ "args": ["-y", "@sync.so/mcp-server"],
41
+ "env": {
42
+ "SYNC_API_KEY": "your-api-key"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ ### Cursor
50
+
51
+ Add to `.cursor/mcp.json` in your project:
52
+
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "sync": {
57
+ "command": "npx",
58
+ "args": ["-y", "@sync.so/mcp-server"],
59
+ "env": {
60
+ "SYNC_API_KEY": "your-api-key"
61
+ }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### Without an API key (interactive login)
68
+
69
+ Omit `SYNC_API_KEY` and the server will start a device auth flow on first run:
70
+
71
+ ```json
72
+ {
73
+ "mcpServers": {
74
+ "sync": {
75
+ "command": "npx",
76
+ "args": ["-y", "@sync.so/mcp-server"]
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ You'll be prompted to visit a URL and enter a code. After approval, the token is cached at `~/.config/sync/mcp-credentials.json`.
83
+
84
+ ## Getting an API Key
85
+
86
+ 1. Sign up at [sync.so](https://sync.so)
87
+ 2. Go to your dashboard settings
88
+ 3. Generate an API key
89
+
90
+ See the [authentication guide](https://sync.so/docs/api-reference/guides/authentication) for details.
91
+
92
+ ## Available Tools
93
+
94
+ Tools are dynamically generated from the Sync API. Core tools include:
95
+
96
+ | Tool | Description |
97
+ |------|-------------|
98
+ | `generate_create-generation` | Create a lipsync video from video + audio inputs |
99
+ | `generate_get-generation` | Get generation status — poll until COMPLETED |
100
+ | `models_get-public` | List available lipsync models |
101
+ | `assets_get-all` | List all assets in your organization |
102
+ | `assets_get` | Get a specific asset by ID |
103
+ | `generations_estimate` | Estimate generation cost before creating |
104
+
105
+ Plus every other public endpoint in the [Sync API](https://sync.so/docs/api-reference).
106
+
107
+ ## Example Prompts
108
+
109
+ Once configured, ask your AI agent:
110
+
111
+ - *"List available Sync models"*
112
+ - *"Create a lipsync video with this video URL and audio URL using the lipsync-2 model"*
113
+ - *"Check the status of generation gen-abc123"*
114
+ - *"Show me my recent generations"*
115
+ - *"How much would it cost to generate a 30-second video?"*
116
+
117
+ ## CLI Options
118
+
119
+ ```
120
+ Usage: sync-mcp [options]
121
+
122
+ Options:
123
+ --api-key <key> API key (or set SYNC_API_KEY env var)
124
+ --base-url <url> API base URL (default: https://api.sync.so)
125
+ --transport <type> stdio (default) or http
126
+ --port <port> HTTP port (default: 3002, only with --transport http)
127
+ -h, --help Show this help message
128
+ ```
129
+
130
+ ### Environment Variables
131
+
132
+ | Variable | Description | Default |
133
+ |----------|-------------|---------|
134
+ | `SYNC_API_KEY` | Your Sync API key | — |
135
+ | `SYNC_BASE_URL` | API base URL | `https://api.sync.so` |
136
+
137
+ ## How It Works
138
+
139
+ 1. On startup, the server fetches the OpenAPI spec from `{baseUrl}/api-json`
140
+ 2. Parses all public endpoints into operation definitions
141
+ 3. Converts each operation into an MCP tool with a Zod input schema
142
+ 4. Registers tools on the MCP server
143
+ 5. Each tool call makes an authenticated HTTP request to the Sync API
144
+
145
+ This means **new API endpoints are automatically available** — just restart the MCP server.
146
+
147
+ ## Programmatic Usage
148
+
149
+ You can also use the server as a library:
150
+
151
+ ```typescript
152
+ import { createSyncMcpServer, resolveConfig } from '@sync.so/mcp-server';
153
+
154
+ const config = resolveConfig({ apiKey: 'your-key' });
155
+ const server = await createSyncMcpServer(config);
156
+ ```
157
+
158
+ ## Development
159
+
160
+ ```bash
161
+ # Install dependencies
162
+ npm install
163
+
164
+ # Build
165
+ npm run build
166
+
167
+ # Run tests
168
+ npm test
169
+
170
+ # Lint
171
+ npm run lint
172
+
173
+ # Type check
174
+ npm run typecheck
175
+ ```
176
+
177
+ ## Learn More
178
+
179
+ - [Sync Documentation](https://sync.so/docs) — Full API reference and guides
180
+ - [Sync API Reference](https://sync.so/docs/api-reference) — Endpoint documentation
181
+ - [MCP Protocol](https://modelcontextprotocol.io) — Learn about the Model Context Protocol
182
+ - [Sync Website](https://sync.so) — Sign up and get started
183
+
184
+ ## License
185
+
186
+ MIT
@@ -0,0 +1,6 @@
1
+ export type ApiKeyAuth = {
2
+ type: 'api-key';
3
+ headers: Record<string, string>;
4
+ };
5
+ export declare function createApiKeyAuth(apiKey: string): ApiKeyAuth;
6
+ //# sourceMappingURL=api-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key.d.ts","sourceRoot":"","sources":["../../src/auth/api-key.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAK3D"}
@@ -0,0 +1,6 @@
1
+ export function createApiKeyAuth(apiKey) {
2
+ return {
3
+ type: 'api-key',
4
+ headers: { 'x-api-key': apiKey },
5
+ };
6
+ }
@@ -0,0 +1,6 @@
1
+ export type DeviceAuthToken = {
2
+ type: 'bearer';
3
+ headers: Record<string, string>;
4
+ };
5
+ export declare function performDeviceAuth(baseUrl: string, log: (message: string) => void): Promise<DeviceAuthToken>;
6
+ //# sourceMappingURL=device-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-auth.d.ts","sourceRoot":"","sources":["../../src/auth/device-auth.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAgBF,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC7B,OAAO,CAAC,eAAe,CAAC,CA8B1B"}
@@ -0,0 +1,44 @@
1
+ import { saveToken } from './token-store.js';
2
+ const POLL_INTERVAL_MS = 5000;
3
+ const CLIENT_ID = 'sync-mcp-server';
4
+ export async function performDeviceAuth(baseUrl, log) {
5
+ const startResponse = await fetch(`${baseUrl}/v2/device-auth/start`, {
6
+ method: 'POST',
7
+ headers: { 'Content-Type': 'application/json' },
8
+ body: JSON.stringify({ clientId: CLIENT_ID }),
9
+ });
10
+ if (!startResponse.ok) {
11
+ throw new Error(`Device auth start failed: ${startResponse.status} ${startResponse.statusText}`);
12
+ }
13
+ const { deviceCode, userCode, verificationUri } = (await startResponse.json());
14
+ log(`\nTo authenticate, visit: ${verificationUri}\nEnter code: ${userCode}\n`);
15
+ const { accessToken, expiresAt } = await pollForToken(baseUrl, deviceCode);
16
+ await saveToken(accessToken, expiresAt);
17
+ log('Authentication successful!\n');
18
+ return {
19
+ type: 'bearer',
20
+ headers: {
21
+ Authorization: `Bearer ${accessToken}`,
22
+ 'x-sync-source': 'mcp',
23
+ },
24
+ };
25
+ }
26
+ async function pollForToken(baseUrl, deviceCode) {
27
+ while (true) {
28
+ await sleep(POLL_INTERVAL_MS);
29
+ const pollResponse = await fetch(`${baseUrl}/v2/device-auth/poll?deviceCode=${encodeURIComponent(deviceCode)}`);
30
+ if (!pollResponse.ok) {
31
+ if (pollResponse.status === 404) {
32
+ throw new Error('Device auth code expired. Please try again.');
33
+ }
34
+ throw new Error(`Device auth poll failed: ${pollResponse.status} ${pollResponse.statusText}`);
35
+ }
36
+ const result = (await pollResponse.json());
37
+ if (result.status === 'ready') {
38
+ return { accessToken: result.accessToken, expiresAt: result.expiresAt };
39
+ }
40
+ }
41
+ }
42
+ function sleep(ms) {
43
+ return new Promise((resolve) => setTimeout(resolve, ms));
44
+ }
@@ -0,0 +1,6 @@
1
+ export type { ApiKeyAuth } from './api-key.js';
2
+ export { createApiKeyAuth } from './api-key.js';
3
+ export type { DeviceAuthToken } from './device-auth.js';
4
+ export { performDeviceAuth } from './device-auth.js';
5
+ export { clearToken, loadToken, saveToken } from './token-store.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { createApiKeyAuth } from './api-key.js';
2
+ export { performDeviceAuth } from './device-auth.js';
3
+ export { clearToken, loadToken, saveToken } from './token-store.js';
@@ -0,0 +1,4 @@
1
+ export declare function loadToken(): Promise<string | null>;
2
+ export declare function saveToken(token: string, expiresAt?: string): Promise<void>;
3
+ export declare function clearToken(): Promise<void>;
4
+ //# sourceMappingURL=token-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAYA,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxD;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhF;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAMhD"}
@@ -0,0 +1,34 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ const CREDENTIALS_DIR = path.join(os.homedir(), '.config', 'sync');
5
+ const CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, 'mcp-credentials.json');
6
+ export async function loadToken() {
7
+ try {
8
+ const content = await fs.readFile(CREDENTIALS_FILE, 'utf-8');
9
+ const creds = JSON.parse(content);
10
+ if (creds.expiresAt && new Date(creds.expiresAt) < new Date()) {
11
+ return null;
12
+ }
13
+ return creds.token;
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ export async function saveToken(token, expiresAt) {
20
+ await fs.mkdir(CREDENTIALS_DIR, { recursive: true });
21
+ const creds = { token, expiresAt };
22
+ await fs.writeFile(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), {
23
+ encoding: 'utf-8',
24
+ mode: 0o600,
25
+ });
26
+ }
27
+ export async function clearToken() {
28
+ try {
29
+ await fs.unlink(CREDENTIALS_FILE);
30
+ }
31
+ catch {
32
+ // File doesn't exist, that's fine
33
+ }
34
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { resolveConfig } from './config.js';
4
+ import { createSyncMcpServer } from './server.js';
5
+ async function main() {
6
+ const args = process.argv.slice(2);
7
+ const config = resolveConfig(parseArgs(args));
8
+ const server = await createSyncMcpServer(config);
9
+ if (config.transport === 'stdio') {
10
+ const transport = new StdioServerTransport();
11
+ await server.connect(transport);
12
+ }
13
+ else {
14
+ // HTTP transport - import dynamically to avoid bundling when unused
15
+ const { StreamableHTTPServerTransport } = await import('@modelcontextprotocol/sdk/server/streamableHttp.js');
16
+ const http = await import('node:http');
17
+ const transport = new StreamableHTTPServerTransport({
18
+ sessionIdGenerator: undefined,
19
+ });
20
+ await server.connect(transport);
21
+ const httpServer = http.createServer(async (req, res) => {
22
+ await transport.handleRequest(req, res);
23
+ });
24
+ httpServer.listen(config.port, () => {
25
+ process.stderr.write(`Sync MCP server listening on http://localhost:${config.port}\n`);
26
+ });
27
+ }
28
+ }
29
+ function parseArgs(args) {
30
+ const result = {};
31
+ for (let i = 0; i < args.length; i++) {
32
+ const arg = args[i];
33
+ const next = args[i + 1];
34
+ if (arg === '--api-key' && next) {
35
+ result.apiKey = next;
36
+ i++;
37
+ }
38
+ else if (arg === '--base-url' && next) {
39
+ result.baseUrl = next;
40
+ i++;
41
+ }
42
+ else if (arg === '--transport' && next) {
43
+ result.transport = next;
44
+ i++;
45
+ }
46
+ else if (arg === '--port' && next) {
47
+ result.port = Number.parseInt(next, 10);
48
+ i++;
49
+ }
50
+ else if (arg === '--help' || arg === '-h') {
51
+ printUsage();
52
+ process.exit(0);
53
+ }
54
+ }
55
+ return result;
56
+ }
57
+ function printUsage() {
58
+ process.stderr.write(`Usage: sync-mcp [options]
59
+
60
+ Options:
61
+ --api-key <key> API key (or set SYNC_API_KEY env var)
62
+ --base-url <url> API base URL (default: https://api.sync.so)
63
+ --transport <type> stdio (default) or http
64
+ --port <port> HTTP port (default: 3002, only with --transport http)
65
+ -h, --help Show this help message
66
+ `);
67
+ }
68
+ main().catch((error) => {
69
+ process.stderr.write(`Fatal error: ${error}\n`);
70
+ process.exit(1);
71
+ });
@@ -0,0 +1,9 @@
1
+ export type SyncMcpConfig = {
2
+ apiKey?: string;
3
+ baseUrl: string;
4
+ transport: 'stdio' | 'http';
5
+ port: number;
6
+ };
7
+ export declare const DEFAULT_CONFIG: SyncMcpConfig;
8
+ export declare function resolveConfig(overrides?: Partial<SyncMcpConfig>): SyncMcpConfig;
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,aAI5B,CAAC;AAEF,wBAAgB,aAAa,CAAC,SAAS,GAAE,OAAO,CAAC,aAAa,CAAM,GAAG,aAAa,CAOnF"}
package/dist/config.js ADDED
@@ -0,0 +1,13 @@
1
+ export const DEFAULT_CONFIG = {
2
+ baseUrl: 'https://api.sync.so',
3
+ transport: 'stdio',
4
+ port: 3002,
5
+ };
6
+ export function resolveConfig(overrides = {}) {
7
+ return {
8
+ apiKey: overrides.apiKey ?? process.env.SYNC_API_KEY,
9
+ baseUrl: overrides.baseUrl ?? process.env.SYNC_BASE_URL ?? DEFAULT_CONFIG.baseUrl,
10
+ transport: overrides.transport ?? DEFAULT_CONFIG.transport,
11
+ port: overrides.port ?? DEFAULT_CONFIG.port,
12
+ };
13
+ }
@@ -0,0 +1,11 @@
1
+ type AuthHeaders = Record<string, string>;
2
+ export type HttpClient = {
3
+ request: (method: string, path: string, options?: {
4
+ query?: Record<string, string>;
5
+ body?: unknown;
6
+ headers?: Record<string, string>;
7
+ }) => Promise<unknown>;
8
+ };
9
+ export declare function createHttpClient(baseUrl: string, authHeaders: AuthHeaders): HttpClient;
10
+ export {};
11
+ //# sourceMappingURL=http-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE1C,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,KACE,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,UAAU,CAkDtF"}
@@ -0,0 +1,43 @@
1
+ const SYNC_SOURCE = 'mcp';
2
+ export function createHttpClient(baseUrl, authHeaders) {
3
+ return {
4
+ async request(method, path, options = {}) {
5
+ const url = new URL(path, baseUrl);
6
+ if (options.query) {
7
+ for (const [key, value] of Object.entries(options.query)) {
8
+ if (value !== undefined && value !== '') {
9
+ url.searchParams.set(key, value);
10
+ }
11
+ }
12
+ }
13
+ const headers = {
14
+ ...authHeaders,
15
+ 'x-sync-source': SYNC_SOURCE,
16
+ ...options.headers,
17
+ };
18
+ if (options.body && method !== 'get') {
19
+ headers['Content-Type'] = 'application/json';
20
+ }
21
+ const response = await fetch(url.toString(), {
22
+ method: method.toUpperCase(),
23
+ headers,
24
+ body: options.body ? JSON.stringify(options.body) : undefined,
25
+ });
26
+ const text = await response.text();
27
+ let parsed;
28
+ try {
29
+ parsed = JSON.parse(text);
30
+ }
31
+ catch {
32
+ parsed = text;
33
+ }
34
+ if (!response.ok) {
35
+ const message = typeof parsed === 'object' && parsed !== null && 'message' in parsed
36
+ ? parsed.message
37
+ : text;
38
+ throw new Error(`API request failed: ${response.status} ${response.statusText} - ${message}`);
39
+ }
40
+ return parsed;
41
+ },
42
+ };
43
+ }
@@ -0,0 +1,12 @@
1
+ export type { ApiKeyAuth, DeviceAuthToken } from './auth/index.js';
2
+ export { clearToken, createApiKeyAuth, loadToken, performDeviceAuth, saveToken, } from './auth/index.js';
3
+ export type { SyncMcpConfig } from './config.js';
4
+ export { resolveConfig } from './config.js';
5
+ export type { HttpClient } from './http-client.js';
6
+ export { createHttpClient } from './http-client.js';
7
+ export type { JsonSchema, OpenApiSpec, ParsedOperation, ParsedParameter, } from './openapi/index.js';
8
+ export { fetchSpec, loadSpecFromFile, parseSpec } from './openapi/index.js';
9
+ export { createSyncMcpServer } from './server.js';
10
+ export type { McpToolDefinition, ToolRegistry } from './tools/index.js';
11
+ export { createToolRegistry, generateTools, getOverride, operationIdToToolName, } from './tools/index.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,SAAS,GACV,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EACV,UAAU,EACV,WAAW,EACX,eAAe,EACf,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,qBAAqB,GACtB,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { clearToken, createApiKeyAuth, loadToken, performDeviceAuth, saveToken, } from './auth/index.js';
2
+ export { resolveConfig } from './config.js';
3
+ export { createHttpClient } from './http-client.js';
4
+ export { fetchSpec, loadSpecFromFile, parseSpec } from './openapi/index.js';
5
+ export { createSyncMcpServer } from './server.js';
6
+ export { createToolRegistry, generateTools, getOverride, operationIdToToolName, } from './tools/index.js';
@@ -0,0 +1,4 @@
1
+ import type { OpenApiSpec } from './types.js';
2
+ export declare function fetchSpec(baseUrl: string): Promise<OpenApiSpec>;
3
+ export declare function loadSpecFromFile(path: string): Promise<OpenApiSpec>;
4
+ //# sourceMappingURL=fetcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../src/openapi/fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CASrE;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAIzE"}
@@ -0,0 +1,13 @@
1
+ export async function fetchSpec(baseUrl) {
2
+ const url = `${baseUrl}/api-json`;
3
+ const response = await fetch(url);
4
+ if (!response.ok) {
5
+ throw new Error(`Failed to fetch OpenAPI spec from ${url}: ${response.status} ${response.statusText}`);
6
+ }
7
+ return (await response.json());
8
+ }
9
+ export async function loadSpecFromFile(path) {
10
+ const fs = await import('node:fs/promises');
11
+ const content = await fs.readFile(path, 'utf-8');
12
+ return JSON.parse(content);
13
+ }
@@ -0,0 +1,4 @@
1
+ export { fetchSpec, loadSpecFromFile } from './fetcher.js';
2
+ export { parseSpec, resolveRef, resolveSchemaRefs } from './parser.js';
3
+ export type { JsonSchema, OpenApiSpec, ParsedOperation, ParsedParameter, } from './types.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/openapi/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACvE,YAAY,EACV,UAAU,EACV,WAAW,EACX,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { fetchSpec, loadSpecFromFile } from './fetcher.js';
2
+ export { parseSpec, resolveRef, resolveSchemaRefs } from './parser.js';
@@ -0,0 +1,5 @@
1
+ import type { JsonSchema, OpenApiSpec, ParsedOperation } from './types.js';
2
+ export declare function parseSpec(spec: OpenApiSpec): ParsedOperation[];
3
+ export declare function resolveRef(spec: OpenApiSpec, ref: string): JsonSchema;
4
+ export declare function resolveSchemaRefs(schema: JsonSchema, spec: OpenApiSpec): JsonSchema;
5
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/openapi/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAC;AAI5F,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,EAAE,CAiC9D;AAgDD,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAUrE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CA2BnF"}