mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-13 22:37:29 +00:00
Everything the two shipped PRD phases deferred, minus what is called out below.
Storage (Phase 2)
@logicsrc/openontology gains a SQLite/Turso adapter. It hydrates the read
model at open, serves reads synchronously — a query evaluator that awaits per
triple pattern is unusable — and buffers mutations as SQL that flush() writes
in one transaction. Versioned idempotent migrations; indexes over subject,
predicate, entity-valued object, status, both time axes, aliases, and external
ids; FTS5 for label/alias search. The append-only status log is replayed on
open, so retractions, supersessions, and merge redirects survive a reopen.
REST + SSE + OpenAPI (Phase 2)
16 paths under /api/ontologies in logicsrc-web, described at
/api/ontologies/openapi and referencing the published JSON Schemas rather
than restating them. No token is read-only; a curator token can apply; an
agent token can propose and cannot apply. Idempotency-Key on mutations,
revision ETags, 409 on a stale base revision, and an SSE stream that emits
the same event objects as the JSON endpoint.
MCP (Phase 2)
OpenOntology and OpenPRD surfaces on the standards server: spec/manifest/
schema/queries and PRD spec/index as resources, 11 ontology tools and 6 PRD
tools, 7 prompts. Read-only by default; OPENONTOLOGY_MCP_WRITABLE=1 buys
proposals, never applies — the denial is the shared policy layer, not a
second rule that could drift.
Interoperability (Phase 3)
RDF/Turtle export and import of the reified profile, plus the plain triple
for asserted relationships so a consumer wanting only the accepted graph gets
one. SHACL for 5 of 7 constraint kinds; `unique` and `query` are reported as
unmapped in both the return value and the generated Turtle, because a shape
that quietly means something narrower is worse than no shape.
Source adapters (Phase 3)
CSV, JSON, YAML, NDJSON, Markdown, generic JSON HTTP, and GitHub. All produce
PROPOSED change-set operations with source, evidence selector, run id, and
confidence attached; fetch is injected so ingestion is offline and testable.
Each declares its capabilities, so "nothing was deleted upstream" is never
confused with "this adapter cannot see deletions" — none of the seven can.
TUI + explorer
Keyboard-first panels (types, entities, claims, sources, queries, change
sets, validation, audit) as plain strings that survive SSH and 60 columns;
status is a glyph and a word, never colour alone; the key bar wraps rather
than truncating. Wired as `logicsrc ontology tui`. A read-only web explorer
at /openontology/explore with entity and claim views showing status, both
clocks, confidence, sources, evidence, and append-only history — plus an
/openprd page for the companion standard.
Bugs found and fixed while testing
- the API built a new engine per request, so `explain` could never find a
resultId from a prior request; engines are now cached per role
- the TUI status bar called engine.validateOntologyPackage(), appending a
package.validated event on every repaint; it now uses the pure validator
Verification: 76 new tests (527 total across the monorepo, all passing); full
build green; the libSQL adapter is exercised against real files, the API
through its route handlers, and MCP over an in-memory transport.
Not included: PWA review/approval write flows (they need an auth story this
deployment does not have), OWL/RDFS mappings, SPARQL/Cypher/Datalog query
adapters, and Phase 4 governed actions. The compatibility matrix marks those
"planned", not "supported".
Refs: prd/0001-add-logicsrc-openontology-spec.md
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
293 lines
10 KiB
TypeScript
293 lines
10 KiB
TypeScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
import { z } from "zod";
|
|
import { assertSchemaKind, parseDocument, schemas, validate, type SchemaKind } from "@logicsrc/validators";
|
|
import { registerOpenOntology } from "./openontology.js";
|
|
import { registerOpenPrd } from "./openprd.js";
|
|
|
|
const docs = {
|
|
"communication-accounts": `LogicSRC Communication Accounts defines shared contracts for connecting social and email identities, granting scoped human/agent/plugin access, evaluating policy gates, brokering credentials, and auditing every account action without exposing raw secrets.`,
|
|
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, draft object generation, OpenOntology knowledge (entities, claims, provenance, governed change sets), and OpenPRD product requirements documents. Treat CommandBoard.run as a reference implementation, not the standards identity. Ontology and PRD write tools propose; they never apply."
|
|
}
|
|
);
|
|
|
|
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(["account-audit-event", "account-grant", "account-provider", "agent", "connected-account", "email-message", "event", "plugin", "run", "social-post", "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(["account-audit-event", "account-grant", "account-provider", "agent", "connected-account", "email-message", "event", "plugin", "run", "social-post", "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}`
|
|
}
|
|
}
|
|
]
|
|
})
|
|
);
|
|
|
|
registerOpenOntology(server);
|
|
registerOpenPrd(server);
|
|
|
|
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 "account-audit-event":
|
|
return {
|
|
id: "acct_audit_123",
|
|
provider: "gmail",
|
|
kind: "email",
|
|
principal: { type: "agent", id: "marketing-agent" },
|
|
action: "email:send",
|
|
decision: "approval_required",
|
|
riskScore: 0.35,
|
|
requestPreview: { draft_id: "draft_123" },
|
|
resultPreview: {},
|
|
createdAt: new Date(0).toISOString()
|
|
};
|
|
case "account-grant":
|
|
return {
|
|
id: "grant_123",
|
|
accountId: "account_123",
|
|
principal: { type: "agent", id: "marketing-agent" },
|
|
permissions: ["email:headers:read", "email:draft"],
|
|
policy: [],
|
|
createdAt: new Date(0).toISOString()
|
|
};
|
|
case "account-provider":
|
|
return {
|
|
id: "gmail",
|
|
name: "Gmail",
|
|
kind: "email",
|
|
authMethods: ["oauth2"],
|
|
capabilities: ["email.headers.read", "email.search"]
|
|
};
|
|
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 "connected-account":
|
|
return {
|
|
id: "account_123",
|
|
ownerUserId: "user_123",
|
|
kind: "email",
|
|
provider: "gmail",
|
|
displayName: "Founder Inbox",
|
|
email: "founder@example.com",
|
|
status: "connected",
|
|
scopes: ["gmail.metadata"],
|
|
capabilities: ["email.headers.read", "email.search"],
|
|
credentialRef: "cred://gmail/account_123",
|
|
metadata: {},
|
|
createdAt: new Date(0).toISOString(),
|
|
updatedAt: new Date(0).toISOString()
|
|
};
|
|
case "email-message":
|
|
return {
|
|
id: "email_msg_123",
|
|
providerMessageId: "provider_msg_123",
|
|
subject: "Hello",
|
|
toAddresses: ["founder@example.com"],
|
|
ccAddresses: [],
|
|
labels: ["inbox"],
|
|
hasAttachments: false
|
|
};
|
|
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 "social-post":
|
|
return {
|
|
id: "social_post_123",
|
|
accountId: "account_123",
|
|
status: "draft",
|
|
text: "Launching today",
|
|
media: [],
|
|
metadata: {},
|
|
createdAt: new Date(0).toISOString(),
|
|
updatedAt: 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
|
|
};
|
|
}
|
|
}
|