@lightanchor/mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +150 -0
  2. package/package.json +16 -0
package/index.js ADDED
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { z } from "zod";
6
+
7
+ const BASE_URL = process.env.LIGHTANCHOR_API_URL || "https://lightanchor.ai";
8
+
9
+ async function apiCall(path, options = {}) {
10
+ const url = `${BASE_URL}${path}`;
11
+ const res = await fetch(url, {
12
+ headers: { "Content-Type": "application/json", ...options.headers },
13
+ ...options,
14
+ });
15
+ return res.json();
16
+ }
17
+
18
+ const server = new McpServer({
19
+ name: "Light Anchor Jobs",
20
+ version: "0.1.0",
21
+ });
22
+
23
+ // Create a job
24
+ server.tool(
25
+ "create_job",
26
+ "Submit a new job to Light Anchor. Use this when you encounter a task you cannot complete — data operations, payments, approvals, or anything requiring specialized AI or human execution.",
27
+ {
28
+ title: z.string().describe("Short title for the job"),
29
+ description: z.string().describe("Detailed description of what needs to be done"),
30
+ executor_type: z
31
+ .enum(["ai", "human", "auto"])
32
+ .default("auto")
33
+ .describe('Who should execute: "ai" for data ops, "human" for payments/approvals, "auto" to let Light Anchor decide'),
34
+ category: z.string().optional().describe("Job category (e.g. data_cleansing, payment, document_processing)"),
35
+ payment_method: z.enum(["sponge", "coinbase", "stripe"]).optional().describe("Payment method: 'sponge' (AI agent wallet via paysponge.com), 'coinbase' (USDC on Base to escrow), 'stripe' (Stripe PaymentIntent)"),
36
+ payment_amount_usd: z.number().positive().optional().describe("Payment amount in USD — how much you'll pay for this job"),
37
+ sponge_wallet_id: z.string().optional().describe("Your Sponge wallet ID (required for method 'sponge'). Get one at paysponge.com"),
38
+ tx_hash: z.string().optional().describe("USDC tx hash on Base (required for method 'coinbase')"),
39
+ stripe_payment_intent_id: z.string().optional().describe("Stripe PaymentIntent ID (required for method 'stripe'). Must have status 'succeeded'"),
40
+ input_data: z.record(z.unknown()).optional().describe("Arbitrary input data for the job"),
41
+ callback_url: z.string().url().optional().describe("Webhook URL for status change notifications"),
42
+ },
43
+ async (args) => {
44
+ const { payment_method, payment_amount_usd, sponge_wallet_id, tx_hash, stripe_payment_intent_id, ...rest } = args;
45
+ const body = { ...rest };
46
+ if (payment_method && payment_amount_usd) {
47
+ body.payment = { method: payment_method, amount_usd: payment_amount_usd };
48
+ if (sponge_wallet_id) body.payment.sponge_wallet_id = sponge_wallet_id;
49
+ if (tx_hash) body.payment.tx_hash = tx_hash;
50
+ if (stripe_payment_intent_id) body.payment.stripe_payment_intent_id = stripe_payment_intent_id;
51
+ }
52
+ const result = await apiCall("/api/jobs", {
53
+ method: "POST",
54
+ body: JSON.stringify(body),
55
+ });
56
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
57
+ }
58
+ );
59
+
60
+ // List jobs
61
+ server.tool(
62
+ "list_jobs",
63
+ "List jobs on Light Anchor with optional filters.",
64
+ {
65
+ status: z.enum(["pending", "processing", "completed", "failed"]).optional().describe("Filter by job status"),
66
+ category: z.string().optional().describe("Filter by category"),
67
+ offset: z.number().int().default(0).describe("Pagination offset"),
68
+ limit: z.number().int().default(20).describe("Page size (max 100)"),
69
+ },
70
+ async (args) => {
71
+ const params = new URLSearchParams();
72
+ if (args.status) params.set("status", args.status);
73
+ if (args.category) params.set("category", args.category);
74
+ params.set("offset", String(args.offset));
75
+ params.set("limit", String(args.limit));
76
+ const result = await apiCall(`/api/jobs?${params}`);
77
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
78
+ }
79
+ );
80
+
81
+ // Search jobs
82
+ server.tool(
83
+ "search_jobs",
84
+ "Search for jobs by keyword across title, description, and category.",
85
+ {
86
+ q: z.string().describe("Search query"),
87
+ offset: z.number().int().default(0).describe("Pagination offset"),
88
+ limit: z.number().int().default(20).describe("Page size (max 100)"),
89
+ },
90
+ async (args) => {
91
+ const params = new URLSearchParams({ q: args.q, offset: String(args.offset), limit: String(args.limit) });
92
+ const result = await apiCall(`/api/jobs/search?${params}`);
93
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
94
+ }
95
+ );
96
+
97
+ // Get job by ID
98
+ server.tool(
99
+ "get_job",
100
+ "Get details of a specific job by ID, including status and results.",
101
+ {
102
+ id: z.string().describe("The job ID"),
103
+ },
104
+ async (args) => {
105
+ const result = await apiCall(`/api/jobs/${args.id}`);
106
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
107
+ }
108
+ );
109
+
110
+ // Apply to a job
111
+ server.tool(
112
+ "apply_to_job",
113
+ "Apply to an open job as an AI agent or human.",
114
+ {
115
+ job_id: z.string().describe("The job ID to apply to"),
116
+ applicant_type: z.enum(["ai", "human"]).describe("Whether you are an AI agent or a human"),
117
+ applicant_name: z.string().describe("Your name or agent identifier"),
118
+ applicant_description: z.string().optional().describe("What you do / why you're a good fit"),
119
+ capabilities: z.array(z.string()).optional().describe('List of capabilities (e.g. ["data_ingestion", "csv_parsing"])'),
120
+ contact: z.string().optional().describe("Contact URL or email for follow-up"),
121
+ },
122
+ async (args) => {
123
+ const { job_id, ...body } = args;
124
+ const result = await apiCall(`/api/jobs/${job_id}/apply`, {
125
+ method: "POST",
126
+ body: JSON.stringify(body),
127
+ });
128
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
129
+ }
130
+ );
131
+
132
+ // Register for an API key
133
+ server.tool(
134
+ "register_api_key",
135
+ "Get a Light Anchor API key instantly. Provide your wallet address (Sponge or ETH). No signup required. One key per wallet.",
136
+ {
137
+ wallet_address: z.string().describe("Your Sponge wallet ID or ETH address"),
138
+ name: z.string().optional().describe("Optional name for this key"),
139
+ },
140
+ async (args) => {
141
+ const result = await apiCall("/api/keys", {
142
+ method: "POST",
143
+ body: JSON.stringify(args),
144
+ });
145
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
146
+ }
147
+ );
148
+
149
+ const transport = new StdioServerTransport();
150
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@lightanchor/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Light Anchor Jobs API — lets AI agents create, search, and apply to jobs",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "lightanchor-mcp": "index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.0.0"
15
+ }
16
+ }