Sign In

@intentwake/mcp

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@intentwake/mcp - npm Package Compare versions

Comparing version
0.1.1
to
0.2.0
+242
-27
dist/index.js

@@ -37,5 +37,11 @@ #!/usr/bin/env node

mcpUrl: safeServiceUrl(env.INTENTWAKE_MCP_URL ?? "https://mcp.intentwake.com/mcp"),
apiUrl: safeServiceUrl(env.INTENTWAKE_API_URL ?? "https://api.intentwake.com")
apiUrl: safeServiceUrl(env.INTENTWAKE_API_URL ?? "https://api.intentwake.com"),
hostedToolPolicy: hostedToolPolicy(env.INTENTWAKE_HOSTED_TOOL_POLICY)
};
}
function hostedToolPolicy(value) {
if (value === void 0 || value === "" || value === "open") return "open";
if (value === "allowlist") return "allowlist";
throw new BridgeError("configuration_error", "Hosted tool policy configuration is invalid.");
}
function safeServiceUrl(value) {

@@ -58,4 +64,10 @@ let url;

import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import {
McpError
} from "@modelcontextprotocol/sdk/types.js";
function createControlPlaneClient(config, fetchImpl = fetch) {
const client = new Client({ name: "intentwake-local-mcp", version: "0.1.1" });
const client = new Client(
{ name: "intentwake-local-mcp", version: "0.2.0" },
{ enforceStrictCapabilities: true }
);
const transport = new StreamableHTTPClientTransport(config.mcpUrl, {

@@ -66,7 +78,14 @@ fetch: fetchImpl,

let connected = null;
const ensureConnected = async () => {
connected ??= client.connect(transport);
await connected;
const capabilities = client.getServerCapabilities();
const server = client.getServerVersion();
if (capabilities?.tools === void 0 || server?.name !== "intentwake-agent-gateway" || typeof server.version !== "string" || server.version.length === 0) {
throw incompatibleManifest();
}
};
const call = async (name, args) => {
try {
connected ??= client.connect(transport);
await connected;
const result = await client.callTool({ name, arguments: args });
const result = await callHostedTool({ name, arguments: args });
if (result.isError) {

@@ -82,3 +101,37 @@ const value = asRecord(result.structuredContent);

};
const callHostedTool = async (params, signal) => {
try {
await ensureConnected();
return await client.callTool(params, void 0, signal ? { signal } : void 0);
} catch (error) {
if (error instanceof BridgeError || error instanceof McpError) throw error;
throw new BridgeError("control_plane_unavailable", "The IntentWake control plane is unavailable.", true);
}
};
return {
async listHostedTools() {
try {
await ensureConnected();
const tools = [];
const cursors = /* @__PURE__ */ new Set();
let cursor;
for (let page = 0; page < 100; page += 1) {
const result = await client.listTools(cursor ? { cursor } : void 0);
tools.push(...result.tools);
if (!result.nextCursor) return tools;
if (cursors.has(result.nextCursor)) throw incompatibleManifest();
cursors.add(result.nextCursor);
cursor = result.nextCursor;
}
throw incompatibleManifest();
} catch (error) {
if (error instanceof BridgeError) throw error;
throw new BridgeError(
"hosted_manifest_unavailable",
"The IntentWake hosted tool surface is unavailable.",
true
);
}
},
callHostedTool,
async startListUpload(input) {

@@ -167,3 +220,3 @@ return parseSession("list", await call("start_bulk_list_upload", { ...input }));

async close() {
if (connected) await transport.close();
if (connected) await client.close();
}

@@ -245,2 +298,8 @@ };

}
function incompatibleManifest() {
return new BridgeError(
"hosted_manifest_incompatible",
"The IntentWake hosted tool surface is incompatible."
);
}

@@ -327,5 +386,62 @@ // src/paths.ts

// src/tools.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js";
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError as McpError2
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
// src/manifest.ts
var PINNED_HOSTED_TOOL_NAMES = [
"get_bulk_lookup_rest_workflow",
"search_intent_taxonomy",
"lookup_signals",
"list_lists",
"get_list",
"delete_list",
"create_list",
"start_bulk_list_upload",
"complete_bulk_list_upload",
"append_list_entries",
"start_bulk_append_upload",
"complete_bulk_append_upload",
"get_list_append",
"set_list_monitoring",
"list_downloads",
"get_download_url",
"download_result_file",
"unlock_download",
"unlock_backfill_results",
"get_recent_results",
"get_wallet",
"get_funding_request",
"request_funding"
];
function validateHostedManifest(tools, policy, localNames) {
if (tools.length === 0) throw incompatibleManifest2();
const names = /* @__PURE__ */ new Set();
for (const tool of tools) {
const annotations = tool.annotations;
if (names.has(tool.name) || localNames.has(tool.name) || typeof annotations?.title !== "string" || annotations.title.trim().length === 0 || typeof annotations.readOnlyHint !== "boolean" || typeof annotations.destructiveHint !== "boolean") {
throw incompatibleManifest2();
}
names.add(tool.name);
}
if (policy === "open") return tools;
if (PINNED_HOSTED_TOOL_NAMES.some((name) => !names.has(name))) {
throw incompatibleManifest2();
}
const pinned = new Set(PINNED_HOSTED_TOOL_NAMES);
return tools.filter(({ name }) => pinned.has(name));
}
function incompatibleManifest2() {
return new BridgeError(
"hosted_manifest_incompatible",
"The IntentWake hosted tool surface is incompatible."
);
}
// src/upload.ts

@@ -883,3 +999,9 @@ async function uploadListFile(input, deps) {

},
annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false, openWorldHint: true },
annotations: {
title: "Upload List File",
readOnlyHint: false,
idempotentHint: true,
destructiveHint: false,
openWorldHint: true
},
run: (args, deps) => uploadListFile(args, deps)

@@ -896,3 +1018,9 @@ },

},
annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false, openWorldHint: true },
annotations: {
title: "Append List File",
readOnlyHint: false,
idempotentHint: true,
destructiveHint: false,
openWorldHint: true
},
run: (args, deps) => appendListFile(args, deps)

@@ -911,27 +1039,99 @@ },

},
annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: true, openWorldHint: true },
annotations: {
title: "Download Result To File",
readOnlyHint: false,
idempotentHint: true,
destructiveHint: true,
openWorldHint: true
},
run: (args, deps) => downloadResultToFile(args, deps)
}
];
function createIntentWakeMcpServer(deps) {
const server = new McpServer({ name: "intentwake-local-mcp", version: "0.1.1" });
for (const tool of TOOL_DEFINITIONS) {
server.registerTool(tool.name, {
description: tool.description,
inputSchema: tool.inputSchema,
annotations: tool.annotations
}, async (args) => {
async function createIntentWakeMcpServer(deps) {
const localTools = new Map(TOOL_DEFINITIONS.map((tool) => [tool.name, tool]));
const hostedTools = validateHostedManifest(
await deps.client.listHostedTools(),
deps.hostedToolPolicy,
new Set(localTools.keys())
);
const hostedNames = new Set(hostedTools.map(({ name }) => name));
const server = new Server(
{ name: "intentwake-local-mcp", version: "0.2.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [...hostedTools, ...TOOL_DEFINITIONS.map(localToolDescriptor)]
}));
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
if (hostedNames.has(request.params.name)) {
try {
const value = await tool.run(args, deps);
const structuredContent = value && typeof value === "object" ? value : { result: value };
return { content: [{ type: "text", text: JSON.stringify(structuredContent) }], structuredContent };
return await deps.client.callHostedTool(request.params, extra.signal);
} catch (error) {
const safe = safeError(error);
const value = { error: safe.code, message: safe.message, retryable: safe.retryable };
return { isError: true, content: [{ type: "text", text: JSON.stringify(value) }], structuredContent: value };
if (error instanceof McpError2) throw relayHostedMcpError(error);
return toolError(safeHostedError(error));
}
}
const tool = localTools.get(request.params.name);
if (!tool) throw new McpError2(ErrorCode.InvalidParams, `Tool ${request.params.name} not found`);
if (request.params.task) {
throw new McpError2(ErrorCode.InvalidParams, `Tool ${request.params.name} does not support tasks`);
}
const parsed = await z.strictObject(tool.inputSchema).safeParseAsync(request.params.arguments ?? {});
if (!parsed.success) throw new McpError2(ErrorCode.InvalidParams, "Invalid tool arguments.");
try {
const value = await tool.run(parsed.data, deps);
const structuredContent = value && typeof value === "object" ? value : { result: value };
return {
content: [{ type: "text", text: JSON.stringify(structuredContent) }],
structuredContent
};
} catch (error) {
return toolError(safeError(error));
}
});
server.onclose = () => {
void Promise.resolve().then(() => deps.client.close()).catch(() => {
});
}
};
return server;
}
function localToolDescriptor(tool) {
return {
name: tool.name,
description: tool.description,
inputSchema: toJsonSchemaCompat(z.strictObject(tool.inputSchema), {
strictUnions: true,
pipeStrategy: "input"
}),
annotations: tool.annotations,
execution: { taskSupport: "forbidden" }
};
}
function safeHostedError(error) {
if (error instanceof BridgeError) return error;
return new BridgeError(
"control_plane_unavailable",
"The IntentWake control plane is unavailable.",
true
);
}
function relayHostedMcpError(error) {
const prefix = `MCP error ${error.code}: `;
return Object.assign(
new Error(error.message.startsWith(prefix) ? error.message.slice(prefix.length) : error.message),
{ code: error.code, data: error.data }
);
}
function toolError(error) {
const value = {
error: error.code,
message: error.message,
retryable: error.retryable
};
return {
isError: true,
content: [{ type: "text", text: JSON.stringify(value) }],
structuredContent: value
};
}

@@ -943,4 +1143,19 @@ // src/index.ts

const client = createControlPlaneClient(config);
const server = createIntentWakeMcpServer({ roots, client });
await server.connect(new StdioServerTransport());
try {
const server = await createIntentWakeMcpServer({
roots,
client,
hostedToolPolicy: config.hostedToolPolicy
});
process.stdin.once("end", () => {
void server.close().catch(() => {
});
});
await server.connect(new StdioServerTransport());
if (process.stdin.readableEnded) await server.close();
} catch (error) {
await client.close().catch(() => {
});
throw error;
}
}

@@ -947,0 +1162,0 @@ main().catch(() => {

+3
-2
{
"name": "@intentwake/mcp",
"version": "0.1.1",
"description": "Local stdio MCP bridge for IntentWake file transfers",
"version": "0.2.0",
"description": "Unified IntentWake stdio MCP proxy with local file transfers",
"keywords": [

@@ -9,2 +9,3 @@ "intentwake",

"stdio",
"proxy",
"file-transfer"

@@ -11,0 +12,0 @@ ],

# `@intentwake/mcp`
Local stdio MCP bridge for complete IntentWake file transfers.
Unified local stdio MCP server for IntentWake account workflows and complete
file transfers.
The bridge exposes three semantic tools:
The server proxies the authenticated hosted IntentWake MCP tool surface without
reimplementing its account, list, wallet, funding, policy, billing,
idempotency, authorization, or audit behavior. It adds three local-only
semantic tools:

@@ -37,2 +41,5 @@ - `upload_list_file` streams an allowed local file into a canonical IntentWake list.

- `INTENTWAKE_API_URL`: optional public API URL override used for canonical CSV streams.
- `INTENTWAKE_HOSTED_TOOL_POLICY`: optional hosted manifest policy. The default,
`open`, exposes every compatible hosted tool. `allowlist` pins the hosted
surface to the names shipped with this package version.

@@ -42,2 +49,10 @@ With no allowed roots configured, every file operation is refused. There are no implicit current

At startup the server negotiates one authenticated hosted MCP connection,
validates the complete hosted manifest, and fails closed if the gateway is
unavailable, incompatible, missing required tool annotations, or collides with
a local tool. That same hosted connection handles proxied calls and the
control-plane steps used by local file transfers. Hosted names, schemas,
annotations, structured results, safe errors, and metadata pass through
unchanged.
## File safety

@@ -44,0 +59,0 @@