mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-13 14:37:26 +00:00
Add LogicSRC standards MCP server
This commit is contained in:
parent
4e4c78140d
commit
dd150f391a
26 changed files with 2250 additions and 15 deletions
23
packages/logicsrc-mcp/package.json
Normal file
23
packages/logicsrc-mcp/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
12
packages/logicsrc-mcp/src/index.ts
Normal file
12
packages/logicsrc-mcp/src/index.ts
Normal 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);
|
||||
}
|
||||
52
packages/logicsrc-mcp/src/server.test.ts
Normal file
52
packages/logicsrc-mcp/src/server.test.ts
Normal 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;
|
||||
}
|
||||
220
packages/logicsrc-mcp/src/server.ts
Normal file
220
packages/logicsrc-mcp/src/server.ts
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
8
packages/logicsrc-mcp/tsconfig.json
Normal file
8
packages/logicsrc-mcp/tsconfig.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue