@remix-gg/mcp 0.4.3
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 +81 -0
- package/dist/client-helpers/index.d.ts +2 -0
- package/dist/client-helpers/index.d.ts.map +1 -0
- package/dist/client-helpers/index.js +2 -0
- package/dist/client-helpers/index.js.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -0
- package/dist/core/api-client.d.ts +4 -0
- package/dist/core/api-client.d.ts.map +1 -0
- package/dist/core/api-client.js +12 -0
- package/dist/core/api-client.js.map +1 -0
- package/dist/core/config.d.ts +6 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +19 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/skills.d.ts +22 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +49 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/tool-defs.d.ts +12 -0
- package/dist/core/tool-defs.d.ts.map +1 -0
- package/dist/core/tool-defs.js +356 -0
- package/dist/core/tool-defs.js.map +1 -0
- package/dist/core/tools/create-game.d.ts +9 -0
- package/dist/core/tools/create-game.d.ts.map +1 -0
- package/dist/core/tools/create-game.js +21 -0
- package/dist/core/tools/create-game.js.map +1 -0
- package/dist/core/tools/create-shop-item.d.ts +14 -0
- package/dist/core/tools/create-shop-item.d.ts.map +1 -0
- package/dist/core/tools/create-shop-item.js +78 -0
- package/dist/core/tools/create-shop-item.js.map +1 -0
- package/dist/core/tools/delete-shop-item.d.ts +9 -0
- package/dist/core/tools/delete-shop-item.d.ts.map +1 -0
- package/dist/core/tools/delete-shop-item.js +19 -0
- package/dist/core/tools/delete-shop-item.js.map +1 -0
- package/dist/core/tools/generate-image.d.ts +8 -0
- package/dist/core/tools/generate-image.d.ts.map +1 -0
- package/dist/core/tools/generate-image.js +32 -0
- package/dist/core/tools/generate-image.js.map +1 -0
- package/dist/core/tools/generate-sprite-sheet.d.ts +14 -0
- package/dist/core/tools/generate-sprite-sheet.d.ts.map +1 -0
- package/dist/core/tools/generate-sprite-sheet.js +29 -0
- package/dist/core/tools/generate-sprite-sheet.js.map +1 -0
- package/dist/core/tools/helpers.d.ts +60 -0
- package/dist/core/tools/helpers.d.ts.map +1 -0
- package/dist/core/tools/helpers.js +68 -0
- package/dist/core/tools/helpers.js.map +1 -0
- package/dist/core/tools/index.d.ts +13 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +13 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/core/tools/list-shop-items.d.ts +8 -0
- package/dist/core/tools/list-shop-items.d.ts.map +1 -0
- package/dist/core/tools/list-shop-items.js +17 -0
- package/dist/core/tools/list-shop-items.js.map +1 -0
- package/dist/core/tools/update-game.d.ts +11 -0
- package/dist/core/tools/update-game.d.ts.map +1 -0
- package/dist/core/tools/update-game.js +22 -0
- package/dist/core/tools/update-game.js.map +1 -0
- package/dist/core/tools/update-shop-item.d.ts +15 -0
- package/dist/core/tools/update-shop-item.d.ts.map +1 -0
- package/dist/core/tools/update-shop-item.js +24 -0
- package/dist/core/tools/update-shop-item.js.map +1 -0
- package/dist/core/tools/upload-game-asset.d.ts +8 -0
- package/dist/core/tools/upload-game-asset.d.ts.map +1 -0
- package/dist/core/tools/upload-game-asset.js +43 -0
- package/dist/core/tools/upload-game-asset.js.map +1 -0
- package/dist/core/tools/upload-version.d.ts +8 -0
- package/dist/core/tools/upload-version.d.ts.map +1 -0
- package/dist/core/tools/upload-version.js +20 -0
- package/dist/core/tools/upload-version.js.map +1 -0
- package/dist/core/tools/validate-game.d.ts +6 -0
- package/dist/core/tools/validate-game.d.ts.map +1 -0
- package/dist/core/tools/validate-game.js +41 -0
- package/dist/core/tools/validate-game.js.map +1 -0
- package/dist/core/tools.test.d.ts +2 -0
- package/dist/core/tools.test.d.ts.map +1 -0
- package/dist/core/tools.test.js +825 -0
- package/dist/core/tools.test.js.map +1 -0
- package/dist/generated/server-api.d.ts +3673 -0
- package/dist/generated/server-api.d.ts.map +1 -0
- package/dist/generated/server-api.js +2 -0
- package/dist/generated/server-api.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +365 -0
- package/dist/index.js.map +1 -0
- package/dist/server/create-server.d.ts +12 -0
- package/dist/server/create-server.d.ts.map +1 -0
- package/dist/server/create-server.js +29 -0
- package/dist/server/create-server.js.map +1 -0
- package/dist/server/create-server.test.d.ts +2 -0
- package/dist/server/create-server.test.d.ts.map +1 -0
- package/dist/server/create-server.test.js +37 -0
- package/dist/server/create-server.test.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +8 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/lib/config.d.ts +2 -0
- package/dist/server/lib/config.d.ts.map +1 -0
- package/dist/server/lib/config.js +2 -0
- package/dist/server/lib/config.js.map +1 -0
- package/dist/server/resources/skills.d.ts +3 -0
- package/dist/server/resources/skills.d.ts.map +1 -0
- package/dist/server/resources/skills.js +60 -0
- package/dist/server/resources/skills.js.map +1 -0
- package/dist/server/resources/skills.test.d.ts +2 -0
- package/dist/server/resources/skills.test.d.ts.map +1 -0
- package/dist/server/resources/skills.test.js +44 -0
- package/dist/server/resources/skills.test.js.map +1 -0
- package/dist/server/tools/register.d.ts +3 -0
- package/dist/server/tools/register.d.ts.map +1 -0
- package/dist/server/tools/register.js +26 -0
- package/dist/server/tools/register.js.map +1 -0
- package/dist/server/tools/register.test.d.ts +2 -0
- package/dist/server/tools/register.test.d.ts.map +1 -0
- package/dist/server/tools/register.test.js +85 -0
- package/dist/server/tools/register.test.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +31 -0
- package/dist/types.js.map +1 -0
- package/package.json +38 -0
- package/skills/SKILL.md +82 -0
- package/skills/actions/open-game.md +18 -0
- package/skills/workflows/add-image-to-game.md +121 -0
- package/skills/workflows/add-sprite-to-game.md +127 -0
- package/skills/workflows/game-creation.md +124 -0
- package/skills/workflows/implement-multiplayer.md +355 -0
- package/skills/workflows/integrate-save-game.md +135 -0
- package/skills/workflows/manage-shop-items.md +246 -0
- package/skills/workflows/upload-game.md +74 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it, mock } from 'bun:test';
|
|
2
|
+
import { SERVER_INSTRUCTIONS, createServer } from './create-server.js';
|
|
3
|
+
class MockMcpServer {
|
|
4
|
+
definition;
|
|
5
|
+
options;
|
|
6
|
+
static instances = [];
|
|
7
|
+
constructor(definition, options) {
|
|
8
|
+
this.definition = definition;
|
|
9
|
+
this.options = options;
|
|
10
|
+
MockMcpServer.instances.push(this);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const registerAllTools = mock(() => { });
|
|
14
|
+
const registerSkillResources = mock(() => { });
|
|
15
|
+
describe('createServer()', () => {
|
|
16
|
+
it('builds the MCP server with the expected metadata and registrations', () => {
|
|
17
|
+
MockMcpServer.instances = [];
|
|
18
|
+
const server = createServer({
|
|
19
|
+
McpServerClass: MockMcpServer,
|
|
20
|
+
registerTools: registerAllTools,
|
|
21
|
+
registerResources: registerSkillResources,
|
|
22
|
+
});
|
|
23
|
+
expect(MockMcpServer.instances).toHaveLength(1);
|
|
24
|
+
const createdServer = server;
|
|
25
|
+
expect(createdServer).toBe(MockMcpServer.instances[0]);
|
|
26
|
+
expect(createdServer.definition).toEqual({
|
|
27
|
+
name: 'remix-mcp',
|
|
28
|
+
version: '0.1.0',
|
|
29
|
+
});
|
|
30
|
+
expect(createdServer.options).toEqual({
|
|
31
|
+
instructions: SERVER_INSTRUCTIONS,
|
|
32
|
+
});
|
|
33
|
+
expect(registerAllTools).toHaveBeenCalledWith(server);
|
|
34
|
+
expect(registerSkillResources).toHaveBeenCalledWith(server);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=create-server.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-server.test.js","sourceRoot":"","sources":["../../src/server/create-server.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AACrD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtE,MAAM,aAAa;IACjB,UAAU,CAAS;IACnB,OAAO,CAAS;IAChB,MAAM,CAAC,SAAS,GAAoB,EAAE,CAAA;IAEtC,YAAY,UAAmB,EAAE,OAAgB;QAC/C,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACpC,CAAC;;AAGH,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;AACvC,MAAM,sBAAsB,GAAG,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;AAE7C,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,aAAa,CAAC,SAAS,GAAG,EAAE,CAAA;QAE5B,MAAM,MAAM,GAAG,YAAY,CAAC;YAC1B,cAAc,EAAE,aAAsB;YACtC,aAAa,EAAE,gBAAgB;YAC/B,iBAAiB,EAAE,sBAAsB;SAC1C,CAAC,CAAA;QAEF,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;QAC/C,MAAM,aAAa,GAAG,MAAkC,CAAA;QAExD,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;QACtD,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC;YACvC,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,OAAO;SACjB,CAAC,CAAA;QACF,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;YACpC,YAAY,EAAE,mBAAmB;SAClC,CAAC,CAAA;QACF,MAAM,CAAC,gBAAgB,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAA;QACrD,MAAM,CAAC,sBAAsB,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAA;IAC7D,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { createServer } from './create-server.js';
|
|
4
|
+
const server = createServer();
|
|
5
|
+
const transport = new StdioServerTransport();
|
|
6
|
+
// biome-ignore lint/suspicious/noExplicitAny: upstream SDK has duplicate installs with incompatible Transport types
|
|
7
|
+
await server.connect(transport);
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,MAAM,MAAM,GAAG,YAAY,EAAE,CAAA;AAC7B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;AAC5C,oHAAoH;AACpH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAgB,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/server/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/server/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAoB,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../../src/server/resources/skills.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAyCxE,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAsB,QAwCvD"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { loadSkillFile } from '../../core/skills.js';
|
|
2
|
+
const SKILL_WORKFLOWS = {
|
|
3
|
+
'game-creation': {
|
|
4
|
+
file: 'workflows/game-creation.md',
|
|
5
|
+
description: 'Create a new HTML5 game from scratch with Remix scaffolding.',
|
|
6
|
+
},
|
|
7
|
+
'upload-game': {
|
|
8
|
+
file: 'workflows/upload-game.md',
|
|
9
|
+
description: 'Package and upload a finished game to the Remix platform.',
|
|
10
|
+
},
|
|
11
|
+
'implement-multiplayer': {
|
|
12
|
+
file: 'workflows/implement-multiplayer.md',
|
|
13
|
+
description: 'Integrate turn-based multiplayer using the Remix SDK.',
|
|
14
|
+
},
|
|
15
|
+
'integrate-save-game': {
|
|
16
|
+
file: 'workflows/integrate-save-game.md',
|
|
17
|
+
description: 'Add save/load game state using the Remix SDK.',
|
|
18
|
+
},
|
|
19
|
+
'add-image-to-game': {
|
|
20
|
+
file: 'workflows/add-image-to-game.md',
|
|
21
|
+
description: 'Add and manage image assets in a Remix game project.',
|
|
22
|
+
},
|
|
23
|
+
'manage-shop-items': {
|
|
24
|
+
file: 'workflows/manage-shop-items.md',
|
|
25
|
+
description: 'Create, list, update, delete, and integrate shop items.',
|
|
26
|
+
},
|
|
27
|
+
'add-sprite-to-game': {
|
|
28
|
+
file: 'workflows/add-sprite-to-game.md',
|
|
29
|
+
description: 'Generate and integrate animated sprite sheets in a Remix game.',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const SKILL_ACTIONS = {
|
|
33
|
+
'open-game': {
|
|
34
|
+
file: 'actions/open-game.md',
|
|
35
|
+
description: 'Open a game in the Remix Studio for preview and editing.',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
export function registerSkillResources(server, loadFile = loadSkillFile) {
|
|
39
|
+
// Static resource: master skill overview
|
|
40
|
+
server.registerResource('skills-overview', 'skills://overview', {
|
|
41
|
+
description: 'Master overview of all available agent skills for the Remix Platform. ' +
|
|
42
|
+
'Read this first to understand what workflows are available.',
|
|
43
|
+
mimeType: 'text/markdown',
|
|
44
|
+
}, async (uri) => ({
|
|
45
|
+
contents: [{ uri: uri.href, text: loadFile('SKILL.md') }],
|
|
46
|
+
}));
|
|
47
|
+
// Individual workflow skill resources
|
|
48
|
+
for (const [key, { file, description }] of Object.entries(SKILL_WORKFLOWS)) {
|
|
49
|
+
server.registerResource(`skill-${key}`, `skills://${key}`, { description, mimeType: 'text/markdown' }, async (uri) => ({
|
|
50
|
+
contents: [{ uri: uri.href, text: loadFile(file) }],
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
// Individual action skill resources
|
|
54
|
+
for (const [key, { file, description }] of Object.entries(SKILL_ACTIONS)) {
|
|
55
|
+
server.registerResource(`skill-${key}`, `skills://${key}`, { description, mimeType: 'text/markdown' }, async (uri) => ({
|
|
56
|
+
contents: [{ uri: uri.href, text: loadFile(file) }],
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../../src/server/resources/skills.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEpD,MAAM,eAAe,GAA0D;IAC7E,eAAe,EAAE;QACf,IAAI,EAAE,4BAA4B;QAClC,WAAW,EAAE,8DAA8D;KAC5E;IACD,aAAa,EAAE;QACb,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,2DAA2D;KACzE;IACD,uBAAuB,EAAE;QACvB,IAAI,EAAE,oCAAoC;QAC1C,WAAW,EAAE,uDAAuD;KACrE;IACD,qBAAqB,EAAE;QACrB,IAAI,EAAE,kCAAkC;QACxC,WAAW,EAAE,+CAA+C;KAC7D;IACD,mBAAmB,EAAE;QACnB,IAAI,EAAE,gCAAgC;QACtC,WAAW,EAAE,sDAAsD;KACpE;IACD,mBAAmB,EAAE;QACnB,IAAI,EAAE,gCAAgC;QACtC,WAAW,EAAE,yDAAyD;KACvE;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,iCAAiC;QACvC,WAAW,EAAE,gEAAgE;KAC9E;CACF,CAAA;AAED,MAAM,aAAa,GAA0D;IAC3E,WAAW,EAAE;QACX,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,0DAA0D;KACxE;CACF,CAAA;AAED,MAAM,UAAU,sBAAsB,CACpC,MAAiB,EACjB,WAAyC,aAAa;IAEtD,yCAAyC;IACzC,MAAM,CAAC,gBAAgB,CACrB,iBAAiB,EACjB,mBAAmB,EACnB;QACE,WAAW,EACT,wEAAwE;YACxE,6DAA6D;QAC/D,QAAQ,EAAE,eAAe;KAC1B,EACD,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACd,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;KAC1D,CAAC,CACH,CAAA;IAED,sCAAsC;IACtC,KAAK,MAAM,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;QAC3E,MAAM,CAAC,gBAAgB,CACrB,SAAS,GAAG,EAAE,EACd,YAAY,GAAG,EAAE,EACjB,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,EAC1C,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;YACd,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;SACpD,CAAC,CACH,CAAA;IACH,CAAC;IAED,oCAAoC;IACpC,KAAK,MAAM,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACzE,MAAM,CAAC,gBAAgB,CACrB,SAAS,GAAG,EAAE,EACd,YAAY,GAAG,EAAE,EACjB,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,EAC1C,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;YACd,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;SACpD,CAAC,CACH,CAAA;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.test.d.ts","sourceRoot":"","sources":["../../../src/server/resources/skills.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it, mock } from 'bun:test';
|
|
2
|
+
const loadSkillFile = mock((filename) => `contents:${filename}`);
|
|
3
|
+
const { registerSkillResources } = await import('./skills.js');
|
|
4
|
+
describe('registerSkillResources()', () => {
|
|
5
|
+
it('registers overview, workflows, and actions with expected loaders', async () => {
|
|
6
|
+
const registrations = [];
|
|
7
|
+
const server = {
|
|
8
|
+
registerResource: mock((name, uri, metadata, handler) => {
|
|
9
|
+
registrations.push({ name, uri, metadata, handler });
|
|
10
|
+
}),
|
|
11
|
+
};
|
|
12
|
+
registerSkillResources(server, loadSkillFile);
|
|
13
|
+
expect(server.registerResource).toHaveBeenCalledTimes(9);
|
|
14
|
+
expect(registrations.map((registration) => registration.uri)).toEqual([
|
|
15
|
+
'skills://overview',
|
|
16
|
+
'skills://game-creation',
|
|
17
|
+
'skills://upload-game',
|
|
18
|
+
'skills://implement-multiplayer',
|
|
19
|
+
'skills://integrate-save-game',
|
|
20
|
+
'skills://add-image-to-game',
|
|
21
|
+
'skills://manage-shop-items',
|
|
22
|
+
'skills://add-sprite-to-game',
|
|
23
|
+
'skills://open-game',
|
|
24
|
+
]);
|
|
25
|
+
const overviewRegistration = registrations[0];
|
|
26
|
+
const openGameRegistration = registrations[8];
|
|
27
|
+
expect(overviewRegistration).toBeDefined();
|
|
28
|
+
expect(openGameRegistration).toBeDefined();
|
|
29
|
+
if (!overviewRegistration || !openGameRegistration) {
|
|
30
|
+
throw new Error('Missing expected skill registrations');
|
|
31
|
+
}
|
|
32
|
+
const overview = await overviewRegistration.handler(new URL('skills://overview'));
|
|
33
|
+
const openGame = await openGameRegistration.handler(new URL('skills://open-game'));
|
|
34
|
+
expect(loadSkillFile).toHaveBeenCalledWith('SKILL.md');
|
|
35
|
+
expect(loadSkillFile).toHaveBeenCalledWith('actions/open-game.md');
|
|
36
|
+
expect(overview).toEqual({
|
|
37
|
+
contents: [{ uri: 'skills://overview', text: 'contents:SKILL.md' }],
|
|
38
|
+
});
|
|
39
|
+
expect(openGame).toEqual({
|
|
40
|
+
contents: [{ uri: 'skills://open-game', text: 'contents:actions/open-game.md' }],
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=skills.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.test.js","sourceRoot":"","sources":["../../../src/server/resources/skills.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AAErD,MAAM,aAAa,GAAG,IAAI,CAAC,CAAC,QAAgB,EAAE,EAAE,CAAC,YAAY,QAAQ,EAAE,CAAC,CAAA;AACxE,MAAM,EAAE,sBAAsB,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAA;AAE9D,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,aAAa,GAKd,EAAE,CAAA;QAEP,MAAM,MAAM,GAAG;YACb,gBAAgB,EAAE,IAAI,CACpB,CACE,IAAY,EACZ,GAAW,EACX,QAAiC,EACjC,OAAuC,EACvC,EAAE;gBACF,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAA;YACtD,CAAC,CACF;SACF,CAAA;QAED,sBAAsB,CAAC,MAAe,EAAE,aAAa,CAAC,CAAA;QAEtD,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QACxD,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;YACpE,mBAAmB;YACnB,wBAAwB;YACxB,sBAAsB;YACtB,gCAAgC;YAChC,8BAA8B;YAC9B,4BAA4B;YAC5B,4BAA4B;YAC5B,6BAA6B;YAC7B,oBAAoB;SACrB,CAAC,CAAA;QAEF,MAAM,oBAAoB,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;QAC7C,MAAM,oBAAoB,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;QAC7C,MAAM,CAAC,oBAAoB,CAAC,CAAC,WAAW,EAAE,CAAA;QAC1C,MAAM,CAAC,oBAAoB,CAAC,CAAC,WAAW,EAAE,CAAA;QAC1C,IAAI,CAAC,oBAAoB,IAAI,CAAC,oBAAoB,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;QACzD,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAA;QACjF,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAA;QAElF,MAAM,CAAC,aAAa,CAAC,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAA;QACtD,MAAM,CAAC,aAAa,CAAC,CAAC,oBAAoB,CAAC,sBAAsB,CAAC,CAAA;QAClE,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;YACvB,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,mBAAmB,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC;SACpE,CAAC,CAAA;QACF,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;YACvB,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,oBAAoB,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC;SACjF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
export declare function registerAllTools(server: McpServer, toolDefs?: import("../../core/tool-defs.js").ToolDefinition<import("zod").ZodObject<any, import("zod/v4/core").$strip>>[]): void;
|
|
3
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/server/tools/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,iHAAY,QAyBvE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { TOOL_DEFS } from '../../core/tool-defs.js';
|
|
2
|
+
export function registerAllTools(server, toolDefs = TOOL_DEFS) {
|
|
3
|
+
for (const def of toolDefs) {
|
|
4
|
+
server.registerTool(def.name, {
|
|
5
|
+
title: def.title,
|
|
6
|
+
description: def.description,
|
|
7
|
+
inputSchema: def.inputSchema.shape,
|
|
8
|
+
},
|
|
9
|
+
// biome-ignore lint/suspicious/noExplicitAny: MCP callback input is schema-validated at runtime
|
|
10
|
+
async (input) => {
|
|
11
|
+
try {
|
|
12
|
+
const result = await def.execute(input);
|
|
13
|
+
return {
|
|
14
|
+
content: [{ type: 'text', text: def.formatResult(result) }],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: 'text', text: err.message }],
|
|
20
|
+
isError: true,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../src/server/tools/register.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAEnD,MAAM,UAAU,gBAAgB,CAAC,MAAiB,EAAE,QAAQ,GAAG,SAAS;IACtE,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,CAAC,YAAY,CACjB,GAAG,CAAC,IAAI,EACR;YACE,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,WAAW,EAAE,GAAG,CAAC,WAAW,CAAC,KAAK;SACnC;QACD,gGAAgG;QAChG,KAAK,EAAE,KAAU,EAAE,EAAE;YACnB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;gBACvC,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;iBACrE,CAAA;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC;oBAClE,OAAO,EAAE,IAAI;iBACd,CAAA;YACH,CAAC;QACH,CAAC,CACF,CAAA;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.test.d.ts","sourceRoot":"","sources":["../../../src/server/tools/register.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { describe, expect, it, mock } from 'bun:test';
|
|
2
|
+
const executeOk = mock(async () => ({ gameId: 'game-1' }));
|
|
3
|
+
const executeError = mock(async () => {
|
|
4
|
+
throw new Error('boom');
|
|
5
|
+
});
|
|
6
|
+
const formatOk = mock(() => 'formatted success');
|
|
7
|
+
const formatUnused = mock(() => 'formatted error');
|
|
8
|
+
const { registerAllTools } = await import('./register.js');
|
|
9
|
+
const toolDefs = [
|
|
10
|
+
{
|
|
11
|
+
name: 'createGame',
|
|
12
|
+
title: 'Create Game',
|
|
13
|
+
description: 'Creates a game',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
shape: {
|
|
16
|
+
name: { type: 'string' },
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
execute: executeOk,
|
|
20
|
+
formatResult: formatOk,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'uploadVersion',
|
|
24
|
+
title: 'Upload Version',
|
|
25
|
+
description: 'Uploads a version',
|
|
26
|
+
inputSchema: {
|
|
27
|
+
shape: {
|
|
28
|
+
gameId: { type: 'string' },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
execute: executeError,
|
|
32
|
+
formatResult: formatUnused,
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
describe('registerAllTools()', () => {
|
|
36
|
+
it('registers each tool and formats successful executions', async () => {
|
|
37
|
+
const registrations = [];
|
|
38
|
+
const server = {
|
|
39
|
+
registerTool: mock((name, metadata, handler) => {
|
|
40
|
+
registrations.push({ name, metadata, handler });
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
registerAllTools(server, toolDefs);
|
|
44
|
+
expect(server.registerTool).toHaveBeenCalledTimes(2);
|
|
45
|
+
expect(registrations[0]?.name).toBe('createGame');
|
|
46
|
+
expect(registrations[0]?.metadata).toEqual({
|
|
47
|
+
title: 'Create Game',
|
|
48
|
+
description: 'Creates a game',
|
|
49
|
+
inputSchema: {
|
|
50
|
+
name: { type: 'string' },
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const registration = registrations[0];
|
|
54
|
+
expect(registration).toBeDefined();
|
|
55
|
+
if (!registration)
|
|
56
|
+
throw new Error('Missing createGame registration');
|
|
57
|
+
const result = await registration.handler({ name: 'Test Game' });
|
|
58
|
+
expect(executeOk).toHaveBeenCalledWith({ name: 'Test Game' });
|
|
59
|
+
expect(formatOk).toHaveBeenCalledWith({ gameId: 'game-1' });
|
|
60
|
+
expect(result).toEqual({
|
|
61
|
+
content: [{ type: 'text', text: 'formatted success' }],
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
it('returns MCP error payloads when tool execution throws', async () => {
|
|
65
|
+
const registrations = [];
|
|
66
|
+
const server = {
|
|
67
|
+
registerTool: mock((_name, _metadata, handler) => {
|
|
68
|
+
registrations.push({ handler });
|
|
69
|
+
}),
|
|
70
|
+
};
|
|
71
|
+
registerAllTools(server, toolDefs);
|
|
72
|
+
const registration = registrations[1];
|
|
73
|
+
expect(registration).toBeDefined();
|
|
74
|
+
if (!registration)
|
|
75
|
+
throw new Error('Missing uploadVersion registration');
|
|
76
|
+
const result = await registration.handler({ gameId: 'game-1' });
|
|
77
|
+
expect(executeError).toHaveBeenCalledWith({ gameId: 'game-1' });
|
|
78
|
+
expect(formatUnused).not.toHaveBeenCalled();
|
|
79
|
+
expect(result).toEqual({
|
|
80
|
+
content: [{ type: 'text', text: 'boom' }],
|
|
81
|
+
isError: true,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=register.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.test.js","sourceRoot":"","sources":["../../../src/server/tools/register.test.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration schema for Remix MCP
|
|
4
|
+
*/
|
|
5
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
6
|
+
gameId: z.ZodOptional<z.ZodString>;
|
|
7
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
8
|
+
apiUrl: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
11
|
+
/**
|
|
12
|
+
* Schema for set_config tool input
|
|
13
|
+
*/
|
|
14
|
+
export declare const SetConfigInputSchema: z.ZodObject<{
|
|
15
|
+
gameId: z.ZodOptional<z.ZodString>;
|
|
16
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
17
|
+
apiUrl: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export type SetConfigInput = z.infer<typeof SetConfigInputSchema>;
|
|
20
|
+
/**
|
|
21
|
+
* Schema for upload_version tool input
|
|
22
|
+
*/
|
|
23
|
+
export declare const UploadVersionInputSchema: z.ZodObject<{
|
|
24
|
+
codePath: z.ZodString;
|
|
25
|
+
}, z.core.$strip>;
|
|
26
|
+
export type UploadVersionInput = z.infer<typeof UploadVersionInputSchema>;
|
|
27
|
+
/**
|
|
28
|
+
* Game object returned from API
|
|
29
|
+
*/
|
|
30
|
+
export interface Game {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
createdAt: string;
|
|
34
|
+
updatedAt: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* API response when listing games
|
|
38
|
+
*/
|
|
39
|
+
export interface ListGamesResponse {
|
|
40
|
+
success: boolean;
|
|
41
|
+
games?: Game[];
|
|
42
|
+
error?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Schema for create_game tool input
|
|
46
|
+
*/
|
|
47
|
+
export declare const CreateGameInputSchema: z.ZodObject<{
|
|
48
|
+
name: z.ZodString;
|
|
49
|
+
codePath: z.ZodString;
|
|
50
|
+
}, z.core.$strip>;
|
|
51
|
+
export type CreateGameInput = z.infer<typeof CreateGameInputSchema>;
|
|
52
|
+
/**
|
|
53
|
+
* API response when creating a game
|
|
54
|
+
*/
|
|
55
|
+
export interface CreateGameResponse {
|
|
56
|
+
success: boolean;
|
|
57
|
+
game?: Game;
|
|
58
|
+
error?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* API response when creating a game version
|
|
62
|
+
*/
|
|
63
|
+
export interface UploadVersionResponse {
|
|
64
|
+
success: boolean;
|
|
65
|
+
version?: {
|
|
66
|
+
id: string;
|
|
67
|
+
title: string;
|
|
68
|
+
createdAt: Date;
|
|
69
|
+
};
|
|
70
|
+
error?: string;
|
|
71
|
+
details?: unknown;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;GAEG;AACH,eAAO,MAAM,YAAY;;;;iBAIvB,CAAA;AAEF,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAEjD;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;iBAI/B,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAEjE;;GAEG;AACH,eAAO,MAAM,wBAAwB;;iBAEnC,CAAA;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAEzE;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;iBAGhC,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AAEnE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE;QACR,EAAE,EAAE,MAAM,CAAA;QACV,KAAK,EAAE,MAAM,CAAA;QACb,SAAS,EAAE,IAAI,CAAA;KAChB,CAAA;IACD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration schema for Remix MCP
|
|
4
|
+
*/
|
|
5
|
+
export const ConfigSchema = z.object({
|
|
6
|
+
gameId: z.string().optional(),
|
|
7
|
+
apiKey: z.string().optional(),
|
|
8
|
+
apiUrl: z.string().url().optional(),
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Schema for set_config tool input
|
|
12
|
+
*/
|
|
13
|
+
export const SetConfigInputSchema = z.object({
|
|
14
|
+
gameId: z.string().optional().describe('The game ID to upload versions for'),
|
|
15
|
+
apiKey: z.string().optional().describe('API key for authentication'),
|
|
16
|
+
apiUrl: z.string().url().optional().describe('API URL (defaults to https://remix.gg)'),
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Schema for upload_version tool input
|
|
20
|
+
*/
|
|
21
|
+
export const UploadVersionInputSchema = z.object({
|
|
22
|
+
codePath: z.string().describe('Path to the game code file to upload'),
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Schema for create_game tool input
|
|
26
|
+
*/
|
|
27
|
+
export const CreateGameInputSchema = z.object({
|
|
28
|
+
name: z.string().describe('Name of the game to create'),
|
|
29
|
+
codePath: z.string().describe('Path to the game code file'),
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAA;AAIF;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IAC5E,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;IACpE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;CACvF,CAAC,CAAA;AAIF;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;CACtE,CAAC,CAAA;AAuBF;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;IACvD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;CAC5D,CAAC,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@remix-gg/mcp",
|
|
3
|
+
"version": "0.4.3",
|
|
4
|
+
"description": "MCP server + skills for building remix HTML games",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"files": ["dist", "skills", "README.md"],
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/client-helpers/index.js",
|
|
12
|
+
"./core": "./dist/core/index.js",
|
|
13
|
+
"./server": "./dist/server/index.js",
|
|
14
|
+
"./skills": "./skills/"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"remix-mcp": "./dist/server/index.js"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"generate:server-api-sdk": "bun run scripts/generate-server-api-sdk.ts",
|
|
21
|
+
"prepublishOnly": "tsc",
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"test": "bun test src",
|
|
24
|
+
"dev": "tsx --watch src/server/index.ts",
|
|
25
|
+
"start": "node dist/server/index.js"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@modelcontextprotocol/sdk": "1.26.0",
|
|
29
|
+
"openapi-fetch": "0.17.0",
|
|
30
|
+
"zod": "^4.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^22.10.5",
|
|
34
|
+
"openapi-typescript": "7.13.0",
|
|
35
|
+
"tsx": "^4.21.0",
|
|
36
|
+
"typescript": "5.8.3"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/skills/SKILL.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Remix Platform — Agent Skills
|
|
2
|
+
|
|
3
|
+
You are an agent working with the Remix Platform. This document provides
|
|
4
|
+
the master overview of how to build, validate, and publish HTML games.
|
|
5
|
+
|
|
6
|
+
## Authentication
|
|
7
|
+
|
|
8
|
+
This server requires a Remix API key via the `REMIX_API_KEY` environment variable.
|
|
9
|
+
Keys can be obtained from https://remix.gg.
|
|
10
|
+
|
|
11
|
+
## Game Settings
|
|
12
|
+
|
|
13
|
+
Game IDs are persisted in a `.remix-settings.json` file in the project root.
|
|
14
|
+
|
|
15
|
+
**CRITICAL:** Before calling `createGame` or any tool that requires `gameId`
|
|
16
|
+
or `versionId`, you MUST read this file to check for existing values. If it
|
|
17
|
+
contains `gameId` and `versionId`, **do NOT call `createGame`** — use the
|
|
18
|
+
existing IDs. Calling `createGame` when IDs already exist creates a duplicate.
|
|
19
|
+
|
|
20
|
+
After calling `createGame` (only when no IDs exist), write the returned IDs
|
|
21
|
+
(and game name) back to `.remix-settings.json` so they persist across sessions.
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"gameId": "uuid-here",
|
|
26
|
+
"versionId": "uuid-here",
|
|
27
|
+
"name": "My Game"
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Available Tools
|
|
32
|
+
|
|
33
|
+
- **validateGame** — Validates an HTML game file for common issues (missing
|
|
34
|
+
DOCTYPE, viewport, inline handlers, infinite loops). Takes a `filePath` — do
|
|
35
|
+
NOT read the game file yourself. Call this before uploading.
|
|
36
|
+
- **createGame** — Creates a new game on the Remix platform. Takes a `name`,
|
|
37
|
+
returns the game ID and version ID needed for subsequent tool calls.
|
|
38
|
+
- **uploadVersion** — Uploads an HTML game file to an existing game version.
|
|
39
|
+
Requires `gameId` and `versionId` from createGame. Takes a `filePath` — do
|
|
40
|
+
NOT read the game file yourself.
|
|
41
|
+
- **generateImage** — Generates an AI image from a text prompt. Returns base64
|
|
42
|
+
image data as an MCP image content block.
|
|
43
|
+
- **uploadGameAsset** — Uploads a local file as a game asset. Takes a
|
|
44
|
+
`filePath`, returns JSON with the hosted asset URL.
|
|
45
|
+
- **listShopItems** — Lists the current shop items for a game.
|
|
46
|
+
- **createShopItem** — Creates a Bits shop item for a game.
|
|
47
|
+
Use this for consumables, one-time unlocks, and tier unlock items. When
|
|
48
|
+
`iconUrl` is omitted for `CONSUMABLE` or `ONE_TIME`, the tool auto-generates
|
|
49
|
+
and uploads a shop icon.
|
|
50
|
+
- **updateShopItem** — Updates an existing shop item.
|
|
51
|
+
- **deleteShopItem** — Deletes or deactivates an existing shop item.
|
|
52
|
+
- **updateGame** — Updates game settings (name, multiplayer flag). Takes a
|
|
53
|
+
`gameId` and optional `name` and `isMultiplayer` fields.
|
|
54
|
+
|
|
55
|
+
## Available Workflows
|
|
56
|
+
|
|
57
|
+
- **game-creation** — How to create a new game from scratch
|
|
58
|
+
- **upload-game** — How to validate and upload a game to the Remix platform
|
|
59
|
+
- **integrate-save-game** — How to persist and restore game state across sessions
|
|
60
|
+
- **add-image-to-game** — How to generate, upload, and integrate images into a game
|
|
61
|
+
- **manage-shop-items** — How to create, list, update, delete, and consume shop items
|
|
62
|
+
- **implement-multiplayer** — How to integrate turn-based multiplayer into a game
|
|
63
|
+
|
|
64
|
+
## Available Actions
|
|
65
|
+
|
|
66
|
+
- **open-game** — Open a game in the Remix Studio for preview
|
|
67
|
+
|
|
68
|
+
## How to Use
|
|
69
|
+
|
|
70
|
+
**Always load the appropriate workflow or action skill before taking action.**
|
|
71
|
+
Each skill contains the exact steps, tool calls, and validation checks needed.
|
|
72
|
+
Do not act on general knowledge — follow the skill instructions.
|
|
73
|
+
|
|
74
|
+
1. Identify which workflow or action matches the task (see "Available Workflows"
|
|
75
|
+
and "Available Actions" above).
|
|
76
|
+
2. Load it via the `getSkill` resource (e.g. `skills://upload-game`,
|
|
77
|
+
`skills://open-game`).
|
|
78
|
+
3. Follow its step-by-step instructions exactly.
|
|
79
|
+
|
|
80
|
+
If you're unsure which skill to use, start with **game-creation** for
|
|
81
|
+
building games, **upload-game** for deploying them, or **open-game** for
|
|
82
|
+
previewing them in the Remix Studio.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Open Game in Remix Studio
|
|
2
|
+
|
|
3
|
+
Open the current game in the Remix Studio for preview and editing.
|
|
4
|
+
|
|
5
|
+
## Steps
|
|
6
|
+
|
|
7
|
+
1. **Read game settings** from `.remix-settings.json` in the project root.
|
|
8
|
+
Extract `gameId` and `versionId`. If either is missing, tell the user
|
|
9
|
+
they need to create/upload a game first.
|
|
10
|
+
|
|
11
|
+
2. **Construct the Studio URL:**
|
|
12
|
+
```
|
|
13
|
+
https://remix.gg/games/{gameId}/v/{versionId}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
3. **Open in the user's default browser:**
|
|
17
|
+
- macOS: `open <url>`
|
|
18
|
+
- Linux: `xdg-open <url>`
|