doubleoh-mcp 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 +34 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +17 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +56 -0
- package/package.json +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# doubleoh-mcp
|
|
2
|
+
|
|
3
|
+
**The DoubleOh fix loop as an MCP server.** Any MCP-capable agent — Claude Code, Claude
|
|
4
|
+
Desktop, and the growing list of clients — gets the loop as three tools with zero
|
|
5
|
+
integration code.
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"mcpServers": {
|
|
10
|
+
"doubleoh": {
|
|
11
|
+
"command": "bunx",
|
|
12
|
+
"args": ["doubleoh-mcp"],
|
|
13
|
+
"env": { "DOUBLEOH_API_KEY": "oo_live_…" }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## The tools
|
|
20
|
+
|
|
21
|
+
- **`doubleoh_skills_for`** — before attempting a task it has failed at before, the agent
|
|
22
|
+
asks what has been learned and receives step-by-step procedures from past human fixes,
|
|
23
|
+
with each one's track record.
|
|
24
|
+
- **`doubleoh_request_fix`** — when stuck, the agent asks for a human. If the fleet
|
|
25
|
+
already knows the wall, the procedure comes back instantly instead and nobody is paged.
|
|
26
|
+
Duplicate walls are answered with "already being fixed, N runs blocked" — an agent in a
|
|
27
|
+
retry loop never spams a team.
|
|
28
|
+
- **`doubleoh_report_skill`** — after following a skill, the agent reports whether it
|
|
29
|
+
worked. Skills that keep failing stop being served.
|
|
30
|
+
|
|
31
|
+
Refusals (a private URL, a quota) come back as readable tool results, not protocol
|
|
32
|
+
errors — the model is told what went wrong in a sentence it can act on.
|
|
33
|
+
|
|
34
|
+
`DOUBLEOH_BASE_URL` points at a self-hosted deployment; it defaults to the hosted service.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** The binary: read the environment, speak stdio. Plain Node — no bun required. */
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { DoubleOh } from "@doubleoh/sdk";
|
|
5
|
+
import { createServer } from "./index.js";
|
|
6
|
+
const apiKey = process.env.DOUBLEOH_API_KEY;
|
|
7
|
+
if (!apiKey) {
|
|
8
|
+
console.error("DOUBLEOH_API_KEY is required (it starts with oo_).");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const client = new DoubleOh({
|
|
12
|
+
apiKey,
|
|
13
|
+
...(process.env.DOUBLEOH_BASE_URL
|
|
14
|
+
? { baseUrl: process.env.DOUBLEOH_BASE_URL }
|
|
15
|
+
: {}),
|
|
16
|
+
});
|
|
17
|
+
await createServer(client).connect(new StdioServerTransport());
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* doubleoh-mcp — the fix loop as an MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Any MCP-capable agent (Claude Code, Claude Desktop, and the growing list of clients) gets the
|
|
5
|
+
* three tools with zero integration code:
|
|
6
|
+
*
|
|
7
|
+
* { "mcpServers": { "doubleoh": {
|
|
8
|
+
* "command": "bunx", "args": ["doubleoh-mcp"],
|
|
9
|
+
* "env": { "DOUBLEOH_API_KEY": "oo_live_…" } } } }
|
|
10
|
+
*
|
|
11
|
+
* The tools themselves come from @doubleoh/sdk's framework-agnostic definitions — this file is only
|
|
12
|
+
* the MCP shell around them, which is the point: one definition, every framework. The low-level
|
|
13
|
+
* Server API is used on purpose: the definitions already carry JSON Schema, which is what MCP puts
|
|
14
|
+
* on the wire, so translating them through zod would be a round trip to nowhere.
|
|
15
|
+
*/
|
|
16
|
+
import { DoubleOh } from "@doubleoh/sdk";
|
|
17
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
18
|
+
export declare function createServer(client: DoubleOh): Server;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* doubleoh-mcp — the fix loop as an MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Any MCP-capable agent (Claude Code, Claude Desktop, and the growing list of clients) gets the
|
|
5
|
+
* three tools with zero integration code:
|
|
6
|
+
*
|
|
7
|
+
* { "mcpServers": { "doubleoh": {
|
|
8
|
+
* "command": "bunx", "args": ["doubleoh-mcp"],
|
|
9
|
+
* "env": { "DOUBLEOH_API_KEY": "oo_live_…" } } } }
|
|
10
|
+
*
|
|
11
|
+
* The tools themselves come from @doubleoh/sdk's framework-agnostic definitions — this file is only
|
|
12
|
+
* the MCP shell around them, which is the point: one definition, every framework. The low-level
|
|
13
|
+
* Server API is used on purpose: the definitions already carry JSON Schema, which is what MCP puts
|
|
14
|
+
* on the wire, so translating them through zod would be a round trip to nowhere.
|
|
15
|
+
*/
|
|
16
|
+
import { DoubleOhError } from "@doubleoh/sdk";
|
|
17
|
+
import { doubleohTools } from "@doubleoh/sdk/tools";
|
|
18
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
19
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
20
|
+
export function createServer(client) {
|
|
21
|
+
const tools = doubleohTools(client);
|
|
22
|
+
const server = new Server({ name: "doubleoh", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
23
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
24
|
+
tools: tools.map((tool) => ({
|
|
25
|
+
name: tool.name,
|
|
26
|
+
description: tool.description,
|
|
27
|
+
inputSchema: tool.parameters,
|
|
28
|
+
})),
|
|
29
|
+
}));
|
|
30
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
31
|
+
const tool = tools.find((candidate) => candidate.name === request.params.name);
|
|
32
|
+
if (!tool) {
|
|
33
|
+
return {
|
|
34
|
+
content: [
|
|
35
|
+
{ type: "text", text: `No tool named ${request.params.name}.` },
|
|
36
|
+
],
|
|
37
|
+
isError: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const text = await tool.execute(request.params.arguments ?? {});
|
|
42
|
+
return { content: [{ type: "text", text }] };
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
/*
|
|
46
|
+
* A refusal is an answer the model should read, not a protocol failure: "that address is not
|
|
47
|
+
* reachable for a fix" tells the agent to stop aiming at intranet hosts.
|
|
48
|
+
*/
|
|
49
|
+
const text = error instanceof DoubleOhError
|
|
50
|
+
? `DoubleOh refused (${error.status}): ${error.message}`
|
|
51
|
+
: `DoubleOh was unreachable: ${String(error)}`;
|
|
52
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return server;
|
|
56
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "doubleoh-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The DoubleOh fix loop as an MCP server: skills_for, request_fix, report_skill.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"doubleoh-mcp": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
13
|
+
"@doubleoh/sdk": "0.1.0"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc -p tsconfig.build.json",
|
|
17
|
+
"typecheck": "tsc --noEmit"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"bun-types": "^1.4.0"
|
|
21
|
+
},
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
}
|
|
30
|
+
}
|