@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.
- package/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/auth/api-key.d.ts +6 -0
- package/dist/auth/api-key.d.ts.map +1 -0
- package/dist/auth/api-key.js +6 -0
- package/dist/auth/device-auth.d.ts +6 -0
- package/dist/auth/device-auth.d.ts.map +1 -0
- package/dist/auth/device-auth.js +44 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +3 -0
- package/dist/auth/token-store.d.ts +4 -0
- package/dist/auth/token-store.d.ts.map +1 -0
- package/dist/auth/token-store.js +34 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +71 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +13 -0
- package/dist/http-client.d.ts +11 -0
- package/dist/http-client.d.ts.map +1 -0
- package/dist/http-client.js +43 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/openapi/fetcher.d.ts +4 -0
- package/dist/openapi/fetcher.d.ts.map +1 -0
- package/dist/openapi/fetcher.js +13 -0
- package/dist/openapi/index.d.ts +4 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +2 -0
- package/dist/openapi/parser.d.ts +5 -0
- package/dist/openapi/parser.d.ts.map +1 -0
- package/dist/openapi/parser.js +101 -0
- package/dist/openapi/parser.test.d.ts +2 -0
- package/dist/openapi/parser.test.d.ts.map +1 -0
- package/dist/openapi/parser.test.js +188 -0
- package/dist/openapi/types.d.ts +55 -0
- package/dist/openapi/types.d.ts.map +1 -0
- package/dist/openapi/types.js +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +65 -0
- package/dist/tools/generator.d.ts +12 -0
- package/dist/tools/generator.d.ts.map +1 -0
- package/dist/tools/generator.js +154 -0
- package/dist/tools/generator.test.d.ts +2 -0
- package/dist/tools/generator.test.d.ts.map +1 -0
- package/dist/tools/generator.test.js +178 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/overrides.d.ts +6 -0
- package/dist/tools/overrides.d.ts.map +1 -0
- package/dist/tools/overrides.js +29 -0
- package/dist/tools/registry.d.ts +8 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +18 -0
- package/package.json +54 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { generateTools, operationIdToToolName } from './generator.js';
|
|
3
|
+
describe('operationIdToToolName', () => {
|
|
4
|
+
it('converts controller operation IDs to tool names', () => {
|
|
5
|
+
expect(operationIdToToolName('GenerateController_createGeneration')).toBe('generate_create-generation');
|
|
6
|
+
expect(operationIdToToolName('AssetsController_getAll')).toBe('assets_get-all');
|
|
7
|
+
expect(operationIdToToolName('ModelsController_getPublic')).toBe('models_get-public');
|
|
8
|
+
});
|
|
9
|
+
it('kebab-cases multi-word controller names', () => {
|
|
10
|
+
expect(operationIdToToolName('DeviceAuthController_start')).toBe('device-auth_start');
|
|
11
|
+
expect(operationIdToToolName('AssetMetadataController_getAll')).toBe('asset-metadata_get-all');
|
|
12
|
+
});
|
|
13
|
+
it('handles single-part operation IDs', () => {
|
|
14
|
+
expect(operationIdToToolName('createSomething')).toBe('create-something');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe('generateTools', () => {
|
|
18
|
+
const mockHttpClient = {
|
|
19
|
+
request: vi.fn().mockResolvedValue({ id: 'gen-123' }),
|
|
20
|
+
};
|
|
21
|
+
const operations = [
|
|
22
|
+
{
|
|
23
|
+
operationId: 'GenerateController_createGeneration',
|
|
24
|
+
method: 'post',
|
|
25
|
+
path: '/v2/generate',
|
|
26
|
+
summary: 'Create a generation',
|
|
27
|
+
tags: ['Generate'],
|
|
28
|
+
parameters: [],
|
|
29
|
+
requestBody: {
|
|
30
|
+
schema: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {
|
|
33
|
+
model: { type: 'string' },
|
|
34
|
+
},
|
|
35
|
+
required: ['model'],
|
|
36
|
+
},
|
|
37
|
+
contentType: 'application/json',
|
|
38
|
+
},
|
|
39
|
+
isMultipart: false,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
operationId: 'GenerateController_getGeneration',
|
|
43
|
+
method: 'get',
|
|
44
|
+
path: '/v2/generate/{id}',
|
|
45
|
+
summary: 'Get a generation by ID',
|
|
46
|
+
tags: ['Generate'],
|
|
47
|
+
parameters: [
|
|
48
|
+
{
|
|
49
|
+
name: 'id',
|
|
50
|
+
in: 'path',
|
|
51
|
+
required: true,
|
|
52
|
+
schema: { type: 'string' },
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
isMultipart: false,
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
it('generates tools from operations', () => {
|
|
59
|
+
const tools = generateTools(operations, mockHttpClient);
|
|
60
|
+
expect(tools).toHaveLength(2);
|
|
61
|
+
expect(tools[0]?.name).toBe('generate_create-generation');
|
|
62
|
+
expect(tools[1]?.name).toBe('generate_get-generation');
|
|
63
|
+
});
|
|
64
|
+
it('uses overrides for known tools', () => {
|
|
65
|
+
const tools = generateTools(operations, mockHttpClient);
|
|
66
|
+
expect(tools[0]?.description).toContain('lipsync video');
|
|
67
|
+
});
|
|
68
|
+
it('handler calls httpClient with correct params', async () => {
|
|
69
|
+
const tools = generateTools(operations, mockHttpClient);
|
|
70
|
+
await tools[0]?.handler({ model: 'sync-1.9.0-beta' });
|
|
71
|
+
expect(mockHttpClient.request).toHaveBeenCalledWith('post', '/v2/generate', {
|
|
72
|
+
query: undefined,
|
|
73
|
+
body: { model: 'sync-1.9.0-beta' },
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
it('handler substitutes path parameters', async () => {
|
|
77
|
+
const tools = generateTools(operations, mockHttpClient);
|
|
78
|
+
await tools[1]?.handler({ id: 'gen-456' });
|
|
79
|
+
expect(mockHttpClient.request).toHaveBeenCalledWith('get', '/v2/generate/gen-456', {
|
|
80
|
+
query: undefined,
|
|
81
|
+
body: undefined,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
it('handler builds query parameters', async () => {
|
|
85
|
+
const opWithQuery = [
|
|
86
|
+
{
|
|
87
|
+
operationId: 'AssetsController_getAll',
|
|
88
|
+
method: 'get',
|
|
89
|
+
path: '/v2/assets',
|
|
90
|
+
summary: 'List assets',
|
|
91
|
+
tags: ['Assets'],
|
|
92
|
+
parameters: [
|
|
93
|
+
{
|
|
94
|
+
name: 'limit',
|
|
95
|
+
in: 'query',
|
|
96
|
+
required: false,
|
|
97
|
+
schema: { type: 'number' },
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
isMultipart: false,
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
const tools = generateTools(opWithQuery, mockHttpClient);
|
|
104
|
+
await tools[0]?.handler({ limit: 10 });
|
|
105
|
+
expect(mockHttpClient.request).toHaveBeenCalledWith('get', '/v2/assets', {
|
|
106
|
+
query: { limit: '10' },
|
|
107
|
+
body: undefined,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it('handles allOf composed body schemas', async () => {
|
|
111
|
+
const opWithAllOf = [
|
|
112
|
+
{
|
|
113
|
+
operationId: 'TestController_create',
|
|
114
|
+
method: 'post',
|
|
115
|
+
path: '/v2/test',
|
|
116
|
+
summary: 'Test allOf',
|
|
117
|
+
tags: ['Test'],
|
|
118
|
+
parameters: [],
|
|
119
|
+
requestBody: {
|
|
120
|
+
schema: {
|
|
121
|
+
allOf: [
|
|
122
|
+
{
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: { name: { type: 'string' } },
|
|
125
|
+
required: ['name'],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
type: 'object',
|
|
129
|
+
properties: { age: { type: 'number' } },
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
contentType: 'application/json',
|
|
134
|
+
},
|
|
135
|
+
isMultipart: false,
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
const tools = generateTools(opWithAllOf, mockHttpClient);
|
|
139
|
+
await tools[0]?.handler({ name: 'test', age: 25 });
|
|
140
|
+
expect(mockHttpClient.request).toHaveBeenCalledWith('post', '/v2/test', {
|
|
141
|
+
query: undefined,
|
|
142
|
+
body: { name: 'test', age: 25 },
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
it('handles schema with both properties and allOf', async () => {
|
|
146
|
+
const opWithBoth = [
|
|
147
|
+
{
|
|
148
|
+
operationId: 'TestController_mixed',
|
|
149
|
+
method: 'post',
|
|
150
|
+
path: '/v2/mixed',
|
|
151
|
+
summary: 'Test mixed',
|
|
152
|
+
tags: ['Test'],
|
|
153
|
+
parameters: [],
|
|
154
|
+
requestBody: {
|
|
155
|
+
schema: {
|
|
156
|
+
type: 'object',
|
|
157
|
+
properties: { base: { type: 'string' } },
|
|
158
|
+
required: ['base'],
|
|
159
|
+
allOf: [
|
|
160
|
+
{
|
|
161
|
+
type: 'object',
|
|
162
|
+
properties: { extra: { type: 'number' } },
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
contentType: 'application/json',
|
|
167
|
+
},
|
|
168
|
+
isMultipart: false,
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
const tools = generateTools(opWithBoth, mockHttpClient);
|
|
172
|
+
await tools[0]?.handler({ base: 'hello', extra: 42 });
|
|
173
|
+
expect(mockHttpClient.request).toHaveBeenCalledWith('post', '/v2/mixed', {
|
|
174
|
+
query: undefined,
|
|
175
|
+
body: { base: 'hello', extra: 42 },
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { McpToolDefinition } from './generator.js';
|
|
2
|
+
export { generateTools, operationIdToToolName } from './generator.js';
|
|
3
|
+
export { getOverride } from './overrides.js';
|
|
4
|
+
export type { ToolRegistry } from './registry.js';
|
|
5
|
+
export { createToolRegistry } from './registry.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../../src/tools/overrides.ts"],"names":[],"mappings":"AAAA,KAAK,YAAY,GAAG;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAgCF,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEtE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const TOOL_OVERRIDES = {
|
|
2
|
+
'generate_create-generation': {
|
|
3
|
+
description: 'Create a lipsync video. Provide video + audio inputs (URLs or asset IDs). Returns a generation ID — poll with get-generation until status is COMPLETED.',
|
|
4
|
+
},
|
|
5
|
+
'generate_get-generation': {
|
|
6
|
+
description: 'Get generation status/result by ID. Poll until status is COMPLETED. Result includes output video URL.',
|
|
7
|
+
},
|
|
8
|
+
'generations_get-generations': {
|
|
9
|
+
description: 'List generations for the current organization. Supports pagination.',
|
|
10
|
+
},
|
|
11
|
+
generations_estimate: {
|
|
12
|
+
description: 'Estimate the cost of a generation before creating it. Returns estimated credits.',
|
|
13
|
+
},
|
|
14
|
+
'assets_get-all': {
|
|
15
|
+
description: 'List all assets (videos, audio files) in the current organization.',
|
|
16
|
+
},
|
|
17
|
+
assets_get: {
|
|
18
|
+
description: 'Get a specific asset by ID. Returns asset details and URL.',
|
|
19
|
+
},
|
|
20
|
+
'models_get-public': {
|
|
21
|
+
description: 'List all available lipsync models. Use this to discover model IDs for generation.',
|
|
22
|
+
},
|
|
23
|
+
'generate_get-generations': {
|
|
24
|
+
description: 'List recent generations. Use to find generation IDs or check batch status.',
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export function getOverride(toolName) {
|
|
28
|
+
return TOOL_OVERRIDES[toolName];
|
|
29
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { McpToolDefinition } from './generator.js';
|
|
2
|
+
export type ToolRegistry = {
|
|
3
|
+
list: () => McpToolDefinition[];
|
|
4
|
+
get: (name: string) => McpToolDefinition | undefined;
|
|
5
|
+
search: (query: string) => McpToolDefinition[];
|
|
6
|
+
};
|
|
7
|
+
export declare function createToolRegistry(tools: McpToolDefinition[]): ToolRegistry;
|
|
8
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,iBAAiB,EAAE,CAAC;IAChC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,iBAAiB,GAAG,SAAS,CAAC;IACrD,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,iBAAiB,EAAE,CAAC;CAChD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,YAAY,CAsB3E"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function createToolRegistry(tools) {
|
|
2
|
+
const toolMap = new Map();
|
|
3
|
+
for (const tool of tools) {
|
|
4
|
+
toolMap.set(tool.name, tool);
|
|
5
|
+
}
|
|
6
|
+
return {
|
|
7
|
+
list() {
|
|
8
|
+
return [...toolMap.values()];
|
|
9
|
+
},
|
|
10
|
+
get(name) {
|
|
11
|
+
return toolMap.get(name);
|
|
12
|
+
},
|
|
13
|
+
search(query) {
|
|
14
|
+
const lower = query.toLowerCase();
|
|
15
|
+
return [...toolMap.values()].filter((t) => t.name.toLowerCase().includes(lower) || t.description.toLowerCase().includes(lower));
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sync.so/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for the Sync API — give AI agents the ability to create lipsync videos, manage assets, and more",
|
|
5
|
+
"keywords": ["mcp", "sync", "lipsync", "ai", "video", "model-context-protocol"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/synchronicity-labs/mcp-server.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://sync.so/docs",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/synchronicity-labs/mcp-server/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"sync-mcp": "./dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"require": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -b",
|
|
34
|
+
"clean": "rm -rf dist .cache",
|
|
35
|
+
"dev": "tsc -b --watch",
|
|
36
|
+
"lint": "biome check ./src",
|
|
37
|
+
"lint:fix": "biome check --fix ./src",
|
|
38
|
+
"format": "biome format --write ./src",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"typecheck": "tsc --noEmit --emitDeclarationOnly false",
|
|
42
|
+
"prepublishOnly": "npm run build"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
46
|
+
"zod": "^4.1.13"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@biomejs/biome": "^2.3.7",
|
|
50
|
+
"@types/node": "^22.5.0",
|
|
51
|
+
"typescript": "^5.9.3",
|
|
52
|
+
"vitest": "^4.0.12"
|
|
53
|
+
}
|
|
54
|
+
}
|