@conciergeprotocol/mcp 0.0.0-stage → 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/package.json CHANGED
@@ -1,6 +1,29 @@
1
1
  {
2
2
  "name": "@conciergeprotocol/mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "MCP server for asking a Concierge Agent Protocol front door.",
5
+ "type": "module",
6
+ "bin": {
7
+ "cap-mcp": "./src/index.js"
8
+ },
9
+ "files": [
10
+ "src"
11
+ ],
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "license": "Apache-2.0",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/conciergeprotocol/js.git",
19
+ "directory": "packages/mcp"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "dependencies": {
25
+ "@conciergeprotocol/client": "workspace:*",
26
+ "@modelcontextprotocol/sdk": "1.32.1",
27
+ "zod": "^4.6.7"
28
+ }
29
+ }
package/src/index.js ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ import { createClient } from '@conciergeprotocol/client'
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
5
+ import * as z from 'zod/v4'
6
+ import { createTools, toolError } from './tools.js'
7
+
8
+ const FROM = /^agent:[A-Za-z0-9_.-]{1,64}$/
9
+ const from = process.env.CAP_FROM || 'agent:cap-mcp'
10
+ if (!FROM.test(from)) {
11
+ console.error('CAP_FROM must be agent:{id}.')
12
+ process.exit(1)
13
+ }
14
+
15
+ const ASK = [
16
+ 'Ask a company that publishes a CAP front door.',
17
+ 'Use this when the person asks about a specific company, product, store, or organization with a known website. Prefer it to browsing when the site has a front door.',
18
+ 'Send only the new question in a follow-up, with conversation_id.',
19
+ 'Never write an answer for the site while it is still answering.',
20
+ 'The reply is the site\'s own framing. For comparisons and reviews, check other sources too.',
21
+ 'Never put the person\'s password or another site\'s data in context.',
22
+ ].join(' ')
23
+
24
+ const tools = createTools(createClient({ from, maxWaitMs: 60_000 }))
25
+
26
+ function ok(value) {
27
+ return { content: [{ type: 'text', text: JSON.stringify(value) }] }
28
+ }
29
+
30
+ function fail(err) {
31
+ return { isError: true, content: [{ type: 'text', text: toolError(err) }] }
32
+ }
33
+
34
+ const server = new McpServer({ name: 'cap', version: '0.1.0' })
35
+
36
+ server.registerTool('cap_discover', {
37
+ description: 'Check whether a website has a CAP front door, and whether visitors can log in.',
38
+ inputSchema: { domain: z.string().describe('The company domain, such as cosmicjs.com') },
39
+ }, async (args) => {
40
+ try {
41
+ return ok(await tools.cap_discover(args))
42
+ } catch (err) {
43
+ return fail(err)
44
+ }
45
+ })
46
+
47
+ server.registerTool('cap_ask', {
48
+ description: ASK,
49
+ inputSchema: {
50
+ domain: z.string().describe('The company domain, such as cosmicjs.com'),
51
+ message: z.string().describe('The question to send. On a follow-up, send only the new question.'),
52
+ context: z.record(z.string(), z.unknown()).optional().describe('A short brief such as sizes, dates, or style. Not a profile.'),
53
+ conversation_id: z.string().optional().describe('The conversation_id from the previous cap_ask, when this continues that conversation.'),
54
+ },
55
+ }, async (args) => {
56
+ try {
57
+ return ok(await tools.cap_ask(args))
58
+ } catch (err) {
59
+ return fail(err)
60
+ }
61
+ })
62
+
63
+ server.registerTool('cap_check', {
64
+ description: 'Read a CAP message that was still being answered. Do not guess a reply while the status is working.',
65
+ inputSchema: {
66
+ domain: z.string().describe('The company domain used in cap_ask'),
67
+ message_id: z.string().describe('The message_id returned by cap_ask'),
68
+ },
69
+ }, async (args) => {
70
+ try {
71
+ return ok(await tools.cap_check(args))
72
+ } catch (err) {
73
+ return fail(err)
74
+ }
75
+ })
76
+
77
+ const transport = new StdioServerTransport()
78
+ await server.connect(transport)
package/src/tools.js ADDED
@@ -0,0 +1,58 @@
1
+ import { CapError, NoDoorError } from '@conciergeprotocol/client'
2
+
3
+ const ASK_HINT = 'The company is still answering. Call cap_check with this message_id. Do not write a reply for the company.'
4
+
5
+ export function createTools(cap) {
6
+ return {
7
+ async cap_discover({ domain }) {
8
+ const door = await cap.discover(domain)
9
+ if (!door) return { door: false }
10
+ return {
11
+ door: true,
12
+ name: door.name,
13
+ chat: door.chat,
14
+ login: Boolean(door.oauth),
15
+ }
16
+ },
17
+
18
+ async cap_ask({ domain, message, context, conversation_id }) {
19
+ try {
20
+ const answer = await cap.ask(domain, message, {
21
+ context,
22
+ previousMessageId: conversation_id,
23
+ })
24
+ if (answer.status === 'working') {
25
+ return {
26
+ status: 'working',
27
+ message_id: answer.id,
28
+ conversation_id: answer.id,
29
+ message: ASK_HINT,
30
+ }
31
+ }
32
+ if (answer.status === 'failed') {
33
+ return { status: 'failed', error: answer.error, conversation_id: answer.id }
34
+ }
35
+ return { status: 'done', reply: answer.reply, conversation_id: answer.id }
36
+ } catch (err) {
37
+ if (err instanceof NoDoorError) {
38
+ return { door: false, message: `${domain} has no CAP front door. Browse the site instead.` }
39
+ }
40
+ throw err
41
+ }
42
+ },
43
+
44
+ async cap_check({ domain, message_id }) {
45
+ const answer = await cap.get(domain, message_id)
46
+ if (answer.status === 'working') {
47
+ return { status: 'working', message_id: answer.id, message: ASK_HINT }
48
+ }
49
+ if (answer.status === 'failed') return { status: 'failed', error: answer.error, message_id: answer.id }
50
+ return { status: 'done', reply: answer.reply, message_id: answer.id }
51
+ },
52
+ }
53
+ }
54
+
55
+ export function toolError(err) {
56
+ if (err instanceof CapError) return err.message
57
+ return 'The front door could not be reached.'
58
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.