@zincapp/mcp-server 1.0.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.
@@ -0,0 +1,8 @@
1
+ import type { McpConfig } from './config.js';
2
+ export declare class ZincAppClient {
3
+ private baseUrl;
4
+ private token;
5
+ constructor(config: McpConfig);
6
+ get<T>(path: string): Promise<T>;
7
+ post<T>(path: string, body?: unknown): Promise<T>;
8
+ }
package/dist/client.js ADDED
@@ -0,0 +1,39 @@
1
+ export class ZincAppClient {
2
+ baseUrl;
3
+ token;
4
+ constructor(config) {
5
+ this.baseUrl = config.apiBaseUrl.replace(/\/$/, '');
6
+ this.token = config.agentToken;
7
+ }
8
+ async get(path) {
9
+ const url = `${this.baseUrl}/agent/v1${path}`;
10
+ const res = await fetch(url, {
11
+ headers: {
12
+ 'Authorization': `Bearer ${this.token}`,
13
+ 'Accept': 'application/json',
14
+ },
15
+ });
16
+ if (!res.ok) {
17
+ const body = await res.text();
18
+ throw new Error(`API error ${res.status}: ${body}`);
19
+ }
20
+ return res.json();
21
+ }
22
+ async post(path, body) {
23
+ const url = `${this.baseUrl}/agent/v1${path}`;
24
+ const res = await fetch(url, {
25
+ method: 'POST',
26
+ headers: {
27
+ 'Authorization': `Bearer ${this.token}`,
28
+ 'Accept': 'application/json',
29
+ 'Content-Type': 'application/json',
30
+ },
31
+ body: body ? JSON.stringify(body) : undefined,
32
+ });
33
+ if (!res.ok) {
34
+ const text = await res.text();
35
+ throw new Error(`API error ${res.status}: ${text}`);
36
+ }
37
+ return res.json();
38
+ }
39
+ }
@@ -0,0 +1,9 @@
1
+ export interface McpConfig {
2
+ agentToken: string;
3
+ apiBaseUrl: string;
4
+ }
5
+ /**
6
+ * Loads config from environment variables, then falls back to .zincapp.json
7
+ * in the current directory or home directory.
8
+ */
9
+ export declare function loadConfig(): McpConfig;
package/dist/config.js ADDED
@@ -0,0 +1,41 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import * as os from 'node:os';
4
+ /**
5
+ * Loads config from environment variables, then falls back to .zincapp.json
6
+ * in the current directory or home directory.
7
+ */
8
+ export function loadConfig() {
9
+ // Environment variables take precedence
10
+ let agentToken = process.env.ZINCAPP_AGENT_TOKEN || '';
11
+ let apiBaseUrl = process.env.ZINCAPP_API_URL || '';
12
+ // Try .zincapp.json if env vars missing
13
+ if (!agentToken) {
14
+ const configPaths = [
15
+ path.join(process.cwd(), '.zincapp.json'),
16
+ path.join(os.homedir(), '.zincapp.json'),
17
+ ];
18
+ for (const configPath of configPaths) {
19
+ try {
20
+ const raw = fs.readFileSync(configPath, 'utf-8');
21
+ const parsed = JSON.parse(raw);
22
+ if (!agentToken && parsed.agentToken)
23
+ agentToken = parsed.agentToken;
24
+ if (!apiBaseUrl && parsed.apiUrl)
25
+ apiBaseUrl = parsed.apiUrl;
26
+ break;
27
+ }
28
+ catch {
29
+ // File doesn't exist or invalid JSON — continue
30
+ }
31
+ }
32
+ }
33
+ if (!agentToken) {
34
+ throw new Error('ZINCAPP_AGENT_TOKEN not set. Configure via environment variable or .zincapp.json file.\n' +
35
+ 'Create a token at: https://developers.zincapp.com/agent-tokens');
36
+ }
37
+ return {
38
+ agentToken,
39
+ apiBaseUrl: apiBaseUrl || 'https://api.zincapp.com/api',
40
+ };
41
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { loadConfig } from './config.js';
5
+ import { ZincAppClient } from './client.js';
6
+ import { registerAllTools } from './tools/index.js';
7
+ import { registerAllResources } from './resources/index.js';
8
+ async function main() {
9
+ const config = loadConfig();
10
+ const client = new ZincAppClient(config);
11
+ const server = new McpServer({
12
+ name: 'zincapp',
13
+ version: '1.0.0',
14
+ });
15
+ registerAllTools(server, client);
16
+ registerAllResources(server, client);
17
+ const transport = new StdioServerTransport();
18
+ await server.connect(transport);
19
+ }
20
+ main().catch((err) => {
21
+ console.error('Failed to start ZincApp MCP server:', err.message || err);
22
+ process.exit(1);
23
+ });
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ZincAppClient } from '../client.js';
3
+ export declare function registerAllResources(server: McpServer, client: ZincAppClient): void;
@@ -0,0 +1,19 @@
1
+ export function registerAllResources(server, client) {
2
+ server.resource('docs-index', 'docs://index', async (uri) => {
3
+ const docs = await client.get('/docs?limit=200');
4
+ const text = docs.length === 0
5
+ ? 'No documentation available.'
6
+ : [
7
+ '# ZincApp Documentation Index',
8
+ '',
9
+ ...docs.map(d => `- **${d.title}** (\`${d.id}\`): ${d.aiSummary || d.description}`),
10
+ ].join('\n');
11
+ return {
12
+ contents: [{
13
+ uri: uri.href,
14
+ mimeType: 'text/markdown',
15
+ text,
16
+ }],
17
+ };
18
+ });
19
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ZincAppClient } from '../client.js';
3
+ export declare function registerGetContract(server: McpServer, client: ZincAppClient): void;
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ export function registerGetContract(server, client) {
3
+ server.tool('get_contract_spec', 'Get details about a specific integration contract, including its API spec and terms.', { contractId: z.number().describe('Contract ID') }, async ({ contractId }) => {
4
+ const contract = await client.get(`/contracts/${contractId}`);
5
+ const parts = [
6
+ `# ${contract.title} (v${contract.version})`,
7
+ '',
8
+ `**Status:** ${contract.status}`,
9
+ ];
10
+ if (contract.description) {
11
+ parts.push('', contract.description);
12
+ }
13
+ if (contract.terms) {
14
+ parts.push('', '## Terms', '', contract.terms);
15
+ }
16
+ if (contract.spec) {
17
+ parts.push('', '## API Spec', '', '```json', contract.spec, '```');
18
+ }
19
+ return { content: [{ type: 'text', text: parts.join('\n') }] };
20
+ });
21
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ZincAppClient } from '../client.js';
3
+ export declare function registerGetEndpoint(server: McpServer, client: ZincAppClient): void;
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod';
2
+ export function registerGetEndpoint(server, client) {
3
+ server.tool('get_api_endpoint', 'Get details about a specific API endpoint by method and path.', {
4
+ method: z.enum(['GET', 'POST', 'PUT', 'DELETE']).describe('HTTP method'),
5
+ path: z.string().describe('API path (e.g. "/containers")'),
6
+ }, async ({ method, path }) => {
7
+ const encodedPath = path.startsWith('/') ? path.substring(1) : path;
8
+ const detail = await client.get(`/reference/endpoints/${method}/${encodedPath}`);
9
+ const text = [
10
+ `## ${detail.method} ${detail.path}`,
11
+ '',
12
+ `**Summary:** ${detail.summary}`,
13
+ `**Auth:** ${detail.auth}`,
14
+ `**Category:** ${detail.category}`,
15
+ ].join('\n');
16
+ return { content: [{ type: 'text', text }] };
17
+ });
18
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ZincAppClient } from '../client.js';
3
+ export declare function registerAllTools(server: McpServer, client: ZincAppClient): void;
@@ -0,0 +1,16 @@
1
+ import { registerSearchDocs } from './search-docs.js';
2
+ import { registerReadDoc } from './read-doc.js';
3
+ import { registerListDocs } from './list-docs.js';
4
+ import { registerListEndpoints } from './list-endpoints.js';
5
+ import { registerGetEndpoint } from './get-endpoint.js';
6
+ import { registerSandboxRequest } from './sandbox-request.js';
7
+ import { registerGetContract } from './get-contract.js';
8
+ export function registerAllTools(server, client) {
9
+ registerSearchDocs(server, client);
10
+ registerReadDoc(server, client);
11
+ registerListDocs(server, client);
12
+ registerListEndpoints(server, client);
13
+ registerGetEndpoint(server, client);
14
+ registerSandboxRequest(server, client);
15
+ registerGetContract(server, client);
16
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ZincAppClient } from '../client.js';
3
+ export declare function registerListDocs(server: McpServer, client: ZincAppClient): void;
@@ -0,0 +1,9 @@
1
+ export function registerListDocs(server, client) {
2
+ server.tool('list_docs', 'List all available ZincApp developer documentation with titles and summaries.', {}, async () => {
3
+ const docs = await client.get('/docs?limit=200');
4
+ const text = docs.length === 0
5
+ ? 'No documentation available.'
6
+ : docs.map(d => `- **${d.title}** (\`${d.id}\`): ${d.aiSummary || d.description}`).join('\n');
7
+ return { content: [{ type: 'text', text }] };
8
+ });
9
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ZincAppClient } from '../client.js';
3
+ export declare function registerListEndpoints(server: McpServer, client: ZincAppClient): void;
@@ -0,0 +1,25 @@
1
+ export function registerListEndpoints(server, client) {
2
+ server.tool('list_api_endpoints', 'List all ZincApp API endpoints available to your integration. Groups by category.', {}, async () => {
3
+ const endpoints = await client.get('/reference/endpoints');
4
+ if (endpoints.length === 0) {
5
+ return { content: [{ type: 'text', text: 'No API endpoints available. You may need an accepted contract.' }] };
6
+ }
7
+ // Group by category
8
+ const grouped = new Map();
9
+ for (const ep of endpoints) {
10
+ const cat = ep.category || 'General';
11
+ if (!grouped.has(cat))
12
+ grouped.set(cat, []);
13
+ grouped.get(cat).push(ep);
14
+ }
15
+ let text = '';
16
+ for (const [category, eps] of grouped) {
17
+ text += `## ${category}\n\n`;
18
+ for (const ep of eps) {
19
+ text += `- \`${ep.method} ${ep.path}\` — ${ep.summary} (auth: ${ep.auth})\n`;
20
+ }
21
+ text += '\n';
22
+ }
23
+ return { content: [{ type: 'text', text: text.trim() }] };
24
+ });
25
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ZincAppClient } from '../client.js';
3
+ export declare function registerReadDoc(server: McpServer, client: ZincAppClient): void;
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ export function registerReadDoc(server, client) {
3
+ server.tool('read_doc', 'Read a specific ZincApp documentation page by its ID. Returns the full markdown content.', { docId: z.string().describe('Document ID (e.g. "api.overview")') }, async ({ docId }) => {
4
+ const doc = await client.get(`/docs/${encodeURIComponent(docId)}`);
5
+ const text = `# ${doc.title}\n\n${doc.markdown}`;
6
+ return { content: [{ type: 'text', text }] };
7
+ });
8
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ZincAppClient } from '../client.js';
3
+ export declare function registerSandboxRequest(server: McpServer, client: ZincAppClient): void;
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod';
2
+ export function registerSandboxRequest(server, client) {
3
+ server.tool('sandbox_api_request', 'Execute a test API call in the ZincApp sandbox environment. Only works with the SANDBOX_API scope.', {
4
+ method: z.enum(['GET', 'POST', 'PUT', 'DELETE']).describe('HTTP method'),
5
+ path: z.string().describe('API path to call (e.g. "/containers")'),
6
+ body: z.string().optional().describe('Request body as JSON string (for POST/PUT)'),
7
+ }, async ({ method, path, body }) => {
8
+ const result = await client.post('/sandbox/relay', {
9
+ method,
10
+ path,
11
+ body: body || null,
12
+ });
13
+ const text = [
14
+ `**Status:** ${result.status}`,
15
+ '',
16
+ '```json',
17
+ result.body || '(empty)',
18
+ '```',
19
+ ].join('\n');
20
+ return { content: [{ type: 'text', text }] };
21
+ });
22
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ZincAppClient } from '../client.js';
3
+ export declare function registerSearchDocs(server: McpServer, client: ZincAppClient): void;
@@ -0,0 +1,10 @@
1
+ import { z } from 'zod';
2
+ export function registerSearchDocs(server, client) {
3
+ server.tool('search_docs', 'Search ZincApp developer documentation by keyword. Returns matching documents ranked by relevance.', { query: z.string().describe('Search query'), limit: z.number().optional().describe('Max results (default 10)') }, async ({ query, limit }) => {
4
+ const results = await client.get(`/docs/search?q=${encodeURIComponent(query)}&limit=${limit || 10}`);
5
+ const text = results.length === 0
6
+ ? 'No documents found matching your query.'
7
+ : results.map(r => `## ${r.title}\n- ID: \`${r.id}\`\n- ${r.aiSummary || r.description}\n- Relevance: ${r.relevance.toFixed(2)}`).join('\n\n');
8
+ return { content: [{ type: 'text', text }] };
9
+ });
10
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@zincapp/mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for ZincApp Developer Portal — enables AI coding assistants to search docs, read API reference, and execute sandbox API calls",
5
+ "type": "module",
6
+ "bin": {
7
+ "zincapp-mcp-server": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js"
14
+ },
15
+ "keywords": [
16
+ "mcp",
17
+ "zincapp",
18
+ "developer-portal",
19
+ "ai-agent",
20
+ "claude-code"
21
+ ],
22
+ "license": "MIT",
23
+ "private": false,
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^1.12.0",
29
+ "zod": "^3.23.0"
30
+ },
31
+ "devDependencies": {
32
+ "typescript": "^5.6.0",
33
+ "@types/node": "^22.0.0"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/vidaldiego/zincapp-mcp-server"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "engines": {
43
+ "node": ">=18"
44
+ }
45
+ }