@jclvsh/dropspace 2.24.0 → 2.25.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 +2 -0
- package/dist/client.d.ts +3 -1
- package/dist/client.js +19 -5
- package/dist/commands/autopilot.js +3 -0
- package/dist/commands/media.js +3 -0
- package/dist/hosted-transport.d.ts +10 -0
- package/dist/hosted-transport.js +17 -0
- package/dist/hosted.d.ts +1 -6
- package/dist/hosted.js +4 -14
- package/dist/idempotency.d.ts +1 -1
- package/dist/idempotency.js +1 -1
- package/dist/policy.d.ts +36 -0
- package/dist/policy.js +33 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +3 -11
- package/dist/tool-policy.d.ts +8 -19
- package/dist/tool-policy.js +6 -25
- package/dist/tools/autopilot.js +5 -0
- package/dist/tools/media.js +4 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ This installs two binaries:
|
|
|
13
13
|
- `dropspace` — CLI for terminal use
|
|
14
14
|
- `dropspace-mcp` — MCP server for AI agents
|
|
15
15
|
|
|
16
|
+
Imported as a library, the package resolves only through its `exports` map (`.`, `./hosted`, `./tool-policy`, `./policy`, `./package.json`); deep imports of `dist/*` are not supported.
|
|
17
|
+
|
|
16
18
|
## CLI
|
|
17
19
|
|
|
18
20
|
```bash
|
package/dist/client.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface DropspaceClientOptions {
|
|
|
7
7
|
export declare class DropspaceClient {
|
|
8
8
|
private apiKey;
|
|
9
9
|
private baseUrl;
|
|
10
|
-
/** True when the base URL was injected; the network-error hint then names
|
|
10
|
+
/** True when the base URL was injected; the network-error hint then names no URL or env var. */
|
|
11
11
|
private hasInjectedBaseUrl;
|
|
12
12
|
constructor(options?: DropspaceClientOptions);
|
|
13
13
|
get<T>(path: string, params?: Record<string, string>): Promise<T>;
|
|
@@ -25,6 +25,8 @@ export declare class DropspaceClient {
|
|
|
25
25
|
replayed: boolean;
|
|
26
26
|
}>;
|
|
27
27
|
private request;
|
|
28
|
+
/** Read a JSON body; a timeout while the body streams gets the same message as one during fetch. */
|
|
29
|
+
private readJson;
|
|
28
30
|
private send;
|
|
29
31
|
private handleError;
|
|
30
32
|
}
|
package/dist/client.js
CHANGED
|
@@ -2,7 +2,7 @@ const REQUEST_TIMEOUT_MS = 30_000;
|
|
|
2
2
|
export class DropspaceClient {
|
|
3
3
|
apiKey;
|
|
4
4
|
baseUrl;
|
|
5
|
-
/** True when the base URL was injected; the network-error hint then names
|
|
5
|
+
/** True when the base URL was injected; the network-error hint then names no URL or env var. */
|
|
6
6
|
hasInjectedBaseUrl;
|
|
7
7
|
constructor(options = {}) {
|
|
8
8
|
const apiKey = options.apiKey ?? process.env.DROPSPACE_API_KEY;
|
|
@@ -10,7 +10,9 @@ export class DropspaceClient {
|
|
|
10
10
|
throw new Error("DROPSPACE_API_KEY environment variable is required");
|
|
11
11
|
}
|
|
12
12
|
if (!apiKey.startsWith("ds_live_")) {
|
|
13
|
-
|
|
13
|
+
// An injected key came from a hosted caller, who never set the env var.
|
|
14
|
+
const subject = options.apiKey !== undefined ? "api key" : "DROPSPACE_API_KEY";
|
|
15
|
+
throw new Error(`${subject} must start with 'ds_live_'. Get one at https://www.dropspace.dev/settings`);
|
|
14
16
|
}
|
|
15
17
|
this.apiKey = apiKey;
|
|
16
18
|
this.hasInjectedBaseUrl = Boolean(options.baseUrl);
|
|
@@ -78,13 +80,25 @@ export class DropspaceClient {
|
|
|
78
80
|
body: JSON.stringify(body),
|
|
79
81
|
});
|
|
80
82
|
return {
|
|
81
|
-
result:
|
|
83
|
+
result: await this.readJson(response),
|
|
82
84
|
replayed: response.headers.get("Idempotent-Replayed") === "true",
|
|
83
85
|
};
|
|
84
86
|
}
|
|
85
87
|
async request(url, init) {
|
|
86
88
|
const response = await this.send(url, init);
|
|
87
|
-
return (
|
|
89
|
+
return this.readJson(response);
|
|
90
|
+
}
|
|
91
|
+
/** Read a JSON body; a timeout while the body streams gets the same message as one during fetch. */
|
|
92
|
+
async readJson(response) {
|
|
93
|
+
try {
|
|
94
|
+
return (await response.json());
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
98
|
+
throw new Error("request timed out after 30s");
|
|
99
|
+
}
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
88
102
|
}
|
|
89
103
|
async send(url, init) {
|
|
90
104
|
let response;
|
|
@@ -104,7 +118,7 @@ export class DropspaceClient {
|
|
|
104
118
|
}
|
|
105
119
|
if (error instanceof TypeError) {
|
|
106
120
|
throw new Error(this.hasInjectedBaseUrl
|
|
107
|
-
? `network error: ${error.message}
|
|
121
|
+
? `network error reaching the api: ${error.message}`
|
|
108
122
|
: `network error: ${error.message}. check DROPSPACE_API_URL and your connection`);
|
|
109
123
|
}
|
|
110
124
|
throw error;
|
|
@@ -30,6 +30,7 @@ export function registerAutopilotCommands(program, client) {
|
|
|
30
30
|
.option("--run-hour <n>", "local hour the nightly run fires (0-23)")
|
|
31
31
|
.option("--timezone <tz>", "IANA timezone, e.g. America/New_York")
|
|
32
32
|
.option("--product-url <url>", "product link included in posts")
|
|
33
|
+
.option("--product-image <url>", "slideshow reference image (\"none\" clears it)")
|
|
33
34
|
.option("--brief <text>", "product brief (what you're building, who it's for)")
|
|
34
35
|
.option("--pillars <list>", "comma-separated content pillars")
|
|
35
36
|
.option("--min-queue <n>", "ideas to keep queued (1-30)")
|
|
@@ -77,6 +78,8 @@ export function registerAutopilotCommands(program, client) {
|
|
|
77
78
|
body.timezone = opts.timezone;
|
|
78
79
|
if (opts.productUrl)
|
|
79
80
|
body.product_url = opts.productUrl;
|
|
81
|
+
if (opts.productImage)
|
|
82
|
+
body.product_image_url = opts.productImage === "none" ? null : opts.productImage;
|
|
80
83
|
if (opts.brief)
|
|
81
84
|
body.product_brief = opts.brief;
|
|
82
85
|
if (opts.pillars)
|
package/dist/commands/media.js
CHANGED
|
@@ -10,6 +10,7 @@ export function registerMediaCommands(program, client) {
|
|
|
10
10
|
.option("--prompt <text>", "explicit prompt (required for script_video)")
|
|
11
11
|
.option("-d, --description <text>", "product description (used to derive prompt)")
|
|
12
12
|
.option("--reference <url>", "reference image URL")
|
|
13
|
+
.option("--product-image", "use the launch's product page image as the reference (image type)")
|
|
13
14
|
.option("--aspect <ratio>", "aspect ratio (e.g. 1:1, 16:9, 9:16)")
|
|
14
15
|
.option("--duration <n>", "video duration in seconds (4, 6, or 8)")
|
|
15
16
|
.option("--seed <n>", "RNG seed for reproducibility")
|
|
@@ -27,6 +28,8 @@ export function registerMediaCommands(program, client) {
|
|
|
27
28
|
body.product_description = opts.description;
|
|
28
29
|
if (opts.reference)
|
|
29
30
|
body.reference_image_url = opts.reference;
|
|
31
|
+
if (opts.productImage)
|
|
32
|
+
body.use_product_image = true;
|
|
30
33
|
const options = {};
|
|
31
34
|
if (opts.aspect)
|
|
32
35
|
options.aspect_ratio = opts.aspect;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* Serve one MCP request over HTTP. LOCKSTEP across every apps/<app>/packages/mcp.
|
|
4
|
+
*
|
|
5
|
+
* Stateless: a fresh transport per request, no sessions, JSON responses - so it runs on
|
|
6
|
+
* serverless functions where consecutive requests from one client can land on different
|
|
7
|
+
* instances. Only POST is meaningful; the host must not route GET here (a stateless GET
|
|
8
|
+
* opens an event stream that never ends).
|
|
9
|
+
*/
|
|
10
|
+
export declare function serveStatelessMcp(server: McpServer, request: Request): Promise<Response>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
2
|
+
/**
|
|
3
|
+
* Serve one MCP request over HTTP. LOCKSTEP across every apps/<app>/packages/mcp.
|
|
4
|
+
*
|
|
5
|
+
* Stateless: a fresh transport per request, no sessions, JSON responses - so it runs on
|
|
6
|
+
* serverless functions where consecutive requests from one client can land on different
|
|
7
|
+
* instances. Only POST is meaningful; the host must not route GET here (a stateless GET
|
|
8
|
+
* opens an event stream that never ends).
|
|
9
|
+
*/
|
|
10
|
+
export async function serveStatelessMcp(server, request) {
|
|
11
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
12
|
+
sessionIdGenerator: undefined,
|
|
13
|
+
enableJsonResponse: true,
|
|
14
|
+
});
|
|
15
|
+
await server.connect(transport);
|
|
16
|
+
return transport.handleRequest(request);
|
|
17
|
+
}
|
package/dist/hosted.d.ts
CHANGED
|
@@ -6,10 +6,5 @@ export interface HostedMcpOptions {
|
|
|
6
6
|
/** Reported as serverInfo.version */
|
|
7
7
|
version: string;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
* Serve one MCP request over HTTP. Stateless: a fresh server and transport per request,
|
|
11
|
-
* no sessions, JSON responses - so it runs on serverless functions where consecutive
|
|
12
|
-
* requests from one client can land on different instances. Only POST is meaningful;
|
|
13
|
-
* the host must not route GET here (a stateless GET opens an event stream that never ends).
|
|
14
|
-
*/
|
|
9
|
+
/** One hosted MCP request: a per-request client and server over the stateless transport. */
|
|
15
10
|
export declare function handleHostedMcpRequest(request: Request, options: HostedMcpOptions): Promise<Response>;
|
package/dist/hosted.js
CHANGED
|
@@ -1,19 +1,9 @@
|
|
|
1
|
-
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
2
1
|
import { DropspaceClient } from "./client.js";
|
|
2
|
+
import { serveStatelessMcp } from "./hosted-transport.js";
|
|
3
3
|
import { createDropspaceServer } from "./server.js";
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
* no sessions, JSON responses - so it runs on serverless functions where consecutive
|
|
7
|
-
* requests from one client can land on different instances. Only POST is meaningful;
|
|
8
|
-
* the host must not route GET here (a stateless GET opens an event stream that never ends).
|
|
9
|
-
*/
|
|
10
|
-
export async function handleHostedMcpRequest(request, options) {
|
|
4
|
+
/** One hosted MCP request: a per-request client and server over the stateless transport. */
|
|
5
|
+
export function handleHostedMcpRequest(request, options) {
|
|
11
6
|
const client = new DropspaceClient({ apiKey: options.apiKey, baseUrl: options.baseUrl });
|
|
12
7
|
const server = createDropspaceServer({ client, version: options.version, profile: "hosted" });
|
|
13
|
-
|
|
14
|
-
sessionIdGenerator: undefined,
|
|
15
|
-
enableJsonResponse: true,
|
|
16
|
-
});
|
|
17
|
-
await server.connect(transport);
|
|
18
|
-
return transport.handleRequest(request);
|
|
8
|
+
return serveStatelessMcp(server, request);
|
|
19
9
|
}
|
package/dist/idempotency.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* The REST API dedupes a request that repeats a key within 24 hours (per key + endpoint)
|
|
5
5
|
* and answers with the original response plus `Idempotent-Replayed: true`. Deriving the
|
|
6
6
|
* key from the arguments means a retry after a timeout - by an agent, or by a person
|
|
7
|
-
* pressing "try again" in an MCP client - returns the
|
|
7
|
+
* pressing "try again" in an MCP client - returns the record that was already created
|
|
8
8
|
* instead of creating a second one.
|
|
9
9
|
*/
|
|
10
10
|
export declare function contentIdempotencyKey(args: unknown): Promise<string>;
|
package/dist/idempotency.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* The REST API dedupes a request that repeats a key within 24 hours (per key + endpoint)
|
|
5
5
|
* and answers with the original response plus `Idempotent-Replayed: true`. Deriving the
|
|
6
6
|
* key from the arguments means a retry after a timeout - by an agent, or by a person
|
|
7
|
-
* pressing "try again" in an MCP client - returns the
|
|
7
|
+
* pressing "try again" in an MCP client - returns the record that was already created
|
|
8
8
|
* instead of creating a second one.
|
|
9
9
|
*/
|
|
10
10
|
function stable(value) {
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generic tool-policy machinery. LOCKSTEP across every apps/<app>/packages/mcp
|
|
4
|
+
* (byte-identical; scripts/check-lockstep-identity.sh enforces it). The per-app
|
|
5
|
+
* table lives in tool-policy.ts next to this file.
|
|
6
|
+
*
|
|
7
|
+
* class:
|
|
8
|
+
* read - retrieves information; changes nothing.
|
|
9
|
+
* write - changes the app's own data, reversibly; nothing leaves the app.
|
|
10
|
+
* sensitive - publishes, sends, or deletes on an external platform, deletes a record
|
|
11
|
+
* with no user-facing restore, spends money, handles credentials, or
|
|
12
|
+
* switches on automatic sending or posting.
|
|
13
|
+
* hosted: whether the app's hosted endpoint (api.<brand>/mcp) serves the tool.
|
|
14
|
+
* category: the documentation grouping every docs count is computed from.
|
|
15
|
+
*/
|
|
16
|
+
export type ToolClass = "read" | "write" | "sensitive";
|
|
17
|
+
export type ToolProfile = "full" | "hosted";
|
|
18
|
+
export interface ToolPolicy<Category extends string = string> {
|
|
19
|
+
class: ToolClass;
|
|
20
|
+
hosted: boolean;
|
|
21
|
+
category: Category;
|
|
22
|
+
}
|
|
23
|
+
export type PolicyTable<Category extends string = string> = Record<string, ToolPolicy<Category>>;
|
|
24
|
+
export declare function toolPolicyFor<C extends string>(table: PolicyTable<C>, name: string): ToolPolicy<C>;
|
|
25
|
+
export declare function isToolEnabledIn<C extends string>(table: PolicyTable<C>, name: string, profile: ToolProfile): boolean;
|
|
26
|
+
export declare function toolAnnotationsFor<C extends string>(table: PolicyTable<C>, name: string): {
|
|
27
|
+
readOnlyHint: boolean;
|
|
28
|
+
destructiveHint: boolean;
|
|
29
|
+
};
|
|
30
|
+
/** Tool names in a category, in table order. */
|
|
31
|
+
export declare function toolsInCategoryOf<C extends string>(table: PolicyTable<C>, category: C): string[];
|
|
32
|
+
/**
|
|
33
|
+
* A stand-in for McpServer that every register*Tools(server, client) function can be
|
|
34
|
+
* handed. It is the one place a tool is filtered by profile and given its annotations.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createPolicyRegistrar<C extends string>(server: McpServer, table: PolicyTable<C>, profile: ToolProfile): McpServer;
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function toolPolicyFor(table, name) {
|
|
2
|
+
const policy = table[name];
|
|
3
|
+
if (!policy) {
|
|
4
|
+
throw new Error(`tool "${name}" has no entry in tool-policy.ts - classify it before registering`);
|
|
5
|
+
}
|
|
6
|
+
return policy;
|
|
7
|
+
}
|
|
8
|
+
export function isToolEnabledIn(table, name, profile) {
|
|
9
|
+
const { hosted } = toolPolicyFor(table, name);
|
|
10
|
+
return profile === "full" || hosted;
|
|
11
|
+
}
|
|
12
|
+
export function toolAnnotationsFor(table, name) {
|
|
13
|
+
const toolClass = toolPolicyFor(table, name).class;
|
|
14
|
+
return { readOnlyHint: toolClass === "read", destructiveHint: toolClass === "sensitive" };
|
|
15
|
+
}
|
|
16
|
+
/** Tool names in a category, in table order. */
|
|
17
|
+
export function toolsInCategoryOf(table, category) {
|
|
18
|
+
return Object.keys(table).filter((name) => table[name].category === category);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A stand-in for McpServer that every register*Tools(server, client) function can be
|
|
22
|
+
* handed. It is the one place a tool is filtered by profile and given its annotations.
|
|
23
|
+
*/
|
|
24
|
+
export function createPolicyRegistrar(server, table, profile) {
|
|
25
|
+
const registerAnnotated = server.tool.bind(server);
|
|
26
|
+
return {
|
|
27
|
+
tool(name, description, schema, handler) {
|
|
28
|
+
if (!isToolEnabledIn(table, name, profile))
|
|
29
|
+
return;
|
|
30
|
+
registerAnnotated(name, description, schema, toolAnnotationsFor(table, name), handler);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import type { DropspaceClient } from "./client.js";
|
|
3
|
-
import { type ToolProfile } from "./
|
|
3
|
+
import { type ToolProfile } from "./policy.js";
|
|
4
4
|
export interface CreateDropspaceServerOptions {
|
|
5
5
|
client: DropspaceClient;
|
|
6
6
|
version: string;
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import {
|
|
2
|
+
import { createPolicyRegistrar } from "./policy.js";
|
|
3
|
+
import { TOOL_POLICY } from "./tool-policy.js";
|
|
3
4
|
import { registerLaunchTools } from "./tools/launches.js";
|
|
4
5
|
import { registerPersonaTools } from "./tools/personas.js";
|
|
5
6
|
import { registerConnectionTools } from "./tools/connections.js";
|
|
@@ -16,16 +17,7 @@ import { registerRedditTools } from "./tools/reddit.js";
|
|
|
16
17
|
import { registerAudienceTools } from "./tools/audience.js";
|
|
17
18
|
export function createDropspaceServer({ client, version, profile }) {
|
|
18
19
|
const server = new McpServer({ name: "dropspace", version });
|
|
19
|
-
const
|
|
20
|
-
// Every register*Tools function calls server.tool(name, description, schema, handler).
|
|
21
|
-
// This stand-in is the one place a tool is filtered by profile and given its annotations.
|
|
22
|
-
const registrar = {
|
|
23
|
-
tool(name, description, schema, handler) {
|
|
24
|
-
if (!isToolEnabled(name, profile))
|
|
25
|
-
return;
|
|
26
|
-
registerAnnotated(name, description, schema, toolAnnotations(name), handler);
|
|
27
|
-
},
|
|
28
|
-
};
|
|
20
|
+
const registrar = createPolicyRegistrar(server, TOOL_POLICY, profile);
|
|
29
21
|
registerLaunchTools(registrar, client);
|
|
30
22
|
registerPersonaTools(registrar, client);
|
|
31
23
|
registerConnectionTools(registrar, client);
|
package/dist/tool-policy.d.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The one place a tool is classified.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* read - retrieves information; changes nothing.
|
|
6
|
-
* write - changes dropspace data only, reversibly; nothing leaves dropspace.
|
|
7
|
-
* sensitive - publishes, schedules, or deletes on a social platform, deletes a record
|
|
8
|
-
* inside dropspace with no user-facing restore, spends money, handles
|
|
9
|
-
* credentials, or switches on something that will publish later.
|
|
4
|
+
* classes are defined in policy.ts; this file only assigns them.
|
|
10
5
|
*
|
|
11
6
|
* hosted: whether the hosted endpoint (https://api.dropspace.dev/mcp) serves the tool.
|
|
12
7
|
* The stdio server serves every tool. Left off hosted: credits and boosts (payments and
|
|
@@ -19,21 +14,15 @@
|
|
|
19
14
|
* Registering a tool with no entry here throws, and __tests__/server.test.ts fails on
|
|
20
15
|
* any mismatch between this table and what the package registers.
|
|
21
16
|
*/
|
|
22
|
-
|
|
23
|
-
export type
|
|
17
|
+
import { type PolicyTable, type ToolClass, type ToolPolicy, type ToolProfile } from "./policy.js";
|
|
18
|
+
export type { ToolClass, ToolPolicy, ToolProfile };
|
|
24
19
|
export declare const TOOL_CATEGORIES: readonly ["launches", "posts", "personas", "media", "connections", "boosts", "credits", "API keys", "webhooks", "dropspace", "usage", "autopilot", "recommendations", "reddit engagement", "audience"];
|
|
25
20
|
export type ToolCategory = (typeof TOOL_CATEGORIES)[number];
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
export declare const TOOL_POLICY: Record<string, ToolPolicy>;
|
|
32
|
-
export declare function toolPolicy(name: string): ToolPolicy;
|
|
33
|
-
export declare function isToolEnabled(name: string, profile: ToolProfile): boolean;
|
|
34
|
-
export declare function toolAnnotations(name: string): {
|
|
21
|
+
export declare const TOOL_POLICY: PolicyTable<ToolCategory>;
|
|
22
|
+
export declare const toolPolicy: (name: string) => ToolPolicy<"launches" | "personas" | "connections" | "webhooks" | "usage" | "credits" | "media" | "boosts" | "autopilot" | "recommendations" | "audience" | "dropspace" | "posts" | "API keys" | "reddit engagement">;
|
|
23
|
+
export declare const isToolEnabled: (name: string, profile: ToolProfile) => boolean;
|
|
24
|
+
export declare const toolAnnotations: (name: string) => {
|
|
35
25
|
readOnlyHint: boolean;
|
|
36
26
|
destructiveHint: boolean;
|
|
37
27
|
};
|
|
38
|
-
|
|
39
|
-
export declare function toolsInCategory(category: ToolCategory): string[];
|
|
28
|
+
export declare const toolsInCategory: (category: ToolCategory) => string[];
|
package/dist/tool-policy.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The one place a tool is classified.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* read - retrieves information; changes nothing.
|
|
6
|
-
* write - changes dropspace data only, reversibly; nothing leaves dropspace.
|
|
7
|
-
* sensitive - publishes, schedules, or deletes on a social platform, deletes a record
|
|
8
|
-
* inside dropspace with no user-facing restore, spends money, handles
|
|
9
|
-
* credentials, or switches on something that will publish later.
|
|
4
|
+
* classes are defined in policy.ts; this file only assigns them.
|
|
10
5
|
*
|
|
11
6
|
* hosted: whether the hosted endpoint (https://api.dropspace.dev/mcp) serves the tool.
|
|
12
7
|
* The stdio server serves every tool. Left off hosted: credits and boosts (payments and
|
|
@@ -19,6 +14,7 @@
|
|
|
19
14
|
* Registering a tool with no entry here throws, and __tests__/server.test.ts fails on
|
|
20
15
|
* any mismatch between this table and what the package registers.
|
|
21
16
|
*/
|
|
17
|
+
import { isToolEnabledIn, toolAnnotationsFor, toolPolicyFor, toolsInCategoryOf, } from "./policy.js";
|
|
22
18
|
export const TOOL_CATEGORIES = [
|
|
23
19
|
"launches",
|
|
24
20
|
"posts",
|
|
@@ -141,22 +137,7 @@ export const TOOL_POLICY = {
|
|
|
141
137
|
test_webhook: localOnly("webhooks", "write"),
|
|
142
138
|
rotate_webhook_secret: localOnly("webhooks", "sensitive"),
|
|
143
139
|
};
|
|
144
|
-
export
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
return policy;
|
|
150
|
-
}
|
|
151
|
-
export function isToolEnabled(name, profile) {
|
|
152
|
-
const policy = toolPolicy(name);
|
|
153
|
-
return profile === "full" || policy.hosted;
|
|
154
|
-
}
|
|
155
|
-
export function toolAnnotations(name) {
|
|
156
|
-
const toolClass = toolPolicy(name).class;
|
|
157
|
-
return { readOnlyHint: toolClass === "read", destructiveHint: toolClass === "sensitive" };
|
|
158
|
-
}
|
|
159
|
-
/** Tool names in a category, in table order. */
|
|
160
|
-
export function toolsInCategory(category) {
|
|
161
|
-
return Object.keys(TOOL_POLICY).filter((name) => TOOL_POLICY[name].category === category);
|
|
162
|
-
}
|
|
140
|
+
export const toolPolicy = (name) => toolPolicyFor(TOOL_POLICY, name);
|
|
141
|
+
export const isToolEnabled = (name, profile) => isToolEnabledIn(TOOL_POLICY, name, profile);
|
|
142
|
+
export const toolAnnotations = (name) => toolAnnotationsFor(TOOL_POLICY, name);
|
|
143
|
+
export const toolsInCategory = (category) => toolsInCategoryOf(TOOL_POLICY, category);
|
package/dist/tools/autopilot.js
CHANGED
|
@@ -42,6 +42,11 @@ export function registerAutopilotTools(server, client) {
|
|
|
42
42
|
run_hour: z.number().int().min(0).max(23).optional().describe("local hour the nightly run fires"),
|
|
43
43
|
timezone: z.string().optional().describe("IANA timezone, e.g. America/New_York"),
|
|
44
44
|
product_url: z.string().nullable().optional().describe("product link included in posts"),
|
|
45
|
+
product_image_url: z
|
|
46
|
+
.string()
|
|
47
|
+
.nullable()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("reference image for slideshow posts; omitted = filled from product_url's page when product_url changes; null clears it"),
|
|
45
50
|
product_brief: z
|
|
46
51
|
.string()
|
|
47
52
|
.nullable()
|
package/dist/tools/media.js
CHANGED
|
@@ -28,6 +28,10 @@ export function registerMediaTools(server, client) {
|
|
|
28
28
|
.url()
|
|
29
29
|
.optional()
|
|
30
30
|
.describe("reference image URL — guides image style/composition via the edit endpoint"),
|
|
31
|
+
use_product_image: z
|
|
32
|
+
.boolean()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("use the first image from the launch's product page as the reference (image type only; ignored when reference_image_url is set; errors when the launch has no product image)"),
|
|
31
35
|
options: z
|
|
32
36
|
.object({
|
|
33
37
|
aspect_ratio: z.enum(ASPECT_RATIOS).optional().describe("aspect ratio"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jclvsh/dropspace",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.25.0",
|
|
4
4
|
"description": "MCP server and CLI for the Dropspace API",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
"types": "./dist/hosted.d.ts",
|
|
20
20
|
"default": "./dist/hosted.js"
|
|
21
21
|
},
|
|
22
|
+
"./policy": {
|
|
23
|
+
"types": "./dist/policy.d.ts",
|
|
24
|
+
"default": "./dist/policy.js"
|
|
25
|
+
},
|
|
22
26
|
"./tool-policy": {
|
|
23
27
|
"types": "./dist/tool-policy.d.ts",
|
|
24
28
|
"default": "./dist/tool-policy.js"
|