@realmkeep/mcp 0.0.0-stage → 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 CHANGED
@@ -1,3 +1,43 @@
1
- # Temporary Holding Version
1
+ # @realmkeep/mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Lets an AI assistant (Claude, Codex, Cursor) fill in a Realmkeep game listing for you.
4
+ It creates and edits a **draft**. You open the link it gives you, add images and press
5
+ Submit, so the site's spam and review checks still apply.
6
+
7
+ 1. Create an account-wide API key at `/dashboard/api-keys`. Keys scoped to one listing
8
+ cannot create new listings.
9
+ 2. Add the server to your client.
10
+
11
+ Claude Code:
12
+
13
+ ```
14
+ claude mcp add realmkeep -e REALMKEEP_API_KEY=rk_your_key -- npx -y @realmkeep/mcp
15
+ ```
16
+
17
+ Claude Desktop, Cursor and others, in the client's MCP config:
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "realmkeep": {
23
+ "command": "npx",
24
+ "args": ["-y", "@realmkeep/mcp"],
25
+ "env": { "REALMKEEP_API_KEY": "rk_your_key" }
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ Codex, in `~/.codex/config.toml`:
32
+
33
+ ```toml
34
+ [mcp_servers.realmkeep]
35
+ command = "npx"
36
+ args = ["-y", "@realmkeep/mcp"]
37
+ env = { REALMKEEP_API_KEY = "rk_your_key" }
38
+ ```
39
+
40
+ `REALMKEEP_URL` overrides the site address (default `https://realmkeep.gg`).
41
+
42
+ Tools: `list_taxonomy`, `list_listings`, `create_listing_draft`, `get_listing`,
43
+ `update_listing`. The API is documented at `/docs/api`.
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,15 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { createClient } from './client.js';
4
+ import { createServer } from './server.js';
5
+ const apiKey = process.env['REALMKEEP_API_KEY'];
6
+ if (apiKey === undefined || apiKey === '') {
7
+ console.error('Set REALMKEEP_API_KEY to an account-wide key from /dashboard/api-keys.');
8
+ process.exit(1);
9
+ }
10
+ const client = createClient({
11
+ apiKey,
12
+ baseUrl: process.env['REALMKEEP_URL'] ?? 'https://realmkeep.gg',
13
+ });
14
+ await createServer(client).connect(new StdioServerTransport());
15
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAC/C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;IAC1C,OAAO,CAAC,KAAK,CAAC,wEAAwE,CAAC,CAAA;IACvF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC;AAED,MAAM,MAAM,GAAG,YAAY,CAAC;IAC1B,MAAM;IACN,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,sBAAsB;CAChE,CAAC,CAAA;AAEF,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAA"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A thin client for the Realmkeep listing API. The MCP server holds no listing logic:
3
+ * every rule (limits, content check, drafts only) lives on the site and comes back as
4
+ * an error message the model can read.
5
+ */
6
+ export interface ClientOptions {
7
+ apiKey: string;
8
+ baseUrl: string;
9
+ fetch?: typeof fetch;
10
+ }
11
+ export declare class RealmkeepError extends Error {
12
+ readonly status: number;
13
+ readonly code: string;
14
+ constructor(status: number, code: string, message: string);
15
+ }
16
+ export interface RealmkeepClient {
17
+ request(method: string, path: string, body?: unknown): Promise<unknown>;
18
+ }
19
+ export declare function createClient(options: ClientOptions): RealmkeepClient;
20
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CACrB;AAED,qBAAa,cAAe,SAAQ,KAAK;IAErC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM;gBADZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM;CAKlB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACxE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,eAAe,CAmCpE"}
package/dist/client.js ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * A thin client for the Realmkeep listing API. The MCP server holds no listing logic:
3
+ * every rule (limits, content check, drafts only) lives on the site and comes back as
4
+ * an error message the model can read.
5
+ */
6
+ export class RealmkeepError extends Error {
7
+ status;
8
+ code;
9
+ constructor(status, code, message) {
10
+ super(message);
11
+ this.status = status;
12
+ this.code = code;
13
+ this.name = 'RealmkeepError';
14
+ }
15
+ }
16
+ export function createClient(options) {
17
+ const doFetch = options.fetch ?? fetch;
18
+ const base = options.baseUrl.replace(/\/$/, '');
19
+ return {
20
+ async request(method, path, body) {
21
+ const response = await doFetch(`${base}/api/v1${path}`, {
22
+ method,
23
+ headers: {
24
+ authorization: `Bearer ${options.apiKey}`,
25
+ 'content-type': 'application/json',
26
+ accept: 'application/json',
27
+ },
28
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
29
+ });
30
+ const text = await response.text();
31
+ let json;
32
+ try {
33
+ json = text === '' ? null : JSON.parse(text);
34
+ }
35
+ catch {
36
+ json = null;
37
+ }
38
+ if (!response.ok) {
39
+ const error = json?.error;
40
+ throw new RealmkeepError(response.status, error?.code ?? 'http_error', error?.message ?? `Realmkeep answered ${response.status}`);
41
+ }
42
+ return json;
43
+ },
44
+ };
45
+ }
46
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAE5B;IACA;IAFX,YACW,MAAc,EACd,IAAY,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAA;QAJL,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAQ;QAIrB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAA;IAC9B,CAAC;CACF;AAMD,MAAM,UAAU,YAAY,CAAC,OAAsB;IACjD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAA;IACtC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAE/C,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI;YAC9B,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,IAAI,UAAU,IAAI,EAAE,EAAE;gBACtD,MAAM;gBACN,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,OAAO,CAAC,MAAM,EAAE;oBACzC,cAAc,EAAE,kBAAkB;oBAClC,MAAM,EAAE,kBAAkB;iBAC3B;gBACD,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;aAC9D,CAAC,CAAA;YAEF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAClC,IAAI,IAAa,CAAA;YACjB,IAAI,CAAC;gBACH,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YAC9C,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,GAAG,IAAI,CAAA;YACb,CAAC;YAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAI,IAA+D,EAAE,KAAK,CAAA;gBACrF,MAAM,IAAI,cAAc,CACtB,QAAQ,CAAC,MAAM,EACf,KAAK,EAAE,IAAI,IAAI,YAAY,EAC3B,KAAK,EAAE,OAAO,IAAI,sBAAsB,QAAQ,CAAC,MAAM,EAAE,CAC1D,CAAA;YACH,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { createClient, RealmkeepError } from './client.js';
2
+ export type { RealmkeepClient } from './client.js';
3
+ export { createServer } from './server.js';
4
+ export { tools } from './tools.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC1D,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { createClient, RealmkeepError } from './client.js';
2
+ export { createServer } from './server.js';
3
+ export { tools } from './tools.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { type RealmkeepClient } from './client.js';
3
+ export declare function createServer(client: RealmkeepClient): McpServer;
4
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAA;AAGlE,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAmB/D"}
package/dist/server.js ADDED
@@ -0,0 +1,22 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { RealmkeepError } from './client.js';
3
+ import { tools } from './tools.js';
4
+ export function createServer(client) {
5
+ const server = new McpServer({ name: 'realmkeep', version: '0.1.0' });
6
+ for (const tool of tools) {
7
+ server.tool(tool.name, tool.description, tool.shape, async (args) => {
8
+ try {
9
+ const result = await tool.run(client, args);
10
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
11
+ }
12
+ catch (error) {
13
+ // The site's message is written to be read: it names the limit, the field, or
14
+ // the reason. Returned as a tool error so the model can correct and retry.
15
+ const message = error instanceof RealmkeepError ? `${error.code}: ${error.message}` : String(error);
16
+ return { isError: true, content: [{ type: 'text', text: message }] };
17
+ }
18
+ });
19
+ }
20
+ return server;
21
+ }
22
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,cAAc,EAAwB,MAAM,aAAa,CAAA;AAClE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,MAAM,UAAU,YAAY,CAAC,MAAuB;IAClD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;IAErE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAClE,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAA+B,CAAC,CAAA;gBACtE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;YACxF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,8EAA8E;gBAC9E,2EAA2E;gBAC3E,MAAM,OAAO,GACX,KAAK,YAAY,cAAc,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBACrF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAA;YAC/E,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod';
2
+ import type { RealmkeepClient } from './client.js';
3
+ export declare const listingFields: {
4
+ readonly name: z.ZodString;
5
+ readonly tagline: z.ZodString;
6
+ readonly descriptionMarkdown: z.ZodString;
7
+ readonly playUrl: z.ZodString;
8
+ readonly contactEmail: z.ZodString;
9
+ readonly launchStatus: z.ZodString;
10
+ readonly nsfw: z.ZodBoolean;
11
+ readonly developerName: z.ZodNullable<z.ZodString>;
12
+ readonly publisherName: z.ZodNullable<z.ZodString>;
13
+ readonly releaseDate: z.ZodNullable<z.ZodString>;
14
+ readonly serverRegion: z.ZodNullable<z.ZodString>;
15
+ readonly languages: z.ZodArray<z.ZodString, "many">;
16
+ readonly monetization: z.ZodNullable<z.ZodString>;
17
+ readonly hasAds: z.ZodBoolean;
18
+ readonly hasIap: z.ZodBoolean;
19
+ readonly payToWin: z.ZodBoolean;
20
+ readonly keyboardOnly: z.ZodBoolean;
21
+ readonly remappableControls: z.ZodBoolean;
22
+ readonly colourblindFriendly: z.ZodBoolean;
23
+ readonly subtitled: z.ZodBoolean;
24
+ readonly noFlashing: z.ZodBoolean;
25
+ readonly ageRatingSystem: z.ZodNullable<z.ZodString>;
26
+ readonly ageRatingValue: z.ZodNullable<z.ZodString>;
27
+ readonly trailerUrl: z.ZodNullable<z.ZodString>;
28
+ readonly serverHost: z.ZodNullable<z.ZodString>;
29
+ readonly serverPort: z.ZodNullable<z.ZodNumber>;
30
+ readonly voteRewardText: z.ZodNullable<z.ZodString>;
31
+ readonly voteCallbackUrl: z.ZodNullable<z.ZodString>;
32
+ readonly redirectAfterVote: z.ZodBoolean;
33
+ readonly links: z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>;
34
+ readonly platforms: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">;
35
+ readonly genres: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">;
36
+ readonly themes: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">;
37
+ readonly engines: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">;
38
+ readonly playerModes: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">;
39
+ };
40
+ export interface Tool {
41
+ name: string;
42
+ description: string;
43
+ shape: z.ZodRawShape;
44
+ run(client: RealmkeepClient, args: Record<string, unknown>): Promise<unknown>;
45
+ }
46
+ export declare const tools: readonly Tool[];
47
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAkBlD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoChB,CAAA;AAEV,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,CAAC,CAAC,WAAW,CAAA;IACpB,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC9E;AAKD,eAAO,MAAM,KAAK,EAAE,SAAS,IAAI,EAiDhC,CAAA"}
package/dist/tools.js ADDED
@@ -0,0 +1,92 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * The tools an AI assistant gets. Each is a single API call and returns the site's own
4
+ * JSON, so what the model sees is what the API says, including the `missing` list and
5
+ * the `dashboardUrl` it should hand to the person.
6
+ */
7
+ const SOCIAL = z
8
+ .record(z.string(), z.string().nullable())
9
+ .describe('Network to URL, for example { "youtube": "https://youtube.com/@x" }. null removes one.');
10
+ const TERMS = z
11
+ .array(z.union([z.string(), z.number()]))
12
+ .describe('Names, slugs or ids from list_taxonomy. A name the site lacks becomes a suggestion.');
13
+ export const listingFields = {
14
+ name: z.string().describe('Game name'),
15
+ tagline: z.string().describe('One line, at least 10 characters'),
16
+ descriptionMarkdown: z.string().describe('Markdown, at least 150 characters of text'),
17
+ playUrl: z.string().describe('https URL where people play or download the game'),
18
+ contactEmail: z.string().describe('Email address for the site to reach the owner'),
19
+ launchStatus: z.string().describe('in_development, alpha, beta, early_access, live or sunset'),
20
+ nsfw: z.boolean().describe('True for adult content'),
21
+ developerName: z.string().nullable(),
22
+ publisherName: z.string().nullable(),
23
+ releaseDate: z.string().nullable().describe('YYYY-MM-DD'),
24
+ serverRegion: z.string().nullable(),
25
+ languages: z.array(z.string()).describe('Language codes'),
26
+ monetization: z.string().nullable(),
27
+ hasAds: z.boolean(),
28
+ hasIap: z.boolean(),
29
+ payToWin: z.boolean(),
30
+ keyboardOnly: z.boolean(),
31
+ remappableControls: z.boolean(),
32
+ colourblindFriendly: z.boolean(),
33
+ subtitled: z.boolean(),
34
+ noFlashing: z.boolean(),
35
+ ageRatingSystem: z.string().nullable().describe('pegi or esrb'),
36
+ ageRatingValue: z.string().nullable(),
37
+ trailerUrl: z.string().nullable().describe('YouTube link or direct .mp4 link'),
38
+ serverHost: z.string().nullable(),
39
+ serverPort: z.number().int().nullable(),
40
+ voteRewardText: z.string().nullable(),
41
+ voteCallbackUrl: z.string().nullable(),
42
+ redirectAfterVote: z.boolean(),
43
+ links: SOCIAL,
44
+ platforms: TERMS,
45
+ genres: TERMS,
46
+ themes: TERMS,
47
+ engines: TERMS.describe('At most one engine'),
48
+ playerModes: TERMS,
49
+ };
50
+ const HAND_OVER = 'You cannot submit a listing or upload images. When the draft is filled in, give the person the dashboardUrl so they can add images and press Submit.';
51
+ export const tools = [
52
+ {
53
+ name: 'list_taxonomy',
54
+ description: 'List the platforms, genres, themes, engines and player modes Realmkeep supports. Call this before choosing terms for a listing.',
55
+ shape: {},
56
+ run: (client) => client.request('GET', '/taxonomy'),
57
+ },
58
+ {
59
+ name: 'list_listings',
60
+ description: "List the account's listings with their id, name and state.",
61
+ shape: {},
62
+ run: (client) => client.request('GET', '/listings'),
63
+ },
64
+ {
65
+ name: 'create_listing_draft',
66
+ description: `Create a new draft listing from a game name. Returns the draft with a missing list. ${HAND_OVER}`,
67
+ shape: { name: listingFields.name },
68
+ run: (client, args) => client.request('POST', '/listings', { name: args['name'] }),
69
+ },
70
+ {
71
+ name: 'get_listing',
72
+ description: 'Read one listing: every field, its links and terms, and what is still missing before it can be submitted.',
73
+ shape: { id: z.string().describe('Listing id') },
74
+ run: (client, args) => client.request('GET', `/listings/${encodeURIComponent(String(args['id']))}`),
75
+ },
76
+ {
77
+ name: 'update_listing',
78
+ description: `Fill in any subset of a draft listing. Send only the fields you want to change; null clears an optional field. Only drafts can be changed. ${HAND_OVER}`,
79
+ shape: {
80
+ id: z.string().describe('Listing id'),
81
+ ...Object.fromEntries(Object.entries(listingFields)
82
+ .filter(([key]) => key !== 'name')
83
+ .map(([key, schema]) => [key, schema.optional()])),
84
+ name: listingFields.name.optional(),
85
+ },
86
+ run: (client, args) => {
87
+ const { id, ...fields } = args;
88
+ return client.request('PATCH', `/listings/${encodeURIComponent(String(id))}`, Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined)));
89
+ },
90
+ },
91
+ ];
92
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,27 @@
1
1
  {
2
2
  "name": "@realmkeep/mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "MCP server that lets an AI assistant fill in a Realmkeep game listing as a draft",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "realmkeep-mcp": "dist/cli.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "!dist/.tsbuildinfo"
14
+ ],
15
+ "dependencies": {
16
+ "@modelcontextprotocol/sdk": "^1.32.2",
17
+ "zod": "^3.24.1"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "license": "UNLICENSED",
23
+ "repository": {
24
+ "type": "git",
25
+ "directory": "packages/mcp"
26
+ }
27
+ }