<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Simfinity.js]]></title><description><![CDATA[Simfinity.js]]></description><link>https://simfinity-js.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Simfinity.js</title><link>https://simfinity-js.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 19 Sep 2026 03:42:39 GMT</lastBuildDate><atom:link href="https://simfinity-js.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[From a GraphQL schema to MCP tools with Simfinity.js]]></title><description><![CDATA[A GraphQL schema already describes argument types and result fields. That makes it a useful starting point for exposing an API through MCP. The remaining choices are concrete: which operations clients]]></description><link>https://simfinity-js.hashnode.dev/from-a-graphql-schema-to-mcp-tools-with-simfinity-js</link><guid isPermaLink="true">https://simfinity-js.hashnode.dev/from-a-graphql-schema-to-mcp-tools-with-simfinity-js</guid><category><![CDATA[GraphQL]]></category><category><![CDATA[mcp]]></category><dc:creator><![CDATA[Juan Pablo Paillet]]></dc:creator><pubDate>Wed, 16 Sep 2026 11:15:52 GMT</pubDate><content:encoded><![CDATA[<p>A GraphQL schema already describes argument types and result fields. That makes it a useful starting point for exposing an API through MCP. The remaining choices are concrete: which operations clients should see, what each tool returns, and where authorization runs.</p>
<p>I contribute to <a href="https://github.com/simtlix/simfinity.js">Simfinity.js</a>. Its optional <code>@simtlix/simfinity-mcp</code> package can generate tools from a GraphQL schema. It works independently of the PostgreSQL and MongoDB adapters, so this example starts with an ordinary GraphQL schema and a small in-memory catalog.</p>
<p>We'll inspect the generated tool, call it directly, and then expose the same operation over stdio. No database or model API key is needed for this example.</p>
<p>The <a href="https://gist.github.com/PailletJuanPablo/1d183ea372ea9645f9904760500bae68">complete example is available as a Gist</a>. Download its ZIP if you prefer to start with the files already in place.</p>
<h2>Start with a schema you can inspect</h2>
<p>In an empty directory, run:</p>
<pre><code class="language-sh">npm init -y
npm pkg set type=module
npm install @simtlix/simfinity-mcp@3.2.0 graphql@^16.11.0
</code></pre>
<p>Create <code>schema.js</code>:</p>
<pre><code class="language-javascript">import {
  GraphQLID,
  GraphQLList,
  GraphQLNonNull,
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString,
} from 'graphql';

const starters = [
  { id: 'pg', name: 'PostgreSQL starter', database: 'postgresql' },
  { id: 'mongo', name: 'MongoDB starter', database: 'mongodb' },
];

const Starter = new GraphQLObjectType({
  name: 'Starter',
  fields: {
    id: { type: new GraphQLNonNull(GraphQLID) },
    name: { type: new GraphQLNonNull(GraphQLString) },
    database: { type: new GraphQLNonNull(GraphQLString) },
  },
});

export const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: {
      starters: {
        type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(Starter))),
        description: 'Find the demo starter projects by database.',
        args: { database: { type: GraphQLString } },
        resolve: (_source, { database }) =&gt; starters.filter(
          (starter) =&gt; !database || starter.database === database,
        ),
      },
      serviceStatus: {
        type: GraphQLString,
        resolve: () =&gt; 'demo',
      },
    },
  }),
});
</code></pre>
<p>The data is a two-row demo catalog. The <code>starters</code> resolver accepts a database name and returns matching rows. A second root field, <code>serviceStatus</code>, will let us check that the MCP configuration exposes only the operation we choose.</p>
<p>In an application, the resolver could use your existing data access code. With a Simfinity-generated schema, pass the built schema once type registration and database initialization have completed.</p>
<h2>Choose the operation and result fields</h2>
<p>Create <code>options.js</code>:</p>
<pre><code class="language-javascript">export const options = {
  include: ['starters'],
  toolNamePrefix: 'catalog_',
  toolOverrides: {
    starters: {
      description: 'Find the demo starter projects by database.',
      selection: 'id name database',
    },
  },
  limits: { maxResultBytes: 8192 },
};
</code></pre>
<p><code>include</code> contains the GraphQL root field name. The prefix changes the name presented to MCP clients to <code>catalog_starters</code>; the underlying GraphQL field remains <code>starters</code>.</p>
<p>The explicit selection fixes the result fields to <code>id</code>, <code>name</code>, and <code>database</code>. Clients supply arguments for that operation. They do not supply an arbitrary GraphQL selection through this tool.</p>
<p>This selection also means the generator omits its automatic <code>outputSchema</code> for the tool. Successful calls still provide <code>structuredContent</code>, which we'll use below.</p>
<p>The 8 KB limit caps the logical result payload. It is useful when choosing how much data to return, but it is not a database execution limit and cannot undo a mutation that already ran.</p>
<h2>Inspect and call the generated tool</h2>
<p>Create <code>inspect.js</code>:</p>
<pre><code class="language-javascript">import { generateMCPTools } from '@simtlix/simfinity-mcp';
import { schema } from './schema.js';
import { options } from './options.js';

const { tools, callTool, getOperation } = generateMCPTools(schema, options);

console.log('Tools:', tools.map((tool) =&gt; tool.name));
console.log('Input schema:', JSON.stringify(tools[0].inputSchema, null, 2));
console.log('GraphQL operation:', getOperation('catalog_starters'));

const result = await callTool('catalog_starters', { database: 'postgresql' });
if (result.isError) throw new Error(JSON.stringify(result.content));
console.log('Result:', JSON.stringify(result.structuredContent, null, 2));
</code></pre>
<p>Run it:</p>
<pre><code class="language-sh">node inspect.js
</code></pre>
<p>The tool list contains one entry:</p>
<pre><code class="language-text">Tools: [ 'catalog_starters' ]
</code></pre>
<p><code>serviceStatus</code> is absent. The generated input schema has an optional, nullable string named <code>database</code>, matching the GraphQL argument. The generated operation is:</p>
<pre><code class="language-graphql">query startersOperation($database: String) {
  starters(database: $database) { id name database }
}
</code></pre>
<p>Calling the tool with <code>database: 'postgresql'</code> returns:</p>
<pre><code class="language-json">{
  "starters": [
    {
      "id": "pg",
      "name": "PostgreSQL starter",
      "database": "postgresql"
    }
  ]
}
</code></pre>
<p>This is a useful place to check the mapping before involving a client. You can see the published name, generated input contract, GraphQL document, and actual result together.</p>
<p>The direct call does not require the MCP SDK. Install the SDK when you're ready to use a protocol transport.</p>
<h2>Serve the same tool over stdio</h2>
<p>Install the optional transport dependency:</p>
<pre><code class="language-sh">npm install "@modelcontextprotocol/sdk@^1.13.0"
</code></pre>
<p>Create <code>stdio.js</code>:</p>
<pre><code class="language-javascript">import { startStdioMCPServer } from '@simtlix/simfinity-mcp';
import { schema } from './schema.js';
import { options } from './options.js';

await startStdioMCPServer(schema, {
  ...options,
  serverName: 'starter-catalog',
  serverVersion: '1.0.0',
});
</code></pre>
<p>Configure your MCP client's local server command to launch <code>node</code> with the absolute path to <code>stdio.js</code>. For example, the command's argument would be <code>/path/to/mcp-example/stdio.js</code> on a POSIX system or <code>C:\path\to\mcp-example\stdio.js</code> on Windows.</p>
<p>The relative imports resolve from the entry-point file, so the client does not need to start in the example directory. Use the full path to your Node executable if the client cannot find it through its environment.</p>
<p>Keep application logs off stdout in this entry point, because stdout carries MCP protocol messages. Use <code>console.error</code> for diagnostics. The separate <code>inspect.js</code> script can print normally because it is not a protocol server.</p>
<p>The client should discover <code>catalog_starters</code> and be able to call it with:</p>
<pre><code class="language-json">{ "database": "postgresql" }
</code></pre>
<h2>Bring application rules with the schema</h2>
<p>This demo reads an in-memory array and has no user accounts. Before replacing it with application data, decide which identity reaches the resolvers and how that identity is verified.</p>
<p>The operation allowlist controls which tools are published. Authorization still belongs in the application. For standalone Simfinity MCP execution, the <code>schemaPlugins</code> option can install schema hooks such as the auth plugin; creating a plugin object alone does not apply it. The application must supply trusted context. Tool arguments should never be treated as proof of the caller's identity.</p>
<p>If you add mutations later, account for the same transaction requirements as the GraphQL API. Cancellation does not roll back database work that has already started. For an HTTP transport, follow the documented authentication and host/origin configuration rather than deploying this local stdio example as a public endpoint.</p>
<p>I checked this example with the published <code>@simtlix/simfinity-mcp</code> 3.2.0 package on Node.js 24.15.0. The checks covered direct execution, filtering, a nullable argument, rejection of an invalid argument type, and discovery and invocation through an actual MCP stdio client. The exposed tool list contained only <code>catalog_starters</code>.</p>
<p>The <a href="https://simtlix.github.io/simfinity.js/guide/mcp.html">MCP guide</a> covers the database-backed setup and HTTP transport. The <a href="https://simtlix.github.io/simfinity.js/reference/mcp.html">MCP reference</a> documents the remaining generation and execution options. For a complete API with storage, start with the <a href="https://simtlix.github.io/simfinity.js/guide/postgresql.html">PostgreSQL</a> or <a href="https://simtlix.github.io/simfinity.js/guide/getting-started.html">MongoDB</a> quick start.</p>
]]></content:encoded></item><item><title><![CDATA[From GraphQL types to a PostgreSQL API with Simfinity.js]]></title><description><![CDATA[A series catalog starts with a small domain: a serie has a name, and each season belongs to a serie. Even that model needs list queries, create/update/delete operations, relationship resolvers, valida]]></description><link>https://simfinity-js.hashnode.dev/from-graphql-types-to-a-postgresql-api-with-simfinity-js</link><guid isPermaLink="true">https://simfinity-js.hashnode.dev/from-graphql-types-to-a-postgresql-api-with-simfinity-js</guid><category><![CDATA[GraphQL]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Juan Pablo Paillet]]></dc:creator><pubDate>Wed, 16 Sep 2026 05:54:06 GMT</pubDate><content:encoded><![CDATA[<p>A series catalog starts with a small domain: a serie has a name, and each season belongs to a serie. Even that model needs list queries, create/update/delete operations, relationship resolvers, validation and database storage.</p>
<p><a href="https://simtlix.github.io/simfinity.js/">Simfinity.js</a> generates those recurring pieces from GraphQL object types. Version 3.2.0 provides MongoDB/Mongoose and PostgreSQL adapters, plus an optional package for exposing GraphQL operations as MCP tools. The project is open source under Apache-2.0.</p>
<p>This walkthrough uses the published PostgreSQL starter. It includes a nested series/season example, so there is a relationship to inspect as well as a working API.</p>
<h2>Start with the domain</h2>
<p>The type definition uses the standard <code>graphql</code> package. A minimal entity looks like this:</p>
<pre><code class="language-javascript">import {
  GraphQLID,
  GraphQLNonNull,
  GraphQLObjectType,
  GraphQLString,
} from 'graphql';

const SerieType = new GraphQLObjectType({
  name: 'Serie',
  fields: {
    id: { type: GraphQLID },
    name: { type: new GraphQLNonNull(GraphQLString) },
  },
});
</code></pre>
<p>Register the type with the PostgreSQL runtime, supplying the singular and plural operation names:</p>
<pre><code class="language-javascript">import pg from 'pg';
import { createPostgres } from '@simtlix/simfinity-postgres';

const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
});
const simfinity = createPostgres({ pool, schema: 'series_api' });

simfinity.connect(null, SerieType, 'serie', 'series');
const schema = simfinity.createSchema();
await simfinity.initializeDatabase({ mode: 'create' });
</code></pre>
<p>The resulting schema can be served through GraphQL Yoga. The downloaded starter includes that server setup, shutdown handling, a <code>Season</code> type and a controller. The snippets above show the core registration flow; the starter is the complete application.</p>
<h2>Run the released starter</h2>
<p>Use Node.js 22+ for the starter and PostgreSQL 15+. Download and extract the <a href="https://simtlix.github.io/simfinity.js/releases/simfinity-3.2.0-starters.zip">v3.2.0 starter archive</a>, then enter its <code>postgres</code> directory:</p>
<pre><code class="language-sh">cd simfinity-3.2.0-starters/postgres
npm install
</code></pre>
<p>For a disposable local database, run:</p>
<pre><code class="language-sh">docker run --name simfinity-postgres-demo -e POSTGRES_PASSWORD=simfinity -e POSTGRES_DB=series -p 127.0.0.1:5432:5432 -d postgres:18-alpine
</code></pre>
<p>In a POSIX shell, set the connection string and start the app:</p>
<pre><code class="language-sh">export DATABASE_URL='postgresql://postgres:simfinity@127.0.0.1:5432/series'
npm start
</code></pre>
<p>In PowerShell, use <code>$env:DATABASE_URL = 'postgresql://postgres:simfinity@127.0.0.1:5432/series'</code> before <code>npm start</code>. If port 5432 is occupied, choose another host port and update the connection string. These credentials are for the disposable local example.</p>
<p>Open <code>http://127.0.0.1:4000/graphql</code>. Create a serie and its first season together:</p>
<pre><code class="language-graphql">mutation {
  addserie(input: {
    name: "The Expanse"
    seasons: { added: [{ number: 1 }] }
  }) {
    id
    name
    createdBy
    seasons { id number }
  }
}
</code></pre>
<p>Then read it back:</p>
<pre><code class="language-graphql">query {
  series {
    name
    seasons { number }
  }
}
</code></pre>
<p>The generated relationship resolves the season from its parent. PostgreSQL also has a real foreign key from the season's <code>serie</code> column to the serie table. The API relationship and the database constraint come from the relationship metadata in the type definitions.</p>
<h2>Add application rules where they belong</h2>
<p>Generated CRUD is the starting point. Controllers and lifecycle hooks let you add domain behavior; validators, query scopes, state transitions and authorization rules provide other extension points.</p>
<p>Authentication remains application-owned. Configure the authorization policy and trusted request context for your deployment. The starter's fixed <code>quickstart-user</code> context is demonstration code, not a login system.</p>
<p>Storage initialization is explicit. <code>create</code> mode creates missing generated objects and checks existing definitions; <code>validate</code> mode checks the schema without DDL. Schema evolution still needs deliberate migrations.</p>
<h2>Expose selected operations to an MCP client</h2>
<p>The optional <code>@simtlix/simfinity-mcp</code> package works with either database adapter. Once the application has initialized its GraphQL schema, tool definitions can be generated from selected operations:</p>
<pre><code class="language-javascript">import { generateMCPTools } from '@simtlix/simfinity-mcp';

const { tools } = generateMCPTools(schema, {
  include: ['series'],
  limits: {
    maxPageSize: 100,
    defaultPagination: { page: 1, size: 20 },
  },
});

console.log(tools.map((tool) =&gt; tool.name));
</code></pre>
<p>Install that package separately when needed. Tool generation needs GraphQL; server/transport factories also use the optional MCP SDK. The <a href="https://simtlix.github.io/simfinity.js/guide/mcp.html">MCP guide</a> covers transports, trusted context and authorization.</p>
<h2>Choosing a backend</h2>
<p>Choose the adapter during application startup. PostgreSQL uses UUID identities, SQL constraints and its own native Model/Session APIs. MongoDB uses Mongoose, and generated mutations require a replica set or sharded cluster. Selecting an adapter does not migrate an existing database or translate arbitrary Mongoose calls into SQL.</p>
<p>Start with the <a href="https://simtlix.github.io/simfinity.js/guide/postgresql.html">PostgreSQL quick start</a>, review the <a href="https://simtlix.github.io/simfinity.js/compatibility.html">compatibility boundaries</a>, or explore the larger <a href="https://github.com/simtlix/series-sample">Series Sample Project</a>.</p>
<p><a href="https://github.com/simtlix/simfinity.js">Source code and issues</a></p>
<p>The downloaded PostgreSQL starter was exercised locally with a nested create, query readback and database foreign-key inspection.</p>
]]></content:encoded></item></channel></rss>