@aioproductoscom/mcp-agent 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 ADDED
@@ -0,0 +1,81 @@
1
+ # @aioproductoscom/mcp-agent
2
+
3
+ Turn **Claude Code, Cursor, or Codex** into an assignable **Backend / Frontend / Mobile / QA**
4
+ teammate inside [ProductOS](https://platform.aioproductos.com). Your team assigns it tasks like any
5
+ teammate; it pulls the work, does it **in your repo on your model**, opens a PR (or writes tests),
6
+ and reports status back. ProductOS owns the work lifecycle; your host owns the runtime — we never
7
+ hold your repo credentials.
8
+
9
+ ## 1. Get a token
10
+
11
+ A team admin opens **ProductOS → Settings → AI Team**, hires a teammate (name it, pick a role), and
12
+ copies the token shown once. One agent = one role; the token decides which tools appear.
13
+
14
+ ## 2. Connect it (in the repo the agent should work in)
15
+
16
+ The only thing you paste is the token. Launch your host from the project directory.
17
+
18
+ ### Claude Code
19
+
20
+ ```bash
21
+ claude mcp add productos-agent \
22
+ -e PRODUCTOS_TOKEN=agent_live_xxx \
23
+ -- npx -y @aioproductoscom/mcp-agent
24
+ ```
25
+
26
+ ### Cursor — `.cursor/mcp.json`
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "productos-agent": {
32
+ "command": "npx",
33
+ "args": ["-y", "@aioproductoscom/mcp-agent"],
34
+ "env": { "PRODUCTOS_TOKEN": "agent_live_xxx" }
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ ### Codex / any MCP host — `mcp.json`
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "productos-agent": {
46
+ "command": "npx",
47
+ "args": ["-y", "@aioproductoscom/mcp-agent"],
48
+ "env": { "PRODUCTOS_TOKEN": "agent_live_xxx" }
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ Self-hosted ProductOS? Add `"PRODUCTOS_URL": "https://platform.your-domain.com"`.
55
+
56
+ ## 3. Run the loop
57
+
58
+ Tell your host: **"You're a ProductOS agent — call `get_skill`, then work your assigned tasks."**
59
+
60
+ | Tool | What it does |
61
+ |---|---|
62
+ | `whoami` | Org, your name, your role |
63
+ | `get_skill` | Your role-specialized workflow + hard rules (read first) |
64
+ | `get_assigned_tasks` | Tasks assigned to you, pullable, with run state |
65
+ | `get_task_context(id)` | Acceptance criteria + linked feature/insight/product + thread |
66
+ | `start_task(id)` | Atomic claim → In Progress (fails if another session holds it) |
67
+ | `report_progress(id, note)` | Progress comment on the task |
68
+ | `submit_work(id, …)` | **Dev** — attach PR + summary + files → In Review |
69
+ | `submit_tests(id, …)` | **QA** — attach tests + coverage + results → In Review |
70
+ | `block_task(id, reason)` | Stuck / underspecified → blocked + a clear question |
71
+ | `ping` | Keep "online" in the team view |
72
+
73
+ ## Guarantees
74
+
75
+ - **Human-in-the-loop.** `submit_work` lands the task **In Review** — it never merges. A human
76
+ approves the PR.
77
+ - **Least privilege.** A QA token can't submit code; a Dev token can't reassign humans or touch
78
+ billing. Scopes are enforced server-side, per role.
79
+ - **No secrets in the bundle.** The token is yours, pasted into your host. The agent acts locally;
80
+ only lifecycle data (status, PR URL, summary, test results) is posted back.
81
+ - **One claim per task.** Two sessions of one token can't open duplicate PRs (atomic claim).
package/dist/client.js ADDED
@@ -0,0 +1,63 @@
1
+ // Thin typed HTTP client over the platform's agent worker endpoints. The agent
2
+ // token is the only credential; nothing is stored. Uses global fetch (Node 18+).
3
+ export class AgentClient {
4
+ baseUrl;
5
+ token;
6
+ constructor(baseUrl, token) {
7
+ this.baseUrl = baseUrl;
8
+ this.token = token;
9
+ }
10
+ async req(path, init) {
11
+ const res = await fetch(`${this.baseUrl}${path}`, {
12
+ ...init,
13
+ headers: {
14
+ "Content-Type": "application/json",
15
+ Authorization: `Bearer ${this.token}`,
16
+ ...(init?.headers ?? {}),
17
+ },
18
+ });
19
+ const text = await res.text();
20
+ const json = text ? JSON.parse(text) : {};
21
+ if (!res.ok) {
22
+ const o = (json ?? {});
23
+ const msg = o.error ? `${o.error}${o.detail ? `: ${o.detail}` : ""}` : `HTTP ${res.status}`;
24
+ const e = new Error(msg);
25
+ e.status = res.status;
26
+ throw e;
27
+ }
28
+ return json;
29
+ }
30
+ whoami() {
31
+ return this.req("/api/agent/me");
32
+ }
33
+ ping() {
34
+ return this.req("/api/agent/ping", { method: "POST" });
35
+ }
36
+ assignedTasks() {
37
+ return this.req("/api/agent/tasks");
38
+ }
39
+ taskContext(id) {
40
+ return this.req(`/api/agent/tasks/${encodeURIComponent(id)}`);
41
+ }
42
+ action(id, body) {
43
+ return this.req(`/api/agent/tasks/${encodeURIComponent(id)}`, {
44
+ method: "POST",
45
+ body: JSON.stringify(body),
46
+ });
47
+ }
48
+ start(id) {
49
+ return this.action(id, { action: "start" });
50
+ }
51
+ progress(id, note) {
52
+ return this.action(id, { action: "progress", note });
53
+ }
54
+ submitWork(id, b) {
55
+ return this.action(id, { action: "submit", ...b });
56
+ }
57
+ submitTests(id, b) {
58
+ return this.action(id, { action: "tests", ...b });
59
+ }
60
+ block(id, reason) {
61
+ return this.action(id, { action: "block", reason });
62
+ }
63
+ }
package/dist/index.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { AgentClient } from "./client.js";
6
+ import { ROLE_SKILLS, ROLE_LABEL, SKILL_VERSION } from "./skills.js";
7
+ const token = process.env.PRODUCTOS_TOKEN;
8
+ const baseUrl = (process.env.PRODUCTOS_URL ?? "https://platform.aioproductos.com").replace(/\/$/, "");
9
+ if (!token) {
10
+ console.error("[productos-agent] PRODUCTOS_TOKEN is required — your team admin generates it in ProductOS → Settings → AI Team.");
11
+ process.exit(1);
12
+ }
13
+ const client = new AgentClient(baseUrl, token);
14
+ function text(data) {
15
+ return {
16
+ content: [
17
+ { type: "text", text: typeof data === "string" ? data : JSON.stringify(data, null, 2) },
18
+ ],
19
+ };
20
+ }
21
+ async function main() {
22
+ // Identify ourselves up front — the token's role decides which tools we expose
23
+ // (the server enforces the same scopes, this is belt-and-braces + a clean UX).
24
+ let role;
25
+ let name;
26
+ try {
27
+ const me = await client.whoami();
28
+ role = me.role;
29
+ name = me.name;
30
+ }
31
+ catch (err) {
32
+ console.error(`[productos-agent] could not authenticate: ${err.message}`);
33
+ process.exit(1);
34
+ }
35
+ const who = `${name ?? "Agent"} · ${ROLE_LABEL[role]}`;
36
+ const isQA = role === "qa";
37
+ const server = new McpServer({ name: "productos-agent", version: "0.1.0" }, {
38
+ instructions: `You are "${who}", an AI teammate inside ProductOS. Work the assigned-task loop: get_assigned_tasks → get_task_context → start_task → (do the work in THIS repo) → report_progress → ${isQA ? "submit_tests" : "submit_work"}; block_task if stuck. Call get_skill first for your full workflow and hard rules (open a PR, never merge). Skill v${SKILL_VERSION}.`,
39
+ });
40
+ server.tool("whoami", "Show this agent's identity (org, name, role).", {}, async () => text(await client.whoami()));
41
+ server.tool("get_skill", "Your role-specialized workflow + hard rules. Read this before starting work.", {}, async () => text(`${ROLE_SKILLS[role]}\n\n— skill v${SKILL_VERSION}`));
42
+ server.tool("get_assigned_tasks", "List the tasks assigned to you (pullable), each with its current run state.", {}, async () => text(await client.assignedTasks()));
43
+ server.tool("get_task_context", "Full context for one assigned task: acceptance criteria, linked feature/insight/product, the comment thread, and your run state.", { id: z.string() }, async (a) => text(await client.taskContext(a.id)));
44
+ server.tool("start_task", "Claim an assigned task (atomic). You then own it and the board moves to In Progress. Fails if another session already holds it.", { id: z.string() }, async (a) => text(await client.start(a.id)));
45
+ server.tool("report_progress", "Post a progress note on the task so the human can follow along.", { id: z.string(), note: z.string() }, async (a) => text(await client.progress(a.id, a.note)));
46
+ server.tool("block_task", "Mark the task blocked with a precise reason (underspecified, missing dependency, untestable). Beats guessing.", { id: z.string(), reason: z.string() }, async (a) => text(await client.block(a.id, a.reason)));
47
+ if (isQA) {
48
+ server.tool("submit_tests", "Attach the tests you wrote + measured coverage + run results. The task lands In Review for a human.", {
49
+ id: z.string(),
50
+ framework: z.string().optional(),
51
+ files: z.array(z.string()).optional(),
52
+ coverage: z.number().optional(),
53
+ results: z.string().optional(),
54
+ idempotency_key: z.string().optional(),
55
+ }, async ({ id, ...b }) => text(await client.submitTests(id, b)));
56
+ }
57
+ else {
58
+ server.tool("submit_work", "Attach your PR URL + summary + files changed. The task lands In Review for a human — it is never merged automatically.", {
59
+ id: z.string(),
60
+ pr_url: z.string().optional(),
61
+ summary: z.string().optional(),
62
+ files_changed: z.array(z.string()).optional(),
63
+ idempotency_key: z.string().optional(),
64
+ }, async ({ id, ...b }) => text(await client.submitWork(id, b)));
65
+ }
66
+ server.tool("ping", "Heartbeat — keep this agent shown as online in the team view.", {}, async () => text(await client.ping()));
67
+ await server.connect(new StdioServerTransport());
68
+ }
69
+ main().catch((err) => {
70
+ console.error("[productos-agent] fatal:", err);
71
+ process.exit(1);
72
+ });
package/dist/skills.js ADDED
@@ -0,0 +1,101 @@
1
+ // Versioned, role-specialized agent skills (the prompt + workflow the customer's
2
+ // host runs). Conservative by design — this is the one quality lever we own, and
3
+ // the agent wears the customer's brand on a model + repo we don't control. The
4
+ // server exposes the right one via get_skill() and frames it in `instructions`.
5
+ //
6
+ // Bump SKILL_VERSION on any behavioural change so a run is traceable to a skill.
7
+ export const SKILL_VERSION = "0.1.0";
8
+ const LOOP = `## The loop
9
+ 1. \`get_assigned_tasks\` — find work assigned to you. Pick the highest-priority unstarted one.
10
+ 2. \`get_task_context(id)\` — read the title, acceptance criteria (description), and any linked
11
+ feature/insight/product. If the goal is unclear, \`block_task\` with a precise question instead
12
+ of guessing.
13
+ 3. \`start_task(id)\` — claim it (you now own it; the board moves to In Progress). If the claim is
14
+ refused, another session already holds it — pick a different task.
15
+ 4. Do the work **in the repo the host was launched in**, following that repo's existing
16
+ conventions, language, and patterns. Small, reviewable changes.
17
+ 5. \`report_progress(id, note)\` at meaningful checkpoints so the human can follow along.
18
+
19
+ ## Hard rules (never break)
20
+ - **Open a PR. Never push to a default/protected branch, never force-push, never merge.** A human
21
+ approves and merges. Your job ends at "ready for review".
22
+ - Don't touch secrets, infra, billing, or unrelated files. Stay in the task's scope.
23
+ - If you're stuck, blocked on a dependency, or the task is underspecified → \`block_task(id, reason)\`.
24
+ A clear block beats a wrong implementation.`;
25
+ const BACKEND = `# ProductOS · Backend Dev
26
+
27
+ You are a **Backend** AI teammate. You implement server-side work — APIs, data models,
28
+ migrations, business logic, jobs — in the customer's repository.
29
+
30
+ ${LOOP}
31
+
32
+ ## Backend specifics
33
+ - Match the project's framework, ORM/query layer, and error-handling style. Validate inputs at
34
+ boundaries; never trust external data.
35
+ - Migrations: additive and reversible where possible; never destructive without an explicit ask.
36
+ - Add/extend tests for the logic you change. Note any new env vars or migration steps in the PR.
37
+
38
+ ## Finishing
39
+ \`submit_work(id, { pr_url, summary, files_changed })\` — attach the PR URL, a short summary of the
40
+ approach, and the files you touched. The task lands **In Review** for a human.`;
41
+ const FRONTEND = `# ProductOS · Frontend Dev
42
+
43
+ You are a **Frontend** AI teammate. You implement UI work — components, pages, state, styling,
44
+ accessibility — in the customer's repository.
45
+
46
+ ${LOOP}
47
+
48
+ ## Frontend specifics
49
+ - Reuse the project's design system, components, and tokens. Don't introduce a new UI library or
50
+ one-off styles when an existing pattern fits.
51
+ - Keep components accessible (semantic HTML, labels, keyboard, focus). Respect reduced-motion.
52
+ - Verify the change renders at the key breakpoints; avoid layout shift. Add component/interaction
53
+ tests where the project has them.
54
+
55
+ ## Finishing
56
+ \`submit_work(id, { pr_url, summary, files_changed })\` — attach the PR URL, a short summary, and the
57
+ files you touched. The task lands **In Review** for a human.`;
58
+ const MOBILE = `# ProductOS · Mobile Dev
59
+
60
+ You are a **Mobile** AI teammate. You implement mobile app work — screens, navigation, native
61
+ integration, platform behaviour — in the customer's repository.
62
+
63
+ ${LOOP}
64
+
65
+ ## Mobile specifics
66
+ - Match the project's stack (React Native / Swift / Kotlin / Flutter) and its navigation + state
67
+ conventions. Handle both platforms when the project targets both.
68
+ - Mind platform differences (safe areas, permissions, lifecycle, offline). Keep bundles lean.
69
+ - Add tests where the project supports them; note any native/config steps in the PR.
70
+
71
+ ## Finishing
72
+ \`submit_work(id, { pr_url, summary, files_changed })\` — attach the PR URL, a short summary, and the
73
+ files you touched. The task lands **In Review** for a human.`;
74
+ const QA = `# ProductOS · QA
75
+
76
+ You are a **QA** AI teammate. You don't ship product code — you **write and run tests** for the
77
+ assigned task in the customer's repository, then report coverage and results.
78
+
79
+ ${LOOP}
80
+
81
+ ## QA specifics
82
+ - Write the test layer the project uses (unit / integration / e2e — Playwright, Vitest, Jest, etc.).
83
+ Cover the acceptance criteria, the happy path, and the edge/error cases.
84
+ - Run the tests. Report real numbers — don't claim coverage you didn't measure.
85
+ - If the feature is untestable as built (no hooks, no data-testids), \`block_task\` with what's needed.
86
+
87
+ ## Finishing
88
+ \`submit_tests(id, { framework, files, coverage, results })\` — attach the framework, the test files,
89
+ the measured coverage, and the run results. The task lands **In Review** for a human.`;
90
+ export const ROLE_SKILLS = {
91
+ backend: BACKEND,
92
+ frontend: FRONTEND,
93
+ mobile: MOBILE,
94
+ qa: QA,
95
+ };
96
+ export const ROLE_LABEL = {
97
+ backend: "Backend",
98
+ frontend: "Frontend",
99
+ mobile: "Mobile",
100
+ qa: "QA",
101
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@aioproductoscom/mcp-agent",
3
+ "version": "0.1.0",
4
+ "description": "ProductOS Agent — an MCP server that turns Claude Code, Cursor, or Codex into an assignable Backend/Frontend/Mobile/QA teammate inside ProductOS.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "AIITDEVELOPMENT DOO Novi Sad",
8
+ "homepage": "https://platform.aioproductos.com",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/businesstalksnetwork/productos.git",
12
+ "directory": "packages/mcps/productos-agent"
13
+ },
14
+ "bugs": { "url": "https://github.com/businesstalksnetwork/productos/issues" },
15
+ "keywords": [
16
+ "mcp",
17
+ "model-context-protocol",
18
+ "productos",
19
+ "claude",
20
+ "cursor",
21
+ "codex",
22
+ "ai-agent",
23
+ "coding-agent"
24
+ ],
25
+ "bin": { "productos-agent": "dist/index.js" },
26
+ "files": ["dist", "README.md"],
27
+ "engines": { "node": ">=18" },
28
+ "publishConfig": { "access": "public" },
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "prepublishOnly": "npm run build",
32
+ "start": "node dist/index.js"
33
+ },
34
+ "dependencies": {
35
+ "@modelcontextprotocol/sdk": "^1.0.0",
36
+ "zod": "^3.23.8"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^22.7.0",
40
+ "typescript": "^5.6.0"
41
+ }
42
+ }