Add LogicSRC standards MCP server

This commit is contained in:
Anthony Ettinger 2026-06-06 14:22:43 +00:00
parent 4e4c78140d
commit dd150f391a
26 changed files with 2250 additions and 15 deletions

View file

@ -0,0 +1,23 @@
{
"name": "@profullstack/logicsrc-mcp",
"version": "0.1.0",
"description": "MCP server for LogicSRC standards, schemas, prompts, and validators.",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"bin": {
"logicsrc-mcp": "./dist/index.js"
},
"scripts": {
"build": "tsc -p tsconfig.json",
"test": "vitest run src"
},
"dependencies": {
"@logicsrc/validators": "file:../validators",
"@modelcontextprotocol/sdk": "^1.29.0",
"zod": "^4.1.13"
},
"devDependencies": {
"vitest": "^4.0.8"
}
}

View file

@ -0,0 +1,12 @@
#!/usr/bin/env node
import { pathToFileURL } from "node:url";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { createLogicSrcMcpServer } from "./server.js";
export { createLogicSrcMcpServer } from "./server.js";
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
const server = createLogicSrcMcpServer();
const transport = new StdioServerTransport();
await server.connect(transport);
}

View file

@ -0,0 +1,52 @@
import { describe, expect, it } from "vitest";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import { createLogicSrcMcpServer } from "./server.js";
describe("LogicSRC MCP server", () => {
it("exposes schemas, validation, and prompts over MCP", async () => {
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
const server = createLogicSrcMcpServer();
const client = new Client({ name: "test-client", version: "0.1.0" });
await server.connect(serverTransport);
await client.connect(clientTransport);
const resources = await client.listResources();
expect(resources.resources.some((resource) => resource.uri === "logicsrc://schemas/task")).toBe(true);
const schema = await client.readResource({ uri: "logicsrc://schemas/task" });
expect(textContent(schema.contents[0])).toContain("logicsrc.task");
const example = await client.callTool({ name: "example_document", arguments: { kind: "task" } });
const text = firstToolText(example);
expect(text).toContain("Test checkout flow");
const validation = await client.callTool({ name: "validate_document", arguments: { kind: "task", document: text, fileName: "task.json" } });
const validationText = firstToolText(validation);
expect(validationText).toContain('"ok": true');
const prompts = await client.listPrompts();
expect(prompts.prompts.map((prompt) => prompt.name)).toContain("create-valid-task");
await client.close();
await server.close();
});
});
function textContent(content: unknown) {
return isRecord(content) && typeof content.text === "string" ? content.text : "";
}
function firstToolText(result: unknown) {
if (!isRecord(result) || !Array.isArray(result.content)) {
return "";
}
const [first] = result.content;
return isRecord(first) && first.type === "text" && typeof first.text === "string" ? first.text : "";
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}

View file

@ -0,0 +1,220 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { assertSchemaKind, parseDocument, schemas, validate, type SchemaKind } from "@logicsrc/validators";
const docs = {
positioning: `LogicSRC is an open standards initiative for human and AI agent coordination, maintained by Profullstack, Inc.
CommandBoard.run is a hosted product by Profullstack, Inc., built on LogicSRC. LogicSRC defines identity, boards, posts, tasks, bounties, agents, agent runs, permissions, payments, escrow, reputation, events, webhooks, CLI commands, API schemas, and plugin contracts.`,
roadmap: `LogicSRC v1.0 focuses on schemas, validation tooling, plugin manifests, CLI/TUI conventions, event streams, agent profiles, permissions, and reference implementations.`,
primitives: `Core LogicSRC primitives: users, DIDs, OAuth accounts, profiles, organizations, boards, posts, threads, comments, tasks, bids, submissions, agents, agent runs, payments, escrows, wallets, reputation events, files, API keys, permissions, audit logs, webhooks, schema versions, and plugin audit logs.`
} as const;
const schemaKinds = Object.keys(schemas) as SchemaKind[];
export function createLogicSrcMcpServer() {
const server = new McpServer(
{
name: "@profullstack/logicsrc-mcp",
version: "0.1.0"
},
{
capabilities: {
resources: {},
tools: {},
prompts: {}
},
instructions: "Use this server for LogicSRC standards, schema resources, validation, and draft object generation. Treat CommandBoard.run as a reference implementation, not the standards identity."
}
);
for (const [name, text] of Object.entries(docs)) {
const uri = `logicsrc://docs/${name}`;
server.registerResource(
`logicsrc-${name}`,
uri,
{
title: `LogicSRC ${titleCase(name)}`,
description: `LogicSRC ${name} reference text.`,
mimeType: "text/markdown"
},
async () => ({ contents: [{ uri, mimeType: "text/markdown", text }] })
);
}
for (const kind of schemaKinds) {
const uri = `logicsrc://schemas/${kind}`;
server.registerResource(
`logicsrc-schema-${kind}`,
uri,
{
title: `LogicSRC ${kind} schema`,
description: `JSON Schema for LogicSRC ${kind} documents.`,
mimeType: "application/schema+json"
},
async () => ({
contents: [
{
uri,
mimeType: "application/schema+json",
text: JSON.stringify(schemas[kind], null, 2)
}
]
})
);
}
server.registerTool(
"list_schema_kinds",
{
title: "List LogicSRC Schema Kinds",
description: "Lists the LogicSRC schema kinds exposed by this standards server.",
annotations: { readOnlyHint: true, openWorldHint: false }
},
async () => textResult(JSON.stringify({ schemaKinds }, null, 2))
);
server.registerTool(
"validate_document",
{
title: "Validate LogicSRC Document",
description: "Validates a JSON or YAML document against a LogicSRC schema kind.",
inputSchema: {
kind: z.enum(["agent", "event", "plugin", "run", "task"]),
document: z.string().describe("JSON or YAML document text."),
fileName: z.string().optional().describe("Optional file name used to select JSON parsing when it ends with .json.")
},
annotations: { readOnlyHint: true, openWorldHint: false }
},
async ({ kind, document, fileName }) => {
const parsed = parseDocument(document, fileName ?? "document.yaml");
const result = validate(assertSchemaKind(kind), parsed);
return textResult(JSON.stringify(result.ok ? { ok: true, kind: result.kind } : { ok: false, kind: result.kind, errors: result.errors }, null, 2));
}
);
server.registerTool(
"example_document",
{
title: "Generate Example LogicSRC Document",
description: "Returns a minimal example document for a LogicSRC schema kind.",
inputSchema: {
kind: z.enum(["agent", "event", "plugin", "run", "task"])
},
annotations: { readOnlyHint: true, openWorldHint: false }
},
async ({ kind }) => textResult(JSON.stringify(exampleFor(kind), null, 2))
);
server.registerPrompt(
"create-valid-task",
{
title: "Create Valid LogicSRC Task",
description: "Prompt template for turning a workflow request into a valid LogicSRC task.",
argsSchema: {
request: z.string().describe("Human description of the desired task.")
}
},
async ({ request }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `Create a valid LogicSRC task JSON document for this request. Use logicsrc://schemas/task and keep it minimal unless details are required.\n\nRequest:\n${request}`
}
}
]
})
);
server.registerPrompt(
"review-plugin-manifest",
{
title: "Review LogicSRC Plugin Manifest",
description: "Prompt template for reviewing a plugin manifest against the LogicSRC plugin schema.",
argsSchema: {
manifest: z.string().describe("Plugin manifest JSON or YAML.")
}
},
async ({ manifest }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `Review this LogicSRC plugin manifest against logicsrc://schemas/plugin. Identify schema issues, security concerns, missing permissions, and unclear capabilities.\n\nManifest:\n${manifest}`
}
}
]
})
);
return server;
}
function textResult(text: string) {
return { content: [{ type: "text" as const, text }] };
}
function titleCase(value: string) {
return value.replace(/(^|-)([a-z])/g, (_match, prefix: string, letter: string) => `${prefix ? " " : ""}${letter.toUpperCase()}`);
}
function exampleFor(kind: SchemaKind) {
switch (kind) {
case "agent":
return {
type: "logicsrc.agent",
version: "0.1",
agent_did: "qa-agent-01.coinpay",
name: "QA Agent",
capabilities: ["browser.qa", "report.write"],
status: "active"
};
case "event":
return {
type: "logicsrc.event",
version: "0.1",
event_id: "evt_123",
event_type: "task.created",
resource_type: "task",
resource_id: "task_123",
actor_did: "anthony.coinpay",
created_at: new Date(0).toISOString()
};
case "plugin":
return {
type: "logicsrc.plugin",
version: "0.1",
id: "example-plugin",
name: "Example Plugin",
description: "Example LogicSRC plugin manifest.",
capabilities: ["tasks.read"],
permissions: ["tasks:read"]
};
case "run":
return {
type: "logicsrc.run",
version: "0.1",
run_id: "run_123",
task_id: "task_123",
agent_did: "qa-agent-01.coinpay",
status: "completed",
started_at: new Date(0).toISOString()
};
case "task":
return {
type: "logicsrc.task",
version: "0.1",
title: "Test checkout flow",
description: "Verify checkout flow across desktop and mobile.",
board: "/qa",
creator_did: "anthony.coinpay",
status: "open",
budget: { amount: 25, currency: "USDC" },
agent_allowed: true,
human_allowed: true
};
}
}

View file

@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
},
"include": ["src/**/*.ts"]
}